@authup/server-core 1.0.0-beta.61 → 1.0.0-beta.63
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/adapters/database/domains/client-scope/entity.d.ts.map +1 -1
- package/dist/adapters/database/domains/client-scope/entity.mjs +16 -8
- package/dist/adapters/database/domains/client-scope/entity.mjs.map +1 -1
- package/dist/adapters/database/domains/identity-provider-account/entity.d.ts.map +1 -1
- package/dist/adapters/database/domains/identity-provider-account/entity.mjs +1 -1
- package/dist/adapters/database/domains/identity-provider-account/entity.mjs.map +1 -1
- package/dist/adapters/database/domains/identity-provider-attribute/subscriber.d.ts +8 -1
- package/dist/adapters/database/domains/identity-provider-attribute/subscriber.d.ts.map +1 -1
- package/dist/adapters/database/domains/identity-provider-attribute/subscriber.mjs +14 -0
- package/dist/adapters/database/domains/identity-provider-attribute/subscriber.mjs.map +1 -1
- package/dist/adapters/database/migrations/mysql/1786631686318-SortIndexesAndAccountUniqueness.d.ts +50 -0
- package/dist/adapters/database/migrations/mysql/1786631686318-SortIndexesAndAccountUniqueness.d.ts.map +1 -0
- package/dist/adapters/database/migrations/mysql/1786631686318-SortIndexesAndAccountUniqueness.mjs +86 -0
- package/dist/adapters/database/migrations/mysql/1786631686318-SortIndexesAndAccountUniqueness.mjs.map +1 -0
- package/dist/adapters/database/migrations/postgres/1786631686318-SortIndexesAndAccountUniqueness.d.ts +42 -0
- package/dist/adapters/database/migrations/postgres/1786631686318-SortIndexesAndAccountUniqueness.d.ts.map +1 -0
- package/dist/adapters/database/migrations/postgres/1786631686318-SortIndexesAndAccountUniqueness.mjs +80 -0
- package/dist/adapters/database/migrations/postgres/1786631686318-SortIndexesAndAccountUniqueness.mjs.map +1 -0
- package/dist/adapters/http/adapters/oauth2/grant-types/refresh_token.d.ts.map +1 -1
- package/dist/adapters/http/adapters/oauth2/grant-types/refresh_token.mjs +8 -2
- package/dist/adapters/http/adapters/oauth2/grant-types/refresh_token.mjs.map +1 -1
- package/dist/adapters/http/adapters/oauth2/grant-types/utils/credentials.mjs +1 -1
- package/dist/adapters/http/adapters/oauth2/grant-types/utils/guess.mjs +1 -1
- package/dist/adapters/http/controllers/entities/client/module.mjs +2 -2
- package/dist/adapters/http/controllers/entities/identity-provider/module.d.ts +24 -4
- package/dist/adapters/http/controllers/entities/identity-provider/module.d.ts.map +1 -1
- package/dist/adapters/http/controllers/entities/identity-provider/module.mjs +117 -106
- package/dist/adapters/http/controllers/entities/identity-provider/module.mjs.map +1 -1
- package/dist/adapters/http/controllers/entities/identity-provider/types.d.ts +6 -4
- package/dist/adapters/http/controllers/entities/identity-provider/types.d.ts.map +1 -1
- package/dist/adapters/http/controllers/entities/realm/module.mjs +2 -2
- package/dist/adapters/http/controllers/entities/session/module.mjs +2 -2
- package/dist/adapters/http/controllers/entities/user/module.mjs +2 -2
- package/dist/adapters/http/controllers/entities/user-authenticator/module.mjs +2 -2
- package/dist/adapters/http/controllers/workflows/authenticator-challenge/module.d.ts +2 -0
- package/dist/adapters/http/controllers/workflows/authenticator-challenge/module.d.ts.map +1 -1
- package/dist/adapters/http/controllers/workflows/authenticator-challenge/module.mjs +21 -3
- package/dist/adapters/http/controllers/workflows/authenticator-challenge/module.mjs.map +1 -1
- package/dist/adapters/http/controllers/workflows/authorize/module.d.ts.map +1 -1
- package/dist/adapters/http/controllers/workflows/authorize/module.mjs +14 -5
- package/dist/adapters/http/controllers/workflows/authorize/module.mjs.map +1 -1
- package/dist/adapters/http/controllers/workflows/token/module.d.ts.map +1 -1
- package/dist/adapters/http/controllers/workflows/token/module.mjs +22 -9
- package/dist/adapters/http/controllers/workflows/token/module.mjs.map +1 -1
- package/dist/adapters/http/controllers/workflows/token/utils/extract.mjs +2 -1
- package/dist/adapters/http/controllers/workflows/token/utils/extract.mjs.map +1 -1
- package/dist/adapters/http/middleware/built-in/assets.mjs +1 -1
- package/dist/adapters/http/middleware/built-in/cors.d.ts.map +1 -1
- package/dist/adapters/http/middleware/built-in/cors.mjs.map +1 -1
- package/dist/adapters/http/middleware/built-in/error.d.ts.map +1 -1
- package/dist/adapters/http/middleware/built-in/error.mjs +10 -1
- package/dist/adapters/http/middleware/built-in/error.mjs.map +1 -1
- package/dist/adapters/http/request/helpers/param-id.d.ts.map +1 -1
- package/dist/adapters/http/request/helpers/param-id.mjs +2 -1
- package/dist/adapters/http/request/helpers/param-id.mjs.map +1 -1
- package/dist/adapters/http/ui/account-console/module.mjs +1 -1
- package/dist/adapters/http/ui/auth-console/http-client.mjs +1 -1
- package/dist/adapters/http/ui/auth-console/module.mjs +1 -1
- package/dist/adapters/http/ui/console-packages/module.d.ts +1 -1
- package/dist/adapters/http/ui/console-packages/module.mjs +2 -2
- package/dist/adapters/http/ui/console-packages/module.mjs.map +1 -1
- package/dist/adapters/http/ui/shared/html.d.ts.map +1 -1
- package/dist/adapters/http/ui/shared/html.mjs +1 -0
- package/dist/adapters/http/ui/shared/html.mjs.map +1 -1
- package/dist/app/modules/config/normalize.d.ts.map +1 -1
- package/dist/app/modules/config/normalize.mjs +2 -2
- package/dist/app/modules/config/normalize.mjs.map +1 -1
- package/dist/app/modules/config/read/env.mjs +1 -1
- package/dist/app/modules/config/types.d.ts +8 -0
- package/dist/app/modules/config/types.d.ts.map +1 -1
- package/dist/app/modules/database/repositories/event/repository.d.ts +2 -2
- package/dist/app/modules/database/repositories/event/repository.d.ts.map +1 -1
- package/dist/app/modules/database/repositories/event/repository.mjs +23 -6
- package/dist/app/modules/database/repositories/event/repository.mjs.map +1 -1
- package/dist/app/modules/database/repositories/identity-provider/repository.d.ts +3 -1
- package/dist/app/modules/database/repositories/identity-provider/repository.d.ts.map +1 -1
- package/dist/app/modules/database/repositories/identity-provider/repository.mjs +2 -2
- package/dist/app/modules/database/repositories/identity-provider/repository.mjs.map +1 -1
- package/dist/app/modules/database/repositories/identity-provider-account/repository.d.ts.map +1 -1
- package/dist/app/modules/database/repositories/identity-provider-account/repository.mjs +9 -1
- package/dist/app/modules/database/repositories/identity-provider-account/repository.mjs.map +1 -1
- package/dist/app/modules/http/modules/controller.d.ts.map +1 -1
- package/dist/app/modules/http/modules/controller.mjs +20 -6
- package/dist/app/modules/http/modules/controller.mjs.map +1 -1
- package/dist/app/modules/identity/module.d.ts.map +1 -1
- package/dist/app/modules/identity/module.mjs +6 -3
- package/dist/app/modules/identity/module.mjs.map +1 -1
- package/dist/app/modules/identity/repositories/user.d.ts +1 -0
- package/dist/app/modules/identity/repositories/user.d.ts.map +1 -1
- package/dist/app/modules/identity/repositories/user.mjs +3 -0
- package/dist/app/modules/identity/repositories/user.mjs.map +1 -1
- package/dist/app/modules/oauth2/constants.d.ts +2 -1
- package/dist/app/modules/oauth2/constants.d.ts.map +1 -1
- package/dist/app/modules/oauth2/constants.mjs +1 -0
- package/dist/app/modules/oauth2/constants.mjs.map +1 -1
- package/dist/app/modules/oauth2/module.d.ts.map +1 -1
- package/dist/app/modules/oauth2/module.mjs +5 -0
- package/dist/app/modules/oauth2/module.mjs.map +1 -1
- package/dist/app/modules/oauth2/repositories/authorize/state/repository.d.ts +1 -2
- package/dist/app/modules/oauth2/repositories/authorize/state/repository.d.ts.map +1 -1
- package/dist/app/modules/oauth2/repositories/authorize/state/repository.mjs +7 -16
- package/dist/app/modules/oauth2/repositories/authorize/state/repository.mjs.map +1 -1
- package/dist/app/modules/oauth2/repositories/constants.d.ts +1 -0
- package/dist/app/modules/oauth2/repositories/constants.d.ts.map +1 -1
- package/dist/app/modules/oauth2/repositories/constants.mjs +1 -0
- package/dist/app/modules/oauth2/repositories/constants.mjs.map +1 -1
- package/dist/app/modules/oauth2/repositories/federated-login/index.d.ts +2 -0
- package/dist/app/modules/oauth2/repositories/federated-login/index.d.ts.map +1 -0
- package/dist/app/modules/oauth2/repositories/federated-login/index.mjs +5 -0
- package/dist/app/modules/oauth2/repositories/federated-login/store.d.ts +9 -0
- package/dist/app/modules/oauth2/repositories/federated-login/store.d.ts.map +1 -0
- package/dist/app/modules/oauth2/repositories/federated-login/store.mjs +33 -0
- package/dist/app/modules/oauth2/repositories/federated-login/store.mjs.map +1 -0
- package/dist/app/modules/oauth2/repositories/index.d.ts +1 -0
- package/dist/app/modules/oauth2/repositories/index.d.ts.map +1 -1
- package/dist/app/modules/oauth2/repositories/index.mjs +3 -1
- package/dist/app/modules/runtime/module.mjs +2 -2
- package/dist/cli/commands/migration.mjs +1 -1
- package/dist/core/entities/client-scope/schema.d.ts.map +1 -1
- package/dist/core/entities/client-scope/schema.mjs +16 -1
- package/dist/core/entities/client-scope/schema.mjs.map +1 -1
- package/dist/core/entities/event/constants.d.ts +9 -0
- package/dist/core/entities/event/constants.d.ts.map +1 -1
- package/dist/core/entities/event/constants.mjs +9 -1
- package/dist/core/entities/event/constants.mjs.map +1 -1
- package/dist/core/entities/event/entity-event-handler.d.ts.map +1 -1
- package/dist/core/entities/event/entity-event-handler.mjs +14 -1
- package/dist/core/entities/event/entity-event-handler.mjs.map +1 -1
- package/dist/core/entities/event/index.mjs +2 -2
- package/dist/core/entities/event/types.d.ts +11 -2
- package/dist/core/entities/event/types.d.ts.map +1 -1
- package/dist/core/entities/identity-provider/types.d.ts +9 -1
- package/dist/core/entities/identity-provider/types.d.ts.map +1 -1
- package/dist/core/entities/identity-provider-account/types.d.ts +6 -0
- package/dist/core/entities/identity-provider-account/types.d.ts.map +1 -1
- package/dist/core/entities/identity-provider-account/types.mjs.map +1 -1
- package/dist/core/entities/index.mjs +2 -2
- package/dist/core/identity/entities/user/types.d.ts +1 -0
- package/dist/core/identity/entities/user/types.d.ts.map +1 -1
- package/dist/core/identity/index.mjs +12 -11
- package/dist/core/identity/provider/account/module.d.ts +3 -0
- package/dist/core/identity/provider/account/module.d.ts.map +1 -1
- package/dist/core/identity/provider/account/module.mjs +54 -27
- package/dist/core/identity/provider/account/module.mjs.map +1 -1
- package/dist/core/identity/provider/account/types.d.ts +7 -0
- package/dist/core/identity/provider/account/types.d.ts.map +1 -1
- package/dist/core/identity/provider/authentication/index.mjs +3 -2
- package/dist/core/identity/provider/authentication/protocols/index.mjs +2 -1
- package/dist/core/identity/provider/authentication/protocols/oauth2/assurance.d.ts +46 -0
- package/dist/core/identity/provider/authentication/protocols/oauth2/assurance.d.ts.map +1 -0
- package/dist/core/identity/provider/authentication/protocols/oauth2/assurance.mjs +89 -0
- package/dist/core/identity/provider/authentication/protocols/oauth2/assurance.mjs.map +1 -0
- package/dist/core/identity/provider/authentication/protocols/oauth2/index.d.ts +1 -0
- package/dist/core/identity/provider/authentication/protocols/oauth2/index.d.ts.map +1 -1
- package/dist/core/identity/provider/authentication/protocols/oauth2/index.mjs +2 -1
- package/dist/core/identity/provider/authentication/protocols/oauth2/module.d.ts.map +1 -1
- package/dist/core/identity/provider/authentication/protocols/oauth2/module.mjs +9 -2
- package/dist/core/identity/provider/authentication/protocols/oauth2/module.mjs.map +1 -1
- package/dist/core/identity/provider/index.mjs +12 -11
- package/dist/core/index.mjs +16 -12
- package/dist/core/oauth2/authorization/code-request/verifier/module.d.ts.map +1 -1
- package/dist/core/oauth2/authorization/code-request/verifier/module.mjs +7 -1
- package/dist/core/oauth2/authorization/code-request/verifier/module.mjs.map +1 -1
- package/dist/core/oauth2/authorization/helpers.d.ts.map +1 -1
- package/dist/core/oauth2/authorization/helpers.mjs +6 -1
- package/dist/core/oauth2/authorization/helpers.mjs.map +1 -1
- package/dist/core/oauth2/authorization/module.d.ts.map +1 -1
- package/dist/core/oauth2/authorization/module.mjs +5 -3
- package/dist/core/oauth2/authorization/module.mjs.map +1 -1
- package/dist/core/oauth2/authorization/state/module.mjs +1 -2
- package/dist/core/oauth2/authorization/state/module.mjs.map +1 -1
- package/dist/core/oauth2/authorization/state/types.d.ts +13 -2
- package/dist/core/oauth2/authorization/state/types.d.ts.map +1 -1
- package/dist/core/oauth2/end-session/service.d.ts.map +1 -1
- package/dist/core/oauth2/end-session/service.mjs +1 -0
- package/dist/core/oauth2/end-session/service.mjs.map +1 -1
- package/dist/core/oauth2/federated-login/constants.d.ts +22 -0
- package/dist/core/oauth2/federated-login/constants.d.ts.map +1 -0
- package/dist/core/oauth2/federated-login/constants.mjs +27 -0
- package/dist/core/oauth2/federated-login/constants.mjs.map +1 -0
- package/dist/core/oauth2/federated-login/index.d.ts +4 -0
- package/dist/core/oauth2/federated-login/index.d.ts.map +1 -0
- package/dist/core/oauth2/federated-login/index.mjs +7 -0
- package/dist/core/oauth2/federated-login/module.d.ts +29 -0
- package/dist/core/oauth2/federated-login/module.d.ts.map +1 -0
- package/dist/core/oauth2/federated-login/module.mjs +193 -0
- package/dist/core/oauth2/federated-login/module.mjs.map +1 -0
- package/dist/core/oauth2/federated-login/types.d.ts +196 -0
- package/dist/core/oauth2/federated-login/types.d.ts.map +1 -0
- package/dist/core/oauth2/federated-login/types.mjs +36 -0
- package/dist/core/oauth2/federated-login/types.mjs.map +1 -0
- package/dist/core/oauth2/grant-types/authorize.mjs +1 -1
- package/dist/core/oauth2/grant-types/client-credentials.mjs +1 -1
- package/dist/core/oauth2/grant-types/identity.mjs +1 -1
- package/dist/core/oauth2/grant-types/password.mjs +1 -1
- package/dist/core/oauth2/grant-types/refresh-token.mjs +1 -1
- package/dist/core/oauth2/index.d.ts +1 -0
- package/dist/core/oauth2/index.d.ts.map +1 -1
- package/dist/core/oauth2/index.mjs +5 -1
- package/dist/core/oauth2/token/verifier/module.d.ts.map +1 -1
- package/dist/core/oauth2/token/verifier/module.mjs +3 -3
- package/dist/core/oauth2/token/verifier/module.mjs.map +1 -1
- package/dist/core/provisioning/entities/policy/validator.d.ts.map +1 -1
- package/dist/core/provisioning/entities/policy/validator.mjs +2 -1
- package/dist/core/provisioning/entities/policy/validator.mjs.map +1 -1
- package/dist/core/provisioning/entities/realm/wildcard-validator.d.ts.map +1 -1
- package/dist/core/provisioning/entities/realm/wildcard-validator.mjs +2 -1
- package/dist/core/provisioning/entities/realm/wildcard-validator.mjs.map +1 -1
- package/dist/core/security/policy/evaluator.mjs.map +1 -1
- package/dist/node_modules/@ebec/core/dist/index.mjs +130 -0
- package/dist/node_modules/@ebec/core/dist/index.mjs.map +1 -0
- package/dist/swagger.json +119 -43
- package/dist/utils/error.d.ts.map +1 -1
- package/dist/utils/error.mjs +5 -6
- package/dist/utils/error.mjs.map +1 -1
- package/package.json +29 -29
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { extractTokenPayload } from "@authup/server-kit";
|
|
5
|
+
import { ValidationError, markInstanceof, matchesInstanceof } from "@authup/errors";
|
|
6
|
+
//#region src/core/identity/provider/authentication/protocols/oauth2/assurance.ts
|
|
7
|
+
const IDENTITY_PROVIDER_ASSURANCE_ERROR_INSTANCE = Symbol.for("@authup/server-core/IdentityProviderAssuranceError");
|
|
8
|
+
/**
|
|
9
|
+
* The upstream did not meet the provider's `requiredAmr` / `requiredAcr`.
|
|
10
|
+
*
|
|
11
|
+
* It adds no dedicated `ErrorCode` and keeps `ValidationError`'s shared
|
|
12
|
+
* `BAD_REQUEST`, so the marker is the ONLY thing the guard may read - a
|
|
13
|
+
* code fallback would match every other `ValidationError` in the process.
|
|
14
|
+
* That is sound here because both callers swallow it into a redirect (the
|
|
15
|
+
* login bounces to the hosted page with `access_denied`, the link callback
|
|
16
|
+
* with `linkError`), so it never crosses a wire boundary. The reason lands
|
|
17
|
+
* in the operator's log instead, which is where a misconfigured allow-list
|
|
18
|
+
* is debugged.
|
|
19
|
+
*/ var IdentityProviderAssuranceError = class extends ValidationError {
|
|
20
|
+
constructor(message) {
|
|
21
|
+
super(message);
|
|
22
|
+
markInstanceof(this, IDENTITY_PROVIDER_ASSURANCE_ERROR_INSTANCE);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
function isIdentityProviderAssuranceError(input) {
|
|
26
|
+
return matchesInstanceof(input, IDENTITY_PROVIDER_ASSURANCE_ERROR_INSTANCE);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* `unknown`, not `string | null`, because these are extra-attribute rows and
|
|
30
|
+
* the EA value column round-trips through `destr`: a stored `"1"` - the
|
|
31
|
+
* canonical PAPE / ISO-29115 acr level, and the shortest value the validator
|
|
32
|
+
* deliberately allows - is read back as the NUMBER `1`. Calling a string
|
|
33
|
+
* method on that threw, and `"0"` (falsy) silently disabled the whole check.
|
|
34
|
+
*/ function splitList(input) {
|
|
35
|
+
if (input === null || typeof input === "undefined") return [];
|
|
36
|
+
return String(input).split(/[\s,]+/).filter(Boolean);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The `acr_values` an outbound authorize request asks for, so the upstream is
|
|
40
|
+
* asked to step up rather than merely observed. Voluntary in OIDC Core
|
|
41
|
+
* 5.5.1.1 - a provider may ignore it and answer anyway, which is exactly what
|
|
42
|
+
* {@see assertIdentityProviderAssurance} is for.
|
|
43
|
+
*/ function buildIdentityProviderAcrValues(provider) {
|
|
44
|
+
const values = splitList(provider.requiredAcr);
|
|
45
|
+
return values.length > 0 ? values.join(" ") : void 0;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Verify what the upstream actually returned against the provider's
|
|
49
|
+
* allow-lists.
|
|
50
|
+
*
|
|
51
|
+
* Fail closed on every uncertainty: the claims are read from the `id_token`
|
|
52
|
+
* alone, so a provider that returns none - or one whose token carries neither
|
|
53
|
+
* claim, or cannot be decoded - is refused once an allow-list is set. Failing
|
|
54
|
+
* open there would make the feature decorative.
|
|
55
|
+
*
|
|
56
|
+
* The token is DECODED, not verified, and that is not an oversight. It is the
|
|
57
|
+
* return value of the server's own back-channel POST to the operator-configured
|
|
58
|
+
* `tokenUrl`, so OIDC Core 3.1.3.7 item 6 accepts TLS server validation in
|
|
59
|
+
* place of checking the signature. No browser-supplied token reaches here, and
|
|
60
|
+
* a provider the operator configured is the authentication authority for these
|
|
61
|
+
* users anyway: one that lies about `amr` could equally mint any subject. What
|
|
62
|
+
* item 6 does NOT waive is the audience, which is checked below because
|
|
63
|
+
* `clientId` is already in hand.
|
|
64
|
+
*/ function assertIdentityProviderAssurance(provider, idToken) {
|
|
65
|
+
const requiredAmr = splitList(provider.requiredAmr);
|
|
66
|
+
const requiredAcr = splitList(provider.requiredAcr);
|
|
67
|
+
if (requiredAmr.length === 0 && requiredAcr.length === 0) return;
|
|
68
|
+
if (typeof idToken !== "string") throw new IdentityProviderAssuranceError("The identity provider returned no id_token to verify.");
|
|
69
|
+
let claims;
|
|
70
|
+
try {
|
|
71
|
+
claims = extractTokenPayload(idToken) ?? {};
|
|
72
|
+
} catch {
|
|
73
|
+
throw new IdentityProviderAssuranceError("The identity provider id_token could not be read.");
|
|
74
|
+
}
|
|
75
|
+
if (provider.clientId) {
|
|
76
|
+
const audiences = Array.isArray(claims.aud) ? claims.aud : [claims.aud];
|
|
77
|
+
if (audiences.length !== 1 || audiences[0] !== provider.clientId) throw new IdentityProviderAssuranceError("The identity provider id_token was not issued for this client alone.");
|
|
78
|
+
if (typeof claims.azp === "string" && claims.azp !== provider.clientId) throw new IdentityProviderAssuranceError("The identity provider id_token was issued for another party.");
|
|
79
|
+
}
|
|
80
|
+
if (requiredAmr.length > 0) {
|
|
81
|
+
const amr = Array.isArray(claims.amr) ? claims.amr : [];
|
|
82
|
+
if (!requiredAmr.some((value) => amr.includes(value))) throw new IdentityProviderAssuranceError(`The identity provider amr claim does not satisfy: ${requiredAmr.join(", ")}.`);
|
|
83
|
+
}
|
|
84
|
+
if (requiredAcr.length > 0 && !requiredAcr.includes(claims.acr)) throw new IdentityProviderAssuranceError(`The identity provider acr claim does not satisfy: ${requiredAcr.join(", ")}.`);
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
export { IDENTITY_PROVIDER_ASSURANCE_ERROR_INSTANCE, IdentityProviderAssuranceError, assertIdentityProviderAssurance, buildIdentityProviderAcrValues, isIdentityProviderAssuranceError };
|
|
88
|
+
|
|
89
|
+
//# sourceMappingURL=assurance.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assurance.mjs","names":["ValidationError","markInstanceof","matchesInstanceof","extractTokenPayload","IDENTITY_PROVIDER_ASSURANCE_ERROR_INSTANCE","Symbol","for","IdentityProviderAssuranceError","message","isIdentityProviderAssuranceError","input","splitList","String","split","filter","Boolean","buildIdentityProviderAcrValues","provider","values","requiredAcr","length","join","undefined","assertIdentityProviderAssurance","idToken","requiredAmr","claims","clientId","audiences","Array","isArray","aud","azp","amr","some","value","includes","acr"],"sources":["../../../../../../../src/core/identity/provider/authentication/protocols/oauth2/assurance.ts"],"sourcesContent":["/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { OAuth2IdentityProviderBase } from '@authup/core-kit';\nimport { ValidationError, markInstanceof, matchesInstanceof } from '@authup/errors';\nimport { extractTokenPayload } from '@authup/server-kit';\n\nexport const IDENTITY_PROVIDER_ASSURANCE_ERROR_INSTANCE = Symbol.for('@authup/server-core/IdentityProviderAssuranceError');\n\n/**\n * The upstream did not meet the provider's `requiredAmr` / `requiredAcr`.\n *\n * It adds no dedicated `ErrorCode` and keeps `ValidationError`'s shared\n * `BAD_REQUEST`, so the marker is the ONLY thing the guard may read - a\n * code fallback would match every other `ValidationError` in the process.\n * That is sound here because both callers swallow it into a redirect (the\n * login bounces to the hosted page with `access_denied`, the link callback\n * with `linkError`), so it never crosses a wire boundary. The reason lands\n * in the operator's log instead, which is where a misconfigured allow-list\n * is debugged.\n */\nexport class IdentityProviderAssuranceError extends ValidationError {\n constructor(message: string) {\n super(message);\n markInstanceof(this, IDENTITY_PROVIDER_ASSURANCE_ERROR_INSTANCE);\n }\n}\n\nexport function isIdentityProviderAssuranceError(input: unknown): input is IdentityProviderAssuranceError {\n return matchesInstanceof(input, IDENTITY_PROVIDER_ASSURANCE_ERROR_INSTANCE);\n}\n\n/**\n * `unknown`, not `string | null`, because these are extra-attribute rows and\n * the EA value column round-trips through `destr`: a stored `\"1\"` - the\n * canonical PAPE / ISO-29115 acr level, and the shortest value the validator\n * deliberately allows - is read back as the NUMBER `1`. Calling a string\n * method on that threw, and `\"0\"` (falsy) silently disabled the whole check.\n */\nfunction splitList(input?: unknown): string[] {\n if (input === null || typeof input === 'undefined') {\n return [];\n }\n\n return String(input).split(/[\\s,]+/).filter(Boolean);\n}\n\n/**\n * The `acr_values` an outbound authorize request asks for, so the upstream is\n * asked to step up rather than merely observed. Voluntary in OIDC Core\n * 5.5.1.1 - a provider may ignore it and answer anyway, which is exactly what\n * {@see assertIdentityProviderAssurance} is for.\n */\nexport function buildIdentityProviderAcrValues(provider: Partial<OAuth2IdentityProviderBase>): string | undefined {\n const values = splitList(provider.requiredAcr);\n return values.length > 0 ? values.join(' ') : undefined;\n}\n\n/**\n * Verify what the upstream actually returned against the provider's\n * allow-lists.\n *\n * Fail closed on every uncertainty: the claims are read from the `id_token`\n * alone, so a provider that returns none - or one whose token carries neither\n * claim, or cannot be decoded - is refused once an allow-list is set. Failing\n * open there would make the feature decorative.\n *\n * The token is DECODED, not verified, and that is not an oversight. It is the\n * return value of the server's own back-channel POST to the operator-configured\n * `tokenUrl`, so OIDC Core 3.1.3.7 item 6 accepts TLS server validation in\n * place of checking the signature. No browser-supplied token reaches here, and\n * a provider the operator configured is the authentication authority for these\n * users anyway: one that lies about `amr` could equally mint any subject. What\n * item 6 does NOT waive is the audience, which is checked below because\n * `clientId` is already in hand.\n */\nexport function assertIdentityProviderAssurance(\n provider: Partial<OAuth2IdentityProviderBase>,\n idToken?: string,\n): void {\n const requiredAmr = splitList(provider.requiredAmr);\n const requiredAcr = splitList(provider.requiredAcr);\n\n if (requiredAmr.length === 0 && requiredAcr.length === 0) {\n return;\n }\n\n if (typeof idToken !== 'string') {\n throw new IdentityProviderAssuranceError('The identity provider returned no id_token to verify.');\n }\n\n let claims : Record<string, any>;\n try {\n // `?? {}` against the declared return type on purpose: a payload of\n // literal `null` parses, so the claim reads below would throw a\n // TypeError out of a callback that has to answer with a refusal.\n claims = extractTokenPayload(idToken) ?? {};\n } catch {\n throw new IdentityProviderAssuranceError('The identity provider id_token could not be read.');\n }\n\n // OIDC Core 3.1.3.7 items 3-5, the part item 6 does not excuse. Guarded on\n // `clientId` because the gate takes a partial provider: an absent client id\n // has nothing to compare against, and inventing a refusal there would fail\n // a login over a value the caller never supplied.\n if (provider.clientId) {\n const audiences = Array.isArray(claims.aud) ? claims.aud : [claims.aud];\n\n // Item 3 rejects a token that lists an audience the client does not\n // trust, and authup has no trusted-audience configuration, so the only\n // trusted audience is this provider's own client id. A membership test\n // would have accepted `[\"someone-else\", <us>]`, where the provider\n // minted one assertion for several parties.\n if (audiences.length !== 1 || audiences[0] !== provider.clientId) {\n throw new IdentityProviderAssuranceError(\n 'The identity provider id_token was not issued for this client alone.',\n );\n }\n\n // Item 5. Redundant against the single audience above for a conformant\n // provider, and not against one that names a different authorized\n // party.\n if (typeof claims.azp === 'string' && claims.azp !== provider.clientId) {\n throw new IdentityProviderAssuranceError(\n 'The identity provider id_token was issued for another party.',\n );\n }\n }\n\n if (requiredAmr.length > 0) {\n const amr = Array.isArray(claims.amr) ? claims.amr : [];\n if (!requiredAmr.some((value) => amr.includes(value))) {\n throw new IdentityProviderAssuranceError(\n `The identity provider amr claim does not satisfy: ${requiredAmr.join(', ')}.`,\n );\n }\n }\n\n if (requiredAcr.length > 0 && !requiredAcr.includes(claims.acr)) {\n throw new IdentityProviderAssuranceError(\n `The identity provider acr claim does not satisfy: ${requiredAcr.join(', ')}.`,\n );\n }\n}\n"],"mappings":";;;;;;AAWA,MAAaI,6CAA6CC,OAAOC,IAAI,oDAAA;;;;;;;;;;;;GAcrE,IAAaC,iCAAb,cAAoDP,gBAAAA;CAChD,YAAYQ,SAAiB;EACzB,MAAMA,OAAAA;EACNP,eAAe,MAAMG,0CAAAA;CACzB;AACJ;AAEA,SAAgBK,iCAAiCC,OAAc;CAC3D,OAAOR,kBAAkBQ,OAAON,0CAAAA;AACpC;;;;;;;GASA,SAASO,UAAUD,OAAe;CAC9B,IAAIA,UAAU,QAAQ,OAAOA,UAAU,aACnC,OAAO,CAAA;CAGX,OAAOE,OAAOF,KAAAA,CAAAA,CAAOG,MAAM,QAAA,CAAA,CAAUC,OAAOC,OAAAA;AAChD;;;;;;GAQA,SAAgBC,+BAA+BC,UAA6C;CACxF,MAAMC,SAASP,UAAUM,SAASE,WAAW;CAC7C,OAAOD,OAAOE,SAAS,IAAIF,OAAOG,KAAK,GAAA,IAAOC,KAAAA;AAClD;;;;;;;;;;;;;;;;;;GAoBA,SAAgBC,gCACZN,UACAO,SAAgB;CAEhB,MAAMC,cAAcd,UAAUM,SAASQ,WAAW;CAClD,MAAMN,cAAcR,UAAUM,SAASE,WAAW;CAElD,IAAIM,YAAYL,WAAW,KAAKD,YAAYC,WAAW,GACnD;CAGJ,IAAI,OAAOI,YAAY,UACnB,MAAM,IAAIjB,+BAA+B,uDAAA;CAG7C,IAAImB;CACJ,IAAI;EAIAA,SAASvB,oBAAoBqB,OAAAA,KAAY,CAAC;CAC9C,QAAQ;EACJ,MAAM,IAAIjB,+BAA+B,mDAAA;CAC7C;CAMA,IAAIU,SAASU,UAAU;EACnB,MAAMC,YAAYC,MAAMC,QAAQJ,OAAOK,GAAG,IAAIL,OAAOK,MAAM,CAACL,OAAOK,GAAI;EAOvE,IAAIH,UAAUR,WAAW,KAAKQ,UAAU,OAAOX,SAASU,UACpD,MAAM,IAAIpB,+BACN,sEAAA;EAOR,IAAI,OAAOmB,OAAOM,QAAQ,YAAYN,OAAOM,QAAQf,SAASU,UAC1D,MAAM,IAAIpB,+BACN,8DAAA;CAGZ;CAEA,IAAIkB,YAAYL,SAAS,GAAG;EACxB,MAAMa,MAAMJ,MAAMC,QAAQJ,OAAOO,GAAG,IAAIP,OAAOO,MAAM,CAAA;EACrD,IAAI,CAACR,YAAYS,MAAMC,UAAUF,IAAIG,SAASD,KAAAA,CAAAA,GAC1C,MAAM,IAAI5B,+BACN,qDAAqDkB,YAAYJ,KAAK,IAAA,EAAM,EAAE;CAG1F;CAEA,IAAIF,YAAYC,SAAS,KAAK,CAACD,YAAYiB,SAASV,OAAOW,GAAG,GAC1D,MAAM,IAAI9B,+BACN,qDAAqDY,YAAYE,KAAK,IAAA,EAAM,EAAE;AAG1F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/identity/provider/authentication/protocols/oauth2/index.ts"],"names":[],"mappings":"AAOA,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/identity/provider/authentication/protocols/oauth2/index.ts"],"names":[],"mappings":"AAOA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
+
import { IDENTITY_PROVIDER_ASSURANCE_ERROR_INSTANCE, IdentityProviderAssuranceError, assertIdentityProviderAssurance, buildIdentityProviderAcrValues, isIdentityProviderAssuranceError } from "./assurance.mjs";
|
|
4
5
|
import { IdentityProviderOAuth2Authenticator } from "./module.mjs";
|
|
5
|
-
export { IdentityProviderOAuth2Authenticator };
|
|
6
|
+
export { IDENTITY_PROVIDER_ASSURANCE_ERROR_INSTANCE, IdentityProviderAssuranceError, IdentityProviderOAuth2Authenticator, assertIdentityProviderAssurance, buildIdentityProviderAcrValues, isIdentityProviderAssuranceError };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/identity/provider/authentication/protocols/oauth2/module.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/identity/provider/authentication/protocols/oauth2/module.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAI7F,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,2BAA2B,CAAC;AACjF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAElE,OAAO,KAAK,EACR,oBAAoB,EACpB,0CAA0C,EAC1C,0CAA0C,EAC1C,mCAAmC,EACtC,MAAM,YAAY,CAAC;AAQpB,qBAAa,mCAAoC,YAAW,oBAAoB,CAAC,IAAI,CAAC;IAClF,SAAS,CAAC,MAAM,EAAG,YAAY,CAAC;IAEhC,SAAS,CAAC,OAAO,EAAG,0CAA0C,CAAC;IAE/D,SAAS,CAAC,cAAc,EAAE,+BAA+B,CAAC;IAE1D,SAAS,CAAC,QAAQ,EAAG,sBAAsB,GAAG,sBAAsB,CAAC;IAErE,SAAS,CAAC,MAAM,CAAC,EAAG,MAAM,CAAC;gBAIf,GAAG,EAAE,0CAA0C;IA4B3D,gBAAgB,CAAC,UAAU,GAAE,OAAO,CAAC,mBAAmB,CAAM,GAAI,MAAM;IA0BlE,eAAe,CAAC,MAAM,EAAE,mCAAmC,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAiB/F,YAAY,CAAC,MAAM,EAAE,mCAAmC,GAAG,OAAO,CAAC,IAAI,CAAC;IAQxE,gBAAgB,CAAC,MAAM,EAAE,mCAAmC,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAiB1F;;;;;;;OAOG;cACa,aAAa,CAAC,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,SAAS,GAAI,OAAO,CAAC,SAAS,CAAC;IAyDjG;;;;OAIG;IACH,SAAS,CAAC,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAI,OAAO,CAAC,CAAC,CAAC;cAY1C,mCAAmC,CAAC,KAAK,EAAE,kBAAkB,GAAI,OAAO,CAAC,wBAAwB,CAAC;CA2BrH"}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
+
import { resolveURL } from "../../../../../../utils/url.mjs";
|
|
5
|
+
import "../../../../../../utils/index.mjs";
|
|
6
|
+
import { assertIdentityProviderAssurance, buildIdentityProviderAcrValues } from "./assurance.mjs";
|
|
4
7
|
import { buildIdentityProviderAuthorizeCallbackPath } from "@authup/core-kit";
|
|
5
8
|
import { extractTokenPayload } from "@authup/server-kit";
|
|
6
9
|
import { ValidationError } from "@authup/errors";
|
|
@@ -24,7 +27,7 @@ var IdentityProviderOAuth2Authenticator = class {
|
|
|
24
27
|
this.client = new OAuth2Client({ options: {
|
|
25
28
|
clientId: ctx.provider.clientId,
|
|
26
29
|
clientSecret: ctx.provider.clientSecret,
|
|
27
|
-
redirectUri:
|
|
30
|
+
redirectUri: resolveURL(ctx.options.baseURL, buildIdentityProviderAuthorizeCallbackPath(ctx.provider.id)),
|
|
28
31
|
scope: ctx.provider.scope || void 0,
|
|
29
32
|
authorizationEndpoint: ctx.provider.authorizeUrl,
|
|
30
33
|
tokenEndpoint: ctx.provider.tokenUrl,
|
|
@@ -33,13 +36,17 @@ var IdentityProviderOAuth2Authenticator = class {
|
|
|
33
36
|
}
|
|
34
37
|
buildRedirectURL(parameters = {}) {
|
|
35
38
|
try {
|
|
36
|
-
|
|
39
|
+
const url = new URL(this.client.authorize.buildURL(parameters));
|
|
40
|
+
const acrValues = buildIdentityProviderAcrValues(this.provider);
|
|
41
|
+
if (acrValues) url.searchParams.set("acr_values", acrValues);
|
|
42
|
+
return url.href;
|
|
37
43
|
} catch {
|
|
38
44
|
throw new ValidationError("The identity provider is misconfigured and has an invalid or missing authorize URL.");
|
|
39
45
|
}
|
|
40
46
|
}
|
|
41
47
|
async resolveIdentity(params) {
|
|
42
48
|
const token = await this.client.token.createWithAuthorizationCode(params);
|
|
49
|
+
assertIdentityProviderAssurance(this.provider, token.id_token);
|
|
43
50
|
const identity = await this.buildIdentityWithTokenGrantResponse(token);
|
|
44
51
|
if (this.options.clientId) identity.clientId = this.options.clientId;
|
|
45
52
|
return identity;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.mjs","names":["buildIdentityProviderAuthorizeCallbackPath","ValidationError","extractTokenPayload","OAuth2Client","USERINFO_TIMEOUT","IdentityProviderOAuth2Authenticator","client","options","accountManager","provider","logger","ctx","clientId","clientSecret","redirectUri","baseURL","id","scope","undefined","authorizationEndpoint","authorizeUrl","tokenEndpoint","tokenUrl","userinfoEndpoint","userInfoUrl","buildRedirectURL","parameters","authorize","buildURL","resolveIdentity","params","token","createWithAuthorizationCode","identity","buildIdentityWithTokenGrantResponse","authenticate","account","save","user","safeAuthenticate","data","success","e","error","resolveClaims","input","payload","claims","id_token","warn","message","userInfo","withTimeout","get","type","access_token","sub","promise","Promise","race","_resolve","reject","setTimeout","Error","unref","attributeCandidates","name","preferred_username","nickname","email"],"sources":["../../../../../../../src/core/identity/provider/authentication/protocols/oauth2/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2023-2023.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { OAuth2IdentityProvider, OpenIDIdentityProvider, User } from '@authup/core-kit';\nimport { buildIdentityProviderAuthorizeCallbackPath } from '@authup/core-kit';\nimport { ValidationError } from '@authup/errors';\nimport type { Result } from '@authup/kit';\nimport type { JWTClaims } from '@authup/specs';\nimport type { Logger } from '@authup/server-kit';\nimport { extractTokenPayload } from '@authup/server-kit';\nimport type { AuthorizeParameters, TokenGrantResponse } from '@hapic/oauth2';\nimport { OAuth2Client } from '@hapic/oauth2';\nimport type { IIdentityProviderAccountManager } from '../../../account/index.ts';\nimport type { IdentityProviderIdentity } from '../../../types.ts';\nimport type {\n IOAuth2Authenticator,\n IdentityProviderOAuth2AuthenticatorContext,\n IdentityProviderOAuth2AuthenticatorOptions,\n OAuth2AuthorizationCodeGrantPayload,\n} from './types.ts';\n\n/**\n * Bound on the optional userinfo enrichment. Short by design: the caller is\n * a browser sitting on a redirect, and the login proceeds without it.\n */\nconst USERINFO_TIMEOUT = 5000;\n\nexport class IdentityProviderOAuth2Authenticator implements IOAuth2Authenticator<User> {\n protected client : OAuth2Client;\n\n protected options : IdentityProviderOAuth2AuthenticatorOptions;\n\n protected accountManager: IIdentityProviderAccountManager;\n\n protected provider : OAuth2IdentityProvider | OpenIDIdentityProvider;\n\n protected logger? : Logger;\n\n //----------------------------------------------------------------------\n\n constructor(ctx: IdentityProviderOAuth2AuthenticatorContext) {\n this.options = ctx.options;\n this.accountManager = ctx.accountManager;\n this.provider = ctx.provider;\n this.logger = ctx.logger;\n\n this.client = new OAuth2Client({\n options: {\n clientId: ctx.provider.clientId,\n clientSecret: ctx.provider.clientSecret,\n redirectUri: `${ctx.options.baseURL}${buildIdentityProviderAuthorizeCallbackPath(ctx.provider.id)}`,\n scope: ctx.provider.scope || undefined,\n authorizationEndpoint: ctx.provider.authorizeUrl,\n tokenEndpoint: ctx.provider.tokenUrl,\n userinfoEndpoint: ctx.provider.userInfoUrl || undefined,\n },\n });\n }\n\n //----------------------------------------------------------------------\n\n buildRedirectURL(parameters: Partial<AuthorizeParameters> = {}) : string {\n try {\n return this.client.authorize.buildURL(parameters);\n } catch {\n throw new ValidationError(\n 'The identity provider is misconfigured and has an invalid or missing authorize URL.',\n );\n }\n }\n\n //----------------------------------------------------------------------\n\n async resolveIdentity(params: OAuth2AuthorizationCodeGrantPayload): Promise<IdentityProviderIdentity> {\n const token = await this.client.token.createWithAuthorizationCode(params);\n\n const identity = await this.buildIdentityWithTokenGrantResponse(token);\n if (this.options.clientId) {\n identity.clientId = this.options.clientId;\n }\n\n return identity;\n }\n\n async authenticate(params: OAuth2AuthorizationCodeGrantPayload): Promise<User> {\n const identity = await this.resolveIdentity(params);\n\n const account = await this.accountManager.save(identity);\n\n return account.user;\n }\n\n async safeAuthenticate(params: OAuth2AuthorizationCodeGrantPayload): Promise<Result<User>> {\n try {\n const data = await this.authenticate(params);\n return {\n success: true,\n data, \n };\n } catch (e) {\n return {\n success: false,\n error: e as Error, \n };\n }\n }\n\n //----------------------------------------------------------------------\n\n /**\n * The claims an external identity is described by, richest source last.\n *\n * The access token is opaque by contract (OIDC Core §2), so it is only\n * the floor: authup's own carries `sub`, `kind` and `realm_name` and no\n * username at all, which is why a federated user used to be provisioned\n * under the remote subject UUID.\n */\n protected async resolveClaims(input: TokenGrantResponse, payload: JWTClaims) : Promise<JWTClaims> {\n let claims = payload;\n\n if (typeof input.id_token === 'string') {\n try {\n claims = {\n ...claims,\n ...extractTokenPayload(input.id_token),\n };\n } catch (e) {\n // an encrypted (five-segment JWE) id_token is not decodable\n // here, and was ignored outright before it was read at all.\n // Logged rather than swallowed silently: every other reason\n // to land here leaves the user provisioned under the remote\n // subject, which is the defect this method exists to fix.\n this.logger?.warn(\n `The identity provider (${this.provider.id}) id_token could not be read: ${(e as Error).message}`,\n );\n }\n }\n\n if (this.provider.userInfoUrl) {\n // the guard is load-bearing: the client carries no baseURL, so\n // hapic's `/userinfo` default would be a relative fetch URL\n try {\n const userInfo = await this.withTimeout(this.client.userInfo.get({\n type: 'Bearer',\n token: input.access_token,\n }));\n\n // OIDC Core 5.3.2: a userinfo response whose `sub` does not\n // match the token's MUST NOT be used. Without this a\n // mis-routed response (a multi-tenant gateway, a token\n // mix-up) would name and, worse, EMAIL the local user after\n // somebody else.\n if (\n typeof userInfo.sub === 'string' &&\n typeof payload.sub === 'string' &&\n userInfo.sub !== payload.sub\n ) {\n this.logger?.warn(\n `The identity provider (${this.provider.id}) userinfo subject does not match the token subject.`,\n );\n } else {\n claims = { ...claims, ...userInfo };\n }\n } catch (e) {\n // enrichment, never a login blocker\n this.logger?.warn(\n `The identity provider (${this.provider.id}) userinfo request failed: ${(e as Error).message}`,\n );\n }\n }\n\n return claims;\n }\n\n /**\n * hapic passes no `signal` to `fetch`, so an endpoint that accepts the\n * connection and never answers would hold the login for undici's 300s\n * headers timeout. Enrichment must not outlast the request it enriches.\n */\n protected withTimeout<T>(promise: Promise<T>) : Promise<T> {\n return Promise.race([\n promise,\n new Promise<T>((_resolve, reject) => {\n setTimeout(\n () => reject(new Error(`the request exceeded ${USERINFO_TIMEOUT}ms`)),\n USERINFO_TIMEOUT,\n ).unref();\n }),\n ]);\n }\n\n protected async buildIdentityWithTokenGrantResponse(input: TokenGrantResponse) : Promise<IdentityProviderIdentity> {\n const payload = extractTokenPayload(input.access_token);\n const claims = await this.resolveClaims(input, payload);\n\n return {\n // the account key: sourcing it from a richer claim set would\n // orphan every existing auth_identity_provider_accounts row\n id: payload.sub!,\n attributeCandidates: {\n name: [\n // keycloak/authentik put the username here, authup its\n // (nullable) display name; a candidate failing name\n // validation shifts to the next one\n claims.preferred_username,\n claims.nickname,\n // authup's own id_token: the real user name\n claims.name,\n claims.sub,\n ],\n email: [\n claims.email,\n ],\n },\n data: payload,\n provider: this.provider,\n };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"module.mjs","names":["buildIdentityProviderAuthorizeCallbackPath","resolveURL","ValidationError","extractTokenPayload","OAuth2Client","assertIdentityProviderAssurance","buildIdentityProviderAcrValues","USERINFO_TIMEOUT","IdentityProviderOAuth2Authenticator","client","options","accountManager","provider","logger","ctx","clientId","clientSecret","redirectUri","baseURL","id","scope","undefined","authorizationEndpoint","authorizeUrl","tokenEndpoint","tokenUrl","userinfoEndpoint","userInfoUrl","buildRedirectURL","parameters","url","URL","authorize","buildURL","acrValues","searchParams","set","href","resolveIdentity","params","token","createWithAuthorizationCode","id_token","identity","buildIdentityWithTokenGrantResponse","authenticate","account","save","user","safeAuthenticate","data","success","e","error","resolveClaims","input","payload","claims","warn","message","userInfo","withTimeout","get","type","access_token","sub","promise","Promise","race","_resolve","reject","setTimeout","Error","unref","attributeCandidates","name","preferred_username","nickname","email"],"sources":["../../../../../../../src/core/identity/provider/authentication/protocols/oauth2/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2023-2023.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { OAuth2IdentityProvider, OpenIDIdentityProvider, User } from '@authup/core-kit';\nimport { buildIdentityProviderAuthorizeCallbackPath } from '@authup/core-kit';\nimport { resolveURL } from '../../../../../../utils/index.ts';\nimport { ValidationError } from '@authup/errors';\nimport type { Result } from '@authup/kit';\nimport type { JWTClaims } from '@authup/specs';\nimport type { Logger } from '@authup/server-kit';\nimport { extractTokenPayload } from '@authup/server-kit';\nimport type { AuthorizeParameters, TokenGrantResponse } from '@hapic/oauth2';\nimport { OAuth2Client } from '@hapic/oauth2';\nimport type { IIdentityProviderAccountManager } from '../../../account/index.ts';\nimport type { IdentityProviderIdentity } from '../../../types.ts';\nimport { assertIdentityProviderAssurance, buildIdentityProviderAcrValues } from './assurance.ts';\nimport type {\n IOAuth2Authenticator,\n IdentityProviderOAuth2AuthenticatorContext,\n IdentityProviderOAuth2AuthenticatorOptions,\n OAuth2AuthorizationCodeGrantPayload,\n} from './types.ts';\n\n/**\n * Bound on the optional userinfo enrichment. Short by design: the caller is\n * a browser sitting on a redirect, and the login proceeds without it.\n */\nconst USERINFO_TIMEOUT = 5000;\n\nexport class IdentityProviderOAuth2Authenticator implements IOAuth2Authenticator<User> {\n protected client : OAuth2Client;\n\n protected options : IdentityProviderOAuth2AuthenticatorOptions;\n\n protected accountManager: IIdentityProviderAccountManager;\n\n protected provider : OAuth2IdentityProvider | OpenIDIdentityProvider;\n\n protected logger? : Logger;\n\n //----------------------------------------------------------------------\n\n constructor(ctx: IdentityProviderOAuth2AuthenticatorContext) {\n this.options = ctx.options;\n this.accountManager = ctx.accountManager;\n this.provider = ctx.provider;\n this.logger = ctx.logger;\n\n this.client = new OAuth2Client({\n options: {\n clientId: ctx.provider.clientId,\n clientSecret: ctx.provider.clientSecret,\n // resolveURL, never a template literal: a publicUrl with a\n // trailing slash produced `//identity-providers/...`, which\n // the router does not match, so the provider sent the person\n // back to a 404 and every federated login died there.\n redirectUri: resolveURL(\n ctx.options.baseURL,\n buildIdentityProviderAuthorizeCallbackPath(ctx.provider.id),\n ),\n scope: ctx.provider.scope || undefined,\n authorizationEndpoint: ctx.provider.authorizeUrl,\n tokenEndpoint: ctx.provider.tokenUrl,\n userinfoEndpoint: ctx.provider.userInfoUrl || undefined,\n },\n });\n }\n\n //----------------------------------------------------------------------\n\n buildRedirectURL(parameters: Partial<AuthorizeParameters> = {}) : string {\n try {\n const url = new URL(this.client.authorize.buildURL(parameters));\n\n // Ask the upstream to step up rather than merely observing what\n // it returns. Defaulted here rather than at the call sites, so\n // the login and the account-link flow both send it.\n //\n // Appended by hand because hapic's buildAuthorizeURL only emits\n // the keys it knows and silently drops the rest (tada5hi/hapic#1071)\n // - drop this once `acr_values` is part of AuthorizeParameters.\n const acrValues = buildIdentityProviderAcrValues(this.provider);\n if (acrValues) {\n url.searchParams.set('acr_values', acrValues);\n }\n\n return url.href;\n } catch {\n throw new ValidationError(\n 'The identity provider is misconfigured and has an invalid or missing authorize URL.',\n );\n }\n }\n\n //----------------------------------------------------------------------\n\n async resolveIdentity(params: OAuth2AuthorizationCodeGrantPayload): Promise<IdentityProviderIdentity> {\n const token = await this.client.token.createWithAuthorizationCode(params);\n\n // Before the identity is built, so a refused login provisions no user\n // and links no account. Reading the token response here rather than\n // inside the identity builder is deliberate: the five presets override\n // the builder, and a preset must not be able to skip the gate.\n assertIdentityProviderAssurance(this.provider, token.id_token);\n\n const identity = await this.buildIdentityWithTokenGrantResponse(token);\n if (this.options.clientId) {\n identity.clientId = this.options.clientId;\n }\n\n return identity;\n }\n\n async authenticate(params: OAuth2AuthorizationCodeGrantPayload): Promise<User> {\n const identity = await this.resolveIdentity(params);\n\n const account = await this.accountManager.save(identity);\n\n return account.user;\n }\n\n async safeAuthenticate(params: OAuth2AuthorizationCodeGrantPayload): Promise<Result<User>> {\n try {\n const data = await this.authenticate(params);\n return {\n success: true,\n data, \n };\n } catch (e) {\n return {\n success: false,\n error: e as Error, \n };\n }\n }\n\n //----------------------------------------------------------------------\n\n /**\n * The claims an external identity is described by, richest source last.\n *\n * The access token is opaque by contract (OIDC Core §2), so it is only\n * the floor: authup's own carries `sub`, `kind` and `realm_name` and no\n * username at all, which is why a federated user used to be provisioned\n * under the remote subject UUID.\n */\n protected async resolveClaims(input: TokenGrantResponse, payload: JWTClaims) : Promise<JWTClaims> {\n let claims = payload;\n\n if (typeof input.id_token === 'string') {\n try {\n claims = {\n ...claims,\n ...extractTokenPayload(input.id_token),\n };\n } catch (e) {\n // an encrypted (five-segment JWE) id_token is not decodable\n // here, and was ignored outright before it was read at all.\n // Logged rather than swallowed silently: every other reason\n // to land here leaves the user provisioned under the remote\n // subject, which is the defect this method exists to fix.\n this.logger?.warn(\n `The identity provider (${this.provider.id}) id_token could not be read: ${(e as Error).message}`,\n );\n }\n }\n\n if (this.provider.userInfoUrl) {\n // the guard is load-bearing: the client carries no baseURL, so\n // hapic's `/userinfo` default would be a relative fetch URL\n try {\n const userInfo = await this.withTimeout(this.client.userInfo.get({\n type: 'Bearer',\n token: input.access_token,\n }));\n\n // OIDC Core 5.3.2: a userinfo response whose `sub` does not\n // match the token's MUST NOT be used. Without this a\n // mis-routed response (a multi-tenant gateway, a token\n // mix-up) would name and, worse, EMAIL the local user after\n // somebody else.\n if (\n typeof userInfo.sub === 'string' &&\n typeof payload.sub === 'string' &&\n userInfo.sub !== payload.sub\n ) {\n this.logger?.warn(\n `The identity provider (${this.provider.id}) userinfo subject does not match the token subject.`,\n );\n } else {\n claims = { ...claims, ...userInfo };\n }\n } catch (e) {\n // enrichment, never a login blocker\n this.logger?.warn(\n `The identity provider (${this.provider.id}) userinfo request failed: ${(e as Error).message}`,\n );\n }\n }\n\n return claims;\n }\n\n /**\n * hapic passes no `signal` to `fetch`, so an endpoint that accepts the\n * connection and never answers would hold the login for undici's 300s\n * headers timeout. Enrichment must not outlast the request it enriches.\n */\n protected withTimeout<T>(promise: Promise<T>) : Promise<T> {\n return Promise.race([\n promise,\n new Promise<T>((_resolve, reject) => {\n setTimeout(\n () => reject(new Error(`the request exceeded ${USERINFO_TIMEOUT}ms`)),\n USERINFO_TIMEOUT,\n ).unref();\n }),\n ]);\n }\n\n protected async buildIdentityWithTokenGrantResponse(input: TokenGrantResponse) : Promise<IdentityProviderIdentity> {\n const payload = extractTokenPayload(input.access_token);\n const claims = await this.resolveClaims(input, payload);\n\n return {\n // the account key: sourcing it from a richer claim set would\n // orphan every existing auth_identity_provider_accounts row\n id: payload.sub!,\n attributeCandidates: {\n name: [\n // keycloak/authentik put the username here, authup its\n // (nullable) display name; a candidate failing name\n // validation shifts to the next one\n claims.preferred_username,\n claims.nickname,\n // authup's own id_token: the real user name\n claims.name,\n claims.sub,\n ],\n email: [\n claims.email,\n ],\n },\n data: payload,\n provider: this.provider,\n };\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
+
import { IdentityProviderLdapAuthenticator } from "./authentication/protocols/ldap/module.mjs";
|
|
5
|
+
import { IdentityProviderLdapCollectionAuthenticator } from "./authentication/protocols/ldap/collection.mjs";
|
|
6
|
+
import { IDENTITY_PROVIDER_ASSURANCE_ERROR_INSTANCE, IdentityProviderAssuranceError, assertIdentityProviderAssurance, buildIdentityProviderAcrValues, isIdentityProviderAssuranceError } from "./authentication/protocols/oauth2/assurance.mjs";
|
|
7
|
+
import { IdentityProviderOAuth2Authenticator } from "./authentication/protocols/oauth2/module.mjs";
|
|
8
|
+
import { IdentityProviderOpenIDAuthenticator } from "./authentication/protocols/open-id/module.mjs";
|
|
9
|
+
import { IdentityProviderFacebookAuthenticator } from "./authentication/presets/facebook.mjs";
|
|
10
|
+
import { IdentityProviderGithubAuthenticator } from "./authentication/presets/github.mjs";
|
|
11
|
+
import { IdentityProviderGoogleAuthenticator } from "./authentication/presets/google.mjs";
|
|
12
|
+
import { IdentityProviderInstagramAuthenticator } from "./authentication/presets/instagram.mjs";
|
|
13
|
+
import { IdentityProviderPaypalAuthenticator } from "./authentication/presets/paypal.mjs";
|
|
14
|
+
import { createIdentityProviderOAuth2Authenticator } from "./authentication/factory.mjs";
|
|
4
15
|
import { IDENTITY_PROVIDER_ACCOUNT_LINK_TTL } from "./account/constants.mjs";
|
|
5
16
|
import { IDENTITY_PROVIDER_ACCOUNT_ALREADY_LINKED_ERROR_INSTANCE, IdentityProviderAccountAlreadyLinkedError, isIdentityProviderAccountAlreadyLinkedError } from "./account/error.mjs";
|
|
6
17
|
import { IdentityProviderMapperOperation } from "./mapper/constants.mjs";
|
|
@@ -11,16 +22,6 @@ import { IdentityProviderPermissionMapper } from "./mapper/permission/module.mjs
|
|
|
11
22
|
import "./mapper/index.mjs";
|
|
12
23
|
import { IdentityProviderAccountManager } from "./account/module.mjs";
|
|
13
24
|
import "./account/index.mjs";
|
|
14
|
-
import { IdentityProviderLdapAuthenticator } from "./authentication/protocols/ldap/module.mjs";
|
|
15
|
-
import { IdentityProviderLdapCollectionAuthenticator } from "./authentication/protocols/ldap/collection.mjs";
|
|
16
|
-
import { IdentityProviderOAuth2Authenticator } from "./authentication/protocols/oauth2/module.mjs";
|
|
17
|
-
import { IdentityProviderOpenIDAuthenticator } from "./authentication/protocols/open-id/module.mjs";
|
|
18
|
-
import { IdentityProviderFacebookAuthenticator } from "./authentication/presets/facebook.mjs";
|
|
19
|
-
import { IdentityProviderGithubAuthenticator } from "./authentication/presets/github.mjs";
|
|
20
|
-
import { IdentityProviderGoogleAuthenticator } from "./authentication/presets/google.mjs";
|
|
21
|
-
import { IdentityProviderInstagramAuthenticator } from "./authentication/presets/instagram.mjs";
|
|
22
|
-
import { IdentityProviderPaypalAuthenticator } from "./authentication/presets/paypal.mjs";
|
|
23
25
|
import { IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN } from "./authentication/constants.mjs";
|
|
24
|
-
import { createIdentityProviderOAuth2Authenticator } from "./authentication/factory.mjs";
|
|
25
26
|
import "./authentication/index.mjs";
|
|
26
|
-
export { IDENTITY_PROVIDER_ACCOUNT_ALREADY_LINKED_ERROR_INSTANCE, IDENTITY_PROVIDER_ACCOUNT_LINK_TTL, IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN, IdentityProviderAccountAlreadyLinkedError, IdentityProviderAccountBaseMapper, IdentityProviderAccountManager, IdentityProviderAttributeMapper, IdentityProviderFacebookAuthenticator, IdentityProviderGithubAuthenticator, IdentityProviderGoogleAuthenticator, IdentityProviderInstagramAuthenticator, IdentityProviderLdapAuthenticator, IdentityProviderLdapCollectionAuthenticator, IdentityProviderMapperOperation, IdentityProviderOAuth2Authenticator, IdentityProviderOpenIDAuthenticator, IdentityProviderPaypalAuthenticator, IdentityProviderPermissionMapper, IdentityProviderRoleMapper, createIdentityProviderOAuth2Authenticator, isIdentityProviderAccountAlreadyLinkedError };
|
|
27
|
+
export { IDENTITY_PROVIDER_ACCOUNT_ALREADY_LINKED_ERROR_INSTANCE, IDENTITY_PROVIDER_ACCOUNT_LINK_TTL, IDENTITY_PROVIDER_ASSURANCE_ERROR_INSTANCE, IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN, IdentityProviderAccountAlreadyLinkedError, IdentityProviderAccountBaseMapper, IdentityProviderAccountManager, IdentityProviderAssuranceError, IdentityProviderAttributeMapper, IdentityProviderFacebookAuthenticator, IdentityProviderGithubAuthenticator, IdentityProviderGoogleAuthenticator, IdentityProviderInstagramAuthenticator, IdentityProviderLdapAuthenticator, IdentityProviderLdapCollectionAuthenticator, IdentityProviderMapperOperation, IdentityProviderOAuth2Authenticator, IdentityProviderOpenIDAuthenticator, IdentityProviderPaypalAuthenticator, IdentityProviderPermissionMapper, IdentityProviderRoleMapper, assertIdentityProviderAssurance, buildIdentityProviderAcrValues, createIdentityProviderOAuth2Authenticator, isIdentityProviderAccountAlreadyLinkedError, isIdentityProviderAssuranceError };
|
package/dist/core/index.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { UserCredentialsService } from "./authentication/credential/entities/use
|
|
|
7
7
|
import { BaseCredentialsAuthenticator } from "./authentication/base.mjs";
|
|
8
8
|
import { ClientAuthenticator } from "./authentication/entities/client/module.mjs";
|
|
9
9
|
import { UserAuthenticator } from "./authentication/entities/user/module.mjs";
|
|
10
|
-
import { EVENT_ACTOR_NAME_MAX_LENGTH, EVENT_LOG_RETENTION_DAYS_DEFAULT } from "./entities/event/constants.mjs";
|
|
10
|
+
import { EVENT_ACTOR_NAME_MAX_LENGTH, EVENT_LOG_RETENTION_DAYS_DEFAULT, EVENT_RETENTION_SWEEP_BATCH_SIZE } from "./entities/event/constants.mjs";
|
|
11
11
|
import { LoginThrottleService } from "./authentication/login-throttle/service.mjs";
|
|
12
12
|
import { CredentialsAuthenticator } from "./authentication/module.mjs";
|
|
13
13
|
import { SessionManager } from "./authentication/session/module.mjs";
|
|
@@ -113,7 +113,21 @@ import { toIdentityPolicyData } from "./identity/permission/identity-policy-data
|
|
|
113
113
|
import { OAuth2Authorization } from "./oauth2/authorization/module.mjs";
|
|
114
114
|
import { OAuth2EndSessionService } from "./oauth2/end-session/service.mjs";
|
|
115
115
|
import { OAuth2EndSessionRequestValidator } from "./oauth2/end-session/validator.mjs";
|
|
116
|
+
import { OAUTH2_FEDERATED_LOGIN_COOKIE, OAUTH2_FEDERATED_LOGIN_TTL } from "./oauth2/federated-login/constants.mjs";
|
|
117
|
+
import { IdentityProviderLdapAuthenticator } from "./identity/provider/authentication/protocols/ldap/module.mjs";
|
|
118
|
+
import { IdentityProviderLdapCollectionAuthenticator } from "./identity/provider/authentication/protocols/ldap/collection.mjs";
|
|
119
|
+
import { IDENTITY_PROVIDER_ASSURANCE_ERROR_INSTANCE, IdentityProviderAssuranceError, assertIdentityProviderAssurance, buildIdentityProviderAcrValues, isIdentityProviderAssuranceError } from "./identity/provider/authentication/protocols/oauth2/assurance.mjs";
|
|
120
|
+
import { IdentityProviderOAuth2Authenticator } from "./identity/provider/authentication/protocols/oauth2/module.mjs";
|
|
121
|
+
import { IdentityProviderOpenIDAuthenticator } from "./identity/provider/authentication/protocols/open-id/module.mjs";
|
|
122
|
+
import { IdentityProviderFacebookAuthenticator } from "./identity/provider/authentication/presets/facebook.mjs";
|
|
123
|
+
import { IdentityProviderGithubAuthenticator } from "./identity/provider/authentication/presets/github.mjs";
|
|
124
|
+
import { IdentityProviderGoogleAuthenticator } from "./identity/provider/authentication/presets/google.mjs";
|
|
125
|
+
import { IdentityProviderInstagramAuthenticator } from "./identity/provider/authentication/presets/instagram.mjs";
|
|
126
|
+
import { IdentityProviderPaypalAuthenticator } from "./identity/provider/authentication/presets/paypal.mjs";
|
|
127
|
+
import { createIdentityProviderOAuth2Authenticator } from "./identity/provider/authentication/factory.mjs";
|
|
116
128
|
import { buildOAuth2BearerTokenResponse } from "./oauth2/response/bearer.mjs";
|
|
129
|
+
import { OAuth2FederatedLoginRefusal } from "./oauth2/federated-login/types.mjs";
|
|
130
|
+
import { OAuth2FederatedLoginService } from "./oauth2/federated-login/module.mjs";
|
|
117
131
|
import { OAuth2AuthorizeGrant } from "./oauth2/grant-types/authorize.mjs";
|
|
118
132
|
import { ClientCredentialsGrant } from "./oauth2/grant-types/client-credentials.mjs";
|
|
119
133
|
import { IdentityGrantType } from "./oauth2/grant-types/identity.mjs";
|
|
@@ -144,17 +158,7 @@ import { IdentityProviderAttributeMapper } from "./identity/provider/mapper/attr
|
|
|
144
158
|
import { IdentityProviderRoleMapper } from "./identity/provider/mapper/role/module.mjs";
|
|
145
159
|
import { IdentityProviderPermissionMapper } from "./identity/provider/mapper/permission/module.mjs";
|
|
146
160
|
import { IdentityProviderAccountManager } from "./identity/provider/account/module.mjs";
|
|
147
|
-
import { IdentityProviderLdapAuthenticator } from "./identity/provider/authentication/protocols/ldap/module.mjs";
|
|
148
|
-
import { IdentityProviderLdapCollectionAuthenticator } from "./identity/provider/authentication/protocols/ldap/collection.mjs";
|
|
149
|
-
import { IdentityProviderOAuth2Authenticator } from "./identity/provider/authentication/protocols/oauth2/module.mjs";
|
|
150
|
-
import { IdentityProviderOpenIDAuthenticator } from "./identity/provider/authentication/protocols/open-id/module.mjs";
|
|
151
|
-
import { IdentityProviderFacebookAuthenticator } from "./identity/provider/authentication/presets/facebook.mjs";
|
|
152
|
-
import { IdentityProviderGithubAuthenticator } from "./identity/provider/authentication/presets/github.mjs";
|
|
153
|
-
import { IdentityProviderGoogleAuthenticator } from "./identity/provider/authentication/presets/google.mjs";
|
|
154
|
-
import { IdentityProviderInstagramAuthenticator } from "./identity/provider/authentication/presets/instagram.mjs";
|
|
155
|
-
import { IdentityProviderPaypalAuthenticator } from "./identity/provider/authentication/presets/paypal.mjs";
|
|
156
161
|
import { IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN } from "./identity/provider/authentication/constants.mjs";
|
|
157
|
-
import { createIdentityProviderOAuth2Authenticator } from "./identity/provider/authentication/factory.mjs";
|
|
158
162
|
import { RegistrationService } from "./identity/registration/service.mjs";
|
|
159
163
|
import { IdentityResolver } from "./identity/resolver/module.mjs";
|
|
160
164
|
import { IdentityRoleProvider } from "./identity/role/module.mjs";
|
|
@@ -188,4 +192,4 @@ import { UserProvisioningSynchronizer } from "./provisioning/synchronizer/user/m
|
|
|
188
192
|
import { WildcardRealmProvisioner, expandWildcardRealmEntry, extractWildcardRealmEntry } from "./provisioning/wildcard/module.mjs";
|
|
189
193
|
import "./provisioning/index.mjs";
|
|
190
194
|
import "./security/index.mjs";
|
|
191
|
-
export { BaseCredentialsAuthenticator, CLIENT_READ_PERMISSIONS, CONSENT_FILTER_KEYS, CONSENT_SCOPE_MAX_LENGTH, ClientAuthenticator, ClientCertificateValidator, ClientCredentialsGrant, ClientCredentialsService, ClientPermissionService, ClientProvisioningSynchronizer, ClientProvisioningValidator, ClientRoleService, ClientScopeService, ClientService, ConsentService, CredentialsAuthenticator, EVENT_ACTOR_NAME_MAX_LENGTH, EVENT_DIFF_SECRET_KEY_REGEX, EVENT_DIFF_VALUE_MAX_LENGTH, EVENT_LOG_RETENTION_DAYS_DEFAULT, EntityEventHandler, EventService, GraphProvisioningSynchronizer, IDENTITY_PROVIDER_ACCOUNT_ALREADY_LINKED_ERROR_INSTANCE, IDENTITY_PROVIDER_ACCOUNT_FILTER_KEYS, IDENTITY_PROVIDER_ACCOUNT_LINK_TTL, IDENTITY_PROVIDER_ACCOUNT_UNLINK_BLOCKED_ERROR_INSTANCE, IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN, IdentityGrantType, IdentityPermissionProvider, IdentityProviderAccountAlreadyLinkedError, IdentityProviderAccountBaseMapper, IdentityProviderAccountManager, IdentityProviderAccountService, IdentityProviderAccountUnlinkBlockedError, IdentityProviderAttributeMapper, IdentityProviderFacebookAuthenticator, IdentityProviderGithubAuthenticator, IdentityProviderGoogleAuthenticator, IdentityProviderInstagramAuthenticator, IdentityProviderLdapAuthenticator, IdentityProviderLdapCollectionAuthenticator, IdentityProviderMapperOperation, IdentityProviderOAuth2Authenticator, IdentityProviderOpenIDAuthenticator, IdentityProviderPaypalAuthenticator, IdentityProviderPermissionMapper, IdentityProviderRoleMapper, IdentityProviderRoleMappingService, IdentityResolver, IdentityRoleProvider, KEY_CERTIFICATE_ERROR_INSTANCE, KeyCertificateError, KeyProvisioner, KeyService, LoginThrottleService, MAIL_TEMPLATE_REGISTRY, MailTemplateName, MailTemplateRenderer, NoopAuthFlowMetrics, OAuth2AccessPolicyEvaluator, OAuth2AccessTokenIssuer, OAuth2Authorization, OAuth2AuthorizationCodeIssuer, OAuth2AuthorizationCodeRequestValidator, OAuth2AuthorizationCodeRequestVerifier, OAuth2AuthorizationCodeVerifier, OAuth2AuthorizationStateManager, OAuth2AuthorizeGrant, OAuth2ClientAuthenticator, OAuth2EndSessionRequestValidator, OAuth2EndSessionService, OAuth2MfaLoginService, OAuth2MfaTokenIssuer, OAuth2OpenIDClaimsBuilder, OAuth2OpenIDTokenIssuer, OAuth2RefreshTokenGrant, OAuth2RefreshTokenIssuer, OAuth2ScopeAttributesResolver, OAuth2TokenRevoker, OAuth2TokenSigner, OAuth2TokenVerifier, PASSWORD_RESET_EXPIRES_IN_MINUTES, PasswordGrantType, PasswordRecoveryService, PermissionBindingPolicyEvaluator, PermissionCheckerService, PermissionPolicyService, PermissionProvisioningRelationsValidator, PermissionProvisioningSynchronizer, PermissionProvisioningValidator, PermissionService, PolicyCheckerService, PolicyEngine, PolicyProvisioningSynchronizer, PolicyProvisioningValidator, PolicyService, ProvisioningEntityResolver, ProvisioningEntityStrategyType, ProvisioningJunctionSynchronizer, ProvisioningStrategyValidator, REALM_CIPHER_BLOB_ERROR_INSTANCE, REALM_CIPHER_BLOB_VERSION, REALM_WILDCARD_NAME, RECORD_QUERY_PARAMETERS, RealmCipher, RealmCipherBlobError, RealmProvisioningSynchronizer, RealmProvisioningValidator, RealmService, RealmWildcardProvisioningValidator, RegistrationService, RoleAttributeService, RolePermissionService, RoleProvisioningSynchronizer, RoleProvisioningValidator, RoleService, RootProvisioningValidator, SESSION_FILTER_KEYS, SESSION_TOKEN_FILTER_KEYS, SESSION_TOKEN_RELATION_MISSING_ERROR_INSTANCE, SYSTEM_CLIENT_DEFINITIONS, SYSTEM_CLIENT_SCOPE_NAMES, ScopeProvisioningSynchronizer, ScopeProvisioningValidator, ScopeService, SessionManager, SessionService, SessionTokenRelationMissingError, SessionTokenService, SystemClientProvisioner, TrustAnchorService, USER_AUTHENTICATOR_ATTEMPT_CACHE_PREFIX, USER_AUTHENTICATOR_ATTEMPT_LOCK_FACTOR, USER_AUTHENTICATOR_ATTEMPT_LOCK_MAX, USER_AUTHENTICATOR_ATTEMPT_WINDOW, USER_AUTHENTICATOR_EMAIL_CODE_CACHE_PREFIX, USER_AUTHENTICATOR_EMAIL_CODE_EXPIRES_IN_MINUTES, USER_AUTHENTICATOR_EMAIL_CODE_LENGTH, USER_AUTHENTICATOR_EMAIL_SEND_CACHE_PREFIX, USER_AUTHENTICATOR_EMAIL_SEND_COOLDOWN, USER_AUTHENTICATOR_FILTER_KEYS, USER_AUTHENTICATOR_RECOVERY_CODE_COUNT, USER_AUTHENTICATOR_THROTTLE_CACHE_PREFIX, USER_AUTHENTICATOR_TOTP_ALGORITHM, USER_AUTHENTICATOR_TOTP_DIGITS, USER_AUTHENTICATOR_TOTP_PERIOD, USER_AUTHENTICATOR_VERIFY_LOCK_CACHE_PREFIX, USER_AUTHENTICATOR_VERIFY_LOCK_RENEW_INTERVAL, USER_AUTHENTICATOR_VERIFY_LOCK_TTL, USER_AUTHENTICATOR_WEBAUTHN_AUTH_CACHE_PREFIX, USER_AUTHENTICATOR_WEBAUTHN_CHALLENGE_WINDOW, USER_AUTHENTICATOR_WEBAUTHN_REG_CACHE_PREFIX, UserAttributeService, UserAuthenticator, UserAuthenticatorService, UserCredentialsService, UserPermissionService, UserProvisioningSynchronizer, UserProvisioningValidator, UserRoleService, UserService, WRAPPED_KEY_MATERIAL_PREFIX, WildcardRealmProvisioner, appendQueryConditions, applyJunctionCreateGrant, assertCertificateMatchesKey, assertClientCertificateEvidenceValidForBinding, assertClientGrantAllowed, base64URLEncode, buildCertificateJwkFields, buildClientCertificateThumbprint, buildEntityDiff, buildJunctionUpdateData, buildOAuth2BearerTokenResponse, buildOAuth2CodeChallenge, buildOAuth2TokenHash, buildProvisioningEntityKey, buildSystemClientAttributes, buildWebauthnAuthenticationOptions, buildWebauthnRegistrationOptions, buildX5c, buildX5tS256, clientPermissionSchema, clientRoleSchema, clientSchema, clientScopeSchema, consentSchema, createIdentityProviderOAuth2Authenticator, createProvisioningEntitiesValidator, createRelationsReadGate, decodeQuery, defineMailTemplate, deriveAmrAcr, describeQuerySchema, eventSchema, expandWildcardRealmEntry, extractWildcardRealmEntry, generateNumericCode, generateOAuth2CodeVerifier, generateRecoveryCode, guessUserAuthenticatorKindByResponse, identityProviderAccountSchema, identityProviderRoleMappingSchema, identityProviderSchema, isIdentityProviderAccountAlreadyLinkedError, isIdentityProviderAccountUnlinkBlockedError, isRealmCipherBlobError, isSessionTokenRelationMissingError, isWrappedKeyMaterial, keySchema, mergeProvisioningEntities, mergeProvisioningEntity, normalizeEntityProvisioningStrategy, parseCertificateChain, parseClientCertificateChain, permissionPolicySchema, permissionSchema, policySchema, queryCodec, realmSchema, roleAttributeSchema, rolePermissionSchema, roleSchema, sanitizeEventData, schemaRegistry, schemas, scopeSchema, sessionSchema, sessionTokenSchema, toIdentityPolicyData, trustAnchorSchema, unwrapKeyMaterial, userAttributeSchema, userAuthenticatorSchema, userPermissionSchema, userRoleSchema, userSchema, verifyWebauthnAuthentication, verifyWebauthnRegistration, wrapKeyMaterial };
|
|
195
|
+
export { BaseCredentialsAuthenticator, CLIENT_READ_PERMISSIONS, CONSENT_FILTER_KEYS, CONSENT_SCOPE_MAX_LENGTH, ClientAuthenticator, ClientCertificateValidator, ClientCredentialsGrant, ClientCredentialsService, ClientPermissionService, ClientProvisioningSynchronizer, ClientProvisioningValidator, ClientRoleService, ClientScopeService, ClientService, ConsentService, CredentialsAuthenticator, EVENT_ACTOR_NAME_MAX_LENGTH, EVENT_DIFF_SECRET_KEY_REGEX, EVENT_DIFF_VALUE_MAX_LENGTH, EVENT_LOG_RETENTION_DAYS_DEFAULT, EVENT_RETENTION_SWEEP_BATCH_SIZE, EntityEventHandler, EventService, GraphProvisioningSynchronizer, IDENTITY_PROVIDER_ACCOUNT_ALREADY_LINKED_ERROR_INSTANCE, IDENTITY_PROVIDER_ACCOUNT_FILTER_KEYS, IDENTITY_PROVIDER_ACCOUNT_LINK_TTL, IDENTITY_PROVIDER_ACCOUNT_UNLINK_BLOCKED_ERROR_INSTANCE, IDENTITY_PROVIDER_ASSURANCE_ERROR_INSTANCE, IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN, IdentityGrantType, IdentityPermissionProvider, IdentityProviderAccountAlreadyLinkedError, IdentityProviderAccountBaseMapper, IdentityProviderAccountManager, IdentityProviderAccountService, IdentityProviderAccountUnlinkBlockedError, IdentityProviderAssuranceError, IdentityProviderAttributeMapper, IdentityProviderFacebookAuthenticator, IdentityProviderGithubAuthenticator, IdentityProviderGoogleAuthenticator, IdentityProviderInstagramAuthenticator, IdentityProviderLdapAuthenticator, IdentityProviderLdapCollectionAuthenticator, IdentityProviderMapperOperation, IdentityProviderOAuth2Authenticator, IdentityProviderOpenIDAuthenticator, IdentityProviderPaypalAuthenticator, IdentityProviderPermissionMapper, IdentityProviderRoleMapper, IdentityProviderRoleMappingService, IdentityResolver, IdentityRoleProvider, KEY_CERTIFICATE_ERROR_INSTANCE, KeyCertificateError, KeyProvisioner, KeyService, LoginThrottleService, MAIL_TEMPLATE_REGISTRY, MailTemplateName, MailTemplateRenderer, NoopAuthFlowMetrics, OAUTH2_FEDERATED_LOGIN_COOKIE, OAUTH2_FEDERATED_LOGIN_TTL, OAuth2AccessPolicyEvaluator, OAuth2AccessTokenIssuer, OAuth2Authorization, OAuth2AuthorizationCodeIssuer, OAuth2AuthorizationCodeRequestValidator, OAuth2AuthorizationCodeRequestVerifier, OAuth2AuthorizationCodeVerifier, OAuth2AuthorizationStateManager, OAuth2AuthorizeGrant, OAuth2ClientAuthenticator, OAuth2EndSessionRequestValidator, OAuth2EndSessionService, OAuth2FederatedLoginRefusal, OAuth2FederatedLoginService, OAuth2MfaLoginService, OAuth2MfaTokenIssuer, OAuth2OpenIDClaimsBuilder, OAuth2OpenIDTokenIssuer, OAuth2RefreshTokenGrant, OAuth2RefreshTokenIssuer, OAuth2ScopeAttributesResolver, OAuth2TokenRevoker, OAuth2TokenSigner, OAuth2TokenVerifier, PASSWORD_RESET_EXPIRES_IN_MINUTES, PasswordGrantType, PasswordRecoveryService, PermissionBindingPolicyEvaluator, PermissionCheckerService, PermissionPolicyService, PermissionProvisioningRelationsValidator, PermissionProvisioningSynchronizer, PermissionProvisioningValidator, PermissionService, PolicyCheckerService, PolicyEngine, PolicyProvisioningSynchronizer, PolicyProvisioningValidator, PolicyService, ProvisioningEntityResolver, ProvisioningEntityStrategyType, ProvisioningJunctionSynchronizer, ProvisioningStrategyValidator, REALM_CIPHER_BLOB_ERROR_INSTANCE, REALM_CIPHER_BLOB_VERSION, REALM_WILDCARD_NAME, RECORD_QUERY_PARAMETERS, RealmCipher, RealmCipherBlobError, RealmProvisioningSynchronizer, RealmProvisioningValidator, RealmService, RealmWildcardProvisioningValidator, RegistrationService, RoleAttributeService, RolePermissionService, RoleProvisioningSynchronizer, RoleProvisioningValidator, RoleService, RootProvisioningValidator, SESSION_FILTER_KEYS, SESSION_TOKEN_FILTER_KEYS, SESSION_TOKEN_RELATION_MISSING_ERROR_INSTANCE, SYSTEM_CLIENT_DEFINITIONS, SYSTEM_CLIENT_SCOPE_NAMES, ScopeProvisioningSynchronizer, ScopeProvisioningValidator, ScopeService, SessionManager, SessionService, SessionTokenRelationMissingError, SessionTokenService, SystemClientProvisioner, TrustAnchorService, USER_AUTHENTICATOR_ATTEMPT_CACHE_PREFIX, USER_AUTHENTICATOR_ATTEMPT_LOCK_FACTOR, USER_AUTHENTICATOR_ATTEMPT_LOCK_MAX, USER_AUTHENTICATOR_ATTEMPT_WINDOW, USER_AUTHENTICATOR_EMAIL_CODE_CACHE_PREFIX, USER_AUTHENTICATOR_EMAIL_CODE_EXPIRES_IN_MINUTES, USER_AUTHENTICATOR_EMAIL_CODE_LENGTH, USER_AUTHENTICATOR_EMAIL_SEND_CACHE_PREFIX, USER_AUTHENTICATOR_EMAIL_SEND_COOLDOWN, USER_AUTHENTICATOR_FILTER_KEYS, USER_AUTHENTICATOR_RECOVERY_CODE_COUNT, USER_AUTHENTICATOR_THROTTLE_CACHE_PREFIX, USER_AUTHENTICATOR_TOTP_ALGORITHM, USER_AUTHENTICATOR_TOTP_DIGITS, USER_AUTHENTICATOR_TOTP_PERIOD, USER_AUTHENTICATOR_VERIFY_LOCK_CACHE_PREFIX, USER_AUTHENTICATOR_VERIFY_LOCK_RENEW_INTERVAL, USER_AUTHENTICATOR_VERIFY_LOCK_TTL, USER_AUTHENTICATOR_WEBAUTHN_AUTH_CACHE_PREFIX, USER_AUTHENTICATOR_WEBAUTHN_CHALLENGE_WINDOW, USER_AUTHENTICATOR_WEBAUTHN_REG_CACHE_PREFIX, UserAttributeService, UserAuthenticator, UserAuthenticatorService, UserCredentialsService, UserPermissionService, UserProvisioningSynchronizer, UserProvisioningValidator, UserRoleService, UserService, WRAPPED_KEY_MATERIAL_PREFIX, WildcardRealmProvisioner, appendQueryConditions, applyJunctionCreateGrant, assertCertificateMatchesKey, assertClientCertificateEvidenceValidForBinding, assertClientGrantAllowed, assertIdentityProviderAssurance, base64URLEncode, buildCertificateJwkFields, buildClientCertificateThumbprint, buildEntityDiff, buildIdentityProviderAcrValues, buildJunctionUpdateData, buildOAuth2BearerTokenResponse, buildOAuth2CodeChallenge, buildOAuth2TokenHash, buildProvisioningEntityKey, buildSystemClientAttributes, buildWebauthnAuthenticationOptions, buildWebauthnRegistrationOptions, buildX5c, buildX5tS256, clientPermissionSchema, clientRoleSchema, clientSchema, clientScopeSchema, consentSchema, createIdentityProviderOAuth2Authenticator, createProvisioningEntitiesValidator, createRelationsReadGate, decodeQuery, defineMailTemplate, deriveAmrAcr, describeQuerySchema, eventSchema, expandWildcardRealmEntry, extractWildcardRealmEntry, generateNumericCode, generateOAuth2CodeVerifier, generateRecoveryCode, guessUserAuthenticatorKindByResponse, identityProviderAccountSchema, identityProviderRoleMappingSchema, identityProviderSchema, isIdentityProviderAccountAlreadyLinkedError, isIdentityProviderAccountUnlinkBlockedError, isIdentityProviderAssuranceError, isRealmCipherBlobError, isSessionTokenRelationMissingError, isWrappedKeyMaterial, keySchema, mergeProvisioningEntities, mergeProvisioningEntity, normalizeEntityProvisioningStrategy, parseCertificateChain, parseClientCertificateChain, permissionPolicySchema, permissionSchema, policySchema, queryCodec, realmSchema, roleAttributeSchema, rolePermissionSchema, roleSchema, sanitizeEventData, schemaRegistry, schemas, scopeSchema, sessionSchema, sessionTokenSchema, toIdentityPolicyData, trustAnchorSchema, unwrapKeyMaterial, userAttributeSchema, userAuthenticatorSchema, userPermissionSchema, userRoleSchema, userSchema, verifyWebauthnAuthentication, verifyWebauthnRegistration, wrapKeyMaterial };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../../../src/core/oauth2/authorization/code-request/verifier/module.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,kBAAkB,CAAC;AAWvE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAExE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,KAAK,EACR,uCAAuC,EACvC,gDAAgD,EAChD,6CAA6C,EAChD,MAAM,YAAY,CAAC;AAEpB,qBAAa,sCAAuC,YAAW,uCAAuC;IAClG,SAAS,CAAC,gBAAgB,EAAE,uBAAuB,CAAC;IAEpD,SAAS,CAAC,eAAe,EAAE,sBAAsB,CAAC;gBAEtC,GAAG,EAAE,6CAA6C;IAK9D;;;OAGG;IACG,MAAM,CACR,IAAI,EAAE,8BAA8B,GACpC,OAAO,CAAC,gDAAgD,CAAC;
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../../../src/core/oauth2/authorization/code-request/verifier/module.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,kBAAkB,CAAC;AAWvE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAExE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,KAAK,EACR,uCAAuC,EACvC,gDAAgD,EAChD,6CAA6C,EAChD,MAAM,YAAY,CAAC;AAEpB,qBAAa,sCAAuC,YAAW,uCAAuC;IAClG,SAAS,CAAC,gBAAgB,EAAE,uBAAuB,CAAC;IAEpD,SAAS,CAAC,eAAe,EAAE,sBAAsB,CAAC;gBAEtC,GAAG,EAAE,6CAA6C;IAK9D;;;OAGG;IACG,MAAM,CACR,IAAI,EAAE,8BAA8B,GACpC,OAAO,CAAC,gDAAgD,CAAC;CA0HhE"}
|
|
@@ -3,7 +3,7 @@ import "node:url";
|
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { assertClientGrantAllowed } from "../../../client/grant-type.mjs";
|
|
5
5
|
import "../../../client/index.mjs";
|
|
6
|
-
import { isSimpleURLMatch, isUUID } from "@authup/kit";
|
|
6
|
+
import { isSafeRedirectURLScheme, isSimpleURLMatch, isUUID } from "@authup/kit";
|
|
7
7
|
import { ScopeName, isClientPublic } from "@authup/core-kit";
|
|
8
8
|
import { OAuth2ClientError, OAuth2GrantError, OAuth2RequestError, OAuth2ScopeError, OAuth2TokenGrant, hasOAuth2Scopes } from "@authup/specs";
|
|
9
9
|
//#region src/core/oauth2/authorization/code-request/verifier/module.ts
|
|
@@ -36,6 +36,12 @@ var OAuth2AuthorizationCodeRequestVerifier = class {
|
|
|
36
36
|
} else data.scope = scopeNames.join(" ");
|
|
37
37
|
let redirectUriVerified = false;
|
|
38
38
|
if (data.redirect_uri) {
|
|
39
|
+
let url;
|
|
40
|
+
try {
|
|
41
|
+
url = new URL(data.redirect_uri);
|
|
42
|
+
} catch {}
|
|
43
|
+
if (url && (url.username || url.password)) throw OAuth2RequestError.malformed("The redirect_uri must not carry userinfo.");
|
|
44
|
+
if (url && !isSafeRedirectURLScheme(data.redirect_uri)) throw OAuth2RequestError.malformed("The redirect_uri scheme is not allowed.");
|
|
39
45
|
const redirectUris = client.redirectUri.split(",");
|
|
40
46
|
if (!isSimpleURLMatch(data.redirect_uri, redirectUris)) throw OAuth2GrantError.redirectUriMismatch();
|
|
41
47
|
redirectUriVerified = true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.mjs","names":["ScopeName","isClientPublic","isSimpleURLMatch","isUUID","OAuth2ClientError","OAuth2GrantError","OAuth2RequestError","OAuth2ScopeError","OAuth2TokenGrant","hasOAuth2Scopes","assertClientGrantAllowed","OAuth2AuthorizationCodeRequestVerifier","clientRepository","scopeRepository","ctx","verify","data","client_id","invalid","realm_id","malformed","client","findOneByIdOrName","active","inactive","AUTHORIZATION_CODE","redirectUri","redirectUriMismatch","code_challenge","state","id","realmId","scopes","findByClientId","scopeNames","map","scope","name","GLOBAL","insufficient","join","redirectUriVerified","redirect_uri","redirectUris","split"],"sources":["../../../../../../src/core/oauth2/authorization/code-request/verifier/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2025.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { OAuth2AuthorizationCodeRequest } from '@authup/core-kit';\nimport { ScopeName, isClientPublic } from '@authup/core-kit';\nimport { isSimpleURLMatch, isUUID } from '@authup/kit';\nimport {\n OAuth2ClientError,\n OAuth2GrantError,\n OAuth2RequestError,\n OAuth2ScopeError,\n OAuth2TokenGrant,\n hasOAuth2Scopes,\n} from '@authup/specs';\nimport type { IOAuth2ClientRepository } from '../../../client/index.ts';\nimport { assertClientGrantAllowed } from '../../../client/index.ts';\nimport type { IOAuth2ScopeRepository } from '../../../scope/index.ts';\nimport type {\n IOAuth2AuthorizationCodeRequestVerifier,\n OAuth2AuthorizationCodeRequestVerificationResult,\n OAuth2AuthorizationCodeRequestVerifierContext,\n} from './types.ts';\n\nexport class OAuth2AuthorizationCodeRequestVerifier implements IOAuth2AuthorizationCodeRequestVerifier {\n protected clientRepository: IOAuth2ClientRepository;\n\n protected scopeRepository: IOAuth2ScopeRepository;\n\n constructor(ctx: OAuth2AuthorizationCodeRequestVerifierContext) {\n this.clientRepository = ctx.clientRepository;\n this.scopeRepository = ctx.scopeRepository;\n }\n\n /**\n * Verify validated authorization code request.\n * @param data\n */\n async verify(\n data: OAuth2AuthorizationCodeRequest,\n ) : Promise<OAuth2AuthorizationCodeRequestVerificationResult> {\n if (!data.client_id) {\n throw OAuth2ClientError.invalid();\n }\n\n // A name-identified client needs a realm hint to resolve deterministically\n // — client names are only unique per realm (every realm carries the\n // same-named system clients, and a wildcard-provisioned client exists in\n // every realm), so a bare name would bind to an arbitrary realm's client\n // (and, post realm-gate, produce a confusing mismatch against a random\n // realm). Require the hint.\n if (!isUUID(data.client_id) && !data.realm_id) {\n throw OAuth2RequestError.malformed('A realm is required to resolve a client by name.');\n }\n\n const client = await this.clientRepository.findOneByIdOrName(data.client_id, data.realm_id);\n if (!client) {\n throw OAuth2ClientError.invalid();\n }\n\n if (!client.active) {\n throw OAuth2ClientError.inactive();\n }\n\n // A non-null grant_types allowlist must cover the code flow — an RP\n // misconfiguration fails at the front door, not at code redemption.\n assertClientGrantAllowed(client, OAuth2TokenGrant.AUTHORIZATION_CODE);\n\n // OAuth 2.1 posture: a client with no registered redirect_uri pattern\n // can never be matched — reject outright instead of trusting whatever\n // redirect_uri the request carries (the server would otherwise issue a\n // code to an attacker-supplied URI).\n if (!client.redirectUri) {\n throw OAuth2GrantError.redirectUriMismatch();\n }\n\n // Public clients MUST use PKCE (RFC 7636 §4.4.1, OAuth 2.1). Without\n // PKCE a public client's code flow has no second factor — anyone who\n // intercepts the redirect can redeem the code at /token. The code flow\n // is the only supported response type, so this holds unconditionally.\n if (isClientPublic(client) && !data.code_challenge) {\n throw OAuth2RequestError.malformed('PKCE code_challenge is required for public clients.');\n }\n\n // Public clients SHOULD include state to bind the redirect to the\n // initiating session and prevent CSRF (RFC 6749 §10.12). Confidential\n // clients are exempt because the /token exchange already authenticates\n // them via client_secret.\n if (isClientPublic(client) && !data.state) {\n throw OAuth2RequestError.malformed('state is required for public clients in the code flow.');\n }\n\n data.client_id = client.id;\n data.realm_id = client.realmId;\n\n const scopes = await this.scopeRepository.findByClientId(client.id);\n const scopeNames = scopes.map((scope) => scope.name);\n if (data.scope) {\n if (\n !hasOAuth2Scopes(scopeNames, data.scope) &&\n !hasOAuth2Scopes(data.scope, ScopeName.GLOBAL)\n ) {\n throw OAuth2ScopeError.insufficient();\n }\n } else {\n data.scope = scopeNames.join(' ');\n }\n\n // Verified only when the request's redirect_uri matched a registered\n // pattern (pattern-less clients were rejected above). A request without\n // a redirect_uri (e.g. the GET page render) stays unverified so\n // consumers never auto-redirect without a match.\n //\n // Set from the match itself, never from mere presence: consumers make\n // redirect decisions on this flag, so it must not survive a refactor\n // that turns the throw below into a soft branch.\n let redirectUriVerified = false;\n if (data.redirect_uri) {\n const redirectUris = client.redirectUri.split(',');\n\n // isSimpleURLMatch, never isSimpleMatch: the raw matcher treats `/`\n // as its only boundary, so a `*` in a registered pattern's host\n // absorbs a `?`, `#` or `\\` and the pattern's remaining host\n // literal lands in the query of a foreign origin. The code would\n // then be issued to that origin.\n if (!isSimpleURLMatch(data.redirect_uri, redirectUris)) {\n throw OAuth2GrantError.redirectUriMismatch();\n }\n\n redirectUriVerified = true;\n }\n\n return {\n data,\n client,\n scopes,\n redirectUriVerified,\n };\n }\n}\n"],"mappings":";;;;;;;;;AA2BA,
|
|
1
|
+
{"version":3,"file":"module.mjs","names":["ScopeName","isClientPublic","isSafeRedirectURLScheme","isSimpleURLMatch","isUUID","OAuth2ClientError","OAuth2GrantError","OAuth2RequestError","OAuth2ScopeError","OAuth2TokenGrant","hasOAuth2Scopes","assertClientGrantAllowed","OAuth2AuthorizationCodeRequestVerifier","clientRepository","scopeRepository","ctx","verify","data","client_id","invalid","realm_id","malformed","client","findOneByIdOrName","active","inactive","AUTHORIZATION_CODE","redirectUri","redirectUriMismatch","code_challenge","state","id","realmId","scopes","findByClientId","scopeNames","map","scope","name","GLOBAL","insufficient","join","redirectUriVerified","redirect_uri","url","URL","username","password","redirectUris","split"],"sources":["../../../../../../src/core/oauth2/authorization/code-request/verifier/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2025.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { OAuth2AuthorizationCodeRequest } from '@authup/core-kit';\nimport { ScopeName, isClientPublic } from '@authup/core-kit';\nimport { isSafeRedirectURLScheme, isSimpleURLMatch, isUUID } from '@authup/kit';\nimport {\n OAuth2ClientError,\n OAuth2GrantError,\n OAuth2RequestError,\n OAuth2ScopeError,\n OAuth2TokenGrant,\n hasOAuth2Scopes,\n} from '@authup/specs';\nimport type { IOAuth2ClientRepository } from '../../../client/index.ts';\nimport { assertClientGrantAllowed } from '../../../client/index.ts';\nimport type { IOAuth2ScopeRepository } from '../../../scope/index.ts';\nimport type {\n IOAuth2AuthorizationCodeRequestVerifier,\n OAuth2AuthorizationCodeRequestVerificationResult,\n OAuth2AuthorizationCodeRequestVerifierContext,\n} from './types.ts';\n\nexport class OAuth2AuthorizationCodeRequestVerifier implements IOAuth2AuthorizationCodeRequestVerifier {\n protected clientRepository: IOAuth2ClientRepository;\n\n protected scopeRepository: IOAuth2ScopeRepository;\n\n constructor(ctx: OAuth2AuthorizationCodeRequestVerifierContext) {\n this.clientRepository = ctx.clientRepository;\n this.scopeRepository = ctx.scopeRepository;\n }\n\n /**\n * Verify validated authorization code request.\n * @param data\n */\n async verify(\n data: OAuth2AuthorizationCodeRequest,\n ) : Promise<OAuth2AuthorizationCodeRequestVerificationResult> {\n if (!data.client_id) {\n throw OAuth2ClientError.invalid();\n }\n\n // A name-identified client needs a realm hint to resolve deterministically\n // — client names are only unique per realm (every realm carries the\n // same-named system clients, and a wildcard-provisioned client exists in\n // every realm), so a bare name would bind to an arbitrary realm's client\n // (and, post realm-gate, produce a confusing mismatch against a random\n // realm). Require the hint.\n if (!isUUID(data.client_id) && !data.realm_id) {\n throw OAuth2RequestError.malformed('A realm is required to resolve a client by name.');\n }\n\n const client = await this.clientRepository.findOneByIdOrName(data.client_id, data.realm_id);\n if (!client) {\n throw OAuth2ClientError.invalid();\n }\n\n if (!client.active) {\n throw OAuth2ClientError.inactive();\n }\n\n // A non-null grant_types allowlist must cover the code flow — an RP\n // misconfiguration fails at the front door, not at code redemption.\n assertClientGrantAllowed(client, OAuth2TokenGrant.AUTHORIZATION_CODE);\n\n // OAuth 2.1 posture: a client with no registered redirect_uri pattern\n // can never be matched — reject outright instead of trusting whatever\n // redirect_uri the request carries (the server would otherwise issue a\n // code to an attacker-supplied URI).\n if (!client.redirectUri) {\n throw OAuth2GrantError.redirectUriMismatch();\n }\n\n // Public clients MUST use PKCE (RFC 7636 §4.4.1, OAuth 2.1). Without\n // PKCE a public client's code flow has no second factor — anyone who\n // intercepts the redirect can redeem the code at /token. The code flow\n // is the only supported response type, so this holds unconditionally.\n if (isClientPublic(client) && !data.code_challenge) {\n throw OAuth2RequestError.malformed('PKCE code_challenge is required for public clients.');\n }\n\n // Public clients SHOULD include state to bind the redirect to the\n // initiating session and prevent CSRF (RFC 6749 §10.12). Confidential\n // clients are exempt because the /token exchange already authenticates\n // them via client_secret.\n if (isClientPublic(client) && !data.state) {\n throw OAuth2RequestError.malformed('state is required for public clients in the code flow.');\n }\n\n data.client_id = client.id;\n data.realm_id = client.realmId;\n\n const scopes = await this.scopeRepository.findByClientId(client.id);\n const scopeNames = scopes.map((scope) => scope.name);\n if (data.scope) {\n if (\n !hasOAuth2Scopes(scopeNames, data.scope) &&\n !hasOAuth2Scopes(data.scope, ScopeName.GLOBAL)\n ) {\n throw OAuth2ScopeError.insufficient();\n }\n } else {\n data.scope = scopeNames.join(' ');\n }\n\n // Verified only when the request's redirect_uri matched a registered\n // pattern (pattern-less clients were rejected above). A request without\n // a redirect_uri (e.g. the GET page render) stays unverified so\n // consumers never auto-redirect without a match.\n //\n // Set from the match itself, never from mere presence: consumers make\n // redirect decisions on this flag, so it must not survive a refactor\n // that turns the throw below into a soft branch.\n let redirectUriVerified = false;\n if (data.redirect_uri) {\n // The matcher canonicalizes the value, which drops userinfo, while\n // the redirect is built from the raw string: `https://u:p@app/cb`\n // matched `https://app/**` and the credential blob then rode the\n // Location header. Refuse it, so the matched value is the\n // navigated value. An unparsable value falls through to the\n // matcher, which rejects it.\n let url : URL | undefined;\n try {\n url = new URL(data.redirect_uri);\n } catch {\n // handled by the matcher below\n }\n if (url && (url.username || url.password)) {\n throw OAuth2RequestError.malformed('The redirect_uri must not carry userinfo.');\n }\n\n // A non-http(s) target is matched verbatim and later navigated\n // client-side (the federated callback's interstitial), so a\n // script-capable scheme would run on the IdP origin.\n if (url && !isSafeRedirectURLScheme(data.redirect_uri)) {\n throw OAuth2RequestError.malformed('The redirect_uri scheme is not allowed.');\n }\n\n const redirectUris = client.redirectUri.split(',');\n\n // isSimpleURLMatch, never isSimpleMatch: the raw matcher treats `/`\n // as its only boundary, so a `*` in a registered pattern's host\n // absorbs a `?`, `#` or `\\` and the pattern's remaining host\n // literal lands in the query of a foreign origin. The code would\n // then be issued to that origin.\n if (!isSimpleURLMatch(data.redirect_uri, redirectUris)) {\n throw OAuth2GrantError.redirectUriMismatch();\n }\n\n redirectUriVerified = true;\n }\n\n return {\n data,\n client,\n scopes,\n redirectUriVerified,\n };\n }\n}\n"],"mappings":";;;;;;;;;AA2BA,IAAaY,yCAAb,MAAaA;CACCC;CAEAC;CAEV,YAAYC,KAAoD;EAC5D,KAAKF,mBAAmBE,IAAIF;EAC5B,KAAKC,kBAAkBC,IAAID;CAC/B;;;;IAMA,MAAME,OACFC,MAC0D;EAC1D,IAAI,CAACA,KAAKC,WACN,MAAMb,kBAAkBc,QAAO;EASnC,IAAI,CAACf,OAAOa,KAAKC,SAAS,KAAK,CAACD,KAAKG,UACjC,MAAMb,mBAAmBc,UAAU,kDAAA;EAGvC,MAAMC,SAAS,MAAM,KAAKT,iBAAiBU,kBAAkBN,KAAKC,WAAWD,KAAKG,QAAQ;EAC1F,IAAI,CAACE,QACD,MAAMjB,kBAAkBc,QAAO;EAGnC,IAAI,CAACG,OAAOE,QACR,MAAMnB,kBAAkBoB,SAAQ;EAKpCd,yBAAyBW,QAAQb,iBAAiBiB,kBAAkB;EAMpE,IAAI,CAACJ,OAAOK,aACR,MAAMrB,iBAAiBsB,oBAAmB;EAO9C,IAAI3B,eAAeqB,MAAAA,KAAW,CAACL,KAAKY,gBAChC,MAAMtB,mBAAmBc,UAAU,qDAAA;EAOvC,IAAIpB,eAAeqB,MAAAA,KAAW,CAACL,KAAKa,OAChC,MAAMvB,mBAAmBc,UAAU,wDAAA;EAGvCJ,KAAKC,YAAYI,OAAOS;EACxBd,KAAKG,WAAWE,OAAOU;EAEvB,MAAMC,SAAS,MAAM,KAAKnB,gBAAgBoB,eAAeZ,OAAOS,EAAE;EAClE,MAAMI,aAAaF,OAAOG,KAAKC,UAAUA,MAAMC,IAAI;EACnD,IAAIrB,KAAKoB,OAED;OAAA,CAAC3B,gBAAgByB,YAAYlB,KAAKoB,KAAK,KACvC,CAAC3B,gBAAgBO,KAAKoB,OAAOrC,UAAUuC,MAAM,GAE7C,MAAM/B,iBAAiBgC,aAAY;EAAA,OAGvCvB,KAAKoB,QAAQF,WAAWM,KAAK,GAAA;EAWjC,IAAIC,sBAAsB;EAC1B,IAAIzB,KAAK0B,cAAc;GAOnB,IAAIC;GACJ,IAAI;IACAA,MAAM,IAAIC,IAAI5B,KAAK0B,YAAY;GACnC,QAAQ,CAER;GACA,IAAIC,QAAQA,IAAIE,YAAYF,IAAIG,WAC5B,MAAMxC,mBAAmBc,UAAU,2CAAA;GAMvC,IAAIuB,OAAO,CAAC1C,wBAAwBe,KAAK0B,YAAY,GACjD,MAAMpC,mBAAmBc,UAAU,yCAAA;GAGvC,MAAM2B,eAAe1B,OAAOK,YAAYsB,MAAM,GAAA;GAO9C,IAAI,CAAC9C,iBAAiBc,KAAK0B,cAAcK,YAAAA,GACrC,MAAM1C,iBAAiBsB,oBAAmB;GAG9Cc,sBAAsB;EAC1B;EAEA,OAAO;GACHzB;GACAK;GACAW;GACAS;EACJ;CACJ;AACJ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../../src/core/oauth2/authorization/helpers.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAMtE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAIhD;;;;;;GAMG;AACH,wBAAgB,YAAY,CACxB,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,GAAG,IAAI,GACtD,IAAI,CAAC,kBAAkB,EAAE,KAAK,GAAG,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../../src/core/oauth2/authorization/helpers.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAMtE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAIhD;;;;;;GAMG;AACH,wBAAgB,YAAY,CACxB,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,GAAG,IAAI,GACtD,IAAI,CAAC,kBAAkB,EAAE,KAAK,GAAG,KAAK,CAAC,CA0CzC;AAED,wBAAgB,0BAA0B,WAazC;AAED,wBAAgB,eAAe,CAAC,WAAW,EAAE,WAAW,UAWvD;AAWD,wBAAsB,oBAAoB,CACtC,KAAK,EAAE,MAAM,EACb,GAAG,CAAC,EAAE,GAAG,YAAY,EAAE,GAAG,IAAI,GAC9B,OAAO,CAAC,MAAM,CAAC,CAkBlB;AAED,wBAAsB,wBAAwB,CAAC,YAAY,EAAE,MAAM,mBAMlE"}
|
|
@@ -25,9 +25,14 @@ import { subtle } from "uncrypto";
|
|
|
25
25
|
default: return {};
|
|
26
26
|
}
|
|
27
27
|
if (session.mfaAt) amr.push(OAuth2AuthenticationMethodReference.OTP);
|
|
28
|
+
if (session.mfaAt) return {
|
|
29
|
+
amr,
|
|
30
|
+
acr: OAuth2AuthenticationContextClass.MFA
|
|
31
|
+
};
|
|
32
|
+
if (session.authMethod === SessionAuthMethod.EXTERNAL) return { amr };
|
|
28
33
|
return {
|
|
29
34
|
amr,
|
|
30
|
-
acr:
|
|
35
|
+
acr: OAuth2AuthenticationContextClass.PASSWORD
|
|
31
36
|
};
|
|
32
37
|
}
|
|
33
38
|
function generateOAuth2CodeVerifier() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.mjs","names":["JWTError","OAuth2AuthenticationContextClass","OAuth2AuthenticationMethodReference","SessionAuthMethod","subtle","deriveAmrAcr","session","authMethod","amr","PASSWORD","LDAP","push","EXTERNAL","mfaAt","OTP","acr","MFA","generateOAuth2CodeVerifier","length","charset","codeVerifier","randomValues","Uint8Array","crypto","getRandomValues","i","base64URLEncode","arrayBuffer","charCode","Array","from","btoa","String","fromCharCode","apply","replace","TOKEN_HASH_DIGESTS","name","halfLength","buildOAuth2TokenHash","value","alg","digest","slice","headerPropertyInvalid","encoder","TextEncoder","data","encode","hashBuffer","halfHash","buildOAuth2CodeChallenge"],"sources":["../../../../src/core/oauth2/authorization/helpers.ts"],"sourcesContent":["/*\n * Copyright (c) 2024.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { JWTAlgorithm, OAuth2TokenPayload } from '@authup/specs';\nimport {\n JWTError,\n OAuth2AuthenticationContextClass,\n OAuth2AuthenticationMethodReference,\n} from '@authup/specs';\nimport type { Session } from '@authup/core-kit';\nimport { SessionAuthMethod } from '@authup/core-kit';\nimport { subtle } from 'uncrypto';\n\n/**\n * Derive the OIDC `amr` / `acr` claims from the backing session's\n * authMethod + mfaAt (plan 050). Pre-column sessions (null authMethod)\n * yield no claims — authup cannot retroactively know how an old session\n * authenticated. M2M methods yield none either (amr/acr are user-flow\n * claims; those grants mint no id_token).\n */\nexport function deriveAmrAcr(\n session: Pick<Session, 'authMethod' | 'mfaAt'> | null,\n): Pick<OAuth2TokenPayload, 'amr' | 'acr'> {\n if (!session || !session.authMethod) {\n return {};\n }\n\n const amr : string[] = [];\n switch (session.authMethod) {\n case SessionAuthMethod.PASSWORD:\n case SessionAuthMethod.LDAP: {\n // LDAP is still a password factor for amr — the finer\n // distinction lives in the authup-local authMethod.\n amr.push(OAuth2AuthenticationMethodReference.PASSWORD);\n break;\n }\n case SessionAuthMethod.EXTERNAL: {\n amr.push(OAuth2AuthenticationMethodReference.EXTERNAL);\n break;\n }\n default: {\n return {};\n }\n }\n\n if (session.mfaAt) {\n amr.push(OAuth2AuthenticationMethodReference.OTP);\n }\n\n
|
|
1
|
+
{"version":3,"file":"helpers.mjs","names":["JWTError","OAuth2AuthenticationContextClass","OAuth2AuthenticationMethodReference","SessionAuthMethod","subtle","deriveAmrAcr","session","authMethod","amr","PASSWORD","LDAP","push","EXTERNAL","mfaAt","OTP","acr","MFA","generateOAuth2CodeVerifier","length","charset","codeVerifier","randomValues","Uint8Array","crypto","getRandomValues","i","base64URLEncode","arrayBuffer","charCode","Array","from","btoa","String","fromCharCode","apply","replace","TOKEN_HASH_DIGESTS","name","halfLength","buildOAuth2TokenHash","value","alg","digest","slice","headerPropertyInvalid","encoder","TextEncoder","data","encode","hashBuffer","halfHash","buildOAuth2CodeChallenge"],"sources":["../../../../src/core/oauth2/authorization/helpers.ts"],"sourcesContent":["/*\n * Copyright (c) 2024.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { JWTAlgorithm, OAuth2TokenPayload } from '@authup/specs';\nimport {\n JWTError,\n OAuth2AuthenticationContextClass,\n OAuth2AuthenticationMethodReference,\n} from '@authup/specs';\nimport type { Session } from '@authup/core-kit';\nimport { SessionAuthMethod } from '@authup/core-kit';\nimport { subtle } from 'uncrypto';\n\n/**\n * Derive the OIDC `amr` / `acr` claims from the backing session's\n * authMethod + mfaAt (plan 050). Pre-column sessions (null authMethod)\n * yield no claims — authup cannot retroactively know how an old session\n * authenticated. M2M methods yield none either (amr/acr are user-flow\n * claims; those grants mint no id_token).\n */\nexport function deriveAmrAcr(\n session: Pick<Session, 'authMethod' | 'mfaAt'> | null,\n): Pick<OAuth2TokenPayload, 'amr' | 'acr'> {\n if (!session || !session.authMethod) {\n return {};\n }\n\n const amr : string[] = [];\n switch (session.authMethod) {\n case SessionAuthMethod.PASSWORD:\n case SessionAuthMethod.LDAP: {\n // LDAP is still a password factor for amr — the finer\n // distinction lives in the authup-local authMethod.\n amr.push(OAuth2AuthenticationMethodReference.PASSWORD);\n break;\n }\n case SessionAuthMethod.EXTERNAL: {\n amr.push(OAuth2AuthenticationMethodReference.EXTERNAL);\n break;\n }\n default: {\n return {};\n }\n }\n\n if (session.mfaAt) {\n amr.push(OAuth2AuthenticationMethodReference.OTP);\n }\n\n if (session.mfaAt) {\n return { amr, acr: OAuth2AuthenticationContextClass.MFA };\n }\n\n // No `acr` for a session an external provider authenticated: authup\n // verified no credential of its own, so it asserts no assurance level.\n // `urn:authup:pwd` would say the subject authenticated with a password,\n // which is exactly what did NOT happen (issue #3478). Per OIDC Core\n // §2 the claim is voluntary, so the absence is the honest answer, and\n // `amr: ['ext']` still says how the login happened.\n if (session.authMethod === SessionAuthMethod.EXTERNAL) {\n return { amr };\n }\n\n return { amr, acr: OAuth2AuthenticationContextClass.PASSWORD };\n}\n\nexport function generateOAuth2CodeVerifier() {\n const length = 64;\n const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';\n\n let codeVerifier = '';\n const randomValues = new Uint8Array(length);\n crypto.getRandomValues(randomValues);\n\n for (let i = 0; i < length; i++) {\n codeVerifier += charset[randomValues[i] % charset.length];\n }\n\n return codeVerifier;\n}\n\nexport function base64URLEncode(arrayBuffer: ArrayBuffer) {\n // Convert the ArrayBuffer to string using Uint8 array.\n // btoa takes chars from 0-255 and base64 encodes.\n // Then convert the base64 encoded to base64url encoded.\n // (replace + with -, replace / with _, trim trailing =)\n const charCode = Array.from(new Uint8Array(arrayBuffer));\n\n return btoa(String.fromCharCode.apply(null, charCode))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n}\n\n// OIDC Core §3.1.3.6: at_hash / c_hash use the hash algorithm of the id_token's\n// JWS alg (…S256 → SHA-256, …S384 → SHA-384, …S512 → SHA-512) and keep the LEFT\n// HALF of the digest — so the half-length follows the digest (16/24/32 bytes).\nconst TOKEN_HASH_DIGESTS : Record<string, { name: string, halfLength: number }> = {\n 256: { name: 'SHA-256', halfLength: 16 },\n 384: { name: 'SHA-384', halfLength: 24 },\n 512: { name: 'SHA-512', halfLength: 32 },\n};\n\nexport async function buildOAuth2TokenHash(\n value: string,\n alg?: `${JWTAlgorithm}` | null,\n) : Promise<string> {\n // A key row without a persisted alg (nullable legacy column) signs with\n // the signer's *256 default (RS256/ES256/HS256) — derive the matching\n // SHA-256 digest. An unrecognized alg still fails loud — silently\n // defaulting there would mint a hash no conforming verifier accepts.\n const digest = alg ?\n TOKEN_HASH_DIGESTS[alg.slice(-3)] :\n TOKEN_HASH_DIGESTS['256'];\n if (!digest) {\n throw JWTError.headerPropertyInvalid('alg');\n }\n\n const encoder = new TextEncoder();\n const data = encoder.encode(value);\n const hashBuffer = await subtle.digest(digest.name, data);\n const halfHash = hashBuffer.slice(0, digest.halfLength);\n\n return base64URLEncode(halfHash);\n}\n\nexport async function buildOAuth2CodeChallenge(codeVerifier: string) {\n const encoder = new TextEncoder();\n const data = encoder.encode(codeVerifier);\n const arrayBuffer = await subtle.digest('SHA-256', data);\n\n return base64URLEncode(arrayBuffer);\n}\n"],"mappings":";;;;;;;;;;;;;GAwBA,SAAgBK,aACZC,SAAqD;CAErD,IAAI,CAACA,WAAW,CAACA,QAAQC,YACrB,OAAO,CAAC;CAGZ,MAAMC,MAAiB,CAAA;CACvB,QAAQF,QAAQC,YAAhB;EACI,KAAKJ,kBAAkBM;EACvB,KAAKN,kBAAkBO;GAGnBF,IAAIG,KAAKT,oCAAoCO,QAAQ;GACrD;EAEJ,KAAKN,kBAAkBS;GACnBJ,IAAIG,KAAKT,oCAAoCU,QAAQ;GACrD;EAEJ,SACI,OAAO,CAAC;CAEhB;CAEA,IAAIN,QAAQO,OACRL,IAAIG,KAAKT,oCAAoCY,GAAG;CAGpD,IAAIR,QAAQO,OACR,OAAO;EAAEL;EAAKO,KAAKd,iCAAiCe;CAAI;CAS5D,IAAIV,QAAQC,eAAeJ,kBAAkBS,UACzC,OAAO,EAAEJ,IAAI;CAGjB,OAAO;EAAEA;EAAKO,KAAKd,iCAAiCQ;CAAS;AACjE;AAEA,SAAgBQ,6BAAAA;CACZ,MAAMC,SAAS;CACf,MAAMC,UAAU;CAEhB,IAAIC,eAAe;CACnB,MAAMC,eAAe,IAAIC,WAAWJ,MAAAA;CACpCK,OAAOC,gBAAgBH,YAAAA;CAEvB,KAAK,IAAII,IAAI,GAAGA,IAAIP,QAAQO,KACxBL,gBAAgBD,QAAQE,aAAaI,KAAKN;CAG9C,OAAOC;AACX;AAEA,SAAgBM,gBAAgBC,aAAwB;CAKpD,MAAMC,WAAWC,MAAMC,KAAK,IAAIR,WAAWK,WAAAA,CAAAA;CAE3C,OAAOI,KAAKC,OAAOC,aAAaC,MAAM,MAAMN,QAAAA,CAAAA,CAAAA,CACvCO,QAAQ,OAAO,GAAA,CAAA,CACfA,QAAQ,OAAO,GAAA,CAAA,CACfA,QAAQ,OAAO,EAAA;AACxB;AAKA,MAAMC,qBAA4E;CAC9E,KAAK;EAAEC,MAAM;EAAWC,YAAY;CAAG;CACvC,KAAK;EAAED,MAAM;EAAWC,YAAY;CAAG;CACvC,KAAK;EAAED,MAAM;EAAWC,YAAY;CAAG;AAC3C;AAEA,eAAsBC,qBAClBC,OACAC,KAA8B;CAM9B,MAAMC,SAASD,MACXL,mBAAmBK,IAAIE,MAAM,EAAC,KAC9BP,mBAAmB;CACvB,IAAI,CAACM,QACD,MAAM1C,SAAS4C,sBAAsB,KAAA;CAIzC,MAAMG,OAAOF,IADOC,YACPD,CAAAA,CAAQG,OAAOR,KAAAA;CAI5B,OAAOd,iBAFUuB,MADQ7C,OAAOsC,OAAOA,OAAOL,MAAMU,IAAAA,EAAAA,CACxBJ,MAAM,GAAGD,OAAOJ,UAErBY,CAAAA;AAC3B;AAEA,eAAsBC,yBAAyB/B,cAAoB;CAE/D,MAAM2B,OAAOF,IADOC,YACPD,CAAAA,CAAQG,OAAO5B,YAAAA;CAG5B,OAAOM,gBAAgBC,MAFGvB,OAAOsC,OAAO,WAAWK,IAAAA,CAE5BpB;AAC3B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../src/core/oauth2/authorization/module.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACR,QAAQ,EAER,8BAA8B,EAEjC,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../src/core/oauth2/authorization/module.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACR,QAAQ,EAER,8BAA8B,EAEjC,MAAM,kBAAkB,CAAC;AAuB1B,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,2BAA2B,CAAC;AAE9E,OAAO,KAAK,EACR,iCAAiC,EACjC,0BAA0B,EAC1B,yBAAyB,EAC5B,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,mCAAmC,EAAE,MAAM,yBAAyB,CAAC;AAClG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAS/D,qBAAa,mBAAmB;IAC5B,SAAS,CAAC,UAAU,EAAG,8BAA8B,CAAC;IAEtD,SAAS,CAAC,cAAc,EAAG,eAAe,CAAC;IAE3C,SAAS,CAAC,YAAY,CAAC,EAAG,aAAa,CAAC;IAExC,SAAS,CAAC,OAAO,CAAC,EAAG,gBAAgB,CAAC;IAEtC,SAAS,CAAC,iBAAiB,EAAG,MAAM,CAAC;IAErC,SAAS,CAAC,kBAAkB,EAAG,MAAM,CAAC;IAEtC,SAAS,CAAC,oBAAoB,CAAC,EAAG,mCAAmC,CAAC;IAEtE,SAAS,CAAC,qBAAqB,CAAC,EAAG,4BAA4B,CAAC;gBAEpD,GAAG,EAAE,iCAAiC;IAWlD;;;;;;OAMG;IACG,SAAS,CACX,IAAI,EAAE,8BAA8B,EACpC,QAAQ,EAAE,QAAQ,EAClB,OAAO,GAAE,0BAA+B,GACxC,OAAO,CAAC,yBAAyB,CAAC;cAmDtB,cAAc,CAC1B,IAAI,EAAE,8BAA8B,EACpC,QAAQ,EAAE,QAAQ,EAClB,OAAO,GAAE,0BAA+B,GACxC,OAAO,CAAC,yBAAyB,CAAC;CA+LzC"}
|
|
@@ -2,7 +2,7 @@ import "node:path";
|
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { toIdentityPolicyData } from "../../identity/permission/identity-policy-data.mjs";
|
|
5
|
-
import { EventName, EventRefType, EventScope, IdentityType } from "@authup/core-kit";
|
|
5
|
+
import { EventName, EventRefType, EventScope, IdentityType, SessionAuthMethod } from "@authup/core-kit";
|
|
6
6
|
import { hasInstanceof } from "@authup/errors";
|
|
7
7
|
import { OAUTH2_ACCESS_DENIED_ERROR_INSTANCE, OAUTH2_LOGIN_REQUIRED_ERROR_INSTANCE, OAUTH2_MFA_REQUIRED_ERROR_INSTANCE, OAuth2AccessDeniedError, OAuth2AuthenticationContextClass, OAuth2AuthorizationPrompt, OAuth2AuthorizationResponseType, OAuth2GrantError, OAuth2LoginRequiredError, OAuth2MfaRequiredError, OAuth2RequestError, OAuth2ResponseTypeError } from "@authup/specs";
|
|
8
8
|
//#region src/core/oauth2/authorization/module.ts
|
|
@@ -97,8 +97,10 @@ var OAuth2Authorization = class {
|
|
|
97
97
|
}
|
|
98
98
|
if (this.mfaChallengeProvider && identity.type === IdentityType.USER) {
|
|
99
99
|
const challenge = await this.mfaChallengeProvider.challenge(identity.data.id, { issueMaterial: false });
|
|
100
|
-
if (
|
|
101
|
-
|
|
100
|
+
if (!(session?.authMethod === SessionAuthMethod.EXTERNAL)) {
|
|
101
|
+
if (challenge.required && !session?.mfaAt) throw OAuth2MfaRequiredError.challengeRequired();
|
|
102
|
+
if (challenge.enrollmentRequired) throw OAuth2MfaRequiredError.enrollmentRequired();
|
|
103
|
+
}
|
|
102
104
|
if (challenge.required && data.acr_values) {
|
|
103
105
|
if (data.acr_values.split(" ").includes(OAuth2AuthenticationContextClass.MFA)) {
|
|
104
106
|
const mfaAtSeconds = session?.mfaAt ? Math.floor(new Date(session.mfaAt).getTime() / 1e3) : null;
|