@ahoo-wang/fetcher-cosec 3.13.15 → 3.15.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/dist/authorizationRequestInterceptor.d.ts.map +1 -1
- package/dist/authorizationResponseInterceptor.d.ts.map +1 -1
- package/dist/cosecConfigurer.d.ts.map +1 -1
- package/dist/cosecRequestInterceptor.d.ts.map +1 -1
- package/dist/deviceIdStorage.d.ts +1 -1
- package/dist/deviceIdStorage.d.ts.map +1 -1
- package/dist/forbiddenErrorInterceptor.d.ts.map +1 -1
- package/dist/index.es.js +269 -925
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +2 -2
- package/dist/index.umd.js.map +1 -1
- package/dist/jwtToken.d.ts.map +1 -1
- package/dist/jwtTokenManager.d.ts.map +1 -1
- package/dist/resourceAttributionRequestInterceptor.d.ts.map +1 -1
- package/dist/spaceIdProvider.d.ts +1 -1
- package/dist/spaceIdProvider.d.ts.map +1 -1
- package/dist/tokenRefresher.d.ts.map +1 -1
- package/dist/tokenStorage.d.ts +1 -1
- package/dist/tokenStorage.d.ts.map +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/unauthorizedErrorInterceptor.d.ts.map +1 -1
- package/package.json +9 -9
package/dist/index.es.js
CHANGED
|
@@ -1,929 +1,273 @@
|
|
|
1
|
-
import { DEFAULT_INTERCEPTOR_ORDER_STEP as
|
|
2
|
-
import { nanoid as
|
|
3
|
-
import { KeyStorage as
|
|
4
|
-
import { BroadcastTypedEventBus as
|
|
5
|
-
|
|
1
|
+
import { DEFAULT_INTERCEPTOR_ORDER_STEP as e, FetcherError as t, ResultExtractors as n, URL_RESOLVE_INTERCEPTOR_ORDER as r } from "@ahoo-wang/fetcher";
|
|
2
|
+
import { nanoid as i } from "nanoid";
|
|
3
|
+
import { KeyStorage as a, typedIdentitySerializer as o } from "@ahoo-wang/fetcher-storage";
|
|
4
|
+
import { BroadcastTypedEventBus as s, SerialTypedEventBus as c } from "@ahoo-wang/fetcher-eventbus";
|
|
5
|
+
//#region src/types.ts
|
|
6
|
+
var l = class {
|
|
7
|
+
static {
|
|
8
|
+
this.DEVICE_ID = "CoSec-Device-Id";
|
|
9
|
+
}
|
|
10
|
+
static {
|
|
11
|
+
this.APP_ID = "CoSec-App-Id";
|
|
12
|
+
}
|
|
13
|
+
static {
|
|
14
|
+
this.SPACE_ID = "CoSec-Space-Id";
|
|
15
|
+
}
|
|
16
|
+
static {
|
|
17
|
+
this.AUTHORIZATION = "Authorization";
|
|
18
|
+
}
|
|
19
|
+
static {
|
|
20
|
+
this.REQUEST_ID = "CoSec-Request-Id";
|
|
21
|
+
}
|
|
22
|
+
}, u = class {
|
|
23
|
+
static {
|
|
24
|
+
this.UNAUTHORIZED = 401;
|
|
25
|
+
}
|
|
26
|
+
static {
|
|
27
|
+
this.FORBIDDEN = 403;
|
|
28
|
+
}
|
|
29
|
+
}, d = {
|
|
30
|
+
ALLOW: {
|
|
31
|
+
authorized: !0,
|
|
32
|
+
reason: "Allow"
|
|
33
|
+
},
|
|
34
|
+
EXPLICIT_DENY: {
|
|
35
|
+
authorized: !1,
|
|
36
|
+
reason: "Explicit Deny"
|
|
37
|
+
},
|
|
38
|
+
IMPLICIT_DENY: {
|
|
39
|
+
authorized: !1,
|
|
40
|
+
reason: "Implicit Deny"
|
|
41
|
+
},
|
|
42
|
+
TOKEN_EXPIRED: {
|
|
43
|
+
authorized: !1,
|
|
44
|
+
reason: "Token Expired"
|
|
45
|
+
},
|
|
46
|
+
TOO_MANY_REQUESTS: {
|
|
47
|
+
authorized: !1,
|
|
48
|
+
reason: "Too Many Requests"
|
|
49
|
+
}
|
|
50
|
+
}, f = class {
|
|
51
|
+
generateId() {
|
|
52
|
+
return i();
|
|
53
|
+
}
|
|
54
|
+
}, p = new f(), m = { resolveSpaceId: () => null }, h = "cosec-space-id", g = class extends a {
|
|
55
|
+
constructor({ key: e = h, eventBus: t = new s({ delegate: new c(h) }), ...n } = {}) {
|
|
56
|
+
super({
|
|
57
|
+
key: e,
|
|
58
|
+
eventBus: t,
|
|
59
|
+
...n,
|
|
60
|
+
serializer: o()
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}, _ = class {
|
|
64
|
+
constructor(e) {
|
|
65
|
+
this.options = e;
|
|
66
|
+
}
|
|
67
|
+
resolveSpaceId(e) {
|
|
68
|
+
return this.options.spacedResourcePredicate.test(e) ? this.options.spaceIdStorage.get() : null;
|
|
69
|
+
}
|
|
70
|
+
}, v = "CoSecRequestInterceptor", y = -(2 ** 53 - 1) + e, b = "Ignore-Refresh-Token", x = class {
|
|
71
|
+
constructor({ appId: e, deviceIdStorage: t, spaceIdProvider: n }) {
|
|
72
|
+
this.name = v, this.order = y, this.appId = e, this.deviceIdStorage = t, this.spaceIdProvider = n ?? m;
|
|
73
|
+
}
|
|
74
|
+
async intercept(e) {
|
|
75
|
+
let t = p.generateId(), n = this.deviceIdStorage.getOrCreate(), r = this.spaceIdProvider.resolveSpaceId(e), i = e.ensureRequestHeaders();
|
|
76
|
+
i[l.APP_ID] = this.appId, i[l.DEVICE_ID] = n, i[l.REQUEST_ID] = t, r && (i[l.SPACE_ID] = r);
|
|
77
|
+
}
|
|
78
|
+
}, S = "AuthorizationRequestInterceptor", C = y + e, w = class {
|
|
79
|
+
constructor(e) {
|
|
80
|
+
this.options = e, this.name = S, this.order = C;
|
|
81
|
+
}
|
|
82
|
+
async intercept(e) {
|
|
83
|
+
let t = this.options.tokenManager.currentToken, n = e.ensureRequestHeaders();
|
|
84
|
+
!t || n[l.AUTHORIZATION] || (!e.attributes.has("Ignore-Refresh-Token") && t.isRefreshNeeded && t.isRefreshable && await this.options.tokenManager.refresh(), t = this.options.tokenManager.currentToken, t && (n[l.AUTHORIZATION] = `Bearer ${t.access.token}`));
|
|
85
|
+
}
|
|
86
|
+
}, T = "AuthorizationResponseInterceptor", E = -(2 ** 53 - 1) + 1e3, D = class {
|
|
87
|
+
constructor(e) {
|
|
88
|
+
this.options = e, this.name = T, this.order = E;
|
|
89
|
+
}
|
|
90
|
+
async intercept(e) {
|
|
91
|
+
let t = e.response;
|
|
92
|
+
if (t && t.status === u.UNAUTHORIZED && this.options.tokenManager.isRefreshable) try {
|
|
93
|
+
await this.options.tokenManager.refresh(), await e.fetcher.interceptors.exchange(e);
|
|
94
|
+
} catch (e) {
|
|
95
|
+
throw this.options.tokenManager.tokenStorage.remove(), e;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}, O = "cosec-device-id", k = class extends a {
|
|
99
|
+
constructor({ key: e = O, eventBus: t = new s({ delegate: new c(O) }), ...n } = {}) {
|
|
100
|
+
super({
|
|
101
|
+
key: e,
|
|
102
|
+
eventBus: t,
|
|
103
|
+
...n,
|
|
104
|
+
serializer: o()
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
generateDeviceId() {
|
|
108
|
+
return p.generateId();
|
|
109
|
+
}
|
|
110
|
+
getOrCreate() {
|
|
111
|
+
let e = this.get();
|
|
112
|
+
return e || (e = this.generateDeviceId(), this.set(e)), e;
|
|
113
|
+
}
|
|
114
|
+
}, A = "ForbiddenErrorInterceptor", j = 0, M = class {
|
|
115
|
+
constructor(e) {
|
|
116
|
+
this.options = e, this.name = A, this.order = 0;
|
|
117
|
+
}
|
|
118
|
+
async intercept(e) {
|
|
119
|
+
e.response?.status === u.FORBIDDEN && await this.options.onForbidden(e);
|
|
120
|
+
}
|
|
121
|
+
}, N = class e extends t {
|
|
122
|
+
constructor(t, n) {
|
|
123
|
+
super("Refresh token failed.", n), this.token = t, this.name = "RefreshTokenError", Object.setPrototypeOf(this, e.prototype);
|
|
124
|
+
}
|
|
125
|
+
}, P = class {
|
|
126
|
+
constructor(e, t) {
|
|
127
|
+
this.tokenStorage = e, this.tokenRefresher = t;
|
|
128
|
+
}
|
|
129
|
+
get currentToken() {
|
|
130
|
+
return this.tokenStorage.get();
|
|
131
|
+
}
|
|
132
|
+
async refresh() {
|
|
133
|
+
let e = this.currentToken;
|
|
134
|
+
if (!e) throw Error("No token found");
|
|
135
|
+
return this.refreshInProgress ||= this.tokenRefresher.refresh(e.token).then((e) => {
|
|
136
|
+
this.tokenStorage.setCompositeToken(e);
|
|
137
|
+
}).catch((t) => {
|
|
138
|
+
throw this.tokenStorage.remove(), new N(e, t);
|
|
139
|
+
}).finally(() => {
|
|
140
|
+
this.refreshInProgress = void 0;
|
|
141
|
+
}), this.refreshInProgress;
|
|
142
|
+
}
|
|
143
|
+
get isRefreshNeeded() {
|
|
144
|
+
return this.currentToken ? this.currentToken.isRefreshNeeded : !1;
|
|
145
|
+
}
|
|
146
|
+
get isRefreshable() {
|
|
147
|
+
return this.currentToken ? this.currentToken.isRefreshable : !1;
|
|
148
|
+
}
|
|
149
|
+
}, F = "tenantId", I = "ownerId", L = "ResourceAttributionRequestInterceptor", R = r - e, z = class {
|
|
150
|
+
constructor({ tenantId: e = F, ownerId: t = I, tokenStorage: n }) {
|
|
151
|
+
this.name = L, this.order = R, this.tenantIdPathKey = e, this.ownerIdPathKey = t, this.tokenStorage = n;
|
|
152
|
+
}
|
|
153
|
+
intercept(e) {
|
|
154
|
+
let t = this.tokenStorage.get();
|
|
155
|
+
if (!t) return;
|
|
156
|
+
let n = t.access.payload;
|
|
157
|
+
if (!n || !n.tenantId && !n.sub) return;
|
|
158
|
+
let r = e.fetcher.urlBuilder.urlTemplateResolver.extractPathParams(e.request.url), i = this.tenantIdPathKey, a = e.ensureRequestUrlParams().path, o = n.tenantId;
|
|
159
|
+
o && r.includes(i) && !a[i] && (a[i] = o);
|
|
160
|
+
let s = this.ownerIdPathKey, c = n.sub;
|
|
161
|
+
c && r.includes(s) && !a[s] && (a[s] = c);
|
|
162
|
+
}
|
|
6
163
|
};
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
let
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Generate a unique request ID.
|
|
23
|
-
*
|
|
24
|
-
* @returns A unique request ID
|
|
25
|
-
*/
|
|
26
|
-
generateId() {
|
|
27
|
-
return b();
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
const P = new z(), k = {
|
|
31
|
-
resolveSpaceId: () => null
|
|
32
|
-
}, O = "cosec-space-id";
|
|
33
|
-
class ue extends p {
|
|
34
|
-
/**
|
|
35
|
-
* Creates a new SpaceIdStorage instance.
|
|
36
|
-
*
|
|
37
|
-
* @param options - Optional configuration options for the storage
|
|
38
|
-
* @param options.key - The storage key (defaults to DEFAULT_COSEC_SPACE_ID_KEY)
|
|
39
|
-
* @param options.eventBus - Custom event bus for cross-tab communication
|
|
40
|
-
* @param options.storage - Custom storage implementation (defaults to localStorage)
|
|
41
|
-
* @param options.serializer - Custom serializer (defaults to identity serializer)
|
|
42
|
-
* @param options.defaultValue - Default value when no space ID is stored
|
|
43
|
-
*
|
|
44
|
-
* @throws Error if the underlying storage is unavailable
|
|
45
|
-
*
|
|
46
|
-
* @example
|
|
47
|
-
* ```typescript
|
|
48
|
-
* // Default configuration
|
|
49
|
-
* const storage = new SpaceIdStorage();
|
|
50
|
-
*
|
|
51
|
-
* // With custom options
|
|
52
|
-
* const storage = new SpaceIdStorage({
|
|
53
|
-
* key: 'custom-space-key',
|
|
54
|
-
* storage: sessionStorage,
|
|
55
|
-
* defaultValue: 'default-space'
|
|
56
|
-
* });
|
|
57
|
-
* ```
|
|
58
|
-
*/
|
|
59
|
-
constructor({
|
|
60
|
-
key: e = O,
|
|
61
|
-
eventBus: t = new R({
|
|
62
|
-
delegate: new T(O)
|
|
63
|
-
}),
|
|
64
|
-
...r
|
|
65
|
-
} = {}) {
|
|
66
|
-
super({ key: e, eventBus: t, ...r, serializer: N() });
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
class de {
|
|
70
|
-
/**
|
|
71
|
-
* Creates a new DefaultSpaceIdProvider instance.
|
|
72
|
-
*
|
|
73
|
-
* @param options - The configuration options containing the predicate and storage
|
|
74
|
-
* @param options.spacedResourcePredicate - Determines which requests require space scoping
|
|
75
|
-
* @param options.spaceIdStorage - Provides access to the persisted space identifier
|
|
76
|
-
*
|
|
77
|
-
* @example
|
|
78
|
-
* ```typescript
|
|
79
|
-
* const provider = new DefaultSpaceIdProvider({
|
|
80
|
-
* spacedResourcePredicate: {
|
|
81
|
-
* test: (exchange) => exchange.request.url.includes('/api/')
|
|
82
|
-
* },
|
|
83
|
-
* spaceIdStorage: new SpaceIdStorage()
|
|
84
|
-
* });
|
|
85
|
-
* ```
|
|
86
|
-
*/
|
|
87
|
-
constructor(e) {
|
|
88
|
-
this.options = e;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Resolves the space identifier for a given fetch exchange.
|
|
92
|
-
*
|
|
93
|
-
* This method first checks if the request requires space scoping by
|
|
94
|
-
* evaluating the predicate. If the predicate returns true, it retrieves
|
|
95
|
-
* the space ID from storage. Otherwise, it returns null.
|
|
96
|
-
*
|
|
97
|
-
* @param exchange - The fetch exchange containing the HTTP request details
|
|
98
|
-
* @returns The space identifier if the request requires scoping and one exists,
|
|
99
|
-
* otherwise null
|
|
100
|
-
*
|
|
101
|
-
* @example
|
|
102
|
-
* ```typescript
|
|
103
|
-
* const provider = new DefaultSpaceIdProvider({
|
|
104
|
-
* spacedResourcePredicate: {
|
|
105
|
-
* test: (exchange) => exchange.request.url.includes('/spaced/')
|
|
106
|
-
* },
|
|
107
|
-
* spaceIdStorage: new SpaceIdStorage()
|
|
108
|
-
* });
|
|
109
|
-
*
|
|
110
|
-
* // For a request to /api/spaces/workspace-alpha/resources
|
|
111
|
-
* const exchange = createMockExchange({ url: '/api/spaces/workspace-alpha/resources' });
|
|
112
|
-
* const spaceId = provider.resolveSpaceId(exchange); // Returns stored space ID
|
|
113
|
-
*
|
|
114
|
-
* // For a request to /api/public/resources
|
|
115
|
-
* const publicExchange = createMockExchange({ url: '/api/public/resources' });
|
|
116
|
-
* const noSpace = provider.resolveSpaceId(publicExchange); // Returns null
|
|
117
|
-
* ```
|
|
118
|
-
*/
|
|
119
|
-
resolveSpaceId(e) {
|
|
120
|
-
return this.options.spacedResourcePredicate.test(e) ? this.options.spaceIdStorage.get() : null;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
const M = "CoSecRequestInterceptor", A = Number.MIN_SAFE_INTEGER + I, D = "Ignore-Refresh-Token";
|
|
124
|
-
class q {
|
|
125
|
-
/**
|
|
126
|
-
* Creates a new CoSecRequestInterceptor instance.
|
|
127
|
-
*
|
|
128
|
-
* @param options - The CoSec configuration options
|
|
129
|
-
* @param options.appId - The application identifier for CoSec authentication
|
|
130
|
-
* @param options.deviceIdStorage - Storage for device identifier management
|
|
131
|
-
* @param options.spaceIdProvider - Optional provider for space identification
|
|
132
|
-
*
|
|
133
|
-
* @throws Error if appId is empty or not provided
|
|
134
|
-
* @throws Error if deviceIdStorage is not provided
|
|
135
|
-
*
|
|
136
|
-
* @example
|
|
137
|
-
* ```typescript
|
|
138
|
-
* // Basic instantiation
|
|
139
|
-
* const interceptor = new CoSecRequestInterceptor({
|
|
140
|
-
* appId: 'my-web-app',
|
|
141
|
-
* deviceIdStorage: new DeviceIdStorage()
|
|
142
|
-
* });
|
|
143
|
-
*
|
|
144
|
-
* // With custom device ID storage
|
|
145
|
-
* const customStorage = new DeviceIdStorage({
|
|
146
|
-
* key: 'custom-device-key',
|
|
147
|
-
* storage: sessionStorage
|
|
148
|
-
* });
|
|
149
|
-
* const interceptorWithCustom = new CoSecRequestInterceptor({
|
|
150
|
-
* appId: 'my-web-app',
|
|
151
|
-
* deviceIdStorage: customStorage
|
|
152
|
-
* });
|
|
153
|
-
*
|
|
154
|
-
* // With space support
|
|
155
|
-
* const spaceStorage = new SpaceIdStorage();
|
|
156
|
-
* const spaceProvider = new DefaultSpaceIdProvider({
|
|
157
|
-
* spacedResourcePredicate: { test: (e) => true },
|
|
158
|
-
* spaceIdStorage: spaceStorage
|
|
159
|
-
* });
|
|
160
|
-
* const interceptorWithSpace = new CoSecRequestInterceptor({
|
|
161
|
-
* appId: 'my-web-app',
|
|
162
|
-
* deviceIdStorage,
|
|
163
|
-
* spaceIdProvider: spaceProvider
|
|
164
|
-
* });
|
|
165
|
-
* ```
|
|
166
|
-
*/
|
|
167
|
-
constructor({
|
|
168
|
-
appId: e,
|
|
169
|
-
deviceIdStorage: t,
|
|
170
|
-
spaceIdProvider: r
|
|
171
|
-
}) {
|
|
172
|
-
this.name = M, this.order = A, this.appId = e, this.deviceIdStorage = t, this.spaceIdProvider = r ?? k;
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* Intercepts outgoing requests to add CoSec authentication headers.
|
|
176
|
-
*
|
|
177
|
-
* This method is called by the Fetcher interceptor chain for each request.
|
|
178
|
-
* It enriches the request with security headers before the HTTP request is made.
|
|
179
|
-
*
|
|
180
|
-
* **Header Injection Process:**
|
|
181
|
-
* 1. Generates a unique request ID using the idGenerator
|
|
182
|
-
* 2. Retrieves or creates a device ID from deviceIdStorage
|
|
183
|
-
* 3. Resolves space ID from spaceIdProvider (if applicable)
|
|
184
|
-
* 4. Adds all headers to the request
|
|
185
|
-
*
|
|
186
|
-
* @param exchange - The fetch exchange containing the request to process
|
|
187
|
-
*
|
|
188
|
-
* @remarks
|
|
189
|
-
* **Header Values:**
|
|
190
|
-
* - `CoSec-App-Id`: Always set to the configured appId
|
|
191
|
-
* - `CoSec-Device-Id`: Retrieved from storage or newly generated
|
|
192
|
-
* - `CoSec-Request-Id`: Unique per request (not persisted)
|
|
193
|
-
* - `CoSec-Space-Id`: Set only if spaceIdProvider returns a value
|
|
194
|
-
*
|
|
195
|
-
* **Error Handling:**
|
|
196
|
-
* - If deviceIdStorage.getOrCreate() throws, the error propagates
|
|
197
|
-
* - If spaceIdProvider.resolveSpaceId() throws, the error propagates
|
|
198
|
-
* - If ensureRequestHeaders() fails, the error propagates
|
|
199
|
-
*
|
|
200
|
-
* **Thread Safety:**
|
|
201
|
-
* - The idGenerator is safe for concurrent use
|
|
202
|
-
* - deviceIdStorage handles concurrent access internally
|
|
203
|
-
* - Each request gets a unique request ID
|
|
204
|
-
*
|
|
205
|
-
* @example
|
|
206
|
-
* ```typescript
|
|
207
|
-
* // The interceptor is called automatically by Fetcher
|
|
208
|
-
* const fetcher = new Fetcher()
|
|
209
|
-
* .addInterceptor(new CoSecRequestInterceptor({
|
|
210
|
-
* appId: 'my-app',
|
|
211
|
-
* deviceIdStorage: new DeviceIdStorage()
|
|
212
|
-
* }));
|
|
213
|
-
*
|
|
214
|
-
* // This request will have CoSec headers added
|
|
215
|
-
* const response = await fetcher.get('/api/data');
|
|
216
|
-
* ```
|
|
217
|
-
*
|
|
218
|
-
* @example
|
|
219
|
-
* ```typescript
|
|
220
|
-
* // Headers added to outgoing request:
|
|
221
|
-
* // GET /api/users HTTP/1.1
|
|
222
|
-
* // Host: api.example.com
|
|
223
|
-
* // CoSec-App-Id: my-app
|
|
224
|
-
* // CoSec-Device-Id: abc123xyz789
|
|
225
|
-
* // CoSec-Request-Id: req_abc123def456
|
|
226
|
-
* // CoSec-Space-Id: workspace-alpha (if space resolved)
|
|
227
|
-
* ```
|
|
228
|
-
*/
|
|
229
|
-
async intercept(e) {
|
|
230
|
-
const t = P.generateId(), r = this.deviceIdStorage.getOrCreate(), o = this.spaceIdProvider.resolveSpaceId(e), n = e.ensureRequestHeaders();
|
|
231
|
-
n[i.APP_ID] = this.appId, n[i.DEVICE_ID] = r, n[i.REQUEST_ID] = t, o && (n[i.SPACE_ID] = o);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
const F = "AuthorizationRequestInterceptor", K = A + I;
|
|
235
|
-
class B {
|
|
236
|
-
/**
|
|
237
|
-
* Creates an AuthorizationRequestInterceptor instance.
|
|
238
|
-
*
|
|
239
|
-
* @param options - Configuration options containing the token manager
|
|
240
|
-
*/
|
|
241
|
-
constructor(e) {
|
|
242
|
-
this.options = e, this.name = F, this.order = K;
|
|
243
|
-
}
|
|
244
|
-
/**
|
|
245
|
-
* Intercepts the request exchange to add authorization headers.
|
|
246
|
-
*
|
|
247
|
-
* This method performs the following operations:
|
|
248
|
-
* 1. Checks if a token exists and if Authorization header is already set
|
|
249
|
-
* 2. Refreshes the token if needed, possible, and not explicitly ignored
|
|
250
|
-
* 3. Adds the Authorization header with Bearer token if a token is available
|
|
251
|
-
*
|
|
252
|
-
* @param exchange - The fetch exchange containing request information
|
|
253
|
-
* @returns Promise that resolves when the interception is complete
|
|
254
|
-
*/
|
|
255
|
-
async intercept(e) {
|
|
256
|
-
let t = this.options.tokenManager.currentToken;
|
|
257
|
-
const r = e.ensureRequestHeaders();
|
|
258
|
-
!t || r[i.AUTHORIZATION] || (!e.attributes.has(D) && t.isRefreshNeeded && t.isRefreshable && await this.options.tokenManager.refresh(), t = this.options.tokenManager.currentToken, t && (r[i.AUTHORIZATION] = `Bearer ${t.access.token}`));
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
const H = "AuthorizationResponseInterceptor", Z = Number.MIN_SAFE_INTEGER + 1e3;
|
|
262
|
-
class x {
|
|
263
|
-
/**
|
|
264
|
-
* Creates a new AuthorizationResponseInterceptor instance.
|
|
265
|
-
* @param options - The CoSec configuration options including token storage and refresher
|
|
266
|
-
*/
|
|
267
|
-
constructor(e) {
|
|
268
|
-
this.options = e, this.name = H, this.order = Z;
|
|
269
|
-
}
|
|
270
|
-
/**
|
|
271
|
-
* Intercepts the response and handles unauthorized responses by refreshing tokens.
|
|
272
|
-
* @param exchange - The fetch exchange containing request and response information
|
|
273
|
-
*/
|
|
274
|
-
async intercept(e) {
|
|
275
|
-
const t = e.response;
|
|
276
|
-
if (t && t.status === h.UNAUTHORIZED && this.options.tokenManager.isRefreshable)
|
|
277
|
-
try {
|
|
278
|
-
await this.options.tokenManager.refresh(), await e.fetcher.interceptors.exchange(e);
|
|
279
|
-
} catch (r) {
|
|
280
|
-
throw this.options.tokenManager.tokenStorage.remove(), r;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
const _ = "cosec-device-id";
|
|
285
|
-
class J extends p {
|
|
286
|
-
constructor({
|
|
287
|
-
key: e = _,
|
|
288
|
-
eventBus: t = new R({
|
|
289
|
-
delegate: new T(_)
|
|
290
|
-
}),
|
|
291
|
-
...r
|
|
292
|
-
} = {}) {
|
|
293
|
-
super({ key: e, eventBus: t, ...r, serializer: N() });
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* Generate a new device ID.
|
|
297
|
-
*
|
|
298
|
-
* @returns A newly generated device ID
|
|
299
|
-
*/
|
|
300
|
-
generateDeviceId() {
|
|
301
|
-
return P.generateId();
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* Get or create a device ID.
|
|
305
|
-
*
|
|
306
|
-
* @returns The existing device ID if available, otherwise a newly generated one
|
|
307
|
-
*/
|
|
308
|
-
getOrCreate() {
|
|
309
|
-
let e = this.get();
|
|
310
|
-
return e || (e = this.generateDeviceId(), this.set(e)), e;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
const L = "ForbiddenErrorInterceptor", Q = 0;
|
|
314
|
-
class Y {
|
|
315
|
-
/**
|
|
316
|
-
* Creates a new ForbiddenErrorInterceptor instance.
|
|
317
|
-
*
|
|
318
|
-
* @param options - Configuration options containing the callback to handle forbidden responses.
|
|
319
|
-
* Must include the `onForbidden` callback function.
|
|
320
|
-
*
|
|
321
|
-
* @throws Will throw an error if options are not provided or if `onForbidden` callback is missing.
|
|
322
|
-
*
|
|
323
|
-
* @example
|
|
324
|
-
* ```typescript
|
|
325
|
-
* const interceptor = new ForbiddenErrorInterceptor({
|
|
326
|
-
* onForbidden: async (exchange) => {
|
|
327
|
-
* // Handle forbidden access
|
|
328
|
-
* }
|
|
329
|
-
* });
|
|
330
|
-
* ```
|
|
331
|
-
*/
|
|
332
|
-
constructor(e) {
|
|
333
|
-
this.options = e, this.name = L, this.order = Q;
|
|
334
|
-
}
|
|
335
|
-
/**
|
|
336
|
-
* Intercepts fetch exchanges to detect and handle forbidden (403) responses.
|
|
337
|
-
*
|
|
338
|
-
* This method examines the response status code and invokes the configured `onForbidden`
|
|
339
|
-
* callback when a 403 Forbidden response is detected. The method is asynchronous to
|
|
340
|
-
* allow the callback to perform async operations like API calls, redirects, or UI updates.
|
|
341
|
-
*
|
|
342
|
-
* The interceptor only acts on responses with status code 403. Other error codes are
|
|
343
|
-
* ignored and passed through to other error interceptors in the chain.
|
|
344
|
-
*
|
|
345
|
-
* @param exchange - The fetch exchange containing request, response, and error information
|
|
346
|
-
* to be inspected for forbidden status codes. The exchange object provides
|
|
347
|
-
* access to the original request, response details, and any error information.
|
|
348
|
-
* @returns Promise that resolves when the forbidden error handling is complete.
|
|
349
|
-
* Returns void - the method does not modify the exchange or return values.
|
|
350
|
-
*
|
|
351
|
-
* @remarks
|
|
352
|
-
* - Only responds to HTTP 403 status codes
|
|
353
|
-
* - Does not retry requests or modify responses
|
|
354
|
-
* - Allows async operations in the callback
|
|
355
|
-
* - Does not throw exceptions - delegates all error handling to the callback
|
|
356
|
-
* - Safe to use with other error interceptors
|
|
357
|
-
*
|
|
358
|
-
* @example
|
|
359
|
-
* ```typescript
|
|
360
|
-
* // The intercept method is called automatically by the fetcher
|
|
361
|
-
* // No manual invocation needed - this is for documentation purposes
|
|
362
|
-
* const interceptor = new ForbiddenErrorInterceptor({
|
|
363
|
-
* onForbidden: async (exchange) => {
|
|
364
|
-
* // exchange.response.status === 403
|
|
365
|
-
* // exchange.request contains original request details
|
|
366
|
-
* await handleForbiddenAccess(exchange);
|
|
367
|
-
* }
|
|
368
|
-
* });
|
|
369
|
-
* ```
|
|
370
|
-
*/
|
|
371
|
-
async intercept(e) {
|
|
372
|
-
e.response?.status === h.FORBIDDEN && await this.options.onForbidden(e);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
class d extends y {
|
|
376
|
-
constructor(e, t) {
|
|
377
|
-
super("Refresh token failed.", t), this.token = e, this.name = "RefreshTokenError", Object.setPrototypeOf(this, d.prototype);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
class j {
|
|
381
|
-
/**
|
|
382
|
-
* Creates a new JwtTokenManager instance
|
|
383
|
-
* @param tokenStorage The storage used to persist tokens
|
|
384
|
-
* @param tokenRefresher The refresher used to refresh expired tokens
|
|
385
|
-
*/
|
|
386
|
-
constructor(e, t) {
|
|
387
|
-
this.tokenStorage = e, this.tokenRefresher = t;
|
|
388
|
-
}
|
|
389
|
-
/**
|
|
390
|
-
* Gets the current JWT composite token from storage
|
|
391
|
-
* @returns The current token or null if none exists
|
|
392
|
-
*/
|
|
393
|
-
get currentToken() {
|
|
394
|
-
return this.tokenStorage.get();
|
|
395
|
-
}
|
|
396
|
-
/**
|
|
397
|
-
* Refreshes the JWT token
|
|
398
|
-
* @returns Promise that resolves when refresh is complete
|
|
399
|
-
* @throws Error if no token is found or refresh fails
|
|
400
|
-
*/
|
|
401
|
-
async refresh() {
|
|
402
|
-
const e = this.currentToken;
|
|
403
|
-
if (!e)
|
|
404
|
-
throw new Error("No token found");
|
|
405
|
-
return this.refreshInProgress ? this.refreshInProgress : (this.refreshInProgress = this.tokenRefresher.refresh(e.token).then((t) => {
|
|
406
|
-
this.tokenStorage.setCompositeToken(t);
|
|
407
|
-
}).catch((t) => {
|
|
408
|
-
throw this.tokenStorage.remove(), new d(e, t);
|
|
409
|
-
}).finally(() => {
|
|
410
|
-
this.refreshInProgress = void 0;
|
|
411
|
-
}), this.refreshInProgress);
|
|
412
|
-
}
|
|
413
|
-
/**
|
|
414
|
-
* Indicates if the current token needs to be refreshed
|
|
415
|
-
* @returns true if the access token is expired and needs refresh, false otherwise
|
|
416
|
-
*/
|
|
417
|
-
get isRefreshNeeded() {
|
|
418
|
-
return this.currentToken ? this.currentToken.isRefreshNeeded : !1;
|
|
419
|
-
}
|
|
420
|
-
/**
|
|
421
|
-
* Indicates if the current token can be refreshed
|
|
422
|
-
* @returns true if the refresh token is still valid, false otherwise
|
|
423
|
-
*/
|
|
424
|
-
get isRefreshable() {
|
|
425
|
-
return this.currentToken ? this.currentToken.isRefreshable : !1;
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
const G = "tenantId", V = "ownerId", W = "ResourceAttributionRequestInterceptor", X = m - I;
|
|
429
|
-
class $ {
|
|
430
|
-
/**
|
|
431
|
-
* Creates a new ResourceAttributionRequestInterceptor
|
|
432
|
-
* @param options - Configuration options for resource attribution including tenantId, ownerId and tokenStorage
|
|
433
|
-
*/
|
|
434
|
-
constructor({
|
|
435
|
-
tenantId: e = G,
|
|
436
|
-
ownerId: t = V,
|
|
437
|
-
tokenStorage: r
|
|
438
|
-
}) {
|
|
439
|
-
this.name = W, this.order = X, this.tenantIdPathKey = e, this.ownerIdPathKey = t, this.tokenStorage = r;
|
|
440
|
-
}
|
|
441
|
-
/**
|
|
442
|
-
* Intercepts outgoing requests and automatically adds tenant and owner ID path parameters
|
|
443
|
-
* if they are defined in the URL template but not provided in the request.
|
|
444
|
-
* @param exchange - The fetch exchange containing the request information
|
|
445
|
-
*/
|
|
446
|
-
intercept(e) {
|
|
447
|
-
const t = this.tokenStorage.get();
|
|
448
|
-
if (!t)
|
|
449
|
-
return;
|
|
450
|
-
const r = t.access.payload;
|
|
451
|
-
if (!r || !r.tenantId && !r.sub)
|
|
452
|
-
return;
|
|
453
|
-
const o = e.fetcher.urlBuilder.urlTemplateResolver.extractPathParams(
|
|
454
|
-
e.request.url
|
|
455
|
-
), n = this.tenantIdPathKey, c = e.ensureRequestUrlParams().path, l = r.tenantId;
|
|
456
|
-
l && o.includes(n) && !c[n] && (c[n] = l);
|
|
457
|
-
const E = this.ownerIdPathKey, f = r.sub;
|
|
458
|
-
f && o.includes(E) && !c[E] && (c[E] = f);
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
function w(s) {
|
|
462
|
-
try {
|
|
463
|
-
if (typeof s != "string")
|
|
464
|
-
return null;
|
|
465
|
-
const e = s.split(".");
|
|
466
|
-
if (e.length !== 3)
|
|
467
|
-
return null;
|
|
468
|
-
const r = e[1].replace(/-/g, "+").replace(/_/g, "/"), o = r.padEnd(
|
|
469
|
-
r.length + (4 - r.length % 4) % 4,
|
|
470
|
-
"="
|
|
471
|
-
), n = decodeURIComponent(
|
|
472
|
-
atob(o).split("").map(function(c) {
|
|
473
|
-
return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
|
|
474
|
-
}).join("")
|
|
475
|
-
);
|
|
476
|
-
return JSON.parse(n);
|
|
477
|
-
} catch (e) {
|
|
478
|
-
return console.error("Failed to parse JWT token", e), null;
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
function ee(s, e = 0) {
|
|
482
|
-
const t = typeof s == "string" ? w(s) : s;
|
|
483
|
-
if (!t)
|
|
484
|
-
return !0;
|
|
485
|
-
const r = t.exp;
|
|
486
|
-
return r ? Date.now() / 1e3 > r - e : !1;
|
|
487
|
-
}
|
|
488
|
-
class g {
|
|
489
|
-
/**
|
|
490
|
-
* Creates a new JwtToken instance.
|
|
491
|
-
*
|
|
492
|
-
* Parses the JWT token string to extract the payload and stores the early period
|
|
493
|
-
* for expiration checks.
|
|
494
|
-
*
|
|
495
|
-
* @param token The raw JWT token string to parse
|
|
496
|
-
* @param earlyPeriod The early expiration period in milliseconds (default: 0).
|
|
497
|
-
* Tokens are considered expired this many milliseconds before their actual expiration time.
|
|
498
|
-
*
|
|
499
|
-
* @throws Will not throw but payload will be null if token parsing fails
|
|
500
|
-
*/
|
|
501
|
-
constructor(e, t = 0) {
|
|
502
|
-
this.token = e, this.earlyPeriod = t, this.payload = w(e);
|
|
503
|
-
}
|
|
504
|
-
/**
|
|
505
|
-
* Checks if the token is expired.
|
|
506
|
-
*
|
|
507
|
-
* Considers both the token's expiration time and the early period.
|
|
508
|
-
* Returns true if the payload is null (parsing failed) or if the token is expired.
|
|
509
|
-
*
|
|
510
|
-
* @returns true if the token is expired or invalid, false otherwise
|
|
511
|
-
*/
|
|
512
|
-
get isExpired() {
|
|
513
|
-
return this.payload ? ee(this.payload, this.earlyPeriod) : !0;
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
class U {
|
|
517
|
-
/**
|
|
518
|
-
* Creates a new JwtCompositeToken instance.
|
|
519
|
-
*
|
|
520
|
-
* Initializes both access and refresh token instances with the provided early period.
|
|
521
|
-
*
|
|
522
|
-
* @param token The composite token containing access and refresh token strings
|
|
523
|
-
* @param earlyPeriod The early expiration period in milliseconds (default: 0)
|
|
524
|
-
*/
|
|
525
|
-
constructor(e, t = 0) {
|
|
526
|
-
this.token = e, this.earlyPeriod = t, this.access = new g(e.accessToken, t), this.refresh = new g(e.refreshToken, t);
|
|
527
|
-
}
|
|
528
|
-
/**
|
|
529
|
-
* Checks if the access token needs to be refreshed.
|
|
530
|
-
*
|
|
531
|
-
* @returns true if the access token is expired, false otherwise
|
|
532
|
-
*/
|
|
533
|
-
get isRefreshNeeded() {
|
|
534
|
-
return this.access.isExpired;
|
|
535
|
-
}
|
|
536
|
-
/**
|
|
537
|
-
* Checks if the refresh token is still valid and can be used to refresh the access token.
|
|
538
|
-
*
|
|
539
|
-
* @returns true if the refresh token is not expired, false otherwise
|
|
540
|
-
*/
|
|
541
|
-
get isRefreshable() {
|
|
542
|
-
return !this.refresh.isExpired;
|
|
543
|
-
}
|
|
544
|
-
/**
|
|
545
|
-
* Checks if the user is currently authenticated (access token is valid).
|
|
546
|
-
*
|
|
547
|
-
* @returns true if the access token is not expired, false otherwise
|
|
548
|
-
*/
|
|
549
|
-
get authenticated() {
|
|
550
|
-
return !this.access.isExpired;
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
class C {
|
|
554
|
-
/**
|
|
555
|
-
* Creates a new JwtCompositeTokenSerializer instance.
|
|
556
|
-
*
|
|
557
|
-
* @param earlyPeriod The early expiration period in milliseconds to use for deserialized tokens (default: 0)
|
|
558
|
-
*/
|
|
559
|
-
constructor(e = 0) {
|
|
560
|
-
this.earlyPeriod = e;
|
|
561
|
-
}
|
|
562
|
-
/**
|
|
563
|
-
* Deserializes a JSON string to a JwtCompositeToken.
|
|
564
|
-
*
|
|
565
|
-
* Parses the JSON string and creates a new JwtCompositeToken instance with the stored tokens.
|
|
566
|
-
*
|
|
567
|
-
* @param value The JSON string representation of a composite token
|
|
568
|
-
* @returns A JwtCompositeToken instance
|
|
569
|
-
* @throws SyntaxError if the JSON string is invalid
|
|
570
|
-
* @throws Error if the parsed object doesn't match the expected CompositeToken structure
|
|
571
|
-
*/
|
|
572
|
-
deserialize(e) {
|
|
573
|
-
const t = JSON.parse(e);
|
|
574
|
-
return new U(t, this.earlyPeriod);
|
|
575
|
-
}
|
|
576
|
-
/**
|
|
577
|
-
* Serializes a JwtCompositeToken to a JSON string.
|
|
578
|
-
*
|
|
579
|
-
* Converts the composite token to a JSON string for storage.
|
|
580
|
-
*
|
|
581
|
-
* @param value The JwtCompositeToken to serialize
|
|
582
|
-
* @returns A JSON string representation of the composite token
|
|
583
|
-
*/
|
|
584
|
-
serialize(e) {
|
|
585
|
-
return JSON.stringify(e.token);
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
const Ee = new C(), S = "cosec-token";
|
|
589
|
-
class te extends p {
|
|
590
|
-
/**
|
|
591
|
-
* Creates a new TokenStorage instance.
|
|
592
|
-
* @param options - Configuration options for the token storage.
|
|
593
|
-
* @param options.key - The storage key for tokens. Defaults to DEFAULT_COSEC_TOKEN_KEY.
|
|
594
|
-
* @param options.eventBus - Event bus for token change notifications. Defaults to a BroadcastTypedEventBus with SerialTypedEventBus delegate.
|
|
595
|
-
* @param options.earlyPeriod - Early period for token refresh in milliseconds. Defaults to 0.
|
|
596
|
-
* @param reset - Additional options passed to KeyStorage.
|
|
597
|
-
*/
|
|
598
|
-
constructor({
|
|
599
|
-
key: e = S,
|
|
600
|
-
eventBus: t = new R({
|
|
601
|
-
delegate: new T(S)
|
|
602
|
-
}),
|
|
603
|
-
earlyPeriod: r = 0,
|
|
604
|
-
...o
|
|
605
|
-
} = {}) {
|
|
606
|
-
super({
|
|
607
|
-
key: e,
|
|
608
|
-
eventBus: t,
|
|
609
|
-
...o,
|
|
610
|
-
serializer: new C(r)
|
|
611
|
-
}), this.earlyPeriod = r;
|
|
612
|
-
}
|
|
613
|
-
/**
|
|
614
|
-
* Sets a composite token in storage.
|
|
615
|
-
* Converts the composite token to a JwtCompositeToken and stores it.
|
|
616
|
-
* @deprecated Use signIn() instead for better semantic clarity.
|
|
617
|
-
* @param compositeToken - The composite token containing access and refresh tokens.
|
|
618
|
-
*/
|
|
619
|
-
setCompositeToken(e) {
|
|
620
|
-
this.signIn(e);
|
|
621
|
-
}
|
|
622
|
-
/**
|
|
623
|
-
* Signs in by storing the composite token.
|
|
624
|
-
* @param compositeToken - The composite token to store for authentication.
|
|
625
|
-
*/
|
|
626
|
-
signIn(e) {
|
|
627
|
-
this.set(new U(e, this.earlyPeriod));
|
|
628
|
-
}
|
|
629
|
-
/**
|
|
630
|
-
* Signs out by removing the stored token.
|
|
631
|
-
* Clears the token from storage.
|
|
632
|
-
*/
|
|
633
|
-
signOut() {
|
|
634
|
-
this.remove();
|
|
635
|
-
}
|
|
636
|
-
/**
|
|
637
|
-
* Checks if the user is authenticated.
|
|
638
|
-
* @returns true if a valid token is present and authenticated, false otherwise.
|
|
639
|
-
*/
|
|
640
|
-
get authenticated() {
|
|
641
|
-
return this.get()?.authenticated === !0;
|
|
642
|
-
}
|
|
643
|
-
/**
|
|
644
|
-
* Gets the current user's JWT payload.
|
|
645
|
-
* @returns The JWT payload of the current user if authenticated, null otherwise.
|
|
646
|
-
*/
|
|
647
|
-
get currentUser() {
|
|
648
|
-
return this.authenticated ? this.get()?.access.payload ?? null : null;
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
const re = "UnauthorizedErrorInterceptor", se = 0;
|
|
652
|
-
class oe {
|
|
653
|
-
/**
|
|
654
|
-
* Creates a new UnauthorizedErrorInterceptor instance.
|
|
655
|
-
*
|
|
656
|
-
* @param options - Configuration options containing the callback to handle unauthorized responses
|
|
657
|
-
*/
|
|
658
|
-
constructor(e) {
|
|
659
|
-
this.options = e, this.name = re, this.order = se;
|
|
660
|
-
}
|
|
661
|
-
/**
|
|
662
|
-
* Intercepts fetch exchanges to detect and handle unauthorized (401) responses
|
|
663
|
-
* and RefreshTokenError exceptions.
|
|
664
|
-
*
|
|
665
|
-
* This method checks if the response status is 401 (Unauthorized) or if the exchange
|
|
666
|
-
* contains an error of type `RefreshTokenError`. If either condition is met, it invokes
|
|
667
|
-
* the configured `onUnauthorized` callback with the exchange details. The method
|
|
668
|
-
* does not return a value or throw exceptions - all error handling is delegated
|
|
669
|
-
* to the callback function.
|
|
670
|
-
*
|
|
671
|
-
* @param exchange - The fetch exchange containing request, response, and error information
|
|
672
|
-
* to be inspected for unauthorized status codes or refresh token errors
|
|
673
|
-
* @returns {void} This method does not return a value
|
|
674
|
-
*
|
|
675
|
-
* @example
|
|
676
|
-
* ```typescript
|
|
677
|
-
* const interceptor = new UnauthorizedErrorInterceptor({
|
|
678
|
-
* onUnauthorized: (exchange) => {
|
|
679
|
-
* // Custom logic here
|
|
680
|
-
* }
|
|
681
|
-
* });
|
|
682
|
-
* ```
|
|
683
|
-
*/
|
|
684
|
-
async intercept(e) {
|
|
685
|
-
(e.response?.status === h.UNAUTHORIZED || e.error instanceof d) && await this.options.onUnauthorized(e);
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
class Ie {
|
|
689
|
-
/**
|
|
690
|
-
* Creates a new CoSecConfigurer instance with the provided configuration.
|
|
691
|
-
*
|
|
692
|
-
* The constructor performs dependency initialization:
|
|
693
|
-
* - Creates or wraps tokenStorage and deviceIdStorage with defaults
|
|
694
|
-
* - Initializes spaceIdProvider (defaults to NoneSpaceIdProvider)
|
|
695
|
-
* - Conditionally creates tokenManager only if tokenRefresher is provided
|
|
696
|
-
*
|
|
697
|
-
* @param config - CoSec configuration object containing all settings
|
|
698
|
-
*
|
|
699
|
-
* @throws Error if appId is not provided
|
|
700
|
-
* @throws Error if provided dependencies are invalid
|
|
701
|
-
*
|
|
702
|
-
* @example
|
|
703
|
-
* ```typescript
|
|
704
|
-
* // Full configuration
|
|
705
|
-
* const configurer = new CoSecConfigurer({
|
|
706
|
-
* appId: 'my-app',
|
|
707
|
-
* tokenStorage: customTokenStorage,
|
|
708
|
-
* deviceIdStorage: customDeviceStorage,
|
|
709
|
-
* tokenRefresher: myRefresher,
|
|
710
|
-
* spaceIdProvider: mySpaceProvider,
|
|
711
|
-
* onUnauthorized: handle401,
|
|
712
|
-
* onForbidden: handle403
|
|
713
|
-
* });
|
|
714
|
-
*
|
|
715
|
-
* // Minimal configuration
|
|
716
|
-
* const configurer = new CoSecConfigurer({
|
|
717
|
-
* appId: 'my-app'
|
|
718
|
-
* });
|
|
719
|
-
*
|
|
720
|
-
* // Custom storage with default refresh
|
|
721
|
-
* const configurer = new CoSecConfigurer({
|
|
722
|
-
* appId: 'my-app',
|
|
723
|
-
* tokenStorage: encryptedStorage,
|
|
724
|
-
* deviceIdStorage: customDeviceStorage
|
|
725
|
-
* });
|
|
726
|
-
* ```
|
|
727
|
-
*/
|
|
728
|
-
constructor(e) {
|
|
729
|
-
this.config = e, this.tokenStorage = e.tokenStorage ?? new te(), this.deviceIdStorage = e.deviceIdStorage ?? new J(), this.spaceIdProvider = e.spaceIdProvider ?? k, e.tokenRefresher && (this.tokenManager = new j(
|
|
730
|
-
this.tokenStorage,
|
|
731
|
-
e.tokenRefresher
|
|
732
|
-
));
|
|
733
|
-
}
|
|
734
|
-
/**
|
|
735
|
-
* Applies CoSec interceptors to the specified Fetcher instance.
|
|
736
|
-
*
|
|
737
|
-
* This method registers all configured interceptors with the Fetcher's
|
|
738
|
-
* interceptor chain. The registration is conditional based on the config:
|
|
739
|
-
*
|
|
740
|
-
* **Always Registered (Headers & Attribution):**
|
|
741
|
-
* - CoSecRequestInterceptor: Injects security headers
|
|
742
|
-
* - ResourceAttributionRequestInterceptor: Adds tenant attribution parameters
|
|
743
|
-
*
|
|
744
|
-
* **Conditional Registration (Authentication):**
|
|
745
|
-
* - AuthorizationRequestInterceptor: Adds Bearer token [requires tokenRefresher]
|
|
746
|
-
* - AuthorizationResponseInterceptor: Handles 401 refresh [requires tokenRefresher]
|
|
747
|
-
*
|
|
748
|
-
* **Conditional Registration (Error Handling):**
|
|
749
|
-
* - UnauthorizedErrorInterceptor: Custom 401 handling [requires onUnauthorized]
|
|
750
|
-
* - ForbiddenErrorInterceptor: Custom 403 handling [requires onForbidden]
|
|
751
|
-
*
|
|
752
|
-
* @param fetcher - The Fetcher instance to configure with CoSec interceptors
|
|
753
|
-
*
|
|
754
|
-
* @throws Error if fetcher is null or undefined
|
|
755
|
-
*
|
|
756
|
-
* @example
|
|
757
|
-
* ```typescript
|
|
758
|
-
* const fetcher = new Fetcher({ baseUrl: 'https://api.example.com' });
|
|
759
|
-
*
|
|
760
|
-
* const configurer = new CoSecConfigurer({
|
|
761
|
-
* appId: 'my-app',
|
|
762
|
-
* tokenRefresher: myRefresher,
|
|
763
|
-
* onUnauthorized: () => redirectToLogin(),
|
|
764
|
-
* onForbidden: () => showError()
|
|
765
|
-
* });
|
|
766
|
-
*
|
|
767
|
-
* configurer.applyTo(fetcher);
|
|
768
|
-
*
|
|
769
|
-
* // Now fetcher has all CoSec interceptors configured
|
|
770
|
-
* const data = await fetcher.get('/api/data');
|
|
771
|
-
* ```
|
|
772
|
-
*
|
|
773
|
-
* @example
|
|
774
|
-
* ```typescript
|
|
775
|
-
* // Applying to multiple fetchers
|
|
776
|
-
* const apiFetcher = new Fetcher({ baseUrl: 'https://api.example.com' });
|
|
777
|
-
* const adminFetcher = new Fetcher({ baseUrl: 'https://admin.example.com' });
|
|
778
|
-
*
|
|
779
|
-
* const configurer = new CoSecConfigurer({
|
|
780
|
-
* appId: 'my-app',
|
|
781
|
-
* tokenRefresher: myRefresher,
|
|
782
|
-
* onForbidden: showAccessDenied
|
|
783
|
-
* });
|
|
784
|
-
*
|
|
785
|
-
* // Apply same configuration to multiple fetchers
|
|
786
|
-
* configurer.applyTo(apiFetcher);
|
|
787
|
-
* configurer.applyTo(adminFetcher);
|
|
788
|
-
* ```
|
|
789
|
-
*/
|
|
790
|
-
applyTo(e) {
|
|
791
|
-
e.interceptors.request.use(
|
|
792
|
-
new q({
|
|
793
|
-
appId: this.config.appId,
|
|
794
|
-
deviceIdStorage: this.deviceIdStorage,
|
|
795
|
-
spaceIdProvider: this.spaceIdProvider
|
|
796
|
-
})
|
|
797
|
-
), e.interceptors.request.use(
|
|
798
|
-
new $({
|
|
799
|
-
tokenStorage: this.tokenStorage
|
|
800
|
-
})
|
|
801
|
-
), this.tokenManager && (e.interceptors.request.use(
|
|
802
|
-
new B({
|
|
803
|
-
tokenManager: this.tokenManager
|
|
804
|
-
})
|
|
805
|
-
), e.interceptors.response.use(
|
|
806
|
-
new x({
|
|
807
|
-
tokenManager: this.tokenManager
|
|
808
|
-
})
|
|
809
|
-
)), this.config.onUnauthorized && e.interceptors.error.use(
|
|
810
|
-
new oe({
|
|
811
|
-
onUnauthorized: this.config.onUnauthorized
|
|
812
|
-
})
|
|
813
|
-
), this.config.onForbidden && e.interceptors.error.use(
|
|
814
|
-
new Y({
|
|
815
|
-
onForbidden: this.config.onForbidden
|
|
816
|
-
})
|
|
817
|
-
);
|
|
818
|
-
}
|
|
164
|
+
//#endregion
|
|
165
|
+
//#region src/jwts.ts
|
|
166
|
+
function B(e) {
|
|
167
|
+
try {
|
|
168
|
+
if (typeof e != "string") return null;
|
|
169
|
+
let t = e.split(".");
|
|
170
|
+
if (t.length !== 3) return null;
|
|
171
|
+
let n = t[1].replace(/-/g, "+").replace(/_/g, "/"), r = n.padEnd(n.length + (4 - n.length % 4) % 4, "="), i = decodeURIComponent(atob(r).split("").map(function(e) {
|
|
172
|
+
return "%" + ("00" + e.charCodeAt(0).toString(16)).slice(-2);
|
|
173
|
+
}).join(""));
|
|
174
|
+
return JSON.parse(i);
|
|
175
|
+
} catch (e) {
|
|
176
|
+
return console.error("Failed to parse JWT token", e), null;
|
|
177
|
+
}
|
|
819
178
|
}
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
* @param {Fetcher} options.fetcher - The HTTP client instance used for making refresh requests.
|
|
826
|
-
* @param {string} options.endpoint - The URL endpoint for token refresh operations.
|
|
827
|
-
*
|
|
828
|
-
* @example
|
|
829
|
-
* ```typescript
|
|
830
|
-
* const refresher = new CoSecTokenRefresher({
|
|
831
|
-
* fetcher: myFetcherInstance,
|
|
832
|
-
* endpoint: '/api/v1/auth/refresh'
|
|
833
|
-
* });
|
|
834
|
-
* ```
|
|
835
|
-
*/
|
|
836
|
-
constructor(e) {
|
|
837
|
-
this.options = e;
|
|
838
|
-
}
|
|
839
|
-
/**
|
|
840
|
-
* Refreshes the given composite token by sending a POST request to the configured endpoint.
|
|
841
|
-
*
|
|
842
|
-
* This method sends the current token pair to the refresh endpoint and expects
|
|
843
|
-
* a new CompositeToken in response.
|
|
844
|
-
*
|
|
845
|
-
* **CRITICAL**: The request includes a special
|
|
846
|
-
* attribute `attributes: new Map([[IGNORE_REFRESH_TOKEN_ATTRIBUTE_KEY, true]])`
|
|
847
|
-
* to prevent infinite loops. Without this attribute, the request interceptor
|
|
848
|
-
* may attempt to refresh the token again, causing a recursive loop.
|
|
849
|
-
*
|
|
850
|
-
* @param {CompositeToken} token - The composite token to refresh, containing both access and refresh tokens.
|
|
851
|
-
* @returns {Promise<CompositeToken>} A promise that resolves to a new CompositeToken with refreshed tokens.
|
|
852
|
-
* @throws {Error} Throws an error if the HTTP request fails, the server returns an error status, or the response cannot be parsed as JSON.
|
|
853
|
-
* @throws {NetworkError} Throws a network error if there are connectivity issues.
|
|
854
|
-
* @throws {AuthenticationError} Throws an authentication error if the refresh token is invalid or expired.
|
|
855
|
-
*
|
|
856
|
-
* @example
|
|
857
|
-
* ```typescript
|
|
858
|
-
* const refresher = new CoSecTokenRefresher({ fetcher, endpoint: '/auth/refresh' });
|
|
859
|
-
* const token = { accessToken: 'expired-token', refreshToken: 'valid-refresh-token' };
|
|
860
|
-
*
|
|
861
|
-
* try {
|
|
862
|
-
* const newToken = await refresher.refresh(token);
|
|
863
|
-
* console.log('Refreshed access token:', newToken.accessToken);
|
|
864
|
-
* } catch (error) {
|
|
865
|
-
* console.error('Token refresh failed:', error.message);
|
|
866
|
-
* }
|
|
867
|
-
* ```
|
|
868
|
-
*
|
|
869
|
-
* @warning **Important**: Always include the `IGNORE_REFRESH_TOKEN_ATTRIBUTE_KEY` attribute
|
|
870
|
-
* in refresh requests to avoid infinite loops caused by recursive token refresh attempts.
|
|
871
|
-
*/
|
|
872
|
-
refresh(e) {
|
|
873
|
-
return this.options.fetcher.post(
|
|
874
|
-
this.options.endpoint,
|
|
875
|
-
{
|
|
876
|
-
body: e
|
|
877
|
-
},
|
|
878
|
-
{
|
|
879
|
-
resultExtractor: v.Json,
|
|
880
|
-
attributes: /* @__PURE__ */ new Map([[D, !0]])
|
|
881
|
-
}
|
|
882
|
-
);
|
|
883
|
-
}
|
|
179
|
+
function V(e, t = 0) {
|
|
180
|
+
let n = typeof e == "string" ? B(e) : e;
|
|
181
|
+
if (!n) return !0;
|
|
182
|
+
let r = n.exp;
|
|
183
|
+
return r ? Date.now() / 1e3 > r - t : !1;
|
|
884
184
|
}
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
185
|
+
//#endregion
|
|
186
|
+
//#region src/jwtToken.ts
|
|
187
|
+
var H = class {
|
|
188
|
+
constructor(e, t = 0) {
|
|
189
|
+
this.token = e, this.earlyPeriod = t, this.payload = B(e);
|
|
190
|
+
}
|
|
191
|
+
get isExpired() {
|
|
192
|
+
return this.payload ? V(this.payload, this.earlyPeriod) : !0;
|
|
193
|
+
}
|
|
194
|
+
}, U = class {
|
|
195
|
+
constructor(e, t = 0) {
|
|
196
|
+
this.token = e, this.earlyPeriod = t, this.access = new H(e.accessToken, t), this.refresh = new H(e.refreshToken, t);
|
|
197
|
+
}
|
|
198
|
+
get isRefreshNeeded() {
|
|
199
|
+
return this.access.isExpired;
|
|
200
|
+
}
|
|
201
|
+
get isRefreshable() {
|
|
202
|
+
return !this.refresh.isExpired;
|
|
203
|
+
}
|
|
204
|
+
get authenticated() {
|
|
205
|
+
return !this.access.isExpired;
|
|
206
|
+
}
|
|
207
|
+
}, W = class {
|
|
208
|
+
constructor(e = 0) {
|
|
209
|
+
this.earlyPeriod = e;
|
|
210
|
+
}
|
|
211
|
+
deserialize(e) {
|
|
212
|
+
return new U(JSON.parse(e), this.earlyPeriod);
|
|
213
|
+
}
|
|
214
|
+
serialize(e) {
|
|
215
|
+
return JSON.stringify(e.token);
|
|
216
|
+
}
|
|
217
|
+
}, G = new W(), K = "cosec-token", q = class extends a {
|
|
218
|
+
constructor({ key: e = K, eventBus: t = new s({ delegate: new c(K) }), earlyPeriod: n = 0, ...r } = {}) {
|
|
219
|
+
super({
|
|
220
|
+
key: e,
|
|
221
|
+
eventBus: t,
|
|
222
|
+
...r,
|
|
223
|
+
serializer: new W(n)
|
|
224
|
+
}), this.earlyPeriod = n;
|
|
225
|
+
}
|
|
226
|
+
setCompositeToken(e) {
|
|
227
|
+
this.signIn(e);
|
|
228
|
+
}
|
|
229
|
+
signIn(e) {
|
|
230
|
+
this.set(new U(e, this.earlyPeriod));
|
|
231
|
+
}
|
|
232
|
+
signOut() {
|
|
233
|
+
this.remove();
|
|
234
|
+
}
|
|
235
|
+
get authenticated() {
|
|
236
|
+
return this.get()?.authenticated === !0;
|
|
237
|
+
}
|
|
238
|
+
get currentUser() {
|
|
239
|
+
return this.authenticated ? this.get()?.access.payload ?? null : null;
|
|
240
|
+
}
|
|
241
|
+
}, J = "UnauthorizedErrorInterceptor", Y = 0, X = class {
|
|
242
|
+
constructor(e) {
|
|
243
|
+
this.options = e, this.name = J, this.order = 0;
|
|
244
|
+
}
|
|
245
|
+
async intercept(e) {
|
|
246
|
+
(e.response?.status === u.UNAUTHORIZED || e.error instanceof N) && await this.options.onUnauthorized(e);
|
|
247
|
+
}
|
|
248
|
+
}, Z = class {
|
|
249
|
+
constructor(e) {
|
|
250
|
+
this.config = e, this.tokenStorage = e.tokenStorage ?? new q(), this.deviceIdStorage = e.deviceIdStorage ?? new k(), this.spaceIdProvider = e.spaceIdProvider ?? m, e.tokenRefresher && (this.tokenManager = new P(this.tokenStorage, e.tokenRefresher));
|
|
251
|
+
}
|
|
252
|
+
applyTo(e) {
|
|
253
|
+
e.interceptors.request.use(new x({
|
|
254
|
+
appId: this.config.appId,
|
|
255
|
+
deviceIdStorage: this.deviceIdStorage,
|
|
256
|
+
spaceIdProvider: this.spaceIdProvider
|
|
257
|
+
})), e.interceptors.request.use(new z({ tokenStorage: this.tokenStorage })), this.tokenManager && (e.interceptors.request.use(new w({ tokenManager: this.tokenManager })), e.interceptors.response.use(new D({ tokenManager: this.tokenManager }))), this.config.onUnauthorized && e.interceptors.error.use(new X({ onUnauthorized: this.config.onUnauthorized })), this.config.onForbidden && e.interceptors.error.use(new M({ onForbidden: this.config.onForbidden }));
|
|
258
|
+
}
|
|
259
|
+
}, Q = class {
|
|
260
|
+
constructor(e) {
|
|
261
|
+
this.options = e;
|
|
262
|
+
}
|
|
263
|
+
refresh(e) {
|
|
264
|
+
return this.options.fetcher.post(this.options.endpoint, { body: e }, {
|
|
265
|
+
resultExtractor: n.Json,
|
|
266
|
+
attributes: new Map([[b, !0]])
|
|
267
|
+
});
|
|
268
|
+
}
|
|
928
269
|
};
|
|
929
|
-
//#
|
|
270
|
+
//#endregion
|
|
271
|
+
export { S as AUTHORIZATION_REQUEST_INTERCEPTOR_NAME, C as AUTHORIZATION_REQUEST_INTERCEPTOR_ORDER, T as AUTHORIZATION_RESPONSE_INTERCEPTOR_NAME, E as AUTHORIZATION_RESPONSE_INTERCEPTOR_ORDER, w as AuthorizationRequestInterceptor, D as AuthorizationResponseInterceptor, d as AuthorizeResults, v as COSEC_REQUEST_INTERCEPTOR_NAME, y as COSEC_REQUEST_INTERCEPTOR_ORDER, Z as CoSecConfigurer, l as CoSecHeaders, x as CoSecRequestInterceptor, Q as CoSecTokenRefresher, O as DEFAULT_COSEC_DEVICE_ID_KEY, h as DEFAULT_COSEC_SPACE_ID_KEY, K as DEFAULT_COSEC_TOKEN_KEY, _ as DefaultSpaceIdProvider, k as DeviceIdStorage, A as FORBIDDEN_ERROR_INTERCEPTOR_NAME, j as FORBIDDEN_ERROR_INTERCEPTOR_ORDER, M as ForbiddenErrorInterceptor, b as IGNORE_REFRESH_TOKEN_ATTRIBUTE_KEY, U as JwtCompositeToken, W as JwtCompositeTokenSerializer, H as JwtToken, P as JwtTokenManager, f as NanoIdGenerator, m as NoneSpaceIdProvider, L as RESOURCE_ATTRIBUTION_REQUEST_INTERCEPTOR_NAME, R as RESOURCE_ATTRIBUTION_REQUEST_INTERCEPTOR_ORDER, N as RefreshTokenError, z as ResourceAttributionRequestInterceptor, u as ResponseCodes, g as SpaceIdStorage, q as TokenStorage, J as UNAUTHORIZED_ERROR_INTERCEPTOR_NAME, Y as UNAUTHORIZED_ERROR_INTERCEPTOR_ORDER, X as UnauthorizedErrorInterceptor, p as idGenerator, V as isTokenExpired, G as jwtCompositeTokenSerializer, B as parseJwtPayload };
|
|
272
|
+
|
|
273
|
+
//# sourceMappingURL=index.es.js.map
|