BarberApp uses Firebase Authentication to securely manage user sessions and provide role-based access to different parts of the application.
Authentication flow
The authentication system is managed by the AuthFacade service, which provides a reactive state management layer using Angular signals.
User submits credentials
Users enter their email and password through the login or registration form.
Firebase Authentication
Credentials are validated against Firebase Authentication service.
User data retrieval
After successful authentication, user data is fetched from Firestore.
Role-based redirect
Users are automatically redirected to their appropriate dashboard based on their role.
Login
The login process is handled through the AuthFacade service:
async login(email: string, password: string): Promise<void> {
this._loading.set(true);
this._error.set(null);
try {
await this.authService.login(email, password);
const user = await this.authService.getCurrentUser();
this._user.set(user);
// Redirect user to appropriate dashboard based on role
if (user) {
this.redirectUserByRole(user);
}
} catch (error: any) {
const errorMessage = this.getLoginErrorMessage(error);
this._error.set(errorMessage || 'Error del servidor. Intenta más tarde.');
this._user.set(null);
} finally {
this._loading.set(false);
this.consoleInform();
}
}
The LoginFormComponent provides a reactive form with email and password validation:
login-form.component.ts:56-62
private createForm(): FormGroup {
return this.fb.group({
email: ['', [Validators.required, Validators.email]],
password: ['', Validators.required],
showPassword: new FormControl(false),
});
}
Error handling
Firebase authentication errors are mapped to user-friendly messages:
private getLoginErrorMessage(error: any): string {
const code = error?.code;
switch (code) {
case 'auth/user-not-found':
case 'auth/wrong-password':
case 'auth/invalid-credential':
return 'Correo o contraseña incorrectos.';
case 'auth/too-many-requests':
return 'Demasiados intentos fallidos. Intenta más tarde.';
case 'auth/invalid-email':
return 'Correo electrónico inválido.';
default:
return 'Error al iniciar sesión. Intenta nuevamente.';
}
}
Registration
New users can register as either clients or specialists. The registration process involves multiple steps:
Choose user type
Select between Client or Specialist role
Fill in basic information
Provide firstName, lastName, DNI, sex, birthDate, email, and password
Add role-specific data
- Clients: Height and weight (optional)
- Specialists: Specialties and availability schedule (required)
Upload profile picture
Optional profile picture upload via Cloudinary
Submit registration
Account is created in Firebase Auth and user data is stored in Firestore
Registration implementation
async register(email: string, password: string): Promise<string | void> {
this._loading.set(true);
this._error.set(null);
try {
const uid = await this.authService.register(email, password);
return uid;
} catch (error: any) {
const errorMessage = this.getRegisterErrorMessage(error);
this._error.set(errorMessage || 'Error del servidor. Intenta más tarde.');
} finally {
this._loading.set(false);
this.consoleInform();
}
}
The registration form validates DNI uniqueness before creating the Firebase account to prevent duplicate users.
Role-based redirect
After successful authentication, users are automatically redirected based on their role:
redirectUserByRole(user: UserBase): void {
switch (user.role) {
case UserRoles.CLIENT:
this.router.navigate(['/dashboard/client']);
break;
case UserRoles.SPECIALIST:
this.router.navigate(['/dashboard/specialist']);
break;
case UserRoles.ADMIN:
this.router.navigate(['/dashboard/specialist']);
break;
default:
// Default fallback
console.warn('Unknown user role:', user.role);
this.router.navigate(['/dashboard/client']);
break;
}
}
Authentication state
The AuthFacade exposes reactive signals for authentication state:
private _user = signal<UserBase | null>(null);
private _loading = signal<boolean>(false);
private _checkingAuth = signal<boolean>(false);
private _error = signal<string | null>(null);
// Public signals to interact with the auth service
readonly user = this._user.asReadonly();
readonly isAuthenticated = computed(() => !!this._user());
readonly isLoading = this._loading.asReadonly();
readonly isCheckingAuth = this._checkingAuth.asReadonly();
readonly error = this._error.asReadonly();
Checking authentication status
async checkAuthStatus(): Promise<void> {
this._checkingAuth.set(true);
console.log('Check Auth Status Started');
try {
const isAuthenticated = await this.authService.isAuthenticated();
console.log(isAuthenticated)
if (isAuthenticated) {
const currentUser = await this.authService.getCurrentUser();
this._user.set(currentUser);
} else {
this._user.set(null);
}
} catch (error: any) {
this._user.set(null);
this._error.set(error.message || 'Error checking authentication status');
} finally {
this._checkingAuth.set(false);
this.consoleInform();
}
}
Logout
Users can log out at any time, which clears the authentication state:
async logout(): Promise<void> {
this._loading.set(true);
this._error.set(null);
try {
await this.authService.logout();
this._user.set(null);
} catch (error: any) {
this._error.set(error.message || 'Logout failed');
} finally {
this._loading.set(false);
this.consoleInform();
}
}
Route protection
BarberApp uses Angular route guards to protect authenticated routes. See the User Roles documentation for details on role-based access control.
Always validate user authentication status on both the client and server side for security.