From 097a6cb360102cc378ddb01c4c42ce399036446b Mon Sep 17 00:00:00 2001 From: Andreas Knuth Date: Thu, 13 Mar 2025 13:55:09 +0100 Subject: [PATCH] #136: EMail Auth on different browsers ... --- bizmatch-server/src/auth/auth.controller.ts | 22 ++++++--- .../email-authorized.component.html | 45 +++++++++++++------ .../email-authorized.component.ts | 34 +++++++++++--- bizmatch/src/app/services/auth.service.ts | 29 +++++++++++- 4 files changed, 103 insertions(+), 27 deletions(-) diff --git a/bizmatch-server/src/auth/auth.controller.ts b/bizmatch-server/src/auth/auth.controller.ts index 090bfb2..323f5b4 100644 --- a/bizmatch-server/src/auth/auth.controller.ts +++ b/bizmatch-server/src/auth/auth.controller.ts @@ -20,21 +20,31 @@ export class AuthController { } try { - // Schritt 1: Hole den Benutzer anhand der E-Mail-Adresse + // Step 1: Get the user by email address const userRecord = await this.firebaseAdmin.auth().getUserByEmail(email); if (userRecord.emailVerified) { - return { message: 'Email is already verified' }; + // Even if already verified, we'll still return a valid token + const customToken = await this.firebaseAdmin.auth().createCustomToken(userRecord.uid); + return { + message: 'Email is already verified', + token: customToken, + }; } - // Schritt 2: Aktualisiere den Benutzerstatus - // Hinweis: Wir können den oobCode nicht serverseitig validieren. - // Wir nehmen an, dass der oobCode korrekt ist, da er von Firebase generiert wurde. + // Step 2: Update the user status to set emailVerified to true await this.firebaseAdmin.auth().updateUser(userRecord.uid, { emailVerified: true, }); - return { message: 'Email successfully verified' }; + // Step 3: Generate a custom Firebase token for the user + // This token can be used on the client side to authenticate with Firebase + const customToken = await this.firebaseAdmin.auth().createCustomToken(userRecord.uid); + + return { + message: 'Email successfully verified', + token: customToken, + }; } catch (error) { throw new HttpException(error.message || 'Failed to verify email', HttpStatus.BAD_REQUEST); } diff --git a/bizmatch/src/app/components/email-authorized/email-authorized.component.html b/bizmatch/src/app/components/email-authorized/email-authorized.component.html index 95eefc4..e4298c3 100644 --- a/bizmatch/src/app/components/email-authorized/email-authorized.component.html +++ b/bizmatch/src/app/components/email-authorized/email-authorized.component.html @@ -1,16 +1,35 @@ -
- -

Verifying your email...

-
+
+
+ + +
+
+
+

Verifying your email address...

+
- -

Your email has been verified

- - Follow this link to access your Account Page -
+ + +
+ + + +
+

Your email has been verified

+

You will be redirected to your account page in 5 seconds

+ Go to Account Page Now +
- -

Verification failed

-

{{ errorMessage }}

-
+ + +
+ + + +
+

Verification Failed

+

{{ errorMessage }}

+ Return to Login +
+
diff --git a/bizmatch/src/app/components/email-authorized/email-authorized.component.ts b/bizmatch/src/app/components/email-authorized/email-authorized.component.ts index 04828af..de82ccb 100644 --- a/bizmatch/src/app/components/email-authorized/email-authorized.component.ts +++ b/bizmatch/src/app/components/email-authorized/email-authorized.component.ts @@ -1,7 +1,7 @@ import { CommonModule } from '@angular/common'; import { HttpClient } from '@angular/common/http'; import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, RouterModule } from '@angular/router'; +import { ActivatedRoute, Router, RouterModule } from '@angular/router'; import { environment } from '../../../environments/environment'; import { AuthService } from '../../services/auth.service'; import { UserService } from '../../services/user.service'; @@ -16,7 +16,7 @@ export class EmailAuthorizedComponent implements OnInit { verificationStatus: 'pending' | 'success' | 'error' = 'pending'; errorMessage: string | null = null; - constructor(private route: ActivatedRoute, private http: HttpClient, private authService: AuthService, private userService: UserService) {} + constructor(private route: ActivatedRoute, private router: Router, private http: HttpClient, private authService: AuthService, private userService: UserService) {} ngOnInit(): void { const oobCode = this.route.snapshot.queryParamMap.get('oobCode'); @@ -32,12 +32,32 @@ export class EmailAuthorizedComponent implements OnInit { } private verifyEmail(oobCode: string, email: string): void { - this.http.post(`${environment.apiBaseUrl}/bizmatch/auth/verify-email`, { oobCode, email }).subscribe({ - next: async () => { + this.http.post<{ message: string; token: string }>(`${environment.apiBaseUrl}/bizmatch/auth/verify-email`, { oobCode, email }).subscribe({ + next: async response => { this.verificationStatus = 'success'; - //await this.authService.refreshToken(); - await this.authService.refreshUserClaims(); - const user = await this.userService.getByMail(email); + + try { + // Use the custom token from the server to sign in with Firebase + await this.authService.signInWithCustomToken(response.token); + + // Try to get user info + try { + const user = await this.userService.getByMail(email); + console.log('User retrieved:', user); + } catch (userError) { + console.error('Error getting user:', userError); + // Don't change verification status - it's still a success + } + + // Redirect to dashboard after a short delay + setTimeout(() => { + this.router.navigate(['/account']); + }, 5000); + } catch (authError) { + console.error('Error signing in with custom token:', authError); + // Keep success status for verification, but add warning about login + this.errorMessage = 'Email verified, but there was an issue signing you in. Please try logging in manually.'; + } }, error: err => { this.verificationStatus = 'error'; diff --git a/bizmatch/src/app/services/auth.service.ts b/bizmatch/src/app/services/auth.service.ts index caf6e05..a7d7dc6 100644 --- a/bizmatch/src/app/services/auth.service.ts +++ b/bizmatch/src/app/services/auth.service.ts @@ -2,7 +2,7 @@ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { Injectable, inject } from '@angular/core'; import { FirebaseApp } from '@angular/fire/app'; -import { GoogleAuthProvider, UserCredential, createUserWithEmailAndPassword, getAuth, signInWithEmailAndPassword, signInWithPopup } from 'firebase/auth'; +import { GoogleAuthProvider, UserCredential, createUserWithEmailAndPassword, getAuth, signInWithCustomToken, signInWithEmailAndPassword, signInWithPopup } from 'firebase/auth'; import { BehaviorSubject, Observable, catchError, firstValueFrom, map, of, shareReplay, take, tap } from 'rxjs'; import { environment } from '../../environments/environment'; import { MailService } from './mail.service'; @@ -274,4 +274,31 @@ export class AuthService { return await this.refreshToken(); } } + + // Add this new method to sign in with a custom token + async signInWithCustomToken(token: string): Promise { + try { + // Sign in to Firebase with the custom token + const userCredential = await signInWithCustomToken(this.auth, token); + + // Store the authentication token + if (userCredential.user) { + const idToken = await userCredential.user.getIdToken(); + localStorage.setItem('authToken', idToken); + localStorage.setItem('refreshToken', userCredential.user.refreshToken); + + if (userCredential.user.photoURL) { + localStorage.setItem('photoURL', userCredential.user.photoURL); + } + + // Load user role from the token + this.loadRoleFromToken(); + } + + return; + } catch (error) { + console.error('Error signing in with custom token:', error); + throw error; + } + } }