@authup/server-core 1.0.0-beta.62 → 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/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/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/controllers/entities/client/module.mjs +1 -1
- package/dist/adapters/http/controllers/entities/identity-provider/module.d.ts +23 -1
- package/dist/adapters/http/controllers/entities/identity-provider/module.d.ts.map +1 -1
- package/dist/adapters/http/controllers/entities/identity-provider/module.mjs +85 -46
- package/dist/adapters/http/controllers/entities/identity-provider/module.mjs.map +1 -1
- 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 +5 -0
- 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/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/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/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/types.d.ts +8 -0
- package/dist/app/modules/config/types.d.ts.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/http/modules/controller.d.ts.map +1 -1
- package/dist/app/modules/http/modules/controller.mjs +5 -2
- package/dist/app/modules/http/modules/controller.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/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/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/identity-provider/types.d.ts +9 -1
- package/dist/core/entities/identity-provider/types.d.ts.map +1 -1
- package/dist/core/identity/index.mjs +2 -1
- package/dist/core/identity/provider/account/module.mjs +1 -1
- package/dist/core/identity/provider/authentication/index.mjs +2 -1
- 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 +2 -1
- package/dist/core/index.mjs +4 -2
- 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/types.d.ts +8 -0
- package/dist/core/oauth2/authorization/state/types.d.ts.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 +1 -0
- package/dist/core/oauth2/federated-login/index.d.ts.map +1 -1
- package/dist/core/oauth2/federated-login/index.mjs +2 -1
- package/dist/core/oauth2/federated-login/module.d.ts +12 -3
- package/dist/core/oauth2/federated-login/module.d.ts.map +1 -1
- package/dist/core/oauth2/federated-login/module.mjs +96 -40
- package/dist/core/oauth2/federated-login/module.mjs.map +1 -1
- package/dist/core/oauth2/federated-login/types.d.ts +82 -24
- package/dist/core/oauth2/federated-login/types.d.ts.map +1 -1
- package/dist/core/oauth2/federated-login/types.mjs +5 -0
- package/dist/core/oauth2/federated-login/types.mjs.map +1 -1
- 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.mjs +3 -2
- 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/swagger.json +107 -59
- package/package.json +13 -13
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { OAUTH2_FEDERATED_LOGIN_TTL } from "../../../../../core/oauth2/federated-login/constants.mjs";
|
|
5
|
+
import "../../../../../core/index.mjs";
|
|
6
|
+
import { CacheOAuth2Prefix } from "../constants.mjs";
|
|
7
|
+
import { createNanoID } from "@authup/kit";
|
|
8
|
+
import { buildCacheKey } from "@authup/server-kit";
|
|
9
|
+
//#region src/app/modules/oauth2/repositories/federated-login/store.ts
|
|
10
|
+
var OAuth2FederatedLoginStore = class {
|
|
11
|
+
cache;
|
|
12
|
+
constructor(cache) {
|
|
13
|
+
this.cache = cache;
|
|
14
|
+
}
|
|
15
|
+
async save(data) {
|
|
16
|
+
const id = createNanoID();
|
|
17
|
+
await this.cache.set(buildCacheKey({
|
|
18
|
+
prefix: CacheOAuth2Prefix.FEDERATED_LOGIN,
|
|
19
|
+
key: id
|
|
20
|
+
}), data, { ttl: OAUTH2_FEDERATED_LOGIN_TTL });
|
|
21
|
+
return id;
|
|
22
|
+
}
|
|
23
|
+
async consume(id) {
|
|
24
|
+
return this.cache.pop(buildCacheKey({
|
|
25
|
+
prefix: CacheOAuth2Prefix.FEDERATED_LOGIN,
|
|
26
|
+
key: id
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
//#endregion
|
|
31
|
+
export { OAuth2FederatedLoginStore };
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=store.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.mjs","names":["createNanoID","buildCacheKey","OAUTH2_FEDERATED_LOGIN_TTL","CacheOAuth2Prefix","OAuth2FederatedLoginStore","cache","save","data","id","set","prefix","FEDERATED_LOGIN","key","ttl","consume","pop"],"sources":["../../../../../../src/app/modules/oauth2/repositories/federated-login/store.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 { createNanoID } from '@authup/kit';\nimport type { ICache } from '@authup/server-kit';\nimport { buildCacheKey } from '@authup/server-kit';\nimport type {\n IOAuth2FederatedLoginStore,\n OAuth2FederatedLoginPending,\n} from '../../../../../core/index.ts';\nimport { OAUTH2_FEDERATED_LOGIN_TTL } from '../../../../../core/index.ts';\nimport { CacheOAuth2Prefix } from '../constants.ts';\n\nexport class OAuth2FederatedLoginStore implements IOAuth2FederatedLoginStore {\n protected cache : ICache;\n\n constructor(cache: ICache) {\n this.cache = cache;\n }\n\n async save(data: OAuth2FederatedLoginPending): Promise<string> {\n const id = createNanoID();\n\n await this.cache.set(\n buildCacheKey({\n prefix: CacheOAuth2Prefix.FEDERATED_LOGIN,\n key: id,\n }),\n data,\n { ttl: OAUTH2_FEDERATED_LOGIN_TTL },\n );\n\n return id;\n }\n\n async consume(id: string): Promise<OAuth2FederatedLoginPending | null> {\n // `pop` is the atomic read-and-drop (redis GETDEL, one tick on the\n // memory adapter). A get followed by a drop is two round-trips, and\n // two simultaneous completions of one pending login would both read\n // the payload before either drop landed.\n return this.cache.pop<OAuth2FederatedLoginPending>(buildCacheKey({\n prefix: CacheOAuth2Prefix.FEDERATED_LOGIN,\n key: id,\n }));\n }\n}\n"],"mappings":";;;;;;;;;AAiBA,IAAaI,4BAAb,MAAaA;CACCC;CAEV,YAAYA,OAAe;EACvB,KAAKA,QAAQA;CACjB;CAEA,MAAMC,KAAKC,MAAoD;EAC3D,MAAMC,KAAKR,aAAAA;EAEX,MAAM,KAAKK,MAAMI,IACbR,cAAc;GACVS,QAAQP,kBAAkBQ;GAC1BC,KAAKJ;EACT,CAAA,GACAD,MACA,EAAEM,KAAKX,2BAA2B,CAAA;EAGtC,OAAOM;CACX;CAEA,MAAMM,QAAQN,IAAyD;EAKnE,OAAO,KAAKH,MAAMU,IAAiCd,cAAc;GAC7DS,QAAQP,kBAAkBQ;GAC1BC,KAAKJ;EACT,CAAA,CAAA;CACJ;AACJ"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from './authorize/index.ts';
|
|
2
2
|
export * from './client/index.ts';
|
|
3
3
|
export * from './consent/index.ts';
|
|
4
|
+
export * from './federated-login/index.ts';
|
|
4
5
|
export * from './scope/index.ts';
|
|
5
6
|
export * from './session-token/index.ts';
|
|
6
7
|
export * from './token/index.ts';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/app/modules/oauth2/repositories/index.ts"],"names":[],"mappings":"AAOA,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,0BAA0B,CAAC;AACzC,cAAc,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/app/modules/oauth2/repositories/index.ts"],"names":[],"mappings":"AAOA,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,kBAAkB,CAAC;AACjC,cAAc,0BAA0B,CAAC;AACzC,cAAc,kBAAkB,CAAC"}
|
|
@@ -8,9 +8,11 @@ import { OAuth2ClientRepository } from "./client/repository.mjs";
|
|
|
8
8
|
import "./client/index.mjs";
|
|
9
9
|
import { ConsentRepositoryAdapter } from "./consent/repository.mjs";
|
|
10
10
|
import "./consent/index.mjs";
|
|
11
|
+
import { OAuth2FederatedLoginStore } from "./federated-login/store.mjs";
|
|
12
|
+
import "./federated-login/index.mjs";
|
|
11
13
|
import { OAuth2ScopeRepository } from "./scope/repository.mjs";
|
|
12
14
|
import { SessionTokenRepositoryAdapter } from "./session-token/repository.mjs";
|
|
13
15
|
import "./session-token/index.mjs";
|
|
14
16
|
import { OAuth2TokenRepository } from "./token/repository.mjs";
|
|
15
17
|
import "./token/index.mjs";
|
|
16
|
-
export { ConsentRepositoryAdapter, OAuth2AuthorizationCodeRepository, OAuth2AuthorizationStateRepository, OAuth2ClientRepository, OAuth2ScopeRepository, OAuth2TokenRepository, SessionTokenRepositoryAdapter };
|
|
18
|
+
export { ConsentRepositoryAdapter, OAuth2AuthorizationCodeRepository, OAuth2AuthorizationStateRepository, OAuth2ClientRepository, OAuth2FederatedLoginStore, OAuth2ScopeRepository, OAuth2TokenRepository, SessionTokenRepositoryAdapter };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-event-handler.d.ts","sourceRoot":"","sources":["../../../../src/core/entities/event/entity-event-handler.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzF,OAAO,KAAK,EACR,yBAAyB,EACzB,mBAAmB,EACnB,aAAa,EAChB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,yBAAyB,GAAG;IACpC,YAAY,EAAE,aAAa,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,mBAAmB,GAAG,SAAS,CAAC;IACvD,OAAO,CAAC,EAAE,yBAAyB,CAAC;CACvC,CAAC;
|
|
1
|
+
{"version":3,"file":"entity-event-handler.d.ts","sourceRoot":"","sources":["../../../../src/core/entities/event/entity-event-handler.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzF,OAAO,KAAK,EACR,yBAAyB,EACzB,mBAAmB,EACnB,aAAa,EAChB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,yBAAyB,GAAG;IACpC,YAAY,EAAE,aAAa,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,mBAAmB,GAAG,SAAS,CAAC;IACvD,OAAO,CAAC,EAAE,yBAAyB,CAAC;CACvC,CAAC;AA2DF;;;GAGG;AACH,qBAAa,kBAAmB,YAAW,mBAAmB;IAC1D,SAAS,CAAC,YAAY,EAAG,aAAa,CAAC;IAEvC,SAAS,CAAC,cAAc,CAAC,EAAG,MAAM,mBAAmB,GAAG,SAAS,CAAC;IAElE,SAAS,CAAC,OAAO,EAAG,yBAAyB,CAAC;gBAElC,GAAG,EAAE,yBAAyB;IAMpC,MAAM,CAAC,GAAG,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC;CA2D9D"}
|
|
@@ -10,6 +10,19 @@ const ENTITY_EVENT_NAME_MAP = {
|
|
|
10
10
|
[EntityDefaultEventName.UPDATED]: EventName.UPDATED,
|
|
11
11
|
[EntityDefaultEventName.DELETED]: EventName.DELETED
|
|
12
12
|
};
|
|
13
|
+
/**
|
|
14
|
+
* Entity types whose rows are generic (name, value) pairs. Their payload
|
|
15
|
+
* column is always called `value`, so {@link buildEntityDiff}'s key-name
|
|
16
|
+
* denylist cannot see what it holds - and for an identity provider it holds
|
|
17
|
+
* the OAuth2 `clientSecret` or the LDAP bind password. The diff is dropped
|
|
18
|
+
* wholesale rather than the denylist widened, because no regex over the
|
|
19
|
+
* column name can ever decide this.
|
|
20
|
+
*/ const ENTITY_OPAQUE_VALUE_TYPES = /* @__PURE__ */ new Set([
|
|
21
|
+
EntityType.IDENTITY_PROVIDER_ATTRIBUTE,
|
|
22
|
+
EntityType.POLICY_ATTRIBUTE,
|
|
23
|
+
EntityType.ROLE_ATTRIBUTE,
|
|
24
|
+
EntityType.USER_ATTRIBUTE
|
|
25
|
+
]);
|
|
13
26
|
const ENTITY_REALM_KEY_MAP = {
|
|
14
27
|
[EntityType.CLIENT]: "realmId",
|
|
15
28
|
[EntityType.CLIENT_PERMISSION]: "clientRealmId",
|
|
@@ -64,7 +77,7 @@ const ENTITY_REALM_KEY_MAP = {
|
|
|
64
77
|
const realmId = typeof realmIdCurrent === "undefined" ? resolveEntityRealmId(ctx.content.type, ctx.dataPrevious) ?? null : realmIdCurrent;
|
|
65
78
|
const requestContext = this.requestContext ? this.requestContext() : void 0;
|
|
66
79
|
let data = null;
|
|
67
|
-
if (name === EventName.UPDATED && isObject(ctx.content.data) && isObject(ctx.dataPrevious)) {
|
|
80
|
+
if (name === EventName.UPDATED && !ENTITY_OPAQUE_VALUE_TYPES.has(ctx.content.type) && isObject(ctx.content.data) && isObject(ctx.dataPrevious)) {
|
|
68
81
|
const diff = buildEntityDiff(ctx.content.data, ctx.dataPrevious);
|
|
69
82
|
if (Object.keys(diff).length > 0) data = { diff };
|
|
70
83
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-event-handler.mjs","names":["EntityDefaultEventName","EntityType","EventName","EventScope","hasOwnProperty","isObject","buildEntityDiff","ENTITY_EVENT_NAME_MAP","CREATED","UPDATED","DELETED","ENTITY_REALM_KEY_MAP","CLIENT","CLIENT_PERMISSION","CLIENT_ROLE","CLIENT_SCOPE","CONSENT","SESSION_TOKEN","EVENT","IDENTITY_PROVIDER","IDENTITY_PROVIDER_ACCOUNT","IDENTITY_PROVIDER_ATTRIBUTE","IDENTITY_PROVIDER_ATTRIBUTE_MAPPING","IDENTITY_PROVIDER_PERMISSION_MAPPING","IDENTITY_PROVIDER_ROLE_MAPPING","KEY","POLICY","POLICY_ATTRIBUTE","PERMISSION","PERMISSION_POLICY","REALM","ROLE","ROLE_ATTRIBUTE","ROLE_PERMISSION","SCOPE","SESSION","TRUST_ANCHOR","USER","USER_ATTRIBUTE","USER_AUTHENTICATOR","USER_PERMISSION","USER_ROLE","EntityEventHandler","eventService","requestContext","options","ctx","handle","content","type","name","event","refId","data","id","realmIdCurrent","resolveEntityRealmId","realmId","dataPrevious","undefined","diff","Object","keys","length","record","scope","ENTITY","refType","actorType","actorId","actorName","sessionId","requestPath","requestMethod","requestIpAddress","requestUserAgent","retentionDays","key"],"sources":["../../../../src/core/entities/event/entity-event-handler.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 {\n EntityDefaultEventName,\n EntityType,\n EventName,\n EventScope,\n} from '@authup/core-kit';\nimport { hasOwnProperty, isObject } from '@authup/kit';\nimport type { DomainEventPublishContext, IDomainEventHandler } from '@authup/server-kit';\nimport { buildEntityDiff } from './diff.ts';\nimport type {\n EntityEventHandlerOptions,\n EventRequestContext,\n IEventService,\n} from './types.ts';\n\nexport type EntityEventHandlerContext = {\n eventService: IEventService,\n requestContext?: () => EventRequestContext | undefined,\n options?: EntityEventHandlerOptions,\n};\n\nconst ENTITY_EVENT_NAME_MAP : Record<string, `${EventName}`> = {\n [EntityDefaultEventName.CREATED]: EventName.CREATED,\n [EntityDefaultEventName.UPDATED]: EventName.UPDATED,\n [EntityDefaultEventName.DELETED]: EventName.DELETED,\n};\n\nconst ENTITY_REALM_KEY_MAP : Record<`${EntityType}`, string> = {\n [EntityType.CLIENT]: 'realmId',\n [EntityType.CLIENT_PERMISSION]: 'clientRealmId',\n [EntityType.CLIENT_ROLE]: 'clientRealmId',\n [EntityType.CLIENT_SCOPE]: 'clientRealmId',\n [EntityType.CONSENT]: 'realmId',\n // Unreachable in practice: auth_session_tokens has no entity subscriber,\n // so no domain event is ever published for it. Present for exhaustiveness,\n // and the key is the one it would resolve through if that changed.\n [EntityType.SESSION_TOKEN]: 'sessionId',\n [EntityType.EVENT]: 'realmId',\n [EntityType.IDENTITY_PROVIDER]: 'realmId',\n [EntityType.IDENTITY_PROVIDER_ACCOUNT]: 'userRealmId',\n [EntityType.IDENTITY_PROVIDER_ATTRIBUTE]: 'realmId',\n [EntityType.IDENTITY_PROVIDER_ATTRIBUTE_MAPPING]: 'providerRealmId',\n [EntityType.IDENTITY_PROVIDER_PERMISSION_MAPPING]: 'providerRealmId',\n [EntityType.IDENTITY_PROVIDER_ROLE_MAPPING]: 'providerRealmId',\n [EntityType.KEY]: 'realmId',\n [EntityType.POLICY]: 'realmId',\n [EntityType.POLICY_ATTRIBUTE]: 'realmId',\n [EntityType.PERMISSION]: 'realmId',\n [EntityType.PERMISSION_POLICY]: 'permissionRealmId',\n [EntityType.REALM]: 'id',\n [EntityType.ROLE]: 'realmId',\n [EntityType.ROLE_ATTRIBUTE]: 'realmId',\n [EntityType.ROLE_PERMISSION]: 'roleRealmId',\n [EntityType.SCOPE]: 'realmId',\n [EntityType.SESSION]: 'realmId',\n [EntityType.TRUST_ANCHOR]: 'realmId',\n [EntityType.USER]: 'realmId',\n [EntityType.USER_ATTRIBUTE]: 'realmId',\n [EntityType.USER_AUTHENTICATOR]: 'realmId',\n [EntityType.USER_PERMISSION]: 'userRealmId',\n [EntityType.USER_ROLE]: 'userRealmId',\n};\n\n/**\n * Bridges every entity create/update/delete published on the\n * DomainEventPublisher bus into the auth_events audit table (scope `entity`).\n */\nexport class EntityEventHandler implements IDomainEventHandler {\n protected eventService : IEventService;\n\n protected requestContext? : () => EventRequestContext | undefined;\n\n protected options : EntityEventHandlerOptions;\n\n constructor(ctx: EntityEventHandlerContext) {\n this.eventService = ctx.eventService;\n this.requestContext = ctx.requestContext;\n this.options = ctx.options ?? {};\n }\n\n async handle(ctx: DomainEventPublishContext): Promise<void> {\n try {\n // recursion guard — no event subscriber exists today (the\n // auth_events entity is deliberately subscriber-less), so this is\n // defense in depth against an audit-write feedback loop.\n if (ctx.content.type === 'event') {\n return;\n }\n\n const name = ENTITY_EVENT_NAME_MAP[ctx.content.event];\n if (!name) {\n return;\n }\n\n const refId = ctx.content.data?.id ?? null;\n const realmIdCurrent = resolveEntityRealmId(ctx.content.type, ctx.content.data);\n const realmId = typeof realmIdCurrent === 'undefined' ?\n (resolveEntityRealmId(ctx.content.type, ctx.dataPrevious) ?? null) :\n realmIdCurrent;\n\n const requestContext = this.requestContext ?\n this.requestContext() :\n undefined;\n\n let data : Record<string, any> | null = null;\n if (name === EventName.UPDATED && isObject(ctx.content.data) && isObject(ctx.dataPrevious)) {\n const diff = buildEntityDiff(ctx.content.data, ctx.dataPrevious);\n if (Object.keys(diff).length > 0) {\n data = { diff };\n }\n }\n\n await this.eventService.record({\n scope: EventScope.ENTITY,\n name,\n refType: ctx.content.type,\n refId,\n realmId,\n actorType: requestContext?.actorType ?? null,\n actorId: requestContext?.actorId ?? null,\n actorName: requestContext?.actorName ?? null,\n sessionId: requestContext?.sessionId ?? null,\n requestPath: requestContext?.requestPath ?? null,\n requestMethod: requestContext?.requestMethod ?? null,\n requestIpAddress: requestContext?.requestIpAddress ?? null,\n requestUserAgent: requestContext?.requestUserAgent ?? null,\n data,\n retentionDays: this.options.retentionDays,\n });\n } catch {\n // record() never throws, but the bridge itself must also never\n // fail the originating publish.\n }\n }\n}\n\nfunction resolveEntityRealmId(\n type: string,\n data: unknown,\n): string | null | undefined {\n if (!isObject(data)) {\n return undefined;\n }\n\n const key = ENTITY_REALM_KEY_MAP[type as `${EntityType}`] ?? 'realmId';\n if (!hasOwnProperty(data, key)) {\n return undefined;\n }\n\n return typeof data[key] === 'string' ? data[key] : null;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"entity-event-handler.mjs","names":["EntityDefaultEventName","EntityType","EventName","EventScope","hasOwnProperty","isObject","buildEntityDiff","ENTITY_EVENT_NAME_MAP","CREATED","UPDATED","DELETED","ENTITY_OPAQUE_VALUE_TYPES","Set","IDENTITY_PROVIDER_ATTRIBUTE","POLICY_ATTRIBUTE","ROLE_ATTRIBUTE","USER_ATTRIBUTE","ENTITY_REALM_KEY_MAP","CLIENT","CLIENT_PERMISSION","CLIENT_ROLE","CLIENT_SCOPE","CONSENT","SESSION_TOKEN","EVENT","IDENTITY_PROVIDER","IDENTITY_PROVIDER_ACCOUNT","IDENTITY_PROVIDER_ATTRIBUTE_MAPPING","IDENTITY_PROVIDER_PERMISSION_MAPPING","IDENTITY_PROVIDER_ROLE_MAPPING","KEY","POLICY","PERMISSION","PERMISSION_POLICY","REALM","ROLE","ROLE_PERMISSION","SCOPE","SESSION","TRUST_ANCHOR","USER","USER_AUTHENTICATOR","USER_PERMISSION","USER_ROLE","EntityEventHandler","eventService","requestContext","options","ctx","handle","content","type","name","event","refId","data","id","realmIdCurrent","resolveEntityRealmId","realmId","dataPrevious","undefined","has","diff","Object","keys","length","record","scope","ENTITY","refType","actorType","actorId","actorName","sessionId","requestPath","requestMethod","requestIpAddress","requestUserAgent","retentionDays","key"],"sources":["../../../../src/core/entities/event/entity-event-handler.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 {\n EntityDefaultEventName,\n EntityType,\n EventName,\n EventScope,\n} from '@authup/core-kit';\nimport { hasOwnProperty, isObject } from '@authup/kit';\nimport type { DomainEventPublishContext, IDomainEventHandler } from '@authup/server-kit';\nimport { buildEntityDiff } from './diff.ts';\nimport type {\n EntityEventHandlerOptions,\n EventRequestContext,\n IEventService,\n} from './types.ts';\n\nexport type EntityEventHandlerContext = {\n eventService: IEventService,\n requestContext?: () => EventRequestContext | undefined,\n options?: EntityEventHandlerOptions,\n};\n\nconst ENTITY_EVENT_NAME_MAP : Record<string, `${EventName}`> = {\n [EntityDefaultEventName.CREATED]: EventName.CREATED,\n [EntityDefaultEventName.UPDATED]: EventName.UPDATED,\n [EntityDefaultEventName.DELETED]: EventName.DELETED,\n};\n\n/**\n * Entity types whose rows are generic (name, value) pairs. Their payload\n * column is always called `value`, so {@link buildEntityDiff}'s key-name\n * denylist cannot see what it holds - and for an identity provider it holds\n * the OAuth2 `clientSecret` or the LDAP bind password. The diff is dropped\n * wholesale rather than the denylist widened, because no regex over the\n * column name can ever decide this.\n */\nconst ENTITY_OPAQUE_VALUE_TYPES = new Set<string>([\n EntityType.IDENTITY_PROVIDER_ATTRIBUTE,\n EntityType.POLICY_ATTRIBUTE,\n EntityType.ROLE_ATTRIBUTE,\n EntityType.USER_ATTRIBUTE,\n]);\n\nconst ENTITY_REALM_KEY_MAP : Record<`${EntityType}`, string> = {\n [EntityType.CLIENT]: 'realmId',\n [EntityType.CLIENT_PERMISSION]: 'clientRealmId',\n [EntityType.CLIENT_ROLE]: 'clientRealmId',\n [EntityType.CLIENT_SCOPE]: 'clientRealmId',\n [EntityType.CONSENT]: 'realmId',\n // Unreachable in practice: auth_session_tokens has no entity subscriber,\n // so no domain event is ever published for it. Present for exhaustiveness,\n // and the key is the one it would resolve through if that changed.\n [EntityType.SESSION_TOKEN]: 'sessionId',\n [EntityType.EVENT]: 'realmId',\n [EntityType.IDENTITY_PROVIDER]: 'realmId',\n [EntityType.IDENTITY_PROVIDER_ACCOUNT]: 'userRealmId',\n [EntityType.IDENTITY_PROVIDER_ATTRIBUTE]: 'realmId',\n [EntityType.IDENTITY_PROVIDER_ATTRIBUTE_MAPPING]: 'providerRealmId',\n [EntityType.IDENTITY_PROVIDER_PERMISSION_MAPPING]: 'providerRealmId',\n [EntityType.IDENTITY_PROVIDER_ROLE_MAPPING]: 'providerRealmId',\n [EntityType.KEY]: 'realmId',\n [EntityType.POLICY]: 'realmId',\n [EntityType.POLICY_ATTRIBUTE]: 'realmId',\n [EntityType.PERMISSION]: 'realmId',\n [EntityType.PERMISSION_POLICY]: 'permissionRealmId',\n [EntityType.REALM]: 'id',\n [EntityType.ROLE]: 'realmId',\n [EntityType.ROLE_ATTRIBUTE]: 'realmId',\n [EntityType.ROLE_PERMISSION]: 'roleRealmId',\n [EntityType.SCOPE]: 'realmId',\n [EntityType.SESSION]: 'realmId',\n [EntityType.TRUST_ANCHOR]: 'realmId',\n [EntityType.USER]: 'realmId',\n [EntityType.USER_ATTRIBUTE]: 'realmId',\n [EntityType.USER_AUTHENTICATOR]: 'realmId',\n [EntityType.USER_PERMISSION]: 'userRealmId',\n [EntityType.USER_ROLE]: 'userRealmId',\n};\n\n/**\n * Bridges every entity create/update/delete published on the\n * DomainEventPublisher bus into the auth_events audit table (scope `entity`).\n */\nexport class EntityEventHandler implements IDomainEventHandler {\n protected eventService : IEventService;\n\n protected requestContext? : () => EventRequestContext | undefined;\n\n protected options : EntityEventHandlerOptions;\n\n constructor(ctx: EntityEventHandlerContext) {\n this.eventService = ctx.eventService;\n this.requestContext = ctx.requestContext;\n this.options = ctx.options ?? {};\n }\n\n async handle(ctx: DomainEventPublishContext): Promise<void> {\n try {\n // recursion guard — no event subscriber exists today (the\n // auth_events entity is deliberately subscriber-less), so this is\n // defense in depth against an audit-write feedback loop.\n if (ctx.content.type === 'event') {\n return;\n }\n\n const name = ENTITY_EVENT_NAME_MAP[ctx.content.event];\n if (!name) {\n return;\n }\n\n const refId = ctx.content.data?.id ?? null;\n const realmIdCurrent = resolveEntityRealmId(ctx.content.type, ctx.content.data);\n const realmId = typeof realmIdCurrent === 'undefined' ?\n (resolveEntityRealmId(ctx.content.type, ctx.dataPrevious) ?? null) :\n realmIdCurrent;\n\n const requestContext = this.requestContext ?\n this.requestContext() :\n undefined;\n\n let data : Record<string, any> | null = null;\n if (\n name === EventName.UPDATED &&\n !ENTITY_OPAQUE_VALUE_TYPES.has(ctx.content.type) &&\n isObject(ctx.content.data) &&\n isObject(ctx.dataPrevious)\n ) {\n const diff = buildEntityDiff(ctx.content.data, ctx.dataPrevious);\n if (Object.keys(diff).length > 0) {\n data = { diff };\n }\n }\n\n await this.eventService.record({\n scope: EventScope.ENTITY,\n name,\n refType: ctx.content.type,\n refId,\n realmId,\n actorType: requestContext?.actorType ?? null,\n actorId: requestContext?.actorId ?? null,\n actorName: requestContext?.actorName ?? null,\n sessionId: requestContext?.sessionId ?? null,\n requestPath: requestContext?.requestPath ?? null,\n requestMethod: requestContext?.requestMethod ?? null,\n requestIpAddress: requestContext?.requestIpAddress ?? null,\n requestUserAgent: requestContext?.requestUserAgent ?? null,\n data,\n retentionDays: this.options.retentionDays,\n });\n } catch {\n // record() never throws, but the bridge itself must also never\n // fail the originating publish.\n }\n }\n}\n\nfunction resolveEntityRealmId(\n type: string,\n data: unknown,\n): string | null | undefined {\n if (!isObject(data)) {\n return undefined;\n }\n\n const key = ENTITY_REALM_KEY_MAP[type as `${EntityType}`] ?? 'realmId';\n if (!hasOwnProperty(data, key)) {\n return undefined;\n }\n\n return typeof data[key] === 'string' ? data[key] : null;\n}\n"],"mappings":"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"}
|
|
@@ -2,7 +2,15 @@ import type { IdentityProvider, IdentityProviderProtocol } from '@authup/core-ki
|
|
|
2
2
|
import type { IEntityRepository } from '@authup/server-kit';
|
|
3
3
|
export interface IIdentityProviderRepository extends IEntityRepository<IdentityProvider> {
|
|
4
4
|
checkUniqueness(data: Partial<IdentityProvider>, existing?: IdentityProvider): Promise<void>;
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* `keepAll` leaves attributes the caller never mentioned in place, which is
|
|
7
|
+
* what a partial update needs: the default replaces the whole set, so an
|
|
8
|
+
* omitted key is deleted. The option is the persistence layer's own, named
|
|
9
|
+
* here structurally so core stays free of the adapter's types.
|
|
10
|
+
*/
|
|
11
|
+
saveWithEA(entity: IdentityProvider, attributes?: Record<string, any>, options?: {
|
|
12
|
+
keepAll?: boolean;
|
|
13
|
+
}): Promise<IdentityProvider>;
|
|
6
14
|
findByProtocol(protocol: IdentityProviderProtocol, realmKey?: string): Promise<IdentityProvider[]>;
|
|
7
15
|
}
|
|
8
16
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/core/entities/identity-provider/types.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AACnF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,MAAM,WAAW,2BAA4B,SAAQ,iBAAiB,CAAC,gBAAgB,CAAC;IACpF,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,gBAAgB,CAAC,EAAE,QAAQ,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7F,UAAU,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/core/entities/identity-provider/types.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AACnF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,MAAM,WAAW,2BAA4B,SAAQ,iBAAiB,CAAC,gBAAgB,CAAC;IACpF,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,gBAAgB,CAAC,EAAE,QAAQ,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7F;;;;;OAKG;IACH,UAAU,CACN,MAAM,EAAE,gBAAgB,EACxB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAChC,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAChC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAE7B,cAAc,CAAC,QAAQ,EAAE,wBAAwB,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;CACtG"}
|
|
@@ -5,6 +5,7 @@ import { applyJunctionCreateGrant, buildJunctionUpdateData } from "./permission/
|
|
|
5
5
|
import { toIdentityPolicyData } from "./permission/identity-policy-data.mjs";
|
|
6
6
|
import { IdentityProviderLdapAuthenticator } from "./provider/authentication/protocols/ldap/module.mjs";
|
|
7
7
|
import { IdentityProviderLdapCollectionAuthenticator } from "./provider/authentication/protocols/ldap/collection.mjs";
|
|
8
|
+
import { IDENTITY_PROVIDER_ASSURANCE_ERROR_INSTANCE, IdentityProviderAssuranceError, assertIdentityProviderAssurance, buildIdentityProviderAcrValues, isIdentityProviderAssuranceError } from "./provider/authentication/protocols/oauth2/assurance.mjs";
|
|
8
9
|
import { IdentityProviderOAuth2Authenticator } from "./provider/authentication/protocols/oauth2/module.mjs";
|
|
9
10
|
import { IdentityProviderOpenIDAuthenticator } from "./provider/authentication/protocols/open-id/module.mjs";
|
|
10
11
|
import { IdentityProviderFacebookAuthenticator } from "./provider/authentication/presets/facebook.mjs";
|
|
@@ -36,4 +37,4 @@ import "./registration/index.mjs";
|
|
|
36
37
|
import { IdentityResolver } from "./resolver/module.mjs";
|
|
37
38
|
import "./resolver/index.mjs";
|
|
38
39
|
import { IdentityRoleProvider } from "./role/module.mjs";
|
|
39
|
-
export { IDENTITY_PROVIDER_ACCOUNT_ALREADY_LINKED_ERROR_INSTANCE, IDENTITY_PROVIDER_ACCOUNT_LINK_TTL, IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN, IdentityPermissionProvider, IdentityProviderAccountAlreadyLinkedError, IdentityProviderAccountBaseMapper, IdentityProviderAccountManager, IdentityProviderAttributeMapper, IdentityProviderFacebookAuthenticator, IdentityProviderGithubAuthenticator, IdentityProviderGoogleAuthenticator, IdentityProviderInstagramAuthenticator, IdentityProviderLdapAuthenticator, IdentityProviderLdapCollectionAuthenticator, IdentityProviderMapperOperation, IdentityProviderOAuth2Authenticator, IdentityProviderOpenIDAuthenticator, IdentityProviderPaypalAuthenticator, IdentityProviderPermissionMapper, IdentityProviderRoleMapper, IdentityResolver, IdentityRoleProvider, PASSWORD_RESET_EXPIRES_IN_MINUTES, PasswordRecoveryService, PermissionCheckerService, PolicyCheckerService, RegistrationService, applyJunctionCreateGrant, buildJunctionUpdateData, createIdentityProviderOAuth2Authenticator, isIdentityProviderAccountAlreadyLinkedError, toIdentityPolicyData };
|
|
40
|
+
export { IDENTITY_PROVIDER_ACCOUNT_ALREADY_LINKED_ERROR_INSTANCE, IDENTITY_PROVIDER_ACCOUNT_LINK_TTL, IDENTITY_PROVIDER_ASSURANCE_ERROR_INSTANCE, IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN, IdentityPermissionProvider, IdentityProviderAccountAlreadyLinkedError, IdentityProviderAccountBaseMapper, IdentityProviderAccountManager, IdentityProviderAssuranceError, IdentityProviderAttributeMapper, IdentityProviderFacebookAuthenticator, IdentityProviderGithubAuthenticator, IdentityProviderGoogleAuthenticator, IdentityProviderInstagramAuthenticator, IdentityProviderLdapAuthenticator, IdentityProviderLdapCollectionAuthenticator, IdentityProviderMapperOperation, IdentityProviderOAuth2Authenticator, IdentityProviderOpenIDAuthenticator, IdentityProviderPaypalAuthenticator, IdentityProviderPermissionMapper, IdentityProviderRoleMapper, IdentityResolver, IdentityRoleProvider, PASSWORD_RESET_EXPIRES_IN_MINUTES, PasswordRecoveryService, PermissionCheckerService, PolicyCheckerService, RegistrationService, applyJunctionCreateGrant, assertIdentityProviderAssurance, buildIdentityProviderAcrValues, buildJunctionUpdateData, createIdentityProviderOAuth2Authenticator, isIdentityProviderAccountAlreadyLinkedError, isIdentityProviderAssuranceError, toIdentityPolicyData };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
import { IdentityProviderAccountAlreadyLinkedError } from "./error.mjs";
|
|
5
4
|
import { describeError } from "../../../../utils/error.mjs";
|
|
6
5
|
import "../../../../utils/index.mjs";
|
|
6
|
+
import { IdentityProviderAccountAlreadyLinkedError } from "./error.mjs";
|
|
7
7
|
import { IdentityProviderIdentityOperation } from "../constants.mjs";
|
|
8
8
|
import { IdentityProviderMapperOperation } from "../mapper/constants.mjs";
|
|
9
9
|
import "../mapper/index.mjs";
|
|
@@ -3,6 +3,7 @@ import "node:url";
|
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { IdentityProviderLdapAuthenticator } from "./protocols/ldap/module.mjs";
|
|
5
5
|
import { IdentityProviderLdapCollectionAuthenticator } from "./protocols/ldap/collection.mjs";
|
|
6
|
+
import { IDENTITY_PROVIDER_ASSURANCE_ERROR_INSTANCE, IdentityProviderAssuranceError, assertIdentityProviderAssurance, buildIdentityProviderAcrValues, isIdentityProviderAssuranceError } from "./protocols/oauth2/assurance.mjs";
|
|
6
7
|
import { IdentityProviderOAuth2Authenticator } from "./protocols/oauth2/module.mjs";
|
|
7
8
|
import { IdentityProviderOpenIDAuthenticator } from "./protocols/open-id/module.mjs";
|
|
8
9
|
import "./protocols/index.mjs";
|
|
@@ -14,4 +15,4 @@ import { IdentityProviderPaypalAuthenticator } from "./presets/paypal.mjs";
|
|
|
14
15
|
import "./presets/index.mjs";
|
|
15
16
|
import { createIdentityProviderOAuth2Authenticator } from "./factory.mjs";
|
|
16
17
|
import { IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN } from "./constants.mjs";
|
|
17
|
-
export { IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN, IdentityProviderFacebookAuthenticator, IdentityProviderGithubAuthenticator, IdentityProviderGoogleAuthenticator, IdentityProviderInstagramAuthenticator, IdentityProviderLdapAuthenticator, IdentityProviderLdapCollectionAuthenticator, IdentityProviderOAuth2Authenticator, IdentityProviderOpenIDAuthenticator, IdentityProviderPaypalAuthenticator, createIdentityProviderOAuth2Authenticator };
|
|
18
|
+
export { IDENTITY_PROVIDER_ASSURANCE_ERROR_INSTANCE, IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN, IdentityProviderAssuranceError, IdentityProviderFacebookAuthenticator, IdentityProviderGithubAuthenticator, IdentityProviderGoogleAuthenticator, IdentityProviderInstagramAuthenticator, IdentityProviderLdapAuthenticator, IdentityProviderLdapCollectionAuthenticator, IdentityProviderOAuth2Authenticator, IdentityProviderOpenIDAuthenticator, IdentityProviderPaypalAuthenticator, assertIdentityProviderAssurance, buildIdentityProviderAcrValues, createIdentityProviderOAuth2Authenticator, isIdentityProviderAssuranceError };
|
|
@@ -4,8 +4,9 @@ import.meta.url;
|
|
|
4
4
|
import { IdentityProviderLdapAuthenticator } from "./ldap/module.mjs";
|
|
5
5
|
import { IdentityProviderLdapCollectionAuthenticator } from "./ldap/collection.mjs";
|
|
6
6
|
import "./ldap/index.mjs";
|
|
7
|
+
import { IDENTITY_PROVIDER_ASSURANCE_ERROR_INSTANCE, IdentityProviderAssuranceError, assertIdentityProviderAssurance, buildIdentityProviderAcrValues, isIdentityProviderAssuranceError } from "./oauth2/assurance.mjs";
|
|
7
8
|
import { IdentityProviderOAuth2Authenticator } from "./oauth2/module.mjs";
|
|
8
9
|
import "./oauth2/index.mjs";
|
|
9
10
|
import { IdentityProviderOpenIDAuthenticator } from "./open-id/module.mjs";
|
|
10
11
|
import "./open-id/index.mjs";
|
|
11
|
-
export { IdentityProviderLdapAuthenticator, IdentityProviderLdapCollectionAuthenticator, IdentityProviderOAuth2Authenticator, IdentityProviderOpenIDAuthenticator };
|
|
12
|
+
export { IDENTITY_PROVIDER_ASSURANCE_ERROR_INSTANCE, IdentityProviderAssuranceError, IdentityProviderLdapAuthenticator, IdentityProviderLdapCollectionAuthenticator, IdentityProviderOAuth2Authenticator, IdentityProviderOpenIDAuthenticator, assertIdentityProviderAssurance, buildIdentityProviderAcrValues, isIdentityProviderAssuranceError };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { OAuth2IdentityProviderBase } from '@authup/core-kit';
|
|
2
|
+
import { ValidationError } from '@authup/errors';
|
|
3
|
+
export declare const IDENTITY_PROVIDER_ASSURANCE_ERROR_INSTANCE: unique symbol;
|
|
4
|
+
/**
|
|
5
|
+
* The upstream did not meet the provider's `requiredAmr` / `requiredAcr`.
|
|
6
|
+
*
|
|
7
|
+
* It adds no dedicated `ErrorCode` and keeps `ValidationError`'s shared
|
|
8
|
+
* `BAD_REQUEST`, so the marker is the ONLY thing the guard may read - a
|
|
9
|
+
* code fallback would match every other `ValidationError` in the process.
|
|
10
|
+
* That is sound here because both callers swallow it into a redirect (the
|
|
11
|
+
* login bounces to the hosted page with `access_denied`, the link callback
|
|
12
|
+
* with `linkError`), so it never crosses a wire boundary. The reason lands
|
|
13
|
+
* in the operator's log instead, which is where a misconfigured allow-list
|
|
14
|
+
* is debugged.
|
|
15
|
+
*/
|
|
16
|
+
export declare class IdentityProviderAssuranceError extends ValidationError {
|
|
17
|
+
constructor(message: string);
|
|
18
|
+
}
|
|
19
|
+
export declare function isIdentityProviderAssuranceError(input: unknown): input is IdentityProviderAssuranceError;
|
|
20
|
+
/**
|
|
21
|
+
* The `acr_values` an outbound authorize request asks for, so the upstream is
|
|
22
|
+
* asked to step up rather than merely observed. Voluntary in OIDC Core
|
|
23
|
+
* 5.5.1.1 - a provider may ignore it and answer anyway, which is exactly what
|
|
24
|
+
* {@see assertIdentityProviderAssurance} is for.
|
|
25
|
+
*/
|
|
26
|
+
export declare function buildIdentityProviderAcrValues(provider: Partial<OAuth2IdentityProviderBase>): string | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Verify what the upstream actually returned against the provider's
|
|
29
|
+
* allow-lists.
|
|
30
|
+
*
|
|
31
|
+
* Fail closed on every uncertainty: the claims are read from the `id_token`
|
|
32
|
+
* alone, so a provider that returns none - or one whose token carries neither
|
|
33
|
+
* claim, or cannot be decoded - is refused once an allow-list is set. Failing
|
|
34
|
+
* open there would make the feature decorative.
|
|
35
|
+
*
|
|
36
|
+
* The token is DECODED, not verified, and that is not an oversight. It is the
|
|
37
|
+
* return value of the server's own back-channel POST to the operator-configured
|
|
38
|
+
* `tokenUrl`, so OIDC Core 3.1.3.7 item 6 accepts TLS server validation in
|
|
39
|
+
* place of checking the signature. No browser-supplied token reaches here, and
|
|
40
|
+
* a provider the operator configured is the authentication authority for these
|
|
41
|
+
* users anyway: one that lies about `amr` could equally mint any subject. What
|
|
42
|
+
* item 6 does NOT waive is the audience, which is checked below because
|
|
43
|
+
* `clientId` is already in hand.
|
|
44
|
+
*/
|
|
45
|
+
export declare function assertIdentityProviderAssurance(provider: Partial<OAuth2IdentityProviderBase>, idToken?: string): void;
|
|
46
|
+
//# sourceMappingURL=assurance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assurance.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/identity/provider/authentication/protocols/oauth2/assurance.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,eAAe,EAAqC,MAAM,gBAAgB,CAAC;AAGpF,eAAO,MAAM,0CAA0C,eAAmE,CAAC;AAE3H;;;;;;;;;;;GAWG;AACH,qBAAa,8BAA+B,SAAQ,eAAe;gBACnD,OAAO,EAAE,MAAM;CAI9B;AAED,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,8BAA8B,CAExG;AAiBD;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,OAAO,CAAC,0BAA0B,CAAC,GAAG,MAAM,GAAG,SAAS,CAGhH;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,+BAA+B,CAC3C,QAAQ,EAAE,OAAO,CAAC,0BAA0B,CAAC,EAC7C,OAAO,CAAC,EAAE,MAAM,GACjB,IAAI,CAgEN"}
|
|
@@ -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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"}
|
|
@@ -3,6 +3,7 @@ import "node:url";
|
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { IdentityProviderLdapAuthenticator } from "./authentication/protocols/ldap/module.mjs";
|
|
5
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";
|
|
6
7
|
import { IdentityProviderOAuth2Authenticator } from "./authentication/protocols/oauth2/module.mjs";
|
|
7
8
|
import { IdentityProviderOpenIDAuthenticator } from "./authentication/protocols/open-id/module.mjs";
|
|
8
9
|
import { IdentityProviderFacebookAuthenticator } from "./authentication/presets/facebook.mjs";
|
|
@@ -23,4 +24,4 @@ import { IdentityProviderAccountManager } from "./account/module.mjs";
|
|
|
23
24
|
import "./account/index.mjs";
|
|
24
25
|
import { IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN } from "./authentication/constants.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 };
|