verfication email & new auth domain

This commit is contained in:
Andreas Knuth 2025-03-01 22:34:38 +01:00
parent 27242819e2
commit 4c19356188
7 changed files with 367 additions and 35 deletions

View File

@ -18,7 +18,17 @@ export class MailController {
return await this.mailService.sendRequest(mailInfo); return await this.mailService.sendRequest(mailInfo);
} }
} }
@Post('verify-email')
async sendVerificationEmail(@Body() data: {
email: string,
redirectConfig: {
protocol: string,
hostname: string,
port?: number
}
}): Promise<void | ErrorResponse> {
return await this.mailService.sendVerificationEmail(data.email, data.redirectConfig);
}
@UseGuards(OptionalAuthGuard) @UseGuards(OptionalAuthGuard)
@Post('subscriptionConfirmation') @Post('subscriptionConfirmation')
async sendSubscriptionConfirmation(@Body() user: User): Promise<void | ErrorResponse> { async sendSubscriptionConfirmation(@Body() user: User): Promise<void | ErrorResponse> {

View File

@ -11,8 +11,6 @@ import { UserModule } from '../user/user.module';
import { UserService } from '../user/user.service'; import { UserService } from '../user/user.service';
import { MailController } from './mail.controller'; import { MailController } from './mail.controller';
import { MailService } from './mail.service'; import { MailService } from './mail.service';
// const __filename = fileURLToPath(import.meta.url);
// const __dirname = path.dirname(__filename);
@Module({ @Module({
imports: [ imports: [
@ -20,32 +18,6 @@ import { MailService } from './mail.service';
UserModule, UserModule,
GeoModule, GeoModule,
FirebaseAdminModule, FirebaseAdminModule,
// ConfigModule.forFeature(mailConfig),
// MailerModule.forRoot({
// transport: {
// host: 'email-smtp.us-east-2.amazonaws.com',
// secure: false,
// port: 587,
// auth: {
// user: user, //'AKIAU6GDWVAQ2QNFLNWN',
// pass: password, //'BDE9nZv/ARbpotim1mIOir52WgIbpSi9cv1oJoH8oEf7',
// },
// },
// defaults: {
// from: '"No Reply" <noreply@example.com>',
// },
// template: {
// dir: join(__dirname, 'templates'),
// adapter: new HandlebarsAdapter({
// eq: function (a, b) {
// return a === b;
// },
// }),
// options: {
// strict: true,
// },
// },
// }),
MailerModule.forRootAsync({ MailerModule.forRootAsync({
useFactory: () => ({ useFactory: () => ({
transport: { transport: {
@ -53,8 +25,8 @@ import { MailService } from './mail.service';
secure: false, secure: false,
port: 587, port: 587,
auth: { auth: {
user: process.env.AMAZON_USER, //'AKIAU6GDWVAQ2QNFLNWN', user: process.env.AMAZON_USER,
pass: process.env.AMAZON_PASSWORD, //'BDE9nZv/ARbpotim1mIOir52WgIbpSi9cv1oJoH8oEf7', pass: process.env.AMAZON_PASSWORD,
}, },
}, },
defaults: { defaults: {

View File

@ -1,5 +1,6 @@
import { MailerService } from '@nestjs-modules/mailer'; import { MailerService } from '@nestjs-modules/mailer';
import { BadRequestException, Injectable } from '@nestjs/common'; import { BadRequestException, Injectable } from '@nestjs/common';
import { getAuth } from 'firebase-admin/auth';
import { join } from 'path'; import { join } from 'path';
import { ZodError } from 'zod'; import { ZodError } from 'zod';
import { SenderSchema, ShareByEMail, ShareByEMailSchema, User } from '../models/db.model'; import { SenderSchema, ShareByEMail, ShareByEMailSchema, User } from '../models/db.model';
@ -52,6 +53,65 @@ export class MailService {
}, },
}); });
} }
async sendVerificationEmail(
email: string,
redirectConfig: { protocol: string, hostname: string, port?: number }
): Promise<void | ErrorResponse> {
try {
// Firebase Auth-Instanz holen
const auth = getAuth();
// Baue den Redirect-URL aus den übergebenen Parametern
let continueUrl = `${redirectConfig.protocol}://${redirectConfig.hostname}`;
if (redirectConfig.port) {
continueUrl += `:${redirectConfig.port}`;
}
continueUrl += '/auth/verify-email-success'; // Beispiel für einen Weiterleitungspfad
// Custom Verification Link generieren
const firebaseActionLink = await auth.generateEmailVerificationLink(email, {
url: continueUrl,
handleCodeInApp: false,
});
// Extrahiere den oobCode aus dem Firebase Link
const actionLinkUrl = new URL(firebaseActionLink);
const oobCode = actionLinkUrl.searchParams.get('oobCode');
if (!oobCode) {
throw new BadRequestException('Failed to generate verification code');
}
// Erstelle die benutzerdefinierte URL mit dem oobCode
let customActionLink = `${redirectConfig.protocol}://${redirectConfig.hostname}`;
if (redirectConfig.port) {
customActionLink += `:${redirectConfig.port}`;
}
// Ersetze die Platzhalter mit den tatsächlichen Werten
customActionLink += `/email-authorized?email=${encodeURIComponent(email)}&mode=verifyEmail&oobCode=${oobCode}`;
// Zufallszahl für die E-Mail generieren
const randomNumber = Math.floor(Math.random() * 10000);
// E-Mail senden
await this.mailerService.sendMail({
to: email,
from: '"Bizmatch Team" <info@bizmatch.net>',
subject: 'Verify your email address',
template: join(__dirname, '../..', 'mail/templates/email-verification.hbs'),
context: {
actionLink: customActionLink,
randomNumber: randomNumber
},
});
return;
} catch (error) {
console.error('Error sending verification email:', error);
throw new BadRequestException('Failed to send verification email');
}
}
async sendRequest(mailInfo: MailInfo): Promise<void | ErrorResponse> { async sendRequest(mailInfo: MailInfo): Promise<void | ErrorResponse> {
try { try {
SenderSchema.parse(mailInfo.sender); SenderSchema.parse(mailInfo.sender);

View File

@ -0,0 +1,249 @@
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml"
xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
<meta charset="utf-8"> <!-- utf-8 works for most cases -->
<meta name="viewport" content="width=device-width"> <!-- Forcing initial-scale shouldn't be necessary -->
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- Use the latest (edge) version of IE rendering engine -->
<meta name="x-apple-disable-message-reformatting"> <!-- Disable auto-scale in iOS 10 Mail entirely -->
<title>Email address verification</title> <!-- The title tag shows in email notifications, like Android 4.4. -->
<link href="https://fonts.googleapis.com/css?family=Lato:300,400,700" rel="stylesheet">
<!-- CSS Reset : BEGIN -->
<style>
/* What it does: Remove spaces around the email design added by some email clients. */
/* Beware: It can remove the padding / margin and add a background color to the compose a reply window. */
html,
body {
margin: 0 auto !important;
padding: 0 !important;
height: 100% !important;
width: 100% !important;
background: #f1f1f1;
overflow: hidden;
}
/* What it does: Stops email clients resizing small text. */
* {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
box-sizing: border-box;
}
/* What it does: Centers email on Android 4.4 */
div[style*="margin: 16px 0"] {
margin: 0 !important;
}
/* What it does: Stops Outlook from adding extra spacing to tables. */
table,
td {
mso-table-lspace: 0pt !important;
mso-table-rspace: 0pt !important;
}
/* What it does: Fixes webkit padding issue. */
table {
border-spacing: 0 !important;
border-collapse: collapse !important;
table-layout: fixed !important;
margin: 0 auto !important;
}
/* What it does: Uses a better rendering method when resizing images in IE. */
img {
-ms-interpolation-mode: bicubic;
}
/* What it does: Prevents Windows 10 Mail from underlining links despite inline CSS. Styles for underlined links should be inline. */
a {
text-decoration: none;
}
/* What it does: A work-around for email clients meddling in triggered links. */
*[x-apple-data-detectors],
/* iOS */
.unstyle-auto-detected-links *,
.aBn {
border-bottom: 0 !important;
cursor: default !important;
color: inherit !important;
text-decoration: none !important;
font-size: inherit !important;
font-family: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
}
/* What it does: Prevents Gmail from displaying a download button on large, non-linked images. */
.a6S {
display: none !important;
opacity: 0.01 !important;
}
/* What it does: Prevents Gmail from changing the text color in conversation threads. */
.im {
color: inherit !important;
}
/* If the above doesn't work, add a .g-img class to any image in question. */
img.g-img+div {
display: none !important;
}
/* What it does: Removes right gutter in Gmail iOS app: https://github.com/TedGoas/Cerberus/issues/89 */
/* Create one of these media queries for each additional viewport size you'd like to fix */
/* iPhone 4, 4S, 5, 5S, 5C, and 5SE */
@media only screen and (min-device-width: 320px) and (max-device-width: 374px) {
u~div .email-container {
min-width: 320px !important;
}
}
/* iPhone 6, 6S, 7, 8, and X */
@media only screen and (min-device-width: 375px) and (max-device-width: 413px) {
u~div .email-container {
min-width: 375px !important;
}
}
/* iPhone 6+, 7+, and 8+ */
@media only screen and (min-device-width: 414px) {
u~div .email-container {
min-width: 414px !important;
}
}
</style>
<!-- CSS Reset : END -->
<!-- Progressive Enhancements : BEGIN -->
<style>
.primary {
background: #30e3ca;
}
.bg_white {
background: #ffffff;
}
.bg_light {
background: #fafafa;
}
.bg_black {
background: #000000;
}
.bg_dark {
background: rgba(0, 0, 0, .8);
}
.email-section {
padding: 2.5em;
}
body {
font-family: 'Lato', sans-serif;
font-weight: 400;
font-size: 15px;
line-height: 1.8;
color: rgba(0, 0, 0, .4);
}
/*HERO*/
.hero {
position: relative;
z-index: 0;
}
.hero .text {
color: rgba(0, 0, 0, .3);
}
.hero .text h2 {
color: #000;
font-size: 40px;
margin-bottom: 0;
font-weight: 400;
line-height: 1.4;
}
.hero .text h3 {
font-size: 24px;
font-weight: 300;
}
.hero .text h2 span {
font-weight: 600;
color: #30e3ca;
}
.email-body {
display: block;
color: black;
line-height: 32px;
font-weight: 300;
font-family: -apple-system, system-ui, BlinkMacSystemFont, sans-serif;
font-size: 22px;
}
@media (max-width:400px) {
.hero img {
width: 200px !important;
}
}
</style>
</head>
<body width="100%"
style="margin: 0; padding: 0 !important; mso-line-height-rule: exactly; background-color: #f1f1f1; display: flex; align-items: center; justify-content: center;">
<div style="width: 100%; background-color: #f1f1f1;">
<div
style="display: none; font-size: 1px;max-height: 0px; max-width: 0px; opacity: 0; overflow: hidden; mso-hide: all; font-family: sans-serif;">
Hello, click on the button below to verify your email address
</div>
<div style="max-width: 600px; margin: 0 auto;" class="email-container">
<!-- BEGIN BODY -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"
style="margin: auto;">
<tr>
<td valign="middle" class="hero bg_white" style="padding: 3em 0 2em 0;">
<img src="https://github.com/ColorlibHQ/email-templates/blob/master/10/images/email.png?raw=true"
alt="" class="g-img" style="width: 200px; height: auto; margin: auto; display: block;">
</td>
</tr>
<!-- end tr -->
<tr>
<td valign="middle" class="hero bg_white" style="padding: 2em 0 4em 0;">
<table>
<tr>
<td>
<div class="text" style="padding: 0 2.5em; text-align: center;">
<h2 style="margin-bottom: 20px; font-size: 32px;">Verify your email address</h2>
<p class="email-body">
Thanks for signup with us. Click on the button below to verify your email
address.
</p>
<a href="{{actionLink}}" target="_blank"
style="padding:15px 40px; background-color: #5D91E8; color: white;">Verify
your email</a>
<p class="email-body">
If this email wasn't intended for you feel free to delete it.<br />
</p>
</div>
</td>
</tr>
</table>
</td>
</tr>
<!-- end tr -->
<span style="color: #f1f1f1; display: none;">{{randomNumber}}</span>
</tr>
</table>
</div>
</div>
</body>
</html>

View File

@ -2,9 +2,10 @@
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Injectable, inject } from '@angular/core'; import { Injectable, inject } from '@angular/core';
import { FirebaseApp } from '@angular/fire/app'; import { FirebaseApp } from '@angular/fire/app';
import { GoogleAuthProvider, UserCredential, createUserWithEmailAndPassword, getAuth, sendEmailVerification, signInWithEmailAndPassword, signInWithPopup } from 'firebase/auth'; import { GoogleAuthProvider, UserCredential, createUserWithEmailAndPassword, getAuth, signInWithEmailAndPassword, signInWithPopup } from 'firebase/auth';
import { firstValueFrom } from 'rxjs'; import { firstValueFrom } from 'rxjs';
import { environment } from '../../environments/environment'; import { environment } from '../../environments/environment';
import { MailService } from './mail.service';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
@ -13,6 +14,7 @@ export class AuthService {
private app = inject(FirebaseApp); private app = inject(FirebaseApp);
private auth = getAuth(this.app); private auth = getAuth(this.app);
private http = inject(HttpClient); private http = inject(HttpClient);
private mailService = inject(MailService);
// Registrierung mit Email und Passwort // Registrierung mit Email und Passwort
async registerWithEmail(email: string, password: string): Promise<UserCredential> { async registerWithEmail(email: string, password: string): Promise<UserCredential> {
@ -41,7 +43,17 @@ async registerWithEmail(email: string, password: string): Promise<UserCredential
// E-Mail-Verifizierung mit den angepassten ActionCode-Einstellungen senden // E-Mail-Verifizierung mit den angepassten ActionCode-Einstellungen senden
if (userCredential.user) { if (userCredential.user) {
await sendEmailVerification(userCredential.user, actionCodeSettings); //await sendEmailVerification(userCredential.user, actionCodeSettings);
this.mailService.sendVerificationEmail(userCredential.user.email).subscribe({
next: () => {
console.log('Verification email sent successfully');
// Erfolgsmeldung anzeigen
},
error: (error) => {
console.error('Error sending verification email', error);
// Fehlermeldung anzeigen
}
});
} }
// Token, RefreshToken und ggf. photoURL speichern // Token, RefreshToken und ggf. photoURL speichern

View File

@ -1,6 +1,6 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { lastValueFrom } from 'rxjs'; import { lastValueFrom, Observable } from 'rxjs';
import { ShareByEMail } from '../../../../bizmatch-server/src/models/db.model'; import { ShareByEMail } from '../../../../bizmatch-server/src/models/db.model';
import { ErrorResponse, MailInfo } from '../../../../bizmatch-server/src/models/main.model'; import { ErrorResponse, MailInfo } from '../../../../bizmatch-server/src/models/main.model';
import { environment } from '../../environments/environment'; import { environment } from '../../environments/environment';
@ -18,4 +18,32 @@ export class MailService {
async mailToFriend(shareByEMail: ShareByEMail): Promise<void | ErrorResponse> { async mailToFriend(shareByEMail: ShareByEMail): Promise<void | ErrorResponse> {
return await lastValueFrom(this.http.post(`${this.apiBaseUrl}/bizmatch/mail/send2Friend`, shareByEMail)); return await lastValueFrom(this.http.post(`${this.apiBaseUrl}/bizmatch/mail/send2Friend`, shareByEMail));
} }
/**
* Sendet eine E-Mail-Verifizierung an die angegebene E-Mail-Adresse
* @param email Die E-Mail-Adresse des Benutzers
* @param redirectConfig Konfiguration für die Weiterleitung nach Verifizierung
* @returns Observable mit der API-Antwort
*/
sendVerificationEmail(
email: string,
redirectConfig?: {
protocol?: string,
hostname?: string,
port?: number
}
): Observable<any> {
// Extrahiere aktuelle URL-Informationen, wenn nicht explizit angegeben
const currentUrl = new URL(window.location.href);
const config = {
protocol: redirectConfig?.protocol || currentUrl.protocol.replace(':', ''),
hostname: redirectConfig?.hostname || currentUrl.hostname,
port: redirectConfig?.port || (currentUrl.port ? parseInt(currentUrl.port) : undefined)
};
return this.http.post(`${this.apiBaseUrl}/bizmatch/mail/verify-email`, {
email,
redirectConfig: config
});
}
} }

View File

@ -14,7 +14,8 @@ export const environment_base = {
ipinfo_token: '7029590fb91214', ipinfo_token: '7029590fb91214',
firebaseConfig: { firebaseConfig: {
apiKey: 'AIzaSyBqVutQqdgUzwD9tKiKJrJq2Q6rD1hNdzw', apiKey: 'AIzaSyBqVutQqdgUzwD9tKiKJrJq2Q6rD1hNdzw',
authDomain: 'bizmatch-net.firebaseapp.com', //authDomain: 'bizmatch-net.firebaseapp.com',
authDomain: 'auth.bizmatch.net',
projectId: 'bizmatch-net', projectId: 'bizmatch-net',
storageBucket: 'bizmatch-net.firebasestorage.app', storageBucket: 'bizmatch-net.firebasestorage.app',
messagingSenderId: '1065122571067', messagingSenderId: '1065122571067',