@seamless-auth/oidc-provider 0.0.0-stage → 0.1.0

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.
Files changed (91) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/LICENSE +201 -0
  3. package/README.md +273 -2
  4. package/dist/adapter.d.ts +44 -0
  5. package/dist/adapter.js +249 -0
  6. package/dist/adapter.js.map +1 -0
  7. package/dist/apiClient.d.ts +35 -0
  8. package/dist/apiClient.js +217 -0
  9. package/dist/apiClient.js.map +1 -0
  10. package/dist/authRequirements.d.ts +37 -0
  11. package/dist/authRequirements.js +58 -0
  12. package/dist/authRequirements.js.map +1 -0
  13. package/dist/clientSecrets.d.ts +10 -0
  14. package/dist/clientSecrets.js +102 -0
  15. package/dist/clientSecrets.js.map +1 -0
  16. package/dist/configCache.d.ts +14 -0
  17. package/dist/configCache.js +45 -0
  18. package/dist/configCache.js.map +1 -0
  19. package/dist/createOidcEdge.d.ts +45 -0
  20. package/dist/createOidcEdge.js +132 -0
  21. package/dist/createOidcEdge.js.map +1 -0
  22. package/dist/deviceFlow.d.ts +53 -0
  23. package/dist/deviceFlow.js +168 -0
  24. package/dist/deviceFlow.js.map +1 -0
  25. package/dist/env.d.ts +16 -0
  26. package/dist/env.js +36 -0
  27. package/dist/env.js.map +1 -0
  28. package/dist/errors.d.ts +18 -0
  29. package/dist/errors.js +40 -0
  30. package/dist/errors.js.map +1 -0
  31. package/dist/firstPartySession.d.ts +21 -0
  32. package/dist/firstPartySession.js +28 -0
  33. package/dist/firstPartySession.js.map +1 -0
  34. package/dist/hostedPages.d.ts +51 -0
  35. package/dist/hostedPages.js +172 -0
  36. package/dist/hostedPages.js.map +1 -0
  37. package/dist/index.d.ts +20 -0
  38. package/dist/index.js +20 -0
  39. package/dist/index.js.map +1 -0
  40. package/dist/interactions.d.ts +17 -0
  41. package/dist/interactions.js +209 -0
  42. package/dist/interactions.js.map +1 -0
  43. package/dist/jwtAccessTokens.d.ts +23 -0
  44. package/dist/jwtAccessTokens.js +125 -0
  45. package/dist/jwtAccessTokens.js.map +1 -0
  46. package/dist/logout.d.ts +22 -0
  47. package/dist/logout.js +86 -0
  48. package/dist/logout.js.map +1 -0
  49. package/dist/originGuard.d.ts +10 -0
  50. package/dist/originGuard.js +32 -0
  51. package/dist/originGuard.js.map +1 -0
  52. package/dist/pkce.d.ts +16 -0
  53. package/dist/pkce.js +32 -0
  54. package/dist/pkce.js.map +1 -0
  55. package/dist/providerConfig.d.ts +50 -0
  56. package/dist/providerConfig.js +305 -0
  57. package/dist/providerConfig.js.map +1 -0
  58. package/dist/rateLimit.d.ts +13 -0
  59. package/dist/rateLimit.js +36 -0
  60. package/dist/rateLimit.js.map +1 -0
  61. package/dist/refreshReuse.d.ts +3 -0
  62. package/dist/refreshReuse.js +56 -0
  63. package/dist/refreshReuse.js.map +1 -0
  64. package/dist/requestContext.d.ts +40 -0
  65. package/dist/requestContext.js +43 -0
  66. package/dist/requestContext.js.map +1 -0
  67. package/dist/resourceServers.d.ts +16 -0
  68. package/dist/resourceServers.js +59 -0
  69. package/dist/resourceServers.js.map +1 -0
  70. package/dist/resumeCookie.d.ts +8 -0
  71. package/dist/resumeCookie.js +46 -0
  72. package/dist/resumeCookie.js.map +1 -0
  73. package/dist/server.d.ts +2 -0
  74. package/dist/server.js +36 -0
  75. package/dist/server.js.map +1 -0
  76. package/dist/signingKey.d.ts +29 -0
  77. package/dist/signingKey.js +142 -0
  78. package/dist/signingKey.js.map +1 -0
  79. package/dist/startup.d.ts +4 -0
  80. package/dist/startup.js +19 -0
  81. package/dist/startup.js.map +1 -0
  82. package/dist/tokenAudit.d.ts +4 -0
  83. package/dist/tokenAudit.js +42 -0
  84. package/dist/tokenAudit.js.map +1 -0
  85. package/dist/tokenLifetimes.d.ts +31 -0
  86. package/dist/tokenLifetimes.js +51 -0
  87. package/dist/tokenLifetimes.js.map +1 -0
  88. package/dist/ui/assets/index-CydLXRvo.js +18 -0
  89. package/dist/ui/assets/index-y3LME-Gv.css +1 -0
  90. package/dist/ui/index.html +15 -0
  91. package/package.json +115 -4
