We use cookies to enhance your experience on the site
CodeWorlds

Authentication and Authorization

JWT Authentication

1// auth.service.ts
2@Injectable({ providedIn: 'root' })
3export class AuthService {
4  private http = inject(HttpClient);
5  private router = inject(Router);
6
7  private currentUser = signal<User | null>(null);
8  readonly isLoggedIn = computed(() => !!this.currentUser());
9
10  login(credentials: LoginCredentials): Observable<AuthResponse> {
11    return this.http.post<AuthResponse>('/api/auth/login', credentials).pipe(
12      tap(response => {
13        localStorage.setItem('access_token', response.accessToken);
14        localStorage.setItem('refresh_token', response.refreshToken);
15        this.currentUser.set(response.user);
16      })
17    );
18  }
19
20  logout(): void {
21    localStorage.removeItem('access_token');
22    localStorage.removeItem('refresh_token');
23    this.currentUser.set(null);
24    this.router.navigate(['/login']);
25  }
26
27  getToken(): string | null {
28    return localStorage.getItem('access_token');
29  }
30
31  refreshToken(): Observable<AuthResponse> {
32    const refreshToken = localStorage.getItem('refresh_token');
33    return this.http.post<AuthResponse>('/api/auth/refresh', { refreshToken }).pipe(
34      tap(response => {
35        localStorage.setItem('access_token', response.accessToken);
36      })
37    );
38  }
39}

Auth Interceptor with Refresh Token

1// auth.interceptor.ts
2export const authInterceptor: HttpInterceptorFn = (req, next) => {
3  const authService = inject(AuthService);
4  const token = authService.getToken();
5
6  // Skip for public endpoints
7  if (req.context.get(SKIP_AUTH)) {
8    return next(req);
9  }
10
11  // Add token
12  if (token) {
13    req = req.clone({
14      headers: req.headers.set('Authorization', \`Bearer \${token}\`)
15    });
16  }
17
18  return next(req).pipe(
19    catchError((error: HttpErrorResponse) => {
20      if (error.status === 401) {
21        // Token expired - try to refresh
22        return authService.refreshToken().pipe(
23          switchMap(response => {
24            const newReq = req.clone({
25              headers: req.headers.set('Authorization', \`Bearer \${response.accessToken}\`)
26            });
27            return next(newReq);
28          }),
29          catchError(() => {
30            authService.logout();
31            return throwError(() => error);
32          })
33        );
34      }
35      return throwError(() => error);
36    })
37  );
38};

Route Guards

1// auth.guard.ts
2export const authGuard: CanActivateFn = (route, state) => {
3  const authService = inject(AuthService);
4  const router = inject(Router);
5
6  if (authService.isLoggedIn()) {
7    return true;
8  }
9
10  // Save URL for redirect after login
11  router.navigate(['/login'], {
12    queryParams: { returnUrl: state.url }
13  });
14  return false;
15};
16
17// role.guard.ts
18export const roleGuard: CanActivateFn = (route, state) => {
19  const authService = inject(AuthService);
20  const requiredRoles = route.data['roles'] as string[];
21
22  const user = authService.currentUser();
23  if (!user) return false;
24
25  return requiredRoles.some(role => user.roles.includes(role));
26};
27
28// Usage
29const routes: Routes = [
30  {
31    path: 'admin',
32    canActivate: [authGuard, roleGuard],
33    data: { roles: ['admin', 'moderator'] },
34    loadComponent: () => import('./admin.component')
35  }
36];
Go to CodeWorlds