@nauth-toolkit/client-angular 0.1.55 → 0.1.57
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/esm2022/lib/auth.guard.mjs +83 -0
- package/esm2022/lib/auth.interceptor.mjs +158 -0
- package/esm2022/lib/social-redirect-callback.guard.mjs +81 -0
- package/esm2022/nauth-toolkit-client-angular.mjs +5 -0
- package/esm2022/ngmodule/auth.interceptor.class.mjs +109 -0
- package/esm2022/ngmodule/auth.service.mjs +777 -0
- package/esm2022/ngmodule/http-adapter.mjs +81 -0
- package/esm2022/ngmodule/nauth.module.mjs +65 -0
- package/esm2022/ngmodule/tokens.mjs +6 -0
- package/esm2022/public-api.mjs +19 -0
- package/esm2022/src/standalone/nauth-toolkit-client-angular-src-standalone.mjs +5 -0
- package/esm2022/src/standalone/public-api.mjs +12 -0
- package/esm2022/standalone/auth.guard.mjs +83 -0
- package/esm2022/standalone/auth.interceptor.mjs +158 -0
- package/esm2022/standalone/auth.service.mjs +777 -0
- package/esm2022/standalone/http-adapter.mjs +81 -0
- package/esm2022/standalone/nauth-toolkit-client-angular-standalone.mjs +5 -0
- package/esm2022/standalone/public-api.mjs +16 -0
- package/esm2022/standalone/social-redirect-callback.guard.mjs +81 -0
- package/esm2022/standalone/tokens.mjs +6 -0
- package/fesm2022/nauth-toolkit-client-angular-src-standalone.mjs +17 -0
- package/fesm2022/nauth-toolkit-client-angular-src-standalone.mjs.map +1 -0
- package/fesm2022/nauth-toolkit-client-angular-standalone.mjs +1183 -0
- package/fesm2022/nauth-toolkit-client-angular-standalone.mjs.map +1 -0
- package/fesm2022/nauth-toolkit-client-angular.mjs +1344 -0
- package/fesm2022/nauth-toolkit-client-angular.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/{src/lib/auth.guard.ts → lib/auth.guard.d.ts} +15 -37
- package/lib/auth.interceptor.d.ts +15 -0
- package/lib/social-redirect-callback.guard.d.ts +25 -0
- package/ngmodule/auth.interceptor.class.d.ts +34 -0
- package/ngmodule/auth.service.d.ts +580 -0
- package/ngmodule/http-adapter.d.ts +37 -0
- package/ngmodule/nauth.module.d.ts +31 -0
- package/{src/ngmodule/tokens.ts → ngmodule/tokens.d.ts} +1 -2
- package/package.json +30 -20
- package/{src/public-api.ts → public-api.d.ts} +0 -6
- package/src/standalone/index.d.ts +5 -0
- package/src/standalone/{public-api.ts → public-api.d.ts} +0 -2
- package/standalone/{auth.guard.ts → auth.guard.d.ts} +15 -37
- package/standalone/auth.interceptor.d.ts +15 -0
- package/standalone/auth.service.d.ts +580 -0
- package/standalone/http-adapter.d.ts +37 -0
- package/standalone/index.d.ts +5 -0
- package/standalone/{public-api.ts → public-api.d.ts} +1 -6
- package/standalone/social-redirect-callback.guard.d.ts +25 -0
- package/standalone/{tokens.ts → tokens.d.ts} +1 -2
- package/ng-package.json +0 -12
- package/src/lib/auth.interceptor.ts +0 -194
- package/src/lib/social-redirect-callback.guard.ts +0 -87
- package/src/ngmodule/auth.interceptor.class.ts +0 -124
- package/src/ngmodule/auth.service.ts +0 -865
- package/src/ngmodule/http-adapter.ts +0 -79
- package/src/ngmodule/nauth.module.ts +0 -59
- package/src/package.json +0 -11
- package/src/standalone/ng-package.json +0 -7
- package/src/standalone/package.json +0 -8
- package/standalone/auth.interceptor.ts +0 -194
- package/standalone/auth.service.ts +0 -865
- package/standalone/http-adapter.ts +0 -79
- package/standalone/ng-package.json +0 -7
- package/standalone/package.json +0 -8
- package/standalone/social-redirect-callback.guard.ts +0 -87
- package/tsconfig.json +0 -10
- package/tsconfig.lib.json +0 -28
- package/tsconfig.lib.prod.json +0 -10
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { Injectable } from '@angular/core';
|
|
2
|
-
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
|
|
3
|
-
import { firstValueFrom } from 'rxjs';
|
|
4
|
-
import { HttpAdapter, HttpRequest, HttpResponse, NAuthClientError, NAuthErrorCode } from '@nauth-toolkit/client';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* HTTP adapter for Angular using HttpClient.
|
|
8
|
-
*
|
|
9
|
-
* This adapter:
|
|
10
|
-
* - Uses Angular's HttpClient for all requests
|
|
11
|
-
* - Works with Angular's HTTP interceptors (including authInterceptor)
|
|
12
|
-
* - Auto-provided via Angular DI (providedIn: 'root')
|
|
13
|
-
* - Converts HttpClient responses to HttpResponse format
|
|
14
|
-
* - Converts HttpErrorResponse to NAuthClientError
|
|
15
|
-
*
|
|
16
|
-
* Users don't need to configure this manually - it's automatically
|
|
17
|
-
* injected when using AuthService in Angular apps.
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```typescript
|
|
21
|
-
* // Automatic usage (no manual setup needed)
|
|
22
|
-
* // AuthService automatically injects AngularHttpAdapter
|
|
23
|
-
* constructor(private auth: AuthService) {}
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
|
-
@Injectable()
|
|
27
|
-
export class AngularHttpAdapter implements HttpAdapter {
|
|
28
|
-
constructor(private readonly http: HttpClient) {}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Execute HTTP request using Angular's HttpClient.
|
|
32
|
-
*
|
|
33
|
-
* @param config - Request configuration
|
|
34
|
-
* @returns Response with parsed data
|
|
35
|
-
* @throws NAuthClientError if request fails
|
|
36
|
-
*/
|
|
37
|
-
async request<T>(config: HttpRequest): Promise<HttpResponse<T>> {
|
|
38
|
-
try {
|
|
39
|
-
// Use Angular's HttpClient - goes through ALL interceptors
|
|
40
|
-
const data = await firstValueFrom(
|
|
41
|
-
this.http.request<T>(config.method, config.url, {
|
|
42
|
-
body: config.body,
|
|
43
|
-
headers: config.headers,
|
|
44
|
-
withCredentials: config.credentials === 'include',
|
|
45
|
-
observe: 'body', // Only return body data
|
|
46
|
-
}),
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
data,
|
|
51
|
-
status: 200, // HttpClient only returns data on success
|
|
52
|
-
headers: {}, // Can extract from observe: 'response' if needed
|
|
53
|
-
};
|
|
54
|
-
} catch (error) {
|
|
55
|
-
if (error instanceof HttpErrorResponse) {
|
|
56
|
-
// Convert Angular's HttpErrorResponse to NAuthClientError
|
|
57
|
-
const errorData = error.error || {};
|
|
58
|
-
const code =
|
|
59
|
-
typeof errorData['code'] === 'string' ? (errorData.code as NAuthErrorCode) : NAuthErrorCode.INTERNAL_ERROR;
|
|
60
|
-
const message =
|
|
61
|
-
typeof errorData['message'] === 'string'
|
|
62
|
-
? (errorData.message as string)
|
|
63
|
-
: error.message || `Request failed with status ${error.status}`;
|
|
64
|
-
const timestamp = typeof errorData['timestamp'] === 'string' ? errorData.timestamp : undefined;
|
|
65
|
-
const details = errorData['details'] as Record<string, unknown> | undefined;
|
|
66
|
-
|
|
67
|
-
throw new NAuthClientError(code, message, {
|
|
68
|
-
statusCode: error.status,
|
|
69
|
-
timestamp,
|
|
70
|
-
details,
|
|
71
|
-
isNetworkError: error.status === 0, // Network error (no response from server)
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Re-throw non-HTTP errors
|
|
76
|
-
throw error;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { NgModule, ModuleWithProviders } from '@angular/core';
|
|
2
|
-
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
|
|
3
|
-
import { NAUTH_CLIENT_CONFIG } from './tokens';
|
|
4
|
-
import { AuthService } from './auth.service';
|
|
5
|
-
import { AngularHttpAdapter } from './http-adapter';
|
|
6
|
-
import { AuthInterceptor } from './auth.interceptor.class';
|
|
7
|
-
import { NAuthClientConfig } from '@nauth-toolkit/client';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* NgModule for nauth-toolkit Angular integration.
|
|
11
|
-
*
|
|
12
|
-
* Use this for NgModule-based apps (Angular 17+ with NgModule or legacy apps).
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```typescript
|
|
16
|
-
* // app.module.ts
|
|
17
|
-
* import { NAuthModule } from '@nauth-toolkit/client-angular';
|
|
18
|
-
*
|
|
19
|
-
* @NgModule({
|
|
20
|
-
* imports: [
|
|
21
|
-
* NAuthModule.forRoot({
|
|
22
|
-
* baseUrl: 'http://localhost:3000/auth',
|
|
23
|
-
* tokenDelivery: 'cookies',
|
|
24
|
-
* }),
|
|
25
|
-
* ],
|
|
26
|
-
* })
|
|
27
|
-
* export class AppModule {}
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
@NgModule({
|
|
31
|
-
imports: [HttpClientModule],
|
|
32
|
-
exports: [HttpClientModule],
|
|
33
|
-
})
|
|
34
|
-
export class NAuthModule {
|
|
35
|
-
static forRoot(config: NAuthClientConfig): ModuleWithProviders<NAuthModule> {
|
|
36
|
-
return {
|
|
37
|
-
ngModule: NAuthModule,
|
|
38
|
-
providers: [
|
|
39
|
-
{
|
|
40
|
-
provide: NAUTH_CLIENT_CONFIG,
|
|
41
|
-
useValue: config,
|
|
42
|
-
},
|
|
43
|
-
AngularHttpAdapter,
|
|
44
|
-
{
|
|
45
|
-
provide: AuthService,
|
|
46
|
-
useFactory: (httpAdapter: AngularHttpAdapter) => {
|
|
47
|
-
return new AuthService(config, httpAdapter);
|
|
48
|
-
},
|
|
49
|
-
deps: [AngularHttpAdapter],
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
provide: HTTP_INTERCEPTORS,
|
|
53
|
-
useClass: AuthInterceptor,
|
|
54
|
-
multi: true,
|
|
55
|
-
},
|
|
56
|
-
],
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
}
|
package/src/package.json
DELETED
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
import { inject, PLATFORM_ID } from '@angular/core';
|
|
2
|
-
import { isPlatformBrowser } from '@angular/common';
|
|
3
|
-
import { HttpHandlerFn, HttpInterceptorFn, HttpRequest, HttpClient, HttpErrorResponse } from '@angular/common/http';
|
|
4
|
-
import { Router } from '@angular/router';
|
|
5
|
-
import { catchError, switchMap, throwError, filter, take, BehaviorSubject, from } from 'rxjs';
|
|
6
|
-
import { NAUTH_CLIENT_CONFIG } from './tokens';
|
|
7
|
-
import { AuthService } from './auth.service';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Refresh state management.
|
|
11
|
-
* BehaviorSubject pattern is the industry-standard for token refresh.
|
|
12
|
-
*/
|
|
13
|
-
let isRefreshing = false;
|
|
14
|
-
const refreshTokenSubject = new BehaviorSubject<string | null>(null);
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Track retried requests to prevent infinite loops.
|
|
18
|
-
*/
|
|
19
|
-
const retriedRequests = new WeakSet<HttpRequest<unknown>>();
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Get CSRF token from cookie.
|
|
23
|
-
*/
|
|
24
|
-
function getCsrfToken(cookieName: string): string | null {
|
|
25
|
-
if (typeof document === 'undefined') return null;
|
|
26
|
-
const match = document.cookie.match(new RegExp(`(^| )${cookieName}=([^;]+)`));
|
|
27
|
-
return match ? decodeURIComponent(match[2]) : null;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Angular HTTP interceptor for nauth-toolkit.
|
|
32
|
-
*
|
|
33
|
-
* Handles:
|
|
34
|
-
* - Cookies mode: withCredentials + CSRF tokens + refresh via POST
|
|
35
|
-
* - JSON mode: refresh via SDK, retry with new token
|
|
36
|
-
*/
|
|
37
|
-
export const authInterceptor: HttpInterceptorFn = (req: HttpRequest<unknown>, next: HttpHandlerFn) => {
|
|
38
|
-
const config = inject(NAUTH_CLIENT_CONFIG);
|
|
39
|
-
const http = inject(HttpClient);
|
|
40
|
-
const authService = inject(AuthService);
|
|
41
|
-
const platformId = inject(PLATFORM_ID);
|
|
42
|
-
const router = inject(Router);
|
|
43
|
-
const isBrowser = isPlatformBrowser(platformId);
|
|
44
|
-
|
|
45
|
-
if (!isBrowser) {
|
|
46
|
-
return next(req);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const tokenDelivery = config.tokenDelivery;
|
|
50
|
-
const baseUrl = config.baseUrl;
|
|
51
|
-
const endpoints = config.endpoints ?? {};
|
|
52
|
-
const refreshPath = endpoints.refresh ?? '/refresh';
|
|
53
|
-
const loginPath = endpoints.login ?? '/login';
|
|
54
|
-
const signupPath = endpoints.signup ?? '/signup';
|
|
55
|
-
const socialExchangePath = endpoints.socialExchange ?? '/social/exchange';
|
|
56
|
-
const refreshUrl = `${baseUrl}${refreshPath}`;
|
|
57
|
-
|
|
58
|
-
const isAuthApiRequest = req.url.includes(baseUrl);
|
|
59
|
-
const isRefreshEndpoint = req.url.includes(refreshPath);
|
|
60
|
-
const isPublicEndpoint =
|
|
61
|
-
req.url.includes(loginPath) || req.url.includes(signupPath) || req.url.includes(socialExchangePath);
|
|
62
|
-
|
|
63
|
-
// Build request with credentials (cookies mode only)
|
|
64
|
-
let authReq = req;
|
|
65
|
-
if (tokenDelivery === 'cookies') {
|
|
66
|
-
authReq = authReq.clone({ withCredentials: true });
|
|
67
|
-
|
|
68
|
-
if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method)) {
|
|
69
|
-
const csrfCookieName = config.csrf?.cookieName ?? 'nauth_csrf_token';
|
|
70
|
-
const csrfHeaderName = config.csrf?.headerName ?? 'x-csrf-token';
|
|
71
|
-
const csrfToken = getCsrfToken(csrfCookieName);
|
|
72
|
-
if (csrfToken) {
|
|
73
|
-
authReq = authReq.clone({ setHeaders: { [csrfHeaderName]: csrfToken } });
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return next(authReq).pipe(
|
|
79
|
-
catchError((error: unknown) => {
|
|
80
|
-
const shouldHandle =
|
|
81
|
-
error instanceof HttpErrorResponse &&
|
|
82
|
-
error.status === 401 &&
|
|
83
|
-
isAuthApiRequest &&
|
|
84
|
-
!isRefreshEndpoint &&
|
|
85
|
-
!isPublicEndpoint &&
|
|
86
|
-
!retriedRequests.has(req);
|
|
87
|
-
|
|
88
|
-
if (!shouldHandle) {
|
|
89
|
-
return throwError(() => error);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (config.debug) {
|
|
93
|
-
console.warn('[nauth-interceptor] 401 detected:', req.url);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (!isRefreshing) {
|
|
97
|
-
isRefreshing = true;
|
|
98
|
-
refreshTokenSubject.next(null);
|
|
99
|
-
|
|
100
|
-
if (config.debug) {
|
|
101
|
-
console.warn('[nauth-interceptor] Starting refresh...');
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Refresh based on mode
|
|
105
|
-
const refresh$ =
|
|
106
|
-
tokenDelivery === 'cookies'
|
|
107
|
-
? http.post<{ accessToken?: string }>(refreshUrl, {}, { withCredentials: true })
|
|
108
|
-
: from(authService.refresh());
|
|
109
|
-
|
|
110
|
-
return refresh$.pipe(
|
|
111
|
-
switchMap((response) => {
|
|
112
|
-
if (config.debug) {
|
|
113
|
-
console.warn('[nauth-interceptor] Refresh successful');
|
|
114
|
-
}
|
|
115
|
-
isRefreshing = false;
|
|
116
|
-
|
|
117
|
-
// Get new token (JSON mode) or signal success (cookies mode)
|
|
118
|
-
const newToken = 'accessToken' in response ? response.accessToken : 'success';
|
|
119
|
-
refreshTokenSubject.next(newToken ?? 'success');
|
|
120
|
-
|
|
121
|
-
// Build retry request
|
|
122
|
-
const retryReq = buildRetryRequest(authReq, tokenDelivery, newToken);
|
|
123
|
-
retriedRequests.add(retryReq);
|
|
124
|
-
|
|
125
|
-
if (config.debug) {
|
|
126
|
-
console.warn('[nauth-interceptor] Retrying:', req.url);
|
|
127
|
-
}
|
|
128
|
-
return next(retryReq);
|
|
129
|
-
}),
|
|
130
|
-
catchError((err) => {
|
|
131
|
-
if (config.debug) {
|
|
132
|
-
console.error('[nauth-interceptor] Refresh failed:', err);
|
|
133
|
-
}
|
|
134
|
-
isRefreshing = false;
|
|
135
|
-
refreshTokenSubject.next(null);
|
|
136
|
-
|
|
137
|
-
// Handle session expiration - redirect to configured URL
|
|
138
|
-
if (config.redirects?.sessionExpired) {
|
|
139
|
-
router.navigateByUrl(config.redirects.sessionExpired).catch((navError) => {
|
|
140
|
-
if (config.debug) {
|
|
141
|
-
console.error('[nauth-interceptor] Navigation failed:', navError);
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return throwError(() => err);
|
|
147
|
-
}),
|
|
148
|
-
);
|
|
149
|
-
} else {
|
|
150
|
-
// Wait for ongoing refresh
|
|
151
|
-
if (config.debug) {
|
|
152
|
-
console.warn('[nauth-interceptor] Waiting for refresh...');
|
|
153
|
-
}
|
|
154
|
-
return refreshTokenSubject.pipe(
|
|
155
|
-
filter((token): token is string => token !== null),
|
|
156
|
-
take(1),
|
|
157
|
-
switchMap((token) => {
|
|
158
|
-
if (config.debug) {
|
|
159
|
-
console.warn('[nauth-interceptor] Refresh done, retrying:', req.url);
|
|
160
|
-
}
|
|
161
|
-
const retryReq = buildRetryRequest(authReq, tokenDelivery, token);
|
|
162
|
-
retriedRequests.add(retryReq);
|
|
163
|
-
return next(retryReq);
|
|
164
|
-
}),
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
}),
|
|
168
|
-
);
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Build retry request with appropriate auth.
|
|
173
|
-
*/
|
|
174
|
-
function buildRetryRequest(
|
|
175
|
-
originalReq: HttpRequest<unknown>,
|
|
176
|
-
tokenDelivery: string,
|
|
177
|
-
newToken?: string,
|
|
178
|
-
): HttpRequest<unknown> {
|
|
179
|
-
if (tokenDelivery === 'json' && newToken && newToken !== 'success') {
|
|
180
|
-
return originalReq.clone({
|
|
181
|
-
setHeaders: { Authorization: `Bearer ${newToken}` },
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
return originalReq.clone();
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Class-based interceptor for NgModule compatibility.
|
|
189
|
-
*/
|
|
190
|
-
export class AuthInterceptor {
|
|
191
|
-
intercept(req: HttpRequest<unknown>, next: HttpHandlerFn) {
|
|
192
|
-
return authInterceptor(req, next);
|
|
193
|
-
}
|
|
194
|
-
}
|