feat: multiple improvements to library functionality #50

Merged
naomi merged 12 commits from feat/tickets into main 2026-02-19 16:52:43 -08:00
Showing only changes of commit ebdb85c3e9 - Show all commits
@@ -23,6 +23,7 @@ export class AuthService {
private currentUser = signal<User | null>(null); private currentUser = signal<User | null>(null);
public readonly user = this.currentUser.asReadonly(); public readonly user = this.currentUser.asReadonly();
private refreshing = false; private refreshing = false;
private refreshInterval?: ReturnType<typeof setInterval>;
login(): void { login(): void {
// Redirect to API login endpoint // Redirect to API login endpoint
@@ -33,6 +34,7 @@ export class AuthService {
return this.api.get<AuthResponse>('/auth/me').pipe( return this.api.get<AuthResponse>('/auth/me').pipe(
tap(response => { tap(response => {
this.currentUser.set(response.user); this.currentUser.set(response.user);
this.startRefreshTimer();
}), }),
catchError(error => { catchError(error => {
if (error.status === 401) { if (error.status === 401) {
@@ -40,9 +42,11 @@ export class AuthService {
switchMap(() => this.api.get<AuthResponse>('/auth/me')), switchMap(() => this.api.get<AuthResponse>('/auth/me')),
tap(response => { tap(response => {
this.currentUser.set(response.user); this.currentUser.set(response.user);
this.startRefreshTimer();
}), }),
catchError(() => { catchError(() => {
this.currentUser.set(null); this.currentUser.set(null);
this.stopRefreshTimer();
return throwError(() => error); return throwError(() => error);
}) })
); );
@@ -66,20 +70,45 @@ export class AuthService {
tap(response => { tap(response => {
this.currentUser.set(response.user); this.currentUser.set(response.user);
this.refreshing = false; this.refreshing = false;
this.startRefreshTimer();
}), }),
catchError(error => { catchError(error => {
this.refreshing = false; this.refreshing = false;
this.currentUser.set(null); this.currentUser.set(null);
this.stopRefreshTimer();
return throwError(() => error); return throwError(() => error);
}) })
); );
} }
private startRefreshTimer(): void {
this.stopRefreshTimer();
// Refresh token every 13 minutes (before 15-minute expiry)
const refreshIntervalMs = 13 * 60 * 1000;
this.refreshInterval = setInterval(() => {
this.refreshToken().subscribe({
error: (err) => {
console.error('Background token refresh failed:', err);
this.stopRefreshTimer();
}
});
}, refreshIntervalMs);
}
private stopRefreshTimer(): void {
if (this.refreshInterval) {
clearInterval(this.refreshInterval);
this.refreshInterval = undefined;
}
}
logout(): Observable<{ message: string }> { logout(): Observable<{ message: string }> {
return this.api.post<{ message: string }>('/auth/logout', {}).pipe( return this.api.post<{ message: string }>('/auth/logout', {}).pipe(
tap(() => { tap(() => {
this.currentUser.set(null); this.currentUser.set(null);
this.api.clearCsrfToken(); this.api.clearCsrfToken();
this.stopRefreshTimer();
this.router.navigate(['/']); this.router.navigate(['/']);
}) })
); );
@@ -87,6 +116,7 @@ export class AuthService {
clearUser(): void { clearUser(): void {
this.currentUser.set(null); this.currentUser.set(null);
this.stopRefreshTimer();
} }
isAuthenticated(): boolean { isAuthenticated(): boolean {