@absolutejs/auth 0.36.0-beta.0 → 0.36.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/dist/index.d.ts +1 -0
- package/dist/index.js +126 -22
- package/dist/index.js.map +7 -6
- package/dist/oidc/config.d.ts +5 -2
- package/dist/oidc/mtls.d.ts +11 -0
- package/dist/oidc/types.d.ts +1 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -15154,6 +15154,7 @@ export type { DpopResult } from './oidc/dpop';
|
|
|
15154
15154
|
export { CLIENT_ASSERTION_TYPE, verifyClientAssertion, verifyJwtSignedByClient } from './oidc/clientAuth';
|
|
15155
15155
|
export { parseSignedRequestObject } from './oidc/jar';
|
|
15156
15156
|
export type { JarParseResult } from './oidc/jar';
|
|
15157
|
+
export { computeCertThumbprint, extractRfc9440ClientCert, resolveClientCert, verifyCertificateBoundToken } from './oidc/mtls';
|
|
15157
15158
|
export { createInMemoryAuthorizationCodeStore, createInMemoryBackchannelAuthStore, createInMemoryClientAssertionJtiStore, createInMemoryClientRegistrationTokenStore, createInMemoryDeviceAuthorizationStore, createInMemoryInitialAccessTokenStore, createInMemoryLogoutDeliveryStore, createInMemoryOAuthClientStore, createInMemoryOidcRefreshTokenStore, createInMemoryPushedAuthorizationRequestStore } from './oidc/inMemoryStores';
|
|
15158
15159
|
export { consumePushedRequest, pushAuthorizationRequest, DEFAULT_PAR_TTL_MS, REQUEST_URI_PREFIX } from './oidc/par';
|
|
15159
15160
|
export { fetchUserInfo, readUserInfoBearer, userInfoChallengeHeader } from './oidc/userinfo';
|
package/dist/index.js
CHANGED
|
@@ -4492,6 +4492,7 @@ var RESERVED_ACCESS_CLAIMS = new Set([
|
|
|
4492
4492
|
var buildAccessClaims = ({
|
|
4493
4493
|
act,
|
|
4494
4494
|
audience,
|
|
4495
|
+
clientCertThumbprint,
|
|
4495
4496
|
clientId,
|
|
4496
4497
|
dpopJkt,
|
|
4497
4498
|
extraClaims,
|
|
@@ -4516,8 +4517,15 @@ var buildAccessClaims = ({
|
|
|
4516
4517
|
};
|
|
4517
4518
|
if (act !== undefined)
|
|
4518
4519
|
claims.act = act;
|
|
4519
|
-
if (dpopJkt !== undefined)
|
|
4520
|
-
|
|
4520
|
+
if (dpopJkt !== undefined || clientCertThumbprint !== undefined) {
|
|
4521
|
+
const cnf = {};
|
|
4522
|
+
if (dpopJkt !== undefined)
|
|
4523
|
+
cnf.jkt = dpopJkt;
|
|
4524
|
+
if (clientCertThumbprint !== undefined) {
|
|
4525
|
+
cnf["x5t#S256"] = clientCertThumbprint;
|
|
4526
|
+
}
|
|
4527
|
+
claims.cnf = cnf;
|
|
4528
|
+
}
|
|
4521
4529
|
return claims;
|
|
4522
4530
|
};
|
|
4523
4531
|
var exchangeToken = async ({
|
|
@@ -4567,6 +4575,7 @@ var exchangeToken = async ({
|
|
|
4567
4575
|
var issueTokenSet = async ({
|
|
4568
4576
|
acr,
|
|
4569
4577
|
claims,
|
|
4578
|
+
clientCertThumbprint,
|
|
4570
4579
|
clientId,
|
|
4571
4580
|
config,
|
|
4572
4581
|
dpopJkt,
|
|
@@ -4584,6 +4593,7 @@ var issueTokenSet = async ({
|
|
|
4584
4593
|
sub
|
|
4585
4594
|
});
|
|
4586
4595
|
const accessPayload = buildAccessClaims({
|
|
4596
|
+
clientCertThumbprint,
|
|
4587
4597
|
clientId,
|
|
4588
4598
|
dpopJkt,
|
|
4589
4599
|
extraClaims: { ...accessExtra, ...acr === undefined ? {} : { acr } },
|
|
@@ -4873,6 +4883,7 @@ var denyBackchannelAuth = async ({
|
|
|
4873
4883
|
}) => decideBackchannel(config, authReqId, { status: "denied" });
|
|
4874
4884
|
var exchangeBackchannelAuth = async ({
|
|
4875
4885
|
authReqId,
|
|
4886
|
+
clientCertThumbprint,
|
|
4876
4887
|
clientId,
|
|
4877
4888
|
config,
|
|
4878
4889
|
dpopJkt,
|
|
@@ -4901,6 +4912,7 @@ var exchangeBackchannelAuth = async ({
|
|
|
4901
4912
|
}
|
|
4902
4913
|
await config.backchannelAuthStore.deleteByAuthReqId(authReqId);
|
|
4903
4914
|
const tokenSet = await issueTokenSet({
|
|
4915
|
+
clientCertThumbprint,
|
|
4904
4916
|
clientId,
|
|
4905
4917
|
config,
|
|
4906
4918
|
dpopJkt,
|
|
@@ -5020,6 +5032,55 @@ var verifyJwtSignedByClient = ({
|
|
|
5020
5032
|
client
|
|
5021
5033
|
}) => verifyJwtSignedByClientImpl(client, jwt);
|
|
5022
5034
|
|
|
5035
|
+
// src/oidc/mtls.ts
|
|
5036
|
+
var RFC9440_HEADER = "client-cert";
|
|
5037
|
+
var SF_BINARY_PREFIX = ":";
|
|
5038
|
+
var SF_BINARY_SUFFIX = ":";
|
|
5039
|
+
var base64Decode2 = (value) => Uint8Array.from(atob(value), (char) => char.charCodeAt(0));
|
|
5040
|
+
var base64UrlEncode2 = (bytes) => {
|
|
5041
|
+
const binary = Array.from(bytes, (byte) => String.fromCharCode(byte)).join("");
|
|
5042
|
+
return btoa(binary).replace(/\+/gu, "-").replace(/\//gu, "_").replace(/=+$/u, "");
|
|
5043
|
+
};
|
|
5044
|
+
var computeCertThumbprint = async (derBytes) => {
|
|
5045
|
+
const digest = await crypto.subtle.digest("SHA-256", derBytes);
|
|
5046
|
+
return base64UrlEncode2(new Uint8Array(digest));
|
|
5047
|
+
};
|
|
5048
|
+
var extractRfc9440ClientCert = (headers) => {
|
|
5049
|
+
const raw = headers.get(RFC9440_HEADER);
|
|
5050
|
+
if (raw === null)
|
|
5051
|
+
return;
|
|
5052
|
+
const trimmed = raw.trim();
|
|
5053
|
+
if (!trimmed.startsWith(SF_BINARY_PREFIX) || !trimmed.endsWith(SF_BINARY_SUFFIX) || trimmed.length <= 2) {
|
|
5054
|
+
return;
|
|
5055
|
+
}
|
|
5056
|
+
try {
|
|
5057
|
+
return base64Decode2(trimmed.slice(1, -1));
|
|
5058
|
+
} catch {
|
|
5059
|
+
return;
|
|
5060
|
+
}
|
|
5061
|
+
};
|
|
5062
|
+
var resolveClientCert = async ({
|
|
5063
|
+
extract,
|
|
5064
|
+
headers
|
|
5065
|
+
}) => {
|
|
5066
|
+
if (extract !== undefined)
|
|
5067
|
+
return extract(headers);
|
|
5068
|
+
return extractRfc9440ClientCert(headers);
|
|
5069
|
+
};
|
|
5070
|
+
var verifyCertificateBoundToken = async ({
|
|
5071
|
+
cnfThumbprint,
|
|
5072
|
+
extract,
|
|
5073
|
+
headers
|
|
5074
|
+
}) => {
|
|
5075
|
+
if (cnfThumbprint === undefined)
|
|
5076
|
+
return false;
|
|
5077
|
+
const cert = await resolveClientCert({ extract, headers });
|
|
5078
|
+
if (cert === undefined)
|
|
5079
|
+
return false;
|
|
5080
|
+
const presented = await computeCertThumbprint(cert);
|
|
5081
|
+
return presented === cnfThumbprint;
|
|
5082
|
+
};
|
|
5083
|
+
|
|
5023
5084
|
// src/oidc/dpop.ts
|
|
5024
5085
|
var DEFAULT_MAX_AGE_MS = 60000;
|
|
5025
5086
|
var SECONDS_TO_MS = 1000;
|
|
@@ -5656,27 +5717,57 @@ var oidcProviderRoutes = (config) => {
|
|
|
5656
5717
|
const matches = await constantTimeEqual(await hashToken(clientSecret), client.hashedSecret);
|
|
5657
5718
|
return matches ? client : undefined;
|
|
5658
5719
|
};
|
|
5720
|
+
const tryMtlsAuth = async ({
|
|
5721
|
+
candidate,
|
|
5722
|
+
extract,
|
|
5723
|
+
requestHeaders
|
|
5724
|
+
}) => {
|
|
5725
|
+
const registered = candidate?.tlsCertificateBoundThumbprints ?? [];
|
|
5726
|
+
if (candidate === undefined || registered.length === 0)
|
|
5727
|
+
return;
|
|
5728
|
+
const cert = await resolveClientCert({
|
|
5729
|
+
extract,
|
|
5730
|
+
headers: requestHeaders
|
|
5731
|
+
});
|
|
5732
|
+
if (cert === undefined)
|
|
5733
|
+
return;
|
|
5734
|
+
const presented = await computeCertThumbprint(cert);
|
|
5735
|
+
if (!registered.includes(presented))
|
|
5736
|
+
return;
|
|
5737
|
+
return { client: candidate, clientCertThumbprint: presented };
|
|
5738
|
+
};
|
|
5659
5739
|
const authenticateTokenClient = async ({
|
|
5660
5740
|
basicClientId,
|
|
5661
5741
|
basicClientSecret,
|
|
5662
5742
|
bodyClientAssertion,
|
|
5663
5743
|
bodyClientAssertionType,
|
|
5664
5744
|
bodyClientId,
|
|
5665
|
-
bodyClientSecret
|
|
5745
|
+
bodyClientSecret,
|
|
5746
|
+
requestHeaders
|
|
5666
5747
|
}) => {
|
|
5667
5748
|
if (bodyClientAssertion !== undefined && bodyClientAssertionType === CLIENT_ASSERTION_TYPE) {
|
|
5668
|
-
|
|
5749
|
+
const client2 = await verifyClientAssertion({
|
|
5669
5750
|
assertion: bodyClientAssertion,
|
|
5670
5751
|
expectedAudience: tokenUrl,
|
|
5671
5752
|
jtiStore: config.clientAssertionJtiStore,
|
|
5672
5753
|
resolveClient: clientStore.findClient
|
|
5673
5754
|
});
|
|
5755
|
+
return client2 === undefined ? undefined : { client: client2, clientCertThumbprint: undefined };
|
|
5674
5756
|
}
|
|
5675
5757
|
const clientId = bodyClientId ?? basicClientId;
|
|
5676
|
-
const clientSecret = bodyClientSecret ?? basicClientSecret;
|
|
5677
5758
|
if (clientId === undefined)
|
|
5678
5759
|
return;
|
|
5679
|
-
|
|
5760
|
+
const candidate = await clientStore.findClient(clientId);
|
|
5761
|
+
const mtlsResult = await tryMtlsAuth({
|
|
5762
|
+
candidate,
|
|
5763
|
+
extract: config.extractTlsClientCert,
|
|
5764
|
+
requestHeaders
|
|
5765
|
+
});
|
|
5766
|
+
if (mtlsResult !== undefined)
|
|
5767
|
+
return mtlsResult;
|
|
5768
|
+
const clientSecret = bodyClientSecret ?? basicClientSecret;
|
|
5769
|
+
const client = await authenticateClient(clientId, clientSecret);
|
|
5770
|
+
return client === undefined ? undefined : { client, clientCertThumbprint: undefined };
|
|
5680
5771
|
};
|
|
5681
5772
|
const dpopNonceChallenge = async (proof) => {
|
|
5682
5773
|
if (proof === undefined || config.dpopNonce === undefined) {
|
|
@@ -5701,7 +5792,7 @@ var oidcProviderRoutes = (config) => {
|
|
|
5701
5792
|
status: HTTP_UNAUTHORIZED2
|
|
5702
5793
|
});
|
|
5703
5794
|
};
|
|
5704
|
-
const grantAuthorizationCode = async (client, body, dpop) => {
|
|
5795
|
+
const grantAuthorizationCode = async (client, body, dpop, clientCertThumbprint) => {
|
|
5705
5796
|
const {
|
|
5706
5797
|
code,
|
|
5707
5798
|
code_verifier: codeVerifier,
|
|
@@ -5725,6 +5816,7 @@ var oidcProviderRoutes = (config) => {
|
|
|
5725
5816
|
return tokenResponse(await issueTokenSet({
|
|
5726
5817
|
acr: record.acr,
|
|
5727
5818
|
claims: record.claims,
|
|
5819
|
+
clientCertThumbprint,
|
|
5728
5820
|
clientId: client.clientId,
|
|
5729
5821
|
config,
|
|
5730
5822
|
dpopJkt: dpopResult?.jkt,
|
|
@@ -5733,7 +5825,7 @@ var oidcProviderRoutes = (config) => {
|
|
|
5733
5825
|
sub: record.userId
|
|
5734
5826
|
}));
|
|
5735
5827
|
};
|
|
5736
|
-
const grantRefreshToken = async (client, body, dpop) => {
|
|
5828
|
+
const grantRefreshToken = async (client, body, dpop, clientCertThumbprint) => {
|
|
5737
5829
|
const presented = body.refresh_token;
|
|
5738
5830
|
if (presented === undefined) {
|
|
5739
5831
|
return oauthError2(HTTP_BAD_REQUEST2, "invalid_request");
|
|
@@ -5755,6 +5847,7 @@ var oidcProviderRoutes = (config) => {
|
|
|
5755
5847
|
return tokenResponse(await issueTokenSet({
|
|
5756
5848
|
acr: record.acr,
|
|
5757
5849
|
claims: record.claims,
|
|
5850
|
+
clientCertThumbprint,
|
|
5758
5851
|
clientId: client.clientId,
|
|
5759
5852
|
config,
|
|
5760
5853
|
dpopJkt: record.dpopJkt,
|
|
@@ -5792,7 +5885,7 @@ var oidcProviderRoutes = (config) => {
|
|
|
5792
5885
|
token_type: dpopResult === undefined ? "Bearer" : "DPoP"
|
|
5793
5886
|
}, HTTP_OK2);
|
|
5794
5887
|
};
|
|
5795
|
-
const grantBackchannel = async (client, body, dpop) => {
|
|
5888
|
+
const grantBackchannel = async (client, body, dpop, clientCertThumbprint) => {
|
|
5796
5889
|
if (config.backchannelAuthStore === undefined) {
|
|
5797
5890
|
return oauthError2(HTTP_BAD_REQUEST2, "unsupported_grant_type");
|
|
5798
5891
|
}
|
|
@@ -5809,6 +5902,7 @@ var oidcProviderRoutes = (config) => {
|
|
|
5809
5902
|
}
|
|
5810
5903
|
const result = await exchangeBackchannelAuth({
|
|
5811
5904
|
authReqId: body.auth_req_id,
|
|
5905
|
+
clientCertThumbprint,
|
|
5812
5906
|
clientId: client.clientId,
|
|
5813
5907
|
config,
|
|
5814
5908
|
dpopJkt: dpopResult?.jkt
|
|
@@ -5885,12 +5979,14 @@ var oidcProviderRoutes = (config) => {
|
|
|
5885
5979
|
response_types_supported: ["code"],
|
|
5886
5980
|
revocation_endpoint: `${issuer}${revokeRoute}`,
|
|
5887
5981
|
subject_types_supported: ["public"],
|
|
5982
|
+
tls_client_certificate_bound_access_tokens: true,
|
|
5888
5983
|
token_endpoint: tokenUrl,
|
|
5889
5984
|
token_endpoint_auth_methods_supported: [
|
|
5890
5985
|
"client_secret_basic",
|
|
5891
5986
|
"client_secret_post",
|
|
5892
5987
|
"none",
|
|
5893
|
-
"private_key_jwt"
|
|
5988
|
+
"private_key_jwt",
|
|
5989
|
+
"self_signed_tls_client_auth"
|
|
5894
5990
|
],
|
|
5895
5991
|
token_endpoint_auth_signing_alg_values_supported: ["ES256"],
|
|
5896
5992
|
userinfo_endpoint: `${issuer}${userinfoRoute}`
|
|
@@ -6097,27 +6193,29 @@ var oidcProviderRoutes = (config) => {
|
|
|
6097
6193
|
scope: t12.Optional(t12.String()),
|
|
6098
6194
|
state: t12.Optional(t12.String())
|
|
6099
6195
|
})
|
|
6100
|
-
}).post(tokenRoute, async ({ body, headers }) => {
|
|
6196
|
+
}).post(tokenRoute, async ({ body, headers, request }) => {
|
|
6101
6197
|
const basic = readBasicAuth2(headers.authorization);
|
|
6102
|
-
const
|
|
6198
|
+
const auth = await authenticateTokenClient({
|
|
6103
6199
|
basicClientId: basic.clientId,
|
|
6104
6200
|
basicClientSecret: basic.clientSecret,
|
|
6105
6201
|
bodyClientAssertion: body.client_assertion,
|
|
6106
6202
|
bodyClientAssertionType: body.client_assertion_type,
|
|
6107
6203
|
bodyClientId: body.client_id,
|
|
6108
|
-
bodyClientSecret: body.client_secret
|
|
6204
|
+
bodyClientSecret: body.client_secret,
|
|
6205
|
+
requestHeaders: request.headers
|
|
6109
6206
|
});
|
|
6110
|
-
if (
|
|
6207
|
+
if (auth === undefined) {
|
|
6111
6208
|
return oauthError2(HTTP_UNAUTHORIZED2, "invalid_client");
|
|
6112
6209
|
}
|
|
6210
|
+
const { client, clientCertThumbprint } = auth;
|
|
6113
6211
|
const nonceChallenge = await dpopNonceChallenge(headers.dpop);
|
|
6114
6212
|
if (nonceChallenge !== undefined)
|
|
6115
6213
|
return nonceChallenge;
|
|
6116
6214
|
if (body.grant_type === "authorization_code") {
|
|
6117
|
-
return grantAuthorizationCode(client, body, headers.dpop);
|
|
6215
|
+
return grantAuthorizationCode(client, body, headers.dpop, clientCertThumbprint);
|
|
6118
6216
|
}
|
|
6119
6217
|
if (body.grant_type === "refresh_token") {
|
|
6120
|
-
return grantRefreshToken(client, body, headers.dpop);
|
|
6218
|
+
return grantRefreshToken(client, body, headers.dpop, clientCertThumbprint);
|
|
6121
6219
|
}
|
|
6122
6220
|
if (body.grant_type === "urn:ietf:params:oauth:grant-type:token-exchange") {
|
|
6123
6221
|
return grantTokenExchange(client, body, headers.dpop);
|
|
@@ -6126,7 +6224,7 @@ var oidcProviderRoutes = (config) => {
|
|
|
6126
6224
|
return grantDeviceCode(client, body, headers.dpop);
|
|
6127
6225
|
}
|
|
6128
6226
|
if (body.grant_type === CIBA_GRANT_TYPE) {
|
|
6129
|
-
return grantBackchannel(client, body, headers.dpop);
|
|
6227
|
+
return grantBackchannel(client, body, headers.dpop, clientCertThumbprint);
|
|
6130
6228
|
}
|
|
6131
6229
|
return oauthError2(HTTP_BAD_REQUEST2, "unsupported_grant_type");
|
|
6132
6230
|
}, {
|
|
@@ -6148,22 +6246,24 @@ var oidcProviderRoutes = (config) => {
|
|
|
6148
6246
|
subject_token: t12.Optional(t12.String()),
|
|
6149
6247
|
subject_token_type: t12.Optional(t12.String())
|
|
6150
6248
|
})
|
|
6151
|
-
}).post(parRoute, async ({ body, headers }) => {
|
|
6249
|
+
}).post(parRoute, async ({ body, headers, request }) => {
|
|
6152
6250
|
if (config.pushedAuthorizationRequestStore === undefined) {
|
|
6153
6251
|
return oauthError2(HTTP_NOT_IMPLEMENTED, "unsupported_response_type");
|
|
6154
6252
|
}
|
|
6155
6253
|
const basic = readBasicAuth2(headers.authorization);
|
|
6156
|
-
const
|
|
6254
|
+
const auth = await authenticateTokenClient({
|
|
6157
6255
|
basicClientId: basic.clientId,
|
|
6158
6256
|
basicClientSecret: basic.clientSecret,
|
|
6159
6257
|
bodyClientAssertion: body.client_assertion,
|
|
6160
6258
|
bodyClientAssertionType: body.client_assertion_type,
|
|
6161
6259
|
bodyClientId: body.client_id,
|
|
6162
|
-
bodyClientSecret: body.client_secret
|
|
6260
|
+
bodyClientSecret: body.client_secret,
|
|
6261
|
+
requestHeaders: request.headers
|
|
6163
6262
|
});
|
|
6164
|
-
if (
|
|
6263
|
+
if (auth === undefined) {
|
|
6165
6264
|
return oauthError2(HTTP_UNAUTHORIZED2, "invalid_client");
|
|
6166
6265
|
}
|
|
6266
|
+
const { client } = auth;
|
|
6167
6267
|
const isAuthField = (key) => key === "client_assertion" || key === "client_assertion_type" || key === "client_secret";
|
|
6168
6268
|
const params = Object.fromEntries(Object.entries(body).filter((entry) => typeof entry[1] === "string" && !isAuthField(entry[0])));
|
|
6169
6269
|
const result = await pushAuthorizationRequest({
|
|
@@ -24240,6 +24340,7 @@ export {
|
|
|
24240
24340
|
verifyDpopNonce,
|
|
24241
24341
|
verifyCognitoSha256,
|
|
24242
24342
|
verifyClientAssertion,
|
|
24343
|
+
verifyCertificateBoundToken,
|
|
24243
24344
|
verifyAuth0Pbkdf2,
|
|
24244
24345
|
verifyAuditChain,
|
|
24245
24346
|
verifyApiKey,
|
|
@@ -24288,6 +24389,7 @@ export {
|
|
|
24288
24389
|
resolveOAuthAuthorization,
|
|
24289
24390
|
resolveCookieSecure,
|
|
24290
24391
|
resolveClientProviderEntry,
|
|
24392
|
+
resolveClientCert,
|
|
24291
24393
|
resolveAuthHtmxRenderers,
|
|
24292
24394
|
resolveApiPrincipal,
|
|
24293
24395
|
removeFromSessionRing,
|
|
@@ -24384,6 +24486,7 @@ export {
|
|
|
24384
24486
|
fingerprintDevice,
|
|
24385
24487
|
fetchUserInfo,
|
|
24386
24488
|
fanOutBackchannelLogout,
|
|
24489
|
+
extractRfc9440ClientCert,
|
|
24387
24490
|
extractPropFromIdentity,
|
|
24388
24491
|
extractDpopNonceClaim,
|
|
24389
24492
|
exportAuditCsv,
|
|
@@ -24543,6 +24646,7 @@ export {
|
|
|
24543
24646
|
consumePushedRequest,
|
|
24544
24647
|
consumeBackupCode,
|
|
24545
24648
|
constantTimeEqual,
|
|
24649
|
+
computeCertThumbprint,
|
|
24546
24650
|
complianceRoutes,
|
|
24547
24651
|
check,
|
|
24548
24652
|
buildClientProviders,
|
|
@@ -24599,5 +24703,5 @@ export {
|
|
|
24599
24703
|
AuthIdentityConflictError
|
|
24600
24704
|
};
|
|
24601
24705
|
|
|
24602
|
-
//# debugId=
|
|
24706
|
+
//# debugId=1069DAAF739CAF9F64756E2164756E21
|
|
24603
24707
|
//# sourceMappingURL=index.js.map
|