Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/lucavallini/aibar-frontend/llms.txt

Use this file to discover all available pages before exploring further.

Aibar SRL App authenticates users against a FastAPI backend using the OAuth2 password flow. When a user submits their credentials, AuthService.login() sends an application/x-www-form-urlencoded POST request to /auth/login. The backend responds with a signed JWT access token, which the Angular client stores in sessionStorage. From that point on, every outgoing HTTP request is automatically stamped with an Authorization: Bearer <token> header by a functional interceptor — no component-level wiring required. A reactive Angular signal keeps the rest of the application instantly aware of the session state without any polling or manual subscriptions.

Data models

The LoginResponse interface describes the exact shape returned by the /auth/login endpoint, and the Usuario interface represents a full user record as returned by the users API:
// src/app/core/models/usuario.model.ts

export interface Usuario {
  id: string;
  nombre_usuario: string;
  nombre_completo: string;
  dni: string;
  rol: 'administrador' | 'empleado';
  activo: boolean;
  creado_en: string;
}

export interface LoginResponse {
  access_token: string;
  token_type: string;
}

Login flow

1

User submits credentials

The LoginComponent binds the username and password inputs via Angular’s NgModel two-way binding. On form submission, onSubmit() delegates to AuthService.login() and manages the loading/error signals.
// src/app/features/auth/login/login.ts
export class LoginComponent {
  nombreUsuario = '';
  password = '';
  errorMensaje = signal<string | null>(null);
  cargando = signal(false);

  constructor(private authService: AuthService, private router: Router) {}

  onSubmit(): void {
    this.errorMensaje.set(null);
    this.cargando.set(true);

    this.authService.login(this.nombreUsuario, this.password).subscribe({
      next: () => {
        this.cargando.set(false);
        this.router.navigate(['/choferes']);
      },
      error: (err) => {
        this.cargando.set(false);
        if (err.status === 401) {
          this.errorMensaje.set('Usuario o contraseña incorrectos');
        } else {
          this.errorMensaje.set('Ocurrió un error, intentá de nuevo');
        }
      }
    });
  }
}
2

AuthService sends the OAuth2 password request

AuthService.login() encodes the credentials as application/x-www-form-urlencoded — the format required by FastAPI’s OAuth2 password endpoint — and POSTs to ${apiUrl}/auth/login. The tap() operator stores the token and flips the reactive signal before the observable reaches any subscriber.
// src/app/core/services/auth.service.ts
login(nombreUsuario: string, password: string): Observable<LoginResponse> {
  const body = new URLSearchParams();
  body.set('grant_type', 'password');
  body.set('username', nombreUsuario);
  body.set('password', password);

  return this.http.post<LoginResponse>(`${this.apiUrl}/auth/login`, body.toString(), {
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
  }).pipe(
    tap(respuesta => {
      sessionStorage.setItem(this.tokenKey, respuesta.access_token);
      this.isLoggedIn.set(true);
    })
  );
}
The API base URL is configured per environment:
// src/environments/environment.ts
export const environment = {
  production: false,
  apiUrl: 'http://127.0.0.1:8000'
};
3

Token stored and session signal updated

Inside the tap() callback, the raw JWT string is persisted to sessionStorage under the key aibar_token, and the isLoggedIn signal is set to true. Any component that reads authService.isLoggedIn() automatically re-renders without needing an explicit change-detection trigger.
4

Interceptor stamps every subsequent request

All subsequent HTTP calls are intercepted by authInterceptor, which reads the token from sessionStorage and clones the request with the Authorization header before forwarding it to the backend.

Token storage

The JWT is stored in sessionStorage using the private key 'aibar_token'. Storage reads and writes are encapsulated inside AuthService — no other class touches sessionStorage directly.
private tokenKey = 'aibar_token';

// Written on successful login (inside tap):
sessionStorage.setItem(this.tokenKey, respuesta.access_token);

// Read for outgoing requests:
getToken(): string | null {
  return sessionStorage.getItem(this.tokenKey);
}

