@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 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).
@@ -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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"}