feat: implement Google OAuth2 authentication flow
Some checks failed
CI / Get Changed Files (pull_request) Successful in 8s
CI / eslint (pull_request) Successful in 33s
CI / prettier (pull_request) Failing after 36s
CI / oxlint (pull_request) Successful in 42s
CI / Checkstyle Main (pull_request) Failing after 1m8s
CI / Docker frontend validation (pull_request) Successful in 1m46s
CI / test-build (pull_request) Successful in 1m46s
CI / Docker backend validation (pull_request) Successful in 2m36s

This commit is contained in:
Constantin Simonis 2025-05-21 11:34:00 +02:00
commit 07b594fa36
No known key found for this signature in database
GPG key ID: 3878FF77C24AF4D2
11 changed files with 342 additions and 18 deletions

View file

@ -39,6 +39,15 @@ export const routes: Routes = [
import('./feature/auth/oauth2/oauth2-callback.component').then(
(m) => m.OAuth2CallbackComponent
),
data: { provider: 'github' }
},
{
path: 'oauth2/callback/google',
loadComponent: () =>
import('./feature/auth/oauth2/oauth2-callback.component').then(
(m) => m.OAuth2CallbackComponent
),
data: { provider: 'google' }
},
{
path: 'game/blackjack',

View file

@ -89,7 +89,7 @@
<div class="flex-grow h-px bg-deep-blue-light/30"></div>
</div>
<div class="mb-4">
<div class="space-y-3 mb-4">
<button
(click)="loginWithGithub()"
class="w-full py-2.5 px-4 rounded flex items-center justify-center bg-gray-800 hover:bg-gray-700 text-white transition-colors"
@ -106,6 +106,35 @@
</svg>
Mit GitHub anmelden
</button>
<button
(click)="loginWithGoogle()"
class="w-full py-2.5 px-4 rounded flex items-center justify-center bg-white hover:bg-gray-100 text-gray-800 transition-colors"
>
<svg
class="h-5 w-5 mr-2"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<path
fill="#EA4335"
d="M5.266 9.765A7.077 7.077 0 0 1 12 4.909c1.69 0 3.218.6 4.418 1.582L19.91 3C17.782 1.145 15.055 0 12 0 7.27 0 3.198 2.698 1.24 6.65l4.026 3.115Z"
/>
<path
fill="#34A853"
d="M16.04 18.013c-1.09.703-2.474 1.078-4.04 1.078a7.077 7.077 0 0 1-6.723-4.823l-4.04 3.067A11.965 11.965 0 0 0 12 24c2.933 0 5.735-1.043 7.834-3l-3.793-2.987Z"
/>
<path
fill="#4A90E2"
d="M19.834 21c2.195-2.048 3.62-5.096 3.62-9 0-.71-.109-1.473-.272-2.182H12v4.637h6.436c-.317 1.559-1.17 2.766-2.395 3.558L19.834 21Z"
/>
<path
fill="#FBBC05"
d="M5.277 14.268A7.12 7.12 0 0 1 4.909 12c0-.782.125-1.533.357-2.235L1.24 6.65A11.934 11.934 0 0 0 0 12c0 1.92.445 3.73 1.237 5.335l4.04-3.067Z"
/>
</svg>
Mit Google anmelden
</button>
</div>
<div class="text-center">

View file

@ -71,6 +71,11 @@ export class LoginComponent {
window.location.href = `${environment.apiUrl}/oauth2/github/authorize`;
}
loginWithGoogle(): void {
this.isLoading.set(true);
window.location.href = `${environment.apiUrl}/oauth2/google/authorize`;
}
switchToForgotPassword() {
this.forgotPassword.emit();
}

View file

@ -32,25 +32,46 @@ export class OAuth2CallbackComponent implements OnInit {
// Check for code in URL params
this.route.queryParams.subscribe((params) => {
const code = params['code'];
const provider = this.route.snapshot.data['provider'] || 'github';
if (code) {
// Exchange GitHub code for a JWT token
this.authService.githubAuth(code).subscribe({
next: () => {
// Redirect to home after successful authentication
this.router.navigate(['/home']);
},
error: (err) => {
console.error('GitHub authentication error:', err);
this.error = err.error?.message || 'Authentication failed. Please try again.';
console.log('Error details:', err);
if (provider === 'google') {
// Exchange Google code for a JWT token
this.authService.googleAuth(code).subscribe({
next: () => {
// Redirect to home after successful authentication
this.router.navigate(['/home']);
},
error: (err) => {
console.error('Google authentication error:', err);
this.error = err.error?.message || 'Authentication failed. Please try again.';
console.log('Error details:', err);
// Redirect back to landing page after showing error
setTimeout(() => {
this.router.navigate(['/']);
}, 3000);
},
});
// Redirect back to landing page after showing error
setTimeout(() => {
this.router.navigate(['/']);
}, 3000);
},
});
} else {
// Exchange GitHub code for a JWT token
this.authService.githubAuth(code).subscribe({
next: () => {
// Redirect to home after successful authentication
this.router.navigate(['/home']);
},
error: (err) => {
console.error('GitHub authentication error:', err);
this.error = err.error?.message || 'Authentication failed. Please try again.';
console.log('Error details:', err);
// Redirect back to landing page after showing error
setTimeout(() => {
this.router.navigate(['/']);
}, 3000);
},
});
}
} else {
this.error = 'Authentication failed. No authorization code received.';

View file

@ -79,6 +79,15 @@ export class AuthService {
);
}
googleAuth(code: string): Observable<AuthResponse> {
return this.http.post<AuthResponse>(`${this.oauthUrl}/google/callback`, { code }).pipe(
tap((response) => {
this.setToken(response.token);
this.loadCurrentUser();
})
);
}
logout(): void {
localStorage.removeItem(TOKEN_KEY);
localStorage.removeItem(USER_KEY);