222 lines
6.7 KiB
TypeScript
222 lines
6.7 KiB
TypeScript
import qs from "querystring";
|
|
import https from "https";
|
|
import axios, { AxiosHeaders, AxiosRequestConfig } from "axios";
|
|
import {
|
|
AccessToken,
|
|
ContentType,
|
|
defaultAuthContentType,
|
|
defaultAuthHeaderName,
|
|
defaultGrantType,
|
|
defaultTokenHeaderName,
|
|
ExecutarRequestParams,
|
|
LhispOauthClientConstructorParams,
|
|
} from "./lhisp-oauth-client.t";
|
|
import logger from "lhisp-logger";
|
|
|
|
export class LhispOauthClient<iAccessToken extends AccessToken = AccessToken> {
|
|
protected authUrl: string;
|
|
protected apiUrl: string;
|
|
protected clientId: string;
|
|
protected clientSecret: string;
|
|
protected authHeaderName: string;
|
|
protected tokenHeaderName: string;
|
|
protected authContentType: ContentType;
|
|
protected certificado?: string | Buffer;
|
|
protected senhaCertificado?: string;
|
|
protected authScope?: string;
|
|
protected headers?: AxiosHeaders;
|
|
protected grantType?: string;
|
|
protected agent: https.Agent;
|
|
protected accessToken?: iAccessToken;
|
|
protected refreshToken?: iAccessToken;
|
|
protected tokenCreatedAt = 0;
|
|
protected tokenExpiresIn = 0;
|
|
protected sendAuthCredentialsOnRequestBody?: boolean;
|
|
protected formatAccessToken?: (accessToken?: iAccessToken) => string;
|
|
|
|
constructor(params: LhispOauthClientConstructorParams) {
|
|
if (params.certificado) {
|
|
this.agent = new https.Agent({
|
|
pfx: Buffer.isBuffer(params.certificado) ? params.certificado : Buffer.from(params.certificado, "base64"),
|
|
passphrase: params.senhaCertificado,
|
|
rejectUnauthorized: false,
|
|
});
|
|
} else {
|
|
this.agent = new https.Agent({
|
|
rejectUnauthorized: false,
|
|
});
|
|
}
|
|
|
|
this.certificado = params.certificado;
|
|
this.headers = (params.headers ? params.headers : {}) as any as AxiosHeaders;
|
|
this.apiUrl = params.apiUrl;
|
|
this.authUrl = params.authUrl;
|
|
this.authScope = params.authScope;
|
|
this.grantType = params.grantType || defaultGrantType;
|
|
this.authContentType = params.authContentType || defaultAuthContentType;
|
|
this.clientId = params.clientId;
|
|
this.clientSecret = params.clientSecret;
|
|
this.authHeaderName = params.authHeaderName || defaultAuthHeaderName;
|
|
this.tokenHeaderName = params.tokenHeaderName || defaultTokenHeaderName;
|
|
this.sendAuthCredentialsOnRequestBody = params.sendAuthCredentialsOnRequestBody;
|
|
this.formatAccessToken = params.formatAccessToken;
|
|
}
|
|
|
|
getAuthHeaderValue(): string {
|
|
return `Basic ${Buffer.from(`${this.clientId}:${this.clientSecret}`).toString("base64")}`;
|
|
}
|
|
|
|
parseData({ data, contentType = ContentType.APPLICATION_JSON }: { data: any; contentType: string }) {
|
|
if (!data || Object.keys(data).length === 0) return undefined;
|
|
|
|
switch (contentType) {
|
|
case ContentType.APPLICATION_JSON:
|
|
return JSON.stringify(data);
|
|
case ContentType.APPLICATION_X_WWW_FORM_URLENCODED:
|
|
return qs.stringify(data);
|
|
default:
|
|
throw new Error(`Content Type Inválido: [${contentType}]`);
|
|
}
|
|
}
|
|
|
|
isTokenValid() {
|
|
if (!this.accessToken) return false;
|
|
if (!this.tokenCreatedAt) return false;
|
|
const timeDiff = (Date.now() - this.tokenCreatedAt) / 1000;
|
|
return timeDiff < this.tokenExpiresIn - 10;
|
|
}
|
|
|
|
async getAccessToken(): Promise<iAccessToken> {
|
|
try {
|
|
if (this.accessToken && this.isTokenValid()) {
|
|
return this.accessToken;
|
|
}
|
|
|
|
// TODO: Implementar Refresh Token.
|
|
|
|
let authRequestOpt: AxiosRequestConfig = {
|
|
method: "POST",
|
|
url: this.authUrl,
|
|
httpsAgent: this.agent,
|
|
headers: {
|
|
[this.authHeaderName]: this.getAuthHeaderValue(),
|
|
"Content-Type": this.authContentType,
|
|
},
|
|
data: {},
|
|
};
|
|
|
|
if (this.grantType) authRequestOpt.data.grant_type = this.grantType;
|
|
if (this.authScope) authRequestOpt.data.scope = this.authScope;
|
|
|
|
if (this.sendAuthCredentialsOnRequestBody) {
|
|
if (this.clientId) authRequestOpt.data.client_id = this.clientId;
|
|
if (this.clientSecret) authRequestOpt.data.client_secret = this.clientSecret;
|
|
}
|
|
|
|
authRequestOpt.data = this.parseData({
|
|
data: authRequestOpt.data,
|
|
contentType: this.authContentType,
|
|
});
|
|
const resp = await axios.request(authRequestOpt);
|
|
this.accessToken = this.buildAccessToken(resp.data);
|
|
this.tokenCreatedAt = new Date().getTime();
|
|
this.tokenExpiresIn = this.accessToken?.expires_in || this.tokenCreatedAt + 60000;
|
|
return this.accessToken;
|
|
} catch (error) {
|
|
logger.error({ message: "LhispOauthClient.getAccessToken", error });
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
buildAccessToken(data: any) {
|
|
return data as iAccessToken;
|
|
}
|
|
|
|
getAuthToken() {
|
|
if (this.formatAccessToken) {
|
|
return this.formatAccessToken(this.accessToken);
|
|
}
|
|
|
|
return `${this.accessToken?.token_type} ${this.accessToken?.access_token}`;
|
|
}
|
|
|
|
async executarRequest<ResponseType>({
|
|
method,
|
|
path,
|
|
data,
|
|
params,
|
|
contentType = ContentType.APPLICATION_JSON,
|
|
}: ExecutarRequestParams): Promise<ResponseType> {
|
|
try {
|
|
await this.getAccessToken();
|
|
|
|
const headers = {
|
|
"Content-Type": contentType,
|
|
[this.tokenHeaderName]: this.getAuthToken(),
|
|
...(this.headers || {}),
|
|
};
|
|
|
|
const response = await axios.request<ResponseType>({
|
|
method,
|
|
url: `${this.apiUrl}${path}`,
|
|
httpsAgent: this.agent,
|
|
headers,
|
|
data,
|
|
params,
|
|
});
|
|
|
|
return response.data;
|
|
} catch (error) {
|
|
logger.error({
|
|
message: "LhispOauthClient.executarRequest",
|
|
method,
|
|
url: `${this.apiUrl}${path}`,
|
|
data,
|
|
params,
|
|
contentType,
|
|
error,
|
|
});
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async get<ResponseType>(opt: ExecutarRequestParams) {
|
|
return this.executarRequest<ResponseType>({
|
|
method: "GET",
|
|
contentType: opt.contentType || ContentType.APPLICATION_JSON,
|
|
...opt,
|
|
});
|
|
}
|
|
|
|
async put<ResponseType>(opt: ExecutarRequestParams) {
|
|
return this.executarRequest<ResponseType>({
|
|
method: "PUT",
|
|
contentType: opt.contentType || ContentType.APPLICATION_JSON,
|
|
...opt,
|
|
});
|
|
}
|
|
|
|
async patch<ResponseType>(opt: ExecutarRequestParams) {
|
|
return this.executarRequest<ResponseType>({
|
|
method: "PATCH",
|
|
contentType: opt.contentType || ContentType.APPLICATION_JSON,
|
|
...opt,
|
|
});
|
|
}
|
|
|
|
async post<ResponseType>(opt: ExecutarRequestParams) {
|
|
return this.executarRequest<ResponseType>({
|
|
method: "POST",
|
|
contentType: opt.contentType || ContentType.APPLICATION_JSON,
|
|
...opt,
|
|
});
|
|
}
|
|
|
|
async delete<ResponseType>(opt: ExecutarRequestParams) {
|
|
return this.executarRequest<ResponseType>({
|
|
method: "DELETE",
|
|
contentType: opt.contentType || ContentType.APPLICATION_JSON,
|
|
...opt,
|
|
});
|
|
}
|
|
}
|