generated from nhcarrigan/template
101 lines
2.6 KiB
TypeScript
101 lines
2.6 KiB
TypeScript
/**
|
|
* @copyright 2026 NHCarrigan
|
|
* @license Naomi's Public License
|
|
* @author Naomi Carrigan
|
|
*/
|
|
|
|
import { Injectable, signal } from '@angular/core';
|
|
import { Router } from '@angular/router';
|
|
import { Observable, tap, catchError, switchMap, throwError, of } from 'rxjs';
|
|
import { ApiService } from './api.service';
|
|
import { AuthResponse, User } from '@library/shared-types';
|
|
import { environment } from '../../environments/environment';
|
|
import { HttpClient } from '@angular/common/http';
|
|
|
|
@Injectable({
|
|
providedIn: 'root'
|
|
})
|
|
export class AuthService {
|
|
private currentUser = signal<User | null>(null);
|
|
public readonly user = this.currentUser.asReadonly();
|
|
private refreshing = false;
|
|
|
|
constructor(
|
|
private api: ApiService,
|
|
private router: Router,
|
|
private http: HttpClient
|
|
) {}
|
|
|
|
login(): void {
|
|
// Redirect to API login endpoint
|
|
window.location.href = `${environment.apiUrl}/auth/login`;
|
|
}
|
|
|
|
getCurrentUser(): Observable<AuthResponse> {
|
|
return this.api.get<AuthResponse>('/auth/me').pipe(
|
|
tap(response => {
|
|
this.currentUser.set(response.user);
|
|
}),
|
|
catchError(error => {
|
|
if (error.status === 401) {
|
|
return this.refreshToken().pipe(
|
|
switchMap(() => this.api.get<AuthResponse>('/auth/me')),
|
|
tap(response => {
|
|
this.currentUser.set(response.user);
|
|
}),
|
|
catchError(() => {
|
|
this.currentUser.set(null);
|
|
return throwError(() => error);
|
|
})
|
|
);
|
|
}
|
|
return throwError(() => error);
|
|
})
|
|
);
|
|
}
|
|
|
|
refreshToken(): Observable<AuthResponse> {
|
|
if (this.refreshing) {
|
|
return of({ user: this.currentUser()!, accessToken: '' });
|
|
}
|
|
|
|
this.refreshing = true;
|
|
return this.http.post<AuthResponse>(
|
|
`${environment.apiUrl}/auth/refresh`,
|
|
{},
|
|
{ withCredentials: true }
|
|
).pipe(
|
|
tap(response => {
|
|
this.currentUser.set(response.user);
|
|
this.refreshing = false;
|
|
}),
|
|
catchError(error => {
|
|
this.refreshing = false;
|
|
this.currentUser.set(null);
|
|
return throwError(() => error);
|
|
})
|
|
);
|
|
}
|
|
|
|
logout(): Observable<{ message: string }> {
|
|
return this.api.post<{ message: string }>('/auth/logout', {}).pipe(
|
|
tap(() => {
|
|
this.currentUser.set(null);
|
|
this.api.clearCsrfToken();
|
|
this.router.navigate(['/']);
|
|
})
|
|
);
|
|
}
|
|
|
|
clearUser(): void {
|
|
this.currentUser.set(null);
|
|
}
|
|
|
|
isAuthenticated(): boolean {
|
|
return this.user() !== null;
|
|
}
|
|
|
|
isAdmin(): boolean {
|
|
return this.user()?.isAdmin === true;
|
|
}
|
|
} |