Add auth-client library for Elixir auth service integration

- Complete Angular client library for authentication and authorization
- JWT token management with automatic refresh and storage
- OAuth integration with social providers (Google, GitHub, etc.)
- Two-factor authentication support (TOTP and backup codes)
- Route guards for authentication and scope-based authorization
- HTTP interceptor for automatic token injection and refresh
- Comprehensive TypeScript interfaces for all API models
- User management features (profile updates, password changes)
- Cross-tab synchronization and token validation
- Complete usage guide with practical examples

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Giuliano Silvestro
2025-09-11 14:56:59 +10:00
parent 246c62fd49
commit 9b40aa3afb
22 changed files with 3450 additions and 2 deletions

View File

@@ -0,0 +1,67 @@
import { Injectable } from '@angular/core';
import { CanActivate, CanActivateChild, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { Observable, of } from 'rxjs';
import { map, take, tap } from 'rxjs/operators';
import { AuthService } from '../services/auth.service';
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate, CanActivateChild {
constructor(
private authService: AuthService,
private router: Router
) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean> {
return this.checkAuth(route, state.url);
}
canActivateChild(
childRoute: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean> {
return this.canActivate(childRoute, state);
}
private checkAuth(route: ActivatedRouteSnapshot, redirectUrl: string): Observable<boolean> {
return this.authService.isAuthenticated$.pipe(
take(1),
map(isAuthenticated => {
if (isAuthenticated) {
// Check for required scopes if specified
const requiredScopes = route.data?.['requiredScopes'] as string[];
if (requiredScopes && requiredScopes.length > 0) {
const hasScopes = route.data?.['requireAllScopes']
? this.authService.hasAllScopes(requiredScopes)
: this.authService.hasAnyScope(requiredScopes);
if (!hasScopes) {
this.handleUnauthorized(route.data?.['unauthorizedRedirect']);
return false;
}
}
return true;
} else {
this.handleUnauthenticated(redirectUrl, route.data?.['loginRedirect']);
return false;
}
})
);
}
private handleUnauthenticated(redirectUrl: string, customLoginPath?: string): void {
const loginPath = customLoginPath || '/login';
this.router.navigate([loginPath], {
queryParams: { returnUrl: redirectUrl }
});
}
private handleUnauthorized(customUnauthorizedPath?: string): void {
const unauthorizedPath = customUnauthorizedPath || '/unauthorized';
this.router.navigate([unauthorizedPath]);
}
}