@stamhoofd/backend 2.67.0 → 2.69.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +10 -10
- package/src/endpoints/auth/CreateTokenEndpoint.ts +16 -3
- package/src/endpoints/{organization/shared/auth → auth}/OpenIDConnectCallbackEndpoint.ts +16 -6
- package/src/endpoints/{organization/shared/auth → auth}/OpenIDConnectStartEndpoint.ts +32 -8
- package/src/endpoints/auth/PatchUserEndpoint.ts +1 -1
- package/src/endpoints/auth/SignupEndpoint.ts +14 -2
- package/src/endpoints/{organization/dashboard/organization/GetOrganizationSSOEndpoint.ts → global/sso/GetSSOEndpoint.ts} +11 -9
- package/src/endpoints/global/sso/SetSSOEndpoint.ts +68 -0
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +16 -2
- package/src/helpers/AdminPermissionChecker.ts +7 -1
- package/src/helpers/OpenIDConnectHelper.ts +12 -4
- package/src/endpoints/organization/dashboard/organization/SetOrganizationSSOEndpoint.ts +0 -50
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stamhoofd/backend",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.69.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -37,14 +37,14 @@
|
|
|
37
37
|
"@simonbackx/simple-encoding": "2.19.0",
|
|
38
38
|
"@simonbackx/simple-endpoints": "1.15.0",
|
|
39
39
|
"@simonbackx/simple-logging": "^1.0.1",
|
|
40
|
-
"@stamhoofd/backend-i18n": "2.
|
|
41
|
-
"@stamhoofd/backend-middleware": "2.
|
|
42
|
-
"@stamhoofd/email": "2.
|
|
43
|
-
"@stamhoofd/models": "2.
|
|
44
|
-
"@stamhoofd/queues": "2.
|
|
45
|
-
"@stamhoofd/sql": "2.
|
|
46
|
-
"@stamhoofd/structures": "2.
|
|
47
|
-
"@stamhoofd/utility": "2.
|
|
40
|
+
"@stamhoofd/backend-i18n": "2.69.0",
|
|
41
|
+
"@stamhoofd/backend-middleware": "2.69.0",
|
|
42
|
+
"@stamhoofd/email": "2.69.0",
|
|
43
|
+
"@stamhoofd/models": "2.69.0",
|
|
44
|
+
"@stamhoofd/queues": "2.69.0",
|
|
45
|
+
"@stamhoofd/sql": "2.69.0",
|
|
46
|
+
"@stamhoofd/structures": "2.69.0",
|
|
47
|
+
"@stamhoofd/utility": "2.69.0",
|
|
48
48
|
"archiver": "^7.0.1",
|
|
49
49
|
"aws-sdk": "^2.885.0",
|
|
50
50
|
"axios": "1.6.8",
|
|
@@ -64,5 +64,5 @@
|
|
|
64
64
|
"publishConfig": {
|
|
65
65
|
"access": "public"
|
|
66
66
|
},
|
|
67
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "1cc1c8c1109298b0c712b1e7ddf7ef37054be5a9"
|
|
68
68
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
2
2
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
3
|
-
import { EmailVerificationCode, PasswordToken, Token, User } from '@stamhoofd/models';
|
|
4
|
-
import { ChallengeGrantStruct, CreateTokenStruct, PasswordGrantStruct, PasswordTokenGrantStruct, RefreshTokenGrantStruct, RequestChallengeGrantStruct, SignupResponse, Token as TokenStruct } from '@stamhoofd/structures';
|
|
3
|
+
import { EmailVerificationCode, PasswordToken, Platform, Token, User } from '@stamhoofd/models';
|
|
4
|
+
import { ChallengeGrantStruct, CreateTokenStruct, LoginMethod, PasswordGrantStruct, PasswordTokenGrantStruct, RefreshTokenGrantStruct, RequestChallengeGrantStruct, SignupResponse, Token as TokenStruct } from '@stamhoofd/structures';
|
|
5
5
|
|
|
6
6
|
import { Context } from '../../helpers/Context';
|
|
7
7
|
|
|
@@ -81,8 +81,21 @@ export class CreateTokenEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
case 'password': {
|
|
84
|
-
|
|
84
|
+
// Increase timout for legacy
|
|
85
85
|
request.request.request?.setTimeout(30 * 1000);
|
|
86
|
+
|
|
87
|
+
if (STAMHOOFD.userMode === 'platform') {
|
|
88
|
+
const platform = await Platform.getShared();
|
|
89
|
+
if (!platform.config.loginMethods.includes(LoginMethod.Password)) {
|
|
90
|
+
throw new SimpleError({
|
|
91
|
+
code: 'not_supported',
|
|
92
|
+
message: 'This platform does not support password login',
|
|
93
|
+
human: 'Dit platform ondersteunt geen wachtwoord login',
|
|
94
|
+
statusCode: 400,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
86
99
|
const user = await User.login(organization?.id ?? null, request.body.username, request.body.password);
|
|
87
100
|
|
|
88
101
|
const errBody = {
|
|
@@ -2,8 +2,10 @@ import { AnyDecoder, Decoder } from '@simonbackx/simple-encoding';
|
|
|
2
2
|
import { DecodedRequest, Endpoint, Request } from '@simonbackx/simple-endpoints';
|
|
3
3
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
4
4
|
|
|
5
|
-
import { Context } from '
|
|
6
|
-
import { OpenIDConnectHelper } from '
|
|
5
|
+
import { Context } from '../../helpers/Context';
|
|
6
|
+
import { OpenIDConnectHelper } from '../../helpers/OpenIDConnectHelper';
|
|
7
|
+
import { OpenIDClientConfiguration } from '@stamhoofd/structures';
|
|
8
|
+
import { Platform } from '@stamhoofd/models';
|
|
7
9
|
|
|
8
10
|
type Params = Record<string, never>;
|
|
9
11
|
type Query = undefined;
|
|
@@ -27,13 +29,21 @@ export class OpenIDConnectCallbackEndpoint extends Endpoint<Params, Query, Body,
|
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
30
|
-
const organization = await Context.
|
|
31
|
-
|
|
32
|
+
const organization = await Context.setOptionalOrganizationScope();
|
|
33
|
+
let configuration: OpenIDClientConfiguration | null;
|
|
34
|
+
const platform = await Platform.getShared();
|
|
35
|
+
|
|
36
|
+
if (organization) {
|
|
37
|
+
configuration = organization.serverMeta.ssoConfiguration;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
configuration = platform.serverConfig.ssoConfiguration;
|
|
41
|
+
}
|
|
32
42
|
|
|
33
43
|
if (!configuration) {
|
|
34
44
|
throw new SimpleError({
|
|
35
|
-
code: '
|
|
36
|
-
message: '
|
|
45
|
+
code: 'invalid_client',
|
|
46
|
+
message: 'SSO not configured',
|
|
37
47
|
statusCode: 400,
|
|
38
48
|
});
|
|
39
49
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Decoder } from '@simonbackx/simple-encoding';
|
|
2
2
|
import { DecodedRequest, Endpoint, Request } from '@simonbackx/simple-endpoints';
|
|
3
3
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
4
|
-
import { Webshop } from '@stamhoofd/models';
|
|
5
|
-
import { StartOpenIDFlowStruct } from '@stamhoofd/structures';
|
|
4
|
+
import { Platform, Webshop } from '@stamhoofd/models';
|
|
5
|
+
import { OpenIDClientConfiguration, StartOpenIDFlowStruct } from '@stamhoofd/structures';
|
|
6
6
|
|
|
7
|
-
import { Context } from '
|
|
8
|
-
import { OpenIDConnectHelper } from '
|
|
7
|
+
import { Context } from '../../helpers/Context';
|
|
8
|
+
import { OpenIDConnectHelper } from '../../helpers/OpenIDConnectHelper';
|
|
9
9
|
|
|
10
10
|
type Params = Record<string, never>;
|
|
11
11
|
type Query = undefined;
|
|
@@ -30,11 +30,28 @@ export class OpenIDConnectStartEndpoint extends Endpoint<Params, Query, Body, Re
|
|
|
30
30
|
|
|
31
31
|
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
32
32
|
// Check webshop and/or organization
|
|
33
|
-
const organization = await Context.
|
|
33
|
+
const organization = await Context.setOptionalOrganizationScope();
|
|
34
34
|
const webshopId = request.body.webshopId;
|
|
35
|
-
|
|
35
|
+
const platform = await Platform.getShared();
|
|
36
|
+
|
|
37
|
+
// Host should match correctly
|
|
38
|
+
let redirectUri = 'https://' + STAMHOOFD.domains.dashboard;
|
|
39
|
+
|
|
40
|
+
if (organization) {
|
|
41
|
+
redirectUri = 'https://' + organization.getHost();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// todo: also support the app as redirect uri using app schemes (could be required for mobile apps)
|
|
36
45
|
|
|
37
46
|
if (webshopId) {
|
|
47
|
+
if (!organization) {
|
|
48
|
+
throw new SimpleError({
|
|
49
|
+
code: 'invalid_organization',
|
|
50
|
+
message: 'Organization required when specifying webshopId',
|
|
51
|
+
statusCode: 400,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
38
55
|
const webshop = await Webshop.getByID(webshopId);
|
|
39
56
|
if (!webshop || webshop.organizationId !== organization.id) {
|
|
40
57
|
throw new SimpleError({
|
|
@@ -68,7 +85,15 @@ export class OpenIDConnectStartEndpoint extends Endpoint<Params, Query, Body, Re
|
|
|
68
85
|
});
|
|
69
86
|
}
|
|
70
87
|
|
|
71
|
-
|
|
88
|
+
let configuration: OpenIDClientConfiguration | null;
|
|
89
|
+
|
|
90
|
+
if (organization) {
|
|
91
|
+
configuration = organization.serverMeta.ssoConfiguration;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
configuration = platform.serverConfig.ssoConfiguration;
|
|
95
|
+
}
|
|
96
|
+
|
|
72
97
|
if (!configuration) {
|
|
73
98
|
throw new SimpleError({
|
|
74
99
|
code: 'invalid_client',
|
|
@@ -76,7 +101,6 @@ export class OpenIDConnectStartEndpoint extends Endpoint<Params, Query, Body, Re
|
|
|
76
101
|
statusCode: 400,
|
|
77
102
|
});
|
|
78
103
|
}
|
|
79
|
-
|
|
80
104
|
const helper = new OpenIDConnectHelper(organization, configuration);
|
|
81
105
|
return await helper.startAuthCodeFlow(redirectUri, request.body.provider, request.body.spaState, request.body.prompt);
|
|
82
106
|
}
|
|
@@ -109,7 +109,7 @@ export class PatchUserEndpoint extends Endpoint<Params, Query, Body, ResponseBod
|
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
if (editUser.id
|
|
112
|
+
if (editUser.id === user.id && request.body.password) {
|
|
113
113
|
// password changes
|
|
114
114
|
await editUser.changePassword(request.body.password);
|
|
115
115
|
await PasswordToken.clearFor(editUser.id);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Decoder } from '@simonbackx/simple-encoding';
|
|
2
2
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
3
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
4
|
-
import { EmailVerificationCode, PasswordToken, sendEmailTemplate, User } from '@stamhoofd/models';
|
|
5
|
-
import { EmailTemplateType, NewUser, Recipient, Replacement, SignupResponse } from '@stamhoofd/structures';
|
|
4
|
+
import { EmailVerificationCode, PasswordToken, Platform, sendEmailTemplate, User } from '@stamhoofd/models';
|
|
5
|
+
import { EmailTemplateType, LoginMethod, NewUser, Recipient, Replacement, SignupResponse } from '@stamhoofd/structures';
|
|
6
6
|
|
|
7
7
|
import { Context } from '../../helpers/Context';
|
|
8
8
|
|
|
@@ -30,6 +30,18 @@ export class SignupEndpoint extends Endpoint<Params, Query, Body, ResponseBody>
|
|
|
30
30
|
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
31
31
|
const organization = await Context.setUserOrganizationScope();
|
|
32
32
|
|
|
33
|
+
if (STAMHOOFD.userMode === 'platform') {
|
|
34
|
+
const platform = await Platform.getShared();
|
|
35
|
+
if (!platform.config.loginMethods.includes(LoginMethod.Password)) {
|
|
36
|
+
throw new SimpleError({
|
|
37
|
+
code: 'not_supported',
|
|
38
|
+
message: 'This platform does not support password login',
|
|
39
|
+
human: 'Dit platform ondersteunt geen wachtwoord login',
|
|
40
|
+
statusCode: 400,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
33
45
|
const u = await User.getForRegister(organization?.id ?? null, request.body.email);
|
|
34
46
|
|
|
35
47
|
// Don't optimize. Always run two queries atm.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
2
2
|
import { OpenIDClientConfiguration } from '@stamhoofd/structures';
|
|
3
3
|
|
|
4
|
-
import { Context } from '
|
|
4
|
+
import { Context } from '../../../helpers/Context';
|
|
5
|
+
import { Platform } from '@stamhoofd/models';
|
|
5
6
|
|
|
6
7
|
type Params = Record<string, never>;
|
|
7
8
|
type Query = undefined;
|
|
@@ -18,7 +19,7 @@ export class GetOrganizationSSOEndpoint extends Endpoint<Params, Query, Body, Re
|
|
|
18
19
|
return [false];
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
const params = Endpoint.parseParameters(request.url, '/
|
|
22
|
+
const params = Endpoint.parseParameters(request.url, '/sso', {});
|
|
22
23
|
|
|
23
24
|
if (params) {
|
|
24
25
|
return [true, params as Params];
|
|
@@ -27,17 +28,18 @@ export class GetOrganizationSSOEndpoint extends Endpoint<Params, Query, Body, Re
|
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
async handle(_: DecodedRequest<Params, Query, Body>) {
|
|
30
|
-
const organization = await Context.
|
|
31
|
+
const organization = await Context.setOptionalOrganizationScope();
|
|
31
32
|
await Context.authenticate();
|
|
32
33
|
|
|
33
|
-
if (!await Context.auth.canManageSSOSettings(organization
|
|
34
|
+
if (!await Context.auth.canManageSSOSettings(organization?.id ?? null)) {
|
|
34
35
|
throw Context.auth.error();
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
if (organization) {
|
|
39
|
+
return new Response(organization.serverMeta.ssoConfiguration ?? OpenIDClientConfiguration.create({}));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const platform = await Platform.getShared();
|
|
43
|
+
return new Response(platform.serverConfig.ssoConfiguration ?? OpenIDClientConfiguration.create({}));
|
|
42
44
|
}
|
|
43
45
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { AutoEncoderPatchType, Decoder } from '@simonbackx/simple-encoding';
|
|
2
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
|
+
import { OpenIDClientConfiguration } from '@stamhoofd/structures';
|
|
4
|
+
|
|
5
|
+
import { Context } from '../../../helpers/Context';
|
|
6
|
+
import { OpenIDConnectHelper } from '../../../helpers/OpenIDConnectHelper';
|
|
7
|
+
import { Platform } from '@stamhoofd/models';
|
|
8
|
+
|
|
9
|
+
type Params = Record<string, never>;
|
|
10
|
+
type Query = undefined;
|
|
11
|
+
type Body = AutoEncoderPatchType<OpenIDClientConfiguration>;
|
|
12
|
+
type ResponseBody = OpenIDClientConfiguration;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* One endpoint to create, patch and delete groups. Usefull because on organization setup, we need to create multiple groups at once. Also, sometimes we need to link values and update multiple groups at once
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export class SetOrganizationSSOEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
19
|
+
bodyDecoder = OpenIDClientConfiguration.patchType() as Decoder<AutoEncoderPatchType<OpenIDClientConfiguration>>;
|
|
20
|
+
|
|
21
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
22
|
+
if (request.method !== 'POST') {
|
|
23
|
+
return [false];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const params = Endpoint.parseParameters(request.url, '/sso', {});
|
|
27
|
+
|
|
28
|
+
if (params) {
|
|
29
|
+
return [true, params as Params];
|
|
30
|
+
}
|
|
31
|
+
return [false];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
35
|
+
const organization = await Context.setOptionalOrganizationScope();
|
|
36
|
+
await Context.authenticate();
|
|
37
|
+
|
|
38
|
+
if (!await Context.auth.canManageSSOSettings(organization?.id ?? null)) {
|
|
39
|
+
throw Context.auth.error();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let newConfig: OpenIDClientConfiguration;
|
|
43
|
+
|
|
44
|
+
if (organization) {
|
|
45
|
+
newConfig = (organization.serverMeta.ssoConfiguration ?? OpenIDClientConfiguration.create({})).patch(request.body);
|
|
46
|
+
|
|
47
|
+
// Validate configuration
|
|
48
|
+
const helper = new OpenIDConnectHelper(organization, newConfig);
|
|
49
|
+
await helper.getClient();
|
|
50
|
+
|
|
51
|
+
organization.serverMeta.ssoConfiguration = newConfig;
|
|
52
|
+
await organization.save();
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
const platform = await Platform.getShared();
|
|
56
|
+
newConfig = (platform.serverConfig.ssoConfiguration ?? OpenIDClientConfiguration.create({})).patch(request.body);
|
|
57
|
+
|
|
58
|
+
// Validate configuration
|
|
59
|
+
const helper = new OpenIDConnectHelper(null, newConfig);
|
|
60
|
+
await helper.getClient();
|
|
61
|
+
|
|
62
|
+
platform.serverConfig.ssoConfiguration = newConfig;
|
|
63
|
+
await platform.save();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return new Response(newConfig);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -243,8 +243,22 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
|
|
|
243
243
|
}
|
|
244
244
|
const platform = await Platform.getSharedStruct();
|
|
245
245
|
|
|
246
|
-
|
|
247
|
-
|
|
246
|
+
const defaultAgeGroup = platform.config.defaultAgeGroups.find(g => g.id === id);
|
|
247
|
+
|
|
248
|
+
if (defaultAgeGroup) {
|
|
249
|
+
const organization = Context.organization;
|
|
250
|
+
const tags = organization?.meta.tags ?? [];
|
|
251
|
+
|
|
252
|
+
if (defaultAgeGroup.isEnabledForTags(tags)) {
|
|
253
|
+
return id;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
throw new SimpleError({
|
|
257
|
+
code: 'invalid_default_age_group',
|
|
258
|
+
message: 'Invalid default age group',
|
|
259
|
+
human: 'De standaard leeftijdsgroep is niet beschikbaar voor deze organisatie',
|
|
260
|
+
statusCode: 400,
|
|
261
|
+
});
|
|
248
262
|
}
|
|
249
263
|
|
|
250
264
|
throw new SimpleError({
|
|
@@ -556,6 +556,9 @@ export class AdminPermissionChecker {
|
|
|
556
556
|
}
|
|
557
557
|
|
|
558
558
|
async canEditUserEmail(user: User) {
|
|
559
|
+
if (user.meta?.loginProviderIds?.size) {
|
|
560
|
+
return false;
|
|
561
|
+
}
|
|
559
562
|
return this.canEditUserName(user);
|
|
560
563
|
}
|
|
561
564
|
|
|
@@ -758,7 +761,10 @@ export class AdminPermissionChecker {
|
|
|
758
761
|
return this.hasFullAccess(organizationId);
|
|
759
762
|
}
|
|
760
763
|
|
|
761
|
-
canManageSSOSettings(organizationId: string) {
|
|
764
|
+
canManageSSOSettings(organizationId: string | null) {
|
|
765
|
+
if (!organizationId) {
|
|
766
|
+
return this.hasPlatformFullAccess();
|
|
767
|
+
}
|
|
762
768
|
return this.hasFullAccess(organizationId);
|
|
763
769
|
}
|
|
764
770
|
|
|
@@ -30,17 +30,25 @@ type SessionContext = {
|
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
export class OpenIDConnectHelper {
|
|
33
|
-
organization: Organization;
|
|
33
|
+
organization: Organization | null;
|
|
34
34
|
configuration: OpenIDClientConfiguration;
|
|
35
35
|
|
|
36
36
|
static sessionStorage = new Map<string, SessionContext>();
|
|
37
37
|
|
|
38
|
-
constructor(organization, configuration: OpenIDClientConfiguration) {
|
|
38
|
+
constructor(organization: Organization | null, configuration: OpenIDClientConfiguration) {
|
|
39
39
|
this.organization = organization;
|
|
40
40
|
this.configuration = configuration;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
get redirectUri() {
|
|
44
|
+
if (this.configuration.redirectUri) {
|
|
45
|
+
return this.configuration.redirectUri;
|
|
46
|
+
}
|
|
47
|
+
// todo: we might need a special url for the app here
|
|
48
|
+
|
|
49
|
+
if (!this.organization) {
|
|
50
|
+
return 'https://' + STAMHOOFD.domains.api + '/openid/callback';
|
|
51
|
+
}
|
|
44
52
|
return 'https://' + this.organization.id + '.' + STAMHOOFD.domains.api + '/openid/callback';
|
|
45
53
|
}
|
|
46
54
|
|
|
@@ -73,7 +81,7 @@ export class OpenIDConnectHelper {
|
|
|
73
81
|
// Store
|
|
74
82
|
CookieHelper.setCookie(response, 'oid_session_id', sessionId, {
|
|
75
83
|
httpOnly: true,
|
|
76
|
-
secure:
|
|
84
|
+
secure: STAMHOOFD.environment !== 'development',
|
|
77
85
|
expires: data.expires,
|
|
78
86
|
});
|
|
79
87
|
}
|
|
@@ -209,7 +217,7 @@ export class OpenIDConnectHelper {
|
|
|
209
217
|
}
|
|
210
218
|
|
|
211
219
|
// Get user from database
|
|
212
|
-
let user = await User.
|
|
220
|
+
let user = await User.getForRegister(this.organization?.id ?? null, claims.email);
|
|
213
221
|
if (!user) {
|
|
214
222
|
// Create a new user
|
|
215
223
|
user = await User.registerSSO(this.organization, {
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { Decoder } from '@simonbackx/simple-encoding';
|
|
2
|
-
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
|
-
import { OpenIDClientConfiguration } from '@stamhoofd/structures';
|
|
4
|
-
|
|
5
|
-
import { Context } from '../../../../helpers/Context';
|
|
6
|
-
import { OpenIDConnectHelper } from '../../../../helpers/OpenIDConnectHelper';
|
|
7
|
-
|
|
8
|
-
type Params = Record<string, never>;
|
|
9
|
-
type Query = undefined;
|
|
10
|
-
type Body = OpenIDClientConfiguration;
|
|
11
|
-
type ResponseBody = OpenIDClientConfiguration;
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* One endpoint to create, patch and delete groups. Usefull because on organization setup, we need to create multiple groups at once. Also, sometimes we need to link values and update multiple groups at once
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
export class SetOrganizationSSOEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
18
|
-
bodyDecoder = OpenIDClientConfiguration as Decoder<OpenIDClientConfiguration>;
|
|
19
|
-
|
|
20
|
-
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
21
|
-
if (request.method !== 'POST') {
|
|
22
|
-
return [false];
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const params = Endpoint.parseParameters(request.url, '/organization/sso', {});
|
|
26
|
-
|
|
27
|
-
if (params) {
|
|
28
|
-
return [true, params as Params];
|
|
29
|
-
}
|
|
30
|
-
return [false];
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
34
|
-
const organization = await Context.setOrganizationScope();
|
|
35
|
-
await Context.authenticate();
|
|
36
|
-
|
|
37
|
-
if (!await Context.auth.canManageSSOSettings(organization.id)) {
|
|
38
|
-
throw Context.auth.error();
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Validate configuration
|
|
42
|
-
const helper = new OpenIDConnectHelper(organization, request.body);
|
|
43
|
-
await helper.getClient();
|
|
44
|
-
|
|
45
|
-
organization.serverMeta.ssoConfiguration = request.body;
|
|
46
|
-
await organization.save();
|
|
47
|
-
|
|
48
|
-
return new Response(request.body);
|
|
49
|
-
}
|
|
50
|
-
}
|