npm package: @geekbears/gb-ngrx-auth-basic
Description: This library provides a robust and highly configurable solution for authentication and authorization in Angular applications, leveraging NgRx for state management. It offers a flexible service (`AuthenticationService`) and routing guards, designed to inte
Latest version: 21.0.0

# GbNgrxAuthBasic

This library provides a robust and highly configurable solution for authentication and authorization in Angular applications, leveraging NgRx for state management. It offers a flexible service (`AuthenticationService`) and routing guards, designed to integrate seamlessly with your backend APIs and custom logic.

## Features

*   **Comprehensive Authentication & Authorization**: Handles user login, signup, logout, session refresh, and user data fetching.
*   **NgRx State Management**: Manages authentication state (e.g., token, user, session status) in a way that can be easily integrated with NgRx stores.
*   **Extensive Customization**: Fully configurable via `AuthenticationNgRxModuleConfig`, allowing you to tailor API endpoints, request/response handling, user mapping, and more.
*   **Flexible Storage**: Supports default `localStorage` for tokens and user data, with an option to provide custom storage solutions.
*   **Device Information**: Automatically captures and can include device information in authentication requests.
*   **Built-in Guards**: Provides guards for route protection based on authentication status and role-based access control (RBAC).
*   **Token Refresh**: Manages automatic token refreshing via interceptors.
*   **Account Management**: Includes functionality for fetching user information and deleting accounts.

## Installation

To install this library, run the following command:

```bash
npm install @gb-ngrx-auth-basic
```

Or if you are using yarn:

```bash
yarn add @gb-ngrx-auth-basic
```

### Peer Dependencies

This library depends on the `@geekbears/gb-ngrx-stack-base` package. Ensure it is installed in your project:

```bash
npm install @geekbears/gb-ngrx-stack-base
```

## Usage

### Module Setup and Configuration

Import `GbNgrxAuthBasicModule` into your application's root module or a feature module. You **must** provide the `AUTHENTICATION_NGRX_MODULE_CONFIG` token with your specific configuration.

