UNPKG

@point3/logto-module

Version:

포인트3 내부 logto Authentication 모듈입니다

215 lines 9.77 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.LogtoM2MClient = exports.LogtoM2MClientToken = void 0; exports.generatePhoneNumberWithCountryCode = generatePhoneNumberWithCountryCode; const common_1 = require("@nestjs/common"); const config_1 = require("./config"); const token_1 = require("../token"); const types_1 = require("./types"); const point3_common_tool_1 = require("point3-common-tool"); const errors_1 = require("../errors"); exports.LogtoM2MClientToken = Symbol.for("LogtoM2MClient"); let LogtoM2MClient = class LogtoM2MClient { constructor(config, tokenVerifier, logger) { this.config = config; this.tokenVerifier = tokenVerifier; this.logger = logger; this.logtoConfig = { endpoint: config.endpoint, appId: config.clientId, appSecret: config.clientSecret, scopes: config.scopes, resources: [config.resource], grantType: config_1.GrantType.ClientCredentials, }; this.authRestTemplate = new types_1.LogtoOAuthRESTTemplate(this.logger, this.logtoConfig.endpoint); this.authRestTemplate.setBasic(this.logtoConfig.appId, this.logtoConfig.appSecret); this.apiRestTemplate = new types_1.LogtoOAuthRESTTemplate(this.logger, config.apiUrl); } async fetchAccessToken() { const params = new URLSearchParams(); params.set('grant_type', this.logtoConfig.grantType); params.set('scope', this.logtoConfig.scopes.join(' ')); params.set('resource', this.logtoConfig.resources.join(' ')); const response = await this.authRestTemplate.post('/token', params.toString(), { headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, }); const { access_token, expires_in } = response.data; const payload = await this.tokenVerifier.verifyToken(access_token); this.accessToken = new token_1.AccessToken(payload.sub, access_token, expires_in); this.apiRestTemplate.setBearer(access_token); } async fetchAccessTokenByPAT(pat) { try { const parameters = new URLSearchParams(); parameters.set("client_id", this.logtoConfig.appId); parameters.set("grant_type", 'urn:ietf:params:oauth:grant-type:token-exchange'); parameters.set("resource", this.logtoConfig.resources.join(" ")); parameters.set("scope", this.logtoConfig.scopes.join(" ")); parameters.set("subject_token", pat); parameters.set("subject_token_type", 'urn:logto:token-type:personal_access_token'); const response = await this.authRestTemplate.post(`${this.logtoConfig.endpoint}/token`, parameters.toString(), { headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, }); return { accessToken: response.data.access_token }; } catch (error) { this.logger.error(`PAT를 이용한 AccessToken 발급 실패: ${error.message}`, error.stack, this.constructor.name); throw new errors_1.PersonalAccessTokenFetchError(); } } async getAccessToken() { if (!this.accessToken || this.accessToken.isExpired()) { await this.fetchAccessToken(); } return this.accessToken.token; } async getRoles() { await this.getAccessToken(); const response = await this.apiRestTemplate.get('/roles'); return response.data; } async getRoleByName(name) { await this.getAccessToken(); const params = new URLSearchParams(); params.set('search.name', name); const response = await this.apiRestTemplate.get(`/roles?${params.toString()}`); return response.data[0]; } async createRole(role) { await this.getAccessToken(); const body = { name: role.name, description: role.description, type: role.type, }; const response = await this.apiRestTemplate.post('/roles', body); if (response instanceof point3_common_tool_1.axiosAdapter.ValidationError) { if (response.code === 'role.name_in_use') { this.logger.error(`이미 존재하는 역할: ${response.code}`, this.constructor.name); return this.getRoleByName(role.name); } throw response; } return response.data; } async assignRoleToUser(userId, roleId) { await this.getAccessToken(); const body = { roleIds: [roleId] }; await this.apiRestTemplate.post(`/users/${userId}/roles`, body); this.logger.log(`사용자에 역할 할당: ${userId}`, this.constructor.name); } async createUser(user) { await this.getAccessToken(); if (user.username && user.primaryEmail && user.password && user.name) { user.passwordAlgorithm = user.passwordAlgorithm ?? types_1.LogtoPasswordAlgorithm.Argon2i; const response = await this.apiRestTemplate.post('/users', user); return response.data.id; } this.logger.error(`필수 필드 누락`, this.constructor.name); throw new errors_1.UserMissingRequiredFieldsError(); } async updateUserClientInfo(userId, clientId) { await this.getAccessToken(); await this.apiRestTemplate.patch(`/users/${userId}`, { customData: { clientId }, }); } async getUser(id) { await this.getAccessToken(); const response = await this.apiRestTemplate.get(`/users/${id}`); return response.data; } async getUserByUsername(username) { await this.getAccessToken(); const params = new URLSearchParams(); params.set('search.username', username); params.set('mode.username', 'exact'); const response = await this.apiRestTemplate.get(`/users?${params.toString()}`); return response.data[0]; } async suspendUser(userId) { await this.getAccessToken(); const response = await this.apiRestTemplate.patch(`/users/${userId}/is-suspended`, { isSuspended: true }); return response.data; } async deleteUser(userId) { await this.getAccessToken(); await this.apiRestTemplate.delete(`/users/${userId}`); } async deleteUserRole(userId, roleId) { await this.getAccessToken(); await this.apiRestTemplate.delete(`/roles/${roleId}/users/${userId}`); } async unsuspendUser(userId) { await this.getAccessToken(); const response = await this.apiRestTemplate.patch(`/users/${userId}/is-suspended`, { isSuspended: false }); return response.data; } async sendVerificationCode(identifier) { await this.getAccessToken(); const method = identifier instanceof types_1.VerificationMethodType.email ? "email" : "phone"; await this.apiRestTemplate.post('/verification-codes', { [method]: identifier.toString(), }); } async verifyCode(identifier, code) { await this.getAccessToken(); const method = identifier instanceof types_1.VerificationMethodType.email ? 'email' : 'phone'; await this.apiRestTemplate.post(`/verification-codes/verify`, { [method]: identifier.toString(), verificationCode: code, }); } async updateUserPassword(userId, password) { await this.getAccessToken(); const response = await this.apiRestTemplate.patch(`/users/${userId}/password`, { password }); return response.data; } async addPersonalAccessToken(userId, name, expiresIn) { await this.getAccessToken(); const body = { name }; if (expiresIn !== undefined && expiresIn !== null) { body.expiresAt = Date.now() + expiresIn * 1000; } const response = await this.apiRestTemplate.post(`/users/${userId}/personal-access-tokens`, body); return response.data; } async deletePersonalAccessToken(userId, name) { await this.getAccessToken(); await this.apiRestTemplate.delete(`/users/${userId}/personal-access-tokens/${name}`); } async getPersonalAccessTokens(userId) { await this.getAccessToken(); const response = await this.apiRestTemplate.get(`/users/${userId}/personal-access-tokens`); return response.data; } }; exports.LogtoM2MClient = LogtoM2MClient; exports.LogtoM2MClient = LogtoM2MClient = __decorate([ (0, common_1.Global)(), (0, common_1.Injectable)(), __metadata("design:paramtypes", [Object, token_1.LogtoTokenVerifier, Object]) ], LogtoM2MClient); function generatePhoneNumberWithCountryCode(countryCode, phoneNumber) { if (phoneNumber.startsWith('0')) { phoneNumber = phoneNumber.slice(1); } return `${countryCode}${phoneNumber}`; } //# sourceMappingURL=m2m-client.js.map