// Checked at service instantiation to restore session across soft navigations:
private hayToken(): boolean {
  return !!sessionStorage.getItem(this.tokenKey);
}
Tokens are session-scoped. sessionStorage is cleared automatically when the browser tab is closed. Closing the tab logs the user out — no explicit logout action is required and no token lingers in the browser after the session ends.

Reactive login state

isLoggedIn is an Angular signal<boolean> initialized from hayToken() at service construction time. This means the session state survives in-app navigation (Angular’s router does not tear down the service), but is correctly false in a fresh tab even if a prior session existed in another tab.
// Initialized once when AuthService is instantiated
isLoggedIn = signal<boolean>(this.hayToken());
Components and guards read the signal value synchronously by calling it as a function — authService.isLoggedIn() — and Angular’s reactivity system automatically propagates changes to any template or computed that depends on it.

Token payload decoding

getPayload() extracts the JWT’s claims by splitting on ., base64-decoding the middle segment, and parsing the resulting JSON. It returns a typed object containing the username (sub) and the user’s role (rol), or null if no token exists or decoding fails.
getPayload(): { sub: string; rol: string } | null {
  const token = this.getToken();
  if (!token) return null;

  try {
    const payloadBase64 = token.split('.')[1];
    return JSON.parse(atob(payloadBase64));
  } catch {
    return null;
  }
}

getRol(): string | null {
  return this.getPayload()?.rol ?? null;
}
getPayload() performs no signature verification — that is the backend’s responsibility. The decoded payload is used purely for reading claims (role, subject) on the client side to drive UI decisions such as guard checks and sidebar visibility.

Logout

Calling logout() removes the token from sessionStorage, resets the isLoggedIn signal to false, and navigates the user back to /login. All three steps happen synchronously.
logout(): void {
  sessionStorage.removeItem(this.tokenKey);
  this.isLoggedIn.set(false);
  this.router.navigate(['/login']);
}

Login form template

The login form uses Angular’s template-driven forms with NgModel. The submit button is disabled while the request is in flight, and error messages are rendered conditionally using Angular’s @if control flow syntax.
<!-- src/app/features/auth/login/login.html -->
<div class="login-container">
  <form (ngSubmit)="onSubmit()" class="login-form">
    <h1>AIBAR SRL</h1>
    <p class="subtitulo">Ingresá con tu usuario</p>

    <label for="nombreUsuario">Usuario</label>
    <input
      id="nombreUsuario"
      type="text"
      name="nombreUsuario"
      [(ngModel)]="nombreUsuario"
      required
      autocomplete="username"
    />

    <label for="password">Contraseña</label>
    <input
      id="password"
      type="password"
      name="password"
      [(ngModel)]="password"
      required
      autocomplete="current-password"
    />

    @if (errorMensaje()) {
      <p class="error">{{ errorMensaje() }}</p>
    }

    <button type="submit" [disabled]="cargando()">
      {{ cargando() ? 'Ingresando...' : 'Ingresar' }}
    </button>
  </form>
</div>

HTTP interceptor

The authInterceptor is a functional HttpInterceptorFn registered in the application’s provider configuration. It intercepts every outgoing request, checks sessionStorage for aibar_token, and — if a token is present — clones the request with the Authorization: Bearer <token> header before passing it along. Requests made when no token exists (e.g., the login call itself) are forwarded unmodified.
// src/app/core/services/auth.service.ts
login(nombreUsuario: string, password: string): Observable<LoginResponse> {
  const body = new URLSearchParams();
  body.set('grant_type', 'password');
  body.set('username', nombreUsuario);
  body.set('password', password);

  return this.http.post<LoginResponse>(`${this.apiUrl}/auth/login`, body.toString(), {
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
  }).pipe(
    tap(respuesta => {
      sessionStorage.setItem(this.tokenKey, respuesta.access_token);
      this.isLoggedIn.set(true);
    })
  );
}
Because the interceptor reads directly from sessionStorage on every request, it always uses the latest token value — there is no risk of a stale reference being captured in a closure.

Build docs developers (and LLMs) love