@solidxai/core 0.1.10-beta.27 → 0.1.10-beta.28
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/MICROSOFT_ACTIVE_DIRECTORY_OAUTH_FLOW.md +616 -0
- package/dist/constants/error-messages.d.ts +1 -0
- package/dist/constants/error-messages.d.ts.map +1 -1
- package/dist/constants/error-messages.js +1 -0
- package/dist/constants/error-messages.js.map +1 -1
- package/dist/controllers/microsoft-active-directory-authentication.controller.d.ts +28 -0
- package/dist/controllers/microsoft-active-directory-authentication.controller.d.ts.map +1 -0
- package/dist/controllers/microsoft-active-directory-authentication.controller.js +122 -0
- package/dist/controllers/microsoft-active-directory-authentication.controller.js.map +1 -0
- package/dist/entities/user.entity.d.ts +3 -0
- package/dist/entities/user.entity.d.ts.map +1 -1
- package/dist/entities/user.entity.js +13 -1
- package/dist/entities/user.entity.js.map +1 -1
- package/dist/helpers/microsoft-active-directory-oauth.helper.d.ts +34 -0
- package/dist/helpers/microsoft-active-directory-oauth.helper.d.ts.map +1 -0
- package/dist/helpers/microsoft-active-directory-oauth.helper.js +43 -0
- package/dist/helpers/microsoft-active-directory-oauth.helper.js.map +1 -0
- package/dist/helpers/user-helper.d.ts.map +1 -1
- package/dist/helpers/user-helper.js +4 -0
- package/dist/helpers/user-helper.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/passport-strategies/microsoft-active-directory-oauth.strategy.d.ts +14 -0
- package/dist/passport-strategies/microsoft-active-directory-oauth.strategy.d.ts.map +1 -0
- package/dist/passport-strategies/microsoft-active-directory-oauth.strategy.js +67 -0
- package/dist/passport-strategies/microsoft-active-directory-oauth.strategy.js.map +1 -0
- package/dist/services/authentication.service.d.ts +12 -0
- package/dist/services/authentication.service.d.ts.map +1 -1
- package/dist/services/authentication.service.js +64 -0
- package/dist/services/authentication.service.js.map +1 -1
- package/dist/services/settings/default-settings-provider.service.d.ts +100 -0
- package/dist/services/settings/default-settings-provider.service.d.ts.map +1 -1
- package/dist/services/settings/default-settings-provider.service.js +56 -0
- package/dist/services/settings/default-settings-provider.service.js.map +1 -1
- package/dist/services/user.service.d.ts +2 -0
- package/dist/services/user.service.d.ts.map +1 -1
- package/dist/services/user.service.js +46 -0
- package/dist/services/user.service.js.map +1 -1
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +4 -0
- package/dist/solid-core.module.js.map +1 -1
- package/package.json +1 -1
- package/src/constants/error-messages.ts +2 -1
- package/src/controllers/microsoft-active-directory-authentication.controller.ts +104 -0
- package/src/entities/user.entity.ts +13 -1
- package/src/helpers/microsoft-active-directory-oauth.helper.ts +83 -0
- package/src/helpers/user-helper.ts +5 -1
- package/src/index.ts +1 -0
- package/src/passport-strategies/microsoft-active-directory-oauth.strategy.ts +63 -0
- package/src/services/authentication.service.ts +78 -0
- package/src/services/settings/default-settings-provider.service.ts +59 -0
- package/src/services/user.service.ts +61 -0
- package/src/solid-core.module.ts +4 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Controller,
|
|
3
|
+
Get,
|
|
4
|
+
InternalServerErrorException,
|
|
5
|
+
Query,
|
|
6
|
+
Req,
|
|
7
|
+
Res,
|
|
8
|
+
UseGuards,
|
|
9
|
+
} from "@nestjs/common";
|
|
10
|
+
import { ApiQuery, ApiTags } from "@nestjs/swagger";
|
|
11
|
+
import { Request, Response } from "express";
|
|
12
|
+
import {
|
|
13
|
+
DEFAULT_MICROSOFT_ACTIVE_DIRECTORY_OAUTH_TENANT,
|
|
14
|
+
MicrosoftActiveDirectoryAuthConfiguration,
|
|
15
|
+
isMicrosoftActiveDirectoryOAuthConfigured,
|
|
16
|
+
} from "../helpers/microsoft-active-directory-oauth.helper";
|
|
17
|
+
import { AuthenticationService } from "../services/authentication.service";
|
|
18
|
+
import { SettingService } from "../services/setting.service";
|
|
19
|
+
import { Public } from "src/decorators/public.decorator";
|
|
20
|
+
import { Auth } from "../decorators/auth.decorator";
|
|
21
|
+
import { AuthType } from "../enums/auth-type.enum";
|
|
22
|
+
import { MicrosoftActiveDirectoryOauthGuard } from "../passport-strategies/microsoft-active-directory-oauth.strategy";
|
|
23
|
+
import { UserService } from "../services/user.service";
|
|
24
|
+
import type { SolidCoreSetting } from "../services/settings/default-settings-provider.service";
|
|
25
|
+
|
|
26
|
+
@Auth(AuthType.None)
|
|
27
|
+
@ApiTags("Solid Core")
|
|
28
|
+
@Controller("iam/microsoft-active-directory")
|
|
29
|
+
export class MicrosoftActiveDirectoryAuthenticationController {
|
|
30
|
+
constructor(
|
|
31
|
+
private readonly userService: UserService,
|
|
32
|
+
private readonly authService: AuthenticationService,
|
|
33
|
+
private readonly settingService: SettingService,
|
|
34
|
+
) {}
|
|
35
|
+
|
|
36
|
+
private async getConfiguration(): Promise<MicrosoftActiveDirectoryAuthConfiguration> {
|
|
37
|
+
return {
|
|
38
|
+
clientID: this.settingService.getConfigValue<SolidCoreSetting>("MICROSOFT_ACTIVE_DIRECTORY_CLIENT_ID"),
|
|
39
|
+
clientSecret: this.settingService.getConfigValue<SolidCoreSetting>("MICROSOFT_ACTIVE_DIRECTORY_CLIENT_SECRET"),
|
|
40
|
+
tenant: this.settingService.getConfigValue<SolidCoreSetting>("MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID" ) ?? DEFAULT_MICROSOFT_ACTIVE_DIRECTORY_OAUTH_TENANT,
|
|
41
|
+
callbackURL: this.settingService.getConfigValue<SolidCoreSetting>("MICROSOFT_ACTIVE_DIRECTORY_CALLBACK_URL"),
|
|
42
|
+
redirectURL: this.settingService.getConfigValue<SolidCoreSetting>("MICROSOFT_ACTIVE_DIRECTORY_REDIRECT_URL"),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private buildFrontendRedirectUrl(redirectURL: string, accessCode: string) {
|
|
47
|
+
const separator = redirectURL.includes("?") ? "&" : "?";
|
|
48
|
+
return `${redirectURL}${separator}accessCode=${encodeURIComponent(accessCode)}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private async validateConfiguration() {
|
|
52
|
+
const config = await this.getConfiguration();
|
|
53
|
+
if (!isMicrosoftActiveDirectoryOAuthConfigured(config)) {
|
|
54
|
+
throw new InternalServerErrorException("Microsoft Active Directory OAuth is not configured");
|
|
55
|
+
}
|
|
56
|
+
return config;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@Public()
|
|
60
|
+
@UseGuards(MicrosoftActiveDirectoryOauthGuard)
|
|
61
|
+
@Get("connect")
|
|
62
|
+
async connect() {
|
|
63
|
+
await this.validateConfiguration();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@Public()
|
|
67
|
+
@Get("connect/callback")
|
|
68
|
+
@UseGuards(MicrosoftActiveDirectoryOauthGuard)
|
|
69
|
+
async microsoftActiveDirectoryAuthCallback(
|
|
70
|
+
@Req() req: Request,
|
|
71
|
+
@Res() res: Response,
|
|
72
|
+
) {
|
|
73
|
+
const config = await this.validateConfiguration();
|
|
74
|
+
const user = req.user;
|
|
75
|
+
return res.redirect(
|
|
76
|
+
this.buildFrontendRedirectUrl(config.redirectURL, user["accessCode"]),
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@Public()
|
|
81
|
+
@Get("dummy-redirect")
|
|
82
|
+
async dummyMicrosoftActiveDirectoryAuthRedirect(
|
|
83
|
+
@Query("accessCode") accessCode,
|
|
84
|
+
) {
|
|
85
|
+
await this.validateConfiguration();
|
|
86
|
+
const user = await this.userService.findOneByAccessCode(accessCode);
|
|
87
|
+
|
|
88
|
+
if (user) {
|
|
89
|
+
delete user["password"];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return user;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@Public()
|
|
96
|
+
@Get("authenticate")
|
|
97
|
+
@ApiQuery({ name: "accessCode", required: true, type: String })
|
|
98
|
+
async microsoftActiveDirectoryAuth(
|
|
99
|
+
@Query("accessCode") accessCode: string,
|
|
100
|
+
) {
|
|
101
|
+
await this.validateConfiguration();
|
|
102
|
+
return this.authService.signInUsingMicrosoftActiveDirectory(accessCode);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -89,6 +89,18 @@ export class User extends CommonEntity {
|
|
|
89
89
|
// don't send to client
|
|
90
90
|
microsoftProfilePicture: string;
|
|
91
91
|
|
|
92
|
+
@Column({ type: "varchar", nullable: true })
|
|
93
|
+
// don't send to client
|
|
94
|
+
microsoftActiveDirectoryId: string;
|
|
95
|
+
|
|
96
|
+
@Column({ type: "varchar", nullable: true })
|
|
97
|
+
// don't send to client
|
|
98
|
+
microsoftActiveDirectoryAccessToken: string;
|
|
99
|
+
|
|
100
|
+
@Column({ type: "varchar", nullable: true })
|
|
101
|
+
// don't send to client
|
|
102
|
+
microsoftActiveDirectoryProfilePicture: string;
|
|
103
|
+
|
|
92
104
|
@Index()
|
|
93
105
|
@Column({ default: true })
|
|
94
106
|
@Expose()
|
|
@@ -194,4 +206,4 @@ export class User extends CommonEntity {
|
|
|
194
206
|
@Expose()
|
|
195
207
|
apiKeys: UserApiKey[];
|
|
196
208
|
|
|
197
|
-
}
|
|
209
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export type MicrosoftActiveDirectoryAuthConfiguration = {
|
|
2
|
+
clientID: string;
|
|
3
|
+
clientSecret: string;
|
|
4
|
+
tenant: string;
|
|
5
|
+
callbackURL: string;
|
|
6
|
+
redirectURL: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const DEFAULT_MICROSOFT_ACTIVE_DIRECTORY_OAUTH_TENANT = "common";
|
|
10
|
+
export const MICROSOFT_ACTIVE_DIRECTORY_OAUTH_SCOPES = [
|
|
11
|
+
"openid",
|
|
12
|
+
"profile",
|
|
13
|
+
"email",
|
|
14
|
+
"User.Read",
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
type MicrosoftActiveDirectoryOauthProfileValue = {
|
|
18
|
+
value?: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type MicrosoftActiveDirectoryOauthProfile = {
|
|
22
|
+
id?: string;
|
|
23
|
+
displayName?: string;
|
|
24
|
+
emails?: MicrosoftActiveDirectoryOauthProfileValue[];
|
|
25
|
+
photos?: MicrosoftActiveDirectoryOauthProfileValue[];
|
|
26
|
+
_json?: {
|
|
27
|
+
id?: string;
|
|
28
|
+
displayName?: string;
|
|
29
|
+
mail?: string;
|
|
30
|
+
userPrincipalName?: string;
|
|
31
|
+
email?: string;
|
|
32
|
+
preferred_username?: string;
|
|
33
|
+
picture?: string;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const isMicrosoftActiveDirectoryOAuthConfigured = (
|
|
38
|
+
config: MicrosoftActiveDirectoryAuthConfiguration,
|
|
39
|
+
): boolean => {
|
|
40
|
+
return !!(
|
|
41
|
+
config.clientID &&
|
|
42
|
+
config.clientSecret &&
|
|
43
|
+
config.tenant &&
|
|
44
|
+
config.callbackURL &&
|
|
45
|
+
config.redirectURL
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const getMicrosoftActiveDirectoryOAuthProfileId = (
|
|
50
|
+
profile: MicrosoftActiveDirectoryOauthProfile,
|
|
51
|
+
): string | null => {
|
|
52
|
+
return profile.id || profile._json?.id || null;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const getMicrosoftActiveDirectoryOAuthEmail = (
|
|
56
|
+
profile: MicrosoftActiveDirectoryOauthProfile,
|
|
57
|
+
): string | null => {
|
|
58
|
+
const email =
|
|
59
|
+
profile.emails?.find((item) => !!item.value)?.value ||
|
|
60
|
+
profile._json?.mail ||
|
|
61
|
+
profile._json?.userPrincipalName ||
|
|
62
|
+
profile._json?.email ||
|
|
63
|
+
profile._json?.preferred_username ||
|
|
64
|
+
null;
|
|
65
|
+
|
|
66
|
+
return email?.trim().toLowerCase() || null;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export const getMicrosoftActiveDirectoryOAuthDisplayName = (
|
|
70
|
+
profile: MicrosoftActiveDirectoryOauthProfile,
|
|
71
|
+
): string | null => {
|
|
72
|
+
return profile.displayName || profile._json?.displayName || null;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const getMicrosoftActiveDirectoryOAuthPicture = (
|
|
76
|
+
profile: MicrosoftActiveDirectoryOauthProfile,
|
|
77
|
+
): string | null => {
|
|
78
|
+
return (
|
|
79
|
+
profile.photos?.find((item) => !!item.value)?.value ||
|
|
80
|
+
profile._json?.picture ||
|
|
81
|
+
null
|
|
82
|
+
);
|
|
83
|
+
};
|
|
@@ -15,6 +15,10 @@ export function getUserExcludedFields(): string[] {
|
|
|
15
15
|
"facebookId",
|
|
16
16
|
"microsoftAccessToken",
|
|
17
17
|
"microsoftId",
|
|
18
|
+
"microsoftProfilePicture",
|
|
19
|
+
"microsoftActiveDirectoryAccessToken",
|
|
20
|
+
"microsoftActiveDirectoryId",
|
|
21
|
+
"microsoftActiveDirectoryProfilePicture",
|
|
18
22
|
"forgotPasswordConfirmedAt",
|
|
19
23
|
"verificationTokenOnForgotPassword",
|
|
20
24
|
"verificationTokenOnForgotPasswordExpiresAt",
|
|
@@ -38,4 +42,4 @@ export function getUserExcludedFields(): string[] {
|
|
|
38
42
|
"userViewMetadataIds",
|
|
39
43
|
"userViewMetadataCommand",
|
|
40
44
|
];
|
|
41
|
-
}
|
|
45
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -269,6 +269,7 @@ export * from './listeners/user-registration.listener'
|
|
|
269
269
|
export * from './passport-strategies/google-oauth.strategy'
|
|
270
270
|
export * from './passport-strategies/facebook-oauth.strategy'
|
|
271
271
|
export * from './passport-strategies/microsoft-oauth.strategy'
|
|
272
|
+
export * from './passport-strategies/microsoft-active-directory-oauth.strategy'
|
|
272
273
|
|
|
273
274
|
export * from './services/selection-providers/list-of-values-selection-providers.service'
|
|
274
275
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Injectable, Logger, UnauthorizedException } from '@nestjs/common';
|
|
2
|
+
import { AuthGuard, PassportStrategy } from '@nestjs/passport';
|
|
3
|
+
import { Strategy } from 'passport-microsoft';
|
|
4
|
+
import { DEFAULT_MICROSOFT_ACTIVE_DIRECTORY_OAUTH_TENANT, MICROSOFT_ACTIVE_DIRECTORY_OAUTH_SCOPES, MicrosoftActiveDirectoryAuthConfiguration, getMicrosoftActiveDirectoryOAuthDisplayName, getMicrosoftActiveDirectoryOAuthEmail, getMicrosoftActiveDirectoryOAuthPicture, getMicrosoftActiveDirectoryOAuthProfileId, isMicrosoftActiveDirectoryOAuthConfigured } from 'src/helpers/microsoft-active-directory-oauth.helper';
|
|
5
|
+
import { v4 as uuid } from 'uuid';
|
|
6
|
+
import { UserService } from '../services/user.service';
|
|
7
|
+
|
|
8
|
+
const DUMMY_CLIENT_ID = 'DUMMY_CLIENT_ID';
|
|
9
|
+
const DUMMY_CLIENT_SECRET = 'DUMMY_CLIENT_SECRET';
|
|
10
|
+
const DUMMY_CALLBACK_URL = 'DUMMY_CALLBACK_URL';
|
|
11
|
+
|
|
12
|
+
@Injectable()
|
|
13
|
+
export class MicrosoftActiveDirectoryOauthGuard extends AuthGuard('microsoft-active-directory') { }
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@Injectable()
|
|
17
|
+
export class MicrosoftActiveDirectoryOAuthStrategy extends PassportStrategy(Strategy, 'microsoft-active-directory') {
|
|
18
|
+
private readonly logger = new Logger(MicrosoftActiveDirectoryOAuthStrategy.name);
|
|
19
|
+
constructor(private readonly userService: UserService) {
|
|
20
|
+
// TODO: Have added default dummy values for the configuration, since the configuration is not mandatory.
|
|
21
|
+
// Perhaps a cleaner way needs to be figured out
|
|
22
|
+
const clientID = process.env.IAM_MICROSOFT_ACTIVE_DIRECTORY_OAUTH_CLIENT_ID || DUMMY_CLIENT_ID;
|
|
23
|
+
const clientSecret = process.env.IAM_MICROSOFT_ACTIVE_DIRECTORY_OAUTH_CLIENT_SECRET || DUMMY_CLIENT_SECRET;
|
|
24
|
+
const tenant = process.env.IAM_MICROSOFT_ACTIVE_DIRECTORY_OAUTH_TENANT_ID || DEFAULT_MICROSOFT_ACTIVE_DIRECTORY_OAUTH_TENANT;
|
|
25
|
+
const callbackURL = process.env.IAM_MICROSOFT_ACTIVE_DIRECTORY_OAUTH_CALLBACK_URL || DUMMY_CALLBACK_URL;
|
|
26
|
+
const redirectURL = process.env.IAM_MICROSOFT_ACTIVE_DIRECTORY_OAUTH_REDIRECT_URL;
|
|
27
|
+
|
|
28
|
+
super({ clientID, clientSecret, callbackURL, tenant, scope: MICROSOFT_ACTIVE_DIRECTORY_OAUTH_SCOPES, addUPNAsEmail: true });
|
|
29
|
+
|
|
30
|
+
const microsoftActiveDirectoryOauth: MicrosoftActiveDirectoryAuthConfiguration = { clientID, clientSecret, tenant, callbackURL, redirectURL }
|
|
31
|
+
if (!isMicrosoftActiveDirectoryOAuthConfigured(microsoftActiveDirectoryOauth)) {
|
|
32
|
+
this.logger.debug('Microsoft Active Directory OAuth strategy is not configured');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async validate(_accessToken: string, _refreshToken: string, profile: any, done: any): Promise<any> {
|
|
37
|
+
const providerId = getMicrosoftActiveDirectoryOAuthProfileId(profile);
|
|
38
|
+
|
|
39
|
+
if (!providerId) {
|
|
40
|
+
return done(new UnauthorizedException('Microsoft Active Directory OAuth profile is missing an id'), false);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// generate a unique access code.
|
|
44
|
+
const loginAccessCode: string = uuid();
|
|
45
|
+
|
|
46
|
+
const user = {
|
|
47
|
+
provider: 'microsoftActiveDirectory',
|
|
48
|
+
providerId,
|
|
49
|
+
email: getMicrosoftActiveDirectoryOAuthEmail(profile),
|
|
50
|
+
name: getMicrosoftActiveDirectoryOAuthDisplayName(profile) || getMicrosoftActiveDirectoryOAuthEmail(profile) || 'Microsoft Active Directory User',
|
|
51
|
+
picture: getMicrosoftActiveDirectoryOAuthPicture(profile),
|
|
52
|
+
accessCode: loginAccessCode,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// store the access code and the access token in the database.
|
|
56
|
+
// while doing this we also check if the user exists in the database if not we create one.
|
|
57
|
+
// if exists then we update the user and store the specified access code & token.
|
|
58
|
+
await this.userService.resolveUserOnOauthMicrosoftActiveDirectory({ ...user, accessToken: _accessToken, refreshToken: null });
|
|
59
|
+
|
|
60
|
+
done(null, user);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
}
|
|
@@ -1997,6 +1997,84 @@ export class AuthenticationService {
|
|
|
1997
1997
|
};
|
|
1998
1998
|
}
|
|
1999
1999
|
|
|
2000
|
+
async validateUserUsingMicrosoftActiveDirectory(user: User) {
|
|
2001
|
+
if (
|
|
2002
|
+
!user.microsoftActiveDirectoryAccessToken ||
|
|
2003
|
+
!user.microsoftActiveDirectoryId
|
|
2004
|
+
) {
|
|
2005
|
+
throw new UnauthorizedException(ERROR_MESSAGES.USER_NOT_FOUND);
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
try {
|
|
2009
|
+
const response = await this.httpService.axiosRef.get(
|
|
2010
|
+
`https://graph.microsoft.com/v1.0/me?$select=id,displayName,mail,userPrincipalName`,
|
|
2011
|
+
{
|
|
2012
|
+
headers: {
|
|
2013
|
+
Authorization: `Bearer ${user.microsoftActiveDirectoryAccessToken}`,
|
|
2014
|
+
},
|
|
2015
|
+
},
|
|
2016
|
+
);
|
|
2017
|
+
const userProfile = response.data;
|
|
2018
|
+
const profileEmail = (userProfile.mail || userProfile.userPrincipalName)
|
|
2019
|
+
?.trim()
|
|
2020
|
+
.toLowerCase();
|
|
2021
|
+
const userEmail = user.email?.trim().toLowerCase();
|
|
2022
|
+
|
|
2023
|
+
if (
|
|
2024
|
+
userProfile.id === user.microsoftActiveDirectoryId &&
|
|
2025
|
+
(!userEmail || profileEmail === userEmail)
|
|
2026
|
+
) {
|
|
2027
|
+
return userProfile;
|
|
2028
|
+
} else {
|
|
2029
|
+
throw new UnauthorizedException(ERROR_MESSAGES.INVALID_USER_PROFILE);
|
|
2030
|
+
}
|
|
2031
|
+
} catch (error: any) {
|
|
2032
|
+
if (error instanceof UnauthorizedException) {
|
|
2033
|
+
throw error;
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
throw new UnauthorizedException(
|
|
2037
|
+
ERROR_MESSAGES.MICROSOFT_ACTIVE_DIRECTORY_OAUTH_PROFILE_FETCH_FAILED,
|
|
2038
|
+
);
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
async signInUsingMicrosoftActiveDirectory(accessCode: string) {
|
|
2043
|
+
const user = await this.userRepository.findOne({
|
|
2044
|
+
where: {
|
|
2045
|
+
accessCode: accessCode,
|
|
2046
|
+
},
|
|
2047
|
+
relations: {
|
|
2048
|
+
roles: true,
|
|
2049
|
+
},
|
|
2050
|
+
});
|
|
2051
|
+
|
|
2052
|
+
if (!user) {
|
|
2053
|
+
throw new UnauthorizedException(ERROR_MESSAGES.USER_NOT_FOUND);
|
|
2054
|
+
}
|
|
2055
|
+
this.checkAccountBlocked(user);
|
|
2056
|
+
|
|
2057
|
+
try {
|
|
2058
|
+
await this.validateUserUsingMicrosoftActiveDirectory(user);
|
|
2059
|
+
} catch (e) {
|
|
2060
|
+
await this.incrementFailedAttempts(user);
|
|
2061
|
+
throw e;
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
await this.resetFailedAttempts(user);
|
|
2065
|
+
const tokens = await this.generateTokens(user);
|
|
2066
|
+
return {
|
|
2067
|
+
user: {
|
|
2068
|
+
email: user.email,
|
|
2069
|
+
mobile: user.mobile,
|
|
2070
|
+
username: user.username,
|
|
2071
|
+
id: user.id,
|
|
2072
|
+
roles: user.roles.map((role) => role.name),
|
|
2073
|
+
},
|
|
2074
|
+
...tokens,
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2000
2078
|
async signInUsingApple(accessCode: string) {
|
|
2001
2079
|
const user = await this.userRepository.findOne({
|
|
2002
2080
|
where: {
|
|
@@ -43,6 +43,16 @@ const getSolidCoreSettings = (isProd: boolean) =>
|
|
|
43
43
|
sortOrder: 50,
|
|
44
44
|
controlType: "boolean",
|
|
45
45
|
},
|
|
46
|
+
{
|
|
47
|
+
moduleName: "solid-core",
|
|
48
|
+
key: "iamMicrosoftActiveDirectoryOAuthEnabled",
|
|
49
|
+
value: false,
|
|
50
|
+
level: SettingLevel.SystemAdminEditable,
|
|
51
|
+
label: "Allow Login / Signup With Microsoft Active Directory",
|
|
52
|
+
group: "authentication-settings",
|
|
53
|
+
sortOrder: 50,
|
|
54
|
+
controlType: "boolean",
|
|
55
|
+
},
|
|
46
56
|
{
|
|
47
57
|
moduleName: "solid-core",
|
|
48
58
|
key: "authPagesLayout",
|
|
@@ -703,6 +713,55 @@ const getSolidCoreSettings = (isProd: boolean) =>
|
|
|
703
713
|
level: SettingLevel.SystemAdminReadonly,
|
|
704
714
|
},
|
|
705
715
|
|
|
716
|
+
// microsoft-active-directory-oauth-settings-provider.service.ts
|
|
717
|
+
{
|
|
718
|
+
moduleName: "solid-core",
|
|
719
|
+
key: "MICROSOFT_ACTIVE_DIRECTORY_CLIENT_ID",
|
|
720
|
+
value: process.env.IAM_MICROSOFT_ACTIVE_DIRECTORY_OAUTH_CLIENT_ID,
|
|
721
|
+
level: SettingLevel.SystemAdminReadonly,
|
|
722
|
+
label: "Microsoft Active Directory OAuth Client ID",
|
|
723
|
+
group: "oauth-settings",
|
|
724
|
+
sortOrder: 70,
|
|
725
|
+
controlType: "shortText",
|
|
726
|
+
},
|
|
727
|
+
{
|
|
728
|
+
moduleName: "solid-core",
|
|
729
|
+
key: "MICROSOFT_ACTIVE_DIRECTORY_CLIENT_SECRET",
|
|
730
|
+
value: process.env.IAM_MICROSOFT_ACTIVE_DIRECTORY_OAUTH_CLIENT_SECRET,
|
|
731
|
+
level: SettingLevel.SystemEnv,
|
|
732
|
+
},
|
|
733
|
+
{
|
|
734
|
+
moduleName: "solid-core",
|
|
735
|
+
key: "MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID",
|
|
736
|
+
value:
|
|
737
|
+
process.env.IAM_MICROSOFT_ACTIVE_DIRECTORY_OAUTH_TENANT_ID || "common",
|
|
738
|
+
level: SettingLevel.SystemAdminReadonly,
|
|
739
|
+
label: "Microsoft Active Directory OAuth Tenant ID",
|
|
740
|
+
group: "oauth-settings",
|
|
741
|
+
sortOrder: 80,
|
|
742
|
+
controlType: "shortText",
|
|
743
|
+
},
|
|
744
|
+
{
|
|
745
|
+
moduleName: "solid-core",
|
|
746
|
+
key: "MICROSOFT_ACTIVE_DIRECTORY_CALLBACK_URL",
|
|
747
|
+
value: process.env.IAM_MICROSOFT_ACTIVE_DIRECTORY_OAUTH_CALLBACK_URL,
|
|
748
|
+
level: SettingLevel.SystemAdminReadonly,
|
|
749
|
+
label: "Microsoft Active Directory OAuth Callback URL",
|
|
750
|
+
group: "oauth-settings",
|
|
751
|
+
sortOrder: 90,
|
|
752
|
+
controlType: "shortText",
|
|
753
|
+
},
|
|
754
|
+
{
|
|
755
|
+
moduleName: "solid-core",
|
|
756
|
+
key: "MICROSOFT_ACTIVE_DIRECTORY_REDIRECT_URL",
|
|
757
|
+
value: process.env.IAM_MICROSOFT_ACTIVE_DIRECTORY_OAUTH_REDIRECT_URL,
|
|
758
|
+
level: SettingLevel.SystemAdminReadonly,
|
|
759
|
+
label: "Microsoft Active Directory OAuth Redirect URL",
|
|
760
|
+
group: "oauth-settings",
|
|
761
|
+
sortOrder: 100,
|
|
762
|
+
controlType: "shortText",
|
|
763
|
+
},
|
|
764
|
+
|
|
706
765
|
// iam-settings-provider.service.ts
|
|
707
766
|
{
|
|
708
767
|
moduleName: "solid-core",
|
|
@@ -30,6 +30,23 @@ export class UserService extends CRUDService<User> {
|
|
|
30
30
|
return normalized || "facebook_user";
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
private buildMicrosoftActiveDirectoryUsernameBase(
|
|
34
|
+
email?: string,
|
|
35
|
+
providerId?: string,
|
|
36
|
+
name?: string,
|
|
37
|
+
): string {
|
|
38
|
+
const source =
|
|
39
|
+
email ||
|
|
40
|
+
name ||
|
|
41
|
+
(providerId ? `microsoft_active_directory_${providerId}` : "");
|
|
42
|
+
const normalized = source
|
|
43
|
+
.trim()
|
|
44
|
+
.toLowerCase()
|
|
45
|
+
.replace(/[^a-z0-9@._-]+/g, "_")
|
|
46
|
+
.replace(/^_+|_+$/g, "");
|
|
47
|
+
return normalized || "microsoft_active_directory_user";
|
|
48
|
+
}
|
|
49
|
+
|
|
33
50
|
private async resolveUniqueUsername(
|
|
34
51
|
preferredUsername: string,
|
|
35
52
|
// fallbackUsername: string,
|
|
@@ -397,6 +414,50 @@ export class UserService extends CRUDService<User> {
|
|
|
397
414
|
return user;
|
|
398
415
|
}
|
|
399
416
|
|
|
417
|
+
async resolveUserOnOauthMicrosoftActiveDirectory(
|
|
418
|
+
oauthUserDto: OauthUserDto,
|
|
419
|
+
): Promise<User> {
|
|
420
|
+
const user = await this.repo.findOne({
|
|
421
|
+
where: {
|
|
422
|
+
email: oauthUserDto.email,
|
|
423
|
+
},
|
|
424
|
+
relations: {
|
|
425
|
+
roles: true,
|
|
426
|
+
},
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
if (!user) {
|
|
430
|
+
const newUser = new User();
|
|
431
|
+
newUser.username = oauthUserDto.email;
|
|
432
|
+
newUser.email = oauthUserDto.email;
|
|
433
|
+
newUser.fullName = oauthUserDto.name;
|
|
434
|
+
newUser.lastLoginProvider = oauthUserDto.provider;
|
|
435
|
+
newUser.accessCode = oauthUserDto.accessCode;
|
|
436
|
+
newUser.microsoftActiveDirectoryAccessToken = oauthUserDto.accessToken;
|
|
437
|
+
newUser.microsoftActiveDirectoryId = oauthUserDto.providerId;
|
|
438
|
+
newUser.microsoftActiveDirectoryProfilePicture = oauthUserDto.picture;
|
|
439
|
+
|
|
440
|
+
const savedUser = await this.repo.save(newUser);
|
|
441
|
+
|
|
442
|
+
await this.initializeRolesForNewUser(
|
|
443
|
+
[this.settingService.getConfigValue<SolidCoreSetting>("defaultRole")],
|
|
444
|
+
savedUser,
|
|
445
|
+
);
|
|
446
|
+
} else {
|
|
447
|
+
const entity = await this.repo.preload({
|
|
448
|
+
id: user.id,
|
|
449
|
+
lastLoginProvider: oauthUserDto.provider,
|
|
450
|
+
accessCode: oauthUserDto.accessCode,
|
|
451
|
+
microsoftActiveDirectoryAccessToken: oauthUserDto.accessToken,
|
|
452
|
+
microsoftActiveDirectoryId: oauthUserDto.providerId,
|
|
453
|
+
microsoftActiveDirectoryProfilePicture: oauthUserDto.picture,
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
await this.repo.save(entity);
|
|
457
|
+
}
|
|
458
|
+
return user;
|
|
459
|
+
}
|
|
460
|
+
|
|
400
461
|
async findUsersByRole(
|
|
401
462
|
roleName: string,
|
|
402
463
|
relations: any = {},
|
package/src/solid-core.module.ts
CHANGED
|
@@ -66,8 +66,10 @@ import { ActionMetadataService } from "./services/action-metadata.service";
|
|
|
66
66
|
|
|
67
67
|
import { FacebookAuthenticationController } from "./controllers/facebook-authentication.controller";
|
|
68
68
|
import { MicrosoftAuthenticationController } from "./controllers/microsoft-authentication.controller";
|
|
69
|
+
import { MicrosoftActiveDirectoryAuthenticationController } from "./controllers/microsoft-active-directory-authentication.controller";
|
|
69
70
|
import { FacebookOAuthStrategy } from "./passport-strategies/facebook-oauth.strategy";
|
|
70
71
|
import { MicrosoftOAuthStrategy } from "./passport-strategies/microsoft-oauth.strategy";
|
|
72
|
+
import { MicrosoftActiveDirectoryOAuthStrategy } from "./passport-strategies/microsoft-active-directory-oauth.strategy";
|
|
71
73
|
|
|
72
74
|
import { GupshupOtpWhatsappService } from "./services/whatsapp/GupshupOtpWhatsappService";
|
|
73
75
|
import { MetaCloudWhatsappService } from "./services/whatsapp/MetaCloudWhatsappService";
|
|
@@ -464,6 +466,7 @@ import { DashboardUserLayoutRepository } from './repositories/dashboard-user-lay
|
|
|
464
466
|
GoogleAuthenticationController,
|
|
465
467
|
FacebookAuthenticationController,
|
|
466
468
|
MicrosoftAuthenticationController,
|
|
469
|
+
MicrosoftActiveDirectoryAuthenticationController,
|
|
467
470
|
ImportTransactionController,
|
|
468
471
|
ImportTransactionErrorLogController,
|
|
469
472
|
ListOfValuesController,
|
|
@@ -637,6 +640,7 @@ import { DashboardUserLayoutRepository } from './repositories/dashboard-user-lay
|
|
|
637
640
|
GoogleOauthStrategy,
|
|
638
641
|
FacebookOAuthStrategy,
|
|
639
642
|
MicrosoftOAuthStrategy,
|
|
643
|
+
MicrosoftActiveDirectoryOAuthStrategy,
|
|
640
644
|
UserRegistrationListener,
|
|
641
645
|
TestQueuePublisher,
|
|
642
646
|
TestQueueSubscriber,
|