@hyper.authio/client 0.7.0 → 0.8.1
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 +49 -13
- package/dist/index.cjs +217 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +159 -26
- package/dist/index.d.ts +159 -26
- package/dist/index.js +210 -46
- 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"));
|
|
@@ -1203,13 +1337,16 @@ function createAuthioClient(options) {
|
|
|
1203
1337
|
};
|
|
1204
1338
|
const token = await clientCredentials.getToken({ ...key, signal: request.signal });
|
|
1205
1339
|
const response = await attempt(token);
|
|
1206
|
-
if (response.status !== 401) return response;
|
|
1340
|
+
if (response.status !== 401 || isStepUpChallenge(response)) return response;
|
|
1207
1341
|
clientCredentials.invalidate({ ...key, accessToken: token.accessToken });
|
|
1208
1342
|
return attempt(await clientCredentials.getToken({ ...key, signal: request.signal }));
|
|
1209
1343
|
};
|
|
1210
1344
|
}
|
|
1211
1345
|
};
|
|
1212
1346
|
}
|
|
1347
|
+
function isStepUpChallenge(response) {
|
|
1348
|
+
return parseAuthioBearerChallenge(response.headers.get("www-authenticate"))?.error === AuthioOAuthErrors.InsufficientUserAuthentication;
|
|
1349
|
+
}
|
|
1213
1350
|
function createTokenClient(connection, discovery, authenticator) {
|
|
1214
1351
|
const requestToken = async (parameters, signal, authenticate = true) => {
|
|
1215
1352
|
const response = await postForm(connection, discovery, authenticate ? authenticator : void 0, "token_endpoint", parameters, signal);
|
|
@@ -1256,7 +1393,7 @@ function createTokenClient(connection, discovery, authenticator) {
|
|
|
1256
1393
|
refresh: (options) => {
|
|
1257
1394
|
requireValue(options?.refreshToken, "refreshToken");
|
|
1258
1395
|
return requestToken(
|
|
1259
|
-
{ grant_type: AuthioGrantTypes.RefreshToken, refresh_token: options.refreshToken, scope: toScopeString(options.scopes) },
|
|
1396
|
+
{ grant_type: AuthioGrantTypes.RefreshToken, refresh_token: options.refreshToken, scope: toScopeString(options.scopes), resource: options.resource },
|
|
1260
1397
|
options.signal
|
|
1261
1398
|
);
|
|
1262
1399
|
},
|
|
@@ -1451,10 +1588,10 @@ async function postForm(connection, discovery, authenticator, endpoint, paramete
|
|
|
1451
1588
|
await authenticator?.apply(form, headers);
|
|
1452
1589
|
return send(connection, url, { method: "POST", headers, body: form.toString() }, signal);
|
|
1453
1590
|
}
|
|
1454
|
-
async function readUserInfoJwt(jwt, document, connection, clientId, decrypt,
|
|
1591
|
+
async function readUserInfoJwt(jwt, document, connection, clientId, decrypt, keys, settings) {
|
|
1455
1592
|
try {
|
|
1456
1593
|
const signed = isAuthioTokenEncrypted(jwt) ? await decrypt(jwt) : jwt;
|
|
1457
|
-
const { payload } = await verifyAuthioJwt(signed,
|
|
1594
|
+
const { payload } = await verifyAuthioJwt(signed, keys, {
|
|
1458
1595
|
issuer: document.issuer.replace(/\/+$/, ""),
|
|
1459
1596
|
audiences: [clientId],
|
|
1460
1597
|
algorithms: settings.algorithms,
|
|
@@ -1486,6 +1623,33 @@ function readBearerChallenge(response) {
|
|
|
1486
1623
|
}
|
|
1487
1624
|
return error ? { error, description } : void 0;
|
|
1488
1625
|
}
|
|
1626
|
+
function resolveCodeChallengeMethod(method) {
|
|
1627
|
+
if (method !== void 0 && method !== "S256" && method !== "plain")
|
|
1628
|
+
throw new AuthioConfigurationError(`The Authio codeChallengeMethod '${String(method)}' is not S256 or plain.`);
|
|
1629
|
+
return method;
|
|
1630
|
+
}
|
|
1631
|
+
function resolveResponseMode(mode) {
|
|
1632
|
+
if (mode !== void 0 && mode !== "query" && mode !== "web_message")
|
|
1633
|
+
throw new AuthioConfigurationError(`The Authio responseMode '${String(mode)}' is not query or web_message.`);
|
|
1634
|
+
return mode;
|
|
1635
|
+
}
|
|
1636
|
+
function resolveSigningAlgorithm(algorithm, setting, clientSecret) {
|
|
1637
|
+
if (algorithm === void 0 || algorithm === "") return void 0;
|
|
1638
|
+
const symmetric = AuthioHmacAlgorithms.includes(algorithm);
|
|
1639
|
+
if (!symmetric && !AuthioAsymmetricAlgorithms.includes(algorithm))
|
|
1640
|
+
throw new AuthioConfigurationError(
|
|
1641
|
+
`The Authio ${setting} '${algorithm}' is not one of ${[...AuthioAsymmetricAlgorithms, ...AuthioHmacAlgorithms].join(", ")}.`
|
|
1642
|
+
);
|
|
1643
|
+
if (symmetric && !clientSecret)
|
|
1644
|
+
throw new AuthioConfigurationError(`The Authio ${setting} ${algorithm} is verified with the client secret (OpenID Connect Core \xA710.1): set clientSecret.`);
|
|
1645
|
+
if (symmetric && typeof clientSecret === "string") requireHmacSecretLength(algorithm, new TextEncoder().encode(clientSecret).length);
|
|
1646
|
+
return algorithm;
|
|
1647
|
+
}
|
|
1648
|
+
async function readSecret(source) {
|
|
1649
|
+
const value = typeof source === "function" ? await source() : source;
|
|
1650
|
+
if (typeof value !== "string" || !value) throw new AuthioConfigurationError("The Authio clientSecret function returned no secret.");
|
|
1651
|
+
return value;
|
|
1652
|
+
}
|
|
1489
1653
|
function requireValue(value, name) {
|
|
1490
1654
|
if (typeof value !== "string" || !value.trim()) throw new TypeError(`${name} is required.`);
|
|
1491
1655
|
}
|
|
@@ -1538,7 +1702,7 @@ function createAuthioAccountCredential(options) {
|
|
|
1538
1702
|
realm: AuthioDefaults.PlatformRealm,
|
|
1539
1703
|
clientId: kind === "client_secret" ? toCredentialClientId(options.credentialId) : AuthioDefaults.PlatformClientId,
|
|
1540
1704
|
clientSecret: kind === "client_secret" ? value : void 0,
|
|
1541
|
-
|
|
1705
|
+
tokenEndpointAuthMethod: kind === "client_secret" ? "client_secret_post" : "none"
|
|
1542
1706
|
});
|
|
1543
1707
|
}
|
|
1544
1708
|
const exchange = async (signal) => {
|
|
@@ -1911,6 +2075,6 @@ function constantTimeEquals(left, right) {
|
|
|
1911
2075
|
return difference === 0;
|
|
1912
2076
|
}
|
|
1913
2077
|
|
|
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 };
|
|
2078
|
+
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
2079
|
//# sourceMappingURL=index.js.map
|
|
1916
2080
|
//# sourceMappingURL=index.js.map
|