@authup/server-core 1.0.0-beta.61 → 1.0.0-beta.62
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/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/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 +3 -5
- package/dist/adapters/http/controllers/entities/identity-provider/module.d.ts.map +1 -1
- package/dist/adapters/http/controllers/entities/identity-provider/module.mjs +54 -82
- 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/authorize/module.d.ts.map +1 -1
- package/dist/adapters/http/controllers/workflows/authorize/module.mjs +9 -5
- package/dist/adapters/http/controllers/workflows/authorize/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/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/read/env.mjs +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-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 +16 -5
- 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/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/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/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-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 +10 -10
- 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 +1 -1
- package/dist/core/identity/provider/index.mjs +10 -10
- package/dist/core/index.mjs +14 -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/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 +5 -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/index.d.ts +3 -0
- package/dist/core/oauth2/federated-login/index.d.ts.map +1 -0
- package/dist/core/oauth2/federated-login/index.mjs +6 -0
- package/dist/core/oauth2/federated-login/module.d.ts +20 -0
- package/dist/core/oauth2/federated-login/module.d.ts.map +1 -0
- package/dist/core/oauth2/federated-login/module.mjs +137 -0
- package/dist/core/oauth2/federated-login/module.mjs.map +1 -0
- package/dist/core/oauth2/federated-login/types.d.ts +138 -0
- package/dist/core/oauth2/federated-login/types.d.ts.map +1 -0
- package/dist/core/oauth2/federated-login/types.mjs +31 -0
- package/dist/core/oauth2/federated-login/types.mjs.map +1 -0
- 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 +4 -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 +30 -2
- 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 +28 -28
|
@@ -5,8 +5,10 @@ import { RECORD_QUERY_PARAMETERS, describeQuerySchema } from "../../../../../cor
|
|
|
5
5
|
import { identityProviderSchema } from "../../../../../core/entities/identity-provider/schema.mjs";
|
|
6
6
|
import { decodeQuery } from "../../../../../core/query/module.mjs";
|
|
7
7
|
import { OAuth2AuthorizationCodeRequestValidator } from "../../../../../core/oauth2/authorization/code-request/validator.mjs";
|
|
8
|
-
import { toIdentityPolicyData } from "../../../../../core/identity/permission/identity-policy-data.mjs";
|
|
9
8
|
import { createIdentityProviderOAuth2Authenticator } from "../../../../../core/identity/provider/authentication/factory.mjs";
|
|
9
|
+
import { describeError } from "../../../../../utils/error.mjs";
|
|
10
|
+
import { resolveURL } from "../../../../../utils/url.mjs";
|
|
11
|
+
import "../../../../../utils/index.mjs";
|
|
10
12
|
import "../../../../../core/index.mjs";
|
|
11
13
|
import { useRequestPermissionEvaluator } from "../../../request/permission/helper.mjs";
|
|
12
14
|
import { useRequestIdentityOrFail } from "../../../request/helpers/identity.mjs";
|
|
@@ -17,15 +19,14 @@ import { applyRouteRealmIDToBody, getRequestRealmID } from "../../../request/hel
|
|
|
17
19
|
import { useRequestEventContext } from "../../../request/event-context.mjs";
|
|
18
20
|
import "../../../request/index.mjs";
|
|
19
21
|
import { ForceLoggedInMiddleware } from "../../../middleware/built-in/loggedin/module.mjs";
|
|
20
|
-
import {
|
|
21
|
-
import { resolveURL } from "../../../../../utils/url.mjs";
|
|
22
|
-
import "../../../../../utils/index.mjs";
|
|
22
|
+
import { renderAuthConsolePage } from "../../../ui/auth-console/module.mjs";
|
|
23
23
|
import "../../../middleware/index.mjs";
|
|
24
|
+
import "../../../ui/index.mjs";
|
|
24
25
|
import { ValidatorGroup, base64URLDecode, isObject, isUUID } from "@authup/kit";
|
|
25
|
-
import { EventName, EventRefType, EventScope, IdentityProviderAttributesValidator, IdentityProviderValidator, IdentityType, PermissionName,
|
|
26
|
+
import { EventName, EventRefType, EventScope, IdentityProviderAttributesValidator, IdentityProviderValidator, IdentityType, PermissionName, buildIdentityProviderAuthorizeCallbackPath, isOAuth2IdentityProvider, isOpenIDIdentityProvider } from "@authup/core-kit";
|
|
26
27
|
import { BuiltInPolicyType, definePolicyData } from "@authup/access";
|
|
27
|
-
import { BadRequestError,
|
|
28
|
-
import {
|
|
28
|
+
import { BadRequestError, EntityNotFoundError } from "@authup/errors";
|
|
29
|
+
import { OAuth2RequestError } from "@authup/specs";
|
|
29
30
|
import { URL } from "node:url";
|
|
30
31
|
import { getRequestHeader, getRequestIP, sendRedirect } from "routup";
|
|
31
32
|
import { readRequestBody } from "@routup/basic/body";
|
|
@@ -49,27 +50,23 @@ function _ts_param(paramIndex, decorator) {
|
|
|
49
50
|
var IdentityProviderController = class {
|
|
50
51
|
options;
|
|
51
52
|
repository;
|
|
52
|
-
realmRepository;
|
|
53
53
|
accountManager;
|
|
54
54
|
linkStore;
|
|
55
55
|
codeRequestVerifier;
|
|
56
56
|
codeRequestValidator;
|
|
57
57
|
stateManager;
|
|
58
|
-
|
|
59
|
-
accessPolicyEvaluator;
|
|
58
|
+
loginService;
|
|
60
59
|
eventService;
|
|
61
60
|
logger;
|
|
62
61
|
constructor(ctx) {
|
|
63
62
|
this.options = ctx.options;
|
|
64
63
|
this.repository = ctx.repository;
|
|
65
|
-
this.realmRepository = ctx.realmRepository;
|
|
66
64
|
this.accountManager = ctx.accountManager;
|
|
67
65
|
this.linkStore = ctx.linkStore;
|
|
68
|
-
this.codeIssuer = ctx.codeIssuer;
|
|
69
66
|
this.codeRequestVerifier = ctx.codeRequestVerifier;
|
|
70
67
|
this.codeRequestValidator = new OAuth2AuthorizationCodeRequestValidator();
|
|
71
68
|
this.stateManager = ctx.stateManager;
|
|
72
|
-
this.
|
|
69
|
+
this.loginService = ctx.loginService;
|
|
73
70
|
this.eventService = ctx.eventService;
|
|
74
71
|
this.logger = ctx.logger;
|
|
75
72
|
}
|
|
@@ -152,22 +149,20 @@ var IdentityProviderController = class {
|
|
|
152
149
|
const id = useRequestParamID(event);
|
|
153
150
|
const entity = await this.resolve(id);
|
|
154
151
|
if (!isOAuth2IdentityProvider(entity) && !isOpenIDIdentityProvider(entity)) throw new BadRequestError("Only an identity-provider based on the oauth protocol supports authorize redirect.");
|
|
155
|
-
|
|
156
|
-
let codeRequest;
|
|
152
|
+
if (!entity.enabled) throw new BadRequestError("The identity provider is not enabled.");
|
|
157
153
|
const query = useRequestQuery(event);
|
|
158
|
-
if (typeof query.codeRequest
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
const codeRequestValidated = await this.codeRequestValidator.run(codeRequestDecoded);
|
|
166
|
-
const data = await this.codeRequestVerifier.verify(codeRequestValidated);
|
|
167
|
-
if (data.client.realmId && entity.realmId && entity.realmId !== data.client.realmId) throw OAuth2RequestError.malformed("The provider and client realm do not match.");
|
|
168
|
-
codeRequest = data.data;
|
|
154
|
+
if (typeof query.codeRequest !== "string") throw OAuth2RequestError.malformed("A federated login requires an authorization code request.");
|
|
155
|
+
let codeRequestDecoded;
|
|
156
|
+
try {
|
|
157
|
+
codeRequestDecoded = JSON.parse(base64URLDecode(query.codeRequest));
|
|
158
|
+
} catch {
|
|
159
|
+
throw OAuth2RequestError.malformed("The code request is malformed and can not be parsed.");
|
|
169
160
|
}
|
|
170
|
-
const
|
|
161
|
+
const codeRequestValidated = await this.codeRequestValidator.run(codeRequestDecoded);
|
|
162
|
+
const data = await this.codeRequestVerifier.verify(codeRequestValidated);
|
|
163
|
+
if (data.client.realmId && entity.realmId && entity.realmId !== data.client.realmId) throw OAuth2RequestError.malformed("The provider and client realm do not match.");
|
|
164
|
+
const authenticator = this.buildProviderAuthenticator(entity);
|
|
165
|
+
const state = await this.saveAuthorizationState(event, { codeRequest: data.data });
|
|
171
166
|
return sendRedirect(event, authenticator.buildRedirectURL({ state }));
|
|
172
167
|
}
|
|
173
168
|
async linkRequest(_id, event) {
|
|
@@ -192,66 +187,43 @@ var IdentityProviderController = class {
|
|
|
192
187
|
const data = await this.verifyAuthorizationState(event);
|
|
193
188
|
const { link } = data;
|
|
194
189
|
if (link) return this.completeLink(event, entity, data, link);
|
|
195
|
-
if (
|
|
196
|
-
const { code } = useRequestQuery(event);
|
|
197
|
-
if (typeof
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const user = await this.buildProviderAuthenticator(entity, { clientId: verified?.data.client_id }).authenticate({ code });
|
|
201
|
-
if (!user.active) throw new EntityInactiveError({ entity: "user" });
|
|
202
|
-
const realm = await this.realmRepository.resolve(entity.realmId, true);
|
|
203
|
-
if (verified?.client.accessPolicyId) {
|
|
204
|
-
let allowed = false;
|
|
205
|
-
const subject = toIdentityPolicyData({
|
|
206
|
-
type: IdentityType.USER,
|
|
207
|
-
data: {
|
|
208
|
-
...user,
|
|
209
|
-
realm
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
if (this.accessPolicyEvaluator && subject) allowed = await this.accessPolicyEvaluator.evaluate(verified.client.accessPolicyId, subject);
|
|
213
|
-
if (!allowed) {
|
|
214
|
-
const url = this.buildHostedAuthorizeURL(verified.data);
|
|
215
|
-
url.searchParams.set("error", OAuth2ErrorCode.ACCESS_DENIED);
|
|
216
|
-
return sendRedirect(event, url.href);
|
|
217
|
-
}
|
|
190
|
+
if (!data.codeRequest) throw OAuth2RequestError.malformed("The state carries no authorization code request.");
|
|
191
|
+
const { code, error } = useRequestQuery(event);
|
|
192
|
+
if (typeof error === "string" && error.length > 0) {
|
|
193
|
+
this.logger?.info(`The identity provider ${entity.id} answered the authorization with ${JSON.stringify(error.slice(0, 64))}.`);
|
|
194
|
+
return sendRedirect(event, this.buildHostedAuthorizeURL(data.codeRequest).href);
|
|
218
195
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
scope: EventScope.OAUTH2,
|
|
228
|
-
name: EventName.AUTHORIZE,
|
|
229
|
-
refType: EventRefType.CLIENT,
|
|
230
|
-
refId: verified?.data.client_id ?? null,
|
|
231
|
-
clientId: verified?.data.client_id ?? null,
|
|
232
|
-
sessionId: null,
|
|
233
|
-
actorType: IdentityType.USER,
|
|
234
|
-
actorId: user.id,
|
|
235
|
-
actorName: user.name,
|
|
236
|
-
realmId: verified?.data.realm_id ?? realm.id,
|
|
237
|
-
requestIpAddress: getRequestIP(event) ?? null,
|
|
238
|
-
requestUserAgent: getRequestHeader(event, "user-agent") ?? null,
|
|
239
|
-
data: {
|
|
240
|
-
reason: "federated",
|
|
241
|
-
providerId: entity.id,
|
|
242
|
-
providerName: entity.name,
|
|
243
|
-
...verified?.data.scope ? { scope: verified.data.scope } : {}
|
|
196
|
+
if (typeof code !== "string" || code.length === 0) throw new BadRequestError("The authorization code is missing.");
|
|
197
|
+
const result = await this.loginService.complete({
|
|
198
|
+
provider: entity,
|
|
199
|
+
codeRequest: data.codeRequest,
|
|
200
|
+
code,
|
|
201
|
+
request: {
|
|
202
|
+
ipAddress: getRequestIP(event),
|
|
203
|
+
userAgent: getRequestHeader(event, "user-agent")
|
|
244
204
|
}
|
|
245
205
|
});
|
|
246
|
-
if (
|
|
247
|
-
const url =
|
|
248
|
-
url.searchParams.set("
|
|
249
|
-
if (verified.data.state) url.searchParams.set("state", verified.data.state);
|
|
206
|
+
if (result.kind === "refused") {
|
|
207
|
+
const url = this.buildHostedAuthorizeURL(result.codeRequest);
|
|
208
|
+
if (result.error) url.searchParams.set("error", result.error);
|
|
250
209
|
return sendRedirect(event, url.href);
|
|
251
210
|
}
|
|
252
|
-
const url = new URL(
|
|
253
|
-
url.searchParams.set("code",
|
|
254
|
-
|
|
211
|
+
const url = new URL(result.redirectUri);
|
|
212
|
+
url.searchParams.set("code", result.code);
|
|
213
|
+
if (result.state) url.searchParams.set("state", result.state);
|
|
214
|
+
if (url.protocol === "http:" || url.protocol === "https:") return sendRedirect(event, url.href);
|
|
215
|
+
const payload = {
|
|
216
|
+
redirect: url.href,
|
|
217
|
+
authorizeUrl: this.buildHostedAuthorizeURL(result.codeRequest).href,
|
|
218
|
+
client: result.client
|
|
219
|
+
};
|
|
220
|
+
return renderAuthConsolePage(event, {
|
|
221
|
+
url: buildIdentityProviderAuthorizeCallbackPath(entity.id),
|
|
222
|
+
payload: {
|
|
223
|
+
config: { baseURL: this.options.baseURL },
|
|
224
|
+
data: payload
|
|
225
|
+
}
|
|
226
|
+
});
|
|
255
227
|
}
|
|
256
228
|
async write(event, options = {}) {
|
|
257
229
|
let group;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.mjs","names":["BuiltInPolicyType","definePolicyData","ValidatorGroup","base64URLDecode","isObject","isUUID","DBody","DContext","DController","DDelete","DGet","DPath","DPost","DPut","DTags","getRequestHeader","getRequestIP","sendRedirect","EventName","EventRefType","EventScope","IdentityProviderAttributesValidator","IdentityProviderValidator","IdentityType","PermissionName","SessionAuthMethod","isOAuth2IdentityProvider","isOpenIDIdentityProvider","BadRequestError","EntityInactiveError","EntityNotFoundError","describeError","resolveURL","useRequestQuery","readRequestBody","OAuth2ErrorCode","OAuth2RequestError","URL","OAuth2AuthorizationCodeRequestValidator","RECORD_QUERY_PARAMETERS","createIdentityProviderOAuth2Authenticator","decodeQuery","describeQuerySchema","identityProviderSchema","toIdentityPolicyData","applyRouteRealmIDToBody","buildActorContext","getBodyRealmID","getRequestParamID","getRequestRealmID","useRequestEventContext","useRequestIdentityOrFail","useRequestParamID","useRequestPermissionEvaluator","ForceLoggedInMiddleware","IdentityProviderController","options","repository","realmRepository","accountManager","linkStore","codeRequestVerifier","codeRequestValidator","stateManager","codeIssuer","accessPolicyEvaluator","eventService","logger","ctx","getProviders","event","data","meta","findMany","schema","actor","permissionEvaluator","preEvaluate","name","IDENTITY_PROVIDER_READ","datum","evaluate","ATTRIBUTES","REALM_MATCH","realmId","getProvider","id","paramId","entity","findOneByIdOrName","editProvider","user","write","updateOnly","put","dropProvider","IDENTITY_PROVIDER_DELETE","findOneBy","entityId","remove","response","status","addProvider","authorizeOut","_id","resolve","authenticator","buildProviderAuthenticator","codeRequest","query","codeRequestDecoded","JSON","parse","malformed","codeRequestValidated","run","verify","client","state","saveAuthorizationState","buildRedirectURL","linkRequest","enabled","identity","type","USER","link","userId","providerId","url","authorizeIn","Error","protocol","verifyAuthorizationState","completeLink","realm_id","code","length","verified","undefined","clientId","client_id","authenticate","active","realm","accessPolicyId","allowed","subject","buildHostedAuthorizeURL","searchParams","set","ACCESS_DENIED","href","authorizationCode","issue","response_type","authMethod","EXTERNAL","record","scope","OAUTH2","AUTHORIZE","refType","CLIENT","refId","sessionId","actorType","actorId","actorName","requestIpAddress","requestUserAgent","reason","providerName","redirectUriVerified","redirect_uri","baseURL","group","body","where","IDENTITY_PROVIDER_UPDATE","UPDATE","IDENTITY_PROVIDER_CREATE","CREATE","validator","attributesValidator","attributes","validateJoinColumns","checkUniqueness","merge","saveWithEA","create","provider","resolveIdentity","handle","save","providerUserId","providerUserName","pickIdentityCandidate","providerUserEmail","e","error","confirmLink","_data","consume","account","attributeCandidates","email","requestContext","IDENTITY","IDENTITY_PROVIDER_LINKED","IDENTITY_PROVIDER_ACCOUNT","userRealmId","requestPath","requestMethod","key","candidates","candidate","codeRequestKeys","Object","keys","codeRequestKey_","codeRequestKey","codeRequestValue","String","findOneById","ip","userAgent","stateInvalid"],"sources":["../../../../../../src/adapters/http/controllers/entities/identity-provider/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2022.\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 { BuiltInPolicyType, definePolicyData } from '@authup/access';\nimport { \n ValidatorGroup, \n base64URLDecode, \n isObject, \n isUUID, \n} from '@authup/kit';\nimport {\n DBody,\n DContext,\n DController,\n DDelete,\n DGet,\n DPath,\n DPost,\n DPut,\n DTags,\n} from '@routup/decorators';\nimport type { IAppEvent } from 'routup';\nimport {\n getRequestHeader,\n getRequestIP,\n sendRedirect,\n} from 'routup';\nimport type {\n IdentityProvider,\n IdentityProviderAccount,\n OAuth2AuthorizationCodeRequest,\n OAuth2IdentityProvider,\n OpenIDIdentityProvider,\n} from '@authup/core-kit';\nimport {\n EventName,\n EventRefType,\n EventScope,\n IdentityProviderAttributesValidator,\n IdentityProviderValidator,\n IdentityType,\n PermissionName,\n SessionAuthMethod,\n isOAuth2IdentityProvider,\n isOpenIDIdentityProvider,\n} from '@authup/core-kit';\nimport { BadRequestError, EntityInactiveError, EntityNotFoundError } from '@authup/errors';\nimport type { Logger } from '@authup/server-kit';\nimport { describeError, resolveURL } from '../../../../../utils/index.ts';\nimport { useRequestQuery } from '@routup/basic/query';\nimport { readRequestBody } from '@routup/basic/body';\nimport { OAuth2ErrorCode, OAuth2RequestError } from '@authup/specs';\nimport type {\n EntityCollectionResponse,\n EntityRecordResponse,\n IdentityProviderCreatePayload,\n IdentityProviderLinkConfirmPayload,\n IdentityProviderLinkRequestResponse,\n IdentityProviderSavePayload,\n IdentityProviderUpdatePayload,\n} from '@authup/core-http-kit';\nimport { URL } from 'node:url';\nimport type {\n IEventService,\n IIdentityProviderAccountLinkStore,\n IIdentityProviderAccountManager,\n IIdentityProviderRepository,\n IOAuth2AccessPolicyEvaluator,\n IOAuth2AuthorizationCodeIssuer,\n IOAuth2AuthorizationCodeRequestVerifier,\n IOAuth2AuthorizationStateManager,\n IRealmRepository,\n IdentityProviderIdentity,\n OAuth2AuthorizationState,\n OAuth2AuthorizationStateLink,\n} from '../../../../../core/index.ts';\nimport {\n OAuth2AuthorizationCodeRequestValidator,\n RECORD_QUERY_PARAMETERS,\n createIdentityProviderOAuth2Authenticator,\n decodeQuery,\n describeQuerySchema,\n identityProviderSchema,\n toIdentityPolicyData,\n} from '../../../../../core/index.ts';\nimport {\n applyRouteRealmIDToBody,\n buildActorContext,\n getBodyRealmID,\n getRequestParamID,\n getRequestRealmID,\n useRequestEventContext,\n useRequestIdentityOrFail,\n useRequestParamID,\n useRequestPermissionEvaluator,\n} from '../../../request/index.ts';\nimport { ForceLoggedInMiddleware } from '../../../middleware/index.ts';\nimport type { IdentityProviderControllerContext, IdentityProviderControllerOptions } from './types.ts';\n\n@DTags('identity')\n@DController(['/identity-providers', '/realms/:realmId/identity-providers'])\nexport class IdentityProviderController {\n protected options: IdentityProviderControllerOptions;\n\n protected repository: IIdentityProviderRepository;\n\n protected realmRepository: IRealmRepository;\n\n protected accountManager: IIdentityProviderAccountManager;\n\n protected linkStore: IIdentityProviderAccountLinkStore;\n\n protected codeRequestVerifier : IOAuth2AuthorizationCodeRequestVerifier;\n\n protected codeRequestValidator : OAuth2AuthorizationCodeRequestValidator;\n\n protected stateManager : IOAuth2AuthorizationStateManager;\n\n protected codeIssuer : IOAuth2AuthorizationCodeIssuer;\n\n protected accessPolicyEvaluator? : IOAuth2AccessPolicyEvaluator;\n\n protected eventService? : IEventService;\n\n protected logger? : Logger;\n\n // ---------------------------------------------------------\n\n constructor(ctx: IdentityProviderControllerContext) {\n this.options = ctx.options;\n this.repository = ctx.repository;\n this.realmRepository = ctx.realmRepository;\n this.accountManager = ctx.accountManager;\n this.linkStore = ctx.linkStore;\n this.codeIssuer = ctx.codeIssuer;\n this.codeRequestVerifier = ctx.codeRequestVerifier;\n this.codeRequestValidator = new OAuth2AuthorizationCodeRequestValidator();\n this.stateManager = ctx.stateManager;\n this.accessPolicyEvaluator = ctx.accessPolicyEvaluator;\n this.eventService = ctx.eventService;\n this.logger = ctx.logger;\n }\n\n // ---------------------------------------------------------\n\n @DGet('', [])\n async getProviders(\n @DContext() event: IAppEvent,\n ): Promise<EntityCollectionResponse<IdentityProvider>> {\n const {\n data,\n meta,\n } = await this.repository.findMany(\n await decodeQuery(useRequestQuery(event), {\n schema: identityProviderSchema,\n actor: buildActorContext(event),\n }),\n );\n\n try {\n const permissionEvaluator = useRequestPermissionEvaluator(event);\n await permissionEvaluator.preEvaluate({ name: PermissionName.IDENTITY_PROVIDER_READ });\n\n for (const datum of data) {\n try {\n await permissionEvaluator.evaluate({\n name: PermissionName.IDENTITY_PROVIDER_READ,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: datum, [BuiltInPolicyType.REALM_MATCH]: datum.realmId ?? null }),\n });\n } catch {\n // do nothing\n }\n }\n } catch {\n // do nothing\n }\n\n return {\n data,\n meta: {\n ...meta,\n schema: describeQuerySchema(identityProviderSchema),\n },\n };\n }\n\n @DGet('/:id', [])\n async getProvider(\n @DPath('id') id: string,\n @DContext() event: IAppEvent,\n ): Promise<EntityRecordResponse<IdentityProvider>> {\n const paramId = useRequestParamID(event, { isUUID: false });\n\n const entity = await this.repository.findOneByIdOrName(\n paramId,\n getRequestRealmID(event),\n );\n\n if (!entity) {\n throw new EntityNotFoundError();\n }\n\n try {\n const permissionEvaluator = useRequestPermissionEvaluator(event);\n await permissionEvaluator.evaluate({\n name: PermissionName.IDENTITY_PROVIDER_READ,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: entity, [BuiltInPolicyType.REALM_MATCH]: entity.realmId ?? null }),\n });\n } catch {\n // do nothing\n }\n\n return { data: entity, meta: { schema: describeQuerySchema(identityProviderSchema, RECORD_QUERY_PARAMETERS) } };\n }\n\n @DPost('/:id', [ForceLoggedInMiddleware])\n async editProvider(\n @DPath('id') id: string,\n @DBody() user: IdentityProviderUpdatePayload,\n @DContext() event: IAppEvent,\n ) : Promise<EntityRecordResponse<IdentityProvider>> {\n return this.write(event, { updateOnly: true });\n }\n\n @DPut('/:id', [ForceLoggedInMiddleware])\n async put(\n @DPath('id') id: string,\n @DBody() user: IdentityProviderSavePayload,\n @DContext() event: IAppEvent,\n ) : Promise<EntityRecordResponse<IdentityProvider>> {\n return this.write(event);\n }\n\n @DDelete('/:id', [ForceLoggedInMiddleware])\n async dropProvider(\n @DPath('id') id: string,\n @DContext() event: IAppEvent,\n ) : Promise<EntityRecordResponse<IdentityProvider>> {\n const paramId = useRequestParamID(event);\n\n const permissionEvaluator = useRequestPermissionEvaluator(event);\n await permissionEvaluator.preEvaluate({ name: PermissionName.IDENTITY_PROVIDER_DELETE });\n\n const entity = await this.repository.findOneBy({ id: paramId });\n\n if (!entity) {\n throw new EntityNotFoundError();\n }\n\n await permissionEvaluator.evaluate({\n name: PermissionName.IDENTITY_PROVIDER_DELETE,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: entity, [BuiltInPolicyType.REALM_MATCH]: entity.realmId ?? null }),\n });\n\n const { id: entityId } = entity;\n\n await this.repository.remove(entity);\n\n entity.id = entityId;\n\n event.response.status = 202;\n\n return { data: entity, meta: {} };\n }\n\n @DPost('', [ForceLoggedInMiddleware])\n async addProvider(\n @DBody() user: IdentityProviderCreatePayload,\n @DContext() event: IAppEvent,\n ) : Promise<EntityRecordResponse<IdentityProvider>> {\n return this.write(event);\n }\n\n // ---------------------------------------------------------\n\n @DGet('/:id/authorize-out', [])\n async authorizeOut(\n @DPath('id') _id: string,\n @DContext() event: IAppEvent,\n ) {\n const id = useRequestParamID(event);\n const entity = await this.resolve(id);\n\n if (!isOAuth2IdentityProvider(entity) && !isOpenIDIdentityProvider(entity)) {\n throw new BadRequestError('Only an identity-provider based on the oauth protocol supports authorize redirect.');\n }\n\n const authenticator = this.buildProviderAuthenticator(entity);\n\n let codeRequest: OAuth2AuthorizationCodeRequest | undefined;\n const query = useRequestQuery(event);\n if (typeof query.codeRequest === 'string') {\n let codeRequestDecoded: OAuth2AuthorizationCodeRequest;\n\n try {\n codeRequestDecoded = JSON.parse(base64URLDecode(query.codeRequest));\n } catch {\n throw OAuth2RequestError.malformed('The code request is malformed and can not be parsed.');\n }\n\n const codeRequestValidated = await this.codeRequestValidator.run(codeRequestDecoded);\n const data = await this.codeRequestVerifier.verify(codeRequestValidated);\n\n if (\n data.client.realmId &&\n entity.realmId &&\n entity.realmId !== data.client.realmId\n ) {\n throw OAuth2RequestError.malformed('The provider and client realm do not match.');\n }\n\n codeRequest = data.data;\n }\n\n const state = await this.saveAuthorizationState(event, { codeRequest });\n\n return sendRedirect(event, authenticator.buildRedirectURL({ state }));\n }\n\n @DPost('/:id/link-request', [ForceLoggedInMiddleware])\n async linkRequest(\n @DPath('id') _id: string,\n @DContext() event: IAppEvent,\n ) : Promise<IdentityProviderLinkRequestResponse> {\n const id = useRequestParamID(event);\n const entity = await this.resolve(id);\n\n if (!isOAuth2IdentityProvider(entity) && !isOpenIDIdentityProvider(entity)) {\n throw new BadRequestError('Only an identity-provider based on the oauth protocol supports account linking.');\n }\n\n if (!entity.enabled) {\n throw new BadRequestError('The identity provider is not enabled.');\n }\n\n const identity = useRequestIdentityOrFail(event);\n if (identity.type !== IdentityType.USER) {\n throw new BadRequestError('Only a user can link an identity provider account.');\n }\n\n if (entity.realmId && entity.realmId !== identity.realmId) {\n throw new BadRequestError('The identity provider does not belong to the user realm.');\n }\n\n const authenticator = this.buildProviderAuthenticator(entity);\n\n const state = await this.saveAuthorizationState(event, { link: { userId: identity.id, providerId: entity.id } });\n\n return { url: authenticator.buildRedirectURL({ state }) };\n }\n\n @DGet('/:id/authorize-in', [])\n async authorizeIn(\n @DPath('id') _id: string,\n @DContext() event: IAppEvent,\n ) {\n const id = useRequestParamID(event);\n\n const entity = await this.resolve(id);\n\n if (!isOAuth2IdentityProvider(entity) && !isOpenIDIdentityProvider(entity)) {\n throw new Error(`The provider protocol ${entity.protocol} is not valid.`);\n }\n\n const data = await this.verifyAuthorizationState(event);\n\n const { link } = data;\n if (link) {\n return this.completeLink(event, entity, data, link);\n }\n\n if (\n entity.realmId &&\n data.codeRequest &&\n data.codeRequest.realm_id &&\n data.codeRequest.realm_id !== entity.realmId\n ) {\n throw OAuth2RequestError.malformed('The provider and client realm do not match.');\n }\n\n const { code } = useRequestQuery(event);\n\n if (typeof code !== 'string' || code.length === 0) {\n throw new BadRequestError('The authorization code is missing.');\n }\n\n // Re-verify the code request that authorize-out stored on the state,\n // BEFORE the provider's single-use code is spent. It re-resolves the\n // client (active, grant allowlist, scopes) and re-matches the\n // redirect_uri against the client's registered patterns, so a client\n // deactivated (or a pattern removed) while the user was away at the\n // provider cannot still receive a code, and a refused completion\n // provisions no user. Same fail-closed-at-completion rule the link path\n // applies to the provider.\n //\n // The redirect below rests on the MATCH, which the verifier enforces by\n // throwing `redirectUriMismatch`; `redirectUriVerified` is the flag\n // that survives to report it. Nothing else on this path knows whether\n // the uri was ever matched.\n const verified = data.codeRequest ?\n await this.codeRequestVerifier.verify(data.codeRequest) :\n undefined;\n\n // The provider must still be enabled, the rule the link path already\n // applies. Disabling a provider has to stop logins in flight too,\n // otherwise it only stops new ones.\n if (!entity.enabled) {\n throw new BadRequestError('The identity provider is not enabled.');\n }\n\n const authenticator = this.buildProviderAuthenticator(entity, { clientId: verified?.data.client_id });\n\n const user = await authenticator.authenticate({ code });\n\n // The local login path refuses an inactive user (EntityInactiveError),\n // and a federated login must not be the way around that. Only reachable\n // for an already-linked user: a first login provisions an active one.\n if (!user.active) {\n throw new EntityInactiveError({ entity: 'user' });\n }\n\n const realm = await this.realmRepository.resolve(entity.realmId, true);\n\n // Application access policy (plan 052), federated leg. The denial\n // bounces back to the hosted authorize page, which renders it as a\n // denial card, rather than becoming an error redirect to the RP: the\n // person is standing in front of the browser and the card is the only\n // surface that can tell them why. A policy id with no wired evaluator\n // denies (fail closed).\n if (verified?.client.accessPolicyId) {\n let allowed = false;\n\n const subject = toIdentityPolicyData({\n type: IdentityType.USER,\n data: {\n ...user,\n realm,\n },\n });\n if (this.accessPolicyEvaluator && subject) {\n allowed = await this.accessPolicyEvaluator.evaluate(\n verified.client.accessPolicyId,\n subject,\n );\n }\n\n if (!allowed) {\n const url = this.buildHostedAuthorizeURL(verified.data);\n url.searchParams.set('error', OAuth2ErrorCode.ACCESS_DENIED);\n\n return sendRedirect(event, url.href);\n }\n }\n\n // The WHOLE verified request reaches the issuer, never a hand-picked\n // subset: it carries code_challenge / code_challenge_method and nonce\n // (plus acr_values, which no redemption path reads yet). A code that\n // lost its PKCE challenge cannot be redeemed by a public client at all\n // (`PKCE is required for public clients`), which is every console\n // client.\n const authorizationCode = await this.codeIssuer.issue(\n verified?.data ?? { response_type: 'code' },\n {\n type: IdentityType.USER,\n data: {\n ...user,\n realm,\n },\n },\n { authMethod: SessionAuthMethod.EXTERNAL },\n );\n\n // The interactive path records this in OAuth2Authorization.authorize();\n // this leg issues its code directly, so without an emit here a\n // federated authorization leaves no trace in auth_events while every\n // other one does. `reason: federated` is what tells the two apart:\n // there was no consent step to report. No session exists yet (the\n // /token exchange creates it), hence a null sessionId. Metrics stay\n // uninstrumented on this leg, as they already are.\n await this.eventService?.record({\n scope: EventScope.OAUTH2,\n name: EventName.AUTHORIZE,\n refType: EventRefType.CLIENT,\n refId: verified?.data.client_id ?? null,\n clientId: verified?.data.client_id ?? null,\n sessionId: null,\n actorType: IdentityType.USER,\n actorId: user.id,\n actorName: user.name,\n realmId: verified?.data.realm_id ?? realm.id,\n requestIpAddress: getRequestIP(event) ?? null,\n requestUserAgent: getRequestHeader(event, 'user-agent') ?? null,\n data: {\n reason: 'federated',\n providerId: entity.id,\n providerName: entity.name,\n ...(verified?.data.scope ? { scope: verified.data.scope } : {}),\n },\n });\n\n // RFC 6749 §4.1.2: the code goes back to the client that asked for it.\n // It is bound to that client_id and that redirect_uri, so the RP is the\n // only party able to redeem it. Handing it to any other page strands\n // the login there (issue #3446).\n if (verified?.redirectUriVerified && verified.data.redirect_uri) {\n const url = new URL(verified.data.redirect_uri);\n url.searchParams.set('code', authorizationCode.id);\n if (verified.data.state) {\n url.searchParams.set('state', verified.data.state);\n }\n\n return sendRedirect(event, url.href);\n }\n\n // Only reachable when the state carried no code request at all: a\n // stored one always has a redirect_uri, because that mount is required\n // in OAuth2AuthorizationCodeRequestValidator (unlike `state`), so a\n // verified request is always a verified redirect target.\n const url = new URL(this.options.baseURL);\n url.searchParams.set('code', authorizationCode.id);\n\n return sendRedirect(event, url.href);\n }\n\n // ---------------------------------------------------------\n\n private async write(event: IAppEvent, options: {\n updateOnly?: boolean\n } = {}): Promise<EntityRecordResponse<IdentityProvider>> {\n let group: string;\n const id = getRequestParamID(event, { isUUID: false });\n const body = await readRequestBody(event);\n applyRouteRealmIDToBody(event, body);\n const realmId = getRequestRealmID(event) ?? getBodyRealmID(body);\n\n let entity: IdentityProvider | null | undefined;\n if (id) {\n const where: Record<string, any> = {};\n if (isUUID(id)) {\n where.id = id;\n } else {\n where.name = id;\n }\n\n if (realmId) {\n where.realmId = realmId;\n }\n\n entity = await this.repository.findOneBy(where);\n // Only a NAME key may upsert-create. A UUID addresses one specific\n // row, so a miss is a 404 (creating would write a different id).\n if (!entity && (options.updateOnly || where.id)) {\n throw new EntityNotFoundError();\n }\n } else if (options.updateOnly) {\n throw new EntityNotFoundError();\n }\n\n const permissionEvaluator = useRequestPermissionEvaluator(event);\n if (entity) {\n await permissionEvaluator.preEvaluate({ name: PermissionName.IDENTITY_PROVIDER_UPDATE });\n\n group = ValidatorGroup.UPDATE;\n } else {\n await permissionEvaluator.preEvaluate({ name: PermissionName.IDENTITY_PROVIDER_CREATE });\n\n group = ValidatorGroup.CREATE;\n }\n\n const validator = new IdentityProviderValidator();\n const data = await validator.run(body, { group });\n\n const attributesValidator = new IdentityProviderAttributesValidator();\n const attributes = await attributesValidator.run(body);\n\n await this.repository.validateJoinColumns(data);\n\n if (entity) {\n await permissionEvaluator.evaluate({\n name: PermissionName.IDENTITY_PROVIDER_UPDATE,\n data: definePolicyData({\n [BuiltInPolicyType.ATTRIBUTES]: {\n ...entity,\n ...data,\n },\n [BuiltInPolicyType.REALM_MATCH]: data.realmId ?? entity.realmId ?? null,\n }),\n });\n } else {\n if (!data.realmId) {\n const identity = useRequestIdentityOrFail(event);\n data.realmId = identity.realmId;\n }\n\n await permissionEvaluator.evaluate({\n name: PermissionName.IDENTITY_PROVIDER_CREATE,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: data, [BuiltInPolicyType.REALM_MATCH]: data.realmId ?? null }),\n });\n }\n\n await this.repository.checkUniqueness(data, entity || undefined);\n\n if (entity) {\n entity = this.repository.merge(entity, data);\n await this.repository.saveWithEA(entity, attributes);\n\n event.response.status = 202;\n\n return { data: entity, meta: {} };\n }\n\n entity = this.repository.create(data);\n await this.repository.saveWithEA(entity, attributes);\n\n event.response.status = 201;\n\n return { data: entity, meta: {} };\n }\n\n // ---------------------------------------------------------\n\n /**\n * Callback half of the account-linking round-trip (plan 091): the\n * external identity is bound to the state-referenced user instead of\n * running the login path. No auth code and no session is minted; the\n * browser lands back on the account console with a marker param.\n */\n private async completeLink(\n event: IAppEvent,\n provider: OAuth2IdentityProvider | OpenIDIdentityProvider,\n state: OAuth2AuthorizationState,\n link: OAuth2AuthorizationStateLink,\n ) {\n // Fixed, server-derived return target (the account console page).\n // No client-supplied redirect exists on this path.\n const url = new URL(resolveURL(this.options.baseURL, 'account/connected-accounts'));\n\n const { code } = useRequestQuery(event);\n\n try {\n // The state is bound to the provider it was minted for, and the\n // provider must still be enabled at completion — a state cannot\n // be replayed against a different provider's callback, nor\n // complete a link after its provider was disabled.\n if (link.providerId !== provider.id || !provider.enabled) {\n throw new BadRequestError('The identity provider is not available for account linking.');\n }\n\n if (typeof code !== 'string' || code.length === 0) {\n throw new BadRequestError('The authorization code is missing.');\n }\n\n const authenticator = this.buildProviderAuthenticator(provider);\n\n const identity = await authenticator.resolveIdentity({ code });\n\n // A provider answering without a subject has nothing to bind. The\n // callback used to reach `link()`, which rejected it; the stash\n // would otherwise carry an empty providerUserId to the confirm.\n if (!identity.id) {\n throw new BadRequestError('The identity provider returned no subject.');\n }\n\n // Nothing is written here. This request is unauthenticated — the\n // browser round-trip cannot carry a bearer — so the only thing\n // distinguishing the browser that started the link from any other\n // would be the state's ip / user-agent binding, and both values\n // are chosen by whoever minted the state. An attacker minting a\n // state carrying their OWN userId and getting a victim to follow\n // it would otherwise bind the VICTIM's external identity to the\n // attacker's account (issue #3439).\n //\n // The resolved identity is stashed under a one-time handle\n // instead, and the account console confirms it with its bearer.\n // The stash keeps the requesting userId so the confirm can\n // require it to equal the AUTHENTICATED user: the handle is a\n // URL parameter, so it reaches browser history and proxy logs,\n // and on its own it must authorize nothing.\n const handle = await this.linkStore.save({\n providerId: provider.id,\n userId: link.userId,\n providerUserId: identity.id,\n providerUserName: this.pickIdentityCandidate(identity, 'name'),\n providerUserEmail: this.pickIdentityCandidate(identity, 'email'),\n });\n\n url.searchParams.set('linkHandle', handle);\n url.searchParams.set('provider', provider.id);\n } catch (e) {\n // The failure is swallowed into a redirect marker, so the log is\n // the only surface it can reach. Without this an unreachable or\n // rejecting provider is indistinguishable from a stale state.\n this.logger?.error(describeError(e, 'The identity-provider account link failed.'));\n\n url.searchParams.set('linkError', 'link_failed');\n }\n\n return sendRedirect(event, url.href);\n }\n\n /**\n * The bearer-authenticated half of the link (issue #3439): the account\n * row is written here, for the AUTHENTICATED user, never in the callback.\n */\n @DPost('/:id/link-confirm', [ForceLoggedInMiddleware])\n async confirmLink(\n @DPath('id') _id: string,\n @DBody() _data: IdentityProviderLinkConfirmPayload,\n @DContext() event: IAppEvent,\n ) : Promise<EntityRecordResponse<IdentityProviderAccount>> {\n const id = useRequestParamID(event);\n const provider = await this.resolve(id);\n\n if (!isOAuth2IdentityProvider(provider) && !isOpenIDIdentityProvider(provider)) {\n throw new BadRequestError('Only an identity-provider based on the oauth protocol supports account linking.');\n }\n\n if (!provider.enabled) {\n throw new BadRequestError('The identity provider is not enabled.');\n }\n\n const identity = useRequestIdentityOrFail(event);\n if (identity.type !== IdentityType.USER) {\n throw new BadRequestError('Only a user can link an identity provider account.');\n }\n\n const body = await readRequestBody(event);\n const handle = isObject(body) ? body.handle : undefined;\n if (typeof handle !== 'string' || handle.length === 0) {\n throw new BadRequestError('The account link handle is missing.');\n }\n\n const link = await this.linkStore.consume(handle);\n if (!link) {\n throw new BadRequestError('The account link request is unknown or expired.');\n }\n\n // The two checks that make the handle inert on its own: it may only\n // be redeemed by the user it was minted for, and only against the\n // provider it was minted for.\n if (link.userId !== identity.id || link.providerId !== provider.id) {\n throw new BadRequestError('The account link request does not belong to the authenticated user.');\n }\n\n const account = await this.accountManager.link(\n {\n id: link.providerUserId,\n // deliberately rebuilt from the stashed scalars: the resolved\n // identity carries the provider secret and the raw external\n // token payload, neither of which belongs in a cache\n attributeCandidates: {\n name: [link.providerUserName],\n email: [link.providerUserEmail],\n },\n data: {},\n provider,\n },\n identity.id,\n );\n\n // the confirm runs under a bearer, so unlike the callback it CAN be\n // attributed to a session and a route (the unlink emit's shape)\n const requestContext = useRequestEventContext();\n\n await this.eventService?.record({\n scope: EventScope.IDENTITY,\n name: EventName.IDENTITY_PROVIDER_LINKED,\n refType: EventRefType.IDENTITY_PROVIDER_ACCOUNT,\n refId: account.id,\n realmId: account.userRealmId ?? provider.realmId ?? null,\n actorType: IdentityType.USER,\n actorId: account.userId,\n actorName: identity.data.name ?? null,\n sessionId: requestContext?.sessionId ?? null,\n requestPath: requestContext?.requestPath ?? null,\n requestMethod: requestContext?.requestMethod ?? null,\n requestIpAddress: requestContext?.requestIpAddress ?? getRequestIP(event) ?? null,\n requestUserAgent: requestContext?.requestUserAgent ?? getRequestHeader(event, 'user-agent') ?? null,\n data: { providerId: provider.id, providerName: provider.name },\n });\n\n return { data: account, meta: {} };\n }\n\n private pickIdentityCandidate(\n identity: IdentityProviderIdentity,\n key: 'name' | 'email',\n ) : string | null {\n const candidates = identity.attributeCandidates?.[key] || [];\n for (const candidate of candidates) {\n if (typeof candidate === 'string' && candidate.length > 0) {\n return candidate;\n }\n }\n\n return null;\n }\n\n // ---------------------------------------------------------\n\n private buildHostedAuthorizeURL(codeRequest: OAuth2AuthorizationCodeRequest): URL {\n const url = new URL(resolveURL(this.options.baseURL, 'authorize'));\n\n const codeRequestKeys = Object.keys(codeRequest);\n for (const codeRequestKey_ of codeRequestKeys) {\n const codeRequestKey = codeRequestKey_ as keyof OAuth2AuthorizationCodeRequest;\n const codeRequestValue = codeRequest[codeRequestKey];\n // Preserve meaningful falsy values (e.g. max_age=0, which OIDC\n // treats as prompt=login) — only skip absent ones.\n if (typeof codeRequestValue !== 'undefined' && codeRequestValue !== null) {\n url.searchParams.set(codeRequestKey, String(codeRequestValue));\n }\n }\n\n return url;\n }\n\n // ---------------------------------------------------------\n\n private async resolve(id: string) {\n const entity = await this.repository.findOneById(id);\n\n if (!entity) {\n throw new EntityNotFoundError();\n }\n\n return entity;\n }\n\n // ---------------------------------------------------------\n\n private buildProviderAuthenticator(\n provider: OAuth2IdentityProvider | OpenIDIdentityProvider,\n options: { clientId?: string } = {},\n ) {\n return createIdentityProviderOAuth2Authenticator({\n accountManager: this.accountManager,\n provider,\n logger: this.logger,\n options: {\n baseURL: this.options.baseURL,\n clientId: options.clientId,\n },\n });\n }\n\n private async saveAuthorizationState(\n event: IAppEvent,\n data: Pick<OAuth2AuthorizationState, 'codeRequest' | 'link'> = {},\n ) : Promise<string> {\n return this.stateManager.save({\n ...data,\n ip: getRequestIP(event) ?? '',\n userAgent: getRequestHeader(event, 'user-agent') ?? undefined,\n });\n }\n\n private async verifyAuthorizationState(event: IAppEvent): Promise<OAuth2AuthorizationState> {\n const query = useRequestQuery(event);\n if (typeof query.state !== 'string') {\n throw OAuth2RequestError.stateInvalid();\n }\n\n return this.stateManager.verify(query.state, {\n ip: getRequestIP(event) ?? '',\n userAgent: getRequestHeader(event, 'user-agent') ?? undefined,\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"module.mjs","names":["BuiltInPolicyType","definePolicyData","ValidatorGroup","base64URLDecode","isObject","isUUID","DBody","DContext","DController","DDelete","DGet","DPath","DPost","DPut","DTags","getRequestHeader","getRequestIP","sendRedirect","EventName","EventRefType","EventScope","IdentityProviderAttributesValidator","IdentityProviderValidator","IdentityType","PermissionName","buildIdentityProviderAuthorizeCallbackPath","isOAuth2IdentityProvider","isOpenIDIdentityProvider","BadRequestError","EntityNotFoundError","describeError","resolveURL","useRequestQuery","readRequestBody","OAuth2RequestError","URL","OAuth2AuthorizationCodeRequestValidator","RECORD_QUERY_PARAMETERS","createIdentityProviderOAuth2Authenticator","decodeQuery","describeQuerySchema","identityProviderSchema","applyRouteRealmIDToBody","buildActorContext","getBodyRealmID","getRequestParamID","getRequestRealmID","useRequestEventContext","useRequestIdentityOrFail","useRequestParamID","useRequestPermissionEvaluator","ForceLoggedInMiddleware","renderAuthConsolePage","IdentityProviderController","options","repository","accountManager","linkStore","codeRequestVerifier","codeRequestValidator","stateManager","loginService","eventService","logger","ctx","getProviders","event","data","meta","findMany","schema","actor","permissionEvaluator","preEvaluate","name","IDENTITY_PROVIDER_READ","datum","evaluate","ATTRIBUTES","REALM_MATCH","realmId","getProvider","id","paramId","entity","findOneByIdOrName","editProvider","user","write","updateOnly","put","dropProvider","IDENTITY_PROVIDER_DELETE","findOneBy","entityId","remove","response","status","addProvider","authorizeOut","_id","resolve","enabled","query","codeRequest","malformed","codeRequestDecoded","JSON","parse","codeRequestValidated","run","verify","client","authenticator","buildProviderAuthenticator","state","saveAuthorizationState","buildRedirectURL","linkRequest","identity","type","USER","link","userId","providerId","url","authorizeIn","Error","protocol","verifyAuthorizationState","completeLink","code","error","length","info","stringify","slice","buildHostedAuthorizeURL","href","result","complete","provider","request","ipAddress","userAgent","kind","searchParams","set","redirectUri","payload","redirect","authorizeUrl","config","baseURL","group","body","where","IDENTITY_PROVIDER_UPDATE","UPDATE","IDENTITY_PROVIDER_CREATE","CREATE","validator","attributesValidator","attributes","validateJoinColumns","checkUniqueness","undefined","merge","saveWithEA","create","resolveIdentity","handle","save","providerUserId","providerUserName","pickIdentityCandidate","providerUserEmail","e","confirmLink","_data","consume","account","attributeCandidates","email","requestContext","record","scope","IDENTITY","IDENTITY_PROVIDER_LINKED","refType","IDENTITY_PROVIDER_ACCOUNT","refId","userRealmId","actorType","actorId","actorName","sessionId","requestPath","requestMethod","requestIpAddress","requestUserAgent","providerName","key","candidates","candidate","codeRequestKeys","Object","keys","codeRequestKey_","codeRequestKey","codeRequestValue","String","findOneById","clientId","ip","stateInvalid"],"sources":["../../../../../../src/adapters/http/controllers/entities/identity-provider/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2022.\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 { BuiltInPolicyType, definePolicyData } from '@authup/access';\nimport {\n ValidatorGroup,\n base64URLDecode,\n isObject,\n isUUID,\n} from '@authup/kit';\nimport {\n DBody,\n DContext,\n DController,\n DDelete,\n DGet,\n DPath,\n DPost,\n DPut,\n DTags,\n} from '@routup/decorators';\nimport type { IAppEvent } from 'routup';\nimport {\n getRequestHeader,\n getRequestIP,\n sendRedirect,\n} from 'routup';\nimport type {\n IdentityProvider,\n IdentityProviderAccount,\n OAuth2AuthorizationCodeRequest,\n OAuth2IdentityProvider,\n OpenIDIdentityProvider,\n} from '@authup/core-kit';\nimport {\n EventName,\n EventRefType,\n EventScope,\n IdentityProviderAttributesValidator,\n IdentityProviderValidator,\n IdentityType,\n PermissionName,\n buildIdentityProviderAuthorizeCallbackPath,\n isOAuth2IdentityProvider,\n isOpenIDIdentityProvider,\n} from '@authup/core-kit';\nimport { BadRequestError, EntityNotFoundError } from '@authup/errors';\nimport type { Logger } from '@authup/server-kit';\nimport { describeError, resolveURL } from '../../../../../utils/index.ts';\nimport { useRequestQuery } from '@routup/basic/query';\nimport { readRequestBody } from '@routup/basic/body';\nimport { OAuth2RequestError } from '@authup/specs';\nimport type {\n EntityCollectionResponse,\n EntityRecordResponse,\n IdentityProviderCreatePayload,\n IdentityProviderLinkConfirmPayload,\n IdentityProviderLinkRequestResponse,\n IdentityProviderSavePayload,\n IdentityProviderUpdatePayload,\n} from '@authup/core-http-kit';\nimport { URL } from 'node:url';\nimport type {\n IEventService,\n IIdentityProviderAccountLinkStore,\n IIdentityProviderAccountManager,\n IIdentityProviderRepository,\n IOAuth2AuthorizationCodeRequestVerifier,\n IOAuth2AuthorizationStateManager,\n IOAuth2FederatedLoginService,\n IdentityProviderIdentity,\n OAuth2AuthorizationState,\n OAuth2AuthorizationStateLink,\n} from '../../../../../core/index.ts';\nimport {\n OAuth2AuthorizationCodeRequestValidator,\n RECORD_QUERY_PARAMETERS,\n createIdentityProviderOAuth2Authenticator,\n decodeQuery,\n describeQuerySchema,\n identityProviderSchema,\n} from '../../../../../core/index.ts';\nimport {\n applyRouteRealmIDToBody,\n buildActorContext,\n getBodyRealmID,\n getRequestParamID,\n getRequestRealmID,\n useRequestEventContext,\n useRequestIdentityOrFail,\n useRequestParamID,\n useRequestPermissionEvaluator,\n} from '../../../request/index.ts';\nimport { ForceLoggedInMiddleware } from '../../../middleware/index.ts';\nimport type { IdentityProviderCallbackPayload } from '@authup/client-auth-console';\nimport { renderAuthConsolePage } from '../../../ui/index.ts';\nimport type { IdentityProviderControllerContext, IdentityProviderControllerOptions } from './types.ts';\n\n@DTags('identity')\n@DController(['/identity-providers', '/realms/:realmId/identity-providers'])\nexport class IdentityProviderController {\n protected options: IdentityProviderControllerOptions;\n\n protected repository: IIdentityProviderRepository;\n\n protected accountManager: IIdentityProviderAccountManager;\n\n protected linkStore: IIdentityProviderAccountLinkStore;\n\n protected codeRequestVerifier : IOAuth2AuthorizationCodeRequestVerifier;\n\n protected codeRequestValidator : OAuth2AuthorizationCodeRequestValidator;\n\n protected stateManager : IOAuth2AuthorizationStateManager;\n\n protected loginService : IOAuth2FederatedLoginService;\n\n protected eventService? : IEventService;\n\n protected logger? : Logger;\n\n // ---------------------------------------------------------\n\n constructor(ctx: IdentityProviderControllerContext) {\n this.options = ctx.options;\n this.repository = ctx.repository;\n this.accountManager = ctx.accountManager;\n this.linkStore = ctx.linkStore;\n this.codeRequestVerifier = ctx.codeRequestVerifier;\n this.codeRequestValidator = new OAuth2AuthorizationCodeRequestValidator();\n this.stateManager = ctx.stateManager;\n this.loginService = ctx.loginService;\n this.eventService = ctx.eventService;\n this.logger = ctx.logger;\n }\n\n // ---------------------------------------------------------\n\n @DGet('', [])\n async getProviders(\n @DContext() event: IAppEvent,\n ): Promise<EntityCollectionResponse<IdentityProvider>> {\n const {\n data,\n meta,\n } = await this.repository.findMany(\n await decodeQuery(useRequestQuery(event), {\n schema: identityProviderSchema,\n actor: buildActorContext(event),\n }),\n );\n\n try {\n const permissionEvaluator = useRequestPermissionEvaluator(event);\n await permissionEvaluator.preEvaluate({ name: PermissionName.IDENTITY_PROVIDER_READ });\n\n for (const datum of data) {\n try {\n await permissionEvaluator.evaluate({\n name: PermissionName.IDENTITY_PROVIDER_READ,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: datum, [BuiltInPolicyType.REALM_MATCH]: datum.realmId ?? null }),\n });\n } catch {\n // do nothing\n }\n }\n } catch {\n // do nothing\n }\n\n return {\n data,\n meta: {\n ...meta,\n schema: describeQuerySchema(identityProviderSchema),\n },\n };\n }\n\n @DGet('/:id', [])\n async getProvider(\n @DPath('id') id: string,\n @DContext() event: IAppEvent,\n ): Promise<EntityRecordResponse<IdentityProvider>> {\n const paramId = useRequestParamID(event, { isUUID: false });\n\n const entity = await this.repository.findOneByIdOrName(\n paramId,\n getRequestRealmID(event),\n );\n\n if (!entity) {\n throw new EntityNotFoundError();\n }\n\n try {\n const permissionEvaluator = useRequestPermissionEvaluator(event);\n await permissionEvaluator.evaluate({\n name: PermissionName.IDENTITY_PROVIDER_READ,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: entity, [BuiltInPolicyType.REALM_MATCH]: entity.realmId ?? null }),\n });\n } catch {\n // do nothing\n }\n\n return { data: entity, meta: { schema: describeQuerySchema(identityProviderSchema, RECORD_QUERY_PARAMETERS) } };\n }\n\n @DPost('/:id', [ForceLoggedInMiddleware])\n async editProvider(\n @DPath('id') id: string,\n @DBody() user: IdentityProviderUpdatePayload,\n @DContext() event: IAppEvent,\n ) : Promise<EntityRecordResponse<IdentityProvider>> {\n return this.write(event, { updateOnly: true });\n }\n\n @DPut('/:id', [ForceLoggedInMiddleware])\n async put(\n @DPath('id') id: string,\n @DBody() user: IdentityProviderSavePayload,\n @DContext() event: IAppEvent,\n ) : Promise<EntityRecordResponse<IdentityProvider>> {\n return this.write(event);\n }\n\n @DDelete('/:id', [ForceLoggedInMiddleware])\n async dropProvider(\n @DPath('id') id: string,\n @DContext() event: IAppEvent,\n ) : Promise<EntityRecordResponse<IdentityProvider>> {\n const paramId = useRequestParamID(event);\n\n const permissionEvaluator = useRequestPermissionEvaluator(event);\n await permissionEvaluator.preEvaluate({ name: PermissionName.IDENTITY_PROVIDER_DELETE });\n\n const entity = await this.repository.findOneBy({ id: paramId });\n\n if (!entity) {\n throw new EntityNotFoundError();\n }\n\n await permissionEvaluator.evaluate({\n name: PermissionName.IDENTITY_PROVIDER_DELETE,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: entity, [BuiltInPolicyType.REALM_MATCH]: entity.realmId ?? null }),\n });\n\n const { id: entityId } = entity;\n\n await this.repository.remove(entity);\n\n entity.id = entityId;\n\n event.response.status = 202;\n\n return { data: entity, meta: {} };\n }\n\n @DPost('', [ForceLoggedInMiddleware])\n async addProvider(\n @DBody() user: IdentityProviderCreatePayload,\n @DContext() event: IAppEvent,\n ) : Promise<EntityRecordResponse<IdentityProvider>> {\n return this.write(event);\n }\n\n // ---------------------------------------------------------\n\n @DGet('/:id/authorize-out', [])\n async authorizeOut(\n @DPath('id') _id: string,\n @DContext() event: IAppEvent,\n ) {\n const id = useRequestParamID(event);\n const entity = await this.resolve(id);\n\n if (!isOAuth2IdentityProvider(entity) && !isOpenIDIdentityProvider(entity)) {\n throw new BadRequestError('Only an identity-provider based on the oauth protocol supports authorize redirect.');\n }\n\n // Refused here as well as at the callback, so a disabled provider\n // costs no provider round trip.\n if (!entity.enabled) {\n throw new BadRequestError('The identity provider is not enabled.');\n }\n\n // A federated login completes an RP's authorization request: the\n // callback mints a code bound to that request and delivers it to the\n // request's redirect_uri. Without one there is nowhere to deliver a\n // code (the callback used to mint an unbound one and hand it to the\n // server root, issue #3457), so the login must not start.\n const query = useRequestQuery(event);\n if (typeof query.codeRequest !== 'string') {\n throw OAuth2RequestError.malformed('A federated login requires an authorization code request.');\n }\n\n let codeRequestDecoded: OAuth2AuthorizationCodeRequest;\n\n try {\n codeRequestDecoded = JSON.parse(base64URLDecode(query.codeRequest));\n } catch {\n throw OAuth2RequestError.malformed('The code request is malformed and can not be parsed.');\n }\n\n const codeRequestValidated = await this.codeRequestValidator.run(codeRequestDecoded);\n const data = await this.codeRequestVerifier.verify(codeRequestValidated);\n\n if (\n data.client.realmId &&\n entity.realmId &&\n entity.realmId !== data.client.realmId\n ) {\n throw OAuth2RequestError.malformed('The provider and client realm do not match.');\n }\n\n const authenticator = this.buildProviderAuthenticator(entity);\n\n const state = await this.saveAuthorizationState(event, { codeRequest: data.data });\n\n return sendRedirect(event, authenticator.buildRedirectURL({ state }));\n }\n\n @DPost('/:id/link-request', [ForceLoggedInMiddleware])\n async linkRequest(\n @DPath('id') _id: string,\n @DContext() event: IAppEvent,\n ) : Promise<IdentityProviderLinkRequestResponse> {\n const id = useRequestParamID(event);\n const entity = await this.resolve(id);\n\n if (!isOAuth2IdentityProvider(entity) && !isOpenIDIdentityProvider(entity)) {\n throw new BadRequestError('Only an identity-provider based on the oauth protocol supports account linking.');\n }\n\n if (!entity.enabled) {\n throw new BadRequestError('The identity provider is not enabled.');\n }\n\n const identity = useRequestIdentityOrFail(event);\n if (identity.type !== IdentityType.USER) {\n throw new BadRequestError('Only a user can link an identity provider account.');\n }\n\n if (entity.realmId && entity.realmId !== identity.realmId) {\n throw new BadRequestError('The identity provider does not belong to the user realm.');\n }\n\n const authenticator = this.buildProviderAuthenticator(entity);\n\n const state = await this.saveAuthorizationState(event, { link: { userId: identity.id, providerId: entity.id } });\n\n return { url: authenticator.buildRedirectURL({ state }) };\n }\n\n @DGet('/:id/authorize-in', [])\n async authorizeIn(\n @DPath('id') _id: string,\n @DContext() event: IAppEvent,\n ) {\n const id = useRequestParamID(event);\n\n const entity = await this.resolve(id);\n\n if (!isOAuth2IdentityProvider(entity) && !isOpenIDIdentityProvider(entity)) {\n throw new Error(`The provider protocol ${entity.protocol} is not valid.`);\n }\n\n const data = await this.verifyAuthorizationState(event);\n\n const { link } = data;\n if (link) {\n return this.completeLink(event, entity, data, link);\n }\n\n // authorize-out no longer mints a login state without a code request;\n // this refuses the ones minted before that (issue #3457).\n if (!data.codeRequest) {\n throw OAuth2RequestError.malformed('The state carries no authorization code request.');\n }\n\n const { code, error } = useRequestQuery(event);\n\n // RFC 6749 section 4.1.2.1: a provider answers a refused or failed\n // authorization (the person cancelled at the provider, the provider\n // is down) with `error` and no code. A top-level browser navigation\n // like every other refusal here, so it lands on the hosted login\n // again. Nothing of the provider's answer is echoed: whoever controls\n // the provider's redirect shapes those values.\n if (typeof error === 'string' && error.length > 0) {\n // JSON-quoted: a raw query value must not be able to forge a log line\n this.logger?.info(`The identity provider ${entity.id} answered the authorization with ${JSON.stringify(error.slice(0, 64))}.`);\n\n return sendRedirect(event, this.buildHostedAuthorizeURL(data.codeRequest).href);\n }\n\n if (typeof code !== 'string' || code.length === 0) {\n throw new BadRequestError('The authorization code is missing.');\n }\n\n const result = await this.loginService.complete({\n provider: entity,\n codeRequest: data.codeRequest,\n code,\n request: {\n ipAddress: getRequestIP(event),\n userAgent: getRequestHeader(event, 'user-agent'),\n },\n });\n\n // A refusal is a decision the person at the browser has to be told\n // about, and this is a top-level navigation, so it lands on the\n // hosted login rather than in a JSON body (issue #3458). A marker is\n // only attached when the refusal carries one; without it the page\n // re-runs the same verifier over the same request and states the\n // reason itself, so nothing is echoed.\n if (result.kind === 'refused') {\n const url = this.buildHostedAuthorizeURL(result.codeRequest);\n if (result.error) {\n url.searchParams.set('error', result.error);\n }\n\n return sendRedirect(event, url.href);\n }\n\n // RFC 6749 §4.1.2: the code goes back to the client that asked for it.\n // It is bound to that client_id and that redirect_uri, so the RP is the\n // only party able to redeem it. Handing it to any other page strands\n // the login there (issue #3446).\n const url = new URL(result.redirectUri);\n url.searchParams.set('code', result.code);\n if (result.state) {\n url.searchParams.set('state', result.state);\n }\n\n if (url.protocol === 'http:' || url.protocol === 'https:') {\n return sendRedirect(event, url.href);\n }\n\n // routup's sendRedirect allows http(s) only. A native app's\n // custom-scheme redirect_uri (RFC 8252, matched verbatim by\n // isSimpleURLMatch) is navigated client-side from an interstitial\n // page instead; the target is the verified redirect_uri and nothing\n // else (the service refuses a script-capable scheme), so the page\n // never reaches an unverified one (issue #3459).\n const payload : IdentityProviderCallbackPayload = {\n redirect: url.href,\n // The browser stays on the consumed callback URL, so a reload\n // would re-run the callback against a popped state. The page\n // swaps the history entry for this first, so a reload lands on\n // the hosted login instead.\n authorizeUrl: this.buildHostedAuthorizeURL(result.codeRequest).href,\n client: result.client,\n };\n\n return renderAuthConsolePage(event, {\n url: buildIdentityProviderAuthorizeCallbackPath(entity.id),\n payload: {\n config: { baseURL: this.options.baseURL },\n data: payload,\n },\n });\n }\n\n // ---------------------------------------------------------\n\n private async write(event: IAppEvent, options: {\n updateOnly?: boolean\n } = {}): Promise<EntityRecordResponse<IdentityProvider>> {\n let group: string;\n const id = getRequestParamID(event, { isUUID: false });\n const body = await readRequestBody(event);\n applyRouteRealmIDToBody(event, body);\n const realmId = getRequestRealmID(event) ?? getBodyRealmID(body);\n\n let entity: IdentityProvider | null | undefined;\n if (id) {\n const where: Record<string, any> = {};\n if (isUUID(id)) {\n where.id = id;\n } else {\n where.name = id;\n }\n\n if (realmId) {\n where.realmId = realmId;\n }\n\n entity = await this.repository.findOneBy(where);\n // Only a NAME key may upsert-create. A UUID addresses one specific\n // row, so a miss is a 404 (creating would write a different id).\n if (!entity && (options.updateOnly || where.id)) {\n throw new EntityNotFoundError();\n }\n } else if (options.updateOnly) {\n throw new EntityNotFoundError();\n }\n\n const permissionEvaluator = useRequestPermissionEvaluator(event);\n if (entity) {\n await permissionEvaluator.preEvaluate({ name: PermissionName.IDENTITY_PROVIDER_UPDATE });\n\n group = ValidatorGroup.UPDATE;\n } else {\n await permissionEvaluator.preEvaluate({ name: PermissionName.IDENTITY_PROVIDER_CREATE });\n\n group = ValidatorGroup.CREATE;\n }\n\n const validator = new IdentityProviderValidator();\n const data = await validator.run(body, { group });\n\n const attributesValidator = new IdentityProviderAttributesValidator();\n const attributes = await attributesValidator.run(body);\n\n await this.repository.validateJoinColumns(data);\n\n if (entity) {\n await permissionEvaluator.evaluate({\n name: PermissionName.IDENTITY_PROVIDER_UPDATE,\n data: definePolicyData({\n [BuiltInPolicyType.ATTRIBUTES]: {\n ...entity,\n ...data,\n },\n [BuiltInPolicyType.REALM_MATCH]: data.realmId ?? entity.realmId ?? null,\n }),\n });\n } else {\n if (!data.realmId) {\n const identity = useRequestIdentityOrFail(event);\n data.realmId = identity.realmId;\n }\n\n await permissionEvaluator.evaluate({\n name: PermissionName.IDENTITY_PROVIDER_CREATE,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: data, [BuiltInPolicyType.REALM_MATCH]: data.realmId ?? null }),\n });\n }\n\n await this.repository.checkUniqueness(data, entity || undefined);\n\n if (entity) {\n entity = this.repository.merge(entity, data);\n await this.repository.saveWithEA(entity, attributes);\n\n event.response.status = 202;\n\n return { data: entity, meta: {} };\n }\n\n entity = this.repository.create(data);\n await this.repository.saveWithEA(entity, attributes);\n\n event.response.status = 201;\n\n return { data: entity, meta: {} };\n }\n\n // ---------------------------------------------------------\n\n /**\n * Callback half of the account-linking round-trip (plan 091): the\n * external identity is bound to the state-referenced user instead of\n * running the login path. No auth code and no session is minted; the\n * browser lands back on the account console with a marker param.\n */\n private async completeLink(\n event: IAppEvent,\n provider: OAuth2IdentityProvider | OpenIDIdentityProvider,\n state: OAuth2AuthorizationState,\n link: OAuth2AuthorizationStateLink,\n ) {\n // Fixed, server-derived return target (the account console page).\n // No client-supplied redirect exists on this path.\n const url = new URL(resolveURL(this.options.baseURL, 'account/connected-accounts'));\n\n const { code } = useRequestQuery(event);\n\n try {\n // The state is bound to the provider it was minted for, and the\n // provider must still be enabled at completion — a state cannot\n // be replayed against a different provider's callback, nor\n // complete a link after its provider was disabled.\n if (link.providerId !== provider.id || !provider.enabled) {\n throw new BadRequestError('The identity provider is not available for account linking.');\n }\n\n if (typeof code !== 'string' || code.length === 0) {\n throw new BadRequestError('The authorization code is missing.');\n }\n\n const authenticator = this.buildProviderAuthenticator(provider);\n\n const identity = await authenticator.resolveIdentity({ code });\n\n // A provider answering without a subject has nothing to bind. The\n // callback used to reach `link()`, which rejected it; the stash\n // would otherwise carry an empty providerUserId to the confirm.\n if (!identity.id) {\n throw new BadRequestError('The identity provider returned no subject.');\n }\n\n // Nothing is written here. This request is unauthenticated — the\n // browser round-trip cannot carry a bearer — so the only thing\n // distinguishing the browser that started the link from any other\n // would be the state's ip / user-agent binding, and both values\n // are chosen by whoever minted the state. An attacker minting a\n // state carrying their OWN userId and getting a victim to follow\n // it would otherwise bind the VICTIM's external identity to the\n // attacker's account (issue #3439).\n //\n // The resolved identity is stashed under a one-time handle\n // instead, and the account console confirms it with its bearer.\n // The stash keeps the requesting userId so the confirm can\n // require it to equal the AUTHENTICATED user: the handle is a\n // URL parameter, so it reaches browser history and proxy logs,\n // and on its own it must authorize nothing.\n const handle = await this.linkStore.save({\n providerId: provider.id,\n userId: link.userId,\n providerUserId: identity.id,\n providerUserName: this.pickIdentityCandidate(identity, 'name'),\n providerUserEmail: this.pickIdentityCandidate(identity, 'email'),\n });\n\n url.searchParams.set('linkHandle', handle);\n url.searchParams.set('provider', provider.id);\n } catch (e) {\n // The failure is swallowed into a redirect marker, so the log is\n // the only surface it can reach. Without this an unreachable or\n // rejecting provider is indistinguishable from a stale state.\n this.logger?.error(describeError(e, 'The identity-provider account link failed.'));\n\n url.searchParams.set('linkError', 'link_failed');\n }\n\n return sendRedirect(event, url.href);\n }\n\n /**\n * The bearer-authenticated half of the link (issue #3439): the account\n * row is written here, for the AUTHENTICATED user, never in the callback.\n */\n @DPost('/:id/link-confirm', [ForceLoggedInMiddleware])\n async confirmLink(\n @DPath('id') _id: string,\n @DBody() _data: IdentityProviderLinkConfirmPayload,\n @DContext() event: IAppEvent,\n ) : Promise<EntityRecordResponse<IdentityProviderAccount>> {\n const id = useRequestParamID(event);\n const provider = await this.resolve(id);\n\n if (!isOAuth2IdentityProvider(provider) && !isOpenIDIdentityProvider(provider)) {\n throw new BadRequestError('Only an identity-provider based on the oauth protocol supports account linking.');\n }\n\n if (!provider.enabled) {\n throw new BadRequestError('The identity provider is not enabled.');\n }\n\n const identity = useRequestIdentityOrFail(event);\n if (identity.type !== IdentityType.USER) {\n throw new BadRequestError('Only a user can link an identity provider account.');\n }\n\n const body = await readRequestBody(event);\n const handle = isObject(body) ? body.handle : undefined;\n if (typeof handle !== 'string' || handle.length === 0) {\n throw new BadRequestError('The account link handle is missing.');\n }\n\n const link = await this.linkStore.consume(handle);\n if (!link) {\n throw new BadRequestError('The account link request is unknown or expired.');\n }\n\n // The two checks that make the handle inert on its own: it may only\n // be redeemed by the user it was minted for, and only against the\n // provider it was minted for.\n if (link.userId !== identity.id || link.providerId !== provider.id) {\n throw new BadRequestError('The account link request does not belong to the authenticated user.');\n }\n\n const account = await this.accountManager.link(\n {\n id: link.providerUserId,\n // deliberately rebuilt from the stashed scalars: the resolved\n // identity carries the provider secret and the raw external\n // token payload, neither of which belongs in a cache\n attributeCandidates: {\n name: [link.providerUserName],\n email: [link.providerUserEmail],\n },\n data: {},\n provider,\n },\n identity.id,\n );\n\n // the confirm runs under a bearer, so unlike the callback it CAN be\n // attributed to a session and a route (the unlink emit's shape)\n const requestContext = useRequestEventContext();\n\n await this.eventService?.record({\n scope: EventScope.IDENTITY,\n name: EventName.IDENTITY_PROVIDER_LINKED,\n refType: EventRefType.IDENTITY_PROVIDER_ACCOUNT,\n refId: account.id,\n realmId: account.userRealmId ?? provider.realmId ?? null,\n actorType: IdentityType.USER,\n actorId: account.userId,\n actorName: identity.data.name ?? null,\n sessionId: requestContext?.sessionId ?? null,\n requestPath: requestContext?.requestPath ?? null,\n requestMethod: requestContext?.requestMethod ?? null,\n requestIpAddress: requestContext?.requestIpAddress ?? getRequestIP(event) ?? null,\n requestUserAgent: requestContext?.requestUserAgent ?? getRequestHeader(event, 'user-agent') ?? null,\n data: { providerId: provider.id, providerName: provider.name },\n });\n\n return { data: account, meta: {} };\n }\n\n private pickIdentityCandidate(\n identity: IdentityProviderIdentity,\n key: 'name' | 'email',\n ) : string | null {\n const candidates = identity.attributeCandidates?.[key] || [];\n for (const candidate of candidates) {\n if (typeof candidate === 'string' && candidate.length > 0) {\n return candidate;\n }\n }\n\n return null;\n }\n\n // ---------------------------------------------------------\n\n private buildHostedAuthorizeURL(codeRequest: OAuth2AuthorizationCodeRequest): URL {\n const url = new URL(resolveURL(this.options.baseURL, 'authorize'));\n\n const codeRequestKeys = Object.keys(codeRequest);\n for (const codeRequestKey_ of codeRequestKeys) {\n const codeRequestKey = codeRequestKey_ as keyof OAuth2AuthorizationCodeRequest;\n const codeRequestValue = codeRequest[codeRequestKey];\n // Preserve meaningful falsy values (e.g. max_age=0, which OIDC\n // treats as prompt=login) — only skip absent ones.\n if (typeof codeRequestValue !== 'undefined' && codeRequestValue !== null) {\n url.searchParams.set(codeRequestKey, String(codeRequestValue));\n }\n }\n\n return url;\n }\n\n // ---------------------------------------------------------\n\n private async resolve(id: string) {\n const entity = await this.repository.findOneById(id);\n\n if (!entity) {\n throw new EntityNotFoundError();\n }\n\n return entity;\n }\n\n // ---------------------------------------------------------\n\n private buildProviderAuthenticator(\n provider: OAuth2IdentityProvider | OpenIDIdentityProvider,\n options: { clientId?: string } = {},\n ) {\n return createIdentityProviderOAuth2Authenticator({\n accountManager: this.accountManager,\n provider,\n logger: this.logger,\n options: {\n baseURL: this.options.baseURL,\n clientId: options.clientId,\n },\n });\n }\n\n private async saveAuthorizationState(\n event: IAppEvent,\n data: Pick<OAuth2AuthorizationState, 'codeRequest' | 'link'> = {},\n ) : Promise<string> {\n return this.stateManager.save({\n ...data,\n ip: getRequestIP(event) ?? '',\n userAgent: getRequestHeader(event, 'user-agent') ?? undefined,\n });\n }\n\n private async verifyAuthorizationState(event: IAppEvent): Promise<OAuth2AuthorizationState> {\n const query = useRequestQuery(event);\n if (typeof query.state !== 'string') {\n throw OAuth2RequestError.stateInvalid();\n }\n\n return this.stateManager.verify(query.state, {\n ip: getRequestIP(event) ?? '',\n userAgent: getRequestHeader(event, 'user-agent') ?? undefined,\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import type { Logger } from '@authup/server-kit';
|
|
2
|
-
import type { IEventService, IIdentityProviderAccountLinkStore, IIdentityProviderAccountManager, IIdentityProviderRepository,
|
|
2
|
+
import type { IEventService, IIdentityProviderAccountLinkStore, IIdentityProviderAccountManager, IIdentityProviderRepository, IOAuth2AuthorizationCodeRequestVerifier, IOAuth2AuthorizationStateManager, IOAuth2FederatedLoginService } from '../../../../../core/index.ts';
|
|
3
3
|
export type IdentityProviderControllerOptions = {
|
|
4
4
|
baseURL: string;
|
|
5
5
|
};
|
|
6
6
|
export type IdentityProviderControllerContext = {
|
|
7
7
|
options: IdentityProviderControllerOptions;
|
|
8
8
|
repository: IIdentityProviderRepository;
|
|
9
|
-
realmRepository: IRealmRepository;
|
|
10
9
|
accountManager: IIdentityProviderAccountManager;
|
|
11
10
|
linkStore: IIdentityProviderAccountLinkStore;
|
|
12
|
-
codeIssuer: IOAuth2AuthorizationCodeIssuer;
|
|
13
11
|
codeRequestVerifier: IOAuth2AuthorizationCodeRequestVerifier;
|
|
14
12
|
stateManager: IOAuth2AuthorizationStateManager;
|
|
15
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Owns the federated-login completion ladder. The controller only maps
|
|
15
|
+
* its result onto a transport.
|
|
16
|
+
*/
|
|
17
|
+
loginService: IOAuth2FederatedLoginService;
|
|
16
18
|
eventService?: IEventService;
|
|
17
19
|
logger?: Logger;
|
|
18
20
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../src/adapters/http/controllers/entities/identity-provider/types.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EACR,aAAa,EACb,iCAAiC,EACjC,+BAA+B,EAC/B,2BAA2B,EAC3B,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../src/adapters/http/controllers/entities/identity-provider/types.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EACR,aAAa,EACb,iCAAiC,EACjC,+BAA+B,EAC/B,2BAA2B,EAC3B,uCAAuC,EACvC,gCAAgC,EAChC,4BAA4B,EAC/B,MAAM,8BAA8B,CAAC;AAEtC,MAAM,MAAM,iCAAiC,GAAG;IAC5C,OAAO,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,iCAAiC,GAAG;IAC5C,OAAO,EAAE,iCAAiC,CAAC;IAE3C,UAAU,EAAE,2BAA2B,CAAC;IAExC,cAAc,EAAE,+BAA+B,CAAC;IAChD,SAAS,EAAE,iCAAiC,CAAC;IAC7C,mBAAmB,EAAE,uCAAuC,CAAC;IAC7D,YAAY,EAAE,gCAAgC,CAAC;IAC/C;;;OAGG;IACH,YAAY,EAAE,4BAA4B,CAAC;IAE3C,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC"}
|
|
@@ -3,12 +3,12 @@ import "node:url";
|
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { RECORD_QUERY_PARAMETERS, describeQuerySchema } from "../../../../../core/query/describe.mjs";
|
|
5
5
|
import { realmSchema } from "../../../../../core/entities/realm/schema.mjs";
|
|
6
|
+
import { resolveURL } from "../../../../../utils/url.mjs";
|
|
7
|
+
import "../../../../../utils/index.mjs";
|
|
6
8
|
import "../../../../../core/index.mjs";
|
|
7
9
|
import { buildActorContext } from "../../../request/helpers/actor.mjs";
|
|
8
10
|
import "../../../request/index.mjs";
|
|
9
11
|
import { ForceLoggedInMiddleware } from "../../../middleware/built-in/loggedin/module.mjs";
|
|
10
|
-
import { resolveURL } from "../../../../../utils/url.mjs";
|
|
11
|
-
import "../../../../../utils/index.mjs";
|
|
12
12
|
import "../../../middleware/index.mjs";
|
|
13
13
|
import { getJwkRouteHandler, getJwksRouteHandler } from "../../workflows/jwks/handlers/read.mjs";
|
|
14
14
|
import "../../workflows/index.mjs";
|
|
@@ -3,13 +3,13 @@ import "node:url";
|
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { RECORD_QUERY_PARAMETERS, describeQuerySchema } from "../../../../../core/query/describe.mjs";
|
|
5
5
|
import { sessionSchema } from "../../../../../core/entities/session/schema.mjs";
|
|
6
|
+
import { isSelfToken } from "../../../../../utils/self-id.mjs";
|
|
7
|
+
import "../../../../../utils/index.mjs";
|
|
6
8
|
import "../../../../../core/index.mjs";
|
|
7
9
|
import { buildActorContext } from "../../../request/helpers/actor.mjs";
|
|
8
10
|
import { useRequestSessionId } from "../../../request/helpers/session-id.mjs";
|
|
9
11
|
import "../../../request/index.mjs";
|
|
10
12
|
import { ForceLoggedInMiddleware } from "../../../middleware/built-in/loggedin/module.mjs";
|
|
11
|
-
import { isSelfToken } from "../../../../../utils/self-id.mjs";
|
|
12
|
-
import "../../../../../utils/index.mjs";
|
|
13
13
|
import "../../../middleware/index.mjs";
|
|
14
14
|
import { useRequestQuery } from "@routup/basic/query";
|
|
15
15
|
import { DContext, DController, DDelete, DGet, DPath, DTags } from "@routup/decorators";
|
|
@@ -3,13 +3,13 @@ import "node:url";
|
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { RECORD_QUERY_PARAMETERS, describeQuerySchema } from "../../../../../core/query/describe.mjs";
|
|
5
5
|
import { userSchema } from "../../../../../core/entities/user/schema.mjs";
|
|
6
|
+
import { isSelfToken } from "../../../../../utils/self-id.mjs";
|
|
7
|
+
import "../../../../../utils/index.mjs";
|
|
6
8
|
import "../../../../../core/index.mjs";
|
|
7
9
|
import { buildActorContext } from "../../../request/helpers/actor.mjs";
|
|
8
10
|
import { applyRouteRealmIDToBody, getRequestRealmID } from "../../../request/helpers/realm-id.mjs";
|
|
9
11
|
import "../../../request/index.mjs";
|
|
10
12
|
import { ForceLoggedInMiddleware } from "../../../middleware/built-in/loggedin/module.mjs";
|
|
11
|
-
import { isSelfToken } from "../../../../../utils/self-id.mjs";
|
|
12
|
-
import "../../../../../utils/index.mjs";
|
|
13
13
|
import "../../../middleware/index.mjs";
|
|
14
14
|
import { useRequestQuery } from "@routup/basic/query";
|
|
15
15
|
import { DBody, DContext, DController, DDelete, DGet, DPath, DPost, DPut, DTags } from "@routup/decorators";
|
|
@@ -3,13 +3,13 @@ import "node:url";
|
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { RECORD_QUERY_PARAMETERS, describeQuerySchema } from "../../../../../core/query/describe.mjs";
|
|
5
5
|
import { userAuthenticatorSchema } from "../../../../../core/entities/user-authenticator/schema.mjs";
|
|
6
|
+
import { isSelfToken } from "../../../../../utils/self-id.mjs";
|
|
7
|
+
import "../../../../../utils/index.mjs";
|
|
6
8
|
import "../../../../../core/index.mjs";
|
|
7
9
|
import { useRequestIdentity } from "../../../request/helpers/identity.mjs";
|
|
8
10
|
import { buildActorContext } from "../../../request/helpers/actor.mjs";
|
|
9
11
|
import "../../../request/index.mjs";
|
|
10
12
|
import { ForceLoggedInMiddleware } from "../../../middleware/built-in/loggedin/module.mjs";
|
|
11
|
-
import { isSelfToken } from "../../../../../utils/self-id.mjs";
|
|
12
|
-
import "../../../../../utils/index.mjs";
|
|
13
13
|
import "../../../middleware/index.mjs";
|
|
14
14
|
import { IdentityType } from "@authup/core-kit";
|
|
15
15
|
import { BadRequestError } from "@authup/errors";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../../../src/adapters/http/controllers/workflows/authorize/module.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../../../src/adapters/http/controllers/workflows/authorize/module.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAexC,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAGlE,OAAO,KAAK,EAAE,uCAAuC,EAAE,MAAM,8BAA8B,CAAC;AAC5F,OAAO,EAAE,uCAAuC,EAAE,MAAM,8BAA8B,CAAC;AACvF,OAAO,KAAK,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAMzF,qBACa,mBAAmB;IAI5B,SAAS,CAAC,OAAO,EAAE,0BAA0B,CAAC;IAE9C,SAAS,CAAC,mBAAmB,EAAG,uCAAuC,CAAC;IAExE,SAAS,CAAC,oBAAoB,EAAG,uCAAuC,CAAC;IAEzE,SAAS,CAAC,UAAU,EAAE,oBAAoB,CAAC;gBAI/B,GAAG,EAAE,0BAA0B;IAyBrC,OAAO,CAAa,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAmC/D,KAAK,CAAa,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC;CAsG7D"}
|
|
@@ -2,18 +2,18 @@ import "node:path";
|
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { OAuth2AuthorizationCodeRequestValidator } from "../../../../../core/oauth2/authorization/code-request/validator.mjs";
|
|
5
|
+
import { sanitizeError } from "../../../../../utils/error.mjs";
|
|
6
|
+
import "../../../../../utils/index.mjs";
|
|
5
7
|
import "../../../../../core/index.mjs";
|
|
6
8
|
import { readFromLocations } from "../../../request/helpers/locations.mjs";
|
|
7
9
|
import "../../../request/index.mjs";
|
|
8
10
|
import { ForceUserLoggedInMiddleware } from "../../../middleware/built-in/loggedin/user.mjs";
|
|
9
11
|
import { renderAuthConsolePage } from "../../../ui/auth-console/module.mjs";
|
|
10
|
-
import { sanitizeError } from "../../../../../utils/error.mjs";
|
|
11
|
-
import "../../../../../utils/index.mjs";
|
|
12
12
|
import "../../../middleware/index.mjs";
|
|
13
13
|
import "../../../ui/index.mjs";
|
|
14
14
|
import { HTTPOAuth2Authorizer } from "../../../adapters/oauth2/authorization/module.mjs";
|
|
15
15
|
import "../../../adapters/index.mjs";
|
|
16
|
-
import { OAuth2AccessDeniedError, OAuth2ErrorCode, isOAuth2AccessDeniedError } from "@authup/specs";
|
|
16
|
+
import { OAuth2AccessDeniedError, OAuth2ErrorCode, OAuth2LoginRequiredError, isOAuth2AccessDeniedError } from "@authup/specs";
|
|
17
17
|
import { URL } from "node:url";
|
|
18
18
|
import { useRequestQuery } from "@routup/basic/query";
|
|
19
19
|
import { DContext, DController, DGet, DPost } from "@routup/decorators";
|
|
@@ -106,8 +106,12 @@ var AuthorizeController = class {
|
|
|
106
106
|
};
|
|
107
107
|
}
|
|
108
108
|
if (!error) {
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
const query = useRequestQuery(event);
|
|
110
|
+
let mapped;
|
|
111
|
+
if (query.error === OAuth2ErrorCode.ACCESS_DENIED) mapped = OAuth2AccessDeniedError.forClient();
|
|
112
|
+
else if (query.error === OAuth2ErrorCode.LOGIN_REQUIRED) mapped = OAuth2LoginRequiredError.providerUnavailable();
|
|
113
|
+
if (mapped) {
|
|
114
|
+
const normalized = sanitizeError(mapped);
|
|
111
115
|
error = {
|
|
112
116
|
...normalized,
|
|
113
117
|
message: normalized.message
|