{"_id":"@amaurylapaque/angular-auth","name":"@amaurylapaque/angular-auth","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.1":{"name":"@amaurylapaque/angular-auth","version":"1.0.1","description":"Pluggable Angular authentication layer with adapters for OIDC, MSAL, Firebase, Supabase, JWT and Mock providers.","license":"MIT","private":false,"sideEffects":false,"homepage":"https://github.com/ALapaque/angular-auth#readme","repository":{"type":"git","url":"git+https://github.com/ALapaque/angular-auth.git"},"bugs":{"url":"https://github.com/ALapaque/angular-auth/issues"},"engines":{"node":">=20.0.0"},"publishConfig":{"access":"public","provenance":true},"keywords":["angular","auth","authentication","oidc","msal","firebase","supabase","jwt","keycloak","auth0"],"peerDependencies":{"@angular/common":">=17.0.0","@angular/core":">=17.0.0","@azure/msal-angular":">=5.0.0","@azure/msal-browser":">=5.0.0","@supabase/supabase-js":">=2.0.0","angular-auth-oidc-client":">=17.0.0","firebase":">=10.0.0","rxjs":">=7.0.0"},"peerDependenciesMeta":{"angular-auth-oidc-client":{"optional":true},"@azure/msal-angular":{"optional":true},"@azure/msal-browser":{"optional":true},"@supabase/supabase-js":{"optional":true},"firebase":{"optional":true}},"module":"fesm2022/amaurylapaque-angular-auth.mjs","typings":"types/amaurylapaque-angular-auth.d.ts","exports":{"./package.json":{"default":"./package.json"},".":{"types":"./types/amaurylapaque-angular-auth.d.ts","default":"./fesm2022/amaurylapaque-angular-auth.mjs"}},"type":"module","dependencies":{"tslib":"^2.3.0"},"_id":"@amaurylapaque/angular-auth@1.0.1","gitHead":"3a8f869ef6e256fef25ff3a31f519d339d4919d6","_nodeVersion":"20.20.2","_npmVersion":"10.8.2","dist":{"integrity":"sha512-GQfrIFkbhpbmJlJyb2wPz8oQtC/tiCy2nk74yZQDRaxtwGkNBOlCHjRp1mBGUhKlpIe30K5g9wTCj5TYcmKlUg==","shasum":"aa9be5af5874a2e7c68c8afcd532e1aa81556f33","tarball":"https://registry.npmjs.org/@amaurylapaque/angular-auth/-/angular-auth-1.0.1.tgz","fileCount":7,"unpackedSize":132191,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@amaurylapaque%2fangular-auth@1.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCQb3BTQmSgOgBQvNadvX7N1jpf+JCHNkPARmS6LZZ6yAIhAL5VgBUrY+W/B0aLhBzXw7cVQMzmnXC0NUouEgq83yUC"}]},"_npmUser":{"name":"amaurylapaque","email":"amaury.lapaque@arlioz.be"},"directories":{},"maintainers":[{"name":"amaurylapaque","email":"amaury.lapaque@arlioz.be"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/angular-auth_1.0.1_1776959061382_0.7097076502922413"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-23T15:44:21.299Z","1.0.1":"2026-04-23T15:44:21.533Z","modified":"2026-04-23T15:44:21.887Z"},"maintainers":[{"name":"amaurylapaque","email":"amaury.lapaque@arlioz.be"}],"description":"Pluggable Angular authentication layer with adapters for OIDC, MSAL, Firebase, Supabase, JWT and Mock providers.","homepage":"https://github.com/ALapaque/angular-auth#readme","keywords":["angular","auth","authentication","oidc","msal","firebase","supabase","jwt","keycloak","auth0"],"repository":{"type":"git","url":"git+https://github.com/ALapaque/angular-auth.git"},"bugs":{"url":"https://github.com/ALapaque/angular-auth/issues"},"license":"MIT","readme":"# @amaurylapaque/angular-auth\n\n[![npm version](https://img.shields.io/npm/v/@amaurylapaque%2Fangular-auth.svg)](https://www.npmjs.com/package/@amaurylapaque/angular-auth)\n[![npm downloads](https://img.shields.io/npm/dm/@amaurylapaque%2Fangular-auth.svg)](https://www.npmjs.com/package/@amaurylapaque/angular-auth)\n[![license](https://img.shields.io/npm/l/@amaurylapaque%2Fangular-auth.svg)](./LICENSE)\n\nPluggable Angular authentication layer. One `AuthService` façade, one `authGuard`, one `authInterceptor` — switch the underlying provider (OIDC, MSAL, Firebase, Supabase, JWT, Mock) by changing a single line in your bootstrap.\n\n**Live demo:** https://angular-auth-demo.vercel.app/ — try the live adapter switcher and browse the in-app docs.\n\n**Documentation:**\n- [`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md) — design, DI model, invariants, trade-offs\n- [`docs/ADAPTERS.md`](./docs/ADAPTERS.md) — per-adapter implementation notes\n- [`docs/CONTRIBUTING.md`](./docs/CONTRIBUTING.md) — local dev, writing a new adapter, tests, release\n- [`SECURITY.md`](./SECURITY.md) — vulnerability disclosure and token-storage guidance\n- [`CHANGELOG.md`](./CHANGELOG.md)\n- In-app docs site — `npm run demo` then open `/docs` for integration guides with copy-pasteable snippets\n\n## Why\n\nAngular apps tend to couple to whichever auth SDK was picked on day one. When the team wants to migrate from, say, Keycloak to Auth0, or support both during a transition, every component that reads `user$` or `getAccessToken()` needs to change. This package puts a thin, stable contract in front of the SDK so the rest of the app never has to know.\n\n## Install\n\n```bash\nnpm install @amaurylapaque/angular-auth\n\n# then install only the SDK(s) for the adapter(s) you actually use\nnpm install angular-auth-oidc-client         # for OIDC (Auth0, Keycloak, Okta, Cognito, ...)\nnpm install @azure/msal-browser @azure/msal-angular   # for MSAL\nnpm install firebase                         # for Firebase\nnpm install @supabase/supabase-js            # for Supabase\n# JWT and Mock adapters have no extra peer deps\n```\n\n## Core concept\n\n```\n┌─────────────────────────────────────────┐\n│  Your components / services / guards    │\n│     inject(AuthService)                 │\n└──────────────────┬──────────────────────┘\n                   │   unified contract\n                   ▼\n            ┌──────────────┐\n            │ AuthProvider │  interface\n            └──────┬───────┘\n      ┌───────┬────┴─────┬──────────┬─────────┬────────┐\n      ▼       ▼          ▼          ▼         ▼        ▼\n    OIDC    MSAL     Firebase   Supabase    JWT      Mock\n```\n\nComponents only ever touch `AuthService`. Swapping the provider is a single-file change in the app bootstrap.\n\n## Bootstrap\n\n`provideAuth(<adapter>, <config>)` installs everything:\n\n```ts\n// main.ts\nimport { bootstrapApplication } from '@angular/platform-browser';\nimport { provideHttpClient, withInterceptors } from '@angular/common/http';\nimport {\n  provideAuth,\n  provideOidc,\n  authInterceptor,\n} from '@amaurylapaque/angular-auth';\n\nimport { AppComponent } from './app/app.component';\n\nbootstrapApplication(AppComponent, {\n  providers: [\n    provideHttpClient(withInterceptors([authInterceptor])),\n    provideAuth(\n      provideOidc({\n        authority: 'https://your-tenant.auth0.com',\n        clientId: 'abc123',\n        redirectUrl: window.location.origin,\n      }),\n      {\n        protectedResourceUrls: ['https://api.example.com'],\n      },\n    ),\n  ],\n});\n```\n\n## Using it in a component\n\n```ts\nimport { Component, inject } from '@angular/core';\nimport { AsyncPipe } from '@angular/common';\nimport { AuthService } from '@amaurylapaque/angular-auth';\n\n@Component({\n  standalone: true,\n  imports: [AsyncPipe],\n  template: `\n    @if (auth.isAuthenticated()) {\n      <p>Hello {{ auth.user()?.name }}</p>\n      <button (click)=\"auth.logout()\">Logout</button>\n    } @else {\n      <button (click)=\"auth.login()\">Login</button>\n    }\n  `,\n})\nexport class HeaderComponent {\n  readonly auth = inject(AuthService);\n}\n```\n\n## Route protection\n\n```ts\n// app.routes.ts\nimport { Routes } from '@angular/router';\nimport { authGuard } from '@amaurylapaque/angular-auth';\n\nexport const routes: Routes = [\n  { path: 'private', loadComponent: () => import('./private.component'), canActivate: [authGuard] },\n];\n```\n\n## Adapters\n\n### 1. OIDC — Auth0, Keycloak, Okta, Cognito, Zitadel, Authentik, ...\n\nAny spec-compliant OpenID Connect issuer.\n\n```ts\nprovideAuth(\n  provideOidc({\n    authority: 'https://your-tenant.auth0.com',    // Auth0\n    // authority: 'https://keycloak/realms/my-app', // Keycloak\n    // authority: 'https://cognito-idp.<region>.amazonaws.com/<pool>', // Cognito\n    clientId: 'abc123',\n    redirectUrl: window.location.origin,\n    scope: 'openid profile email offline_access',\n  }),\n);\n```\n\n### 2. MSAL — Azure AD / Entra ID\n\n```ts\nprovideAuth(\n  provideMsal({\n    clientId: '00000000-0000-0000-0000-000000000000',\n    authority: 'https://login.microsoftonline.com/<tenant-id>',\n    redirectUri: window.location.origin,\n    scopes: ['User.Read'],\n    interactionType: 'redirect', // or 'popup'\n  }),\n);\n```\n\n### 3. Firebase\n\n```ts\nimport { GoogleAuthProvider } from 'firebase/auth';\n\nprovideAuth(\n  provideFirebase({\n    firebaseOptions: {\n      apiKey: '...',\n      authDomain: '...',\n      projectId: '...',\n    },\n    defaultStrategy: { type: 'popup', provider: new GoogleAuthProvider() },\n  }),\n);\n```\n\nEmail-password, custom token and anonymous login are all supported — pick a strategy at config time or pass it per call via `login({ extra: { strategy: {...} } })`.\n\n### 4. Supabase\n\n```ts\nprovideAuth(\n  provideSupabase({\n    url: 'https://xyz.supabase.co',\n    anonKey: '...',\n    defaultStrategy: { type: 'password', email: 'a@b.c', password: 'secret' },\n    // or { type: 'oauth', provider: 'github' }\n    // or { type: 'otp', email: 'a@b.c' }  (magic link)\n  }),\n);\n```\n\nThe adapter wraps `@supabase/supabase-js` — OAuth providers (github, google, discord…), password, OTP and session refresh all go through the unified `AuthService`.\n\n### 5. JWT — custom backend\n\n```ts\nprovideAuth(\n  provideJwt({\n    loginUrl: 'https://api.example.com/auth/login',\n    refreshUrl: 'https://api.example.com/auth/refresh',\n    logoutUrl: 'https://api.example.com/auth/logout',\n    storage: 'local',\n  }),\n);\n\n// usage:\nauth.login({ extra: { email: 'foo@bar.com', password: 'secret' } });\n```\n\n`mapLoginResponse` and `mapUser` let you adapt any backend shape without changing the call sites.\n\n### 6. Mock — dev & tests\n\n```ts\nprovideAuth(\n  provideMock({\n    startAuthenticated: true,\n    user: { id: 'u1', name: 'Alice', email: 'alice@test.dev', roles: ['admin'] },\n  }),\n);\n```\n\nInjecting `MockAuthAdapter` in a test gives you `setUser(user | null)` for flipping state synchronously between scenarios.\n\n## Switching providers in practice\n\nIn most teams the choice is an environment concern, not a code concern:\n\n```ts\n// bootstrap helper\nimport { environment } from './environments/environment';\n\nfunction authFeature() {\n  switch (environment.auth.kind) {\n    case 'oidc':     return provideOidc(environment.auth.config);\n    case 'msal':     return provideMsal(environment.auth.config);\n    case 'firebase': return provideFirebase(environment.auth.config);\n    case 'supabase': return provideSupabase(environment.auth.config);\n    case 'jwt':      return provideJwt(environment.auth.config);\n    case 'mock':     return provideMock(environment.auth.config);\n  }\n}\n\nbootstrapApplication(AppComponent, {\n  providers: [\n    provideHttpClient(withInterceptors([authInterceptor])),\n    provideAuth(authFeature()),\n  ],\n});\n```\n\n## Writing your own adapter\n\nImplement `AuthProvider`, expose a `provideXxx()` that wires it behind the `AUTH_PROVIDER` token:\n\n```ts\n@Injectable()\nclass MyAdapter implements AuthProvider {\n  readonly user$ = /* ... */;\n  readonly isAuthenticated$ = /* ... */;\n  readonly isLoading$ = /* ... */;\n  init(): Promise<void> { /* ... */ }\n  login(): Promise<void> { /* ... */ }\n  logout(): Promise<void> { /* ... */ }\n  getAccessToken(): Promise<string | null> { /* ... */ }\n}\n\nexport function provideMyAdapter(config: MyConfig): AuthAdapterFeature {\n  return {\n    providers: [\n      { provide: MY_CONFIG, useValue: config },\n      MyAdapter,\n      { provide: AUTH_PROVIDER, useExisting: MyAdapter },\n    ],\n  };\n}\n```\n\n## Running the demo\n\nA minimal standalone Angular app lives under `demo/`. It boots with the **Mock** adapter so it runs offline with no credentials — switching providers is a single line in `demo/src/app/app.config.ts`. The demo also ships with a live **adapter switcher** in the UI (backed by localStorage + reload) showing how different Mock configurations behave with the exact same components.\n\n```bash\nnpm install\nnpm run demo         # ng serve demo → http://localhost:4200\nnpm run demo:build   # production build into dist/demo\n```\n\n## Running the test suite\n\n```bash\nnpm test             # Vitest + @analogjs/vitest-angular, jsdom\nnpm run test:watch\n```\n\nCurrent coverage (42 specs): every adapter has its own spec with the upstream SDK mocked via `vi.mock` — `MockAuthAdapter`, `JwtAuthAdapter` (login / refresh / expiry / storage / custom response mapping), `OidcAuthAdapter` (Keycloak + Cognito claim shapes), `MsalAuthAdapter` (event subjects, redirect vs popup, silent token), `FirebaseAuthAdapter` (auth state, strategies, id token), `SupabaseAuthAdapter` (session hydration, `onAuthStateChange`, password / OAuth / OTP strategies, refresh). Plus `AuthService` façade and `authInterceptor` matching rules.\n\n## Security\n\n> Full details in [`SECURITY.md`](./SECURITY.md). Quick summary for integrators:\n\n- **Never use `protectedResourceUrls: ['*']` in production.** It leaks your users' tokens to every domain you call — including third-party APIs, CDNs and analytics. Always list your own API origins explicitly.\n- **`localStorage` is readable by any script on your origin.** A single XSS vulnerability exfiltrates every stored token. The JWT adapter defaults to `local` for DX; switch to `storage: 'session'` (per-tab) or `storage: 'memory'` (no persistence) for high-sensitivity apps, or move auth to HttpOnly server cookies.\n- **Register redirect URIs strictly at your identity provider.** Never accept open redirects.\n- **Review `npm audit` output** before every release. CI runs it automatically via `.github/workflows/ci.yml`.\n- **Report vulnerabilities privately** via the GitHub security advisory link in `SECURITY.md` — never in a public issue.\n\n## What's out of scope (for now)\n\n- SAML / CAS — protocol support belongs in a separate adapter\n- Passkeys / WebAuthn — planned, but spec is still shifting\n- Server-side session renewal strategies — each adapter uses its SDK's defaults\n","readmeFilename":"README.md","_rev":"1-ba46097037423845728f9b2c820215ed"}