@hyper.authio/client 0.7.0 → 0.8.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/README.md +46 -11
- package/dist/index.cjs +213 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +156 -25
- package/dist/index.d.ts +156 -25
- package/dist/index.js +206 -45
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,15 +26,22 @@ const authio = createAuthioClient({
|
|
|
26
26
|
| --- | --- | --- |
|
|
27
27
|
| `issuer`, or `authority` + `realm` | | The realm's issuer. Must be HTTPS, except on loopback or with `requireHttps: false`. |
|
|
28
28
|
| `clientId` | | Required. |
|
|
29
|
-
| `
|
|
29
|
+
| `tokenEndpointAuthMethod` | `client_secret_basic` with a secret, `private_key_jwt` with only a key, `none` with neither | The client's authentication method in Authio, which matches it exactly: `client_secret_basic`, `client_secret_post`, `private_key_jwt` or `none` (a public client). |
|
|
30
30
|
| `clientSecret` | | For `client_secret_basic` / `client_secret_post`. A function returning the secret is called for every request, so a rotated secret is used without a restart. |
|
|
31
31
|
| `clientPrivateKey`, `clientPrivateKeyPath`, `clientKeyId`, `clientSigningAlgorithm` | RS256 for RSA, the curve's ES algorithm for EC | For `private_key_jwt`: PEM (PKCS#8, PKCS#1, SEC1) or JWK; the file is re-read when it changes; RSA keys need 2048 bits. |
|
|
32
32
|
| `clientAssertionLifetime` | 120 s | At most 3600. |
|
|
33
|
-
| `
|
|
33
|
+
| `checks` | none | What the sign-in checks: `pkce`, `state`, `nonce` (or `none`). Only the listed checks are made. |
|
|
34
|
+
| `codeChallengeMethod` | `S256` | `S256` or `plain`, with the `pkce` check: one of the client's allowed PKCE methods. |
|
|
35
|
+
| `responseMode` | not sent | `query` or `web_message`. |
|
|
36
|
+
| `idTokenSignedResponseAlg` | the asymmetric algorithms the discovery document announces | The client's signing algorithm: RS/PS/ES 256/384/512, or HS256/384/512 verified with `clientSecret` (at least 32, 48 or 64 bytes, RFC 7518 §3.2). Also used for logout tokens. |
|
|
37
|
+
| `accessTokenSignedResponseAlg` | the asymmetric algorithms the discovery document announces | The algorithm of the client's access tokens (Authio's second signing setting), for `readAccessTokenClaims`; HS* with `clientSecret`. |
|
|
38
|
+
| `userinfoSignedResponseAlg` | `idTokenSignedResponseAlg` | For UserInfo answered as a signed JWT. |
|
|
39
|
+
| `clientEncryptionKey`, `clientEncryptionKeyPath` | | The private half of the client's encryption key (JWK or PEM, or a function returning it; the path is Node only), to read ID tokens, UserInfo and logout tokens Authio encrypts. Without it nothing is decrypted. |
|
|
40
|
+
| `encryptionAlgorithms` | `RSA-OAEP`, `RSA-OAEP-256` | The JWE `alg` accepted. RSA1_5 cannot be listed: Node and WebCrypto cannot decrypt it. |
|
|
41
|
+
| `encryptionMethods` | `A128CBC-HS256`, `A192CBC-HS384`, `A256CBC-HS512` | The JWE `enc` accepted; `A128GCM`, `A192GCM` and `A256GCM` may be listed. |
|
|
34
42
|
| `allowLegacyEncryptionAlgorithms` | `false` | Accepted for parity; RSA1_5 stays refused in Node, which cannot decrypt it (`legacy_algorithm`). |
|
|
35
43
|
| `redirectUri`, `postLogoutRedirectUri` | | Defaults for sign-in and sign-out requests. |
|
|
36
44
|
| `clockSkew` | 30 s | Tolerated on ID tokens, logout tokens and signed UserInfo. |
|
|
37
|
-
| `validAlgorithms` | RS*, PS*, ES* | Accepted signing algorithms for those tokens. |
|
|
38
45
|
| `discoveryCacheDuration` | 3600 s | |
|
|
39
46
|
| `httpTimeout` | 30 s | |
|
|
40
47
|
| `tokenRefreshSkew` | 60 s | How early cached tokens are renewed (at most half their lifetime). |
|
|
@@ -45,11 +52,30 @@ when it names another issuer.
|
|
|
45
52
|
|
|
46
53
|
Option names follow the shared `conformance/options.md`; there are no aliases for older names.
|
|
47
54
|
|
|
55
|
+
## Mirror the client
|
|
56
|
+
|
|
57
|
+
The options describe the client as it is registered in Authio's console, and an option left out is off: the library
|
|
58
|
+
hard-codes none of it. Authio refuses a request that does not match the client and accepts what the client does not
|
|
59
|
+
require.
|
|
60
|
+
|
|
61
|
+
| In the console | Options |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| PKCE required, method S256 | `checks: ["pkce", "state"]` (add `"nonce"` to bind the ID token), `codeChallengeMethod: "S256"` |
|
|
64
|
+
| PKCE required, method plain only | `checks: ["pkce", "state"]`, `codeChallengeMethod: "plain"` |
|
|
65
|
+
| PKCE off | `checks: ["state"]`, or leave `checks` out to check nothing (sending PKCE anyway is accepted) |
|
|
66
|
+
| Authentication on, `client_secret_post` | `clientSecret`, `tokenEndpointAuthMethod: "client_secret_post"` |
|
|
67
|
+
| Authentication on, `private_key_jwt` | `clientPrivateKey` (or `clientPrivateKeyPath`), `clientKeyId` |
|
|
68
|
+
| Authentication off (public client) | no secret nor key: `tokenEndpointAuthMethod` is `none` |
|
|
69
|
+
| ID token signing algorithm HS256 | `clientSecret`, `idTokenSignedResponseAlg: "HS256"` |
|
|
70
|
+
| Access token signing algorithm HS256 | `clientSecret`, `accessTokenSignedResponseAlg: "HS256"` (an API: the verifier's `accessTokenSignedResponseAlg`) |
|
|
71
|
+
| Tokens encrypted with the client's key, RSA-OAEP-256 | `clientEncryptionKey`, `encryptionAlgorithms: ["RSA-OAEP-256"]` |
|
|
72
|
+
|
|
48
73
|
## Sign people in
|
|
49
74
|
|
|
50
75
|
```ts
|
|
51
76
|
const authio = createAuthioClient({
|
|
52
|
-
issuer, clientId: "storefront",
|
|
77
|
+
issuer, clientId: "storefront", // a public client: no secret nor key, so tokenEndpointAuthMethod is none
|
|
78
|
+
checks: ["pkce", "state", "nonce"], codeChallengeMethod: "S256", // the client requires PKCE with S256
|
|
53
79
|
redirectUri: "https://shop.example.com/callback",
|
|
54
80
|
});
|
|
55
81
|
|
|
@@ -62,12 +88,15 @@ const { url, transaction } = await authio.createAuthorizationUrl({
|
|
|
62
88
|
maxAge: 900, acrValues: "2", loginHint, uiLocales: "pt-BR", idpHint: "google",
|
|
63
89
|
});
|
|
64
90
|
|
|
65
|
-
// 2. On the callback: state and iss (RFC 9207) are checked, the code is redeemed with the
|
|
66
|
-
// and the ID token validated (nonce, at_hash, auth_time
|
|
91
|
+
// 2. On the callback: state (with the state check) and iss (RFC 9207) are checked, the code is redeemed (with the
|
|
92
|
+
// PKCE verifier, with the pkce check), and the ID token validated (nonce with the nonce check, at_hash, auth_time
|
|
93
|
+
// against maxAge).
|
|
67
94
|
const { tokens, idToken } = await authio.handleAuthorizationCallback(request.url, transaction);
|
|
68
95
|
```
|
|
69
96
|
|
|
70
|
-
|
|
97
|
+
Each check generates its parameter and is verified on the callback; the transaction lists the checks it was made with,
|
|
98
|
+
and a `state` it did not ask for is ignored. A request may pass its own `checks`, `codeChallengeMethod` and
|
|
99
|
+
`responseMode`. `popup: true` sets `display=popup` and
|
|
71
100
|
`response_mode=web_message`; pass the parameters the popup posted to the opener to `handleAuthorizationCallback`. A
|
|
72
101
|
callback that is not for the transaction throws `AuthioCallbackError` (`reason`: `state_mismatch`, `issuer_mismatch`,
|
|
73
102
|
`missing_issuer`, `missing_code`); an OAuth error Authio sent back (`login_required` after `prompt: "none"`, for one)
|
|
@@ -81,13 +110,19 @@ const logoutUrl = await authio.createEndSessionUrl({ idTokenHint, postLogoutRedi
|
|
|
81
110
|
const logout = await authio.validateLogoutToken(form.logout_token); // { subject, sessionId, tokenId, claims }
|
|
82
111
|
```
|
|
83
112
|
|
|
84
|
-
ID tokens are checked against the realm's JWKS (cached, re-read on an unknown `kid`)
|
|
113
|
+
ID tokens are checked against the realm's JWKS (cached, re-read on an unknown `kid`), or against `clientSecret` for
|
|
114
|
+
the HS* algorithm `idTokenSignedResponseAlg` names: `iss`, `aud` = the client id,
|
|
85
115
|
`exp` with `clockSkew`, payload `typ` = `ID`, `azp` when there are several audiences, `nonce`, `at_hash` (hash of the
|
|
86
116
|
signing algorithm) and `auth_time` against `maxAge`. An encrypted ID token is decrypted with `clientEncryptionKey`
|
|
87
117
|
first. A back-channel logout token must be typed `logout+jwt`, carry the back-channel event, no `nonce`, and a `sub`
|
|
88
118
|
or `sid`. Every refusal is an `AuthioTokenValidationError` whose `reason` uses the outcome names of
|
|
89
|
-
`conformance/tokens.json` (`invalid_nonce`, `invalid_at_hash`, `invalid_logout_token`, `expired`,
|
|
90
|
-
`authioBackchannelLogout` from `@hyper.authio/express` is the endpoint.
|
|
119
|
+
`conformance/tokens.json` (`invalid_nonce`, `invalid_at_hash`, `invalid_logout_token`, `invalid_algorithm`, `expired`,
|
|
120
|
+
…). In Express, `authioBackchannelLogout` from `@hyper.authio/express` is the endpoint.
|
|
121
|
+
|
|
122
|
+
`readAccessTokenClaims(accessToken)` returns the payload of an access token the client received once it is decrypted
|
|
123
|
+
and its signature, `iss` and `exp` verify (`undefined` for an opaque token). For front-channel logout, Authio loads the
|
|
124
|
+
client's front-channel logout URI in an iframe; `validateFrontChannelLogout(request.url)` checks `iss` and returns the
|
|
125
|
+
`sessionId` (`sid`) to end (`authioFrontchannelLogout` in `@hyper.authio/express`).
|
|
91
126
|
|
|
92
127
|
### Step-up
|
|
93
128
|
|
|
@@ -104,7 +139,7 @@ const { url, transaction } = await authio.createStepUpAuthorizationUrl(challenge
|
|
|
104
139
|
```ts
|
|
105
140
|
await authio.tokens.clientCredentials({ scopes: ["orders:read"], resource: "https://orders.example.com" });
|
|
106
141
|
await authio.tokens.authorizationCode({ code, redirectUri, codeVerifier });
|
|
107
|
-
await authio.tokens.refresh({ refreshToken });
|
|
142
|
+
await authio.tokens.refresh({ refreshToken, resource }); // store the rotated refresh_token it returns; resource: RFC 8707
|
|
108
143
|
await authio.tokens.exchange({ subjectToken, resource }); // on-behalf-of (RFC 8693)
|
|
109
144
|
await authio.tokens.password({ username, password });
|
|
110
145
|
await authio.tokens.personalAccessToken({ token: "pat_…", clientId: "billing-api" }); // clientId only for a consumer's PAT
|
package/dist/index.cjs
CHANGED
|
@@ -149,6 +149,8 @@ var AuthioAsymmetricAlgorithms = [
|
|
|
149
149
|
"ES384",
|
|
150
150
|
"ES512"
|
|
151
151
|
];
|
|
152
|
+
var AuthioHmacAlgorithms = ["HS256", "HS384", "HS512"];
|
|
153
|
+
var AuthioHmacMinimumSecretBytes = Object.freeze({ HS256: 32, HS384: 48, HS512: 64 });
|
|
152
154
|
var AuthioDefaults = {
|
|
153
155
|
DiscoveryPath: "/.well-known/openid-configuration",
|
|
154
156
|
ProtectedResourceMetadataPath: "/.well-known/oauth-protected-resource",
|
|
@@ -280,6 +282,12 @@ var systemClock = {
|
|
|
280
282
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
281
283
|
})
|
|
282
284
|
};
|
|
285
|
+
var AuthioTokenEndpointAuthMethods = [
|
|
286
|
+
"client_secret_basic",
|
|
287
|
+
"client_secret_post",
|
|
288
|
+
"private_key_jwt",
|
|
289
|
+
"none"
|
|
290
|
+
];
|
|
283
291
|
function resolveConnection(options) {
|
|
284
292
|
const requireHttps = options.requireHttps ?? true;
|
|
285
293
|
const issuer = resolveIssuer(options);
|
|
@@ -595,7 +603,9 @@ function createClientAuthenticator(settings, connection) {
|
|
|
595
603
|
const clientId = settings.clientId?.trim();
|
|
596
604
|
if (!clientId) throw new AuthioConfigurationError("The Authio clientId is required.");
|
|
597
605
|
const hasKey = settings.clientPrivateKey !== void 0 || !!settings.clientPrivateKeyPath;
|
|
598
|
-
const method = settings.
|
|
606
|
+
const method = settings.tokenEndpointAuthMethod ?? (settings.clientSecret ? "client_secret_basic" : hasKey ? "private_key_jwt" : "none");
|
|
607
|
+
if (!AuthioTokenEndpointAuthMethods.includes(method))
|
|
608
|
+
throw new AuthioConfigurationError(`The Authio tokenEndpointAuthMethod '${String(method)}' is not one of ${AuthioTokenEndpointAuthMethods.join(", ")}.`);
|
|
599
609
|
if ((method === "client_secret_basic" || method === "client_secret_post") && !settings.clientSecret)
|
|
600
610
|
throw new AuthioConfigurationError(`The Authio clientSecret is required with ${method}.`);
|
|
601
611
|
if (method === "private_key_jwt" && !hasKey)
|
|
@@ -677,6 +687,7 @@ function createKeyLoader(settings) {
|
|
|
677
687
|
var AuthioKeyManagementAlgorithms = ["RSA-OAEP", "RSA-OAEP-256"];
|
|
678
688
|
var AuthioLegacyKeyManagementAlgorithms = ["RSA1_5"];
|
|
679
689
|
var AuthioContentEncryptionAlgorithms = ["A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512"];
|
|
690
|
+
var AuthioSupportedContentEncryptionAlgorithms = [...AuthioContentEncryptionAlgorithms, "A128GCM", "A192GCM", "A256GCM"];
|
|
680
691
|
var rsaAlgorithmIdentifier = [48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0];
|
|
681
692
|
function isAuthioTokenEncrypted(token) {
|
|
682
693
|
return typeof token === "string" && token.split(".").length === 5;
|
|
@@ -684,6 +695,13 @@ function isAuthioTokenEncrypted(token) {
|
|
|
684
695
|
function createAuthioTokenDecrypter(key, options = {}) {
|
|
685
696
|
const settings = typeof options === "string" ? { setting: options } : options;
|
|
686
697
|
const setting = settings.setting ?? "clientEncryptionKey";
|
|
698
|
+
const keyManagementAlgorithms = resolveAllowlist(settings.encryptionAlgorithms, AuthioKeyManagementAlgorithms, AuthioKeyManagementAlgorithms, "encryptionAlgorithms");
|
|
699
|
+
const contentEncryptionAlgorithms = resolveAllowlist(
|
|
700
|
+
settings.encryptionMethods,
|
|
701
|
+
AuthioContentEncryptionAlgorithms,
|
|
702
|
+
AuthioSupportedContentEncryptionAlgorithms,
|
|
703
|
+
"encryptionMethods"
|
|
704
|
+
);
|
|
687
705
|
const imported = /* @__PURE__ */ new Map();
|
|
688
706
|
const importFor = (algorithm) => {
|
|
689
707
|
let pending = imported.get(algorithm);
|
|
@@ -702,10 +720,16 @@ function createAuthioTokenDecrypter(key, options = {}) {
|
|
|
702
720
|
"legacy_algorithm",
|
|
703
721
|
settings.allowLegacyEncryptionAlgorithms ? "The token is encrypted with RSA1_5 (PKCS#1 v1.5), which Node and WebCrypto cannot decrypt even with allowLegacyEncryptionAlgorithms; register RSA-OAEP-256 as the client's encryption algorithm in Authio." : "The token is encrypted with the legacy RSA1_5 algorithm, which is refused; register RSA-OAEP-256 as the client's encryption algorithm in Authio."
|
|
704
722
|
);
|
|
705
|
-
if (!
|
|
723
|
+
if (!keyManagementAlgorithms.includes(algorithm))
|
|
724
|
+
throw new AuthioTokenValidationError(
|
|
725
|
+
"decryption_failed",
|
|
726
|
+
`The token is encrypted with '${algorithm}', and encryptionAlgorithms accepts ${keyManagementAlgorithms.join(" or ")}.`
|
|
727
|
+
);
|
|
728
|
+
const method = contentMethodOf(token);
|
|
729
|
+
if (!contentEncryptionAlgorithms.includes(method))
|
|
706
730
|
throw new AuthioTokenValidationError(
|
|
707
731
|
"decryption_failed",
|
|
708
|
-
`The token is encrypted with '${
|
|
732
|
+
`The token's content is encrypted with '${method}', and encryptionMethods accepts ${contentEncryptionAlgorithms.join(" or ")}.`
|
|
709
733
|
);
|
|
710
734
|
if (key === void 0 || key === null || key === "")
|
|
711
735
|
throw new AuthioTokenValidationError(
|
|
@@ -714,8 +738,8 @@ function createAuthioTokenDecrypter(key, options = {}) {
|
|
|
714
738
|
);
|
|
715
739
|
try {
|
|
716
740
|
const { plaintext } = await jose.compactDecrypt(token, (header) => importFor(header.alg), {
|
|
717
|
-
keyManagementAlgorithms: [...
|
|
718
|
-
contentEncryptionAlgorithms: [...
|
|
741
|
+
keyManagementAlgorithms: [...keyManagementAlgorithms],
|
|
742
|
+
contentEncryptionAlgorithms: [...contentEncryptionAlgorithms]
|
|
719
743
|
});
|
|
720
744
|
return new TextDecoder().decode(plaintext);
|
|
721
745
|
} catch (error) {
|
|
@@ -749,6 +773,24 @@ function algorithmOf(token) {
|
|
|
749
773
|
throw new AuthioTokenValidationError("decryption_failed", "The encrypted token's header could not be read.", { cause: error });
|
|
750
774
|
}
|
|
751
775
|
}
|
|
776
|
+
function contentMethodOf(token) {
|
|
777
|
+
try {
|
|
778
|
+
return String(jose.decodeProtectedHeader(token).enc ?? "");
|
|
779
|
+
} catch (error) {
|
|
780
|
+
throw new AuthioTokenValidationError("decryption_failed", "The encrypted token's header could not be read.", { cause: error });
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
function resolveAllowlist(configured, defaults, supported, setting) {
|
|
784
|
+
if (configured === void 0 || configured.length === 0) return defaults;
|
|
785
|
+
for (const value of configured) {
|
|
786
|
+
if (AuthioLegacyKeyManagementAlgorithms.includes(value))
|
|
787
|
+
throw new AuthioConfigurationError(
|
|
788
|
+
`${setting} cannot hold ${value}: Node and WebCrypto cannot decrypt PKCS#1 v1.5; register RSA-OAEP-256 as the client's encryption algorithm in Authio.`
|
|
789
|
+
);
|
|
790
|
+
if (!supported.includes(value)) throw new AuthioConfigurationError(`${setting} holds '${value}'; it accepts ${supported.join(", ")}.`);
|
|
791
|
+
}
|
|
792
|
+
return [...new Set(configured)];
|
|
793
|
+
}
|
|
752
794
|
async function importDecryptionKey(key, algorithm, setting) {
|
|
753
795
|
try {
|
|
754
796
|
if (typeof key !== "string") return await importPrivateJwk(key, algorithm, setting);
|
|
@@ -805,7 +847,7 @@ async function verifyAuthioJwt(token, keys, expected) {
|
|
|
805
847
|
try {
|
|
806
848
|
const { payload, protectedHeader } = await jose.jwtVerify(token, keys, {
|
|
807
849
|
issuer: expected.issuer,
|
|
808
|
-
audience: [...expected.audiences],
|
|
850
|
+
audience: expected.audiences ? [...expected.audiences] : void 0,
|
|
809
851
|
algorithms: [...expected.algorithms && expected.algorithms.length > 0 ? expected.algorithms : AuthioAsymmetricAlgorithms],
|
|
810
852
|
clockTolerance: expected.clockSkew ?? AuthioDefaults.ClockSkew,
|
|
811
853
|
requiredClaims: [.../* @__PURE__ */ new Set(["exp", ...expected.requiredClaims ?? []])],
|
|
@@ -813,9 +855,34 @@ async function verifyAuthioJwt(token, keys, expected) {
|
|
|
813
855
|
});
|
|
814
856
|
return { payload, header: protectedHeader };
|
|
815
857
|
} catch (error) {
|
|
816
|
-
|
|
858
|
+
if (error instanceof AuthioConfigurationError) throw error;
|
|
859
|
+
throw toTokenValidationError(error, expected.audiences ?? [], token);
|
|
817
860
|
}
|
|
818
861
|
}
|
|
862
|
+
function withClientSecret(keys, secret) {
|
|
863
|
+
if (!secret) return keys;
|
|
864
|
+
return async (header, token) => {
|
|
865
|
+
const algorithm = String(header.alg);
|
|
866
|
+
if (!AuthioHmacAlgorithms.includes(algorithm)) return keys(header, token);
|
|
867
|
+
const bytes = new TextEncoder().encode(await secret());
|
|
868
|
+
requireHmacSecretLength(algorithm, bytes.length);
|
|
869
|
+
return bytes;
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
function requireHmacSecretLength(algorithm, length) {
|
|
873
|
+
const minimum = AuthioHmacMinimumSecretBytes[algorithm];
|
|
874
|
+
if (minimum !== void 0 && length < minimum)
|
|
875
|
+
throw new AuthioConfigurationError(
|
|
876
|
+
`The Authio clientSecret has ${length} bytes, and ${algorithm} needs at least ${minimum} (RFC 7518 \xA73.2): Authio refuses a shorter secret for it.`
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
function acceptedSigningAlgorithms(configured, announced) {
|
|
880
|
+
if (configured) return [configured];
|
|
881
|
+
const asymmetric = (Array.isArray(announced) ? announced : []).filter(
|
|
882
|
+
(algorithm) => typeof algorithm === "string" && AuthioAsymmetricAlgorithms.includes(algorithm)
|
|
883
|
+
);
|
|
884
|
+
return asymmetric.length > 0 ? asymmetric : AuthioAsymmetricAlgorithms;
|
|
885
|
+
}
|
|
819
886
|
function toTokenValidationError(error, audiences, token) {
|
|
820
887
|
if (error instanceof AuthioTokenValidationError) return error;
|
|
821
888
|
const refuse = (reason, message) => new AuthioTokenValidationError(reason, message, { cause: error });
|
|
@@ -947,6 +1014,7 @@ function toBase64Url(bytes) {
|
|
|
947
1014
|
}
|
|
948
1015
|
|
|
949
1016
|
// src/authorization.ts
|
|
1017
|
+
var AuthioChecks = ["pkce", "state", "nonce", "none"];
|
|
950
1018
|
async function createAuthioPkce(method = "S256") {
|
|
951
1019
|
if (method !== "S256" && method !== "plain") throw new TypeError(`The PKCE method '${String(method)}' is not S256 or plain.`);
|
|
952
1020
|
const codeVerifier = randomValue(32);
|
|
@@ -956,14 +1024,26 @@ async function createAuthioPkce(method = "S256") {
|
|
|
956
1024
|
function randomValue(bytes = 32) {
|
|
957
1025
|
return toBase64Url2(crypto.getRandomValues(new Uint8Array(bytes)));
|
|
958
1026
|
}
|
|
1027
|
+
function resolveChecks(checks, setting = "checks") {
|
|
1028
|
+
const resolved = [];
|
|
1029
|
+
for (const check of checks ?? []) {
|
|
1030
|
+
if (!AuthioChecks.includes(check))
|
|
1031
|
+
throw new AuthioConfigurationError(`The Authio ${setting} value '${String(check)}' is not one of ${AuthioChecks.join(", ")}.`);
|
|
1032
|
+
if (check !== "none" && !resolved.includes(check)) resolved.push(check);
|
|
1033
|
+
}
|
|
1034
|
+
return Object.freeze(resolved);
|
|
1035
|
+
}
|
|
959
1036
|
async function buildAuthorizationUrl(endpoint, clientId, request) {
|
|
960
|
-
const
|
|
961
|
-
const
|
|
962
|
-
const
|
|
1037
|
+
const checks = resolveChecks(request.checks);
|
|
1038
|
+
const pkce = checks.includes("pkce") ? await createAuthioPkce(request.codeChallengeMethod ?? "S256") : void 0;
|
|
1039
|
+
const state = checks.includes("state") ? request.state?.trim() || randomValue() : void 0;
|
|
1040
|
+
const nonce = checks.includes("nonce") ? request.nonce?.trim() || randomValue() : void 0;
|
|
963
1041
|
if (request.maxAge !== void 0 && (!Number.isInteger(request.maxAge) || request.maxAge < 0))
|
|
964
1042
|
throw new RangeError("maxAge must be a non-negative whole number of seconds.");
|
|
965
1043
|
const display = request.popup ? "popup" : request.display;
|
|
966
1044
|
const responseMode = request.popup ? "web_message" : request.responseMode;
|
|
1045
|
+
if (responseMode !== void 0 && responseMode !== "query" && responseMode !== "web_message")
|
|
1046
|
+
throw new AuthioConfigurationError(`The Authio responseMode '${String(responseMode)}' is not query or web_message.`);
|
|
967
1047
|
const url = new URL(endpoint);
|
|
968
1048
|
const parameters = {
|
|
969
1049
|
...request.extraParameters,
|
|
@@ -971,10 +1051,10 @@ async function buildAuthorizationUrl(endpoint, clientId, request) {
|
|
|
971
1051
|
client_id: clientId,
|
|
972
1052
|
redirect_uri: request.redirectUri,
|
|
973
1053
|
scope: toScopeString(request.scopes) ?? "openid",
|
|
974
|
-
state,
|
|
975
|
-
nonce,
|
|
976
|
-
code_challenge: pkce
|
|
977
|
-
code_challenge_method: pkce
|
|
1054
|
+
state: state ?? request.state?.trim(),
|
|
1055
|
+
nonce: nonce ?? request.nonce?.trim(),
|
|
1056
|
+
code_challenge: pkce?.codeChallenge,
|
|
1057
|
+
code_challenge_method: pkce?.codeChallengeMethod,
|
|
978
1058
|
resource: request.resource,
|
|
979
1059
|
organization: request.organization,
|
|
980
1060
|
prompt: joined(request.prompt),
|
|
@@ -991,10 +1071,10 @@ async function buildAuthorizationUrl(endpoint, clientId, request) {
|
|
|
991
1071
|
return {
|
|
992
1072
|
url: url.toString(),
|
|
993
1073
|
transaction: Object.freeze({
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
codeChallengeMethod: pkce.codeChallengeMethod,
|
|
1074
|
+
checks,
|
|
1075
|
+
...state ? { state } : {},
|
|
1076
|
+
...nonce ? { nonce } : {},
|
|
1077
|
+
...pkce ? { codeVerifier: pkce.codeVerifier, codeChallengeMethod: pkce.codeChallengeMethod } : {},
|
|
998
1078
|
redirectUri: request.redirectUri,
|
|
999
1079
|
...request.resource ? { resource: request.resource } : {},
|
|
1000
1080
|
...request.maxAge !== void 0 ? { maxAge: request.maxAge } : {}
|
|
@@ -1016,6 +1096,16 @@ function readCallbackParameters(callback) {
|
|
|
1016
1096
|
}
|
|
1017
1097
|
return parameters;
|
|
1018
1098
|
}
|
|
1099
|
+
function readAuthioFrontChannelLogout(request, issuer) {
|
|
1100
|
+
const parameters = readCallbackParameters(request);
|
|
1101
|
+
const sessionId = parameters.sid?.trim() || void 0;
|
|
1102
|
+
const sentIssuer = parameters.iss?.trim() || void 0;
|
|
1103
|
+
if (sentIssuer !== void 0 && sentIssuer.replace(/\/+$/, "") !== issuer.replace(/\/+$/, ""))
|
|
1104
|
+
throw new AuthioCallbackError("issuer_mismatch", `The front-channel logout comes from the issuer '${sentIssuer}', not '${issuer}'.`);
|
|
1105
|
+
if (sessionId !== void 0 && sentIssuer === void 0)
|
|
1106
|
+
throw new AuthioCallbackError("missing_issuer", "The front-channel logout names a sid without its iss.");
|
|
1107
|
+
return Object.freeze({ sessionId, issuer: sentIssuer });
|
|
1108
|
+
}
|
|
1019
1109
|
function parseAuthioBearerChallenge(header) {
|
|
1020
1110
|
if (!header || !/^\s*bearer(\s|$)/i.test(header)) return void 0;
|
|
1021
1111
|
const values = {};
|
|
@@ -1051,22 +1141,35 @@ function createAuthioClient(options) {
|
|
|
1051
1141
|
const authenticator = createClientAuthenticator(options, connection);
|
|
1052
1142
|
const refreshSkewMs = seconds(options.tokenRefreshSkew, AuthioDefaults.TokenRefreshSkew, "tokenRefreshSkew");
|
|
1053
1143
|
const clockSkew = seconds(options.clockSkew, AuthioDefaults.ClockSkew, "clockSkew") / 1e3;
|
|
1054
|
-
const
|
|
1144
|
+
const encryptionKey = resolveEncryptionKey(options);
|
|
1145
|
+
const decrypt = createAuthioTokenDecrypter(encryptionKey, {
|
|
1055
1146
|
setting: options.clientEncryptionKey === void 0 && options.clientEncryptionKeyPath ? "clientEncryptionKeyPath" : "clientEncryptionKey",
|
|
1056
|
-
allowLegacyEncryptionAlgorithms: options.allowLegacyEncryptionAlgorithms
|
|
1147
|
+
allowLegacyEncryptionAlgorithms: options.allowLegacyEncryptionAlgorithms,
|
|
1148
|
+
encryptionAlgorithms: options.encryptionAlgorithms,
|
|
1149
|
+
encryptionMethods: options.encryptionMethods
|
|
1057
1150
|
});
|
|
1151
|
+
const checks = resolveChecks(options.checks);
|
|
1152
|
+
const codeChallengeMethod = resolveCodeChallengeMethod(options.codeChallengeMethod);
|
|
1153
|
+
const responseMode = resolveResponseMode(options.responseMode);
|
|
1154
|
+
const idTokenAlgorithm = resolveSigningAlgorithm(options.idTokenSignedResponseAlg, "idTokenSignedResponseAlg", options.clientSecret);
|
|
1155
|
+
const userInfoAlgorithm = resolveSigningAlgorithm(options.userinfoSignedResponseAlg, "userinfoSignedResponseAlg", options.clientSecret) ?? idTokenAlgorithm;
|
|
1156
|
+
const accessTokenAlgorithm = resolveSigningAlgorithm(options.accessTokenSignedResponseAlg, "accessTokenSignedResponseAlg", options.clientSecret);
|
|
1157
|
+
const clientSecret = options.clientSecret === void 0 ? void 0 : () => readSecret(options.clientSecret);
|
|
1058
1158
|
const signingKeys = createAuthioSigningKeys(connection, discovery);
|
|
1059
1159
|
const tokens = createTokenClient(connection, discovery, authenticator);
|
|
1060
1160
|
const clientCredentials = createClientCredentialsCache(connection, authenticator.clientId, tokens, refreshSkewMs);
|
|
1061
|
-
const validationContext = async (signal) =>
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1161
|
+
const validationContext = async (signal) => {
|
|
1162
|
+
const document = await discovery.get(signal);
|
|
1163
|
+
return {
|
|
1164
|
+
keys: withClientSecret(await signingKeys(signal), clientSecret),
|
|
1165
|
+
decrypt,
|
|
1166
|
+
issuer: connection.issuer,
|
|
1167
|
+
clientId: authenticator.clientId,
|
|
1168
|
+
algorithms: acceptedSigningAlgorithms(idTokenAlgorithm, document.id_token_signing_alg_values_supported),
|
|
1169
|
+
clockSkew,
|
|
1170
|
+
now: connection.clock.now()
|
|
1171
|
+
};
|
|
1172
|
+
};
|
|
1070
1173
|
const validateIdToken = async (idToken, expectations = {}) => {
|
|
1071
1174
|
requireValue(idToken, "idToken");
|
|
1072
1175
|
return validateAuthioIdToken(idToken, await validationContext(expectations.signal), expectations);
|
|
@@ -1075,7 +1178,13 @@ function createAuthioClient(options) {
|
|
|
1075
1178
|
const redirectUri = request.redirectUri ?? options.redirectUri;
|
|
1076
1179
|
requireValue(redirectUri, "redirectUri");
|
|
1077
1180
|
const document = await discovery.get(request.signal);
|
|
1078
|
-
return buildAuthorizationUrl(requireEndpoint(document, "authorization_endpoint"), authenticator.clientId, {
|
|
1181
|
+
return buildAuthorizationUrl(requireEndpoint(document, "authorization_endpoint"), authenticator.clientId, {
|
|
1182
|
+
...request,
|
|
1183
|
+
redirectUri,
|
|
1184
|
+
checks: request.checks ?? checks,
|
|
1185
|
+
codeChallengeMethod: request.codeChallengeMethod ?? codeChallengeMethod,
|
|
1186
|
+
responseMode: request.responseMode ?? responseMode
|
|
1187
|
+
});
|
|
1079
1188
|
};
|
|
1080
1189
|
return {
|
|
1081
1190
|
issuer: connection.issuer,
|
|
@@ -1119,12 +1228,19 @@ function createAuthioClient(options) {
|
|
|
1119
1228
|
);
|
|
1120
1229
|
const mediaType = response.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase();
|
|
1121
1230
|
if (response.ok && mediaType === "application/jwt")
|
|
1122
|
-
return readUserInfoJwt(
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1231
|
+
return readUserInfoJwt(
|
|
1232
|
+
(await response.text()).trim(),
|
|
1233
|
+
document,
|
|
1234
|
+
connection,
|
|
1235
|
+
authenticator.clientId,
|
|
1236
|
+
decrypt,
|
|
1237
|
+
withClientSecret(await signingKeys(callOptions.signal), clientSecret),
|
|
1238
|
+
{
|
|
1239
|
+
algorithms: acceptedSigningAlgorithms(userInfoAlgorithm, document.userinfo_signing_alg_values_supported ?? document.id_token_signing_alg_values_supported),
|
|
1240
|
+
clockSkew,
|
|
1241
|
+
status: response.status
|
|
1242
|
+
}
|
|
1243
|
+
);
|
|
1128
1244
|
if (!response.ok) {
|
|
1129
1245
|
const body = await response.text();
|
|
1130
1246
|
const challenge = toErrorResponse(body) ? void 0 : readBearerChallenge(response);
|
|
@@ -1134,9 +1250,10 @@ function createAuthioClient(options) {
|
|
|
1134
1250
|
},
|
|
1135
1251
|
createAuthorizationUrl,
|
|
1136
1252
|
async handleAuthorizationCallback(callback, transaction, callOptions = {}) {
|
|
1137
|
-
if (!transaction?.
|
|
1253
|
+
if (!transaction?.redirectUri || !Array.isArray(transaction.checks))
|
|
1254
|
+
throw new TypeError("transaction is required: keep what createAuthorizationUrl returned.");
|
|
1138
1255
|
const parameters = readCallbackParameters(callback);
|
|
1139
|
-
if (parameters.state !== transaction.state)
|
|
1256
|
+
if (transaction.checks.includes("state") && (!transaction.state || parameters.state !== transaction.state))
|
|
1140
1257
|
throw new AuthioCallbackError("state_mismatch", "The callback's state is not the one this sign-in was started with.");
|
|
1141
1258
|
const document = await discovery.get(callOptions.signal);
|
|
1142
1259
|
if (parameters.iss !== void 0) {
|
|
@@ -1150,12 +1267,12 @@ function createAuthioClient(options) {
|
|
|
1150
1267
|
const answer = await tokens.authorizationCode({
|
|
1151
1268
|
code: parameters.code,
|
|
1152
1269
|
redirectUri: transaction.redirectUri,
|
|
1153
|
-
codeVerifier: transaction.codeVerifier,
|
|
1270
|
+
codeVerifier: transaction.checks.includes("pkce") ? transaction.codeVerifier : void 0,
|
|
1154
1271
|
resource: transaction.resource,
|
|
1155
1272
|
signal: callOptions.signal
|
|
1156
1273
|
});
|
|
1157
1274
|
const idToken = answer.id_token ? await validateIdToken(answer.id_token, {
|
|
1158
|
-
nonce: transaction.nonce,
|
|
1275
|
+
nonce: transaction.checks.includes("nonce") ? transaction.nonce : void 0,
|
|
1159
1276
|
accessToken: answer.access_token,
|
|
1160
1277
|
maxAge: transaction.maxAge,
|
|
1161
1278
|
signal: callOptions.signal
|
|
@@ -1167,6 +1284,23 @@ function createAuthioClient(options) {
|
|
|
1167
1284
|
requireValue(logoutToken, "logoutToken");
|
|
1168
1285
|
return validateAuthioLogoutToken(logoutToken, await validationContext(callOptions.signal));
|
|
1169
1286
|
},
|
|
1287
|
+
validateFrontChannelLogout(request) {
|
|
1288
|
+
return readAuthioFrontChannelLogout(request, connection.issuer);
|
|
1289
|
+
},
|
|
1290
|
+
async readAccessTokenClaims(accessToken, callOptions = {}) {
|
|
1291
|
+
requireValue(accessToken, "accessToken");
|
|
1292
|
+
if (isAuthioTokenEncrypted(accessToken) && encryptionKey === void 0) return void 0;
|
|
1293
|
+
const signed = await decrypt(accessToken);
|
|
1294
|
+
if (signed.split(".").length !== 3) return void 0;
|
|
1295
|
+
const document = await discovery.get(callOptions.signal);
|
|
1296
|
+
const { payload } = await verifyAuthioJwt(signed, withClientSecret(await signingKeys(callOptions.signal), clientSecret), {
|
|
1297
|
+
issuer: connection.issuer,
|
|
1298
|
+
algorithms: acceptedSigningAlgorithms(accessTokenAlgorithm, document.id_token_signing_alg_values_supported),
|
|
1299
|
+
clockSkew,
|
|
1300
|
+
now: connection.clock.now()
|
|
1301
|
+
});
|
|
1302
|
+
return payload;
|
|
1303
|
+
},
|
|
1170
1304
|
async createEndSessionUrl(request = {}) {
|
|
1171
1305
|
const document = await discovery.get(request.signal);
|
|
1172
1306
|
const url = new URL(requireEndpoint(document, "end_session_endpoint"));
|
|
@@ -1258,7 +1392,7 @@ function createTokenClient(connection, discovery, authenticator) {
|
|
|
1258
1392
|
refresh: (options) => {
|
|
1259
1393
|
requireValue(options?.refreshToken, "refreshToken");
|
|
1260
1394
|
return requestToken(
|
|
1261
|
-
{ grant_type: AuthioGrantTypes.RefreshToken, refresh_token: options.refreshToken, scope: toScopeString(options.scopes) },
|
|
1395
|
+
{ grant_type: AuthioGrantTypes.RefreshToken, refresh_token: options.refreshToken, scope: toScopeString(options.scopes), resource: options.resource },
|
|
1262
1396
|
options.signal
|
|
1263
1397
|
);
|
|
1264
1398
|
},
|
|
@@ -1453,10 +1587,10 @@ async function postForm(connection, discovery, authenticator, endpoint, paramete
|
|
|
1453
1587
|
await authenticator?.apply(form, headers);
|
|
1454
1588
|
return send(connection, url, { method: "POST", headers, body: form.toString() }, signal);
|
|
1455
1589
|
}
|
|
1456
|
-
async function readUserInfoJwt(jwt, document, connection, clientId, decrypt,
|
|
1590
|
+
async function readUserInfoJwt(jwt, document, connection, clientId, decrypt, keys, settings) {
|
|
1457
1591
|
try {
|
|
1458
1592
|
const signed = isAuthioTokenEncrypted(jwt) ? await decrypt(jwt) : jwt;
|
|
1459
|
-
const { payload } = await verifyAuthioJwt(signed,
|
|
1593
|
+
const { payload } = await verifyAuthioJwt(signed, keys, {
|
|
1460
1594
|
issuer: document.issuer.replace(/\/+$/, ""),
|
|
1461
1595
|
audiences: [clientId],
|
|
1462
1596
|
algorithms: settings.algorithms,
|
|
@@ -1488,6 +1622,33 @@ function readBearerChallenge(response) {
|
|
|
1488
1622
|
}
|
|
1489
1623
|
return error ? { error, description } : void 0;
|
|
1490
1624
|
}
|
|
1625
|
+
function resolveCodeChallengeMethod(method) {
|
|
1626
|
+
if (method !== void 0 && method !== "S256" && method !== "plain")
|
|
1627
|
+
throw new AuthioConfigurationError(`The Authio codeChallengeMethod '${String(method)}' is not S256 or plain.`);
|
|
1628
|
+
return method;
|
|
1629
|
+
}
|
|
1630
|
+
function resolveResponseMode(mode) {
|
|
1631
|
+
if (mode !== void 0 && mode !== "query" && mode !== "web_message")
|
|
1632
|
+
throw new AuthioConfigurationError(`The Authio responseMode '${String(mode)}' is not query or web_message.`);
|
|
1633
|
+
return mode;
|
|
1634
|
+
}
|
|
1635
|
+
function resolveSigningAlgorithm(algorithm, setting, clientSecret) {
|
|
1636
|
+
if (algorithm === void 0 || algorithm === "") return void 0;
|
|
1637
|
+
const symmetric = AuthioHmacAlgorithms.includes(algorithm);
|
|
1638
|
+
if (!symmetric && !AuthioAsymmetricAlgorithms.includes(algorithm))
|
|
1639
|
+
throw new AuthioConfigurationError(
|
|
1640
|
+
`The Authio ${setting} '${algorithm}' is not one of ${[...AuthioAsymmetricAlgorithms, ...AuthioHmacAlgorithms].join(", ")}.`
|
|
1641
|
+
);
|
|
1642
|
+
if (symmetric && !clientSecret)
|
|
1643
|
+
throw new AuthioConfigurationError(`The Authio ${setting} ${algorithm} is verified with the client secret (OpenID Connect Core \xA710.1): set clientSecret.`);
|
|
1644
|
+
if (symmetric && typeof clientSecret === "string") requireHmacSecretLength(algorithm, new TextEncoder().encode(clientSecret).length);
|
|
1645
|
+
return algorithm;
|
|
1646
|
+
}
|
|
1647
|
+
async function readSecret(source) {
|
|
1648
|
+
const value = typeof source === "function" ? await source() : source;
|
|
1649
|
+
if (typeof value !== "string" || !value) throw new AuthioConfigurationError("The Authio clientSecret function returned no secret.");
|
|
1650
|
+
return value;
|
|
1651
|
+
}
|
|
1491
1652
|
function requireValue(value, name) {
|
|
1492
1653
|
if (typeof value !== "string" || !value.trim()) throw new TypeError(`${name} is required.`);
|
|
1493
1654
|
}
|
|
@@ -1540,7 +1701,7 @@ function createAuthioAccountCredential(options) {
|
|
|
1540
1701
|
realm: AuthioDefaults.PlatformRealm,
|
|
1541
1702
|
clientId: kind === "client_secret" ? toCredentialClientId(options.credentialId) : AuthioDefaults.PlatformClientId,
|
|
1542
1703
|
clientSecret: kind === "client_secret" ? value : void 0,
|
|
1543
|
-
|
|
1704
|
+
tokenEndpointAuthMethod: kind === "client_secret" ? "client_secret_post" : "none"
|
|
1544
1705
|
});
|
|
1545
1706
|
}
|
|
1546
1707
|
const exchange = async (signal) => {
|
|
@@ -1917,6 +2078,7 @@ exports.AuthioAdminError = AuthioAdminError;
|
|
|
1917
2078
|
exports.AuthioAsymmetricAlgorithms = AuthioAsymmetricAlgorithms;
|
|
1918
2079
|
exports.AuthioCallbackError = AuthioCallbackError;
|
|
1919
2080
|
exports.AuthioCallerKinds = AuthioCallerKinds;
|
|
2081
|
+
exports.AuthioChecks = AuthioChecks;
|
|
1920
2082
|
exports.AuthioClaimTypes = AuthioClaimTypes;
|
|
1921
2083
|
exports.AuthioClientAssertionAlgorithms = AuthioClientAssertionAlgorithms;
|
|
1922
2084
|
exports.AuthioConfigurationError = AuthioConfigurationError;
|
|
@@ -1925,6 +2087,8 @@ exports.AuthioDefaults = AuthioDefaults;
|
|
|
1925
2087
|
exports.AuthioDiscoveryError = AuthioDiscoveryError;
|
|
1926
2088
|
exports.AuthioError = AuthioError;
|
|
1927
2089
|
exports.AuthioGrantTypes = AuthioGrantTypes;
|
|
2090
|
+
exports.AuthioHmacAlgorithms = AuthioHmacAlgorithms;
|
|
2091
|
+
exports.AuthioHmacMinimumSecretBytes = AuthioHmacMinimumSecretBytes;
|
|
1928
2092
|
exports.AuthioKeyManagementAlgorithms = AuthioKeyManagementAlgorithms;
|
|
1929
2093
|
exports.AuthioLegacyKeyManagementAlgorithms = AuthioLegacyKeyManagementAlgorithms;
|
|
1930
2094
|
exports.AuthioOAuthErrors = AuthioOAuthErrors;
|
|
@@ -1933,6 +2097,8 @@ exports.AuthioProtocolError = AuthioProtocolError;
|
|
|
1933
2097
|
exports.AuthioResourceAccess = AuthioResourceAccess;
|
|
1934
2098
|
exports.AuthioResourceAccessClaims = AuthioResourceAccessClaims;
|
|
1935
2099
|
exports.AuthioScopes = AuthioScopes;
|
|
2100
|
+
exports.AuthioSupportedContentEncryptionAlgorithms = AuthioSupportedContentEncryptionAlgorithms;
|
|
2101
|
+
exports.AuthioTokenEndpointAuthMethods = AuthioTokenEndpointAuthMethods;
|
|
1936
2102
|
exports.AuthioTokenTypes = AuthioTokenTypes;
|
|
1937
2103
|
exports.AuthioTokenValidationError = AuthioTokenValidationError;
|
|
1938
2104
|
exports.AuthioWebhookEventTypes = AuthioWebhookEventTypes;
|
|
@@ -1969,7 +2135,9 @@ exports.parseResourceAccessClaim = parseResourceAccessClaim;
|
|
|
1969
2135
|
exports.parseWebhookEvent = parseWebhookEvent;
|
|
1970
2136
|
exports.randomValue = randomValue;
|
|
1971
2137
|
exports.readAuthioAdminResponse = readAuthioAdminResponse;
|
|
2138
|
+
exports.readAuthioFrontChannelLogout = readAuthioFrontChannelLogout;
|
|
1972
2139
|
exports.readWebhookEventData = readWebhookEventData;
|
|
2140
|
+
exports.requireHmacSecretLength = requireHmacSecretLength;
|
|
1973
2141
|
exports.resolveConnection = resolveConnection;
|
|
1974
2142
|
exports.resolveIssuer = resolveIssuer;
|
|
1975
2143
|
exports.resourceAccessSourceOf = resourceAccessSourceOf;
|
|
@@ -1986,5 +2154,6 @@ exports.toTokenValidationError = toTokenValidationError;
|
|
|
1986
2154
|
exports.tryParseWebhookEvent = tryParseWebhookEvent;
|
|
1987
2155
|
exports.verifyAuthioJwt = verifyAuthioJwt;
|
|
1988
2156
|
exports.verifyWebhookSignature = verifyWebhookSignature;
|
|
2157
|
+
exports.withClientSecret = withClientSecret;
|
|
1989
2158
|
//# sourceMappingURL=index.cjs.map
|
|
1990
2159
|
//# sourceMappingURL=index.cjs.map
|