@starci/auth 0.4.0 → 0.6.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 +17 -0
- package/README.md +104 -13
- package/dist/client/oidc/index.d.ts +5 -0
- package/dist/client/oidc/index.js +3 -0
- package/dist/client/oidc/oidc-client.d.ts +85 -0
- package/dist/client/oidc/oidc-client.js +304 -0
- package/dist/client/oidc/pkce.d.ts +15 -0
- package/dist/client/oidc/pkce.js +29 -0
- package/dist/contracts/index.d.ts +12 -0
- package/dist/contracts/index.js +14 -0
- package/dist/contracts/oidc.d.ts +148 -0
- package/dist/contracts/oidc.js +68 -0
- package/dist/oidc/admin.d.ts +42 -0
- package/dist/oidc/admin.js +139 -0
- package/dist/oidc/broker.d.ts +57 -0
- package/dist/oidc/broker.js +77 -0
- package/dist/oidc/client.d.ts +58 -0
- package/dist/oidc/client.js +119 -0
- package/dist/oidc/discovery.d.ts +23 -0
- package/dist/oidc/discovery.js +64 -0
- package/dist/oidc/errors.d.ts +10 -0
- package/dist/oidc/errors.js +10 -0
- package/dist/oidc/grants.d.ts +41 -0
- package/dist/oidc/grants.js +82 -0
- package/dist/oidc/index.d.ts +35 -0
- package/dist/oidc/index.js +18 -0
- package/dist/oidc/issuers/github-actions.d.ts +54 -0
- package/dist/oidc/issuers/github-actions.js +84 -0
- package/dist/oidc/issuers/google.d.ts +30 -0
- package/dist/oidc/issuers/google.js +39 -0
- package/dist/oidc/issuers/index.d.ts +8 -0
- package/dist/oidc/issuers/index.js +4 -0
- package/dist/oidc/issuers/issuer.d.ts +79 -0
- package/dist/oidc/issuers/issuer.js +39 -0
- package/dist/oidc/issuers/keycloak.d.ts +26 -0
- package/dist/oidc/issuers/keycloak.js +41 -0
- package/dist/oidc/jwks.d.ts +26 -0
- package/dist/oidc/jwks.js +80 -0
- package/dist/oidc/oidc.d.ts +32 -0
- package/dist/oidc/oidc.js +21 -0
- package/dist/oidc/oidc.module.d.ts +34 -0
- package/dist/oidc/oidc.module.js +51 -0
- package/dist/oidc/oidc.tokens.d.ts +18 -0
- package/dist/oidc/oidc.tokens.js +18 -0
- package/dist/oidc/options.d.ts +119 -0
- package/dist/oidc/options.js +98 -0
- package/dist/oidc/primitives.d.ts +15 -0
- package/dist/oidc/primitives.js +11 -0
- package/dist/oidc/refresh.d.ts +10 -0
- package/dist/oidc/refresh.js +24 -0
- package/dist/oidc/roles.d.ts +12 -0
- package/dist/oidc/roles.js +51 -0
- package/dist/oidc/tokens.d.ts +26 -0
- package/dist/oidc/tokens.js +79 -0
- package/dist/oidc/transport.d.ts +8 -0
- package/dist/oidc/transport.js +19 -0
- package/dist/oidc/verifier.d.ts +24 -0
- package/dist/oidc/verifier.js +64 -0
- package/dist/server/auth.module.d.ts +1 -1
- package/dist/server/auth.module.js +20 -6
- package/dist/server/auth.tokens.d.ts +2 -0
- package/dist/server/auth.tokens.js +2 -0
- package/dist/server/doors.d.ts +14 -8
- package/dist/server/doors.js +22 -11
- package/dist/server/errors.d.ts +2 -2
- package/dist/server/errors.js +3 -5
- package/dist/server/flows.d.ts +13 -5
- package/dist/server/flows.js +40 -20
- package/dist/server/index.d.ts +7 -7
- package/dist/server/index.js +3 -4
- package/dist/server/options.d.ts +11 -13
- package/dist/server/ports.d.ts +8 -1
- package/dist/server/primitives.d.ts +0 -2
- package/dist/server/primitives.js +2 -3
- package/dist/server/providers/github.d.ts +59 -0
- package/dist/server/providers/github.js +111 -0
- package/dist/server/providers/google.d.ts +56 -0
- package/dist/server/providers/google.js +87 -0
- package/dist/server/providers/index.d.ts +14 -0
- package/dist/server/providers/index.js +9 -0
- package/dist/server/providers/oauth-state.d.ts +47 -0
- package/dist/server/providers/oauth-state.js +74 -0
- package/dist/server/providers/provider.d.ts +58 -0
- package/dist/server/providers/provider.js +32 -0
- package/dist/server/providers/registry.d.ts +30 -0
- package/dist/server/providers/registry.js +37 -0
- package/dist/server/strategies.d.ts +3 -18
- package/dist/server/strategies.js +2 -41
- package/dist/server/verifier.d.ts +1 -1
- package/dist/testing/index.d.ts +3 -0
- package/dist/testing/index.js +2 -0
- package/dist/testing/oidc.d.ts +175 -0
- package/dist/testing/oidc.js +493 -0
- package/package.json +24 -5
- package/dist/server/google-state.d.ts +0 -33
- package/dist/server/google-state.js +0 -73
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { JWTPayload } from "jose";
|
|
2
|
+
import type { OidcAlgorithm, OidcOutcome, OidcRefusalReason } from "../../contracts/oidc.js";
|
|
3
|
+
import type { OidcHttp, OidcJwksOptions } from "../options.js";
|
|
4
|
+
import type { OidcClock } from "../primitives.js";
|
|
5
|
+
/** Where an issuer's keys come from: an address the preset pins (Google, GitHub Actions), or the issuer's discovery document (Keycloak). */
|
|
6
|
+
export type IssuerKeys = {
|
|
7
|
+
readonly kind: "jwks";
|
|
8
|
+
readonly uri: string;
|
|
9
|
+
} | {
|
|
10
|
+
readonly kind: "discovery";
|
|
11
|
+
};
|
|
12
|
+
/** The addresses of a test double standing in for an issuer (the fake issuer of `@starci/auth/testing`); a deployment never sets them. */
|
|
13
|
+
export interface IssuerEndpoints {
|
|
14
|
+
/** The `iss` the double signs with. */
|
|
15
|
+
readonly issuer: string;
|
|
16
|
+
/** The double's key set address. */
|
|
17
|
+
readonly jwksUri: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* One identity issuer whose signed tokens an app verifies: who signs (issuer), with what (algorithms, pinned before any key is looked up),
|
|
21
|
+
* for whom (audience, never empty), where the keys are, which claims a token must carry, and how verified claims become what the app reads
|
|
22
|
+
* (`identityOf`, which may refuse a token whose claims break the preset's policy). A preset is data plus one mapping; the verifier is the
|
|
23
|
+
* package's (`createIssuerVerifier`).
|
|
24
|
+
*/
|
|
25
|
+
export interface IssuerPreset<T> {
|
|
26
|
+
/** The preset name, for a log line (`github-actions`). */
|
|
27
|
+
readonly name: string;
|
|
28
|
+
/** The exact issuer. */
|
|
29
|
+
readonly issuer: string;
|
|
30
|
+
/** Other spellings of the same issuer the `iss` claim may carry (`accounts.google.com`). */
|
|
31
|
+
readonly issuerAliases: readonly string[];
|
|
32
|
+
/** Where the keys come from. */
|
|
33
|
+
readonly keys: IssuerKeys;
|
|
34
|
+
/** The algorithms a token may be signed with. */
|
|
35
|
+
readonly algorithms: readonly OidcAlgorithm[];
|
|
36
|
+
/** The audience a token must name: one of these. */
|
|
37
|
+
readonly audience: readonly string[];
|
|
38
|
+
/** Claims a token must carry beyond `exp` and `sub`. */
|
|
39
|
+
readonly requiredClaims: readonly string[];
|
|
40
|
+
/** What the verified claims stand for, or a refusal naming the reason and the claim. */
|
|
41
|
+
readonly identityOf: (claims: JWTPayload) => IssuerMapping<T>;
|
|
42
|
+
}
|
|
43
|
+
/** The result of a preset's claim mapping. */
|
|
44
|
+
export type IssuerMapping<T> = {
|
|
45
|
+
readonly kind: "ok";
|
|
46
|
+
readonly value: T;
|
|
47
|
+
} | {
|
|
48
|
+
readonly kind: "refused";
|
|
49
|
+
readonly reason: OidcRefusalReason;
|
|
50
|
+
readonly claim?: string;
|
|
51
|
+
};
|
|
52
|
+
/** A mapped identity. */
|
|
53
|
+
export declare const mapped: <T>(value: T) => IssuerMapping<T>;
|
|
54
|
+
/** A refused mapping: the reason, and the claim that decided it. */
|
|
55
|
+
export declare const unmapped: (reason: OidcRefusalReason, claim?: string) => IssuerMapping<never>;
|
|
56
|
+
/** The transport and timing of an issuer verifier; the preset decides everything that is trust. */
|
|
57
|
+
export interface IssuerVerifierOptions {
|
|
58
|
+
/** The outbound HTTP port (the platform `fetch` when absent): the app's bounded client. */
|
|
59
|
+
readonly http?: OidcHttp | undefined;
|
|
60
|
+
/** The clock (system time when absent). */
|
|
61
|
+
readonly clock?: OidcClock | undefined;
|
|
62
|
+
/** The deadline of every key or discovery request, in milliseconds (10000). */
|
|
63
|
+
readonly timeoutMs?: number | undefined;
|
|
64
|
+
/** The clock skew a token's `exp` and `nbf` tolerate, in seconds (5). */
|
|
65
|
+
readonly clockToleranceSeconds?: number | undefined;
|
|
66
|
+
/** The key set cache. */
|
|
67
|
+
readonly jwks?: OidcJwksOptions | undefined;
|
|
68
|
+
/** Admit `http:` addresses: only for a test double on a developer machine. */
|
|
69
|
+
readonly allowInsecureHttp?: boolean | undefined;
|
|
70
|
+
}
|
|
71
|
+
/** A verifier of one issuer's tokens. */
|
|
72
|
+
export interface IssuerVerifier<T> {
|
|
73
|
+
/** The preset it verifies for. */
|
|
74
|
+
readonly preset: IssuerPreset<T>;
|
|
75
|
+
/** What a token stands for when it passes the algorithm pin, the signature against the issuer's keys, issuer, audience, expiry, the required claims and the preset's policy; else a refusal. An issuer that cannot be reached throws `Unavailable`. */
|
|
76
|
+
verify(token: string, now?: Date): Promise<OidcOutcome<T>>;
|
|
77
|
+
}
|
|
78
|
+
/** Builds the verifier of one preset; a preset that does not meet the contract (no audience, an admitted algorithm list, an address) throws `ConfigurationInvalid`. */
|
|
79
|
+
export declare const createIssuerVerifier: <T>(preset: IssuerPreset<T>, options?: IssuerVerifierOptions) => IssuerVerifier<T>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { OIDC_ACCESS_TOKEN_INVALID, OIDC_ERROR_CODES } from "../../contracts/oidc.js";
|
|
2
|
+
import { OidcDiscovery } from "../discovery.js";
|
|
3
|
+
import { OidcError } from "../errors.js";
|
|
4
|
+
import { OidcKeys } from "../jwks.js";
|
|
5
|
+
import { isAdmissibleUrl, resolveOptions } from "../options.js";
|
|
6
|
+
import { OidcTokenVerifier } from "../tokens.js";
|
|
7
|
+
/** A mapped identity. */
|
|
8
|
+
export const mapped = (value) => ({ kind: "ok", value });
|
|
9
|
+
/** A refused mapping: the reason, and the claim that decided it. */
|
|
10
|
+
export const unmapped = (reason, claim) => (claim === undefined ? { kind: "refused", reason } : { kind: "refused", reason, claim });
|
|
11
|
+
const refuse = (reason, claim) => ({
|
|
12
|
+
kind: "refused",
|
|
13
|
+
code: OIDC_ACCESS_TOKEN_INVALID,
|
|
14
|
+
detail: claim === undefined ? { reason } : { reason, claim },
|
|
15
|
+
});
|
|
16
|
+
const keySourceOf = (preset, options) => {
|
|
17
|
+
if (preset.keys.kind === "discovery")
|
|
18
|
+
return new OidcDiscovery(options);
|
|
19
|
+
const uri = preset.keys.uri;
|
|
20
|
+
if (!isAdmissibleUrl(uri, options.allowInsecureHttp))
|
|
21
|
+
throw new OidcError(OIDC_ERROR_CODES.ConfigurationInvalid, { params: { key: "keys.uri" } });
|
|
22
|
+
return { get: () => Promise.resolve({ jwksUri: uri }) };
|
|
23
|
+
};
|
|
24
|
+
/** Builds the verifier of one preset; a preset that does not meet the contract (no audience, an admitted algorithm list, an address) throws `ConfigurationInvalid`. */
|
|
25
|
+
export const createIssuerVerifier = (preset, options = {}) => {
|
|
26
|
+
const resolved = resolveOptions({ ...options, issuer: preset.issuer, audience: preset.audience, algorithms: preset.algorithms });
|
|
27
|
+
const tokens = new OidcTokenVerifier(resolved, new OidcKeys(resolved, keySourceOf(preset, resolved)));
|
|
28
|
+
const issuers = [preset.issuer, ...preset.issuerAliases];
|
|
29
|
+
return {
|
|
30
|
+
preset,
|
|
31
|
+
async verify(token, now = resolved.clock.now()) {
|
|
32
|
+
const verified = await tokens.verify(token, { audience: resolved.audience, issuer: issuers, requiredClaims: preset.requiredClaims }, now);
|
|
33
|
+
if (verified.kind === "refused")
|
|
34
|
+
return verified;
|
|
35
|
+
const identity = preset.identityOf(verified.value);
|
|
36
|
+
return identity.kind === "ok" ? { kind: "ok", value: identity.value } : refuse(identity.reason, identity.claim);
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { OidcAlgorithm, OidcPrincipal } from "../../contracts/oidc.js";
|
|
2
|
+
import type { OidcOptions, OidcRoleMapping } from "../options.js";
|
|
3
|
+
import type { IssuerEndpoints, IssuerPreset } from "./issuer.js";
|
|
4
|
+
/** A Keycloak realm and what its access tokens must say. */
|
|
5
|
+
export interface KeycloakIssuerOptions {
|
|
6
|
+
/** The Keycloak origin (`https://id.example.com`). */
|
|
7
|
+
readonly origin: string;
|
|
8
|
+
/** The realm. */
|
|
9
|
+
readonly realm: string;
|
|
10
|
+
/** The audience an access token must name: one of these. */
|
|
11
|
+
readonly audience: string | readonly string[];
|
|
12
|
+
/** The algorithms (`RS256`, what Keycloak signs with by default). */
|
|
13
|
+
readonly algorithms?: readonly OidcAlgorithm[] | undefined;
|
|
14
|
+
/** When set, an access token's `azp` must equal it. */
|
|
15
|
+
readonly authorizedParty?: string | undefined;
|
|
16
|
+
/** Which roles become the principal's roles (the realm roles). */
|
|
17
|
+
readonly roles?: OidcRoleMapping | undefined;
|
|
18
|
+
/** A test double's issuer (its keys come from its discovery document like Keycloak's). */
|
|
19
|
+
readonly endpoints?: Pick<IssuerEndpoints, "issuer"> | undefined;
|
|
20
|
+
}
|
|
21
|
+
/** The issuer address of a Keycloak realm: `<origin>/realms/<realm>`. */
|
|
22
|
+
export declare const keycloakIssuerUrl: (origin: string, realm: string) => string;
|
|
23
|
+
/** The trust half of an `OidcModule` registration for a Keycloak realm: spread it with the app's transport (`OidcModule.register({ ...keycloakOptions(o), http, clock })`). */
|
|
24
|
+
export declare const keycloakOptions: (options: KeycloakIssuerOptions) => Pick<OidcOptions, "issuer" | "audience" | "algorithms" | "authorizedParty" | "roles">;
|
|
25
|
+
/** The Keycloak preset: realm access tokens, keys from the realm's discovery document, the principal the module's verifier builds. */
|
|
26
|
+
export declare const keycloakIssuer: (options: KeycloakIssuerOptions) => IssuerPreset<OidcPrincipal>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { OIDC_DEFAULT_ALGORITHMS } from "../../contracts/oidc.js";
|
|
2
|
+
import { principalOf } from "../verifier.js";
|
|
3
|
+
import { mapped, unmapped } from "./issuer.js";
|
|
4
|
+
/** The issuer address of a Keycloak realm: `<origin>/realms/<realm>`. */
|
|
5
|
+
export const keycloakIssuerUrl = (origin, realm) => `${origin.replace(/\/+$/u, "")}/realms/${encodeURIComponent(realm)}`;
|
|
6
|
+
/** The trust half of an `OidcModule` registration for a Keycloak realm: spread it with the app's transport (`OidcModule.register({ ...keycloakOptions(o), http, clock })`). */
|
|
7
|
+
export const keycloakOptions = (options) => ({
|
|
8
|
+
issuer: options.endpoints?.issuer ?? keycloakIssuerUrl(options.origin, options.realm),
|
|
9
|
+
audience: options.audience,
|
|
10
|
+
algorithms: options.algorithms ?? OIDC_DEFAULT_ALGORITHMS,
|
|
11
|
+
authorizedParty: options.authorizedParty,
|
|
12
|
+
roles: options.roles,
|
|
13
|
+
});
|
|
14
|
+
/** The Keycloak preset: realm access tokens, keys from the realm's discovery document, the principal the module's verifier builds. */
|
|
15
|
+
export const keycloakIssuer = (options) => {
|
|
16
|
+
const trust = keycloakOptions(options);
|
|
17
|
+
const audience = typeof options.audience === "string" ? [options.audience] : options.audience;
|
|
18
|
+
const rules = {
|
|
19
|
+
issuer: trust.issuer,
|
|
20
|
+
authorizedParty: options.authorizedParty,
|
|
21
|
+
realmRoles: options.roles?.realm ?? true,
|
|
22
|
+
roleClients: options.roles?.clients ?? [],
|
|
23
|
+
roleClaims: options.roles?.claims ?? [],
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
name: "keycloak",
|
|
27
|
+
issuer: trust.issuer,
|
|
28
|
+
issuerAliases: [],
|
|
29
|
+
keys: { kind: "discovery" },
|
|
30
|
+
algorithms: trust.algorithms ?? OIDC_DEFAULT_ALGORITHMS,
|
|
31
|
+
audience,
|
|
32
|
+
requiredClaims: [],
|
|
33
|
+
identityOf: (claims) => {
|
|
34
|
+
const principal = principalOf(claims, rules);
|
|
35
|
+
if (principal.kind === "ok")
|
|
36
|
+
return mapped(principal.value);
|
|
37
|
+
const reason = principal.detail?.reason;
|
|
38
|
+
return unmapped(reason === "token-type" || reason === "authorized-party" ? reason : "claims");
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { createLocalJWKSet } from "jose";
|
|
2
|
+
import type { JSONWebKeySet } from "jose";
|
|
3
|
+
import type { OidcMetadata } from "./discovery.js";
|
|
4
|
+
import type { ResolvedOidcOptions } from "./options.js";
|
|
5
|
+
/** The key set jose selects a verification key from. */
|
|
6
|
+
export type OidcKeySet = ReturnType<typeof createLocalJWKSet>;
|
|
7
|
+
/** Where the key set address comes from: the discovery document (`OidcDiscovery`), or an address an issuer preset pins. */
|
|
8
|
+
export interface OidcKeySource {
|
|
9
|
+
get(): Promise<Pick<OidcMetadata, "jwksUri">>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Narrows a fetched document to public signature keys. Keys the provider marks for encryption are dropped (Keycloak publishes
|
|
13
|
+
* them beside its signing keys); a document with no key left, with more than `maxKeys`, or with a private or symmetric key is rejected.
|
|
14
|
+
*/
|
|
15
|
+
export declare const readKeySet: (value: unknown, maxKeys: number) => JSONWebKeySet;
|
|
16
|
+
/**
|
|
17
|
+
* The provider's signing keys: fetched from the discovered `jwks_uri`, cached for `ttlMs`, and refreshed when a token names a `kid` the
|
|
18
|
+
* cache lacks (the provider rotated), at most once per `refreshCooldownMs`. When the provider cannot be reached the expired set is still
|
|
19
|
+
* used for `staleTtlMs`, so a provider outage does not sign everybody out; past that the call throws `Unavailable`. Concurrent callers share one fetch.
|
|
20
|
+
*/
|
|
21
|
+
export declare class OidcKeys {
|
|
22
|
+
#private;
|
|
23
|
+
constructor(options: ResolvedOidcOptions, discovery: OidcKeySource);
|
|
24
|
+
/** The key set to verify a token whose header names `kid` (undefined when it names none). */
|
|
25
|
+
keysFor(kid: string | undefined): Promise<OidcKeySet>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { createLocalJWKSet } from "jose";
|
|
2
|
+
import { OIDC_ERROR_CODES } from "../contracts/oidc.js";
|
|
3
|
+
import { OidcError } from "./errors.js";
|
|
4
|
+
import { isRecord } from "./primitives.js";
|
|
5
|
+
/** The members that make a JWK private or symmetric: a key set carrying one is never verification input. */
|
|
6
|
+
const PRIVATE_MEMBERS = ["d", "p", "q", "dp", "dq", "qi", "k", "oth"];
|
|
7
|
+
const KEY_TYPES = ["RSA", "EC", "OKP"];
|
|
8
|
+
const isPublicKey = (key) => isRecord(key) && typeof key.kty === "string" && KEY_TYPES.includes(key.kty) && PRIVATE_MEMBERS.every((member) => key[member] === undefined);
|
|
9
|
+
/**
|
|
10
|
+
* Narrows a fetched document to public signature keys. Keys the provider marks for encryption are dropped (Keycloak publishes
|
|
11
|
+
* them beside its signing keys); a document with no key left, with more than `maxKeys`, or with a private or symmetric key is rejected.
|
|
12
|
+
*/
|
|
13
|
+
export const readKeySet = (value, maxKeys) => {
|
|
14
|
+
if (!isRecord(value) || !Array.isArray(value.keys) || value.keys.length === 0 || value.keys.length > maxKeys)
|
|
15
|
+
throw new OidcError(OIDC_ERROR_CODES.Rejected);
|
|
16
|
+
const keys = value.keys;
|
|
17
|
+
if (!keys.every(isPublicKey))
|
|
18
|
+
throw new OidcError(OIDC_ERROR_CODES.Rejected);
|
|
19
|
+
const signing = keys.filter(isPublicKey).filter((key) => key.use !== "enc");
|
|
20
|
+
if (signing.length === 0)
|
|
21
|
+
throw new OidcError(OIDC_ERROR_CODES.Rejected);
|
|
22
|
+
// Every key was checked above to be public key material of a known type; jose checks the members it needs when it selects a key.
|
|
23
|
+
return { keys: signing.map((key) => ({ ...key })) };
|
|
24
|
+
};
|
|
25
|
+
/** The key ids a held set publishes. */
|
|
26
|
+
const kidsOf = (set) => new Set(set.keys.flatMap((key) => (typeof key.kid === "string" ? [key.kid] : [])));
|
|
27
|
+
/**
|
|
28
|
+
* The provider's signing keys: fetched from the discovered `jwks_uri`, cached for `ttlMs`, and refreshed when a token names a `kid` the
|
|
29
|
+
* cache lacks (the provider rotated), at most once per `refreshCooldownMs`. When the provider cannot be reached the expired set is still
|
|
30
|
+
* used for `staleTtlMs`, so a provider outage does not sign everybody out; past that the call throws `Unavailable`. Concurrent callers share one fetch.
|
|
31
|
+
*/
|
|
32
|
+
export class OidcKeys {
|
|
33
|
+
#options;
|
|
34
|
+
#discovery;
|
|
35
|
+
#held;
|
|
36
|
+
#pending;
|
|
37
|
+
constructor(options, discovery) {
|
|
38
|
+
this.#options = options;
|
|
39
|
+
this.#discovery = discovery;
|
|
40
|
+
}
|
|
41
|
+
/** The key set to verify a token whose header names `kid` (undefined when it names none). */
|
|
42
|
+
async keysFor(kid) {
|
|
43
|
+
const options = this.#options;
|
|
44
|
+
const now = options.clock.now().getTime();
|
|
45
|
+
const held = this.#held;
|
|
46
|
+
if (held !== undefined) {
|
|
47
|
+
const age = now - held.at;
|
|
48
|
+
const known = kid === undefined || kidsOf(held.set).has(kid);
|
|
49
|
+
if (age < options.jwksTtlMs && (known || age < options.jwksCooldownMs))
|
|
50
|
+
return held.local;
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
return (await this.#refresh()).local;
|
|
54
|
+
}
|
|
55
|
+
catch (cause) {
|
|
56
|
+
if (held !== undefined && now - held.at < options.jwksStaleMs)
|
|
57
|
+
return held.local;
|
|
58
|
+
throw cause;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
#refresh() {
|
|
62
|
+
this.#pending ??= this.#fetch().finally(() => {
|
|
63
|
+
this.#pending = undefined;
|
|
64
|
+
});
|
|
65
|
+
return this.#pending;
|
|
66
|
+
}
|
|
67
|
+
async #fetch() {
|
|
68
|
+
const options = this.#options;
|
|
69
|
+
const { jwksUri } = await this.#discovery.get();
|
|
70
|
+
const response = await options.http.request({ method: "GET", url: jwksUri, timeoutMs: options.timeoutMs }).catch((cause) => {
|
|
71
|
+
throw new OidcError(OIDC_ERROR_CODES.Unavailable, { cause });
|
|
72
|
+
});
|
|
73
|
+
if (response.status !== 200)
|
|
74
|
+
throw new OidcError(OIDC_ERROR_CODES.Unavailable);
|
|
75
|
+
const set = readKeySet(response.body, options.jwksMaxKeys);
|
|
76
|
+
const held = { set, local: createLocalJWKSet(set), at: options.clock.now().getTime() };
|
|
77
|
+
this.#held = held;
|
|
78
|
+
return held;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { OidcAdminClient } from "./admin.js";
|
|
2
|
+
import { OidcBroker } from "./broker.js";
|
|
3
|
+
import { OidcClient } from "./client.js";
|
|
4
|
+
import { OidcDiscovery } from "./discovery.js";
|
|
5
|
+
import { OidcTokenClient } from "./grants.js";
|
|
6
|
+
import { OidcKeys } from "./jwks.js";
|
|
7
|
+
import type { OidcOptions, ResolvedOidcOptions } from "./options.js";
|
|
8
|
+
import { OidcTokenVerifier } from "./tokens.js";
|
|
9
|
+
import { OidcPrincipalVerifier } from "./verifier.js";
|
|
10
|
+
/** The parts of one OIDC registration, built from validated options. */
|
|
11
|
+
export interface Oidc {
|
|
12
|
+
/** The options after validation, defaults applied. */
|
|
13
|
+
readonly options: ResolvedOidcOptions;
|
|
14
|
+
/** The discovery document of the issuer. */
|
|
15
|
+
readonly discovery: OidcDiscovery;
|
|
16
|
+
/** The provider's signing keys. */
|
|
17
|
+
readonly keys: OidcKeys;
|
|
18
|
+
/** The verifier of the provider's signed tokens. */
|
|
19
|
+
readonly tokens: OidcTokenVerifier;
|
|
20
|
+
/** The `PrincipalVerifier` guards inject. */
|
|
21
|
+
readonly verifier: OidcPrincipalVerifier;
|
|
22
|
+
/** The authorization code flow client; null when the registration declares no client. */
|
|
23
|
+
readonly client: OidcClient | null;
|
|
24
|
+
/** The server-side grants (password, refresh, revocation); each call throws a `ConfigurationInvalid` error when the registration declares no client. */
|
|
25
|
+
readonly tokenClient: OidcTokenClient;
|
|
26
|
+
/** The provider's admin API as the client's service account; needs a client secret. */
|
|
27
|
+
readonly admin: OidcAdminClient;
|
|
28
|
+
/** The facade an app binds its identity port to: grants, social sign-in and account administration. */
|
|
29
|
+
readonly broker: OidcBroker;
|
|
30
|
+
}
|
|
31
|
+
/** Builds one registration without Nest: the module uses it, and so can a script or a spec. Invalid options throw a `ConfigurationInvalid` error. */
|
|
32
|
+
export declare const createOidc: (input: OidcOptions) => Oidc;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { OidcAdminClient } from "./admin.js";
|
|
2
|
+
import { OidcBroker } from "./broker.js";
|
|
3
|
+
import { OidcClient } from "./client.js";
|
|
4
|
+
import { OidcDiscovery } from "./discovery.js";
|
|
5
|
+
import { OidcTokenClient } from "./grants.js";
|
|
6
|
+
import { OidcKeys } from "./jwks.js";
|
|
7
|
+
import { resolveOptions } from "./options.js";
|
|
8
|
+
import { OidcTokenVerifier } from "./tokens.js";
|
|
9
|
+
import { OidcPrincipalVerifier } from "./verifier.js";
|
|
10
|
+
/** Builds one registration without Nest: the module uses it, and so can a script or a spec. Invalid options throw a `ConfigurationInvalid` error. */
|
|
11
|
+
export const createOidc = (input) => {
|
|
12
|
+
const options = resolveOptions(input);
|
|
13
|
+
const discovery = new OidcDiscovery(options);
|
|
14
|
+
const keys = new OidcKeys(options, discovery);
|
|
15
|
+
const tokens = new OidcTokenVerifier(options, keys);
|
|
16
|
+
const verifier = new OidcPrincipalVerifier(options, tokens);
|
|
17
|
+
const client = options.client === undefined ? null : new OidcClient(options, discovery, tokens);
|
|
18
|
+
const tokenClient = new OidcTokenClient(options, discovery, verifier);
|
|
19
|
+
const admin = new OidcAdminClient(options, discovery);
|
|
20
|
+
return { options, discovery, keys, tokens, verifier, client, tokenClient, admin, broker: new OidcBroker(tokenClient, client, admin) };
|
|
21
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ConfigurableModuleHost, DynamicModule } from "@nestjs/common";
|
|
2
|
+
import type { OidcOptions } from "./options.js";
|
|
3
|
+
/** The extras every registration sets beside the options. */
|
|
4
|
+
export interface OidcModuleExtras {
|
|
5
|
+
/** Whether the module is global. */
|
|
6
|
+
readonly isGlobal: boolean;
|
|
7
|
+
}
|
|
8
|
+
type Host = ConfigurableModuleHost<OidcOptions, "register", "createOidcOptions", OidcModuleExtras>;
|
|
9
|
+
/** The generated base class of the module (its `register` and `registerAsync`). */
|
|
10
|
+
export declare const ConfigurableModuleClass: Host["ConfigurableModuleClass"];
|
|
11
|
+
/** The injection token of the options the registration supplied. */
|
|
12
|
+
export declare const MODULE_OPTIONS_TOKEN: Host["MODULE_OPTIONS_TOKEN"];
|
|
13
|
+
/** Carries the type of the synchronous registration. */
|
|
14
|
+
export declare const OPTIONS_TYPE: Host["OPTIONS_TYPE"];
|
|
15
|
+
/** Carries the type of the asynchronous registration. */
|
|
16
|
+
export declare const ASYNC_OPTIONS_TYPE: Host["ASYNC_OPTIONS_TYPE"];
|
|
17
|
+
/** What `OidcModule.register` takes: the options, plus `isGlobal`. */
|
|
18
|
+
export type OidcRegisterOptions = typeof OPTIONS_TYPE;
|
|
19
|
+
/** What `OidcModule.registerAsync` takes: `imports`, `inject` and one of `useFactory`, `useClass`, `useExisting`, plus `isGlobal`. */
|
|
20
|
+
export type OidcRegisterAsyncOptions = typeof ASYNC_OPTIONS_TYPE;
|
|
21
|
+
/**
|
|
22
|
+
* The OIDC client of a full app as one Nest capability: the discovery document, the provider's signing keys, the token verifier, the
|
|
23
|
+
* `PrincipalVerifier` guards inject and, when the registration declares a client, the authorization code flow, the server-side grants, the
|
|
24
|
+
* admin API of its service account and the `OidcBroker` facade that joins them. Registered once at the api
|
|
25
|
+
* root; the app parses its settings (the package reads no environment) and passes them as typed options. A registration whose options do
|
|
26
|
+
* not meet the contract fails the boot with a `ConfigurationInvalid` error.
|
|
27
|
+
*/
|
|
28
|
+
export declare class OidcModule extends ConfigurableModuleClass {
|
|
29
|
+
/** Registers the module from options known now. */
|
|
30
|
+
static register(options: OidcRegisterOptions): DynamicModule;
|
|
31
|
+
/** Registers the module from options a factory, class or existing provider supplies. */
|
|
32
|
+
static registerAsync(options: OidcRegisterAsyncOptions): DynamicModule;
|
|
33
|
+
}
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { ConfigurableModuleBuilder, Module } from "@nestjs/common";
|
|
2
|
+
import { createOidc } from "./oidc.js";
|
|
3
|
+
import { OIDC_ADMIN_CLIENT, OIDC_BROKER, OIDC_CLIENT, OIDC_DISCOVERY, OIDC_KEYS, OIDC_PRINCIPAL_VERIFIER, OIDC_REGISTRATION, OIDC_TOKEN_CLIENT, OIDC_TOKEN_VERIFIER } from "./oidc.tokens.js";
|
|
4
|
+
const built = new ConfigurableModuleBuilder({ moduleName: "Oidc" })
|
|
5
|
+
.setClassMethodName("register")
|
|
6
|
+
.setFactoryMethodName("createOidcOptions")
|
|
7
|
+
.setExtras({ isGlobal: false }, (definition, extras) => ({ ...definition, global: extras.isGlobal }))
|
|
8
|
+
.build();
|
|
9
|
+
/** The generated base class of the module (its `register` and `registerAsync`). */
|
|
10
|
+
export const ConfigurableModuleClass = built.ConfigurableModuleClass;
|
|
11
|
+
/** The injection token of the options the registration supplied. */
|
|
12
|
+
export const MODULE_OPTIONS_TOKEN = built.MODULE_OPTIONS_TOKEN;
|
|
13
|
+
/** Carries the type of the synchronous registration. */
|
|
14
|
+
export const OPTIONS_TYPE = built.OPTIONS_TYPE;
|
|
15
|
+
/** Carries the type of the asynchronous registration. */
|
|
16
|
+
export const ASYNC_OPTIONS_TYPE = built.ASYNC_OPTIONS_TYPE;
|
|
17
|
+
const providers = [
|
|
18
|
+
{ provide: OIDC_REGISTRATION, inject: [MODULE_OPTIONS_TOKEN], useFactory: (options) => createOidc(options) },
|
|
19
|
+
{ provide: OIDC_DISCOVERY, inject: [OIDC_REGISTRATION], useFactory: (oidc) => oidc.discovery },
|
|
20
|
+
{ provide: OIDC_KEYS, inject: [OIDC_REGISTRATION], useFactory: (oidc) => oidc.keys },
|
|
21
|
+
{ provide: OIDC_TOKEN_VERIFIER, inject: [OIDC_REGISTRATION], useFactory: (oidc) => oidc.tokens },
|
|
22
|
+
{ provide: OIDC_CLIENT, inject: [OIDC_REGISTRATION], useFactory: (oidc) => oidc.client },
|
|
23
|
+
{ provide: OIDC_TOKEN_CLIENT, inject: [OIDC_REGISTRATION], useFactory: (oidc) => oidc.tokenClient },
|
|
24
|
+
{ provide: OIDC_ADMIN_CLIENT, inject: [OIDC_REGISTRATION], useFactory: (oidc) => oidc.admin },
|
|
25
|
+
{ provide: OIDC_BROKER, inject: [OIDC_REGISTRATION], useFactory: (oidc) => oidc.broker },
|
|
26
|
+
{ provide: OIDC_PRINCIPAL_VERIFIER, inject: [OIDC_REGISTRATION], useFactory: (oidc) => oidc.verifier },
|
|
27
|
+
];
|
|
28
|
+
const EXPORTED = [OIDC_DISCOVERY, OIDC_KEYS, OIDC_TOKEN_VERIFIER, OIDC_CLIENT, OIDC_TOKEN_CLIENT, OIDC_ADMIN_CLIENT, OIDC_BROKER, OIDC_PRINCIPAL_VERIFIER];
|
|
29
|
+
const withProviders = (base) => ({
|
|
30
|
+
...base,
|
|
31
|
+
providers: [...(base.providers ?? []), ...providers],
|
|
32
|
+
exports: [...(base.exports ?? []), ...EXPORTED],
|
|
33
|
+
});
|
|
34
|
+
/**
|
|
35
|
+
* The OIDC client of a full app as one Nest capability: the discovery document, the provider's signing keys, the token verifier, the
|
|
36
|
+
* `PrincipalVerifier` guards inject and, when the registration declares a client, the authorization code flow, the server-side grants, the
|
|
37
|
+
* admin API of its service account and the `OidcBroker` facade that joins them. Registered once at the api
|
|
38
|
+
* root; the app parses its settings (the package reads no environment) and passes them as typed options. A registration whose options do
|
|
39
|
+
* not meet the contract fails the boot with a `ConfigurationInvalid` error.
|
|
40
|
+
*/
|
|
41
|
+
export class OidcModule extends ConfigurableModuleClass {
|
|
42
|
+
/** Registers the module from options known now. */
|
|
43
|
+
static register(options) {
|
|
44
|
+
return withProviders(super.register(options));
|
|
45
|
+
}
|
|
46
|
+
/** Registers the module from options a factory, class or existing provider supplies. */
|
|
47
|
+
static registerAsync(options) {
|
|
48
|
+
return withProviders(super.registerAsync(options));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
Module({})(OidcModule);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Token of the `Oidc` registration (the parts below are its fields). */
|
|
2
|
+
export declare const OIDC_REGISTRATION: unique symbol;
|
|
3
|
+
/** Token of `OidcDiscovery`. */
|
|
4
|
+
export declare const OIDC_DISCOVERY: unique symbol;
|
|
5
|
+
/** Token of `OidcKeys`, the provider's signing keys. */
|
|
6
|
+
export declare const OIDC_KEYS: unique symbol;
|
|
7
|
+
/** Token of `OidcTokenVerifier`. */
|
|
8
|
+
export declare const OIDC_TOKEN_VERIFIER: unique symbol;
|
|
9
|
+
/** Token of `OidcClient`, the authorization code flow; null when the registration declares no client. */
|
|
10
|
+
export declare const OIDC_CLIENT: unique symbol;
|
|
11
|
+
/** Token of `OidcTokenClient`, the server-side password, refresh and revoke grants. */
|
|
12
|
+
export declare const OIDC_TOKEN_CLIENT: unique symbol;
|
|
13
|
+
/** Token of `OidcAdminClient`, the provider's admin API as the client's service account. */
|
|
14
|
+
export declare const OIDC_ADMIN_CLIENT: unique symbol;
|
|
15
|
+
/** Token of `OidcBroker`, the one facade an app binds its identity port to. */
|
|
16
|
+
export declare const OIDC_BROKER: unique symbol;
|
|
17
|
+
/** Token of the `PrincipalVerifier` the identity domain injects. */
|
|
18
|
+
export declare const OIDC_PRINCIPAL_VERIFIER: unique symbol;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Token of the `Oidc` registration (the parts below are its fields). */
|
|
2
|
+
export const OIDC_REGISTRATION = Symbol.for("@starci/auth-oidc/registration");
|
|
3
|
+
/** Token of `OidcDiscovery`. */
|
|
4
|
+
export const OIDC_DISCOVERY = Symbol.for("@starci/auth-oidc/discovery");
|
|
5
|
+
/** Token of `OidcKeys`, the provider's signing keys. */
|
|
6
|
+
export const OIDC_KEYS = Symbol.for("@starci/auth-oidc/keys");
|
|
7
|
+
/** Token of `OidcTokenVerifier`. */
|
|
8
|
+
export const OIDC_TOKEN_VERIFIER = Symbol.for("@starci/auth-oidc/token-verifier");
|
|
9
|
+
/** Token of `OidcClient`, the authorization code flow; null when the registration declares no client. */
|
|
10
|
+
export const OIDC_CLIENT = Symbol.for("@starci/auth-oidc/client");
|
|
11
|
+
/** Token of `OidcTokenClient`, the server-side password, refresh and revoke grants. */
|
|
12
|
+
export const OIDC_TOKEN_CLIENT = Symbol.for("@starci/auth-oidc/token-client");
|
|
13
|
+
/** Token of `OidcAdminClient`, the provider's admin API as the client's service account. */
|
|
14
|
+
export const OIDC_ADMIN_CLIENT = Symbol.for("@starci/auth-oidc/admin-client");
|
|
15
|
+
/** Token of `OidcBroker`, the one facade an app binds its identity port to. */
|
|
16
|
+
export const OIDC_BROKER = Symbol.for("@starci/auth-oidc/broker");
|
|
17
|
+
/** Token of the `PrincipalVerifier` the identity domain injects. */
|
|
18
|
+
export const OIDC_PRINCIPAL_VERIFIER = Symbol.for("@starci/auth-oidc/principal-verifier");
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { OidcAlgorithm } from "../contracts/oidc.js";
|
|
2
|
+
import type { OidcClock, SecretInput } from "./primitives.js";
|
|
3
|
+
/** One outbound request of the provider protocol. */
|
|
4
|
+
export interface OidcHttpRequest {
|
|
5
|
+
readonly method: "GET" | "POST" | "PUT";
|
|
6
|
+
readonly url: string;
|
|
7
|
+
/** The deadline of this request, in milliseconds. */
|
|
8
|
+
readonly timeoutMs: number;
|
|
9
|
+
/** A form-encoded body (`application/x-www-form-urlencoded`) for a POST. */
|
|
10
|
+
readonly form?: Readonly<Record<string, string>>;
|
|
11
|
+
/** A JSON body (`application/json`) for a POST or a PUT; use either `body` or `form`, not both. The provider's admin API takes JSON. */
|
|
12
|
+
readonly body?: unknown;
|
|
13
|
+
/** Extra request headers (`authorization` of the admin API). */
|
|
14
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
15
|
+
}
|
|
16
|
+
/** The answer to an outbound request: the status and the parsed JSON body (`undefined` when the body is not JSON). */
|
|
17
|
+
export interface OidcHttpResponse {
|
|
18
|
+
readonly status: number;
|
|
19
|
+
readonly body: unknown;
|
|
20
|
+
/** The `Location` header of the answer, when it has one and the port reads it (a created user's address); optional, nothing depends on it. */
|
|
21
|
+
readonly location?: string | undefined;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The outbound HTTP port. An app passes its own bounded client (`platform/http`) through its glue, so the provider is spoken to
|
|
25
|
+
* through the one door the app owns; a registration without it uses the platform `fetch` with the request's deadline.
|
|
26
|
+
*/
|
|
27
|
+
export interface OidcHttp {
|
|
28
|
+
/** Sends one request; a transport failure rejects, any answer resolves. */
|
|
29
|
+
request(request: OidcHttpRequest): Promise<OidcHttpResponse>;
|
|
30
|
+
}
|
|
31
|
+
/** Which roles of a token become the principal's roles. */
|
|
32
|
+
export interface OidcRoleMapping {
|
|
33
|
+
/** Read `realm_access.roles` (true when absent). */
|
|
34
|
+
readonly realm?: boolean | undefined;
|
|
35
|
+
/** Read `resource_access.<client>.roles` of each of these clients. */
|
|
36
|
+
readonly clients?: readonly string[] | undefined;
|
|
37
|
+
/** Read these flat claims, each an array of text (`groups`, `roles`). */
|
|
38
|
+
readonly claims?: readonly string[] | undefined;
|
|
39
|
+
}
|
|
40
|
+
/** How the JWKS is cached. */
|
|
41
|
+
export interface OidcJwksOptions {
|
|
42
|
+
/** How long a fetched key set is used without asking again (10 minutes). */
|
|
43
|
+
readonly ttlMs?: number | undefined;
|
|
44
|
+
/** The least time between two fetches that an unknown `kid` may cause, so a forged `kid` cannot make the provider a target (30 seconds). */
|
|
45
|
+
readonly refreshCooldownMs?: number | undefined;
|
|
46
|
+
/** How long an expired key set is still used when the provider cannot be reached (24 hours). */
|
|
47
|
+
readonly staleTtlMs?: number | undefined;
|
|
48
|
+
/** The most keys a set may hold (100). */
|
|
49
|
+
readonly maxKeys?: number | undefined;
|
|
50
|
+
}
|
|
51
|
+
/** The registered client of the authorization code flow. Absent for an API that only verifies bearer tokens. */
|
|
52
|
+
export interface OidcClientRegistration {
|
|
53
|
+
/** The client id; the audience of the ID token. */
|
|
54
|
+
readonly clientId: string;
|
|
55
|
+
/** The client secret of a confidential client, revealed only for the token request. Absent for a public client. */
|
|
56
|
+
readonly clientSecret?: SecretInput | undefined;
|
|
57
|
+
/** The exact registered redirect address. */
|
|
58
|
+
readonly callbackUri: string;
|
|
59
|
+
/** The exact registered address the provider returns to after logout. */
|
|
60
|
+
readonly postLogoutUri: string;
|
|
61
|
+
/** The requested scope (`openid`). */
|
|
62
|
+
readonly scope?: string | undefined;
|
|
63
|
+
}
|
|
64
|
+
/** Everything the package needs, typed and parsed by the app (the package reads no environment). */
|
|
65
|
+
export interface OidcOptions {
|
|
66
|
+
/** The exact issuer: the `iss` claim of every token and the `issuer` of the discovery document. */
|
|
67
|
+
readonly issuer: string;
|
|
68
|
+
/** The audience an access token must name: one of these. */
|
|
69
|
+
readonly audience: string | readonly string[];
|
|
70
|
+
/** The algorithms a token may be signed with (`RS256`). Asymmetric only. */
|
|
71
|
+
readonly algorithms?: readonly OidcAlgorithm[] | undefined;
|
|
72
|
+
/** When set, an access token's `azp` claim must equal it. */
|
|
73
|
+
readonly authorizedParty?: string | undefined;
|
|
74
|
+
/** The authorization code flow client; needed to build an authorization address, exchange a code or build a logout address. */
|
|
75
|
+
readonly client?: OidcClientRegistration | undefined;
|
|
76
|
+
/** Which roles become the principal's roles (the realm roles). */
|
|
77
|
+
readonly roles?: OidcRoleMapping | undefined;
|
|
78
|
+
/** The deadline of every provider request, in milliseconds (10000). */
|
|
79
|
+
readonly timeoutMs?: number | undefined;
|
|
80
|
+
/** The clock skew a token's `exp` and `nbf` tolerate, in seconds (5). */
|
|
81
|
+
readonly clockToleranceSeconds?: number | undefined;
|
|
82
|
+
/** How long a discovery document is used (1 hour). */
|
|
83
|
+
readonly discoveryTtlMs?: number | undefined;
|
|
84
|
+
/** The JWKS cache. */
|
|
85
|
+
readonly jwks?: OidcJwksOptions | undefined;
|
|
86
|
+
/** Admit `http:` addresses. Only for a provider on a developer machine or a test double; never in a deployment. */
|
|
87
|
+
readonly allowInsecureHttp?: boolean | undefined;
|
|
88
|
+
/** The outbound HTTP port (the platform `fetch` when absent). */
|
|
89
|
+
readonly http?: OidcHttp | undefined;
|
|
90
|
+
/** The clock (system time when absent). */
|
|
91
|
+
readonly clock?: OidcClock | undefined;
|
|
92
|
+
}
|
|
93
|
+
/** The options after validation, with every default applied. */
|
|
94
|
+
export interface ResolvedOidcOptions {
|
|
95
|
+
readonly issuer: string;
|
|
96
|
+
readonly audience: readonly string[];
|
|
97
|
+
readonly algorithms: readonly OidcAlgorithm[];
|
|
98
|
+
readonly authorizedParty: string | undefined;
|
|
99
|
+
readonly client: OidcClientRegistration | undefined;
|
|
100
|
+
readonly realmRoles: boolean;
|
|
101
|
+
readonly roleClients: readonly string[];
|
|
102
|
+
readonly roleClaims: readonly string[];
|
|
103
|
+
readonly timeoutMs: number;
|
|
104
|
+
readonly clockToleranceSeconds: number;
|
|
105
|
+
readonly discoveryTtlMs: number;
|
|
106
|
+
readonly jwksTtlMs: number;
|
|
107
|
+
readonly jwksCooldownMs: number;
|
|
108
|
+
readonly jwksStaleMs: number;
|
|
109
|
+
readonly jwksMaxKeys: number;
|
|
110
|
+
readonly allowInsecureHttp: boolean;
|
|
111
|
+
readonly http: OidcHttp;
|
|
112
|
+
readonly clock: OidcClock;
|
|
113
|
+
}
|
|
114
|
+
/** True when the address is an absolute, credential-free URL on the scheme the registration admits (https; http only when allowed). */
|
|
115
|
+
export declare const isAdmissibleUrl: (value: string, insecure: boolean) => boolean;
|
|
116
|
+
/** The platform `fetch` as the outbound port: JSON answers are parsed, the request's deadline aborts it. */
|
|
117
|
+
export declare const fetchHttp: OidcHttp;
|
|
118
|
+
/** Validates the registration and applies the defaults. A key that does not meet the contract throws a `ConfigurationInvalid` error naming it. */
|
|
119
|
+
export declare const resolveOptions: (options: OidcOptions) => ResolvedOidcOptions;
|