Merged PR 58: API実装(アカウント登録/Azure AD B2C)
## 概要 [Task1550: API実装(アカウント登録/Azure AD B2C)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/1550) - アカウント登録APIでAzure ADB2Cにユーザを登録する処理を追加しました。 - GraphAPIで登録処理をしています。 ## レビューポイント - ADB2Cへ登録する情報は認識通りか - ADB2CへのGraphAPI接続のためにADB2Cテナントにアプリを追加しているが認証として問題ないか。 - 環境変数にアプリの情報を設定しています - ADB2C_TENANT_ID=xxxxxxxx - ADB2C_CLIENT_ID=xxxxxxxx - ADB2C_CLIENT_SECRET=xxxxxxxx ## UIの変更 無し ## 動作確認状況 - ローカルで確認 - ADB2C、DBに設定項目が追加されていることを確認
This commit is contained in:
parent
731c633189
commit
0099614a5f
@ -4,6 +4,10 @@ PORT=8081
|
||||
AZURE_TENANT_ID=xxxxxxxx
|
||||
AZURE_CLIENT_ID=xxxxxxxx
|
||||
AZURE_CLIENT_SECRET=xxxxxxxx
|
||||
# 開発環境ではADB2Cが別テナントになる都合上、環境変数を分けている
|
||||
ADB2C_TENANT_ID=xxxxxxxx
|
||||
ADB2C_CLIENT_ID=xxxxxxxx
|
||||
ADB2C_CLIENT_SECRET=xxxxxxxx
|
||||
KEY_VAULT_NAME=kv-odms-secret-dev
|
||||
JWT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA5IZZNgDew9eGmuFTezwdHYLSaJvUPPIKYoiOeVLD1paWNI51\n7Vkaoh0ngprcKOdv6T1N07V4igK7mOim2zY3yCTR6wcWR3PfFJrl9vh5SOo79koZ\noJb27YiM4jtxfx2dezzp0T2GoNR5rRolPUbWFJXnDe0DVXYXpJLb4LAlF2XAyYX0\nSYKUVUsJnzm5k4xbXtnwPwVbpm0EdswBE6qSfiL9zWk9dvHoKzSnfSDzDFoFcEoV\nchawzYXf/MM1YR4wo5XyzECc6Q5Ah4z522//mBNNaDHv83Yuw3mGShT73iJ0JQdk\nTturshv2Ecma38r6ftrIwNYXw4VVatJM8+GOOQIDAQABAoIBADrwp7u097+dK/tw\nWD61n3DIGAqg/lmFt8X4IH8MKLSE/FKr16CS1bqwOEuIM3ZdUtDeXd9Xs7IsyEPE\n5ZwuXK7DSF0M4+Mj8Ip49Q0Aww9aUoLQU9HGfgN/r4599GTrt31clZXA/6Mlighq\ncOZgCcEfdItz8OMu5SQuOIW4CKkCuaWnPOP26UqZocaXNZfpZH0iFLATMMH/TT8x\nay9ToHTQYE17ijdQ/EOLSwoeDV1CU1CIE3P4YfLJjvpKptly5dTevriHEzBi70Jx\n/KEPUn9Jj2gZafrUxRVhmMbm1zkeYxL3gsqRuTzRjEeeILuZhSJyCkQZyUNARxsg\nQY4DZfECgYEA+YLKUtmYTx60FS6DJ4s31TAsXY8kwhq/lB9E3GBZKDd0DPayXEeK\n4UWRQDTT6MI6fedW69FOZJ5sFLp8HQpcssb4Weq9PCpDhNTx8MCbdH3Um5QR3vfW\naKq/1XM8MDUnx5XcNYd87Aw3azvJAvOPr69as8IPnj6sKaRR9uQjbYUCgYEA6nfV\n5j0qmn0EJXZJblk4mvvjLLoWSs17j9YlrZJlJxXMDFRYtgnelv73xMxOMvcGoxn5\nifs7dpaM2x5EmA6jVU5sYaB/beZGEPWqPYGyjIwXPvUGAAv8Gbnvpp+xlSco/Dum\nIq0w+43ry5/xWh6CjfrvKV0J2bDOiJwPEdu/8iUCgYEAnBBSvL+dpN9vhFAzeOh7\nY71eAqcmNsLEUcG9MJqTKbSFwhYMOewF0iHRWHeylEPokhfBJn8kqYrtz4lVWFTC\n5o/Nh3BsLNXCpbMMIapXkeWiti1HgE9ErPMgSkJpwz18RDpYIqM8X+jEQS6D7HSr\nyxfDg+w+GJza0rEVE3hfMIECgYBw+KZ2VfhmEWBjEHhXE+QjQMR3s320MwebCUqE\nNCpKx8TWF/naVC0MwfLtvqbbBY0MHyLN6d//xpA9r3rLbRojqzKrY2KiuDYAS+3n\nzssRzxoQOozWju+8EYu30/ADdqfXyIHG6X3VZs87AGiQzGyJLmP3oR1y5y7MQa09\nJI16hQKBgHK5uwJhGa281Oo5/FwQ3uYLymbNwSGrsOJXiEu2XwJEXwVi2ELOKh4/\n03pBk3Kva3fIwEK+vCzDNnxShIQqBE76/2I1K1whOfoUehhYvKHGaXl2j70Zz9Ks\nrkGW1cx7p+yDqATDrwHBHTHFh5bUTTn8dN40n0e0W/llurpbBkJM\n-----END RSA PRIVATE KEY-----\n"
|
||||
JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5IZZNgDew9eGmuFTezwd\nHYLSaJvUPPIKYoiOeVLD1paWNI517Vkaoh0ngprcKOdv6T1N07V4igK7mOim2zY3\nyCTR6wcWR3PfFJrl9vh5SOo79koZoJb27YiM4jtxfx2dezzp0T2GoNR5rRolPUbW\nFJXnDe0DVXYXpJLb4LAlF2XAyYX0SYKUVUsJnzm5k4xbXtnwPwVbpm0EdswBE6qS\nfiL9zWk9dvHoKzSnfSDzDFoFcEoVchawzYXf/MM1YR4wo5XyzECc6Q5Ah4z522//\nmBNNaDHv83Yuw3mGShT73iJ0JQdkTturshv2Ecma38r6ftrIwNYXw4VVatJM8+GO\nOQIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||
|
||||
65
dictation_server/package-lock.json
generated
65
dictation_server/package-lock.json
generated
@ -11,6 +11,7 @@
|
||||
"dependencies": {
|
||||
"@azure/identity": "^3.1.3",
|
||||
"@azure/keyvault-secrets": "^4.6.0",
|
||||
"@microsoft/microsoft-graph-client": "^3.0.5",
|
||||
"@nestjs/axios": "^0.1.0",
|
||||
"@nestjs/common": "^8.0.0",
|
||||
"@nestjs/config": "^2.2.0",
|
||||
@ -1031,6 +1032,17 @@
|
||||
"@babel/core": "^7.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz",
|
||||
"integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.13.11"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.20.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
|
||||
@ -1731,6 +1743,32 @@
|
||||
"integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@microsoft/microsoft-graph-client": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/microsoft-graph-client/-/microsoft-graph-client-3.0.5.tgz",
|
||||
"integrity": "sha512-xQADFNLUhE78RzYadFZtOmy/5wBZenSZhVK193m40MTDC5hl1aYMQO1QOJApnKga8WcvMCDCU10taRhuXTOz5w==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@azure/identity": {
|
||||
"optional": true
|
||||
},
|
||||
"@azure/msal-browser": {
|
||||
"optional": true
|
||||
},
|
||||
"buffer": {
|
||||
"optional": true
|
||||
},
|
||||
"stream-browserify": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/axios": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-0.1.1.tgz",
|
||||
@ -8987,6 +9025,11 @@
|
||||
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
|
||||
"integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg=="
|
||||
},
|
||||
"node_modules/regenerator-runtime": {
|
||||
"version": "0.13.11",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
|
||||
},
|
||||
"node_modules/require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
@ -11810,6 +11853,14 @@
|
||||
"@babel/helper-plugin-utils": "^7.19.0"
|
||||
}
|
||||
},
|
||||
"@babel/runtime": {
|
||||
"version": "7.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz",
|
||||
"integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.11"
|
||||
}
|
||||
},
|
||||
"@babel/template": {
|
||||
"version": "7.20.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
|
||||
@ -12366,6 +12417,15 @@
|
||||
"integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
|
||||
"dev": true
|
||||
},
|
||||
"@microsoft/microsoft-graph-client": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/microsoft-graph-client/-/microsoft-graph-client-3.0.5.tgz",
|
||||
"integrity": "sha512-xQADFNLUhE78RzYadFZtOmy/5wBZenSZhVK193m40MTDC5hl1aYMQO1QOJApnKga8WcvMCDCU10taRhuXTOz5w==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"tslib": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"@nestjs/axios": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-0.1.1.tgz",
|
||||
@ -17907,6 +17967,11 @@
|
||||
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
|
||||
"integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg=="
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.11",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
|
||||
},
|
||||
"require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
"dependencies": {
|
||||
"@azure/identity": "^3.1.3",
|
||||
"@azure/keyvault-secrets": "^4.6.0",
|
||||
"@microsoft/microsoft-graph-client": "^3.0.5",
|
||||
"@nestjs/axios": "^0.1.0",
|
||||
"@nestjs/common": "^8.0.0",
|
||||
"@nestjs/config": "^2.2.0",
|
||||
|
||||
@ -10,6 +10,7 @@ E01XXXX : 業務エラー
|
||||
EXX00XX : 内部エラー(内部プログラムのエラー)
|
||||
EXX01XX : トークンエラー(トークン認証関連)
|
||||
EXX02XX : DBエラー(DB関連)
|
||||
EXX03XX : ADB2Cエラー(DB関連)
|
||||
*/
|
||||
export const ErrorCodes = [
|
||||
'E009999', // 汎用エラー
|
||||
@ -20,4 +21,5 @@ export const ErrorCodes = [
|
||||
'E000105', // トークン発行元エラー
|
||||
'E000106', // トークンアルゴリズムエラー
|
||||
'E010201', // 未認証ユーザエラー
|
||||
'E010301', // メールアドレス登録済みエラー
|
||||
] as const;
|
||||
|
||||
@ -10,4 +10,5 @@ export const errors: Errors = {
|
||||
E000105: 'Token invalid issuer Error.',
|
||||
E000106: 'Token invalid algorithm Error.',
|
||||
E010201: 'Email not verified user Error.',
|
||||
E010301: 'This email user already created Error',
|
||||
};
|
||||
|
||||
@ -40,7 +40,7 @@ export class AccountsController {
|
||||
} = body;
|
||||
const role = 'none';
|
||||
|
||||
const { accountId, userId } = await this.accountService.createAccount(
|
||||
await this.accountService.createAccount(
|
||||
companyName,
|
||||
country,
|
||||
dealerAccountId,
|
||||
|
||||
@ -3,7 +3,11 @@ import { ConfigService } from '@nestjs/config';
|
||||
import { SendGridService } from '../../gateways/sendgrid/sendgrid.service';
|
||||
import { UsersRepositoryService } from '../../repositories/users/users.repository.service';
|
||||
import { AccountsRepositoryService } from '../../repositories/accounts/accounts.repository.service';
|
||||
import { AdB2cService } from '../../gateways/adb2c/adb2c.service';
|
||||
import {
|
||||
AdB2cService,
|
||||
ConflictError,
|
||||
isConflictError,
|
||||
} from '../../gateways/adb2c/adb2c.service';
|
||||
import { Account } from '../../repositories/accounts/entity/account.entity';
|
||||
import { User } from '../../repositories/users/entity/user.entity';
|
||||
import { TIER_5 } from '../../constants';
|
||||
@ -36,7 +40,7 @@ export class AccountsService {
|
||||
role: string,
|
||||
acceptedTermsVersion: string,
|
||||
): Promise<{ accountId: number; userId: number; externalUserId: string }> {
|
||||
let externalUser: { sub: string };
|
||||
let externalUser: { sub: string } | ConflictError;
|
||||
try {
|
||||
// idpにユーザーを作成
|
||||
externalUser = await this.adB2cService.createUser(
|
||||
@ -47,12 +51,21 @@ export class AccountsService {
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
console.log('create externalUser failed');
|
||||
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
// メールアドレス重複エラー
|
||||
if (isConflictError(externalUser)) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E010301'),
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
let account: Account;
|
||||
let user: User;
|
||||
try {
|
||||
|
||||
@ -2,25 +2,93 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import axios from 'axios';
|
||||
import { JwkSignKey, B2cMetadata } from '../../common/token';
|
||||
import { Client } from '@microsoft/microsoft-graph-client';
|
||||
import { TokenCredentialAuthenticationProvider } from '@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials';
|
||||
import { ClientSecretCredential } from '@azure/identity';
|
||||
|
||||
export type ConflictError = {
|
||||
reason: 'email';
|
||||
message: string;
|
||||
};
|
||||
|
||||
export const isConflictError = (arg: unknown): arg is ConflictError => {
|
||||
const value = arg as ConflictError;
|
||||
if (value.message === undefined) {
|
||||
return false;
|
||||
}
|
||||
if (value.reason === 'email') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class AdB2cService {
|
||||
constructor(private readonly configService: ConfigService) {}
|
||||
private readonly logger = new Logger(AdB2cService.name);
|
||||
private readonly tenantName = this.configService.get<string>('TENANT_NAME');
|
||||
private readonly flowName =
|
||||
this.configService.get<string>('SIGNIN_FLOW_NAME');
|
||||
private graphClient: Client;
|
||||
|
||||
constructor(private readonly configService: ConfigService) {
|
||||
// ADB2Cへの認証情報
|
||||
const credential = new ClientSecretCredential(
|
||||
this.configService.get('ADB2C_TENANT_ID'),
|
||||
this.configService.get('ADB2C_CLIENT_ID'),
|
||||
this.configService.get('ADB2C_CLIENT_SECRET'),
|
||||
);
|
||||
const authProvider = new TokenCredentialAuthenticationProvider(credential, {
|
||||
scopes: ['https://graph.microsoft.com/.default'],
|
||||
});
|
||||
this.graphClient = Client.initWithMiddleware({ authProvider });
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates user AzureADB2Cにユーザーを追加する
|
||||
* @param email 管理ユーザーのメールアドレス
|
||||
* @param password 管理ユーザーのパスワード
|
||||
* @param username 管理ユーザーの名前
|
||||
* @returns user
|
||||
*/
|
||||
async createUser(
|
||||
email: string,
|
||||
password: string,
|
||||
username: string,
|
||||
): Promise<{ sub: string }> {
|
||||
// XXX GraphAPIの呼び出しを行う
|
||||
console.log(email);
|
||||
console.log(password);
|
||||
console.log(username);
|
||||
return { sub: 'xxxxxxxx' };
|
||||
): Promise<{ sub: string } | ConflictError> {
|
||||
this.logger.log(`[IN] ${this.createUser.name}`);
|
||||
try {
|
||||
// ユーザをADB2Cに登録
|
||||
const newUser = await this.graphClient.api('users/').post({
|
||||
accountEnabled: true,
|
||||
displayName: username,
|
||||
passwordProfile: {
|
||||
forceChangePasswordNextSignIn: false,
|
||||
password: password,
|
||||
},
|
||||
identities: [
|
||||
{
|
||||
signinType: 'emailAddress',
|
||||
issuer: `${this.tenantName}.onmicrosoft.com`,
|
||||
issuerAssignedId: email,
|
||||
},
|
||||
],
|
||||
});
|
||||
return { sub: newUser.id };
|
||||
} catch (e) {
|
||||
this.logger.error(e);
|
||||
if (e?.statusCode === 400 && e?.body) {
|
||||
const error = JSON.parse(e.body);
|
||||
|
||||
// エラーが競合エラーである場合は、メールアドレス重複としてエラーを返す
|
||||
if (error?.details?.find((x) => x.code === 'ObjectConflict')) {
|
||||
return { reason: 'email', message: 'ObjectConflict' };
|
||||
}
|
||||
}
|
||||
|
||||
throw e;
|
||||
} finally {
|
||||
this.logger.log(`[OUT] ${this.createUser.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user