@point3/logto-module
Version:
포인트3 내부 logto Authentication 모듈입니다
215 lines • 9.77 kB
JavaScript
"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