@anarchitects/auth-angular 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -0
- package/config/README.md +28 -0
- package/data-access/README.md +43 -0
- package/feature/README.md +86 -0
- package/fesm2022/anarchitects-auth-angular-config.mjs +34 -0
- package/fesm2022/anarchitects-auth-angular-config.mjs.map +1 -0
- package/fesm2022/anarchitects-auth-angular-data-access.mjs +214 -0
- package/fesm2022/anarchitects-auth-angular-data-access.mjs.map +1 -0
- package/fesm2022/anarchitects-auth-angular-feature.mjs +484 -0
- package/fesm2022/anarchitects-auth-angular-feature.mjs.map +1 -0
- package/fesm2022/anarchitects-auth-angular-state.mjs +175 -0
- package/fesm2022/anarchitects-auth-angular-state.mjs.map +1 -0
- package/fesm2022/anarchitects-auth-angular-util.mjs +10 -0
- package/fesm2022/anarchitects-auth-angular-util.mjs.map +1 -0
- package/fesm2022/anarchitects-auth-angular.mjs +10 -0
- package/fesm2022/anarchitects-auth-angular.mjs.map +1 -0
- package/package.json +55 -0
- package/state/README.md +34 -0
- package/types/anarchitects-auth-angular-config.d.ts +18 -0
- package/types/anarchitects-auth-angular-data-access.d.ts +58 -0
- package/types/anarchitects-auth-angular-feature.d.ts +97 -0
- package/types/anarchitects-auth-angular-state.d.ts +68 -0
- package/types/anarchitects-auth-angular-util.d.ts +17 -0
- package/types/anarchitects-auth-angular.d.ts +5 -0
- package/util/README.md +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# @anarchitects/auth-angular
|
|
2
|
+
|
|
3
|
+
Angular bricks for the Anarchitecture auth domain. The library is organized into standalone feature slices (config, data-access, feature, state, util, ui) that compose together to provide contract-driven authentication flows for Angular applications.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- `config`: DI tokens and provider helpers (API base URL, defaults)
|
|
8
|
+
- `data-access`: generated OpenAPI clients plus adapters over the Nest API
|
|
9
|
+
- `state`: signal-based store that drives login/logout, token refresh, and ability hydration
|
|
10
|
+
- `feature`: router policy guard that consumes the shared ability
|
|
11
|
+
- `util`: CASL ability helpers (`createAppAbility`, `AppAbility`)
|
|
12
|
+
- `ui`: presentational components (reserved for future expansions)
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @anarchitects/auth-angular
|
|
18
|
+
# or
|
|
19
|
+
yarn add @anarchitects/auth-angular
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Peer dependencies: Angular v20+, `@ngrx/signals`, `@sinclair/typebox`, and the sibling packages `@anarchitects/auth-ts` & `@anarchitects/auth-nest` (for end-to-end flows).
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### Quick start
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
// app.config.ts
|
|
30
|
+
import { ApplicationConfig } from '@angular/core';
|
|
31
|
+
import { provideAuthConfig } from '@anarchitects/auth-angular/config';
|
|
32
|
+
import { provideAuthDataAccess } from '@anarchitects/auth-angular/data-access';
|
|
33
|
+
|
|
34
|
+
export const appConfig: ApplicationConfig = {
|
|
35
|
+
providers: [
|
|
36
|
+
provideAuthConfig({
|
|
37
|
+
apiBaseUrl: 'https://api.anarchitects.dev',
|
|
38
|
+
}),
|
|
39
|
+
provideAuthDataAccess(),
|
|
40
|
+
],
|
|
41
|
+
};
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
// app.component.ts
|
|
46
|
+
import { Component, inject } from '@angular/core';
|
|
47
|
+
import { AuthStore } from '@anarchitects/auth-angular/state';
|
|
48
|
+
|
|
49
|
+
@Component({
|
|
50
|
+
selector: 'app-root',
|
|
51
|
+
template: `
|
|
52
|
+
<button (click)="login()">Login</button>
|
|
53
|
+
<p *ngIf="store.isLoggedIn()">Welcome {{ store.loggedInUser()?.email }}</p>
|
|
54
|
+
`,
|
|
55
|
+
})
|
|
56
|
+
export class AppComponent {
|
|
57
|
+
readonly store = inject(AuthStore);
|
|
58
|
+
|
|
59
|
+
login() {
|
|
60
|
+
this.store.login({ credential: 'user@example.com', password: 'secret' });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
// app.routes.ts
|
|
67
|
+
import { Routes } from '@angular/router';
|
|
68
|
+
import { policyGuard } from '@anarchitects/auth-angular/feature';
|
|
69
|
+
|
|
70
|
+
export const routes: Routes = [
|
|
71
|
+
{
|
|
72
|
+
path: 'admin',
|
|
73
|
+
canMatch: [policyGuard],
|
|
74
|
+
data: { action: 'manage', subject: 'admin-section' },
|
|
75
|
+
loadComponent: () => import('./admin.component').then((m) => m.AdminComponent),
|
|
76
|
+
},
|
|
77
|
+
];
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Secondary entry points
|
|
81
|
+
|
|
82
|
+
| Import path | Description |
|
|
83
|
+
| ---------------------------------------- | ---------------------------------------- |
|
|
84
|
+
| `@anarchitects/auth-angular/config` | DI tokens and providers |
|
|
85
|
+
| `@anarchitects/auth-angular/data-access` | Generated API clients and HTTP adapters |
|
|
86
|
+
| `@anarchitects/auth-angular/state` | Signal store and CASL ability sync |
|
|
87
|
+
| `@anarchitects/auth-angular/feature` | Router policy guard |
|
|
88
|
+
| `@anarchitects/auth-angular/util` | CASL ability factory and typings |
|
|
89
|
+
| `@anarchitects/auth-angular/ui` | Presentational components (if available) |
|
|
90
|
+
|
|
91
|
+
## Nx scripts
|
|
92
|
+
|
|
93
|
+
- `nx build auth-angular` – build the Angular package
|
|
94
|
+
- `nx test auth-angular` – execute unit tests (Jest)
|
|
95
|
+
- `nx lint auth-angular` – run ESLint against the library
|
|
96
|
+
|
|
97
|
+
## Development notes
|
|
98
|
+
|
|
99
|
+
- DTO contracts live in `@anarchitects/auth-ts`; regenerate them when the API contract changes.
|
|
100
|
+
- Data-access layer should always use the generated OpenAPI clients—no manual HTTP calls.
|
|
101
|
+
- State layer uses Angular signals via `@ngrx/signals` for reactive updates and caches the CASL ability returned by the API.
|
|
102
|
+
- Ability creation is centralised in `@anarchitects/auth-angular/util`; import `createAppAbility` instead of instantiating CASL directly.
|
|
103
|
+
- Keep UI, feature, data-access, state, and config layers decoupled per architecture guidelines.
|
|
104
|
+
|
|
105
|
+
## License
|
|
106
|
+
|
|
107
|
+
Licensed under the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0).
|
package/config/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# @anarchitects/auth-angular/config
|
|
2
|
+
|
|
3
|
+
Configuration helpers and Angular DI tokens for the auth domain. Import from `@anarchitects/auth-angular/config` to customize how the data-access layer resolves API URLs.
|
|
4
|
+
|
|
5
|
+
## Exports
|
|
6
|
+
|
|
7
|
+
- `AUTH_CONFIG` / `AuthConfig`: typed configuration describing the auth API resource path
|
|
8
|
+
- `API_RESOURCE_PATH`: resolved string token used by the data-access `AuthApi`
|
|
9
|
+
- `provideAuthConfig(config)`: helper that registers `AUTH_CONFIG` and `API_RESOURCE_PATH`
|
|
10
|
+
- `provideAuthDefaults()`: registers the library defaults (`apiResourcePath: 'auth'`)
|
|
11
|
+
- `injectAuthConfig()` and `injectApiResourcePath()`: convenience accessors with sane fallbacks
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { ApplicationConfig } from '@angular/core';
|
|
17
|
+
import { provideAuthConfig } from '@anarchitects/auth-angular/config';
|
|
18
|
+
|
|
19
|
+
export const appConfig: ApplicationConfig = {
|
|
20
|
+
providers: [
|
|
21
|
+
...provideAuthConfig({
|
|
22
|
+
apiResourcePath: 'identity/auth',
|
|
23
|
+
}),
|
|
24
|
+
],
|
|
25
|
+
};
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Register these providers once (e.g. in `bootstrapApplication`) so all dependent layers resolve the correct API base path.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# @anarchitects/auth-angular/data-access
|
|
2
|
+
|
|
3
|
+
HTTP adapters that bridge Angular apps to the auth Nest API using the OpenAPI-generated DTOs. Import from `@anarchitects/auth-angular/data-access` when you need to call auth endpoints directly or wire them into feature/state layers.
|
|
4
|
+
|
|
5
|
+
## Exports
|
|
6
|
+
|
|
7
|
+
- `AuthApi`: injectable service backed by Angular `HttpClient`
|
|
8
|
+
- `authBearerTokenInterceptor`: adds `Authorization: Bearer <accessToken>` when an access token is stored
|
|
9
|
+
- `authErrorInterceptor`: handles `401/403`, attempts token refresh, retries request, and redirects to login on terminal auth failure
|
|
10
|
+
- `withAuthHttpInterceptors()`: convenience helper for `provideHttpClient(...)`
|
|
11
|
+
- Uses configuration from `@anarchitects/auth-angular/config` to resolve the `/api/{resource}` path
|
|
12
|
+
- Methods map 1:1 to contract operations (`registerUser`, `login`, `logout`, `refreshTokens`, etc.)
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { Injectable } from '@angular/core';
|
|
18
|
+
import { AuthApi } from '@anarchitects/auth-angular/data-access';
|
|
19
|
+
import { LoginRequestDTO } from '@anarchitects/auth-ts/dtos';
|
|
20
|
+
|
|
21
|
+
@Injectable({ providedIn: 'root' })
|
|
22
|
+
export class AuthFacade {
|
|
23
|
+
constructor(private readonly authApi: AuthApi) {}
|
|
24
|
+
|
|
25
|
+
login(dto: LoginRequestDTO) {
|
|
26
|
+
return this.authApi.login(dto);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Prefer consuming `AuthApi` from orchestrating feature services or signal stores so UI components remain presentation-only.
|
|
32
|
+
|
|
33
|
+
## Interceptor usage
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { ApplicationConfig } from '@angular/core';
|
|
37
|
+
import { provideHttpClient } from '@angular/common/http';
|
|
38
|
+
import { withAuthHttpInterceptors } from '@anarchitects/auth-angular/data-access';
|
|
39
|
+
|
|
40
|
+
export const appConfig: ApplicationConfig = {
|
|
41
|
+
providers: [provideHttpClient(withAuthHttpInterceptors())],
|
|
42
|
+
};
|
|
43
|
+
```
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# @anarchitects/auth-angular/feature
|
|
2
|
+
|
|
3
|
+
Feature layer for the Angular auth brick. It ships route guards plus standalone form-based feature components that orchestrate auth actions via `AuthStore`.
|
|
4
|
+
|
|
5
|
+
## Exports
|
|
6
|
+
|
|
7
|
+
- `policyGuard`: standalone `CanMatchFn` that denies routes unless the logged-in ability can perform the configured action on the configured subject.
|
|
8
|
+
- `AnarchitectsFeatureRegister`
|
|
9
|
+
- `AnarchitectsFeatureLogin`
|
|
10
|
+
- `AnarchitectsFeatureActivateUser`
|
|
11
|
+
- `AnarchitectsFeatureForgotPassword`
|
|
12
|
+
- `AnarchitectsFeatureResetPassword`
|
|
13
|
+
- `AnarchitectsFeatureVerifyEmail`
|
|
14
|
+
- `AnarchitectsFeatureChangePassword`
|
|
15
|
+
- `AnarchitectsFeatureUpdateEmail`
|
|
16
|
+
- `AnarchitectsFeatureLogout`
|
|
17
|
+
- `AnarchitectsFeatureRefreshTokens`
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { Routes } from '@angular/router';
|
|
23
|
+
import { policyGuard } from '@anarchitects/auth-angular/feature';
|
|
24
|
+
|
|
25
|
+
export const routes: Routes = [
|
|
26
|
+
{
|
|
27
|
+
path: 'admin',
|
|
28
|
+
canMatch: [policyGuard],
|
|
29
|
+
data: { action: 'manage', subject: 'admin-section' },
|
|
30
|
+
loadComponent: () => import('./admin.component').then((m) => m.AdminComponent),
|
|
31
|
+
},
|
|
32
|
+
];
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The guard reads the `AuthStore` ability snapshot. Ensure the state layer is providing abilities by wiring the data-access and util modules in your application bootstrap.
|
|
36
|
+
|
|
37
|
+
### Token-driven actions
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { Component } from '@angular/core';
|
|
41
|
+
import { AnarchitectsFeatureVerifyEmail } from '@anarchitects/auth-angular/feature';
|
|
42
|
+
|
|
43
|
+
@Component({
|
|
44
|
+
selector: 'app-verify-email-page',
|
|
45
|
+
imports: [AnarchitectsFeatureVerifyEmail],
|
|
46
|
+
template: `<anarchitects-auth-feature-verify-email [token]="token" />`,
|
|
47
|
+
})
|
|
48
|
+
export class VerifyEmailPageComponent {
|
|
49
|
+
token = 'verification-token-from-route';
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### User-id actions
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { Component } from '@angular/core';
|
|
57
|
+
import { AnarchitectsFeatureChangePassword } from '@anarchitects/auth-angular/feature';
|
|
58
|
+
|
|
59
|
+
@Component({
|
|
60
|
+
selector: 'app-change-password-page',
|
|
61
|
+
imports: [AnarchitectsFeatureChangePassword],
|
|
62
|
+
template: `<anarchitects-auth-feature-change-password [userId]="userId" />`,
|
|
63
|
+
})
|
|
64
|
+
export class ChangePasswordPageComponent {
|
|
65
|
+
userId = 'current-user-id';
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Token refresh/logout
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import { Component } from '@angular/core';
|
|
73
|
+
import { AnarchitectsFeatureRefreshTokens, AnarchitectsFeatureLogout } from '@anarchitects/auth-angular/feature';
|
|
74
|
+
|
|
75
|
+
@Component({
|
|
76
|
+
selector: 'app-session-page',
|
|
77
|
+
imports: [AnarchitectsFeatureRefreshTokens, AnarchitectsFeatureLogout],
|
|
78
|
+
template: `
|
|
79
|
+
<anarchitects-auth-feature-refresh-tokens [userId]="userId" />
|
|
80
|
+
<anarchitects-auth-feature-logout />
|
|
81
|
+
`,
|
|
82
|
+
})
|
|
83
|
+
export class SessionPageComponent {
|
|
84
|
+
userId = 'current-user-id';
|
|
85
|
+
}
|
|
86
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { InjectionToken, inject } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
const AUTH_CONFIG = new InjectionToken('AUTH_CONFIG');
|
|
4
|
+
const API_RESOURCE_PATH = new InjectionToken('AUTH_API_RESOURCE_PATH');
|
|
5
|
+
/** Library-level sensible defaults */
|
|
6
|
+
const AUTH_DEFAULTS = {
|
|
7
|
+
apiResourcePath: 'auth',
|
|
8
|
+
};
|
|
9
|
+
/** Safe injectors that fall back to defaults if no providers are registered */
|
|
10
|
+
function injectAuthConfig() {
|
|
11
|
+
return inject(AUTH_CONFIG, { optional: true }) ?? AUTH_DEFAULTS;
|
|
12
|
+
}
|
|
13
|
+
function injectApiResourcePath() {
|
|
14
|
+
return (inject(API_RESOURCE_PATH, { optional: true }) ??
|
|
15
|
+
AUTH_DEFAULTS.apiResourcePath);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function provideAuthConfig(cfg) {
|
|
19
|
+
const merged = { ...AUTH_DEFAULTS, ...cfg };
|
|
20
|
+
return [
|
|
21
|
+
{ provide: AUTH_CONFIG, useValue: merged },
|
|
22
|
+
{ provide: API_RESOURCE_PATH, useValue: merged.apiResourcePath },
|
|
23
|
+
];
|
|
24
|
+
}
|
|
25
|
+
function provideAuthDefaults() {
|
|
26
|
+
return provideAuthConfig({});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Generated bundle index. Do not edit.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
export { API_RESOURCE_PATH, AUTH_CONFIG, AUTH_DEFAULTS, injectApiResourcePath, injectAuthConfig, provideAuthConfig, provideAuthDefaults };
|
|
34
|
+
//# sourceMappingURL=anarchitects-auth-angular-config.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anarchitects-auth-angular-config.mjs","sources":["../../../../../libs/auth/angular/config/src/tokens.ts","../../../../../libs/auth/angular/config/src/providers.ts","../../../../../libs/auth/angular/config/src/anarchitects-auth-angular-config.ts"],"sourcesContent":["import { InjectionToken, inject } from '@angular/core';\n\nexport type AuthConfig = {\n apiResourcePath: string;\n};\n\nexport const AUTH_CONFIG = new InjectionToken<AuthConfig>('AUTH_CONFIG');\nexport const API_RESOURCE_PATH = new InjectionToken<string>(\n 'AUTH_API_RESOURCE_PATH'\n);\n\n/** Library-level sensible defaults */\nexport const AUTH_DEFAULTS: AuthConfig = {\n apiResourcePath: 'auth',\n};\n\n/** Safe injectors that fall back to defaults if no providers are registered */\nexport function injectAuthConfig(): AuthConfig {\n return inject(AUTH_CONFIG, { optional: true }) ?? AUTH_DEFAULTS;\n}\n\nexport function injectApiResourcePath(): string {\n return (\n inject(API_RESOURCE_PATH, { optional: true }) ??\n AUTH_DEFAULTS.apiResourcePath\n );\n}\n","import { Provider } from '@angular/core';\nimport {\n API_RESOURCE_PATH,\n AUTH_CONFIG,\n AUTH_DEFAULTS,\n AuthConfig,\n} from './tokens';\n\nexport function provideAuthConfig(cfg: Partial<AuthConfig>): Provider[] {\n const merged: AuthConfig = { ...AUTH_DEFAULTS, ...cfg };\n return [\n { provide: AUTH_CONFIG, useValue: merged },\n { provide: API_RESOURCE_PATH, useValue: merged.apiResourcePath },\n ];\n}\n\nexport function provideAuthDefaults(): Provider[] {\n return provideAuthConfig({});\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;MAMa,WAAW,GAAG,IAAI,cAAc,CAAa,aAAa;MAC1D,iBAAiB,GAAG,IAAI,cAAc,CACjD,wBAAwB;AAG1B;AACO,MAAM,aAAa,GAAe;AACvC,IAAA,eAAe,EAAE,MAAM;;AAGzB;SACgB,gBAAgB,GAAA;AAC9B,IAAA,OAAO,MAAM,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,IAAI,aAAa;AACjE;SAEgB,qBAAqB,GAAA;IACnC,QACE,MAAM,CAAC,iBAAiB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAC7C,aAAa,CAAC,eAAe;AAEjC;;AClBM,SAAU,iBAAiB,CAAC,GAAwB,EAAA;IACxD,MAAM,MAAM,GAAe,EAAE,GAAG,aAAa,EAAE,GAAG,GAAG,EAAE;IACvD,OAAO;AACL,QAAA,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE;QAC1C,EAAE,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,CAAC,eAAe,EAAE;KACjE;AACH;SAEgB,mBAAmB,GAAA;AACjC,IAAA,OAAO,iBAAiB,CAAC,EAAE,CAAC;AAC9B;;AClBA;;AAEG;;;;"}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { injectApiResourcePath } from '@anarchitects/auth-angular/config';
|
|
2
|
+
import { HttpClient, HttpContextToken, HttpErrorResponse, HttpStatusCode, HttpBackend, withInterceptors } from '@angular/common/http';
|
|
3
|
+
import * as i0 from '@angular/core';
|
|
4
|
+
import { inject, Injectable } from '@angular/core';
|
|
5
|
+
import { jwtDecode } from 'jwt-decode';
|
|
6
|
+
import { Router } from '@angular/router';
|
|
7
|
+
import { tap, finalize, shareReplay, catchError, throwError, switchMap } from 'rxjs';
|
|
8
|
+
|
|
9
|
+
class AuthApi {
|
|
10
|
+
http = inject(HttpClient);
|
|
11
|
+
resourceUrl = `/api/${injectApiResourcePath()}`;
|
|
12
|
+
registerUser(dto) {
|
|
13
|
+
return this.http.post(`${this.resourceUrl}/register`, dto);
|
|
14
|
+
}
|
|
15
|
+
activateUser(dto) {
|
|
16
|
+
return this.http.patch(`${this.resourceUrl}/activate`, dto);
|
|
17
|
+
}
|
|
18
|
+
login(dto) {
|
|
19
|
+
return this.http.post(`${this.resourceUrl}/login`, dto);
|
|
20
|
+
}
|
|
21
|
+
logout(dto) {
|
|
22
|
+
return this.http.post(`${this.resourceUrl}/logout`, dto);
|
|
23
|
+
}
|
|
24
|
+
changePassword(userId, dto) {
|
|
25
|
+
return this.http.patch(`${this.resourceUrl}/change-password/${userId}`, dto);
|
|
26
|
+
}
|
|
27
|
+
forgotPassword(dto) {
|
|
28
|
+
return this.http.post(`${this.resourceUrl}/forgot-password`, dto);
|
|
29
|
+
}
|
|
30
|
+
resetPassword(dto) {
|
|
31
|
+
return this.http.post(`${this.resourceUrl}/reset-password`, dto);
|
|
32
|
+
}
|
|
33
|
+
verifyEmail(dto) {
|
|
34
|
+
return this.http.post(`${this.resourceUrl}/verify-email`, dto);
|
|
35
|
+
}
|
|
36
|
+
updateEmail(userId, dto) {
|
|
37
|
+
return this.http.patch(`${this.resourceUrl}/update-email/${userId}`, dto);
|
|
38
|
+
}
|
|
39
|
+
refreshTokens(userId, dto) {
|
|
40
|
+
return this.http.post(`${this.resourceUrl}/refresh-tokens/${userId}`, dto);
|
|
41
|
+
}
|
|
42
|
+
getLoggedInUserInfo() {
|
|
43
|
+
return this.http.get(`${this.resourceUrl}/me`);
|
|
44
|
+
}
|
|
45
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: AuthApi, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
46
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: AuthApi, providedIn: 'root' });
|
|
47
|
+
}
|
|
48
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: AuthApi, decorators: [{
|
|
49
|
+
type: Injectable,
|
|
50
|
+
args: [{
|
|
51
|
+
providedIn: 'root',
|
|
52
|
+
}]
|
|
53
|
+
}] });
|
|
54
|
+
|
|
55
|
+
const ACCESS_TOKEN_STORAGE_KEY = 'accessToken';
|
|
56
|
+
const REFRESH_TOKEN_STORAGE_KEY = 'refreshToken';
|
|
57
|
+
function getStoredToken(key) {
|
|
58
|
+
try {
|
|
59
|
+
if (typeof localStorage === 'undefined') {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
return localStorage.getItem(key) ?? undefined;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function storeTokens(tokens) {
|
|
69
|
+
try {
|
|
70
|
+
if (typeof localStorage === 'undefined') {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
localStorage.setItem(ACCESS_TOKEN_STORAGE_KEY, tokens.accessToken);
|
|
74
|
+
localStorage.setItem(REFRESH_TOKEN_STORAGE_KEY, tokens.refreshToken);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// noop: storage can be unavailable in non-browser environments
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function clearStoredTokens() {
|
|
81
|
+
try {
|
|
82
|
+
if (typeof localStorage === 'undefined') {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
localStorage.removeItem(ACCESS_TOKEN_STORAGE_KEY);
|
|
86
|
+
localStorage.removeItem(REFRESH_TOKEN_STORAGE_KEY);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// noop: storage can be unavailable in non-browser environments
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function resolveUserIdFromAccessToken(accessToken) {
|
|
93
|
+
if (!accessToken) {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const decoded = jwtDecode(accessToken);
|
|
98
|
+
return decoded.sub;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const authBearerTokenInterceptor = (req, next) => {
|
|
106
|
+
const accessToken = getStoredToken(ACCESS_TOKEN_STORAGE_KEY);
|
|
107
|
+
if (!accessToken || req.headers.has('Authorization')) {
|
|
108
|
+
return next(req);
|
|
109
|
+
}
|
|
110
|
+
return next(req.clone({
|
|
111
|
+
setHeaders: {
|
|
112
|
+
Authorization: `Bearer ${accessToken}`,
|
|
113
|
+
},
|
|
114
|
+
}));
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const AUTH_RETRY_ATTEMPTED = new HttpContextToken(() => false);
|
|
118
|
+
const LOGIN_REDIRECT_PATH = '/login';
|
|
119
|
+
let refreshTokensRequest$ = null;
|
|
120
|
+
function isUnauthorizedError(error) {
|
|
121
|
+
return (error instanceof HttpErrorResponse &&
|
|
122
|
+
(error.status === HttpStatusCode.Unauthorized ||
|
|
123
|
+
error.status === HttpStatusCode.Forbidden));
|
|
124
|
+
}
|
|
125
|
+
function isAuthPublicEndpoint(url, authBaseUrl) {
|
|
126
|
+
const publicEndpoints = [
|
|
127
|
+
'/login',
|
|
128
|
+
'/register',
|
|
129
|
+
'/activate',
|
|
130
|
+
'/forgot-password',
|
|
131
|
+
'/reset-password',
|
|
132
|
+
'/verify-email',
|
|
133
|
+
];
|
|
134
|
+
return publicEndpoints.some((endpoint) => url.includes(`${authBaseUrl}${endpoint}`));
|
|
135
|
+
}
|
|
136
|
+
function isRefreshEndpoint(url, authBaseUrl) {
|
|
137
|
+
return url.includes(`${authBaseUrl}/refresh-tokens/`);
|
|
138
|
+
}
|
|
139
|
+
function redirectToLogin(router) {
|
|
140
|
+
if (router) {
|
|
141
|
+
void router.navigateByUrl(LOGIN_REDIRECT_PATH);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (typeof window !== 'undefined') {
|
|
145
|
+
window.location.assign(LOGIN_REDIRECT_PATH);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function getRefreshTokensRequest(http, refreshUrl, refreshToken) {
|
|
149
|
+
if (!refreshTokensRequest$) {
|
|
150
|
+
refreshTokensRequest$ = http
|
|
151
|
+
.post(refreshUrl, { refreshToken })
|
|
152
|
+
.pipe(tap((tokens) => storeTokens(tokens)), finalize(() => {
|
|
153
|
+
refreshTokensRequest$ = null;
|
|
154
|
+
}), shareReplay(1));
|
|
155
|
+
}
|
|
156
|
+
return refreshTokensRequest$;
|
|
157
|
+
}
|
|
158
|
+
const authErrorInterceptor = (req, next) => {
|
|
159
|
+
const authBaseUrl = `/api/${injectApiResourcePath()}`;
|
|
160
|
+
const backend = inject(HttpBackend);
|
|
161
|
+
const router = inject(Router, { optional: true });
|
|
162
|
+
const rawHttp = new HttpClient(backend);
|
|
163
|
+
return next(req).pipe(catchError((error) => {
|
|
164
|
+
if (!isUnauthorizedError(error)) {
|
|
165
|
+
return throwError(() => error);
|
|
166
|
+
}
|
|
167
|
+
if (req.context.get(AUTH_RETRY_ATTEMPTED) ||
|
|
168
|
+
isRefreshEndpoint(req.url, authBaseUrl) ||
|
|
169
|
+
isAuthPublicEndpoint(req.url, authBaseUrl)) {
|
|
170
|
+
if (req.context.get(AUTH_RETRY_ATTEMPTED)) {
|
|
171
|
+
clearStoredTokens();
|
|
172
|
+
redirectToLogin(router ?? null);
|
|
173
|
+
}
|
|
174
|
+
return throwError(() => error);
|
|
175
|
+
}
|
|
176
|
+
const refreshToken = getStoredToken(REFRESH_TOKEN_STORAGE_KEY);
|
|
177
|
+
const accessToken = getStoredToken(ACCESS_TOKEN_STORAGE_KEY);
|
|
178
|
+
const userId = resolveUserIdFromAccessToken(accessToken);
|
|
179
|
+
if (!refreshToken || !userId) {
|
|
180
|
+
clearStoredTokens();
|
|
181
|
+
redirectToLogin(router ?? null);
|
|
182
|
+
return throwError(() => error);
|
|
183
|
+
}
|
|
184
|
+
const refreshUrl = `${authBaseUrl}/refresh-tokens/${userId}`;
|
|
185
|
+
return getRefreshTokensRequest(rawHttp, refreshUrl, refreshToken).pipe(switchMap(({ accessToken: nextAccessToken }) => {
|
|
186
|
+
const retryRequest = req.clone({
|
|
187
|
+
context: req.context.set(AUTH_RETRY_ATTEMPTED, true),
|
|
188
|
+
setHeaders: {
|
|
189
|
+
Authorization: `Bearer ${nextAccessToken}`,
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
return next(retryRequest);
|
|
193
|
+
}), catchError((refreshError) => {
|
|
194
|
+
clearStoredTokens();
|
|
195
|
+
redirectToLogin(router ?? null);
|
|
196
|
+
return throwError(() => refreshError);
|
|
197
|
+
}));
|
|
198
|
+
}));
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const AUTH_HTTP_INTERCEPTORS = [
|
|
202
|
+
authBearerTokenInterceptor,
|
|
203
|
+
authErrorInterceptor,
|
|
204
|
+
];
|
|
205
|
+
function withAuthHttpInterceptors() {
|
|
206
|
+
return withInterceptors(AUTH_HTTP_INTERCEPTORS);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Generated bundle index. Do not edit.
|
|
211
|
+
*/
|
|
212
|
+
|
|
213
|
+
export { AUTH_HTTP_INTERCEPTORS, AuthApi, authBearerTokenInterceptor, authErrorInterceptor, withAuthHttpInterceptors };
|
|
214
|
+
//# sourceMappingURL=anarchitects-auth-angular-data-access.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anarchitects-auth-angular-data-access.mjs","sources":["../../../../../libs/auth/angular/data-access/src/auth-api.ts","../../../../../libs/auth/angular/data-access/src/interceptors/auth-token.utils.ts","../../../../../libs/auth/angular/data-access/src/interceptors/bearer-token.interceptor.ts","../../../../../libs/auth/angular/data-access/src/interceptors/auth-error.interceptor.ts","../../../../../libs/auth/angular/data-access/src/interceptors/index.ts","../../../../../libs/auth/angular/data-access/src/anarchitects-auth-angular-data-access.ts"],"sourcesContent":["import { injectApiResourcePath } from '@anarchitects/auth-angular/config';\nimport {\n ActivateUserRequestDTO,\n ChangePasswordRequestDTO,\n ForgotPasswordRequestDTO,\n LoginRequestDTO,\n LoginResponseDTO,\n LogoutRequestDTO,\n RefreshTokenRequestDTO,\n RegisterRequestDTO,\n RegisterResponseDTO,\n ResetPasswordRequestDTO,\n UpdateEmailRequestDTO,\n VerifyEmailRequestDTO,\n} from '@anarchitects/auth-ts/dtos';\nimport { PolicyRule, User } from '@anarchitects/auth-ts/models';\nimport { HttpClient } from '@angular/common/http';\nimport { inject, Injectable } from '@angular/core';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class AuthApi {\n private readonly http = inject(HttpClient);\n private readonly resourceUrl = `/api/${injectApiResourcePath()}`;\n\n registerUser(dto: RegisterRequestDTO) {\n return this.http.post<RegisterResponseDTO>(\n `${this.resourceUrl}/register`,\n dto\n );\n }\n\n activateUser(dto: ActivateUserRequestDTO) {\n return this.http.patch<{ success: boolean }>(\n `${this.resourceUrl}/activate`,\n dto\n );\n }\n\n login(dto: LoginRequestDTO) {\n return this.http.post<LoginResponseDTO>(`${this.resourceUrl}/login`, dto);\n }\n\n logout(dto: LogoutRequestDTO) {\n return this.http.post<{ success: boolean }>(\n `${this.resourceUrl}/logout`,\n dto\n );\n }\n\n changePassword(userId: string, dto: ChangePasswordRequestDTO) {\n return this.http.patch<{ success: boolean }>(\n `${this.resourceUrl}/change-password/${userId}`,\n dto\n );\n }\n\n forgotPassword(dto: ForgotPasswordRequestDTO) {\n return this.http.post<{ success: boolean }>(\n `${this.resourceUrl}/forgot-password`,\n dto\n );\n }\n\n resetPassword(dto: ResetPasswordRequestDTO) {\n return this.http.post<{ success: boolean }>(\n `${this.resourceUrl}/reset-password`,\n dto\n );\n }\n\n verifyEmail(dto: VerifyEmailRequestDTO) {\n return this.http.post<{ success: boolean }>(\n `${this.resourceUrl}/verify-email`,\n dto\n );\n }\n\n updateEmail(userId: string, dto: UpdateEmailRequestDTO) {\n return this.http.patch<{ success: boolean }>(\n `${this.resourceUrl}/update-email/${userId}`,\n dto\n );\n }\n\n refreshTokens(userId: string, dto: RefreshTokenRequestDTO) {\n return this.http.post<LoginResponseDTO>(\n `${this.resourceUrl}/refresh-tokens/${userId}`,\n dto\n );\n }\n\n getLoggedInUserInfo() {\n return this.http.get<{ user: User; rbac: PolicyRule[] }>(\n `${this.resourceUrl}/me`\n );\n }\n}\n","import { jwtDecode } from 'jwt-decode';\n\nexport const ACCESS_TOKEN_STORAGE_KEY = 'accessToken';\nexport const REFRESH_TOKEN_STORAGE_KEY = 'refreshToken';\n\nexport type LoginTokens = {\n accessToken: string;\n refreshToken: string;\n};\n\nexport function getStoredToken(key: string): string | undefined {\n try {\n if (typeof localStorage === 'undefined') {\n return undefined;\n }\n\n return localStorage.getItem(key) ?? undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function storeTokens(tokens: LoginTokens): void {\n try {\n if (typeof localStorage === 'undefined') {\n return;\n }\n\n localStorage.setItem(ACCESS_TOKEN_STORAGE_KEY, tokens.accessToken);\n localStorage.setItem(REFRESH_TOKEN_STORAGE_KEY, tokens.refreshToken);\n } catch {\n // noop: storage can be unavailable in non-browser environments\n }\n}\n\nexport function clearStoredTokens(): void {\n try {\n if (typeof localStorage === 'undefined') {\n return;\n }\n\n localStorage.removeItem(ACCESS_TOKEN_STORAGE_KEY);\n localStorage.removeItem(REFRESH_TOKEN_STORAGE_KEY);\n } catch {\n // noop: storage can be unavailable in non-browser environments\n }\n}\n\nexport function resolveUserIdFromAccessToken(\n accessToken: string | undefined\n): string | undefined {\n if (!accessToken) {\n return undefined;\n }\n\n try {\n const decoded = jwtDecode<{ sub?: string }>(accessToken);\n return decoded.sub;\n } catch {\n return undefined;\n }\n}\n","import { HttpInterceptorFn } from '@angular/common/http';\nimport { ACCESS_TOKEN_STORAGE_KEY, getStoredToken } from './auth-token.utils';\n\nexport const authBearerTokenInterceptor: HttpInterceptorFn = (req, next) => {\n const accessToken = getStoredToken(ACCESS_TOKEN_STORAGE_KEY);\n\n if (!accessToken || req.headers.has('Authorization')) {\n return next(req);\n }\n\n return next(\n req.clone({\n setHeaders: {\n Authorization: `Bearer ${accessToken}`,\n },\n })\n );\n};\n","import {\n HttpBackend,\n HttpClient,\n HttpContextToken,\n HttpErrorResponse,\n HttpInterceptorFn,\n HttpStatusCode,\n} from '@angular/common/http';\nimport { inject } from '@angular/core';\nimport { injectApiResourcePath } from '@anarchitects/auth-angular/config';\nimport { LoginResponseDTO } from '@anarchitects/auth-ts/dtos';\nimport { Router } from '@angular/router';\nimport {\n catchError,\n finalize,\n Observable,\n shareReplay,\n switchMap,\n tap,\n throwError,\n} from 'rxjs';\nimport {\n ACCESS_TOKEN_STORAGE_KEY,\n REFRESH_TOKEN_STORAGE_KEY,\n clearStoredTokens,\n getStoredToken,\n resolveUserIdFromAccessToken,\n storeTokens,\n} from './auth-token.utils';\n\nconst AUTH_RETRY_ATTEMPTED = new HttpContextToken<boolean>(() => false);\nconst LOGIN_REDIRECT_PATH = '/login';\n\nlet refreshTokensRequest$: Observable<LoginResponseDTO> | null = null;\n\nfunction isUnauthorizedError(error: unknown): error is HttpErrorResponse {\n return (\n error instanceof HttpErrorResponse &&\n (error.status === HttpStatusCode.Unauthorized ||\n error.status === HttpStatusCode.Forbidden)\n );\n}\n\nfunction isAuthPublicEndpoint(url: string, authBaseUrl: string): boolean {\n const publicEndpoints = [\n '/login',\n '/register',\n '/activate',\n '/forgot-password',\n '/reset-password',\n '/verify-email',\n ];\n\n return publicEndpoints.some((endpoint) =>\n url.includes(`${authBaseUrl}${endpoint}`)\n );\n}\n\nfunction isRefreshEndpoint(url: string, authBaseUrl: string): boolean {\n return url.includes(`${authBaseUrl}/refresh-tokens/`);\n}\n\nfunction redirectToLogin(router: Router | null): void {\n if (router) {\n void router.navigateByUrl(LOGIN_REDIRECT_PATH);\n return;\n }\n\n if (typeof window !== 'undefined') {\n window.location.assign(LOGIN_REDIRECT_PATH);\n }\n}\n\nfunction getRefreshTokensRequest(\n http: HttpClient,\n refreshUrl: string,\n refreshToken: string\n): Observable<LoginResponseDTO> {\n if (!refreshTokensRequest$) {\n refreshTokensRequest$ = http\n .post<LoginResponseDTO>(refreshUrl, { refreshToken })\n .pipe(\n tap((tokens) => storeTokens(tokens)),\n finalize(() => {\n refreshTokensRequest$ = null;\n }),\n shareReplay(1)\n );\n }\n\n return refreshTokensRequest$;\n}\n\nexport const authErrorInterceptor: HttpInterceptorFn = (req, next) => {\n const authBaseUrl = `/api/${injectApiResourcePath()}`;\n const backend = inject(HttpBackend);\n const router = inject(Router, { optional: true });\n const rawHttp = new HttpClient(backend);\n\n return next(req).pipe(\n catchError((error) => {\n if (!isUnauthorizedError(error)) {\n return throwError(() => error);\n }\n\n if (\n req.context.get(AUTH_RETRY_ATTEMPTED) ||\n isRefreshEndpoint(req.url, authBaseUrl) ||\n isAuthPublicEndpoint(req.url, authBaseUrl)\n ) {\n if (req.context.get(AUTH_RETRY_ATTEMPTED)) {\n clearStoredTokens();\n redirectToLogin(router ?? null);\n }\n\n return throwError(() => error);\n }\n\n const refreshToken = getStoredToken(REFRESH_TOKEN_STORAGE_KEY);\n const accessToken = getStoredToken(ACCESS_TOKEN_STORAGE_KEY);\n const userId = resolveUserIdFromAccessToken(accessToken);\n\n if (!refreshToken || !userId) {\n clearStoredTokens();\n redirectToLogin(router ?? null);\n return throwError(() => error);\n }\n\n const refreshUrl = `${authBaseUrl}/refresh-tokens/${userId}`;\n\n return getRefreshTokensRequest(rawHttp, refreshUrl, refreshToken).pipe(\n switchMap(({ accessToken: nextAccessToken }) => {\n const retryRequest = req.clone({\n context: req.context.set(AUTH_RETRY_ATTEMPTED, true),\n setHeaders: {\n Authorization: `Bearer ${nextAccessToken}`,\n },\n });\n\n return next(retryRequest);\n }),\n catchError((refreshError) => {\n clearStoredTokens();\n redirectToLogin(router ?? null);\n return throwError(() => refreshError);\n })\n );\n })\n );\n};\n","import { HttpInterceptorFn, withInterceptors } from '@angular/common/http';\nimport { authBearerTokenInterceptor } from './bearer-token.interceptor';\nimport { authErrorInterceptor } from './auth-error.interceptor';\n\nexport const AUTH_HTTP_INTERCEPTORS: HttpInterceptorFn[] = [\n authBearerTokenInterceptor,\n authErrorInterceptor,\n];\n\nexport function withAuthHttpInterceptors() {\n return withInterceptors(AUTH_HTTP_INTERCEPTORS);\n}\n\nexport * from './bearer-token.interceptor';\nexport * from './auth-error.interceptor';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|