```typescript
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { RouterModule } from '@angular/router';
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';

import { GbNgrxAuthBasicModule, AUTHENTICATION_NGRX_MODULE_CONFIG, AuthenticationNgRxModuleConfig } from 'gb-ngrx-auth-basic'; // Adjust import path if necessary

// Example custom user mapper
const customUserMapper = (userMap: { [key: string]: any }): Promise<any> => {
  // Transform userMap into your application's User model
  return Promise.resolve({ ...userMap, customField: 'mapped' });
};

// Example custom login response parser
const customLoginResponseParser = async (response: any): Promise<any> => {
  // Parse the raw API response to extract AuthenticationData
  return {
    token: response.data.token,
    refreshToken: response.data.refreshToken,
    user: response.data.user, // Assuming user object is directly available
  };
};

const appConfig: AuthenticationNgRxModuleConfig = {
  // --- API Endpoints ---
  loginApiUrl: () => '/api/auth/login',
  signupApiUrl: () => '/api/auth/signup',
  logoutApiUrl: (authData) => `/api/auth/logout?token=${authData.token}`, // Example with authData
  refreshTokenApiUri: () => '/api/auth/refresh',
  fetchUserInformationAPIendpoint: (data) => `/api/users/${data.user.id}`, // Example using user ID from session
  deleteAccountAPIendpoint: (userId) => `/api/users/${userId}`,

  // --- Custom Mappers & Parsers ---
  customLoginRequestMapper: async (email, password, deviceInfo) => {
    return {
      email,
      password,
      device: deviceInfo ? deviceInfo.toMap() : undefined,
      customParam: 'example',
    };
  },
  customLoginResponseParser: customLoginResponseParser,
  customSignupRequestMapper: async (payload) => {
    return payload.toMap(); // Assuming AuthSignupPayload has a toMap method
  },
  customSignupResponseParser: customLoginResponseParser, // Can reuse if response structure is similar
  customLogoutRequestMapper: async (authData) => {
    return { authorization: authData.token };
  },
  customRefreshTokenRequestMapper: async (authData) => {
    return { refreshToken: authData.refreshToken };
  },
  customRefreshTokenResponseParser: customLoginResponseParser, // Can reuse if response structure is similar
  customUserMapper: customUserMapper,
  customFetchUserInformationResponseMapper: async (data) => {
    // If fetchUserInformationAPIendpoint returns data that needs further mapping before customUserMapper
    return data.userProfile; // Example: extract user profile from a nested object
  },

  // --- Route Guard Redirection ---
  basicAuthGuardRedirect: (authData, router, route, snapshot) => {
    // Example: Redirect to login if not authenticated
    if (!authData) {
      return router.createUrlTree(['/login']);
    }
    return of(true); // Allow access
  },
  roleBasedGuardRedirect: (authData, router, route, snapshot) => {
    // Example: Redirect to unauthorized page if role is insufficient
    if (authData && !authData.user.roles.includes(route.data['roles'][0])) {
      return router.createUrlTree(['/unauthorized']);
    }
    return of(true); // Allow access
  },
  onAuthenticatedGuardRedirect: (authData, router, route) => {
    // Example: Redirect to dashboard if already authenticated
    if (authData && authData.user) {
      return router.createUrlTree(['/dashboard']);
    }
    return of(true); // Allow access
  },
  onAuthenticatedChangeRedirect: (authData, router, route) => {
    // Example: Redirect to login on logout, dashboard on login
    if (!authData) {
      return router.createUrlTree(['/login']);
    } else {
      return router.createUrlTree(['/dashboard']);
    }
  },
  authenticatedChangeRedirectOnSignificant: true, // Default: true

  // --- Other Options ---
  authenticateOnSignup: true, // Default: true
  provideDeviceInfo: true, // Default: true
  initialAuthCheckedAfterUserFetch: true,
  autoLogoutOnSessionExpired: true, // Default: true
  autoLogoutOnUnauthorizedUserInfoFetch: true, // Default: true
};

@NgModule({
  imports: [
    BrowserModule,
    HttpClientModule,
    RouterModule.forRoot([]), // Your app routes
    StoreModule.forRoot({}), // Your NgRx stores
    EffectsModule.forRoot([]), // Your NgRx effects
    GbNgrxAuthBasicModule.forRoot(appConfig), // Provide the configuration here
  ],
  // ...
})
export class AppModule { }
```

### Authentication Service Methods

Inject `AuthenticationService` to use its methods.

*   **`loginWithCredentials(email: string, password: string): Observable<AuthenticationData>`**:
    Initiates the login process. Uses `loginApiUrl` and `customLoginRequestMapper`/`customLoginResponseParser` if provided.
    ```typescript
    this.authService.loginWithCredentials('user@example.com', 'password123').subscribe(session => {
      console.log('Login successful:', session);
    });
    ```

*   **`signupWithCredentials(payload: AuthSignupPayload): Observable<AuthenticationData>`**:
    Handles user registration. Uses `signupApiUrl` and `customSignupRequestMapper`/`customSignupResponseParser`. If `authenticateOnSignup` is true, it will attempt to authenticate the user after signup.
    ```typescript
    const signupPayload = new AuthSignupPayload({ email: 'newuser@example.com', password: 'securepassword' });
    this.authService.signupWithCredentials(signupPayload).subscribe(session => {
      console.log('Signup successful:', session);
    });
    ```

*   **`fetchAuthUserData(): Observable<IUser>`**:
    Fetches the current user's detailed information from the backend using `fetchUserInformationAPIendpoint`. It uses `customFetchUserInformationResponseMapper` and `customUserMapper` for data transformation.
    ```typescript
    this.authService.fetchAuthUserData().subscribe(user => {
      console.log('User data:', user);
    });
    ```

