@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/dist/index.js
CHANGED
|
@@ -147,6 +147,8 @@ var AuthioAsymmetricAlgorithms = [
|
|
|
147
147
|
"ES384",
|
|
148
148
|
"ES512"
|
|
149
149
|
];
|
|
150
|
+
var AuthioHmacAlgorithms = ["HS256", "HS384", "HS512"];
|
|
151
|
+
var AuthioHmacMinimumSecretBytes = Object.freeze({ HS256: 32, HS384: 48, HS512: 64 });
|
|
150
152
|
var AuthioDefaults = {
|
|
151
153
|
DiscoveryPath: "/.well-known/openid-configuration",
|
|
152
154
|
ProtectedResourceMetadataPath: "/.well-known/oauth-protected-resource",
|
|
@@ -278,6 +280,12 @@ var systemClock = {
|
|
|
278
280
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
279
281
|
})
|
|
280
282
|
};
|
|
283
|
+
var AuthioTokenEndpointAuthMethods = [
|
|
284
|
+
"client_secret_basic",
|
|
285
|
+
"client_secret_post",
|
|
286
|
+
"private_key_jwt",
|
|
287
|
+
"none"
|
|
288
|
+
];
|
|
281
289
|
function resolveConnection(options) {
|
|
282
290
|
const requireHttps = options.requireHttps ?? true;
|
|
283
291
|
const issuer = resolveIssuer(options);
|
|
@@ -593,7 +601,9 @@ function createClientAuthenticator(settings, connection) {
|
|
|
593
601
|
const clientId = settings.clientId?.trim();
|
|
594
602
|
if (!clientId) throw new AuthioConfigurationError("The Authio clientId is required.");
|
|
595
603
|
const hasKey = settings.clientPrivateKey !== void 0 || !!settings.clientPrivateKeyPath;
|
|
596
|
-
const method = settings.
|
|
604
|
+
const method = settings.tokenEndpointAuthMethod ?? (settings.clientSecret ? "client_secret_basic" : hasKey ? "private_key_jwt" : "none");
|
|
605
|
+
if (!AuthioTokenEndpointAuthMethods.includes(method))
|
|
606
|
+
throw new AuthioConfigurationError(`The Authio tokenEndpointAuthMethod '${String(method)}' is not one of ${AuthioTokenEndpointAuthMethods.join(", ")}.`);
|
|
597
607
|
if ((method === "client_secret_basic" || method === "client_secret_post") && !settings.clientSecret)
|
|
598
608
|
throw new AuthioConfigurationError(`The Authio clientSecret is required with ${method}.`);
|
|
599
609
|
if (method === "private_key_jwt" && !hasKey)
|
|
@@ -675,6 +685,7 @@ function createKeyLoader(settings) {
|
|
|
675
685
|
var AuthioKeyManagementAlgorithms = ["RSA-OAEP", "RSA-OAEP-256"];
|
|
676
686
|
var AuthioLegacyKeyManagementAlgorithms = ["RSA1_5"];
|
|
677
687
|
var AuthioContentEncryptionAlgorithms = ["A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512"];
|
|
688
|
+
var AuthioSupportedContentEncryptionAlgorithms = [...AuthioContentEncryptionAlgorithms, "A128GCM", "A192GCM", "A256GCM"];
|
|
678
689
|
var rsaAlgorithmIdentifier = [48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0];
|
|
679
690
|
function isAuthioTokenEncrypted(token) {
|
|
680
691
|
return typeof token === "string" && token.split(".").length === 5;
|
|
@@ -682,6 +693,13 @@ function isAuthioTokenEncrypted(token) {
|
|
|
682
693
|
function createAuthioTokenDecrypter(key, options = {}) {
|
|
683
694
|
const settings = typeof options === "string" ? { setting: options } : options;
|
|
684
695
|
const setting = settings.setting ?? "clientEncryptionKey";
|
|
696
|
+
const keyManagementAlgorithms = resolveAllowlist(settings.encryptionAlgorithms, AuthioKeyManagementAlgorithms, AuthioKeyManagementAlgorithms, "encryptionAlgorithms");
|
|
697
|
+
const contentEncryptionAlgorithms = resolveAllowlist(
|
|
698
|
+
settings.encryptionMethods,
|
|
699
|
+
AuthioContentEncryptionAlgorithms,
|
|
700
|
+
AuthioSupportedContentEncryptionAlgorithms,
|
|
701
|
+
"encryptionMethods"
|
|
702
|
+
);
|
|
685
703
|
const imported = /* @__PURE__ */ new Map();
|
|
686
704
|
const importFor = (algorithm) => {
|
|
687
705
|
let pending = imported.get(algorithm);
|
|
@@ -700,10 +718,16 @@ function createAuthioTokenDecrypter(key, options = {}) {
|
|
|
700
718
|
"legacy_algorithm",
|
|
701
719
|
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."
|
|
702
720
|
);
|
|
703
|
-
if (!
|
|
721
|
+
if (!keyManagementAlgorithms.includes(algorithm))
|
|
722
|
+
throw new AuthioTokenValidationError(
|
|
723
|
+
"decryption_failed",
|
|
724
|
+
`The token is encrypted with '${algorithm}', and encryptionAlgorithms accepts ${keyManagementAlgorithms.join(" or ")}.`
|
|
725
|
+
);
|
|
726
|
+
const method = contentMethodOf(token);
|
|
727
|
+
if (!contentEncryptionAlgorithms.includes(method))
|
|
704
728
|
throw new AuthioTokenValidationError(
|
|
705
729
|
"decryption_failed",
|
|
706
|
-
`The token is encrypted with '${
|
|
730
|
+
`The token's content is encrypted with '${method}', and encryptionMethods accepts ${contentEncryptionAlgorithms.join(" or ")}.`
|
|
707
731
|
);
|
|
708
732
|
if (key === void 0 || key === null || key === "")
|
|
709
733
|
throw new AuthioTokenValidationError(
|
|
@@ -712,8 +736,8 @@ function createAuthioTokenDecrypter(key, options = {}) {
|
|
|
712
736
|
);
|
|
713
737
|
try {
|
|
714
738
|
const { plaintext } = await compactDecrypt(token, (header) => importFor(header.alg), {
|
|
715
|
-
keyManagementAlgorithms: [...
|
|
716
|
-
contentEncryptionAlgorithms: [...
|
|
739
|
+
keyManagementAlgorithms: [...keyManagementAlgorithms],
|
|
740
|
+
contentEncryptionAlgorithms: [...contentEncryptionAlgorithms]
|
|
717
741
|
});
|
|
718
742
|
return new TextDecoder().decode(plaintext);
|
|
719
743
|
} catch (error) {
|
|
@@ -747,6 +771,24 @@ function algorithmOf(token) {
|
|
|
747
771
|
throw new AuthioTokenValidationError("decryption_failed", "The encrypted token's header could not be read.", { cause: error });
|
|
748
772
|
}
|
|
749
773
|
}
|
|
774
|
+
function contentMethodOf(token) {
|
|
775
|
+
try {
|
|
776
|
+
return String(decodeProtectedHeader(token).enc ?? "");
|
|
777
|
+
} catch (error) {
|
|
778
|
+
throw new AuthioTokenValidationError("decryption_failed", "The encrypted token's header could not be read.", { cause: error });
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
function resolveAllowlist(configured, defaults, supported, setting) {
|
|
782
|
+
if (configured === void 0 || configured.length === 0) return defaults;
|
|
783
|
+
for (const value of configured) {
|
|
784
|
+
if (AuthioLegacyKeyManagementAlgorithms.includes(value))
|
|
785
|
+
throw new AuthioConfigurationError(
|
|
786
|
+
`${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.`
|
|
787
|
+
);
|
|
788
|
+
if (!supported.includes(value)) throw new AuthioConfigurationError(`${setting} holds '${value}'; it accepts ${supported.join(", ")}.`);
|
|
789
|
+
}
|
|
790
|
+
return [...new Set(configured)];
|
|
791
|
+
}
|
|
750
792
|
async function importDecryptionKey(key, algorithm, setting) {
|
|
751
793
|
try {
|
|
752
794
|
if (typeof key !== "string") return await importPrivateJwk(key, algorithm, setting);
|
|
@@ -803,7 +845,7 @@ async function verifyAuthioJwt(token, keys, expected) {
|
|
|
803
845
|
try {
|
|
804
846
|
const { payload, protectedHeader } = await jwtVerify(token, keys, {
|
|
805
847
|
issuer: expected.issuer,
|
|
806
|
-
audience: [...expected.audiences],
|
|
848
|
+
audience: expected.audiences ? [...expected.audiences] : void 0,
|
|
807
849
|
algorithms: [...expected.algorithms && expected.algorithms.length > 0 ? expected.algorithms : AuthioAsymmetricAlgorithms],
|
|
808
850
|
clockTolerance: expected.clockSkew ?? AuthioDefaults.ClockSkew,
|
|
809
851
|
requiredClaims: [.../* @__PURE__ */ new Set(["exp", ...expected.requiredClaims ?? []])],
|
|
@@ -811,9 +853,34 @@ async function verifyAuthioJwt(token, keys, expected) {
|
|
|
811
853
|
});
|
|
812
854
|
return { payload, header: protectedHeader };
|
|
813
855
|
} catch (error) {
|
|
814
|
-
|
|
856
|
+
if (error instanceof AuthioConfigurationError) throw error;
|
|
857
|
+
throw toTokenValidationError(error, expected.audiences ?? [], token);
|
|
815
858
|
}
|
|
816
859
|
}
|
|
860
|
+
function withClientSecret(keys, secret) {
|
|
861
|
+
if (!secret) return keys;
|
|
862
|
+
return async (header, token) => {
|
|
863
|
+
const algorithm = String(header.alg);
|
|
864
|
+
if (!AuthioHmacAlgorithms.includes(algorithm)) return keys(header, token);
|
|
865
|
+
const bytes = new TextEncoder().encode(await secret());
|
|
866
|
+
requireHmacSecretLength(algorithm, bytes.length);
|
|
867
|
+
return bytes;
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
function requireHmacSecretLength(algorithm, length) {
|
|
871
|
+
const minimum = AuthioHmacMinimumSecretBytes[algorithm];
|
|
872
|
+
if (minimum !== void 0 && length < minimum)
|
|
873
|
+
throw new AuthioConfigurationError(
|
|
874
|
+
`The Authio clientSecret has ${length} bytes, and ${algorithm} needs at least ${minimum} (RFC 7518 \xA73.2): Authio refuses a shorter secret for it.`
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
function acceptedSigningAlgorithms(configured, announced) {
|
|
878
|
+
if (configured) return [configured];
|
|
879
|
+
const asymmetric = (Array.isArray(announced) ? announced : []).filter(
|
|
880
|
+
(algorithm) => typeof algorithm === "string" && AuthioAsymmetricAlgorithms.includes(algorithm)
|
|
881
|
+
);
|
|
882
|
+
return asymmetric.length > 0 ? asymmetric : AuthioAsymmetricAlgorithms;
|
|
883
|
+
}
|
|
817
884
|
function toTokenValidationError(error, audiences, token) {
|
|
818
885
|
if (error instanceof AuthioTokenValidationError) return error;
|
|
819
886
|
const refuse = (reason, message) => new AuthioTokenValidationError(reason, message, { cause: error });
|
|
@@ -945,6 +1012,7 @@ function toBase64Url(bytes) {
|
|
|
945
1012
|
}
|
|
946
1013
|
|
|
947
1014
|
// src/authorization.ts
|
|
1015
|
+
var AuthioChecks = ["pkce", "state", "nonce", "none"];
|
|
948
1016
|
async function createAuthioPkce(method = "S256") {
|
|
949
1017
|
if (method !== "S256" && method !== "plain") throw new TypeError(`The PKCE method '${String(method)}' is not S256 or plain.`);
|
|
950
1018
|
const codeVerifier = randomValue(32);
|
|
@@ -954,14 +1022,26 @@ async function createAuthioPkce(method = "S256") {
|
|
|
954
1022
|
function randomValue(bytes = 32) {
|
|
955
1023
|
return toBase64Url2(crypto.getRandomValues(new Uint8Array(bytes)));
|
|
956
1024
|
}
|
|
1025
|
+
function resolveChecks(checks, setting = "checks") {
|
|
1026
|
+
const resolved = [];
|
|
1027
|
+
for (const check of checks ?? []) {
|
|
1028
|
+
if (!AuthioChecks.includes(check))
|
|
1029
|
+
throw new AuthioConfigurationError(`The Authio ${setting} value '${String(check)}' is not one of ${AuthioChecks.join(", ")}.`);
|
|
1030
|
+
if (check !== "none" && !resolved.includes(check)) resolved.push(check);
|
|
1031
|
+
}
|
|
1032
|
+
return Object.freeze(resolved);
|
|
1033
|
+
}
|
|
957
1034
|
async function buildAuthorizationUrl(endpoint, clientId, request) {
|
|
958
|
-
const
|
|
959
|
-
const
|
|
960
|
-
const
|
|
1035
|
+
const checks = resolveChecks(request.checks);
|
|
1036
|
+
const pkce = checks.includes("pkce") ? await createAuthioPkce(request.codeChallengeMethod ?? "S256") : void 0;
|
|
1037
|
+
const state = checks.includes("state") ? request.state?.trim() || randomValue() : void 0;
|
|
1038
|
+
const nonce = checks.includes("nonce") ? request.nonce?.trim() || randomValue() : void 0;
|
|
961
1039
|
if (request.maxAge !== void 0 && (!Number.isInteger(request.maxAge) || request.maxAge < 0))
|
|
962
1040
|
throw new RangeError("maxAge must be a non-negative whole number of seconds.");
|
|
963
1041
|
const display = request.popup ? "popup" : request.display;
|
|
964
1042
|
const responseMode = request.popup ? "web_message" : request.responseMode;
|
|
1043
|
+
if (responseMode !== void 0 && responseMode !== "query" && responseMode !== "web_message")
|
|
1044
|
+
throw new AuthioConfigurationError(`The Authio responseMode '${String(responseMode)}' is not query or web_message.`);
|
|
965
1045
|
const url = new URL(endpoint);
|
|
966
1046
|
const parameters = {
|
|
967
1047
|
...request.extraParameters,
|
|
@@ -969,10 +1049,10 @@ async function buildAuthorizationUrl(endpoint, clientId, request) {
|
|
|
969
1049
|
client_id: clientId,
|
|
970
1050
|
redirect_uri: request.redirectUri,
|
|
971
1051
|
scope: toScopeString(request.scopes) ?? "openid",
|
|
972
|
-
state,
|
|
973
|
-
nonce,
|
|
974
|
-
code_challenge: pkce
|
|
975
|
-
code_challenge_method: pkce
|
|
1052
|
+
state: state ?? request.state?.trim(),
|
|
1053
|
+
nonce: nonce ?? request.nonce?.trim(),
|
|
1054
|
+
code_challenge: pkce?.codeChallenge,
|
|
1055
|
+
code_challenge_method: pkce?.codeChallengeMethod,
|
|
976
1056
|
resource: request.resource,
|
|
977
1057
|
organization: request.organization,
|
|
978
1058
|
prompt: joined(request.prompt),
|
|
@@ -989,10 +1069,10 @@ async function buildAuthorizationUrl(endpoint, clientId, request) {
|
|
|
989
1069
|
return {
|
|
990
1070
|
url: url.toString(),
|
|
991
1071
|
transaction: Object.freeze({
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
codeChallengeMethod: pkce.codeChallengeMethod,
|
|
1072
|
+
checks,
|
|
1073
|
+
...state ? { state } : {},
|
|
1074
|
+
...nonce ? { nonce } : {},
|
|
1075
|
+
...pkce ? { codeVerifier: pkce.codeVerifier, codeChallengeMethod: pkce.codeChallengeMethod } : {},
|
|
996
1076
|
redirectUri: request.redirectUri,
|
|
997
1077
|
...request.resource ? { resource: request.resource } : {},
|
|
998
1078
|
...request.maxAge !== void 0 ? { maxAge: request.maxAge } : {}
|
|
@@ -1014,6 +1094,16 @@ function readCallbackParameters(callback) {
|
|
|
1014
1094
|
}
|
|
1015
1095
|
return parameters;
|
|
1016
1096
|
}
|
|
1097
|
+
function readAuthioFrontChannelLogout(request, issuer) {
|
|
1098
|
+
const parameters = readCallbackParameters(request);
|
|
1099
|
+
const sessionId = parameters.sid?.trim() || void 0;
|
|
1100
|
+
const sentIssuer = parameters.iss?.trim() || void 0;
|
|
1101
|
+
if (sentIssuer !== void 0 && sentIssuer.replace(/\/+$/, "") !== issuer.replace(/\/+$/, ""))
|
|
1102
|
+
throw new AuthioCallbackError("issuer_mismatch", `The front-channel logout comes from the issuer '${sentIssuer}', not '${issuer}'.`);
|
|
1103
|
+
if (sessionId !== void 0 && sentIssuer === void 0)
|
|
1104
|
+
throw new AuthioCallbackError("missing_issuer", "The front-channel logout names a sid without its iss.");
|
|
1105
|
+
return Object.freeze({ sessionId, issuer: sentIssuer });
|
|
1106
|
+
}
|
|
1017
1107
|
function parseAuthioBearerChallenge(header) {
|
|
1018
1108
|
if (!header || !/^\s*bearer(\s|$)/i.test(header)) return void 0;
|
|
1019
1109
|
const values = {};
|
|
@@ -1049,22 +1139,35 @@ function createAuthioClient(options) {
|
|
|
1049
1139
|
const authenticator = createClientAuthenticator(options, connection);
|
|
1050
1140
|
const refreshSkewMs = seconds(options.tokenRefreshSkew, AuthioDefaults.TokenRefreshSkew, "tokenRefreshSkew");
|
|
1051
1141
|
const clockSkew = seconds(options.clockSkew, AuthioDefaults.ClockSkew, "clockSkew") / 1e3;
|
|
1052
|
-
const
|
|
1142
|
+
const encryptionKey = resolveEncryptionKey(options);
|
|
1143
|
+
const decrypt = createAuthioTokenDecrypter(encryptionKey, {
|
|
1053
1144
|
setting: options.clientEncryptionKey === void 0 && options.clientEncryptionKeyPath ? "clientEncryptionKeyPath" : "clientEncryptionKey",
|
|
1054
|
-
allowLegacyEncryptionAlgorithms: options.allowLegacyEncryptionAlgorithms
|
|
1145
|
+
allowLegacyEncryptionAlgorithms: options.allowLegacyEncryptionAlgorithms,
|
|
1146
|
+
encryptionAlgorithms: options.encryptionAlgorithms,
|
|
1147
|
+
encryptionMethods: options.encryptionMethods
|
|
1055
1148
|
});
|
|
1149
|
+
const checks = resolveChecks(options.checks);
|
|
1150
|
+
const codeChallengeMethod = resolveCodeChallengeMethod(options.codeChallengeMethod);
|
|
1151
|
+
const responseMode = resolveResponseMode(options.responseMode);
|
|
1152
|
+
const idTokenAlgorithm = resolveSigningAlgorithm(options.idTokenSignedResponseAlg, "idTokenSignedResponseAlg", options.clientSecret);
|
|
1153
|
+
const userInfoAlgorithm = resolveSigningAlgorithm(options.userinfoSignedResponseAlg, "userinfoSignedResponseAlg", options.clientSecret) ?? idTokenAlgorithm;
|
|
1154
|
+
const accessTokenAlgorithm = resolveSigningAlgorithm(options.accessTokenSignedResponseAlg, "accessTokenSignedResponseAlg", options.clientSecret);
|
|
1155
|
+
const clientSecret = options.clientSecret === void 0 ? void 0 : () => readSecret(options.clientSecret);
|
|
1056
1156
|
const signingKeys = createAuthioSigningKeys(connection, discovery);
|
|
1057
1157
|
const tokens = createTokenClient(connection, discovery, authenticator);
|
|
1058
1158
|
const clientCredentials = createClientCredentialsCache(connection, authenticator.clientId, tokens, refreshSkewMs);
|
|
1059
|
-
const validationContext = async (signal) =>
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1159
|
+
const validationContext = async (signal) => {
|
|
1160
|
+
const document = await discovery.get(signal);
|
|
1161
|
+
return {
|
|
1162
|
+
keys: withClientSecret(await signingKeys(signal), clientSecret),
|
|
1163
|
+
decrypt,
|
|
1164
|
+
issuer: connection.issuer,
|
|
1165
|
+
clientId: authenticator.clientId,
|
|
1166
|
+
algorithms: acceptedSigningAlgorithms(idTokenAlgorithm, document.id_token_signing_alg_values_supported),
|
|
1167
|
+
clockSkew,
|
|
1168
|
+
now: connection.clock.now()
|
|
1169
|
+
};
|
|
1170
|
+
};
|
|
1068
1171
|
const validateIdToken = async (idToken, expectations = {}) => {
|
|
1069
1172
|
requireValue(idToken, "idToken");
|
|
1070
1173
|
return validateAuthioIdToken(idToken, await validationContext(expectations.signal), expectations);
|
|
@@ -1073,7 +1176,13 @@ function createAuthioClient(options) {
|
|
|
1073
1176
|
const redirectUri = request.redirectUri ?? options.redirectUri;
|
|
1074
1177
|
requireValue(redirectUri, "redirectUri");
|
|
1075
1178
|
const document = await discovery.get(request.signal);
|
|
1076
|
-
return buildAuthorizationUrl(requireEndpoint(document, "authorization_endpoint"), authenticator.clientId, {
|
|
1179
|
+
return buildAuthorizationUrl(requireEndpoint(document, "authorization_endpoint"), authenticator.clientId, {
|
|
1180
|
+
...request,
|
|
1181
|
+
redirectUri,
|
|
1182
|
+
checks: request.checks ?? checks,
|
|
1183
|
+
codeChallengeMethod: request.codeChallengeMethod ?? codeChallengeMethod,
|
|
1184
|
+
responseMode: request.responseMode ?? responseMode
|
|
1185
|
+
});
|
|
1077
1186
|
};
|
|
1078
1187
|
return {
|
|
1079
1188
|
issuer: connection.issuer,
|
|
@@ -1117,12 +1226,19 @@ function createAuthioClient(options) {
|
|
|
1117
1226
|
);
|
|
1118
1227
|
const mediaType = response.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase();
|
|
1119
1228
|
if (response.ok && mediaType === "application/jwt")
|
|
1120
|
-
return readUserInfoJwt(
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1229
|
+
return readUserInfoJwt(
|
|
1230
|
+
(await response.text()).trim(),
|
|
1231
|
+
document,
|
|
1232
|
+
connection,
|
|
1233
|
+
authenticator.clientId,
|
|
1234
|
+
decrypt,
|
|
1235
|
+
withClientSecret(await signingKeys(callOptions.signal), clientSecret),
|
|
1236
|
+
{
|
|
1237
|
+
algorithms: acceptedSigningAlgorithms(userInfoAlgorithm, document.userinfo_signing_alg_values_supported ?? document.id_token_signing_alg_values_supported),
|
|
1238
|
+
clockSkew,
|
|
1239
|
+
status: response.status
|
|
1240
|
+
}
|
|
1241
|
+
);
|
|
1126
1242
|
if (!response.ok) {
|
|
1127
1243
|
const body = await response.text();
|
|
1128
1244
|
const challenge = toErrorResponse(body) ? void 0 : readBearerChallenge(response);
|
|
@@ -1132,9 +1248,10 @@ function createAuthioClient(options) {
|
|
|
1132
1248
|
},
|
|
1133
1249
|
createAuthorizationUrl,
|
|
1134
1250
|
async handleAuthorizationCallback(callback, transaction, callOptions = {}) {
|
|
1135
|
-
if (!transaction?.
|
|
1251
|
+
if (!transaction?.redirectUri || !Array.isArray(transaction.checks))
|
|
1252
|
+
throw new TypeError("transaction is required: keep what createAuthorizationUrl returned.");
|
|
1136
1253
|
const parameters = readCallbackParameters(callback);
|
|
1137
|
-
if (parameters.state !== transaction.state)
|
|
1254
|
+
if (transaction.checks.includes("state") && (!transaction.state || parameters.state !== transaction.state))
|
|
1138
1255
|
throw new AuthioCallbackError("state_mismatch", "The callback's state is not the one this sign-in was started with.");
|
|
1139
1256
|
const document = await discovery.get(callOptions.signal);
|
|
1140
1257
|
if (parameters.iss !== void 0) {
|
|
@@ -1148,12 +1265,12 @@ function createAuthioClient(options) {
|
|
|
1148
1265
|
const answer = await tokens.authorizationCode({
|
|
1149
1266
|
code: parameters.code,
|
|
1150
1267
|
redirectUri: transaction.redirectUri,
|
|
1151
|
-
codeVerifier: transaction.codeVerifier,
|
|
1268
|
+
codeVerifier: transaction.checks.includes("pkce") ? transaction.codeVerifier : void 0,
|
|
1152
1269
|
resource: transaction.resource,
|
|
1153
1270
|
signal: callOptions.signal
|
|
1154
1271
|
});
|
|
1155
1272
|
const idToken = answer.id_token ? await validateIdToken(answer.id_token, {
|
|
1156
|
-
nonce: transaction.nonce,
|
|
1273
|
+
nonce: transaction.checks.includes("nonce") ? transaction.nonce : void 0,
|
|
1157
1274
|
accessToken: answer.access_token,
|
|
1158
1275
|
maxAge: transaction.maxAge,
|
|
1159
1276
|
signal: callOptions.signal
|
|
@@ -1165,6 +1282,23 @@ function createAuthioClient(options) {
|
|
|
1165
1282
|
requireValue(logoutToken, "logoutToken");
|
|
1166
1283
|
return validateAuthioLogoutToken(logoutToken, await validationContext(callOptions.signal));
|
|
1167
1284
|
},
|
|
1285
|
+
validateFrontChannelLogout(request) {
|
|
1286
|
+
return readAuthioFrontChannelLogout(request, connection.issuer);
|
|
1287
|
+
},
|
|
1288
|
+
async readAccessTokenClaims(accessToken, callOptions = {}) {
|
|
1289
|
+
requireValue(accessToken, "accessToken");
|
|
1290
|
+
if (isAuthioTokenEncrypted(accessToken) && encryptionKey === void 0) return void 0;
|
|
1291
|
+
const signed = await decrypt(accessToken);
|
|
1292
|
+
if (signed.split(".").length !== 3) return void 0;
|
|
1293
|
+
const document = await discovery.get(callOptions.signal);
|
|
1294
|
+
const { payload } = await verifyAuthioJwt(signed, withClientSecret(await signingKeys(callOptions.signal), clientSecret), {
|
|
1295
|
+
issuer: connection.issuer,
|
|
1296
|
+
algorithms: acceptedSigningAlgorithms(accessTokenAlgorithm, document.id_token_signing_alg_values_supported),
|
|
1297
|
+
clockSkew,
|
|
1298
|
+
now: connection.clock.now()
|
|
1299
|
+
});
|
|
1300
|
+
return payload;
|
|
1301
|
+
},
|
|
1168
1302
|
async createEndSessionUrl(request = {}) {
|
|
1169
1303
|
const document = await discovery.get(request.signal);
|
|
1170
1304
|
const url = new URL(requireEndpoint(document, "end_session_endpoint"));
|
|
@@ -1256,7 +1390,7 @@ function createTokenClient(connection, discovery, authenticator) {
|
|
|
1256
1390
|
refresh: (options) => {
|
|
1257
1391
|
requireValue(options?.refreshToken, "refreshToken");
|
|
1258
1392
|
return requestToken(
|
|
1259
|
-
{ grant_type: AuthioGrantTypes.RefreshToken, refresh_token: options.refreshToken, scope: toScopeString(options.scopes) },
|
|
1393
|
+
{ grant_type: AuthioGrantTypes.RefreshToken, refresh_token: options.refreshToken, scope: toScopeString(options.scopes), resource: options.resource },
|
|
1260
1394
|
options.signal
|
|
1261
1395
|
);
|
|
1262
1396
|
},
|
|
@@ -1451,10 +1585,10 @@ async function postForm(connection, discovery, authenticator, endpoint, paramete
|
|
|
1451
1585
|
await authenticator?.apply(form, headers);
|
|
1452
1586
|
return send(connection, url, { method: "POST", headers, body: form.toString() }, signal);
|
|
1453
1587
|
}
|
|
1454
|
-
async function readUserInfoJwt(jwt, document, connection, clientId, decrypt,
|
|
1588
|
+
async function readUserInfoJwt(jwt, document, connection, clientId, decrypt, keys, settings) {
|
|
1455
1589
|
try {
|
|
1456
1590
|
const signed = isAuthioTokenEncrypted(jwt) ? await decrypt(jwt) : jwt;
|
|
1457
|
-
const { payload } = await verifyAuthioJwt(signed,
|
|
1591
|
+
const { payload } = await verifyAuthioJwt(signed, keys, {
|
|
1458
1592
|
issuer: document.issuer.replace(/\/+$/, ""),
|
|
1459
1593
|
audiences: [clientId],
|
|
1460
1594
|
algorithms: settings.algorithms,
|
|
@@ -1486,6 +1620,33 @@ function readBearerChallenge(response) {
|
|
|
1486
1620
|
}
|
|
1487
1621
|
return error ? { error, description } : void 0;
|
|
1488
1622
|
}
|
|
1623
|
+
function resolveCodeChallengeMethod(method) {
|
|
1624
|
+
if (method !== void 0 && method !== "S256" && method !== "plain")
|
|
1625
|
+
throw new AuthioConfigurationError(`The Authio codeChallengeMethod '${String(method)}' is not S256 or plain.`);
|
|
1626
|
+
return method;
|
|
1627
|
+
}
|
|
1628
|
+
function resolveResponseMode(mode) {
|
|
1629
|
+
if (mode !== void 0 && mode !== "query" && mode !== "web_message")
|
|
1630
|
+
throw new AuthioConfigurationError(`The Authio responseMode '${String(mode)}' is not query or web_message.`);
|
|
1631
|
+
return mode;
|
|
1632
|
+
}
|
|
1633
|
+
function resolveSigningAlgorithm(algorithm, setting, clientSecret) {
|
|
1634
|
+
if (algorithm === void 0 || algorithm === "") return void 0;
|
|
1635
|
+
const symmetric = AuthioHmacAlgorithms.includes(algorithm);
|
|
1636
|
+
if (!symmetric && !AuthioAsymmetricAlgorithms.includes(algorithm))
|
|
1637
|
+
throw new AuthioConfigurationError(
|
|
1638
|
+
`The Authio ${setting} '${algorithm}' is not one of ${[...AuthioAsymmetricAlgorithms, ...AuthioHmacAlgorithms].join(", ")}.`
|
|
1639
|
+
);
|
|
1640
|
+
if (symmetric && !clientSecret)
|
|
1641
|
+
throw new AuthioConfigurationError(`The Authio ${setting} ${algorithm} is verified with the client secret (OpenID Connect Core \xA710.1): set clientSecret.`);
|
|
1642
|
+
if (symmetric && typeof clientSecret === "string") requireHmacSecretLength(algorithm, new TextEncoder().encode(clientSecret).length);
|
|
1643
|
+
return algorithm;
|
|
1644
|
+
}
|
|
1645
|
+
async function readSecret(source) {
|
|
1646
|
+
const value = typeof source === "function" ? await source() : source;
|
|
1647
|
+
if (typeof value !== "string" || !value) throw new AuthioConfigurationError("The Authio clientSecret function returned no secret.");
|
|
1648
|
+
return value;
|
|
1649
|
+
}
|
|
1489
1650
|
function requireValue(value, name) {
|
|
1490
1651
|
if (typeof value !== "string" || !value.trim()) throw new TypeError(`${name} is required.`);
|
|
1491
1652
|
}
|
|
@@ -1538,7 +1699,7 @@ function createAuthioAccountCredential(options) {
|
|
|
1538
1699
|
realm: AuthioDefaults.PlatformRealm,
|
|
1539
1700
|
clientId: kind === "client_secret" ? toCredentialClientId(options.credentialId) : AuthioDefaults.PlatformClientId,
|
|
1540
1701
|
clientSecret: kind === "client_secret" ? value : void 0,
|
|
1541
|
-
|
|
1702
|
+
tokenEndpointAuthMethod: kind === "client_secret" ? "client_secret_post" : "none"
|
|
1542
1703
|
});
|
|
1543
1704
|
}
|
|
1544
1705
|
const exchange = async (signal) => {
|
|
@@ -1911,6 +2072,6 @@ function constantTimeEquals(left, right) {
|
|
|
1911
2072
|
return difference === 0;
|
|
1912
2073
|
}
|
|
1913
2074
|
|
|
1914
|
-
export { AuthioAdminError, AuthioAsymmetricAlgorithms, AuthioCallbackError, AuthioCallerKinds, AuthioClaimTypes, AuthioClientAssertionAlgorithms, AuthioConfigurationError, AuthioContentEncryptionAlgorithms, AuthioDefaults, AuthioDiscoveryError, AuthioError, AuthioGrantTypes, AuthioKeyManagementAlgorithms, AuthioLegacyKeyManagementAlgorithms, AuthioOAuthErrors, AuthioPrompts, AuthioProtocolError, AuthioResourceAccess, AuthioResourceAccessClaims, AuthioScopes, AuthioTokenTypes, AuthioTokenValidationError, AuthioWebhookEventTypes, AuthioWebhookHeaders, asAdminResourceEvent, asAttackDetectedEvent, asCredentialCompromisedEvent, asKeysRotatedEvent, asLoginEvent, asPingEvent, asTokenReuseDetectedEvent, asTokenRevokedEvent, asUserEvent, asUserLockedOutEvent, asUserSuspensionEvent, computeAuthioTokenHash, computeWebhookSignatureHeader, createAuthioAccountCredential, createAuthioClient, createAuthioDiscoveryClient, createAuthioPkce, createAuthioSigningKeys, createAuthioTokenDecrypter, encryptionKeyFromFile, escapeDataString, isAllowedUrl, isAuthioTokenEncrypted, isKnownWebhookEventType, isWebhookEventOfType, parseAuthioAdminEnvelope, parseAuthioBearerChallenge, parseResourceAccess, parseResourceAccessClaim, parseWebhookEvent, randomValue, readAuthioAdminResponse, readWebhookEventData, resolveConnection, resolveIssuer, resourceAccessSourceOf, seconds, systemClock, toBasicAuthorization, toCamelCasedMembers, toClientCredentialsToken, toConnectionExchangeResult, toCredentialClientId, toDevicePollOutcome, toScopeString, toTokenValidationError, tryParseWebhookEvent, verifyAuthioJwt, verifyWebhookSignature };
|
|
2075
|
+
export { AuthioAdminError, AuthioAsymmetricAlgorithms, AuthioCallbackError, AuthioCallerKinds, AuthioChecks, AuthioClaimTypes, AuthioClientAssertionAlgorithms, AuthioConfigurationError, AuthioContentEncryptionAlgorithms, AuthioDefaults, AuthioDiscoveryError, AuthioError, AuthioGrantTypes, AuthioHmacAlgorithms, AuthioHmacMinimumSecretBytes, AuthioKeyManagementAlgorithms, AuthioLegacyKeyManagementAlgorithms, AuthioOAuthErrors, AuthioPrompts, AuthioProtocolError, AuthioResourceAccess, AuthioResourceAccessClaims, AuthioScopes, AuthioSupportedContentEncryptionAlgorithms, AuthioTokenEndpointAuthMethods, AuthioTokenTypes, AuthioTokenValidationError, AuthioWebhookEventTypes, AuthioWebhookHeaders, asAdminResourceEvent, asAttackDetectedEvent, asCredentialCompromisedEvent, asKeysRotatedEvent, asLoginEvent, asPingEvent, asTokenReuseDetectedEvent, asTokenRevokedEvent, asUserEvent, asUserLockedOutEvent, asUserSuspensionEvent, computeAuthioTokenHash, computeWebhookSignatureHeader, createAuthioAccountCredential, createAuthioClient, createAuthioDiscoveryClient, createAuthioPkce, createAuthioSigningKeys, createAuthioTokenDecrypter, encryptionKeyFromFile, escapeDataString, isAllowedUrl, isAuthioTokenEncrypted, isKnownWebhookEventType, isWebhookEventOfType, parseAuthioAdminEnvelope, parseAuthioBearerChallenge, parseResourceAccess, parseResourceAccessClaim, parseWebhookEvent, randomValue, readAuthioAdminResponse, readAuthioFrontChannelLogout, readWebhookEventData, requireHmacSecretLength, resolveConnection, resolveIssuer, resourceAccessSourceOf, seconds, systemClock, toBasicAuthorization, toCamelCasedMembers, toClientCredentialsToken, toConnectionExchangeResult, toCredentialClientId, toDevicePollOutcome, toScopeString, toTokenValidationError, tryParseWebhookEvent, verifyAuthioJwt, verifyWebhookSignature, withClientSecret };
|
|
1915
2076
|
//# sourceMappingURL=index.js.map
|
|
1916
2077
|
//# sourceMappingURL=index.js.map
|