@things-factory/auth-azure-ad 6.1.185 → 6.1.186
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/config/config.development.js +24 -15
- package/config/config.production.js +24 -15
- package/dist-server/controllers/get-access-token.js +6 -7
- package/dist-server/controllers/get-access-token.js.map +1 -1
- package/dist-server/controllers/subscribe-users-change.js +3 -4
- package/dist-server/controllers/subscribe-users-change.js.map +1 -1
- package/dist-server/controllers/sync-user-info.js +25 -8
- package/dist-server/controllers/sync-user-info.js.map +1 -1
- package/dist-server/index.js +12 -0
- package/dist-server/index.js.map +1 -1
- package/dist-server/middlewares/azure-ad-authenticate-middleware.js +2 -2
- package/dist-server/middlewares/azure-ad-authenticate-middleware.js.map +1 -1
- package/dist-server/middlewares/index.js +1 -1
- package/dist-server/middlewares/index.js.map +1 -1
- package/dist-server/router/auth-azure-ad-webhook-router.js +51 -40
- package/dist-server/router/auth-azure-ad-webhook-router.js.map +1 -1
- package/dist-server/routes.js +10 -1
- package/dist-server/routes.js.map +1 -1
- package/dist-server/service/auth-azure-ad/auth-azure-ad-mutation.js +7 -14
- package/dist-server/service/auth-azure-ad/auth-azure-ad-mutation.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/server/controllers/get-access-token.ts +9 -8
- package/server/controllers/subscribe-users-change.ts +4 -4
- package/server/controllers/sync-user-info.ts +39 -10
- package/server/index.ts +14 -0
- package/server/middlewares/azure-ad-authenticate-middleware.ts +2 -1
- package/server/middlewares/index.ts +1 -1
- package/server/router/auth-azure-ad-webhook-router.ts +62 -48
- package/server/routes.ts +13 -1
- package/server/service/auth-azure-ad/auth-azure-ad-mutation.ts +7 -7
|
@@ -1,16 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
1
|
+
/**
|
|
2
|
+
* SSO configuration for Azure Active Directory
|
|
3
|
+
*
|
|
4
|
+
{
|
|
5
|
+
sso: {
|
|
6
|
+
azure: {
|
|
7
|
+
title: 'Microsoft Entra ID',
|
|
8
|
+
link: '/auth/signin-with-azure', // signin-link
|
|
9
|
+
config: {
|
|
10
|
+
clientID: 'your_client_id',
|
|
11
|
+
clientSecret: 'your_client_secret',
|
|
12
|
+
identityMetadata: 'https://login.microsoftonline.com/your_tenant_id/v2.0/.well-known/openid-configuration',
|
|
13
|
+
responseType: 'code id_token',
|
|
14
|
+
responseMode: 'form_post',
|
|
15
|
+
redirectUrl: 'http://localhost:3000/auth/openid/return',
|
|
16
|
+
allowHttpForRedirectUrl: true,
|
|
17
|
+
validateIssuer: false,
|
|
18
|
+
passReqToCallback: false
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
16
22
|
}
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
module.exports = {}
|
|
@@ -1,16 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
1
|
+
/**
|
|
2
|
+
* SSO configuration for Azure Active Directory
|
|
3
|
+
*
|
|
4
|
+
{
|
|
5
|
+
sso: {
|
|
6
|
+
azure: {
|
|
7
|
+
title: 'Microsoft Entra ID',
|
|
8
|
+
link: '/auth/signin-with-azure', // signin-link
|
|
9
|
+
config: {
|
|
10
|
+
clientID: 'your_client_id',
|
|
11
|
+
clientSecret: 'your_client_secret',
|
|
12
|
+
identityMetadata: 'https://login.microsoftonline.com/your_tenant_id/v2.0/.well-known/openid-configuration',
|
|
13
|
+
responseType: 'code id_token',
|
|
14
|
+
responseMode: 'form_post',
|
|
15
|
+
redirectUrl: 'http://localhost:3000/auth/openid/return',
|
|
16
|
+
allowHttpForRedirectUrl: true,
|
|
17
|
+
validateIssuer: false,
|
|
18
|
+
passReqToCallback: false
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
16
22
|
}
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
module.exports = {}
|
|
@@ -3,20 +3,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getAccessToken = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const node_fetch_1 = tslib_1.__importDefault(require("node-fetch"));
|
|
6
|
-
const env_1 = require("@things-factory/env");
|
|
7
|
-
const SSOAzureADConfig = env_1.config.get('SSOAzureAD');
|
|
8
6
|
const authorityHostUrl = 'https://login.microsoftonline.com';
|
|
9
|
-
|
|
10
|
-
const resource = 'https://graph.microsoft.com';
|
|
11
|
-
const OAUTH2_URL = `${authorityUrl}/oauth2/token?api-version=1.0`;
|
|
12
|
-
async function getAccessToken() {
|
|
7
|
+
async function getAccessToken(authProvider) {
|
|
13
8
|
try {
|
|
9
|
+
const { clientId, clientSecret, tenantId } = authProvider;
|
|
10
|
+
const authorityUrl = `${authorityHostUrl}/${tenantId}`;
|
|
11
|
+
const resource = 'https://graph.microsoft.com';
|
|
12
|
+
const OAUTH2_URL = `${authorityUrl}/oauth2/token?api-version=1.0`;
|
|
14
13
|
const tokenResponse = await (0, node_fetch_1.default)(OAUTH2_URL, {
|
|
15
14
|
method: 'POST',
|
|
16
15
|
headers: {
|
|
17
16
|
'Content-Type': 'application/x-www-form-urlencoded'
|
|
18
17
|
},
|
|
19
|
-
body: `client_id=${
|
|
18
|
+
body: `client_id=${clientId}&resource=${resource}&client_secret=${clientSecret}&grant_type=client_credentials`
|
|
20
19
|
});
|
|
21
20
|
const result = await tokenResponse.json();
|
|
22
21
|
const { access_token } = result;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-access-token.js","sourceRoot":"","sources":["../../server/controllers/get-access-token.ts"],"names":[],"mappings":";;;;AAAA,oEAA8B;
|
|
1
|
+
{"version":3,"file":"get-access-token.js","sourceRoot":"","sources":["../../server/controllers/get-access-token.ts"],"names":[],"mappings":";;;;AAAA,oEAA8B;AAI9B,MAAM,gBAAgB,GAAG,mCAAmC,CAAA;AAErD,KAAK,UAAU,cAAc,CAAC,YAA0B;IAC7D,IAAI;QACF,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,GAAG,YAAY,CAAA;QAEzD,MAAM,YAAY,GAAG,GAAG,gBAAgB,IAAI,QAAQ,EAAE,CAAA;QACtD,MAAM,QAAQ,GAAG,6BAA6B,CAAA;QAC9C,MAAM,UAAU,GAAG,GAAG,YAAY,+BAA+B,CAAA;QAEjE,MAAM,aAAa,GAAG,MAAM,IAAA,oBAAK,EAAC,UAAU,EAAE;YAC5C,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,mCAAmC;aACpD;YACD,IAAI,EAAE,aAAa,QAAQ,aAAa,QAAQ,kBAAkB,YAAY,gCAAgC;SAC/G,CAAC,CAAA;QAEF,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,CAAA;QACzC,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,CAAA;QAC/B,OAAO,YAAY,CAAA;KACpB;IAAC,OAAO,KAAK,EAAE;QACd,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAA;QACpD,OAAO,IAAI,CAAA;KACZ;AACH,CAAC;AAvBD,wCAuBC","sourcesContent":["import fetch from 'node-fetch'\n\nimport { AuthProvider } from '@things-factory/auth-base'\n\nconst authorityHostUrl = 'https://login.microsoftonline.com'\n\nexport async function getAccessToken(authProvider: AuthProvider): Promise<string | null> {\n try {\n const { clientId, clientSecret, tenantId } = authProvider\n\n const authorityUrl = `${authorityHostUrl}/${tenantId}`\n const resource = 'https://graph.microsoft.com'\n const OAUTH2_URL = `${authorityUrl}/oauth2/token?api-version=1.0`\n\n const tokenResponse = await fetch(OAUTH2_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded'\n },\n body: `client_id=${clientId}&resource=${resource}&client_secret=${clientSecret}&grant_type=client_credentials`\n })\n\n const result = await tokenResponse.json()\n const { access_token } = result\n return access_token\n } catch (error) {\n console.error('Error fetching access token:', error)\n return null\n }\n}\n"]}
|
|
@@ -3,17 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.msgraphSubscriptions = exports.getClientState = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const node_fetch_1 = tslib_1.__importDefault(require("node-fetch"));
|
|
6
|
-
const crypto_1 = require("crypto");
|
|
7
6
|
const get_access_token_1 = require("./get-access-token");
|
|
8
7
|
var clientState;
|
|
9
8
|
function getClientState() {
|
|
10
9
|
return clientState;
|
|
11
10
|
}
|
|
12
11
|
exports.getClientState = getClientState;
|
|
13
|
-
async function msgraphSubscriptions() {
|
|
14
|
-
const accessToken = await (0, get_access_token_1.getAccessToken)();
|
|
12
|
+
async function msgraphSubscriptions(authProvider) {
|
|
13
|
+
const accessToken = await (0, get_access_token_1.getAccessToken)(authProvider);
|
|
15
14
|
const expirationDateTime = new Date(Date.now() + 3 * 24 * 60 * 60 * 1000);
|
|
16
|
-
clientState =
|
|
15
|
+
clientState = `${authProvider.domainId}:${authProvider.id}`; // TODO make secure
|
|
17
16
|
const response = await (0, node_fetch_1.default)('https://graph.microsoft.com/v1.0/subscriptions', {
|
|
18
17
|
method: 'POST',
|
|
19
18
|
headers: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscribe-users-change.js","sourceRoot":"","sources":["../../server/controllers/subscribe-users-change.ts"],"names":[],"mappings":";;;;AAAA,oEAA8B;
|
|
1
|
+
{"version":3,"file":"subscribe-users-change.js","sourceRoot":"","sources":["../../server/controllers/subscribe-users-change.ts"],"names":[],"mappings":";;;;AAAA,oEAA8B;AAE9B,yDAAmD;AAGnD,IAAI,WAAW,CAAA;AAEf,SAAgB,cAAc;IAC5B,OAAO,WAAW,CAAA;AACpB,CAAC;AAFD,wCAEC;AAEM,KAAK,UAAU,oBAAoB,CAAC,YAA0B;IACnE,MAAM,WAAW,GAAG,MAAM,IAAA,iCAAc,EAAC,YAAY,CAAC,CAAA;IACtD,MAAM,kBAAkB,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;IAEzE,WAAW,GAAG,GAAG,YAAY,CAAC,QAAQ,IAAI,YAAY,CAAC,EAAE,EAAE,CAAA,CAAC,mBAAmB;IAE/E,MAAM,QAAQ,GAAG,MAAM,IAAA,oBAAK,EAAC,gDAAgD,EAAE;QAC7E,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,WAAW,EAAE;YACtC,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,UAAU,EAAE,iBAAiB;YAC7B,eAAe,EAAE,4BAA4B,GAAG,yBAAyB;YACzE,QAAQ,EAAE,QAAQ;YAClB,kBAAkB,EAAE,kBAAkB,CAAC,WAAW,EAAE,CAAC,2BAA2B;YAChF,WAAW;SACZ,CAAC;KACH,CAAC,CAAA;IAEF,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;AACpC,CAAC;AAtBD,oDAsBC","sourcesContent":["import fetch from 'node-fetch'\n\nimport { getAccessToken } from './get-access-token'\nimport { AuthProvider } from '@things-factory/auth-base'\n\nvar clientState\n\nexport function getClientState() {\n return clientState\n}\n\nexport async function msgraphSubscriptions(authProvider: AuthProvider) {\n const accessToken = await getAccessToken(authProvider)\n const expirationDateTime = new Date(Date.now() + 3 * 24 * 60 * 60 * 1000)\n\n clientState = `${authProvider.domainId}:${authProvider.id}` // TODO make secure\n\n const response = await fetch('https://graph.microsoft.com/v1.0/subscriptions', {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n changeType: 'created,updated',\n notificationUrl: 'YOUR_WEBHOOK_ENDPOINT_BASE' + '/webhook/azure-ad-users',\n resource: '/users',\n expirationDateTime: expirationDateTime.toISOString() /* 최대 3일 이내, UTC 타임으로 설정 */,\n clientState\n })\n })\n\n const data = await response.json()\n}\n"]}
|
|
@@ -5,8 +5,8 @@ const tslib_1 = require("tslib");
|
|
|
5
5
|
const node_fetch_1 = tslib_1.__importDefault(require("node-fetch"));
|
|
6
6
|
const auth_base_1 = require("@things-factory/auth-base");
|
|
7
7
|
const get_access_token_1 = require("./get-access-token");
|
|
8
|
-
async function syncAllUserInfo(context) {
|
|
9
|
-
const accessToken = await (0, get_access_token_1.getAccessToken)();
|
|
8
|
+
async function syncAllUserInfo(authProvider, context) {
|
|
9
|
+
const accessToken = await (0, get_access_token_1.getAccessToken)(authProvider);
|
|
10
10
|
if (!accessToken) {
|
|
11
11
|
throw new Error('Failed to obtain access token');
|
|
12
12
|
}
|
|
@@ -18,11 +18,11 @@ async function syncAllUserInfo(context) {
|
|
|
18
18
|
latestData.value
|
|
19
19
|
.filter(({ mail }) => mail)
|
|
20
20
|
.forEach(async (user) => {
|
|
21
|
-
await updateUserInfo(user, context);
|
|
21
|
+
await updateUserInfo(authProvider, user, context);
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
24
|
exports.syncAllUserInfo = syncAllUserInfo;
|
|
25
|
-
async function updateUserInfo(userInfo, context) {
|
|
25
|
+
async function updateUserInfo(authProvider, userInfo, context) {
|
|
26
26
|
const { tx, domain, user } = context.state;
|
|
27
27
|
// {
|
|
28
28
|
// "id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
|
|
@@ -51,7 +51,7 @@ async function updateUserInfo(userInfo, context) {
|
|
|
51
51
|
const salt = auth_base_1.User.generateSalt();
|
|
52
52
|
/* normally they don't login with this password. */
|
|
53
53
|
const password = salt;
|
|
54
|
-
|
|
54
|
+
const createdUser = await repository.save({
|
|
55
55
|
id,
|
|
56
56
|
name: displayName,
|
|
57
57
|
email: mail,
|
|
@@ -61,17 +61,34 @@ async function updateUserInfo(userInfo, context) {
|
|
|
61
61
|
roles: [],
|
|
62
62
|
salt,
|
|
63
63
|
userType: 'user',
|
|
64
|
-
ssoId: id,
|
|
64
|
+
// ssoId: id,
|
|
65
65
|
locale: preferredLanguage,
|
|
66
66
|
status: auth_base_1.UserStatus.ACTIVATED,
|
|
67
67
|
passwordUpdatedAt: new Date(),
|
|
68
68
|
password: auth_base_1.User.encode(password, salt)
|
|
69
69
|
});
|
|
70
|
-
|
|
70
|
+
await tx.getRepository(auth_base_1.UsersAuthProviders).save({
|
|
71
|
+
domain,
|
|
72
|
+
user: createdUser,
|
|
73
|
+
authProvider,
|
|
74
|
+
ssoId: id
|
|
75
|
+
});
|
|
76
|
+
return createdUser;
|
|
71
77
|
}
|
|
72
78
|
else {
|
|
73
79
|
// 3. 사용자가 있다면, 업데이트한다.
|
|
74
|
-
|
|
80
|
+
const { domains } = existingUser;
|
|
81
|
+
if (!domains.find(existing => existing.id == domain.id)) {
|
|
82
|
+
domains.push(domain);
|
|
83
|
+
}
|
|
84
|
+
const updatedUser = await repository.save(Object.assign(Object.assign({}, existingUser), { name: displayName,
|
|
85
|
+
// ssoId: id,
|
|
86
|
+
domains, locale: preferredLanguage, updater: user }));
|
|
87
|
+
const usersAuthProviders = await tx.getRepository(auth_base_1.UsersAuthProviders).findOne({
|
|
88
|
+
where: { domain: { id: domain.id }, user: { id: updatedUser.id }, authProvider: { id: authProvider.id } }
|
|
89
|
+
});
|
|
90
|
+
await tx.getRepository(auth_base_1.UsersAuthProviders).save(Object.assign(Object.assign({}, usersAuthProviders), { domain, user: updatedUser, authProvider, ssoId: id }));
|
|
91
|
+
return updatedUser;
|
|
75
92
|
}
|
|
76
93
|
}
|
|
77
94
|
exports.updateUserInfo = updateUserInfo;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync-user-info.js","sourceRoot":"","sources":["../../server/controllers/sync-user-info.ts"],"names":[],"mappings":";;;;AAAA,oEAA8B;AAE9B,
|
|
1
|
+
{"version":3,"file":"sync-user-info.js","sourceRoot":"","sources":["../../server/controllers/sync-user-info.ts"],"names":[],"mappings":"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","sourcesContent":["import fetch from 'node-fetch'\n\nimport { AuthProvider, User, UserStatus, UsersAuthProviders } from '@things-factory/auth-base'\nimport { getAccessToken } from './get-access-token'\n\nexport async function syncAllUserInfo(authProvider: AuthProvider, context: ResolverContext) {\n const accessToken = await getAccessToken(authProvider)\n\n if (!accessToken) {\n throw new Error('Failed to obtain access token')\n }\n\n const latestData = await fetch('https://graph.microsoft.com/v1.0/users', {\n headers: {\n Authorization: `Bearer ${accessToken}`\n }\n }).then(res => res.json())\n\n latestData.value\n .filter(({ mail }) => mail)\n .forEach(async user => {\n await updateUserInfo(authProvider, user, context)\n })\n}\n\nexport async function updateUserInfo(authProvider: AuthProvider, userInfo, context: ResolverContext) {\n const { tx, domain, user } = context.state\n\n // {\n // \"id\": \"d290f1ee-6c54-4b01-90e6-d701748f0851\",\n // \"businessPhones\": [\n // \"+1 412 555 0109\"\n // ],\n // \"displayName\": \"John Doe\",\n // \"givenName\": \"John\",\n // \"jobTitle\": \"Developer\",\n // \"mail\": \"john.doe@contoso.com\",\n // \"mobilePhone\": \"+1 412 555 0109\",\n // \"officeLocation\": \"Floor 2\",\n // \"preferredLanguage\": \"en-US\",\n // \"surname\": \"Doe\",\n // \"userPrincipalName\": \"john.doe@contoso.com\"\n // }\n const {\n id,\n businessPhones,\n displayName,\n givenName,\n surname,\n jobTitle,\n mail,\n mobilePhone,\n officeLocation,\n preferredLanguage,\n userPricipalName\n } = userInfo\n\n const repository = tx.getRepository(User)\n\n // 1. 사용자를 찾는다.(email 정보로)\n const existingUser = await repository.findOne({\n where: { email: mail },\n relations: ['domains']\n })\n\n if (!existingUser) {\n // 2. 사용자가 없다면, 생성한다.\n const salt = User.generateSalt()\n\n /* normally they don't login with this password. */\n const password = salt\n\n const createdUser = await repository.save({\n id,\n name: displayName,\n email: mail,\n creator: user,\n updater: user,\n domains: domain ? [domain] : [],\n roles: [],\n salt,\n userType: 'user',\n // ssoId: id,\n locale: preferredLanguage,\n status: UserStatus.ACTIVATED,\n passwordUpdatedAt: new Date(),\n password: User.encode(password, salt)\n })\n\n await tx.getRepository(UsersAuthProviders).save({\n domain,\n user: createdUser,\n authProvider,\n ssoId: id\n })\n\n return createdUser\n } else {\n // 3. 사용자가 있다면, 업데이트한다.\n const { domains } = existingUser\n\n if (!domains.find(existing => existing.id == domain.id)) {\n domains.push(domain)\n }\n\n const updatedUser = await repository.save({\n ...existingUser,\n name: displayName,\n // ssoId: id,\n domains,\n locale: preferredLanguage,\n updater: user\n })\n\n const usersAuthProviders = await tx.getRepository(UsersAuthProviders).findOne({\n where: { domain: { id: domain.id }, user: { id: updatedUser.id }, authProvider: { id: authProvider.id } }\n })\n\n await tx.getRepository(UsersAuthProviders).save({\n ...usersAuthProviders,\n domain,\n user: updatedUser,\n authProvider,\n ssoId: id\n })\n\n return updatedUser\n }\n}\n"]}
|
package/dist-server/index.js
CHANGED
|
@@ -4,4 +4,16 @@ const tslib_1 = require("tslib");
|
|
|
4
4
|
tslib_1.__exportStar(require("./middlewares"), exports);
|
|
5
5
|
tslib_1.__exportStar(require("./service"), exports);
|
|
6
6
|
require("./routes");
|
|
7
|
+
const auth_base_1 = require("@things-factory/auth-base");
|
|
8
|
+
const sync_user_info_1 = require("./controllers/sync-user-info");
|
|
9
|
+
auth_base_1.AuthProvider.register('azure', {
|
|
10
|
+
type: 'azure',
|
|
11
|
+
description: 'Authentication Provider for Azure Active Directory',
|
|
12
|
+
help: '',
|
|
13
|
+
parameterSpec: null,
|
|
14
|
+
async synchronizeUsers(authProvider, context) {
|
|
15
|
+
await (0, sync_user_info_1.syncAllUserInfo)(authProvider, context);
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
7
19
|
//# sourceMappingURL=index.js.map
|
package/dist-server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../server/index.ts"],"names":[],"mappings":";;;AAAA,wDAA6B;AAC7B,oDAAyB;AAEzB,oBAAiB","sourcesContent":["export * from './middlewares'\nexport * from './service'\n\nimport './routes'\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../server/index.ts"],"names":[],"mappings":";;;AAAA,wDAA6B;AAC7B,oDAAyB;AAEzB,oBAAiB;AAEjB,yDAAwD;AACxD,iEAA8D;AAE9D,wBAAY,CAAC,QAAQ,CAAC,OAAO,EAAE;IAC7B,IAAI,EAAE,OAAO;IACb,WAAW,EAAE,oDAAoD;IACjE,IAAI,EAAE,EAAE;IACR,aAAa,EAAE,IAAI;IACnB,KAAK,CAAC,gBAAgB,CAAC,YAA0B,EAAE,OAAwB;QACzE,MAAM,IAAA,gCAAe,EAAC,YAAY,EAAE,OAAO,CAAC,CAAA;QAC5C,OAAO,IAAI,CAAA;IACb,CAAC;CACF,CAAC,CAAA","sourcesContent":["export * from './middlewares'\nexport * from './service'\n\nimport './routes'\n\nimport { AuthProvider } from '@things-factory/auth-base'\nimport { syncAllUserInfo } from './controllers/sync-user-info'\n\nAuthProvider.register('azure', {\n type: 'azure',\n description: 'Authentication Provider for Azure Active Directory',\n help: '',\n parameterSpec: null,\n async synchronizeUsers(authProvider: AuthProvider, context: ResolverContext): Promise<boolean> {\n await syncAllUserInfo(authProvider, context)\n return true\n }\n})\n"]}
|
|
@@ -6,9 +6,9 @@ const koa_passport_1 = tslib_1.__importDefault(require("koa-passport"));
|
|
|
6
6
|
const passport_azure_ad_1 = require("passport-azure-ad");
|
|
7
7
|
const env_1 = require("@things-factory/env");
|
|
8
8
|
const auth_base_1 = require("@things-factory/auth-base");
|
|
9
|
-
const SSOAzureADConfig = env_1.config.get('
|
|
9
|
+
const SSOAzureADConfig = env_1.config.get('sso/azure/config');
|
|
10
10
|
if (SSOAzureADConfig) {
|
|
11
|
-
koa_passport_1.default.use(new passport_azure_ad_1.OIDCStrategy(Object.assign(Object.assign({ allowHttpForRedirectUrl: true }, SSOAzureADConfig), { passReqToCallback: true, isB2C: false, scope: ['openid', 'email', 'profile'] }), async (req, iss, sub, profile, accessToken, refreshToken, done) => {
|
|
11
|
+
koa_passport_1.default.use(new passport_azure_ad_1.OIDCStrategy(Object.assign(Object.assign({ allowHttpForRedirectUrl: true }, SSOAzureADConfig), { validateIssuer: false /* multi-tenant application 이므로 */, passReqToCallback: true, isB2C: false, scope: ['openid', 'email', 'profile'] }), async (req, iss, sub, profile, accessToken, refreshToken, done) => {
|
|
12
12
|
var _a;
|
|
13
13
|
if (!profile.oid || !((_a = profile._json) === null || _a === void 0 ? void 0 : _a.email)) {
|
|
14
14
|
return done(new Error('No oid or no email found'), null);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"azure-ad-authenticate-middleware.js","sourceRoot":"","sources":["../../server/middlewares/azure-ad-authenticate-middleware.ts"],"names":[],"mappings":";;;;AAAA,wEAAmC;AACnC,yDAAgD;AAEhD,6CAA4C;AAC5C,yDAAgD;AAEhD,MAAM,gBAAgB,GAAG,YAAM,CAAC,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"azure-ad-authenticate-middleware.js","sourceRoot":"","sources":["../../server/middlewares/azure-ad-authenticate-middleware.ts"],"names":[],"mappings":";;;;AAAA,wEAAmC;AACnC,yDAAgD;AAEhD,6CAA4C;AAC5C,yDAAgD;AAEhD,MAAM,gBAAgB,GAAG,YAAM,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAA;AAEvD,IAAI,gBAAgB,EAAE;IACpB,sBAAQ,CAAC,GAAG,CACV,IAAI,gCAAY,+BAEZ,uBAAuB,EAAE,IAAI,IAC1B,gBAAgB,KACnB,cAAc,EAAE,KAAK,CAAC,kCAAkC,EACxD,iBAAiB,EAAE,IAAI,EACvB,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,CAAC,KAEvC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE;;QAChE,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAA,MAAA,OAAO,CAAC,KAAK,0CAAE,KAAK,CAAA,EAAE;YACzC,OAAO,IAAI,CAAC,IAAI,KAAK,CAAC,0BAA0B,CAAC,EAAE,IAAI,CAAC,CAAA;SACzD;QAED,IAAI;YACF,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;YAE/B,MAAM,IAAI,GAAG,MAAM,gBAAI,CAAC,kBAAkB,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;YAErD,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;YAErC,OAAO,IAAI,CAAC,IAAI,EAAE;gBAChB,EAAE;gBACF,QAAQ;gBACR,MAAM;aACP,CAAC,CAAA;SACH;QAAC,OAAO,KAAK,EAAE;YACd,OAAO,IAAI,CAAC,KAAK,CAAC,CAAA;SACnB;IACH,CAAC,CACF,CACF,CAAA;CACF;AAEM,KAAK,UAAU,iBAAiB,CAAC,OAAO,EAAE,IAAI;IACnD,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAA;IACxB,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAE9B,IAAI,IAAI,EAAE;QACR,OAAO,MAAM,IAAI,EAAE,CAAA;KACpB;IAED,MAAM,sBAAQ,CAAC,YAAY,CAAC,uBAAuB,EAAE;QACnD,eAAe,EAAE,cAAc;QAC/B,eAAe,EAAE,eAAe;KACjC,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;AACnB,CAAC;AAZD,8CAYC;AAEM,KAAK,UAAU,6BAA6B,CAAC,OAAO,EAAE,IAAI;;IAC/D,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAA;IACxB,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAE9B,IAAI,IAAI,EAAE;QACR,OAAO,MAAM,IAAI,EAAE,CAAA;KACpB;IAED,MAAM,sBAAQ,CAAC,YAAY,CAAC,uBAAuB,EAAE;QACnD,eAAe,EAAE,cAAc;QAC/B,eAAe,EAAE,eAAe;KACjC,CAAC,CAAC,OAAO,CAAC,CAAA;IAEX,wCAAwC;IACxC,MAAM,cAAc,GAAG,MAAA,OAAO,CAAC,OAAO,0CAAE,QAAQ,CAAA;IAChD,IAAI,cAAc,EAAE;QAClB,MAAM,UAAU,GAAG,MAAM,gBAAI,CAAC,SAAS,CAAC,cAAc,aAAd,cAAc,uBAAd,cAAc,CAAE,IAAI,CAAC,CAAA;QAC7D,OAAO,CAAC,KAAK,CAAC,IAAI,GAAG,UAAU,CAAA;KAChC;IAED,OAAO,MAAM,IAAI,EAAE,CAAA;AACrB,CAAC;AArBD,sEAqBC","sourcesContent":["import passport from 'koa-passport'\nimport { OIDCStrategy } from 'passport-azure-ad'\n\nimport { config } from '@things-factory/env'\nimport { User } from '@things-factory/auth-base'\n\nconst SSOAzureADConfig = config.get('sso/azure/config')\n\nif (SSOAzureADConfig) {\n passport.use(\n new OIDCStrategy(\n {\n allowHttpForRedirectUrl: true,\n ...SSOAzureADConfig,\n validateIssuer: false /* multi-tenant application 이므로 */,\n passReqToCallback: true,\n isB2C: false,\n scope: ['openid', 'email', 'profile']\n },\n async (req, iss, sub, profile, accessToken, refreshToken, done) => {\n if (!profile.oid || !profile._json?.email) {\n return done(new Error('No oid or no email found'), null)\n }\n\n try {\n const { email } = profile._json\n\n const user = await User.checkAuthWithEmail({ email })\n\n const { id, userType, status } = user\n\n return done(null, {\n id,\n userType,\n status\n })\n } catch (error) {\n return done(error)\n }\n }\n )\n )\n}\n\nexport async function azureADMiddleware(context, next) {\n const { path } = context\n const { user } = context.state\n\n if (user) {\n return await next()\n }\n\n await passport.authenticate('azuread-openidconnect', {\n failureRedirect: '/auth/signin',\n successRedirect: '/auth/checkin'\n })(context, next)\n}\n\nexport async function azureADSubscriptionMiddleware(context, next) {\n const { path } = context\n const { user } = context.state\n\n if (user) {\n return await next()\n }\n\n await passport.authenticate('azuread-openidconnect', {\n failureRedirect: '/auth/signin',\n successRedirect: '/auth/checkin'\n })(context)\n\n /* 다음은, websocket 에서 인증을 처리하기 위한 작업임. */\n const passportObject = context.session?.passport\n if (passportObject) {\n const userEntity = await User.checkAuth(passportObject?.user)\n context.state.user = userEntity\n }\n\n return await next()\n}\n"]}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
const env_1 = require("@things-factory/env");
|
|
5
|
-
const SSOAzureADConfig = env_1.config.get('
|
|
5
|
+
const SSOAzureADConfig = env_1.config.get('sso/azure/config');
|
|
6
6
|
const azure_ad_authenticate_middleware_1 = require("./azure-ad-authenticate-middleware");
|
|
7
7
|
if (SSOAzureADConfig) {
|
|
8
8
|
process.on('bootstrap-module-subscription', (app, subscriptionMiddleware) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/middlewares/index.ts"],"names":[],"mappings":";;;AAAA,6CAA4C;AAE5C,MAAM,gBAAgB,GAAG,YAAM,CAAC,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/middlewares/index.ts"],"names":[],"mappings":";;;AAAA,6CAA4C;AAE5C,MAAM,gBAAgB,GAAG,YAAM,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAA;AAEvD,yFAAkF;AAElF,IAAI,gBAAgB,EAAE;IACpB,OAAO,CAAC,EAAE,CAAC,+BAAsC,EAAE,CAAC,GAAG,EAAE,sBAAsB,EAAE,EAAE;QACjF,sBAAsB,CAAC,OAAO,CAAC,gEAA6B,CAAC,CAAA;IAC/D,CAAC,CAAC,CAAA;CACH;AAED,6EAAkD","sourcesContent":["import { config } from '@things-factory/env'\n\nconst SSOAzureADConfig = config.get('sso/azure/config')\n\nimport { azureADSubscriptionMiddleware } from './azure-ad-authenticate-middleware'\n\nif (SSOAzureADConfig) {\n process.on('bootstrap-module-subscription' as any, (app, subscriptionMiddleware) => {\n subscriptionMiddleware.unshift(azureADSubscriptionMiddleware)\n })\n}\n\nexport * from './azure-ad-authenticate-middleware'\n"]}
|
|
@@ -7,56 +7,67 @@ const node_fetch_1 = tslib_1.__importDefault(require("node-fetch"));
|
|
|
7
7
|
const shell_1 = require("@things-factory/shell");
|
|
8
8
|
const sync_user_info_1 = require("../controllers/sync-user-info");
|
|
9
9
|
const get_access_token_1 = require("../controllers/get-access-token");
|
|
10
|
-
const
|
|
10
|
+
const auth_base_1 = require("@things-factory/auth-base");
|
|
11
11
|
exports.authAzureADWebhookRouter = new koa_router_1.default();
|
|
12
12
|
exports.authAzureADWebhookRouter.post('/webhook/azure-ad-users', async (context) => {
|
|
13
|
-
const accessToken = await (0, get_access_token_1.getAccessToken)();
|
|
14
13
|
const notifications = context.request.body.value;
|
|
15
14
|
const clientState = context.request.body.clientState;
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
const [domainId, authProviderId] = (clientState === null || clientState === void 0 ? void 0 : clientState.split(':')) || [];
|
|
16
|
+
if (!domainId || !authProviderId) {
|
|
18
17
|
context.status = 401; // unauthorized
|
|
19
|
-
context.body = 'Invalid
|
|
18
|
+
context.body = 'Invalid ClientState';
|
|
20
19
|
return;
|
|
21
20
|
}
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (!userResponse.ok) {
|
|
31
|
-
throw new Error(`Failed to fetch user data: ${userResponse.statusText}`);
|
|
21
|
+
await (0, shell_1.getDataSource)('tx').transaction(async (tx) => {
|
|
22
|
+
const wrap = context.app.createContext(context.req || {}, context.res || {});
|
|
23
|
+
wrap.state = Object.assign(Object.assign({}, context.state), { domain: null, tx });
|
|
24
|
+
wrap.t = context.t;
|
|
25
|
+
const authProvider = await tx.getRepository(auth_base_1.AuthProvider).findOne({
|
|
26
|
+
where: {
|
|
27
|
+
domain: { id: domainId },
|
|
28
|
+
id: authProviderId
|
|
32
29
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
// "givenName": "Megan",
|
|
39
|
-
// "jobTitle": "Auditor",
|
|
40
|
-
// "mail": "MeganB@M365x214355.onmicrosoft.com",
|
|
41
|
-
// "mobilePhone": "+1 412 555 0109",
|
|
42
|
-
// "officeLocation": "12/1110",
|
|
43
|
-
// "preferredLanguage": "en-US",
|
|
44
|
-
// "surname": "Bowen",
|
|
45
|
-
// "userPrincipalName": "MeganB@M365x214355.onmicrosoft.com",
|
|
46
|
-
// "id": "48d31887-5fad-4d73-a9f5-3c356e68a038"
|
|
47
|
-
// }
|
|
48
|
-
// webhook 호출로부터 호출한 tenant를 알 수 있다면, 매핑된 도메인 정보를 포함할 수 있을 것 같은데..
|
|
49
|
-
return await (0, shell_1.getDataSource)('tx').transaction(async (tx) => {
|
|
50
|
-
const wrap = context.app.createContext(context.req || {}, context.res || {});
|
|
51
|
-
wrap.state = Object.assign(Object.assign({}, context.state), { domain: null, tx });
|
|
52
|
-
wrap.t = context.t;
|
|
53
|
-
return await (0, sync_user_info_1.updateUserInfo)(userInfo, wrap);
|
|
54
|
-
});
|
|
30
|
+
});
|
|
31
|
+
if (!authProvider) {
|
|
32
|
+
context.status = 401; // unauthorized
|
|
33
|
+
context.body = 'Invalid ClientState';
|
|
34
|
+
return;
|
|
55
35
|
}
|
|
56
|
-
|
|
57
|
-
|
|
36
|
+
const accessToken = await (0, get_access_token_1.getAccessToken)(authProvider);
|
|
37
|
+
for (const notification of notifications) {
|
|
38
|
+
const userId = notification.resourceData.id;
|
|
39
|
+
try {
|
|
40
|
+
const userResponse = await (0, node_fetch_1.default)(`https://graph.microsoft.com/v1.0/users/${userId}`, {
|
|
41
|
+
headers: {
|
|
42
|
+
Authorization: `Bearer ${accessToken}`
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
if (!userResponse.ok) {
|
|
46
|
+
throw new Error(`Failed to fetch user data: ${userResponse.statusText}`);
|
|
47
|
+
}
|
|
48
|
+
const userInfo = await userResponse.json();
|
|
49
|
+
// {
|
|
50
|
+
// "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users/$entity",
|
|
51
|
+
// "businessPhones": ["+1 412 555 0109"],
|
|
52
|
+
// "displayName": "Megan Bowen",
|
|
53
|
+
// "givenName": "Megan",
|
|
54
|
+
// "jobTitle": "Auditor",
|
|
55
|
+
// "mail": "MeganB@M365x214355.onmicrosoft.com",
|
|
56
|
+
// "mobilePhone": "+1 412 555 0109",
|
|
57
|
+
// "officeLocation": "12/1110",
|
|
58
|
+
// "preferredLanguage": "en-US",
|
|
59
|
+
// "surname": "Bowen",
|
|
60
|
+
// "userPrincipalName": "MeganB@M365x214355.onmicrosoft.com",
|
|
61
|
+
// "id": "48d31887-5fad-4d73-a9f5-3c356e68a038"
|
|
62
|
+
// }
|
|
63
|
+
// webhook 호출로부터 호출한 tenant를 알 수 있다면, 매핑된 도메인 정보를 포함할 수 있을 것 같은데..
|
|
64
|
+
await (0, sync_user_info_1.updateUserInfo)(authProvider, userInfo, wrap);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
console.error('Error fetching user data from Microsoft Graph:', error);
|
|
68
|
+
}
|
|
58
69
|
}
|
|
59
|
-
}
|
|
70
|
+
});
|
|
60
71
|
context.status = 202;
|
|
61
72
|
});
|
|
62
73
|
//# sourceMappingURL=auth-azure-ad-webhook-router.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-azure-ad-webhook-router.js","sourceRoot":"","sources":["../../server/router/auth-azure-ad-webhook-router.ts"],"names":[],"mappings":";;;;AAAA,oEAA+B;AAC/B,oEAA8B;AAE9B,
|
|
1
|
+
{"version":3,"file":"auth-azure-ad-webhook-router.js","sourceRoot":"","sources":["../../server/router/auth-azure-ad-webhook-router.ts"],"names":[],"mappings":"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","sourcesContent":["import Router from 'koa-router'\nimport fetch from 'node-fetch'\n\nimport { getRepository, getDataSource } from '@things-factory/shell'\n\nimport { updateUserInfo } from '../controllers/sync-user-info'\nimport { getAccessToken } from '../controllers/get-access-token'\nimport { AuthProvider } from '@things-factory/auth-base'\n\nexport const authAzureADWebhookRouter = new Router()\n\n// Microsoft Graph에서 발송되는 알림을 처리하기 위한 타입 정의\ninterface Notification {\n resourceData: {\n id: string\n }\n}\n\nauthAzureADWebhookRouter.post('/webhook/azure-ad-users', async context => {\n const notifications: Notification[] = context.request.body.value\n const clientState = context.request.body.clientState\n const [domainId, authProviderId] = clientState?.split(':') || []\n\n if (!domainId || !authProviderId) {\n context.status = 401 // unauthorized\n context.body = 'Invalid ClientState'\n return\n }\n\n await getDataSource('tx').transaction(async tx => {\n const wrap = context.app.createContext(context.req || {}, context.res || {})\n\n wrap.state = {\n ...context.state,\n domain: null,\n tx\n }\n wrap.t = context.t\n\n const authProvider = await tx.getRepository(AuthProvider).findOne({\n where: {\n domain: { id: domainId },\n id: authProviderId\n }\n })\n\n if (!authProvider) {\n context.status = 401 // unauthorized\n context.body = 'Invalid ClientState'\n return\n }\n\n const accessToken = await getAccessToken(authProvider)\n\n for (const notification of notifications) {\n const userId = notification.resourceData.id\n\n try {\n const userResponse = await fetch(`https://graph.microsoft.com/v1.0/users/${userId}`, {\n headers: {\n Authorization: `Bearer ${accessToken}`\n }\n })\n\n if (!userResponse.ok) {\n throw new Error(`Failed to fetch user data: ${userResponse.statusText}`)\n }\n\n const userInfo = await userResponse.json()\n\n // {\n // \"@odata.context\": \"https://graph.microsoft.com/v1.0/$metadata#users/$entity\",\n // \"businessPhones\": [\"+1 412 555 0109\"],\n // \"displayName\": \"Megan Bowen\",\n // \"givenName\": \"Megan\",\n // \"jobTitle\": \"Auditor\",\n // \"mail\": \"MeganB@M365x214355.onmicrosoft.com\",\n // \"mobilePhone\": \"+1 412 555 0109\",\n // \"officeLocation\": \"12/1110\",\n // \"preferredLanguage\": \"en-US\",\n // \"surname\": \"Bowen\",\n // \"userPrincipalName\": \"MeganB@M365x214355.onmicrosoft.com\",\n // \"id\": \"48d31887-5fad-4d73-a9f5-3c356e68a038\"\n // }\n\n // webhook 호출로부터 호출한 tenant를 알 수 있다면, 매핑된 도메인 정보를 포함할 수 있을 것 같은데..\n\n await updateUserInfo(authProvider, userInfo, wrap)\n } catch (error) {\n console.error('Error fetching user data from Microsoft Graph:', error)\n }\n }\n })\n\n context.status = 202\n})\n"]}
|
package/dist-server/routes.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const env_1 = require("@things-factory/env");
|
|
4
|
-
const
|
|
4
|
+
const auth_base_1 = require("@things-factory/auth-base");
|
|
5
|
+
const SSOAzureADConfig = env_1.config.get('sso/azure/config');
|
|
5
6
|
const middlewares_1 = require("./middlewares");
|
|
6
7
|
const router_1 = require("./router");
|
|
7
8
|
if (SSOAzureADConfig) {
|
|
@@ -12,5 +13,13 @@ if (SSOAzureADConfig) {
|
|
|
12
13
|
domainPublicRouter.use(router_1.authAzureADRouter.routes(), router_1.authAzureADRouter.allowedMethods());
|
|
13
14
|
domainPublicRouter.use(router_1.authAzureADWebhookRouter.routes(), router_1.authAzureADWebhookRouter.allowedMethods());
|
|
14
15
|
});
|
|
16
|
+
process.on('bootstrap-module-global-public-route', (app, globalPublicRouter) => {
|
|
17
|
+
globalPublicRouter.get('/auth/signin-with-azure', middlewares_1.azureADMiddleware, async (context) => {
|
|
18
|
+
const { user } = context.state;
|
|
19
|
+
const token = user.sign();
|
|
20
|
+
(0, auth_base_1.setAccessTokenCookie)(context, token);
|
|
21
|
+
context.redirect('/auth/checkin');
|
|
22
|
+
});
|
|
23
|
+
});
|
|
15
24
|
}
|
|
16
25
|
//# sourceMappingURL=routes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../server/routes.ts"],"names":[],"mappings":";;AAAA,6CAA4C;
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../server/routes.ts"],"names":[],"mappings":";;AAAA,6CAA4C;AAC5C,yDAAgE;AAEhE,MAAM,gBAAgB,GAAG,YAAM,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAA;AAEvD,+CAAiD;AACjD,qCAAsE;AAEtE,IAAI,gBAAgB,EAAE;IACpB,OAAO,CAAC,EAAE,CAAC,kCAAyC,EAAE,CAAC,CAAC,EAAE,cAAc,EAAE,EAAE;QAC1E,cAAc,CAAC,IAAI,CAAC,+BAAiB,CAAC,CAAA;IACxC,CAAC,CAAC,CAAA;IAEF,OAAO,CAAC,EAAE,CAAC,sCAA6C,EAAE,CAAC,GAAG,EAAE,kBAAkB,EAAE,EAAE;QACpF,kBAAkB,CAAC,GAAG,CAAC,0BAAiB,CAAC,MAAM,EAAE,EAAE,0BAAiB,CAAC,cAAc,EAAE,CAAC,CAAA;QACtF,kBAAkB,CAAC,GAAG,CAAC,iCAAwB,CAAC,MAAM,EAAE,EAAE,iCAAwB,CAAC,cAAc,EAAE,CAAC,CAAA;IACtG,CAAC,CAAC,CAAA;IAEF,OAAO,CAAC,EAAE,CAAC,sCAA6C,EAAE,CAAC,GAAG,EAAE,kBAAkB,EAAE,EAAE;QACpF,kBAAkB,CAAC,GAAG,CAAC,yBAAyB,EAAE,+BAAiB,EAAE,KAAK,EAAC,OAAO,EAAC,EAAE;YACnF,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;YAE9B,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;YACzB,IAAA,gCAAoB,EAAC,OAAO,EAAE,KAAK,CAAC,CAAA;YAEpC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAA;QACnC,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;CACH","sourcesContent":["import { config } from '@things-factory/env'\nimport { setAccessTokenCookie } from '@things-factory/auth-base'\n\nconst SSOAzureADConfig = config.get('sso/azure/config')\n\nimport { azureADMiddleware } from './middlewares'\nimport { authAzureADRouter, authAzureADWebhookRouter } from './router'\n\nif (SSOAzureADConfig) {\n process.on('bootstrap-collect-sso-middleware' as any, (_, ssoMiddlewares) => {\n ssoMiddlewares.push(azureADMiddleware)\n })\n\n process.on('bootstrap-module-domain-public-route' as any, (app, domainPublicRouter) => {\n domainPublicRouter.use(authAzureADRouter.routes(), authAzureADRouter.allowedMethods())\n domainPublicRouter.use(authAzureADWebhookRouter.routes(), authAzureADWebhookRouter.allowedMethods())\n })\n\n process.on('bootstrap-module-global-public-route' as any, (app, globalPublicRouter) => {\n globalPublicRouter.get('/auth/signin-with-azure', azureADMiddleware, async context => {\n const { user } = context.state\n\n const token = user.sign()\n setAccessTokenCookie(context, token)\n\n context.redirect('/auth/checkin')\n })\n })\n}\n"]}
|
|
@@ -3,26 +3,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.AuthAzureAdMutation = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const type_graphql_1 = require("type-graphql");
|
|
6
|
-
const sync_user_info_1 = require("../../controllers/sync-user-info");
|
|
7
6
|
let AuthAzureAdMutation = class AuthAzureAdMutation {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
// @Directive('@transaction')
|
|
8
|
+
// @Directive('@privilege(superUserGranted:true)')
|
|
9
|
+
// @Mutation(returns => Boolean, { description: 'To synchronize azure active directory users' })
|
|
10
|
+
// async synchronizeAzureADUsers(@Ctx() context: ResolverContext): Promise<boolean> {
|
|
11
|
+
// syncAllUserInfo(context)
|
|
12
|
+
// return true
|
|
13
|
+
// }
|
|
12
14
|
async subscribeAzureADUsers(context) {
|
|
13
15
|
const { domain, user, tx } = context.state;
|
|
14
16
|
return true;
|
|
15
17
|
}
|
|
16
18
|
};
|
|
17
|
-
tslib_1.__decorate([
|
|
18
|
-
(0, type_graphql_1.Directive)('@transaction'),
|
|
19
|
-
(0, type_graphql_1.Directive)('@privilege(superUserGranted:true)'),
|
|
20
|
-
(0, type_graphql_1.Mutation)(returns => Boolean, { description: 'To synchronize azure active directory users' }),
|
|
21
|
-
tslib_1.__param(0, (0, type_graphql_1.Ctx)()),
|
|
22
|
-
tslib_1.__metadata("design:type", Function),
|
|
23
|
-
tslib_1.__metadata("design:paramtypes", [Object]),
|
|
24
|
-
tslib_1.__metadata("design:returntype", Promise)
|
|
25
|
-
], AuthAzureAdMutation.prototype, "synchronizeAzureADUsers", null);
|
|
26
19
|
tslib_1.__decorate([
|
|
27
20
|
(0, type_graphql_1.Directive)('@transaction'),
|
|
28
21
|
(0, type_graphql_1.Directive)('@privilege(superUserGranted:true)'),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-azure-ad-mutation.js","sourceRoot":"","sources":["../../../server/service/auth-azure-ad/auth-azure-ad-mutation.ts"],"names":[],"mappings":";;;;AAAA,+CAAsE;
|
|
1
|
+
{"version":3,"file":"auth-azure-ad-mutation.js","sourceRoot":"","sources":["../../../server/service/auth-azure-ad/auth-azure-ad-mutation.ts"],"names":[],"mappings":";;;;AAAA,+CAAsE;AAK/D,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IAC9B,6BAA6B;IAC7B,kDAAkD;IAClD,gGAAgG;IAChG,qFAAqF;IACrF,6BAA6B;IAE7B,gBAAgB;IAChB,IAAI;IAKE,AAAN,KAAK,CAAC,qBAAqB,CAAQ,OAAwB;QACzD,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAE1C,OAAO,IAAI,CAAA;IACb,CAAC;CACF,CAAA;AALO;IAHL,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,wBAAS,EAAC,mCAAmC,CAAC;IAC9C,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,2CAA2C,EAAE,CAAC;IAC9D,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;gEAIjC;AAjBU,mBAAmB;IAD/B,IAAA,uBAAQ,GAAE;GACE,mBAAmB,CAkB/B;AAlBY,kDAAmB","sourcesContent":["import { Resolver, Mutation, Arg, Ctx, Directive } from 'type-graphql'\n\nimport { syncAllUserInfo } from '../../controllers/sync-user-info'\n\n@Resolver()\nexport class AuthAzureAdMutation {\n // @Directive('@transaction')\n // @Directive('@privilege(superUserGranted:true)')\n // @Mutation(returns => Boolean, { description: 'To synchronize azure active directory users' })\n // async synchronizeAzureADUsers(@Ctx() context: ResolverContext): Promise<boolean> {\n // syncAllUserInfo(context)\n\n // return true\n // }\n\n @Directive('@transaction')\n @Directive('@privilege(superUserGranted:true)')\n @Mutation(returns => Boolean, { description: 'To subscribe azure active directory users' })\n async subscribeAzureADUsers(@Ctx() context: ResolverContext): Promise<boolean> {\n const { domain, user, tx } = context.state\n\n return true\n }\n}\n"]}
|