@atlasauth/backend 0.29.0 → 0.31.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.
- package/CHANGELOG.md +34 -1
- package/README.md +34 -0
- package/dist/backchannel-handlers.js +2 -2
- package/dist/backchannel-handlers.js.map +1 -1
- package/dist/client/domains.d.ts +33 -3
- package/dist/client/domains.js.map +1 -1
- package/dist/client/index.d.ts +64 -11
- package/dist/client/index.js +3 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/instance-security.d.ts +9 -1
- package/dist/client/instance-security.js +6 -1
- package/dist/client/instance-security.js.map +1 -1
- package/dist/client/instance.d.ts +42 -0
- package/dist/client/instance.js +16 -0
- package/dist/client/instance.js.map +1 -1
- package/dist/client/invitations.d.ts +4 -0
- package/dist/client/invitations.js.map +1 -1
- package/dist/client/jwt-connections.d.ts +46 -0
- package/dist/client/jwt-connections.js +24 -0
- package/dist/client/jwt-connections.js.map +1 -0
- package/dist/client/messaging.d.ts +7 -0
- package/dist/client/messaging.js.map +1 -1
- package/dist/client/oauth-clients.d.ts +70 -0
- package/dist/client/oauth-clients.js +18 -0
- package/dist/client/oauth-clients.js.map +1 -1
- package/dist/client/org-ldap-connections.d.ts +10 -0
- package/dist/client/org-ldap-connections.js.map +1 -1
- package/dist/client/organizations.d.ts +106 -8
- package/dist/client/organizations.js +35 -3
- package/dist/client/organizations.js.map +1 -1
- package/dist/client/platform.d.ts +10 -0
- package/dist/client/platform.js.map +1 -1
- package/dist/client/referrals.d.ts +12 -5
- package/dist/client/referrals.js.map +1 -1
- package/dist/client/scim-tokens.d.ts +7 -0
- package/dist/client/scim-tokens.js.map +1 -1
- package/dist/client/sso-connections.d.ts +14 -0
- package/dist/client/sso-connections.js.map +1 -1
- package/dist/client/sso-onboarding.d.ts +14 -1
- package/dist/client/sso-onboarding.js +3 -2
- package/dist/client/sso-onboarding.js.map +1 -1
- package/dist/client/user-hooks.d.ts +15 -6
- package/dist/client/user-hooks.js.map +1 -1
- package/dist/client/users.d.ts +19 -2
- package/dist/client/users.js.map +1 -1
- package/dist/client/webhooks.d.ts +4 -0
- package/dist/client/webhooks.js.map +1 -1
- package/dist/index.d.ts +0 -4
- package/dist/index.js +4 -10
- package/dist/index.js.map +1 -1
- package/dist/jwks-cache.d.ts +27 -0
- package/dist/jwks-cache.js +45 -4
- package/dist/jwks-cache.js.map +1 -1
- package/dist/oidc.d.ts +35 -1
- package/dist/oidc.js +39 -0
- package/dist/oidc.js.map +1 -1
- package/dist/pca-entry.d.ts +11 -0
- package/dist/pca-entry.js +18 -0
- package/dist/pca-entry.js.map +1 -0
- package/dist/rp.d.ts +253 -0
- package/dist/rp.js +311 -0
- package/dist/rp.js.map +1 -0
- package/dist/verify.d.ts +8 -0
- package/dist/verify.js +3 -1
- package/dist/verify.js.map +1 -1
- package/dist/webhook-common.d.ts +53 -0
- package/dist/webhook-common.js +48 -0
- package/dist/webhook-common.js.map +1 -0
- package/dist/webhooks-web.d.ts +24 -0
- package/dist/webhooks-web.js +82 -0
- package/dist/webhooks-web.js.map +1 -0
- package/dist/webhooks.d.ts +23 -41
- package/dist/webhooks.js +19 -70
- package/dist/webhooks.js.map +1 -1
- package/package.json +41 -4
- package/dist/pca/require-pca.d.ts +0 -47
- package/dist/pca/require-pca.js +0 -121
- package/dist/pca/require-pca.js.map +0 -1
- package/dist/pca/verify.d.ts +0 -36
- package/dist/pca/verify.js +0 -136
- package/dist/pca/verify.js.map +0 -1
package/dist/rp.d.ts
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@atlasauth/backend/rp` (also published alone as `@atlasauth/rp`, whose
|
|
3
|
+
* only dependency is `jose`): the server-side relying party for the hosted
|
|
4
|
+
* redirect, as `(Request) => Response` handlers for any framework.
|
|
5
|
+
*
|
|
6
|
+
* const rp = createRelyingParty({ ...settings, onSignedIn: (user, tokens) => yourSession(user) });
|
|
7
|
+
* const response = await rp.handle(request); // sign-in, callback and sign-out; null for any other path
|
|
8
|
+
*
|
|
9
|
+
* Or route them yourself:
|
|
10
|
+
*
|
|
11
|
+
* const rp = createRelyingParty({ issuer, clientId, clientSecret, redirectUri, secret, secretKey });
|
|
12
|
+
* app.get('/auth/sign-in', (req) => rp.start(req));
|
|
13
|
+
* app.get('/auth/callback', async (req) => {
|
|
14
|
+
* const { user, response } = await rp.callback(req);
|
|
15
|
+
* // create your session for user.sub, keep user.sid on it, then:
|
|
16
|
+
* response.headers.append('set-cookie', yourSessionCookie);
|
|
17
|
+
* return response;
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* This entry point loads only Web standards (fetch, Web Crypto, Request,
|
|
21
|
+
* Response, URL) and `jose`, itself Web Crypto only: no `Buffer`, no
|
|
22
|
+
* `node:*`. It runs unchanged on Node 18+, Deno, Bun and Workers. The full
|
|
23
|
+
* `@atlasauth/backend` entry (management client, migration bridges, ...) is
|
|
24
|
+
* never pulled in.
|
|
25
|
+
*
|
|
26
|
+
* Everything here is a thin layer over `oidc.start` / `oidc.callback` (state,
|
|
27
|
+
* nonce and PKCE S256 in one sealed short-lived cookie; ID-token checks with the
|
|
28
|
+
* algorithm pinned, `iss`, `aud`/`azp`, `exp`/`iat`, `nonce`, `at_hash`) plus
|
|
29
|
+
* the Atlas session calls a relying party that keeps its own session needs:
|
|
30
|
+
* keep-alive (`POST /v1/sessions/{id}/touch`) and server-side sign-out
|
|
31
|
+
* (`POST /v1/sessions/{id}/revoke`, or with `serverSignOut` the one-call
|
|
32
|
+
* `POST /v1/oauth_clients/{id}/logout`).
|
|
33
|
+
*/
|
|
34
|
+
import { AtlasOidcError, type OidcClientAuth, type OidcIdentity, type OidcStartOptions } from './oidc';
|
|
35
|
+
export { AtlasOidcError, isSafeReturnTo, oidcCallback, oidcLogoutUrl, oidcStart, oidcVerifyIdToken, resetOidcCaches } from './oidc';
|
|
36
|
+
export type { OidcIdentity, OidcStartOptions, OidcCallbackOptions, AtlasOidcErrorCode } from './oidc';
|
|
37
|
+
export { AtlasApiError } from './client/error';
|
|
38
|
+
export { BACKCHANNEL_LOGOUT_EVENT, LogoutTokenVerifier, memoryLogoutReplayStore, verifyLogoutToken, } from './logout-token';
|
|
39
|
+
export type { LogoutReplayStore, LogoutTokenClaims, LogoutTokenResult, LogoutTokenVerifierOptions } from './logout-token';
|
|
40
|
+
export { expressBackchannelLogout, fastifyBackchannelLogout, fastifyBackchannelLogoutPlugin, fetchBackchannelLogout, handleBackchannelLogout, } from './backchannel-handlers';
|
|
41
|
+
export type { BackchannelLogoutOptions } from './backchannel-handlers';
|
|
42
|
+
export { ATLAS_WEBHOOK_HEADERS } from './webhook-common';
|
|
43
|
+
export { verifyAtlasWebhook, verifyAtlasWebhookRequest } from './webhooks-web';
|
|
44
|
+
export type { WebhookVerifyFailure, WebhookVerifyResult } from './webhook-common';
|
|
45
|
+
export type { VerifyAtlasWebhookInput } from './webhooks-web';
|
|
46
|
+
export { authenticatedWithin, usedMultiFactor } from './recent-auth';
|
|
47
|
+
export interface RelyingPartyConfig {
|
|
48
|
+
/** Your instance's issuer: `https://` + its frontend API host. */
|
|
49
|
+
issuer: string;
|
|
50
|
+
/** The OAuth client id. */
|
|
51
|
+
clientId: string;
|
|
52
|
+
/** The client secret (a confidential client). */
|
|
53
|
+
clientSecret?: string;
|
|
54
|
+
/** Token-endpoint authentication. Default `client_secret_basic` with a secret. */
|
|
55
|
+
clientAuth?: OidcClientAuth;
|
|
56
|
+
/** The callback URL, registered on the client exactly. */
|
|
57
|
+
redirectUri: string;
|
|
58
|
+
/** At least 32 characters: seals the short-lived sign-in cookie. */
|
|
59
|
+
secret: string;
|
|
60
|
+
/** Scopes. Default `openid profile email`. */
|
|
61
|
+
scope?: string;
|
|
62
|
+
/** Where the callback response sends the browser when the sign-in carried no return path. Default `/`. */
|
|
63
|
+
afterSignInPath?: string;
|
|
64
|
+
/** Where Atlas sends the browser after sign-out. Must be registered on the client exactly. */
|
|
65
|
+
postLogoutRedirectUri?: string;
|
|
66
|
+
/** The instance secret key (`sk_...`, scope `sessions:write`). Needed for `touchIfDue` and server-side revoke. */
|
|
67
|
+
secretKey?: string;
|
|
68
|
+
/** The Backend API origin. Default `https://api.atlasauth.net`. */
|
|
69
|
+
apiUrl?: string;
|
|
70
|
+
/** Flow cookie name. Default `atlas_oidc`. */
|
|
71
|
+
cookieName?: string;
|
|
72
|
+
/**
|
|
73
|
+
* Sign out with ONE server call, `POST /v1/oauth_clients/{clientId}/logout`
|
|
74
|
+
* (the key needs `oauth_clients:write` as well as `sessions:write`): it
|
|
75
|
+
* revokes this app's grants and the Atlas session, so `signOut` redirects
|
|
76
|
+
* straight to the post-logout URI (or `/`) without the `/oauth2/logout` hop.
|
|
77
|
+
* Off by default: revoke via `/v1/sessions/{id}/revoke`, then `/oauth2/logout`.
|
|
78
|
+
*/
|
|
79
|
+
serverSignOut?: boolean;
|
|
80
|
+
/** Minimum time between two keep-alive calls for one session. Default 5 minutes. */
|
|
81
|
+
touchIntervalMs?: number;
|
|
82
|
+
fetchImpl?: typeof fetch;
|
|
83
|
+
/** A clock in epoch milliseconds, for tests. */
|
|
84
|
+
now?: () => number;
|
|
85
|
+
/**
|
|
86
|
+
* More query parameters to pass from your sign-in URL to `/oauth2/authorize`
|
|
87
|
+
* unchanged, for example `['ref', 'utm_source']` for referral attribution.
|
|
88
|
+
* Only the names listed here are forwarded; a value over 512 characters is
|
|
89
|
+
* dropped. The parameters this helper owns (`state`, `nonce`, `redirect_uri`,
|
|
90
|
+
* `scope`, `return_to`, ...) are refused at setup.
|
|
91
|
+
*/
|
|
92
|
+
forwardParams?: string[];
|
|
93
|
+
/**
|
|
94
|
+
* The paths {@link RelyingParty.handle} answers. Defaults: `signIn`
|
|
95
|
+
* `/auth/sign-in`, `callback` the path of `redirectUri`, `signOut` `/auth/sign-out`.
|
|
96
|
+
*/
|
|
97
|
+
routes?: {
|
|
98
|
+
signIn?: string;
|
|
99
|
+
callback?: string;
|
|
100
|
+
signOut?: string;
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Called by {@link RelyingParty.handle} after a verified sign-in: create your
|
|
104
|
+
* own session here. Return a `Response` to answer with it, or nothing to
|
|
105
|
+
* answer with `result.response` (a 302 to the return path) after appending
|
|
106
|
+
* your session cookie to it. The sign-in cookie is cleared either way.
|
|
107
|
+
*/
|
|
108
|
+
onSignedIn?: (user: RelyingPartyUser, tokens: RelyingPartyTokens, request: Request, result: CallbackResult) => Promise<Response | void> | Response | void;
|
|
109
|
+
/**
|
|
110
|
+
* Called by {@link RelyingParty.handle} when the callback fails with an
|
|
111
|
+
* {@link AtlasOidcError}. Default: a plain `400`. The sign-in cookie is cleared either way.
|
|
112
|
+
*/
|
|
113
|
+
onSignInError?: (error: AtlasOidcError, request: Request) => Promise<Response> | Response;
|
|
114
|
+
/**
|
|
115
|
+
* Called by {@link RelyingParty.handle} on the sign-out path: end your own
|
|
116
|
+
* session and return the Atlas `sid` and `idToken` you kept, plus the
|
|
117
|
+
* `setCookies` that clear your session cookie.
|
|
118
|
+
*/
|
|
119
|
+
onSignOut?: (request: Request) => Promise<SignOutOptions | void> | SignOutOptions | void;
|
|
120
|
+
}
|
|
121
|
+
/** Who signed in: the ID-token claims an app needs, in the token's own names. */
|
|
122
|
+
export interface RelyingPartyUser {
|
|
123
|
+
sub: string;
|
|
124
|
+
/** The Atlas session id. Keep it on your session: touch, revoke and back-channel logout all name it. */
|
|
125
|
+
sid: string | null;
|
|
126
|
+
email: string | null;
|
|
127
|
+
/** True only when the ID token says so. Never match accounts on an unverified email. */
|
|
128
|
+
email_verified: boolean;
|
|
129
|
+
name: string | null;
|
|
130
|
+
/** Your own id for the user, when the token carries one. */
|
|
131
|
+
external_id?: string;
|
|
132
|
+
/** The organization the sign-in was made in, when there was one. */
|
|
133
|
+
org_id?: string;
|
|
134
|
+
org_role?: string;
|
|
135
|
+
/** When the user last authenticated, epoch seconds. Store it for {@link RelyingParty.requireRecentAuth}. */
|
|
136
|
+
auth_time: number | null;
|
|
137
|
+
}
|
|
138
|
+
export interface RelyingPartyTokens {
|
|
139
|
+
id_token: string;
|
|
140
|
+
access_token: string;
|
|
141
|
+
refresh_token?: string;
|
|
142
|
+
/** When the access token expires, epoch milliseconds. */
|
|
143
|
+
expires_at: number;
|
|
144
|
+
}
|
|
145
|
+
export interface CallbackResult {
|
|
146
|
+
user: RelyingPartyUser;
|
|
147
|
+
tokens: RelyingPartyTokens;
|
|
148
|
+
/** Every verified claim and the organization / SSO context, as `oidc.callback` returns them. */
|
|
149
|
+
identity: OidcIdentity;
|
|
150
|
+
/**
|
|
151
|
+
* A 302 to the return path (or `afterSignInPath`) that already clears the
|
|
152
|
+
* sign-in cookie. Its headers are mutable: append your own session cookie.
|
|
153
|
+
*/
|
|
154
|
+
response: Response;
|
|
155
|
+
}
|
|
156
|
+
/** Per-request overrides for {@link RelyingParty.start}. */
|
|
157
|
+
export type StartOverrides = Pick<OidcStartOptions, 'prompt' | 'loginHint' | 'uiLocales' | 'maxAge' | 'organization' | 'returnTo' | 'expectedSub' | 'scope' | 'extraParams'>;
|
|
158
|
+
export type TouchResult =
|
|
159
|
+
/** Touched less than `touchIntervalMs` ago: nothing sent. Keep your `lastTouchedAt`. */
|
|
160
|
+
{
|
|
161
|
+
status: 'skipped';
|
|
162
|
+
lastTouchedAt: number;
|
|
163
|
+
}
|
|
164
|
+
/** Atlas slid the idle window. Store `lastTouchedAt`. */
|
|
165
|
+
| {
|
|
166
|
+
status: 'touched';
|
|
167
|
+
lastTouchedAt: number;
|
|
168
|
+
idleExpiresAt: string | null;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* The Atlas session has ended (signed out, revoked, timed out, or unknown).
|
|
172
|
+
* End your own session: it is no longer backed by a live Atlas session.
|
|
173
|
+
*/
|
|
174
|
+
| {
|
|
175
|
+
status: 'ended';
|
|
176
|
+
reason: 'revoked' | 'idle_expired' | 'expired' | 'not_found';
|
|
177
|
+
};
|
|
178
|
+
export interface SignOutOptions {
|
|
179
|
+
/** The ID token from sign-in: lets Atlas end the session without asking. */
|
|
180
|
+
idToken?: string;
|
|
181
|
+
/** The Atlas session id. With a `secretKey`, it is revoked server-side before the redirect. */
|
|
182
|
+
sid?: string | null;
|
|
183
|
+
/** Override the configured `postLogoutRedirectUri`. */
|
|
184
|
+
postLogoutRedirectUri?: string;
|
|
185
|
+
state?: string;
|
|
186
|
+
/** `Set-Cookie` values to send on the redirect, for example the one that clears your session cookie. */
|
|
187
|
+
setCookies?: string[];
|
|
188
|
+
}
|
|
189
|
+
export interface RecentAuthOptions {
|
|
190
|
+
/** The signed-in user's `sub`: the re-authentication must come back as the same user. */
|
|
191
|
+
sub: string;
|
|
192
|
+
/** The `auth_time` you stored at sign-in (or after the last step-up). */
|
|
193
|
+
authTime: number | null | undefined;
|
|
194
|
+
/** Where to come back to after confirming. Default: the request's own path and query. */
|
|
195
|
+
returnTo?: string;
|
|
196
|
+
}
|
|
197
|
+
export interface RelyingParty {
|
|
198
|
+
/**
|
|
199
|
+
* Begin sign-in: a 302 to `/oauth2/authorize` with the sign-in cookie set.
|
|
200
|
+
* `prompt`, `login_hint`, `ui_locales`, `max_age`, `organization` and
|
|
201
|
+
* `return_to` on the request's query are passed through, as are the
|
|
202
|
+
* `forwardParams` you configured; `overrides` win (`extraParams` merge).
|
|
203
|
+
*/
|
|
204
|
+
start(request: Request, overrides?: StartOverrides): Promise<Response>;
|
|
205
|
+
/**
|
|
206
|
+
* Finish sign-in. Throws {@link AtlasOidcError} on any failure; send its
|
|
207
|
+
* `clearCookie` as `Set-Cookie` on your error response.
|
|
208
|
+
*/
|
|
209
|
+
callback(request: Request): Promise<CallbackResult>;
|
|
210
|
+
/**
|
|
211
|
+
* Step-up: `null` when `authTime` is within `maxAgeSeconds`; otherwise a 302
|
|
212
|
+
* that asks the same user to authenticate again (`max_age`, plus
|
|
213
|
+
* `prompt=login` for 0). The callback then refuses another user or an old
|
|
214
|
+
* `auth_time`; store the new `user.auth_time` (and `user.sid`). `0` asks
|
|
215
|
+
* unless the user authenticated in the last {@link CONFIRM_NOW_WINDOW_SECONDS}
|
|
216
|
+
* seconds, so the request that returns from the confirmation goes through.
|
|
217
|
+
*/
|
|
218
|
+
requireRecentAuth(request: Request, maxAgeSeconds: number, options: RecentAuthOptions): Promise<Response | null>;
|
|
219
|
+
/**
|
|
220
|
+
* Keep the Atlas session alive while the user is active in your app, at
|
|
221
|
+
* most once per `touchIntervalMs` per session. Call it on each authenticated
|
|
222
|
+
* request with the `sid` and the time you last touched (0 or null at first).
|
|
223
|
+
*/
|
|
224
|
+
touchIfDue(sid: string, lastTouchedAt: number | null | undefined): Promise<TouchResult>;
|
|
225
|
+
/**
|
|
226
|
+
* End the Atlas session server-side (`POST /v1/sessions/{id}/revoke`); every
|
|
227
|
+
* app signed in through it gets a back-channel logout. An already-ended
|
|
228
|
+
* session is `revoked` too; `not_found` means this instance has no such
|
|
229
|
+
* session (check you use the same instance's secret key). Other failures throw.
|
|
230
|
+
*/
|
|
231
|
+
revokeSession(sid: string): Promise<'revoked' | 'not_found'>;
|
|
232
|
+
/**
|
|
233
|
+
* Sign-out: with a `sid` and a `secretKey`, revokes the Atlas session
|
|
234
|
+
* server-side first, then 302s the browser to `/oauth2/logout`. With
|
|
235
|
+
* `serverSignOut`, one call ends this app's grants and the Atlas session and
|
|
236
|
+
* the 302 goes straight to the post-logout URI. End your own session (clear
|
|
237
|
+
* your cookie on this response) as well.
|
|
238
|
+
*/
|
|
239
|
+
signOut(options?: SignOutOptions): Promise<Response>;
|
|
240
|
+
/**
|
|
241
|
+
* All three routes in one handler: `GET` on the sign-in path runs `start`,
|
|
242
|
+
* `GET` on the callback path runs `callback` then `onSignedIn`, and `POST`
|
|
243
|
+
* or `GET` on the sign-out path runs `onSignOut` then `signOut`. Any other
|
|
244
|
+
* request gets `null`, so your app answers it. Needs `onSignedIn`.
|
|
245
|
+
*
|
|
246
|
+
* export default { fetch: async (req) => (await rp.handle(req)) ?? app(req) };
|
|
247
|
+
*/
|
|
248
|
+
handle(request: Request): Promise<Response | null>;
|
|
249
|
+
}
|
|
250
|
+
/** How long a `requireRecentAuth(…, 0)` confirmation counts, in seconds. */
|
|
251
|
+
export declare const CONFIRM_NOW_WINDOW_SECONDS = 60;
|
|
252
|
+
/** Create the relying party once, at startup; reuse it for every request. */
|
|
253
|
+
export declare function createRelyingParty(config: RelyingPartyConfig): RelyingParty;
|
package/dist/rp.js
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CONFIRM_NOW_WINDOW_SECONDS = exports.usedMultiFactor = exports.authenticatedWithin = exports.verifyAtlasWebhookRequest = exports.verifyAtlasWebhook = exports.ATLAS_WEBHOOK_HEADERS = exports.handleBackchannelLogout = exports.fetchBackchannelLogout = exports.fastifyBackchannelLogoutPlugin = exports.fastifyBackchannelLogout = exports.expressBackchannelLogout = exports.verifyLogoutToken = exports.memoryLogoutReplayStore = exports.LogoutTokenVerifier = exports.BACKCHANNEL_LOGOUT_EVENT = exports.AtlasApiError = exports.resetOidcCaches = exports.oidcVerifyIdToken = exports.oidcStart = exports.oidcLogoutUrl = exports.oidcCallback = exports.isSafeReturnTo = exports.AtlasOidcError = void 0;
|
|
4
|
+
exports.createRelyingParty = createRelyingParty;
|
|
5
|
+
/**
|
|
6
|
+
* `@atlasauth/backend/rp` (also published alone as `@atlasauth/rp`, whose
|
|
7
|
+
* only dependency is `jose`): the server-side relying party for the hosted
|
|
8
|
+
* redirect, as `(Request) => Response` handlers for any framework.
|
|
9
|
+
*
|
|
10
|
+
* const rp = createRelyingParty({ ...settings, onSignedIn: (user, tokens) => yourSession(user) });
|
|
11
|
+
* const response = await rp.handle(request); // sign-in, callback and sign-out; null for any other path
|
|
12
|
+
*
|
|
13
|
+
* Or route them yourself:
|
|
14
|
+
*
|
|
15
|
+
* const rp = createRelyingParty({ issuer, clientId, clientSecret, redirectUri, secret, secretKey });
|
|
16
|
+
* app.get('/auth/sign-in', (req) => rp.start(req));
|
|
17
|
+
* app.get('/auth/callback', async (req) => {
|
|
18
|
+
* const { user, response } = await rp.callback(req);
|
|
19
|
+
* // create your session for user.sub, keep user.sid on it, then:
|
|
20
|
+
* response.headers.append('set-cookie', yourSessionCookie);
|
|
21
|
+
* return response;
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* This entry point loads only Web standards (fetch, Web Crypto, Request,
|
|
25
|
+
* Response, URL) and `jose`, itself Web Crypto only: no `Buffer`, no
|
|
26
|
+
* `node:*`. It runs unchanged on Node 18+, Deno, Bun and Workers. The full
|
|
27
|
+
* `@atlasauth/backend` entry (management client, migration bridges, ...) is
|
|
28
|
+
* never pulled in.
|
|
29
|
+
*
|
|
30
|
+
* Everything here is a thin layer over `oidc.start` / `oidc.callback` (state,
|
|
31
|
+
* nonce and PKCE S256 in one sealed short-lived cookie; ID-token checks with the
|
|
32
|
+
* algorithm pinned, `iss`, `aud`/`azp`, `exp`/`iat`, `nonce`, `at_hash`) plus
|
|
33
|
+
* the Atlas session calls a relying party that keeps its own session needs:
|
|
34
|
+
* keep-alive (`POST /v1/sessions/{id}/touch`) and server-side sign-out
|
|
35
|
+
* (`POST /v1/sessions/{id}/revoke`, or with `serverSignOut` the one-call
|
|
36
|
+
* `POST /v1/oauth_clients/{id}/logout`).
|
|
37
|
+
*/
|
|
38
|
+
const oidc_1 = require("./oidc");
|
|
39
|
+
const error_1 = require("./client/error");
|
|
40
|
+
const request_1 = require("./client/request");
|
|
41
|
+
var oidc_2 = require("./oidc");
|
|
42
|
+
Object.defineProperty(exports, "AtlasOidcError", { enumerable: true, get: function () { return oidc_2.AtlasOidcError; } });
|
|
43
|
+
Object.defineProperty(exports, "isSafeReturnTo", { enumerable: true, get: function () { return oidc_2.isSafeReturnTo; } });
|
|
44
|
+
Object.defineProperty(exports, "oidcCallback", { enumerable: true, get: function () { return oidc_2.oidcCallback; } });
|
|
45
|
+
Object.defineProperty(exports, "oidcLogoutUrl", { enumerable: true, get: function () { return oidc_2.oidcLogoutUrl; } });
|
|
46
|
+
Object.defineProperty(exports, "oidcStart", { enumerable: true, get: function () { return oidc_2.oidcStart; } });
|
|
47
|
+
Object.defineProperty(exports, "oidcVerifyIdToken", { enumerable: true, get: function () { return oidc_2.oidcVerifyIdToken; } });
|
|
48
|
+
Object.defineProperty(exports, "resetOidcCaches", { enumerable: true, get: function () { return oidc_2.resetOidcCaches; } });
|
|
49
|
+
var error_2 = require("./client/error");
|
|
50
|
+
Object.defineProperty(exports, "AtlasApiError", { enumerable: true, get: function () { return error_2.AtlasApiError; } });
|
|
51
|
+
// Back-channel logout: verify the logout_token (typ logout+jwt, events, no nonce, sid/sub, jti replay hook).
|
|
52
|
+
var logout_token_1 = require("./logout-token");
|
|
53
|
+
Object.defineProperty(exports, "BACKCHANNEL_LOGOUT_EVENT", { enumerable: true, get: function () { return logout_token_1.BACKCHANNEL_LOGOUT_EVENT; } });
|
|
54
|
+
Object.defineProperty(exports, "LogoutTokenVerifier", { enumerable: true, get: function () { return logout_token_1.LogoutTokenVerifier; } });
|
|
55
|
+
Object.defineProperty(exports, "memoryLogoutReplayStore", { enumerable: true, get: function () { return logout_token_1.memoryLogoutReplayStore; } });
|
|
56
|
+
Object.defineProperty(exports, "verifyLogoutToken", { enumerable: true, get: function () { return logout_token_1.verifyLogoutToken; } });
|
|
57
|
+
var backchannel_handlers_1 = require("./backchannel-handlers");
|
|
58
|
+
Object.defineProperty(exports, "expressBackchannelLogout", { enumerable: true, get: function () { return backchannel_handlers_1.expressBackchannelLogout; } });
|
|
59
|
+
Object.defineProperty(exports, "fastifyBackchannelLogout", { enumerable: true, get: function () { return backchannel_handlers_1.fastifyBackchannelLogout; } });
|
|
60
|
+
Object.defineProperty(exports, "fastifyBackchannelLogoutPlugin", { enumerable: true, get: function () { return backchannel_handlers_1.fastifyBackchannelLogoutPlugin; } });
|
|
61
|
+
Object.defineProperty(exports, "fetchBackchannelLogout", { enumerable: true, get: function () { return backchannel_handlers_1.fetchBackchannelLogout; } });
|
|
62
|
+
Object.defineProperty(exports, "handleBackchannelLogout", { enumerable: true, get: function () { return backchannel_handlers_1.handleBackchannelLogout; } });
|
|
63
|
+
// Webhooks, verified with Web Crypto.
|
|
64
|
+
var webhook_common_1 = require("./webhook-common");
|
|
65
|
+
Object.defineProperty(exports, "ATLAS_WEBHOOK_HEADERS", { enumerable: true, get: function () { return webhook_common_1.ATLAS_WEBHOOK_HEADERS; } });
|
|
66
|
+
var webhooks_web_1 = require("./webhooks-web");
|
|
67
|
+
Object.defineProperty(exports, "verifyAtlasWebhook", { enumerable: true, get: function () { return webhooks_web_1.verifyAtlasWebhook; } });
|
|
68
|
+
Object.defineProperty(exports, "verifyAtlasWebhookRequest", { enumerable: true, get: function () { return webhooks_web_1.verifyAtlasWebhookRequest; } });
|
|
69
|
+
var recent_auth_1 = require("./recent-auth");
|
|
70
|
+
Object.defineProperty(exports, "authenticatedWithin", { enumerable: true, get: function () { return recent_auth_1.authenticatedWithin; } });
|
|
71
|
+
Object.defineProperty(exports, "usedMultiFactor", { enumerable: true, get: function () { return recent_auth_1.usedMultiFactor; } });
|
|
72
|
+
const PASSTHROUGH = [
|
|
73
|
+
['prompt', 'prompt'],
|
|
74
|
+
['login_hint', 'loginHint'],
|
|
75
|
+
['ui_locales', 'uiLocales'],
|
|
76
|
+
['organization', 'organization'],
|
|
77
|
+
['return_to', 'returnTo'],
|
|
78
|
+
];
|
|
79
|
+
/** Parameters `forwardParams` may not name: the helper sets them, or they mean something else here. */
|
|
80
|
+
const NOT_FORWARDABLE = new Set([
|
|
81
|
+
'response_type',
|
|
82
|
+
'client_id',
|
|
83
|
+
'redirect_uri',
|
|
84
|
+
'state',
|
|
85
|
+
'nonce',
|
|
86
|
+
'code_challenge',
|
|
87
|
+
'code_challenge_method',
|
|
88
|
+
'scope',
|
|
89
|
+
'return_to',
|
|
90
|
+
'id_token_hint',
|
|
91
|
+
'request',
|
|
92
|
+
'request_uri',
|
|
93
|
+
]);
|
|
94
|
+
const MAX_FORWARDED_VALUE = 512;
|
|
95
|
+
const DEFAULT_TOUCH_INTERVAL_MS = 5 * 60_000;
|
|
96
|
+
/** How long a `requireRecentAuth(…, 0)` confirmation counts, in seconds. */
|
|
97
|
+
exports.CONFIRM_NOW_WINDOW_SECONDS = 60;
|
|
98
|
+
/** Create the relying party once, at startup; reuse it for every request. */
|
|
99
|
+
function createRelyingParty(config) {
|
|
100
|
+
if (typeof config.secret !== 'string' || config.secret.length < 32) {
|
|
101
|
+
throw new TypeError('createRelyingParty: `secret` (at least 32 characters) is required to seal the sign-in cookie.');
|
|
102
|
+
}
|
|
103
|
+
const afterSignIn = config.afterSignInPath ?? '/';
|
|
104
|
+
if (!(0, oidc_1.isSafeReturnTo)(afterSignIn))
|
|
105
|
+
throw new TypeError('createRelyingParty: `afterSignInPath` must be a path starting with a single `/`.');
|
|
106
|
+
const forwardParams = config.forwardParams ?? [];
|
|
107
|
+
for (const name of forwardParams) {
|
|
108
|
+
if (typeof name !== 'string' || !/^[A-Za-z0-9_.-]{1,64}$/.test(name) || NOT_FORWARDABLE.has(name)) {
|
|
109
|
+
throw new TypeError(`createRelyingParty: \`forwardParams\` cannot forward ${JSON.stringify(name)}: use a plain parameter name the helper does not set itself.`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const base = {
|
|
113
|
+
issuer: config.issuer,
|
|
114
|
+
clientId: config.clientId,
|
|
115
|
+
redirectUri: config.redirectUri,
|
|
116
|
+
secret: config.secret,
|
|
117
|
+
cookieName: config.cookieName ?? oidc_1.OIDC_FLOW_COOKIE,
|
|
118
|
+
fetchImpl: config.fetchImpl,
|
|
119
|
+
now: config.now,
|
|
120
|
+
};
|
|
121
|
+
const clock = () => (config.now ?? Date.now)();
|
|
122
|
+
const doFetch = (input, init) => (config.fetchImpl ?? fetch)(input, init);
|
|
123
|
+
const interval = config.touchIntervalMs ?? DEFAULT_TOUCH_INTERVAL_MS;
|
|
124
|
+
async function start(request, overrides = {}) {
|
|
125
|
+
const query = new URL(request.url).searchParams;
|
|
126
|
+
const fromQuery = {};
|
|
127
|
+
for (const [param, key] of PASSTHROUGH) {
|
|
128
|
+
const v = query.get(param);
|
|
129
|
+
// A bad return_to from the query is dropped, never an error page.
|
|
130
|
+
if (v && (key !== 'returnTo' || (0, oidc_1.isSafeReturnTo)(v)))
|
|
131
|
+
fromQuery[key] = v;
|
|
132
|
+
}
|
|
133
|
+
const maxAge = query.get('max_age');
|
|
134
|
+
if (maxAge !== null && /^\d{1,9}$/.test(maxAge))
|
|
135
|
+
fromQuery.maxAge = Number(maxAge);
|
|
136
|
+
const forwarded = {};
|
|
137
|
+
for (const name of forwardParams) {
|
|
138
|
+
const v = query.get(name);
|
|
139
|
+
if (v && v.length <= MAX_FORWARDED_VALUE)
|
|
140
|
+
forwarded[name] = v;
|
|
141
|
+
}
|
|
142
|
+
const extraParams = { ...forwarded, ...overrides.extraParams };
|
|
143
|
+
const { url, cookie } = await (0, oidc_1.oidcStart)({ ...base, scope: config.scope, ...fromQuery, ...overrides, extraParams });
|
|
144
|
+
return redirect(url, [cookie]);
|
|
145
|
+
}
|
|
146
|
+
async function callback(request) {
|
|
147
|
+
const identity = await (0, oidc_1.oidcCallback)({
|
|
148
|
+
...base,
|
|
149
|
+
clientSecret: config.clientSecret,
|
|
150
|
+
clientAuth: config.clientAuth,
|
|
151
|
+
query: new URL(request.url).searchParams,
|
|
152
|
+
cookie: request.headers.get('cookie'),
|
|
153
|
+
});
|
|
154
|
+
const c = identity.claims;
|
|
155
|
+
const user = {
|
|
156
|
+
sub: identity.sub,
|
|
157
|
+
sid: identity.sid,
|
|
158
|
+
email: identity.email,
|
|
159
|
+
email_verified: identity.emailVerified,
|
|
160
|
+
name: identity.name,
|
|
161
|
+
...(typeof c.external_id === 'string' && c.external_id ? { external_id: c.external_id } : {}),
|
|
162
|
+
...(identity.organization ? { org_id: identity.organization.id, org_role: identity.organization.role } : {}),
|
|
163
|
+
auth_time: identity.authTime,
|
|
164
|
+
};
|
|
165
|
+
const tokens = {
|
|
166
|
+
id_token: identity.idToken,
|
|
167
|
+
access_token: identity.accessToken,
|
|
168
|
+
...(identity.refreshToken ? { refresh_token: identity.refreshToken } : {}),
|
|
169
|
+
expires_at: identity.expiresAt,
|
|
170
|
+
};
|
|
171
|
+
return { user, tokens, identity, response: redirect(identity.returnTo ?? afterSignIn, [identity.clearCookie]) };
|
|
172
|
+
}
|
|
173
|
+
async function requireRecentAuth(request, maxAgeSeconds, options) {
|
|
174
|
+
if (!Number.isSafeInteger(maxAgeSeconds) || maxAgeSeconds < 0) {
|
|
175
|
+
throw new TypeError('requireRecentAuth: `maxAgeSeconds` must be a whole number of seconds, 0 or more.');
|
|
176
|
+
}
|
|
177
|
+
if (typeof options?.sub !== 'string' || !options.sub)
|
|
178
|
+
throw new TypeError('requireRecentAuth: `sub` is required.');
|
|
179
|
+
const at = options.authTime;
|
|
180
|
+
// 0 means "confirm now": it still passes for a minute after the confirmation,
|
|
181
|
+
// or the request that comes back from it would be sent to confirm again, forever.
|
|
182
|
+
const allowed = maxAgeSeconds === 0 ? exports.CONFIRM_NOW_WINDOW_SECONDS : maxAgeSeconds;
|
|
183
|
+
if (typeof at === 'number' && Number.isFinite(at) && clock() / 1000 - at <= allowed)
|
|
184
|
+
return null;
|
|
185
|
+
const here = new URL(request.url);
|
|
186
|
+
const returnTo = options.returnTo ?? `${here.pathname}${here.search}`;
|
|
187
|
+
const { url, cookie } = await (0, oidc_1.oidcStart)({
|
|
188
|
+
...base,
|
|
189
|
+
scope: config.scope,
|
|
190
|
+
maxAge: maxAgeSeconds,
|
|
191
|
+
expectedSub: options.sub,
|
|
192
|
+
...(maxAgeSeconds === 0 ? { prompt: 'login' } : {}),
|
|
193
|
+
...((0, oidc_1.isSafeReturnTo)(returnTo) ? { returnTo } : {}),
|
|
194
|
+
});
|
|
195
|
+
return redirect(url, [cookie]);
|
|
196
|
+
}
|
|
197
|
+
function sessionCall(sid, action) {
|
|
198
|
+
if (!config.secretKey)
|
|
199
|
+
throw new TypeError(`${action === 'touch' ? 'touchIfDue' : 'revokeSession'}: \`secretKey\` is not configured.`);
|
|
200
|
+
if (typeof sid !== 'string' || !sid)
|
|
201
|
+
throw new TypeError('A session id (`sid`) is required.');
|
|
202
|
+
const url = `${(config.apiUrl ?? request_1.DEFAULT_API_URL).replace(/\/+$/, '')}/v1/sessions/${encodeURIComponent(sid)}/${action}`;
|
|
203
|
+
return doFetch(url, { method: 'POST', headers: { authorization: `Bearer ${config.secretKey}`, accept: 'application/json' } });
|
|
204
|
+
}
|
|
205
|
+
async function touchIfDue(sid, lastTouchedAt) {
|
|
206
|
+
const now = clock();
|
|
207
|
+
if (typeof lastTouchedAt === 'number' && now - lastTouchedAt < interval && now >= lastTouchedAt) {
|
|
208
|
+
return { status: 'skipped', lastTouchedAt };
|
|
209
|
+
}
|
|
210
|
+
const res = await sessionCall(sid, 'touch');
|
|
211
|
+
const body = (await res.json().catch(() => null));
|
|
212
|
+
if (res.ok) {
|
|
213
|
+
return { status: 'touched', lastTouchedAt: now, idleExpiresAt: typeof body?.idle_expires_at === 'string' ? body.idle_expires_at : null };
|
|
214
|
+
}
|
|
215
|
+
if (res.status === 404)
|
|
216
|
+
return { status: 'ended', reason: 'not_found' };
|
|
217
|
+
if (res.status === 409) {
|
|
218
|
+
const code = errorsOf(body)[0]?.code;
|
|
219
|
+
if (code === 'SESSION_REVOKED')
|
|
220
|
+
return { status: 'ended', reason: 'revoked' };
|
|
221
|
+
if (code === 'SESSION_IDLE_EXPIRED')
|
|
222
|
+
return { status: 'ended', reason: 'idle_expired' };
|
|
223
|
+
return { status: 'ended', reason: 'expired' };
|
|
224
|
+
}
|
|
225
|
+
// A key, scope or availability problem is not "signed out": say so loudly.
|
|
226
|
+
throw new error_1.AtlasApiError(res.status, errorsOf(body));
|
|
227
|
+
}
|
|
228
|
+
async function revokeSession(sid) {
|
|
229
|
+
const res = await sessionCall(sid, 'revoke');
|
|
230
|
+
if (res.ok)
|
|
231
|
+
return 'revoked';
|
|
232
|
+
if (res.status === 404)
|
|
233
|
+
return 'not_found';
|
|
234
|
+
throw new error_1.AtlasApiError(res.status, errorsOf(await res.json().catch(() => null)));
|
|
235
|
+
}
|
|
236
|
+
async function signOut(options = {}) {
|
|
237
|
+
if (config.serverSignOut && options.sid && config.secretKey) {
|
|
238
|
+
const url = `${(config.apiUrl ?? request_1.DEFAULT_API_URL).replace(/\/+$/, '')}/v1/oauth_clients/${encodeURIComponent(config.clientId)}/logout`;
|
|
239
|
+
const res = await doFetch(url, {
|
|
240
|
+
method: 'POST',
|
|
241
|
+
headers: { authorization: `Bearer ${config.secretKey}`, accept: 'application/json', 'content-type': 'application/json' },
|
|
242
|
+
body: JSON.stringify({ sid: options.sid }),
|
|
243
|
+
});
|
|
244
|
+
// 404: this instance has no such session any more, so there is nothing left to end.
|
|
245
|
+
if (!res.ok && res.status !== 404)
|
|
246
|
+
throw new error_1.AtlasApiError(res.status, errorsOf(await res.json().catch(() => null)));
|
|
247
|
+
const target = new URL(options.postLogoutRedirectUri ?? config.postLogoutRedirectUri ?? '/', 'http://relative.invalid');
|
|
248
|
+
if (options.state)
|
|
249
|
+
target.searchParams.set('state', options.state);
|
|
250
|
+
return redirect(target.origin === 'http://relative.invalid' ? `${target.pathname}${target.search}` : target.toString(), []);
|
|
251
|
+
}
|
|
252
|
+
if (options.sid && config.secretKey)
|
|
253
|
+
await revokeSession(options.sid);
|
|
254
|
+
const url = await (0, oidc_1.oidcLogoutUrl)({
|
|
255
|
+
...base,
|
|
256
|
+
postLogoutRedirectUri: options.postLogoutRedirectUri ?? config.postLogoutRedirectUri,
|
|
257
|
+
idTokenHint: options.idToken,
|
|
258
|
+
state: options.state,
|
|
259
|
+
});
|
|
260
|
+
return redirect(url, options.setCookies ?? []);
|
|
261
|
+
}
|
|
262
|
+
async function handle(request) {
|
|
263
|
+
if (!config.onSignedIn)
|
|
264
|
+
throw new TypeError('handle: `onSignedIn` is not configured; it is where your app creates its session.');
|
|
265
|
+
const { pathname } = new URL(request.url);
|
|
266
|
+
const method = request.method.toUpperCase();
|
|
267
|
+
const routes = config.routes ?? {};
|
|
268
|
+
if (method === 'GET' && pathname === (routes.signIn ?? '/auth/sign-in'))
|
|
269
|
+
return start(request);
|
|
270
|
+
if (method === 'GET' && pathname === (routes.callback ?? new URL(config.redirectUri).pathname)) {
|
|
271
|
+
let result;
|
|
272
|
+
try {
|
|
273
|
+
result = await callback(request);
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
if (!(error instanceof oidc_1.AtlasOidcError))
|
|
277
|
+
throw error;
|
|
278
|
+
const failed = config.onSignInError
|
|
279
|
+
? await config.onSignInError(error, request)
|
|
280
|
+
: new Response('Sign-in failed. Please try again.', { status: 400, headers: { 'content-type': 'text/plain; charset=utf-8', 'cache-control': 'no-store' } });
|
|
281
|
+
return withCookie(failed, error.clearCookie);
|
|
282
|
+
}
|
|
283
|
+
const own = await config.onSignedIn(result.user, result.tokens, request, result);
|
|
284
|
+
return own instanceof Response ? withCookie(own, result.identity.clearCookie) : result.response;
|
|
285
|
+
}
|
|
286
|
+
if ((method === 'POST' || method === 'GET') && pathname === (routes.signOut ?? '/auth/sign-out')) {
|
|
287
|
+
return signOut((await config.onSignOut?.(request)) ?? {});
|
|
288
|
+
}
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
return { start, callback, requireRecentAuth, touchIfDue, revokeSession, signOut, handle };
|
|
292
|
+
}
|
|
293
|
+
/** A copy of `response` with mutable headers and one more `Set-Cookie`. */
|
|
294
|
+
function withCookie(response, cookie) {
|
|
295
|
+
const out = new Response(response.body, response);
|
|
296
|
+
if (cookie)
|
|
297
|
+
out.headers.append('set-cookie', cookie);
|
|
298
|
+
return out;
|
|
299
|
+
}
|
|
300
|
+
/** A 302 built with `new Response` (not `Response.redirect`), so its headers stay mutable. */
|
|
301
|
+
function redirect(location, cookies) {
|
|
302
|
+
const headers = new Headers({ location, 'cache-control': 'no-store' });
|
|
303
|
+
for (const c of cookies)
|
|
304
|
+
headers.append('set-cookie', c);
|
|
305
|
+
return new Response(null, { status: 302, headers });
|
|
306
|
+
}
|
|
307
|
+
function errorsOf(body) {
|
|
308
|
+
const errors = body?.errors;
|
|
309
|
+
return Array.isArray(errors) ? errors : [];
|
|
310
|
+
}
|
|
311
|
+
//# sourceMappingURL=rp.js.map
|
package/dist/rp.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rp.js","sourceRoot":"","sources":["../src/rp.ts"],"names":[],"mappings":"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"}
|
package/dist/verify.d.ts
CHANGED
|
@@ -175,6 +175,14 @@ export interface AtlasBackendOptions {
|
|
|
175
175
|
jwks?: Jwks;
|
|
176
176
|
/** Expected `iss`. Required — an unchecked issuer accepts any Atlas instance. */
|
|
177
177
|
issuer: string;
|
|
178
|
+
/**
|
|
179
|
+
* Earlier issuers of THIS instance, also accepted: its old sign-in host while
|
|
180
|
+
* the instance moves to a new one (a custom domain or a path mount going
|
|
181
|
+
* active). The keys are the instance's own, so nothing else changes. The
|
|
182
|
+
* instance's discovery document lists them in `atlas_previous_issuers` with
|
|
183
|
+
* an `accepted_until`; remove them after that.
|
|
184
|
+
*/
|
|
185
|
+
previousIssuers?: readonly string[];
|
|
178
186
|
/**
|
|
179
187
|
* §7.3 optional azp allowlist. When set, a token minted for a different
|
|
180
188
|
* origin is refused, which is what stops a token issued to one of the
|
package/dist/verify.js
CHANGED
|
@@ -181,7 +181,9 @@ class AtlasBackend {
|
|
|
181
181
|
// Pinned. Without this, a token whose header says `alg: none` — or any
|
|
182
182
|
// algorithm the key material can be coerced into — verifies.
|
|
183
183
|
algorithms: algs_1.ATLAS_SIGNING_ALGS,
|
|
184
|
-
issuer: this.options.
|
|
184
|
+
issuer: this.options.previousIssuers?.length
|
|
185
|
+
? [this.options.issuer, ...this.options.previousIssuers]
|
|
186
|
+
: this.options.issuer,
|
|
185
187
|
// §S4 enforce `aud` ONLY when the caller configured an expected audience.
|
|
186
188
|
// Unset leaves `aud` unchecked, so a token carrying its default
|
|
187
189
|
// instance-id audience still verifies (backwards compatible).
|