@@ -0,0 +1,249 @@
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 { randomBytes } from 'node:crypto';
7
+ import { OidcPayloadModelSchema, } from '@seamless-auth/types';
8
+ import { errors } from 'oidc-provider';
9
+ import { checkDeviceRequest, DEVICE_CODE_GRANT, DevicePollThrottle, isDeviceGrant, withLoginPrompt, } from './deviceFlow.js';
10
+ import { adoptSession, currentRequestContext, markDeviceFlow, } from './requestContext.js';
11
+ /** A client assertion or proof presented twice. Answered as `invalid_client`, not a 500. */
12
+ export class ReplayDetectedError extends errors.InvalidClientAuth {
13
+ constructor() {
14
+ super('a replay detection record already exists for this assertion');
15
+ this.name = 'ReplayDetectedError';
16
+ }
17
+ }
18
+ const SHARED_SECRET_METHODS = new Set(['client_secret_basic', 'client_secret_post']);
19
+ const SINGLE_USE = {
20
+ RefreshToken: 'refresh token',
21
+ AuthorizationCode: 'authorization code',
22
+ DeviceCode: 'device code',
23
+ };
24
+ export class UnsupportedModelError extends Error {
25
+ constructor(name, operation) {
26
+ super(`${operation} is not supported for oidc-provider model ${name}`);
27
+ this.name = 'UnsupportedModelError';
28
+ }
29
+ }
30
+ // The browser's provider session and the grant can outlive the first-party session they were
31
+ // made under, so they only fill an empty context. Every other record is the thing the request
32
+ // is acting on.
33
+ const CONTEXTUAL_MODELS = new Set(['Session', 'Grant']);
34
+ function toAdapterPayload(model, record) {
35
+ if (!record) {
36
+ return undefined;
37
+ }
38
+ adoptSession(record.sessionId, !CONTEXTUAL_MODELS.has(model));
39
+ const payload = { ...record.payload };
40
+ if (!CONTEXTUAL_MODELS.has(model) && isDeviceGrant(payload)) {
41
+ markDeviceFlow();
42
+ }
43
+ if (record.consumedAt) {
44
+ payload.consumed = Math.floor(Date.parse(record.consumedAt) / 1000);
45
+ }
46
+ return payload;
47
+ }
48
+ /** Stores one library model through the API's `/oidc/payloads/*` routes. */
49
+ export class ApiPayloadAdapter {
50
+ #name;
51
+ #model;
52
+ #client;
53
+ #polls;
54
+ constructor(name, client, polls = new DevicePollThrottle()) {
55
+ this.#name = name;
56
+ this.#client = client;
57
+ this.#polls = polls;
58
+ const parsed = OidcPayloadModelSchema.safeParse(name);
59
+ this.#model = parsed.success ? parsed.data : undefined;
60
+ }
61
+ #requireModel(operation) {
62
+ if (!this.#model) {
63
+ throw new UnsupportedModelError(this.#name, operation);
64
+ }
65
+ return this.#model;
66
+ }
67
+ async upsert(id, payload, expiresIn) {
68
+ const model = this.#requireModel('upsert');
69
+ if (!expiresIn || expiresIn <= 0) {
70
+ throw new UnsupportedModelError(this.#name, 'upsert without an expiry');
71
+ }
72
+ const sessionId = currentRequestContext()?.sessionId;
73
+ const stored = { ...payload };
74
+ if (model === 'DeviceCode') {
75
+ const params = stored.params;
76
+ checkDeviceRequest(params);
77
+ // The verification page reads its authorization parameters from the stored code, so
78
+ // this is where every approval is made to sign in again rather than ride a session
79
+ // the browser already had: approving a device is the step phishing aims at.
80
+ if (params && !stored.accountId) {
81
+ stored.params = { ...params, prompt: withLoginPrompt(params.prompt) };
82
+ }
83
+ // ADR D10: never carried into the device's tokens, whatever the approver earned.
84
+ delete stored.acr;
85
+ }
86
+ const { created } = await this.#client.upsertPayload(model, id, {
87
+ payload: stored,
88
+ expiresIn: Math.ceil(expiresIn),
89
+ ...(sessionId ? { sessionId } : {}),
90
+ });
91
+ // The library checks for an existing record before it saves one, so this only
92
+ // fires when two requests race. Failing the request is the closed outcome.
93
+ if (model === 'ReplayDetection' && !created) {
94
+ throw new ReplayDetectedError();
95
+ }
96
+ }
97
+ async find(id) {
98
+ const model = this.#requireModel('find');
99
+ const context = currentRequestContext();
100
+ const record = await this.#client.findPayload(model, id);
101
+ // The grant loads the code more than once per request, and only the first is the poll.
102
+ // Only a code that exists is tracked, so made-up ones cannot fill the throttle.
103
+ if (model === 'DeviceCode' && record && context?.tokenRequest && !context.devicePolled) {
104
+ context.devicePolled = true;
105
+ this.#polls.check(id);
106
+ }
107
+ const payload = toAdapterPayload(model, record);
108
+ if (context && model in SINGLE_USE && typeof payload?.grantId === 'string') {
109
+ context.singleUseGrants ??= new Map();
110
+ context.singleUseGrants.set(`${model}:${id}`, payload.grantId);
111
+ }
112
+ // The library loads only opaque records. A JWT access token the edge verified on this
113
+ // request is read like one, so introspection and revocation can see it.
114
+ if (payload?.format === 'jwt' &&
115
+ (model === 'AccessToken' || model === 'ClientCredentials') &&
116
+ currentRequestContext()?.verifiedJwtTokenId === id) {
117
+ delete payload.format;
118
+ }
119
+ if (model === 'RefreshToken' && payload && record?.sessionId) {
120
+ await this.#adoptCurrentAuthContext(payload, record.sessionId);
121
+ }
122
+ return payload;
123
+ }
124
+ /**
125
+ * A refresh carries the acr, amr and auth_time of the session's current row, not the ones
126
+ * the token was first issued with: a step-up since then moved them, and `/oidc/sign`
127
+ * refuses any value that differs from the row.
128
+ */
129
+ async #adoptCurrentAuthContext(payload, sessionId) {
130
+ const { clientId, accountId } = payload;
131
+ if (typeof clientId !== 'string') {
132
+ return;
133
+ }
134
+ const context = await this.#client.authContext(sessionId, clientId);
135
+ if (!context || !context.active || context.recovery || context.userId !== accountId) {
136
+ return;
137
+ }
138
+ payload.authTime = context.authTime;
139
+ if (context.acr && !isDeviceGrant(payload)) {
140
+ payload.acr = context.acr;
141
+ }
142
+ else {
143
+ delete payload.acr;
144
+ }
145
+ if (context.amr.length > 0) {
146
+ payload.amr = [...context.amr];
147
+ }
148
+ else {
149
+ delete payload.amr;
150
+ }
151
+ }
152
+ async findByUserCode(userCode) {
153
+ const model = this.#requireModel('findByUserCode');
154
+ return toAdapterPayload(model, await this.#client.findPayloadByUserCode(model, userCode));
155
+ }
156
+ async findByUid(uid) {
157
+ const model = this.#requireModel('findByUid');
158
+ return toAdapterPayload(model, await this.#client.findPayloadByUid(model, uid));
159
+ }
160
+ async consume(id) {
161
+ const model = this.#requireModel('consume');
162
+ const consumed = await this.#client.consumePayload(model, id);
163
+ const label = SINGLE_USE[model];
164
+ // The library checked `consumed` when it loaded the record, so another request can still
165
+ // have consumed it since. Only the API's answer says which of the two came first.
166
+ if (!consumed && label) {
167
+ const context = currentRequestContext();
168
+ if (context) {
169
+ const grantId = context.singleUseGrants?.get(`${model}:${id}`);
170
+ context.lostConsume = {
171
+ model: model,
172
+ ...(grantId ? { grantId } : {}),
173
+ };
174
+ }
175
+ throw new errors.InvalidGrant(`${label} already used`);
176
+ }
177
+ }
178
+ async destroy(id) {
179
+ await this.#client.destroyPayload(this.#requireModel('destroy'), id);
180
+ }
181
+ async revokeByGrantId(grantId) {
182
+ await this.#client.revokePayloadsByGrant(this.#requireModel('revokeByGrantId'), grantId);
183
+ }
184
+ }
185
+ /**
186
+ * Reads clients from the API's registry. Clients are managed there, never here. A third-party
187
+ * client does not exist while `third_party_clients_enabled` is off (ADR D10).
188
+ *
189
+ * The registry never returns a secret. A client on a shared-secret method gets the one-time
190
+ * value `verifyClientSecrets` set for a secret the API accepted on this request, and otherwise
191
+ * a random value nothing can present, which the library needs to load the client at all.
192
+ */
193
+ export class ApiClientAdapter {
194
+ #client;
195
+ #config;
196
+ constructor(client, config) {
197
+ this.#client = client;
198
+ this.#config = config;
199
+ }
200
+ async find(id) {
201
+ const metadata = await this.#client.client(id);
202
+ if (!metadata) {
203
+ return undefined;
204
+ }
205
+ if (!metadata['urn:seamless:first_party']) {
206
+ const { thirdPartyClientsEnabled } = await this.#config.get();
207
+ if (!thirdPartyClientsEnabled) {
208
+ return undefined;
209
+ }
210
+ }
211
+ // ADR D10: the device grant is first-party only. The API filters it too.
212
+ const grants = metadata['urn:seamless:first_party']
213
+ ? metadata.grant_types
214
+ : metadata.grant_types.filter((grant) => grant !== DEVICE_CODE_GRANT);
215
+ if (!SHARED_SECRET_METHODS.has(metadata.token_endpoint_auth_method)) {
216
+ return { ...metadata, grant_types: grants };
217
+ }
218
+ const verified = currentRequestContext()?.verifiedClientSecret;
219
+ const secret = verified?.clientId === metadata.client_id
220
+ ? verified.value
221
+ : randomBytes(32).toString('base64url');
222
+ return { ...metadata, grant_types: grants, client_secret: secret };
223
+ }
224
+ async upsert() {
225
+ throw new UnsupportedModelError('Client', 'upsert');
226
+ }
227
+ async findByUserCode() {
228
+ throw new UnsupportedModelError('Client', 'findByUserCode');
229
+ }
230
+ async findByUid() {
231
+ throw new UnsupportedModelError('Client', 'findByUid');
232
+ }
233
+ async consume() {
234
+ throw new UnsupportedModelError('Client', 'consume');
235
+ }
236
+ async destroy() {
237
+ throw new UnsupportedModelError('Client', 'destroy');
238
+ }
239
+ async revokeByGrantId() {
240
+ throw new UnsupportedModelError('Client', 'revokeByGrantId');
241
+ }
242
+ }
243
+ export function createApiAdapterFactory(client, config) {
244
+ const polls = new DevicePollThrottle();
245
+ return (name) => name === 'Client'
246
+ ? new ApiClientAdapter(client, config)
247
+ : new ApiPayloadAdapter(name, client, polls);
248
+ }
249
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import { type OidcAccountClaims, type OidcAccountQuery, type OidcAuthContext, type OidcClientMetadata, type OidcClientResource, type OidcEdgeConfigResponse, type OidcEdgeEvent, type OidcJwksResponse, type OidcPayloadModel, type OidcPayloadRecord, type OidcPayloadUpsertRequest, type OidcPayloadUpsertResponse, type OidcSessionRevokeRequest, type OidcSignRequest, type OidcSignResponse } from '@seamless-auth/types';
2
+ export declare const SERVICE_TOKEN_SUBJECT = "seamless-oidc-provider";
3
+ export interface OidcApiClientOptions {
4
+ apiUrl: string;
5
+ serviceSecret: string;
6
+ /** The `kid` header on the service token. Defaults to the API's development kid. */
7
+ serviceTokenKid?: string;
8
+ fetch?: typeof fetch;
9
+ }
10
+ export declare function routePath(template: string, params?: Record<string, string>): string;
11
+ export declare class OidcApiClient {
12
+ #private;
13
+ constructor(options: OidcApiClientOptions);
14
+ config(): Promise<OidcEdgeConfigResponse | undefined>;
15
+ upsertPayload(model: OidcPayloadModel, id: string, body: OidcPayloadUpsertRequest): Promise<OidcPayloadUpsertResponse>;
16
+ findPayload(model: OidcPayloadModel, id: string): Promise<OidcPayloadRecord | undefined>;
17
+ findPayloadByUserCode(model: OidcPayloadModel, userCode: string): Promise<OidcPayloadRecord | undefined>;
18
+ findPayloadByUid(model: OidcPayloadModel, uid: string): Promise<OidcPayloadRecord | undefined>;
19
+ /** True only if this call consumed the record. A record that is gone was not consumed here. */
20
+ consumePayload(model: OidcPayloadModel, id: string): Promise<boolean>;
21
+ destroyPayload(model: OidcPayloadModel, id: string): Promise<void>;
22
+ revokePayloadsByGrant(model: OidcPayloadModel, grantId: string): Promise<void>;
23
+ sign(body: OidcSignRequest): Promise<OidcSignResponse>;
24
+ jwks(): Promise<OidcJwksResponse>;
25
+ client(id: string): Promise<OidcClientMetadata | undefined>;
26
+ /** An API from before this route, or a client that is not active, has none. */
27
+ clientResources(id: string): Promise<OidcClientResource[]>;
28
+ verifySecret(id: string, secret: string): Promise<boolean>;
29
+ /** Answers only for the user behind a session holding a grant for `clientId`. */
30
+ account(id: string, binding: OidcAccountQuery): Promise<OidcAccountClaims | undefined>;
31
+ /** Answers only while the session holds a grant for `clientId`, so save the Grant first. */
32
+ authContext(sessionId: string, clientId: string): Promise<OidcAuthContext | undefined>;
33
+ revokeSession(sessionId: string, body: OidcSessionRevokeRequest): Promise<void>;
34
+ event(event: OidcEdgeEvent): Promise<void>;
35
+ }
@@ -0,0 +1,217 @@
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 { createServiceToken, DEV_JWKS_KID, SERVICE_TOKEN_AUDIENCE, SERVICE_TOKEN_ISSUER, } from '@seamless-auth/core';
7
+ import { OidcAccountClaimsSchema, OidcAuthContextSchema, OidcClientMetadataSchema, OidcClientResourcesResponseSchema, OidcEdgeConfigResponseSchema, OidcEdgeRoutes, OidcJwksResponseSchema, OidcPayloadConsumeResponseSchema, OidcPayloadRecordSchema, OidcPayloadUpsertResponseSchema, OidcSignRefusalSchema, OidcSignResponseSchema, OidcVerifySecretResponseSchema, } from '@seamless-auth/types';
8
+ import { OidcApiError, OidcSignRefusedError } from './errors.js';
9
+ import { currentRequestContext } from './requestContext.js';
10
+ export const SERVICE_TOKEN_SUBJECT = 'seamless-oidc-provider';
11
+ // createServiceToken mints a 60 second token. Reusing it for less than that means a
12
+ // request never presents a token that expires in flight.
13
+ const SERVICE_TOKEN_REUSE_MS = 45_000;
14
+ export function routePath(template, params = {}) {
15
+ return template.replace(/:([A-Za-z]+)/g, (_match, name) => {
16
+ const value = params[name];
17
+ if (value === undefined) {
18
+ throw new Error(`Missing path parameter ${name} for ${template}`);
19
+ }
20
+ return encodeURIComponent(value);
21
+ });
22
+ }
23
+ export class OidcApiClient {
24
+ #apiUrl;
25
+ #serviceSecret;
26
+ #serviceTokenKid;
27
+ #fetch;
28
+ #token;
29
+ constructor(options) {
30
+ this.#apiUrl = options.apiUrl.replace(/\/+$/, '');
31
+ this.#serviceSecret = options.serviceSecret;
32
+ this.#serviceTokenKid = options.serviceTokenKid ?? DEV_JWKS_KID;
33
+ this.#fetch = options.fetch ?? globalThis.fetch;
34
+ }
35
+ config() {
36
+ return this.#request('GET', OidcEdgeRoutes.config, {
37
+ schema: OidcEdgeConfigResponseSchema,
38
+ allowNotFound: true,
39
+ });
40
+ }
41
+ async upsertPayload(model, id, body) {
42
+ return this.#required('PUT', routePath(OidcEdgeRoutes.payload, { model, id }), OidcPayloadUpsertResponseSchema, body);
43
+ }
44
+ findPayload(model, id) {
45
+ return this.#request('GET', routePath(OidcEdgeRoutes.payload, { model, id }), {
46
+ schema: OidcPayloadRecordSchema,
47
+ allowNotFound: true,
48
+ });
49
+ }
50
+ findPayloadByUserCode(model, userCode) {
51
+ return this.#request('GET', routePath(OidcEdgeRoutes.payloadByUserCode, { model, userCode }), {
52
+ schema: OidcPayloadRecordSchema,
53
+ allowNotFound: true,
54
+ });
55
+ }
56
+ findPayloadByUid(model, uid) {
57
+ return this.#request('GET', routePath(OidcEdgeRoutes.payloadByUid, { model, uid }), {
58
+ schema: OidcPayloadRecordSchema,
59
+ allowNotFound: true,
60
+ });
61
+ }
62
+ /** True only if this call consumed the record. A record that is gone was not consumed here. */
63
+ async consumePayload(model, id) {
64
+ const result = await this.#request('POST', routePath(OidcEdgeRoutes.payloadConsume, { model, id }), { schema: OidcPayloadConsumeResponseSchema, allowNotFound: true });
65
+ return result?.consumed === true;
66
+ }
67
+ async destroyPayload(model, id) {
68
+ await this.#request('DELETE', routePath(OidcEdgeRoutes.payload, { model, id }), {
69
+ allowNotFound: true,
70
+ });
71
+ }
72
+ async revokePayloadsByGrant(model, grantId) {
73
+ await this.#request('DELETE', routePath(OidcEdgeRoutes.payloadsByGrant, { model, grantId }), {
74
+ allowNotFound: true,
75
+ });
76
+ }
77
+ async sign(body) {
78
+ const response = await this.#send('POST', OidcEdgeRoutes.sign, body);
79
+ const json = await readJson(response);
80
+ if (!response.ok) {
81
+ const refusal = OidcSignRefusalSchema.safeParse(json);
82
+ if (refusal.success) {
83
+ throw new OidcSignRefusedError(refusal.data.error, refusal.data.message);
84
+ }
85
+ throw new OidcApiError(response.status, OidcEdgeRoutes.sign);
86
+ }
87
+ const parsed = OidcSignResponseSchema.safeParse(json);
88
+ if (!parsed.success) {
89
+ throw unexpectedShape(response.status, OidcEdgeRoutes.sign);
90
+ }
91
+ return parsed.data;
92
+ }
93
+ jwks() {
94
+ return this.#required('GET', OidcEdgeRoutes.jwks, OidcJwksResponseSchema);
95
+ }
96
+ client(id) {
97
+ return this.#request('GET', routePath(OidcEdgeRoutes.client, { id }), {
98
+ schema: OidcClientMetadataSchema,
99
+ allowNotFound: true,
100
+ });
101
+ }
102
+ /** An API from before this route, or a client that is not active, has none. */
103
+ async clientResources(id) {
104
+ const result = await this.#request('GET', routePath(OidcEdgeRoutes.clientResources, { id }), {
105
+ schema: OidcClientResourcesResponseSchema,
106
+ allowNotFound: true,
107
+ });
108
+ return result?.resources ?? [];
109
+ }
110
+ async verifySecret(id, secret) {
111
+ const result = await this.#request('POST', routePath(OidcEdgeRoutes.clientVerifySecret, { id }), { body: { secret }, schema: OidcVerifySecretResponseSchema, allowNotFound: true });
112
+ return result?.valid === true;
113
+ }
114
+ /** Answers only for the user behind a session holding a grant for `clientId`. */
115
+ account(id, binding) {
116
+ const query = new URLSearchParams(binding).toString();
117
+ return this.#request('GET', `${routePath(OidcEdgeRoutes.account, { id })}?${query}`, {
118
+ schema: OidcAccountClaimsSchema,
119
+ allowNotFound: true,
120
+ });
121
+ }
122
+ /** Answers only while the session holds a grant for `clientId`, so save the Grant first. */
123
+ authContext(sessionId, clientId) {
124
+ const query = new URLSearchParams({ clientId }).toString();
125
+ const path = routePath(OidcEdgeRoutes.sessionAuthContext, { id: sessionId });
126
+ return this.#request('GET', `${path}?${query}`, {
127
+ schema: OidcAuthContextSchema,
128
+ allowNotFound: true,
129
+ });
130
+ }
131
+ async revokeSession(sessionId, body) {
132
+ await this.#request('POST', routePath(OidcEdgeRoutes.sessionRevoke, { id: sessionId }), {
133
+ body,
134
+ allowNotFound: true,
135
+ });
136
+ }
137
+ async event(event) {
138
+ await this.#request('POST', OidcEdgeRoutes.events, { body: event });
139
+ }
140
+ async #required(method, path, schema, body) {
141
+ return this.#parse(await this.#send(method, path, body), path, schema);
142
+ }
143
+ async #request(method, path, options) {
144
+ const response = await this.#send(method, path, options.body);
145
+ if (response.status === 404 && options.allowNotFound) {
146
+ return undefined;
147
+ }
148
+ if (!options.schema) {
149
+ if (!response.ok) {
150
+ throw new OidcApiError(response.status, path);
151
+ }
152
+ return undefined;
153
+ }
154
+ return this.#parse(response, path, options.schema);
155
+ }
156
+ async #parse(response, path, schema) {
157
+ if (!response.ok) {
158
+ throw new OidcApiError(response.status, path);
159
+ }
160
+ const parsed = schema.safeParse(await readJson(response));
161
+ if (!parsed.success) {
162
+ throw unexpectedShape(response.status, path);
163
+ }
164
+ return parsed.data;
165
+ }
166
+ #send(method, path, body) {
167
+ const headers = {
168
+ accept: 'application/json',
169
+ 'x-seamless-service-token': this.#serviceToken(),
170
+ };
171
+ // The API rate limits per client IP, so a call made for a browser request carries
172
+ // that browser's address and user agent rather than the edge's own.
173
+ const context = currentRequestContext();
174
+ if (context?.clientIp) {
175
+ headers['x-seamless-client-ip'] = context.clientIp;
176
+ }
177
+ if (context?.userAgent) {
178
+ headers['x-seamless-client-user-agent'] = context.userAgent;
179
+ }
180
+ if (body !== undefined) {
181
+ headers['content-type'] = 'application/json';
182
+ }
183
+ return this.#fetch(`${this.#apiUrl}${path}`, {
184
+ method,
185
+ headers,
186
+ body: body === undefined ? undefined : JSON.stringify(body),
187
+ redirect: 'error',
188
+ });
189
+ }
190
+ #serviceToken() {
191
+ const now = Date.now();
192
+ if (this.#token && this.#token.expiresAt > now) {
193
+ return this.#token.value;
194
+ }
195
+ const value = createServiceToken({
196
+ subject: SERVICE_TOKEN_SUBJECT,
197
+ issuer: SERVICE_TOKEN_ISSUER,
198
+ audience: SERVICE_TOKEN_AUDIENCE,
199
+ serviceSecret: this.#serviceSecret,
200
+ keyId: this.#serviceTokenKid,
201
+ });
202
+ this.#token = { value, expiresAt: now + SERVICE_TOKEN_REUSE_MS };
203
+ return value;
204
+ }
205
+ }
206
+ function unexpectedShape(status, path) {
207
+ return new OidcApiError(status, path, `Unexpected response shape from ${path}`);
208
+ }
209
+ async function readJson(response) {
210
+ try {
211
+ return await response.json();
212
+ }
213
+ catch {
214
+ return undefined;
215
+ }
216
+ }
217
+ //# sourceMappingURL=apiClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiClient.js","sourceRoot":"","sources":["../src/apiClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import type { OidcAcrValue } from '@seamless-auth/types';
2
+ export declare const ACR_VALUES: readonly ["phr", "phrh"];
3
+ /** What an authorization request asks of the authentication behind it (ADR D3, D9). */
4
+ export interface AuthRequirements {
5
+ /** The weakest acr the request accepts. phrh satisfies a request for phr. */
6
+ acr?: OidcAcrValue;
7
+ maxAge?: number;
8
+ /** `prompt=login`: only an authentication during this interaction counts. */
9
+ reauthenticate?: boolean;
10
+ }
11
+ /** The acr and auth_time the API reported for the session. Never computed here. */
12
+ export interface AuthState {
13
+ acr: OidcAcrValue | null | undefined;
14
+ authTime: number;
15
+ }
16
+ /**
17
+ * Reads `acr_values`, `max_age` and `prompt=login`. The claims parameter is not enabled, so
18
+ * `acr_values` is the only way to ask for an acr. Unknown values are ignored.
19
+ */
20
+ export declare function authRequirements(params: Record<string, unknown>): AuthRequirements;
21
+ export declare function hasRequirements(requirements: AuthRequirements): boolean;
22
+ export declare function meetsAcr(actual: unknown, required: OidcAcrValue): boolean;
23
+ export interface UnmetRequirements {
24
+ acr: boolean;
25
+ freshness: boolean;
26
+ }
27
+ /**
28
+ * `interactionStartedAt` is when the hosted pages began this interaction. An authentication
29
+ * since then satisfies any `max_age`, including 0, as the library's own check allows.
30
+ */
31
+ export declare function unmetRequirements(requirements: AuthRequirements, state: AuthState, interactionStartedAt?: number, now?: number): UnmetRequirements | undefined;
32
+ export type StepUpMethod = 'passkey' | 'totp';
33
+ /**
34
+ * The API moves auth_time only for a step-up that alone earns the session's acr, and TOTP
35
+ * never earns one. So TOTP helps only with freshness on a session that has no acr.
36
+ */
37
+ export declare function stepUpMethods(unmet: UnmetRequirements, state: AuthState): StepUpMethod[];
@@ -0,0 +1,58 @@
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
+ export const ACR_VALUES = ['phr', 'phrh'];
7
+ const ACR_RANK = { phr: 1, phrh: 2 };
8
+ function isAcrValue(value) {
9
+ return typeof value === 'string' && value in ACR_RANK;
10
+ }
11
+ /**
12
+ * Reads `acr_values`, `max_age` and `prompt=login`. The claims parameter is not enabled, so
13
+ * `acr_values` is the only way to ask for an acr. Unknown values are ignored.
14
+ */
15
+ export function authRequirements(params) {
16
+ const requested = typeof params.acr_values === 'string' ? params.acr_values.split(' ') : [];
17
+ const acr = requested
18
+ .filter(isAcrValue)
19
+ .sort((a, b) => ACR_RANK[a] - ACR_RANK[b])
20
+ .at(0);
21
+ const rawMaxAge = params.max_age;
22
+ const maxAge = typeof rawMaxAge === 'string' ? Number(rawMaxAge) : rawMaxAge;
23
+ const prompts = typeof params.prompt === 'string' ? params.prompt.split(' ') : [];
24
+ return {
25
+ ...(acr ? { acr } : {}),
26
+ ...(typeof maxAge === 'number' && Number.isInteger(maxAge) && maxAge >= 0 ? { maxAge } : {}),
27
+ ...(prompts.includes('login') ? { reauthenticate: true } : {}),
28
+ };
29
+ }
30
+ export function hasRequirements(requirements) {
31
+ return (requirements.acr !== undefined ||
32
+ requirements.maxAge !== undefined ||
33
+ requirements.reauthenticate === true);
34
+ }
35
+ export function meetsAcr(actual, required) {
36
+ return isAcrValue(actual) && ACR_RANK[actual] >= ACR_RANK[required];
37
+ }
38
+ /**
39
+ * `interactionStartedAt` is when the hosted pages began this interaction. An authentication
40
+ * since then satisfies any `max_age`, including 0, as the library's own check allows.
41
+ */
42
+ export function unmetRequirements(requirements, state, interactionStartedAt, now = Math.floor(Date.now() / 1000)) {
43
+ const freshSinceStart = interactionStartedAt !== undefined && state.authTime >= interactionStartedAt;
44
+ const acr = requirements.acr !== undefined && !meetsAcr(state.acr, requirements.acr);
45
+ const stale = (requirements.reauthenticate === true && !freshSinceStart) ||
46
+ (requirements.maxAge !== undefined &&
47
+ now - state.authTime > requirements.maxAge &&
48
+ !freshSinceStart);
49
+ return acr || stale ? { acr, freshness: stale } : undefined;
50
+ }
51
+ /**
52
+ * The API moves auth_time only for a step-up that alone earns the session's acr, and TOTP
53
+ * never earns one. So TOTP helps only with freshness on a session that has no acr.
54
+ */
55
+ export function stepUpMethods(unmet, state) {
56
+ return unmet.acr || state.acr ? ['passkey'] : ['passkey', 'totp'];
57
+ }
58
+ //# sourceMappingURL=authRequirements.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authRequirements.js","sourceRoot":"","sources":["../src/authRequirements.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { Middleware } from 'koa';
2
+ import type { OidcApiClient } from './apiClient.js';
3
+ /**
4
+ * ADR D6: a shared secret is checked by the API, never by this service. A presented secret
5
+ * is sent to `verify-secret`, and when the API accepts it, it is swapped in the request for a
6
+ * one-time value that the client's metadata carries for this request only (see
7
+ * `ApiClientAdapter`). The library compares against that value, so it never holds the real
8
+ * secret, and a secret the API did not accept can match nothing.
9
+ */
10
+ export declare function verifyClientSecrets(client: OidcApiClient): Middleware;