added s authState which helps conditonaly render components based on this state stored user details in localStoarge so that server side end point does not get hit in every page load. add a guard which protects routes and redirects to login if user is not logged in. create a logout route
101 lines
3.0 KiB
TypeScript
101 lines
3.0 KiB
TypeScript
import { computed, inject, Injectable, Signal, signal, WritableSignal } from "@angular/core";
|
|
import { RegisterUserRequest, User } from "../../../core/models/user.model";
|
|
import { HttpClient, HttpErrorResponse } from "@angular/common/http";
|
|
import { API_URL, BACKEND_URL } from "../../../core/tokens/api-url-tokens";
|
|
import { switchMap, tap } from "rxjs";
|
|
import { LocalStorageService } from "../../../core/services/local-storage.service";
|
|
|
|
export enum AuthState {
|
|
Loading = "loading",
|
|
Authenticated = "authenticated",
|
|
Unauthenticated = "unauthenticated",
|
|
}
|
|
|
|
/**
|
|
* UserService - manages user related operations
|
|
*
|
|
* ## Auth States
|
|
* - loading: user is being fetched from the server
|
|
* - authenticated: user is authenticated
|
|
* - unauthenticated: user is not authenticated
|
|
*/
|
|
|
|
@Injectable({
|
|
providedIn: "root",
|
|
})
|
|
export class AuthService {
|
|
// User states
|
|
readonly authState: WritableSignal<AuthState>;
|
|
readonly user: WritableSignal<User | null>;
|
|
|
|
// Computed state for easy checking
|
|
readonly isAuthenticated: Signal<boolean>;
|
|
// Dependent services
|
|
private localStorage = inject(LocalStorageService);
|
|
private http: HttpClient = inject(HttpClient);
|
|
// Constants
|
|
private readonly userKey = "ekart_user";
|
|
private apiUrl = inject(API_URL);
|
|
private backendURL = inject(BACKEND_URL);
|
|
|
|
constructor() {
|
|
const cachedUser = this.localStorage.getItem<User>(this.userKey);
|
|
this.authState = signal<AuthState>(
|
|
cachedUser ? AuthState.Authenticated : AuthState.Unauthenticated,
|
|
);
|
|
this.user = signal<User | null>(cachedUser);
|
|
this.isAuthenticated = computed(() => !!this.user());
|
|
}
|
|
|
|
register(userRequest: RegisterUserRequest) {
|
|
return this.http.post(`${this.apiUrl}/register`, userRequest);
|
|
}
|
|
|
|
/**
|
|
* Laravel API expects the csrf cookie to be set before making a request.
|
|
* First set the cookie then attempt to login.
|
|
* If the login is successful, set the user in the state.
|
|
*/
|
|
login(credentials: { email: string; password: string }) {
|
|
return this.getCsrfCookie().pipe(
|
|
switchMap(() =>
|
|
this.http.post(`${this.backendURL}/login`, credentials, { observe: "response" }),
|
|
),
|
|
switchMap(() => this.getCurrentUser()),
|
|
);
|
|
}
|
|
|
|
getCurrentUser() {
|
|
return this.http.get<User>(`${this.apiUrl}/user`).pipe(
|
|
tap({
|
|
next: (user) => this.setAuth(user),
|
|
error: (error: HttpErrorResponse) => this.purgeAuth(),
|
|
}),
|
|
);
|
|
}
|
|
|
|
logout() {
|
|
return this.http.post(`${this.backendURL}/logout`, {}).pipe(
|
|
tap({
|
|
next: () => this.purgeAuth(),
|
|
}),
|
|
);
|
|
}
|
|
|
|
private getCsrfCookie() {
|
|
return this.http.get(`${this.backendURL}/sanctum/csrf-cookie`);
|
|
}
|
|
|
|
private setAuth(user: User) {
|
|
this.localStorage.setItem<User>(this.userKey, user);
|
|
this.user.set(user);
|
|
this.authState.set(AuthState.Authenticated);
|
|
}
|
|
|
|
private purgeAuth() {
|
|
this.localStorage.removeItem(this.userKey);
|
|
this.user.set(null);
|
|
this.authState.set(AuthState.Unauthenticated);
|
|
}
|
|
}
|