Claude
Skills
Sign in
Back

auth-patterns

Included with Lifetime
$97 forever

Secure authentication and authorization patterns including JWT, OAuth2, guards, and role-based access control

General

What this skill does


# Authentication & Authorization Patterns

Complete guide to secure authentication and authorization in Angular applications.

## Table of Contents

1. [Authentication Basics](#authentication-basics)
2. [JWT Implementation](#jwt-implementation)
3. [OAuth2 & Social Login](#oauth2--social-login)
4. [Token Management](#token-management)
5. [Route Guards](#route-guards)
6. [Role-Based Access Control](#role-based-access-control)
7. [HTTP Interceptors](#http-interceptors)
8. [Session Management](#session-management)

---

## Authentication Basics

### Authentication Flow

```
1. User enters credentials
2. Frontend sends to backend
3. Backend validates credentials
4. Backend generates JWT token
5. Frontend stores token securely
6. Frontend includes token in API requests
7. Backend validates token on each request
```

### Secure Authentication Service

```typescript
@Injectable({ providedIn: 'root' })
export class AuthService {
  private readonly TOKEN_KEY = 'auth_token';
  private currentUserSubject = new BehaviorSubject<User | null>(null);
  public currentUser$ = this.currentUserSubject.asObservable();
  
  constructor(
    private http: HttpClient,
    private router: Router
  ) {
    // Initialize user from token on app start
    this.loadUserFromToken();
  }
  
  login(email: string, password: string): Observable<AuthResponse> {
    return this.http.post<AuthResponse>('/api/auth/login', {
      email,
      password
    }).pipe(
      tap(response => {
        this.setSession(response);
      }),
      catchError(error => {
        console.error('Login failed:', error);
        return throwError(() => error);
      })
    );
  }
  
  logout(): void {
    // Clear token
    localStorage.removeItem(this.TOKEN_KEY);
    
    // Clear user state
    this.currentUserSubject.next(null);
    
    // Redirect to login
    this.router.navigate(['/login']);
    
    // Optional: Call backend to invalidate token
    this.http.post('/api/auth/logout', {}).subscribe();
  }
  
  isAuthenticated(): boolean {
    const token = this.getToken();
    if (!token) return false;
    
    // Check if token is expired
    return !this.isTokenExpired(token);
  }
  
  getToken(): string | null {
    return localStorage.getItem(this.TOKEN_KEY);
  }
  
  private setSession(authResult: AuthResponse): void {
    // Store token
    localStorage.setItem(this.TOKEN_KEY, authResult.token);
    
    // Update current user
    this.currentUserSubject.next(authResult.user);
  }
  
  private loadUserFromToken(): void {
    const token = this.getToken();
    if (token && !this.isTokenExpired(token)) {
      // Decode token to get user info
      const decoded = this.decodeToken(token);
      this.currentUserSubject.next(decoded.user);
    }
  }
  
  private isTokenExpired(token: string): boolean {
    try {
      const decoded = this.decodeToken(token);
      const expiryTime = decoded.exp * 1000; // Convert to milliseconds
      return Date.now() >= expiryTime;
    } catch {
      return true;
    }
  }
  
  private decodeToken(token: string): any {
    try {
      const payload = token.split('.')[1];
      return JSON.parse(atob(payload));
    } catch {
      return null;
    }
  }
}
```

---

## JWT Implementation

### JWT Structure

```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.  // Header
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.  // Payload
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c  // Signature
```

### JWT Payload

```typescript
interface JwtPayload {
  sub: string;        // Subject (user ID)
  email: string;      // User email
  name: string;       // User name
  role: string;       // User role
  iat: number;        // Issued at
  exp: number;        // Expiration time
}
```

### JWT Service

```typescript
@Injectable({ providedIn: 'root' })
export class JwtService {
  private readonly SECRET_KEY = 'your-secret-key'; // Server-side only!
  
  // Decode JWT (client-side)
  decode(token: string): any {
    try {
      const parts = token.split('.');
      if (parts.length !== 3) {
        throw new Error('Invalid token format');
      }
      
      const payload = parts[1];
      const decoded = atob(payload.replace(/-/g, '+').replace(/_/g, '/'));
      return JSON.parse(decoded);
    } catch (error) {
      console.error('Failed to decode token:', error);
      return null;
    }
  }
  
  // Check if token is expired
  isExpired(token: string): boolean {
    const decoded = this.decode(token);
    if (!decoded || !decoded.exp) return true;
    
    const expiryTime = decoded.exp * 1000;
    return Date.now() >= expiryTime;
  }
  
  // Get expiry date
  getExpiryDate(token: string): Date | null {
    const decoded = this.decode(token);
    if (!decoded || !decoded.exp) return null;
    
    return new Date(decoded.exp * 1000);
  }
  
  // Get time until expiry
  getTimeUntilExpiry(token: string): number {
    const expiryDate = this.getExpiryDate(token);
    if (!expiryDate) return 0;
    
    return expiryDate.getTime() - Date.now();
  }
}
```

### Token Refresh

```typescript
@Injectable({ providedIn: 'root' })
export class TokenRefreshService {
  private refreshInProgress = false;
  private refreshSubject = new Subject<string>();
  
  constructor(
    private http: HttpClient,
    private authService: AuthService
  ) {
    // Auto-refresh before expiry
    this.setupAutoRefresh();
  }
  
  refreshToken(): Observable<string> {
    if (this.refreshInProgress) {
      // Return existing refresh observable
      return this.refreshSubject.pipe(
        filter(token => !!token),
        take(1)
      );
    }
    
    this.refreshInProgress = true;
    
    return this.http.post<{ token: string }>('/api/auth/refresh', {
      refreshToken: this.authService.getRefreshToken()
    }).pipe(
      tap(response => {
        this.authService.setToken(response.token);
        this.refreshInProgress = false;
        this.refreshSubject.next(response.token);
      }),
      catchError(error => {
        this.refreshInProgress = false;
        this.authService.logout();
        return throwError(() => error);
      })
    );
  }
  
  private setupAutoRefresh(): void {
    // Refresh token 5 minutes before expiry
    const REFRESH_BEFORE_EXPIRY = 5 * 60 * 1000; // 5 minutes
    
    interval(60000).pipe( // Check every minute
      filter(() => this.authService.isAuthenticated()),
      switchMap(() => {
        const token = this.authService.getToken();
        if (!token) return of(null);
        
        const timeUntilExpiry = this.getTimeUntilExpiry(token);
        
        if (timeUntilExpiry <= REFRESH_BEFORE_EXPIRY) {
          return this.refreshToken();
        }
        return of(null);
      })
    ).subscribe();
  }
  
  private getTimeUntilExpiry(token: string): number {
    const decoded = this.decodeToken(token);
    if (!decoded?.exp) return 0;
    return (decoded.exp * 1000) - Date.now();
  }
}
```

---

## OAuth2 & Social Login

### OAuth2 Flow

```
1. User clicks "Login with Google"
2. Redirect to OAuth provider (Google)
3. User authorizes app
4. Provider redirects back with authorization code
5. Exchange code for access token
6. Use token to get user info
7. Create session in your app
```

### Social Login Service

```typescript
@Injectable({ providedIn: 'root' })
export class SocialAuthService {
  constructor(
    private http: HttpClient,
    private authService: AuthService
  ) {}
  
  // Google OAuth2
  loginWithGoogle(): void {
    const clientId = environment.googleClientId;
    const redirectUri = `${window.location.origin}/auth/google/callback`;
    const scope = 'openid email profile';
    
    const authUrl = `https://accounts.google.com/o/oauth2/v2/auth?` +
      `client_id=${clientId}&` +
      `redirect_uri=${encodeURIComponent(redirectUri)}&` +
      `response_type=code&` +
      `scope=${encodeURIComponent(scope)}`;
    
    window.location.href = authUrl;
  }
  
  // Handle OAuth callback
  handleOAuthCallback(code: string, p

Related in General