@seamless-auth/oidc-provider 0.0.0-stage → 0.1.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/CHANGELOG.md +65 -0
- package/LICENSE +201 -0
- package/README.md +273 -2
- package/dist/adapter.d.ts +44 -0
- package/dist/adapter.js +249 -0
- package/dist/adapter.js.map +1 -0
- package/dist/apiClient.d.ts +35 -0
- package/dist/apiClient.js +217 -0
- package/dist/apiClient.js.map +1 -0
- package/dist/authRequirements.d.ts +37 -0
- package/dist/authRequirements.js +58 -0
- package/dist/authRequirements.js.map +1 -0
- package/dist/clientSecrets.d.ts +10 -0
- package/dist/clientSecrets.js +102 -0
- package/dist/clientSecrets.js.map +1 -0
- package/dist/configCache.d.ts +14 -0
- package/dist/configCache.js +45 -0
- package/dist/configCache.js.map +1 -0
- package/dist/createOidcEdge.d.ts +45 -0
- package/dist/createOidcEdge.js +132 -0
- package/dist/createOidcEdge.js.map +1 -0
- package/dist/deviceFlow.d.ts +53 -0
- package/dist/deviceFlow.js +168 -0
- package/dist/deviceFlow.js.map +1 -0
- package/dist/env.d.ts +16 -0
- package/dist/env.js +36 -0
- package/dist/env.js.map +1 -0
- package/dist/errors.d.ts +18 -0
- package/dist/errors.js +40 -0
- package/dist/errors.js.map +1 -0
- package/dist/firstPartySession.d.ts +21 -0
- package/dist/firstPartySession.js +28 -0
- package/dist/firstPartySession.js.map +1 -0
- package/dist/hostedPages.d.ts +51 -0
- package/dist/hostedPages.js +172 -0
- package/dist/hostedPages.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/interactions.d.ts +17 -0
- package/dist/interactions.js +209 -0
- package/dist/interactions.js.map +1 -0
- package/dist/jwtAccessTokens.d.ts +23 -0
- package/dist/jwtAccessTokens.js +125 -0
- package/dist/jwtAccessTokens.js.map +1 -0
- package/dist/logout.d.ts +22 -0
- package/dist/logout.js +86 -0
- package/dist/logout.js.map +1 -0
- package/dist/originGuard.d.ts +10 -0
- package/dist/originGuard.js +32 -0
- package/dist/originGuard.js.map +1 -0
- package/dist/pkce.d.ts +16 -0
- package/dist/pkce.js +32 -0
- package/dist/pkce.js.map +1 -0
- package/dist/providerConfig.d.ts +50 -0
- package/dist/providerConfig.js +305 -0
- package/dist/providerConfig.js.map +1 -0
- package/dist/rateLimit.d.ts +13 -0
- package/dist/rateLimit.js +36 -0
- package/dist/rateLimit.js.map +1 -0
- package/dist/refreshReuse.d.ts +3 -0
- package/dist/refreshReuse.js +56 -0
- package/dist/refreshReuse.js.map +1 -0
- package/dist/requestContext.d.ts +40 -0
- package/dist/requestContext.js +43 -0
- package/dist/requestContext.js.map +1 -0
- package/dist/resourceServers.d.ts +16 -0
- package/dist/resourceServers.js +59 -0
- package/dist/resourceServers.js.map +1 -0
- package/dist/resumeCookie.d.ts +8 -0
- package/dist/resumeCookie.js +46 -0
- package/dist/resumeCookie.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +36 -0
- package/dist/server.js.map +1 -0
- package/dist/signingKey.d.ts +29 -0
- package/dist/signingKey.js +142 -0
- package/dist/signingKey.js.map +1 -0
- package/dist/startup.d.ts +4 -0
- package/dist/startup.js +19 -0
- package/dist/startup.js.map +1 -0
- package/dist/tokenAudit.d.ts +4 -0
- package/dist/tokenAudit.js +42 -0
- package/dist/tokenAudit.js.map +1 -0
- package/dist/tokenLifetimes.d.ts +31 -0
- package/dist/tokenLifetimes.js +51 -0
- package/dist/tokenLifetimes.js.map +1 -0
- package/dist/ui/assets/index-CydLXRvo.js +18 -0
- package/dist/ui/assets/index-y3LME-Gv.css +1 -0
- package/dist/ui/index.html +15 -0
- package/package.json +115 -4
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2026 Fells Code, LLC
|
|
3
|
+
* Licensed under the Apache License, Version 2.0
|
|
4
|
+
* See LICENSE file in the project root for full license information
|
|
5
|
+
*/
|
|
6
|
+
import { interactionPolicy, } from 'oidc-provider';
|
|
7
|
+
import { createApiAdapterFactory } from './adapter.js';
|
|
8
|
+
import { ACR_VALUES, authRequirements, hasRequirements, meetsAcr, unmetRequirements, } from './authRequirements.js';
|
|
9
|
+
import { createDevicePages, DEVICE_CODE_TTL_SECONDS } from './deviceFlow.js';
|
|
10
|
+
import { FIRST_PARTY_CLIENT_PROPERTY } from './interactions.js';
|
|
11
|
+
import { storeJwtAccessToken } from './jwtAccessTokens.js';
|
|
12
|
+
import { createLogoutPages } from './logout.js';
|
|
13
|
+
import { NONCE_REQUIRED_PROPERTY, PKCE_REQUIRED_PROPERTY, pkceRequired } from './pkce.js';
|
|
14
|
+
import { currentRequestContext } from './requestContext.js';
|
|
15
|
+
import { createResourceIndicators } from './resourceServers.js';
|
|
16
|
+
import { accessTokenTtl, CLIENT_TTL_PROPERTIES, grantTtl, idTokenTtl, refreshTokenTtl, } from './tokenLifetimes.js';
|
|
17
|
+
export const PROVIDER_COOKIE_NAMES = {
|
|
18
|
+
session: '__Host-oidc_session',
|
|
19
|
+
interaction: '__Host-oidc_interaction',
|
|
20
|
+
// The library scopes this one to the resume path. hostOnlyResumeCookie moves it to Path=/.
|
|
21
|
+
resume: '__Host-oidc_resume',
|
|
22
|
+
};
|
|
23
|
+
const ASYMMETRIC_ALGS = ['RS256', 'PS256', 'ES256', 'Ed25519', 'EdDSA'];
|
|
24
|
+
function isAsymmetricAlg(alg) {
|
|
25
|
+
return ASYMMETRIC_ALGS.includes(alg);
|
|
26
|
+
}
|
|
27
|
+
// The routes where the library loads the browser session's account before the grant. The
|
|
28
|
+
// device flow's verification page runs the same authorization checks in the browser.
|
|
29
|
+
const AUTHORIZATION_ROUTES = new Set([
|
|
30
|
+
'authorization',
|
|
31
|
+
'resume',
|
|
32
|
+
'code_verification',
|
|
33
|
+
'device_resume',
|
|
34
|
+
]);
|
|
35
|
+
export function createFindAccount(client) {
|
|
36
|
+
return async (ctx, sub) => {
|
|
37
|
+
// The API releases claims only for the session and client this request serves.
|
|
38
|
+
const clientId = ctx.oidc?.client?.clientId;
|
|
39
|
+
const sessionId = currentRequestContext()?.sessionId;
|
|
40
|
+
const claims = clientId && sessionId ? await client.account(sub, { clientId, sessionId }) : undefined;
|
|
41
|
+
if (claims) {
|
|
42
|
+
const { org_id, ...rest } = claims;
|
|
43
|
+
return {
|
|
44
|
+
accountId: claims.sub,
|
|
45
|
+
claims: () => ({ ...rest, ...(org_id ? { org_id } : {}) }),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
// A client this session has not used yet has no grant, so the API will not describe the
|
|
49
|
+
// account. At authorization the library only needs to know the provider session's own
|
|
50
|
+
// account; claims are read at the token and userinfo endpoints, once the grant exists.
|
|
51
|
+
if (AUTHORIZATION_ROUTES.has(ctx.oidc?.route) && ctx.oidc.session?.accountId === sub) {
|
|
52
|
+
return { accountId: sub, claims: () => ({ sub }) };
|
|
53
|
+
}
|
|
54
|
+
return undefined;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* First-party clients have consent implied, so their grant is created or topped up with what
|
|
59
|
+
* they ask for. Third-party clients (ADR D10) see the consent page on every authorization: only the
|
|
60
|
+
* grant the interaction just produced counts, never one remembered on the provider session.
|
|
61
|
+
*/
|
|
62
|
+
export async function loadExistingGrant(ctx) {
|
|
63
|
+
const { oidc } = ctx;
|
|
64
|
+
const client = oidc.client;
|
|
65
|
+
if (!client) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
const firstParty = client[FIRST_PARTY_CLIENT_PROPERTY] === true;
|
|
69
|
+
const grantId = oidc.result?.consent?.grantId ??
|
|
70
|
+
(firstParty ? oidc.session?.grantIdFor(client.clientId) : undefined);
|
|
71
|
+
const accountId = oidc.session?.accountId;
|
|
72
|
+
if (!grantId && !(firstParty && accountId)) {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
const grant = grantId
|
|
76
|
+
? await oidc.provider.Grant.find(grantId)
|
|
77
|
+
: new oidc.provider.Grant({ accountId, clientId: client.clientId });
|
|
78
|
+
if (!grant || !firstParty) {
|
|
79
|
+
return grant;
|
|
80
|
+
}
|
|
81
|
+
const granted = new Set(grant.getOIDCScope().split(' '));
|
|
82
|
+
const scopes = [...oidc.requestParamOIDCScopes].filter((scope) => !granted.has(scope));
|
|
83
|
+
const grantedClaims = new Set(grant.getOIDCClaims());
|
|
84
|
+
const claims = [...oidc.requestParamClaims].filter((claim) => !grantedClaims.has(claim));
|
|
85
|
+
let resourcesChanged = false;
|
|
86
|
+
for (const [indicator, server] of Object.entries(oidc.resourceServers ?? {})) {
|
|
87
|
+
const offered = new Set((server.scope ?? '').split(' '));
|
|
88
|
+
const held = new Set((grant.getResourceScope(indicator) ?? '').split(' '));
|
|
89
|
+
const missing = [...oidc.requestParamScopes].filter((scope) => offered.has(scope) && !held.has(scope));
|
|
90
|
+
if (missing.length > 0) {
|
|
91
|
+
grant.addResourceScope(indicator, missing.join(' '));
|
|
92
|
+
resourcesChanged = true;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (!grantId || scopes.length > 0 || claims.length > 0 || resourcesChanged) {
|
|
96
|
+
grant.addOIDCScope(scopes.join(' '));
|
|
97
|
+
grant.addOIDCClaims(claims);
|
|
98
|
+
await grant.save();
|
|
99
|
+
}
|
|
100
|
+
return grant;
|
|
101
|
+
}
|
|
102
|
+
function adoptAuthContext(session, context) {
|
|
103
|
+
session.acr = context.acr ?? undefined;
|
|
104
|
+
session.amr = context.amr.length > 0 ? [...context.amr] : undefined;
|
|
105
|
+
session.loginTs = context.authTime;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Replaces the library's max_age and essential acr checks, which read the provider session's
|
|
109
|
+
* copy of the login and want an exact acr match. The API's auth context is the record: it
|
|
110
|
+
* moves with every step-up, and /oidc/sign refuses any acr, amr or auth_time that differs.
|
|
111
|
+
*/
|
|
112
|
+
export function authContextCheck(client) {
|
|
113
|
+
return new interactionPolicy.Check('auth_context', 'the requested authentication could not be obtained', async (ctx) => {
|
|
114
|
+
const { oidc } = ctx;
|
|
115
|
+
const accountId = oidc.session?.accountId;
|
|
116
|
+
if (!oidc.session || !accountId || !oidc.client) {
|
|
117
|
+
return interactionPolicy.Check.NO_NEED_TO_PROMPT;
|
|
118
|
+
}
|
|
119
|
+
const requirements = authRequirements(oidc.params ?? {});
|
|
120
|
+
const login = oidc.result?.login;
|
|
121
|
+
// A device approval always signs in again (see the DeviceCode upsert in adapter.ts).
|
|
122
|
+
if (oidc.route === 'code_verification' && !login) {
|
|
123
|
+
return interactionPolicy.Check.REQUEST_PROMPT;
|
|
124
|
+
}
|
|
125
|
+
if (login) {
|
|
126
|
+
// The interaction copied these from the API after checking freshness itself.
|
|
127
|
+
return requirements.acr && !meetsAcr(login.acr, requirements.acr)
|
|
128
|
+
? interactionPolicy.Check.REQUEST_PROMPT
|
|
129
|
+
: interactionPolicy.Check.NO_NEED_TO_PROMPT;
|
|
130
|
+
}
|
|
131
|
+
const sessionId = currentRequestContext()?.sessionId;
|
|
132
|
+
const context = sessionId
|
|
133
|
+
? await client.authContext(sessionId, oidc.client.clientId)
|
|
134
|
+
: undefined;
|
|
135
|
+
if (context) {
|
|
136
|
+
if (!context.active || context.recovery || context.userId !== accountId) {
|
|
137
|
+
return interactionPolicy.Check.REQUEST_PROMPT;
|
|
138
|
+
}
|
|
139
|
+
adoptAuthContext(oidc.session, context);
|
|
140
|
+
}
|
|
141
|
+
if (!hasRequirements(requirements)) {
|
|
142
|
+
return interactionPolicy.Check.NO_NEED_TO_PROMPT;
|
|
143
|
+
}
|
|
144
|
+
return !context || unmetRequirements(requirements, context)
|
|
145
|
+
? interactionPolicy.Check.REQUEST_PROMPT
|
|
146
|
+
: interactionPolicy.Check.NO_NEED_TO_PROMPT;
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
export function buildInteractionPolicy(client) {
|
|
150
|
+
const policy = interactionPolicy.base();
|
|
151
|
+
const login = policy.get('login');
|
|
152
|
+
if (!login) {
|
|
153
|
+
throw new Error('The base interaction policy has no login prompt');
|
|
154
|
+
}
|
|
155
|
+
for (const reason of ['max_age', 'essential_acrs', 'essential_acr']) {
|
|
156
|
+
login.checks.remove(reason);
|
|
157
|
+
}
|
|
158
|
+
login.checks.add(authContextCheck(client));
|
|
159
|
+
return policy;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* A client may introspect its own tokens. A resource server is a confidential client linked
|
|
163
|
+
* to its resource, so it may introspect tokens whose audience is that resource and nothing
|
|
164
|
+
* else: no client sees the tokens of apps it has no part in.
|
|
165
|
+
*/
|
|
166
|
+
export function createIntrospectionPolicy(client) {
|
|
167
|
+
return async (_ctx, caller, token) => {
|
|
168
|
+
if (token.clientId === caller.clientId) {
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
if (caller.clientAuthMethod === 'none' || typeof token.aud !== 'string') {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
const resources = await client.clientResources(caller.clientId);
|
|
175
|
+
return resources.some((resource) => resource.identifier === token.aud);
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* The locked configuration from ADR 0001 (D1, D6). The API's admin routes refuse client
|
|
180
|
+
* settings that would loosen it, so this is the ceiling, not a default.
|
|
181
|
+
*/
|
|
182
|
+
export function buildProviderConfiguration(input) {
|
|
183
|
+
const [activeKey] = input.signingKeys;
|
|
184
|
+
if (!activeKey) {
|
|
185
|
+
throw new Error('No provider signing key is available');
|
|
186
|
+
}
|
|
187
|
+
const activeAlg = activeKey.alg;
|
|
188
|
+
if (!isAsymmetricAlg(activeAlg)) {
|
|
189
|
+
throw new Error(`The active provider key uses unsupported alg ${activeAlg}`);
|
|
190
|
+
}
|
|
191
|
+
const signingAlgs = [activeAlg];
|
|
192
|
+
const resourceIndicators = createResourceIndicators(input.client, activeAlg);
|
|
193
|
+
return {
|
|
194
|
+
adapter: createApiAdapterFactory(input.client, input.configCache),
|
|
195
|
+
findAccount: createFindAccount(input.client),
|
|
196
|
+
loadExistingGrant,
|
|
197
|
+
jwks: { keys: input.signingKeys },
|
|
198
|
+
responseTypes: ['code'],
|
|
199
|
+
pkce: { required: pkceRequired },
|
|
200
|
+
// Many RPs read the profile from the ID token rather than calling userinfo.
|
|
201
|
+
conformIdTokenClaims: false,
|
|
202
|
+
rotateRefreshToken: true,
|
|
203
|
+
enableHttpPostMethods: false,
|
|
204
|
+
// ADR D6. Shared secrets are offered only on an instance that allows them, and are
|
|
205
|
+
// checked by the API (verifyClientSecrets). client_secret_jwt is never offered.
|
|
206
|
+
clientAuthMethods: [
|
|
207
|
+
'private_key_jwt',
|
|
208
|
+
'none',
|
|
209
|
+
...(input.config.allowClientSecrets
|
|
210
|
+
? ['client_secret_basic', 'client_secret_post']
|
|
211
|
+
: []),
|
|
212
|
+
],
|
|
213
|
+
enabledJWA: {
|
|
214
|
+
clientAuthSigningAlgValues: [...ASYMMETRIC_ALGS],
|
|
215
|
+
requestObjectSigningAlgValues: [...ASYMMETRIC_ALGS],
|
|
216
|
+
idTokenSigningAlgValues: signingAlgs,
|
|
217
|
+
userinfoSigningAlgValues: signingAlgs,
|
|
218
|
+
},
|
|
219
|
+
acrValues: [...ACR_VALUES],
|
|
220
|
+
scopes: ['openid', 'offline_access', 'email', 'phone', 'seamless'],
|
|
221
|
+
// Listing claims replaces the library's defaults. The session claims sit under openid so
|
|
222
|
+
// every ID token carries the assurance the API recorded (ADR D9, D11), asked for or not.
|
|
223
|
+
claims: {
|
|
224
|
+
sid: null,
|
|
225
|
+
openid: ['sub', 'acr', 'amr', 'auth_time'],
|
|
226
|
+
email: ['email', 'email_verified'],
|
|
227
|
+
phone: ['phone_number', 'phone_number_verified'],
|
|
228
|
+
seamless: ['roles', 'org_id'],
|
|
229
|
+
},
|
|
230
|
+
extraClientMetadata: {
|
|
231
|
+
properties: [
|
|
232
|
+
'urn:seamless:first_party',
|
|
233
|
+
PKCE_REQUIRED_PROPERTY,
|
|
234
|
+
NONCE_REQUIRED_PROPERTY,
|
|
235
|
+
...Object.values(CLIENT_TTL_PROPERTIES),
|
|
236
|
+
],
|
|
237
|
+
},
|
|
238
|
+
routes: {
|
|
239
|
+
authorization: '/authorize',
|
|
240
|
+
token: '/token',
|
|
241
|
+
introspection: '/token/introspection',
|
|
242
|
+
revocation: '/token/revocation',
|
|
243
|
+
userinfo: '/userinfo',
|
|
244
|
+
jwks: '/jwks',
|
|
245
|
+
end_session: '/session/end',
|
|
246
|
+
code_verification: '/device',
|
|
247
|
+
device_authorization: '/device/auth',
|
|
248
|
+
},
|
|
249
|
+
formats: { customizers: { jwt: storeJwtAccessToken } },
|
|
250
|
+
ttl: {
|
|
251
|
+
// Functions, so the resource's and the application's own lifetimes apply.
|
|
252
|
+
AccessToken: accessTokenTtl,
|
|
253
|
+
IdToken: idTokenTtl,
|
|
254
|
+
RefreshToken: refreshTokenTtl,
|
|
255
|
+
AuthorizationCode: 60,
|
|
256
|
+
DeviceCode: DEVICE_CODE_TTL_SECONDS,
|
|
257
|
+
Interaction: 600,
|
|
258
|
+
// The API hides every record whose first-party session has ended, so these are
|
|
259
|
+
// ceilings, and the session's own lifetime is what actually bounds them.
|
|
260
|
+
Grant: grantTtl,
|
|
261
|
+
Session: 1_209_600,
|
|
262
|
+
},
|
|
263
|
+
cookies: {
|
|
264
|
+
keys: input.cookieKeys,
|
|
265
|
+
names: PROVIDER_COOKIE_NAMES,
|
|
266
|
+
long: { httpOnly: true, sameSite: 'lax', secure: true, path: '/' },
|
|
267
|
+
short: { httpOnly: true, sameSite: 'lax', secure: true, path: '/' },
|
|
268
|
+
},
|
|
269
|
+
interactions: {
|
|
270
|
+
policy: buildInteractionPolicy(input.client),
|
|
271
|
+
url: (_ctx, interaction) => `/interaction/${interaction.uid}`,
|
|
272
|
+
},
|
|
273
|
+
clientBasedCORS: (ctx, origin, client) => {
|
|
274
|
+
if (origin === 'null') {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
if (ctx.oidc.route === 'userinfo' || client.clientAuthMethod === 'none') {
|
|
278
|
+
return (client.redirectUris ?? []).some((uri) => URL.parse(uri)?.origin === origin);
|
|
279
|
+
}
|
|
280
|
+
return false;
|
|
281
|
+
},
|
|
282
|
+
features: {
|
|
283
|
+
devInteractions: { enabled: false },
|
|
284
|
+
registration: { enabled: false },
|
|
285
|
+
// The API sends the logout tokens (see leaveBackchannelLogoutToApi).
|
|
286
|
+
backchannelLogout: { enabled: true },
|
|
287
|
+
externalSigningSupport: { enabled: true, ack: 'experimental-01' },
|
|
288
|
+
resourceIndicators: {
|
|
289
|
+
enabled: true,
|
|
290
|
+
defaultResource: resourceIndicators.defaultResource,
|
|
291
|
+
// An RP that named one resource at authorization gets a token for it without repeating
|
|
292
|
+
// it at the token endpoint, which many RP libraries never do.
|
|
293
|
+
useGrantedResource: () => true,
|
|
294
|
+
getResourceServerInfo: resourceIndicators.getResourceServerInfo,
|
|
295
|
+
},
|
|
296
|
+
clientCredentials: { enabled: true },
|
|
297
|
+
rpInitiatedLogout: { enabled: true, ...createLogoutPages(input.renderPage) },
|
|
298
|
+
introspection: { enabled: true, allowedPolicy: createIntrospectionPolicy(input.client) },
|
|
299
|
+
revocation: { enabled: true },
|
|
300
|
+
// First-party clients only: the API lists the grant for no other (ADR D10).
|
|
301
|
+
deviceFlow: { enabled: true, ...createDevicePages(input.renderPage, input.client) },
|
|
302
|
+
},
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
//# sourceMappingURL=providerConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providerConfig.js","sourceRoot":"","sources":["../src/providerConfig.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { NextFunction, Request, Response } from 'express';
|
|
2
|
+
export interface RateLimitOptions {
|
|
3
|
+
limit: number;
|
|
4
|
+
windowMs: number;
|
|
5
|
+
/** Defaults to the client IP. */
|
|
6
|
+
key?: (req: Request) => string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A fixed window per client IP, per instance. Each call reaches the API's store, which has no
|
|
10
|
+
* limit of its own on reads, so this keeps one caller from turning these endpoints into a
|
|
11
|
+
* load generator against it.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createRateLimiter({ limit, windowMs, key: keyOf }: RateLimitOptions): (req: Request, res: Response, next: NextFunction) => void;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2026 Fells Code, LLC
|
|
3
|
+
* Licensed under the Apache License, Version 2.0
|
|
4
|
+
* See LICENSE file in the project root for full license information
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* A fixed window per client IP, per instance. Each call reaches the API's store, which has no
|
|
8
|
+
* limit of its own on reads, so this keeps one caller from turning these endpoints into a
|
|
9
|
+
* load generator against it.
|
|
10
|
+
*/
|
|
11
|
+
export function createRateLimiter({ limit, windowMs, key: keyOf }) {
|
|
12
|
+
const windows = new Map();
|
|
13
|
+
return (req, res, next) => {
|
|
14
|
+
const now = Date.now();
|
|
15
|
+
if (windows.size > 10_000) {
|
|
16
|
+
for (const [key, window] of windows) {
|
|
17
|
+
if (window.resetAt <= now)
|
|
18
|
+
windows.delete(key);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const key = keyOf ? keyOf(req) : (req.ip ?? 'unknown');
|
|
22
|
+
let window = windows.get(key);
|
|
23
|
+
if (!window || window.resetAt <= now) {
|
|
24
|
+
window = { count: 0, resetAt: now + windowMs };
|
|
25
|
+
windows.set(key, window);
|
|
26
|
+
}
|
|
27
|
+
window.count += 1;
|
|
28
|
+
if (window.count > limit) {
|
|
29
|
+
res.set('retry-after', String(Math.ceil((window.resetAt - now) / 1000)));
|
|
30
|
+
res.status(429).json({ error: 'rate_limited' });
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
next();
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=rateLimit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rateLimit.js","sourceRoot":"","sources":["../src/rateLimit.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAWH;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAoB;IACjF,MAAM,OAAO,GAAG,IAAI,GAAG,EAA8C,CAAC;IAEtE,OAAO,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;QACzD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,OAAO,CAAC,IAAI,GAAG,MAAM,EAAE,CAAC;YAC1B,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;gBACpC,IAAI,MAAM,CAAC,OAAO,IAAI,GAAG;oBAAE,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjD,CAAC;QACH,CAAC;QACD,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,SAAS,CAAC,CAAC;QACvD,IAAI,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,OAAO,IAAI,GAAG,EAAE,CAAC;YACrC,MAAM,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,GAAG,QAAQ,EAAE,CAAC;YAC/C,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAC3B,CAAC;QACD,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC;QAClB,IAAI,MAAM,CAAC,KAAK,GAAG,KAAK,EAAE,CAAC;YACzB,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACzE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;YAChD,OAAO;QACT,CAAC;QACD,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2026 Fells Code, LLC
|
|
3
|
+
* Licensed under the Apache License, Version 2.0
|
|
4
|
+
* See LICENSE file in the project root for full license information
|
|
5
|
+
*/
|
|
6
|
+
import { currentRequestContext } from './requestContext.js';
|
|
7
|
+
/**
|
|
8
|
+
* A rotated refresh token presented again means two parties hold it, and so does one that
|
|
9
|
+
* a concurrent refresh consumed first (the API's consume answers which call won). The library revokes the
|
|
10
|
+
* grant; this ends the first-party session it was issued under too (ADR D3), so whoever has
|
|
11
|
+
* the copy cannot carry on through the user's other apps.
|
|
12
|
+
*/
|
|
13
|
+
async function revokeGrant(ctx, grantId) {
|
|
14
|
+
const { provider } = ctx.oidc;
|
|
15
|
+
try {
|
|
16
|
+
await Promise.all([
|
|
17
|
+
provider.AccessToken.revokeByGrantId(grantId),
|
|
18
|
+
provider.RefreshToken.revokeByGrantId(grantId),
|
|
19
|
+
provider.Grant.adapter.destroy(grantId),
|
|
20
|
+
]);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
console.error(`[oidc-provider] could not revoke a reused code's grant: ${String(error)}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function revokeSessionOnRefreshReuse(client) {
|
|
27
|
+
return async (ctx, next) => {
|
|
28
|
+
await next();
|
|
29
|
+
const entities = ctx.oidc?.entities;
|
|
30
|
+
const context = currentRequestContext();
|
|
31
|
+
if (ctx.oidc?.route !== 'token' || !context) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
// A code redeemed by a concurrent request: as the library does for a sequential reuse,
|
|
35
|
+
// everything issued under its grant goes.
|
|
36
|
+
const lost = context.lostConsume;
|
|
37
|
+
if (lost && lost.model !== 'RefreshToken' && lost.grantId) {
|
|
38
|
+
await revokeGrant(ctx, lost.grantId);
|
|
39
|
+
}
|
|
40
|
+
const reused = lost?.model === 'RefreshToken' ||
|
|
41
|
+
(Boolean(entities?.RefreshToken?.consumed) && !entities?.RotatedRefreshToken);
|
|
42
|
+
if (!reused || !context.sessionId) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
await client.revokeSession(context.sessionId, {
|
|
47
|
+
reason: 'refresh_token_reuse',
|
|
48
|
+
correlationId: context.correlationId,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
console.error(`[oidc-provider] could not revoke session after refresh token reuse: ${String(error)}`);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=refreshReuse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refreshReuse.js","sourceRoot":"","sources":["../src/refreshReuse.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE5D;;;;;GAKG;AACH,KAAK,UAAU,WAAW,CAAC,GAAuB,EAAE,OAAe;IACjE,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;IAC9B,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,GAAG,CAAC;YAChB,QAAQ,CAAC,WAAW,CAAC,eAAe,CAAC,OAAO,CAAC;YAC7C,QAAQ,CAAC,YAAY,CAAC,eAAe,CAAC,OAAO,CAAC;YAC9C,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;SACxC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,2DAA2D,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC5F,CAAC;AACH,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,MAAqB;IAC/D,OAAO,KAAK,EAAE,GAAuB,EAAE,IAA4B,EAAE,EAAE;QACrE,MAAM,IAAI,EAAE,CAAC;QACb,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC;QACpC,MAAM,OAAO,GAAG,qBAAqB,EAAE,CAAC;QACxC,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,KAAK,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;YAC5C,OAAO;QACT,CAAC;QACD,uFAAuF;QACvF,0CAA0C;QAC1C,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC;QACjC,IAAI,IAAI,IAAI,IAAI,CAAC,KAAK,KAAK,cAAc,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC1D,MAAM,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACvC,CAAC;QACD,MAAM,MAAM,GACV,IAAI,EAAE,KAAK,KAAK,cAAc;YAC9B,CAAC,OAAO,CAAC,QAAQ,EAAE,YAAY,EAAE,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;QAChF,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YAClC,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,SAAS,EAAE;gBAC5C,MAAM,EAAE,qBAAqB;gBAC7B,aAAa,EAAE,OAAO,CAAC,aAAa;aACrC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CACX,uEAAuE,MAAM,CAAC,KAAK,CAAC,EAAE,CACvF,CAAC;QACJ,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export interface EdgeRequestContext {
|
|
2
|
+
correlationId: string;
|
|
3
|
+
clientIp?: string;
|
|
4
|
+
userAgent?: string;
|
|
5
|
+
/** The first-party `sessions` row the request acts for, once a sign-in has linked one. */
|
|
6
|
+
sessionId?: string;
|
|
7
|
+
/** Set when a verified first-party session named `sessionId`, so stored records cannot. */
|
|
8
|
+
sessionBound?: boolean;
|
|
9
|
+
/** The one-time stand-in for a client secret the API verified on this request. */
|
|
10
|
+
verifiedClientSecret?: {
|
|
11
|
+
clientId: string;
|
|
12
|
+
value: string;
|
|
13
|
+
};
|
|
14
|
+
/** The `jti` of a JWT access token this request presented and the edge verified. */
|
|
15
|
+
verifiedJwtTokenId?: string;
|
|
16
|
+
/** Set once the request loads a record from a device approval (ADR D10). */
|
|
17
|
+
deviceFlow?: boolean;
|
|
18
|
+
/** A POST to the token endpoint, where loading a device code is a poll. */
|
|
19
|
+
tokenRequest?: boolean;
|
|
20
|
+
devicePolled?: boolean;
|
|
21
|
+
/** A grant source another request consumed first, which makes this one a reuse. */
|
|
22
|
+
lostConsume?: {
|
|
23
|
+
model: SingleUseModel;
|
|
24
|
+
grantId?: string;
|
|
25
|
+
};
|
|
26
|
+
/** The grant behind each single-use record this request loaded, by `model:id`. */
|
|
27
|
+
singleUseGrants?: Map<string, string>;
|
|
28
|
+
}
|
|
29
|
+
export type SingleUseModel = 'RefreshToken' | 'AuthorizationCode' | 'DeviceCode';
|
|
30
|
+
export declare function runWithRequestContext<T>(context: EdgeRequestContext, fn: () => T): T;
|
|
31
|
+
export declare function currentRequestContext(): EdgeRequestContext | undefined;
|
|
32
|
+
/** Pins the request to a first-party session the edge verified itself. */
|
|
33
|
+
export declare function bindSession(sessionId: string): void;
|
|
34
|
+
export declare function markDeviceFlow(): void;
|
|
35
|
+
/**
|
|
36
|
+
* Takes the session a stored record was saved with. An `authoritative` record (the code,
|
|
37
|
+
* token or interaction the request is acting on) replaces what an earlier lookup set, such as
|
|
38
|
+
* the browser's provider session. Neither overrides a session bound with `bindSession`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function adoptSession(sessionId: string | null, authoritative: boolean): void;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2026 Fells Code, LLC
|
|
3
|
+
* Licensed under the Apache License, Version 2.0
|
|
4
|
+
* See LICENSE file in the project root for full license information
|
|
5
|
+
*/
|
|
6
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
7
|
+
const storage = new AsyncLocalStorage();
|
|
8
|
+
export function runWithRequestContext(context, fn) {
|
|
9
|
+
return storage.run(context, fn);
|
|
10
|
+
}
|
|
11
|
+
export function currentRequestContext() {
|
|
12
|
+
return storage.getStore();
|
|
13
|
+
}
|
|
14
|
+
/** Pins the request to a first-party session the edge verified itself. */
|
|
15
|
+
export function bindSession(sessionId) {
|
|
16
|
+
const context = storage.getStore();
|
|
17
|
+
if (!context) {
|
|
18
|
+
throw new Error('bindSession called outside a request context');
|
|
19
|
+
}
|
|
20
|
+
context.sessionId = sessionId;
|
|
21
|
+
context.sessionBound = true;
|
|
22
|
+
}
|
|
23
|
+
export function markDeviceFlow() {
|
|
24
|
+
const context = storage.getStore();
|
|
25
|
+
if (context) {
|
|
26
|
+
context.deviceFlow = true;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Takes the session a stored record was saved with. An `authoritative` record (the code,
|
|
31
|
+
* token or interaction the request is acting on) replaces what an earlier lookup set, such as
|
|
32
|
+
* the browser's provider session. Neither overrides a session bound with `bindSession`.
|
|
33
|
+
*/
|
|
34
|
+
export function adoptSession(sessionId, authoritative) {
|
|
35
|
+
const context = storage.getStore();
|
|
36
|
+
if (!context || !sessionId || context.sessionBound) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (authoritative || !context.sessionId) {
|
|
40
|
+
context.sessionId = sessionId;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=requestContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requestContext.js","sourceRoot":"","sources":["../src/requestContext.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AA2BrD,MAAM,OAAO,GAAG,IAAI,iBAAiB,EAAsB,CAAC;AAE5D,MAAM,UAAU,qBAAqB,CAAI,OAA2B,EAAE,EAAW;IAC/E,OAAO,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,qBAAqB;IACnC,OAAO,OAAO,CAAC,QAAQ,EAAE,CAAC;AAC5B,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,WAAW,CAAC,SAAiB;IAC3C,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IACnC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAC9B,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IACnC,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAC5B,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,SAAwB,EAAE,aAAsB;IAC3E,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IACnC,IAAI,CAAC,OAAO,IAAI,CAAC,SAAS,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;QACnD,OAAO;IACT,CAAC;IACD,IAAI,aAAa,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;QACxC,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAChC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type AsymmetricSigningAlgorithm, type Client, type KoaContextWithOIDC, type ResourceServer } from 'oidc-provider';
|
|
2
|
+
import type { OidcApiClient } from './apiClient.js';
|
|
3
|
+
export declare const DEFAULT_ACCESS_TOKEN_TTL = 600;
|
|
4
|
+
/**
|
|
5
|
+
* RFC 8707 and RFC 9068: a token for a resource is a JWT whose `aud` is the resource's
|
|
6
|
+
* identifier, carrying only scopes the client is allowed there. The API keeps the links and
|
|
7
|
+
* `/oidc/sign` checks the same ones, so this only decides what to ask it to sign.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createResourceIndicators(client: OidcApiClient, signingAlg: AsymmetricSigningAlgorithm): {
|
|
10
|
+
/**
|
|
11
|
+
* Without a resource a user's access token stays an opaque one for userinfo. A client
|
|
12
|
+
* credentials token is only ever for a resource: the client's only one, or the one named.
|
|
13
|
+
*/
|
|
14
|
+
defaultResource(ctx: KoaContextWithOIDC, oidcClient: Client, oneOf?: readonly string[]): Promise<string | readonly string[] | undefined>;
|
|
15
|
+
getResourceServerInfo(ctx: KoaContextWithOIDC, indicator: string, oidcClient: Client): Promise<ResourceServer>;
|
|
16
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2026 Fells Code, LLC
|
|
3
|
+
* Licensed under the Apache License, Version 2.0
|
|
4
|
+
* See LICENSE file in the project root for full license information
|
|
5
|
+
*/
|
|
6
|
+
import { errors, } from 'oidc-provider';
|
|
7
|
+
export const DEFAULT_ACCESS_TOKEN_TTL = 600;
|
|
8
|
+
/**
|
|
9
|
+
* RFC 8707 and RFC 9068: a token for a resource is a JWT whose `aud` is the resource's
|
|
10
|
+
* identifier, carrying only scopes the client is allowed there. The API keeps the links and
|
|
11
|
+
* `/oidc/sign` checks the same ones, so this only decides what to ask it to sign.
|
|
12
|
+
*/
|
|
13
|
+
export function createResourceIndicators(client, signingAlg) {
|
|
14
|
+
// The library asks once per indicator, sometimes several times in one request.
|
|
15
|
+
const perRequest = new WeakMap();
|
|
16
|
+
function resourcesFor(ctx, clientId) {
|
|
17
|
+
let pending = perRequest.get(ctx);
|
|
18
|
+
if (!pending) {
|
|
19
|
+
pending = client.clientResources(clientId);
|
|
20
|
+
perRequest.set(ctx, pending);
|
|
21
|
+
}
|
|
22
|
+
return pending;
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
/**
|
|
26
|
+
* Without a resource a user's access token stays an opaque one for userinfo. A client
|
|
27
|
+
* credentials token is only ever for a resource: the client's only one, or the one named.
|
|
28
|
+
*/
|
|
29
|
+
async defaultResource(ctx, oidcClient, oneOf) {
|
|
30
|
+
if (oneOf) {
|
|
31
|
+
return oneOf;
|
|
32
|
+
}
|
|
33
|
+
if (ctx.oidc.route !== 'token' || ctx.oidc.params?.grant_type !== 'client_credentials') {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
const resources = await resourcesFor(ctx, oidcClient.clientId);
|
|
37
|
+
const [only] = resources;
|
|
38
|
+
if (resources.length !== 1 || !only) {
|
|
39
|
+
throw new errors.InvalidTarget('name the resource this token is for');
|
|
40
|
+
}
|
|
41
|
+
return only.identifier;
|
|
42
|
+
},
|
|
43
|
+
async getResourceServerInfo(ctx, indicator, oidcClient) {
|
|
44
|
+
const resources = await resourcesFor(ctx, oidcClient.clientId);
|
|
45
|
+
const resource = resources.find((candidate) => candidate.identifier === indicator);
|
|
46
|
+
if (!resource) {
|
|
47
|
+
throw new errors.InvalidTarget();
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
audience: resource.identifier,
|
|
51
|
+
scope: resource.scopes.join(' '),
|
|
52
|
+
accessTokenTTL: resource.accessTokenTtlSeconds ?? DEFAULT_ACCESS_TOKEN_TTL,
|
|
53
|
+
accessTokenFormat: 'jwt',
|
|
54
|
+
jwt: { sign: { alg: signingAlg } },
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=resourceServers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resourceServers.js","sourceRoot":"","sources":["../src/resourceServers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAGL,MAAM,GAGP,MAAM,eAAe,CAAC;AAIvB,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAC;AAE5C;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CACtC,MAAqB,EACrB,UAAsC;IAEtC,+EAA+E;IAC/E,MAAM,UAAU,GAAG,IAAI,OAAO,EAAqD,CAAC;IAEpF,SAAS,YAAY,CAAC,GAAuB,EAAE,QAAgB;QAC7D,IAAI,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,GAAG,MAAM,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;YAC3C,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC/B,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,OAAO;QACL;;;WAGG;QACH,KAAK,CAAC,eAAe,CACnB,GAAuB,EACvB,UAAkB,EAClB,KAAyB;YAEzB,IAAI,KAAK,EAAE,CAAC;gBACV,OAAO,KAAK,CAAC;YACf,CAAC;YACD,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,KAAK,oBAAoB,EAAE,CAAC;gBACvF,OAAO,SAAS,CAAC;YACnB,CAAC;YACD,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,GAAG,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;YAC/D,MAAM,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC;YACzB,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;gBACpC,MAAM,IAAI,MAAM,CAAC,aAAa,CAAC,qCAAqC,CAAC,CAAC;YACxE,CAAC;YACD,OAAO,IAAI,CAAC,UAAU,CAAC;QACzB,CAAC;QAED,KAAK,CAAC,qBAAqB,CACzB,GAAuB,EACvB,SAAiB,EACjB,UAAkB;YAElB,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,GAAG,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;YAC/D,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC;YACnF,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,MAAM,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;YACnC,CAAC;YACD,OAAO;gBACL,QAAQ,EAAE,QAAQ,CAAC,UAAU;gBAC7B,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;gBAChC,cAAc,EAAE,QAAQ,CAAC,qBAAqB,IAAI,wBAAwB;gBAC1E,iBAAiB,EAAE,KAAK;gBACxB,GAAG,EAAE,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE,EAAE;aACnC,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Middleware } from 'koa';
|
|
2
|
+
/**
|
|
3
|
+
* The library scopes its resume cookie to `Path=/authorize/<uid>`, and a `__Host-` cookie
|
|
4
|
+
* must have `Path=/`. This moves it to `Path=/` so a sibling subdomain cannot plant one, and,
|
|
5
|
+
* since one browser now holds a single resume cookie, refuses to resume an authorization
|
|
6
|
+
* whose uid is not the cookie's (the library would otherwise resume the cookie's).
|
|
7
|
+
*/
|
|
8
|
+
export declare function hostOnlyResumeCookie(cookieName: string, resumePath: string): Middleware;
|