*   **`getAuthenticatedSession(): Observable<AuthenticationData | null>`**:
    Retrieves the current authentication session (token, refresh token, user) from storage.

*   **`getRefreshedSession(refreshToken?: string): Observable<AuthenticationDataModel | null>`**:
    Requests a new session token using the refresh token. Uses `refreshTokenApiUri` and custom mappers.

*   **`logout(): Observable<void>`**:
    Logs the user out by clearing tokens from storage and optionally calling a `logoutApiUrl`.

*   **`deleteAccount(userId: string): Observable<IUser>`**:
    Deletes the user account via the `deleteAccountAPIendpoint`.

### NgRx State Management

This library manages its state using NgRx. You can access the authentication state directly using the provided selectors.

**Selectors**

Import these from the library to query the store:

*   `isAuthenticatedSelector`: `Observable<boolean>` - Returns `true` if the user is currently authenticated.
*   `authenticatedUserSelector`: `Observable<IUser | null>` - Returns the current authenticated user object.
*   `authenticationDataSelector`: `Observable<AuthenticationData | null>` - Returns the full authentication session data (tokens, user, etc.).
*   `authTokenSelector`: `Observable<string | undefined>` - Returns the current access token.
*   `isAuthenticatingSelector`: `Observable<boolean>` - Returns `true` while a login request is in progress.
*   `loginFailureSelector`: `Observable<Failure | null>` - Returns any error that occurred during the last login attempt.
*   `isSigningUpSelector`: `Observable<boolean>` - Returns `true` while a signup request is in progress.
*   `signupFailureSelector`: `Observable<Failure | null>` - Returns any error that occurred during the last signup attempt.

**Actions**

While the `AuthenticationService` handles most interactions, you can also dispatch actions directly if needed. Actions are available strictly via `AuthenticationNgRxActions`.

Example:
```typescript
import { Store } from '@ngrx/store';
import { AuthenticationNgRxActions } from 'gb-ngrx-auth-basic';

constructor(private store: Store) {}

logout() {
  this.store.dispatch(AuthenticationNgRxActions.logout());
}
```

### Route Guards

The library provides guards that can be configured to redirect users based on authentication status or roles.

*   **`BasicAuthGuard`**: Protects routes requiring authentication. Uses `basicAuthGuardRedirect` and `onAuthenticatedGuardRedirect` from the config.
*   **`RbacGuard`**: Protects routes based on user roles. Uses `roleBasedGuardRedirect` and `onAuthenticatedGuardRedirect` from the config.
*   **`RedirectOnAuthGuard`**: Can be used to redirect authenticated users away from certain pages (e.g., login page). Uses `onAuthenticatedGuardRedirect` from the config.

Configure the redirection logic within the `AuthenticationNgRxModuleConfig` object passed to `GbNgrxAuthBasicModule.forRoot()`.

### Storage

By default, the library stores authentication tokens, refresh tokens, user data, and device information in `localStorage` using predefined keys (`s_token`, `s_refreshToken`, `s_user`, `s_device_info`). You can provide a custom storage implementation by injecting a service bound to `CustomInternalStorageToken`.

### Device Info

If `provideDeviceInfo` is set to `true` (default), the library automatically captures device details to include in authentication requests. This helps in tracking and managing user sessions across different devices.

**Captured Information:**
*   **ID**: A generated UUID that uniquely identifies the browser session.
*   **Name**: A combination of the User Agent string and screen resolution (e.g., `Mozilla/5.0 ... - (1920 x 1080) 24`).

You can customize how this information is sent to your API using the `customLoginRequestMapper` in the configuration.

## Development

This library was generated with Angular CLI version 12.2.1.

### Development server

Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.

### Code scaffolding

Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.

### Build

Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.

### Running unit tests

Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).

### Running end-to-end tests

Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.

### Further help

To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.