UNPKG

@azteam/google-api

Version:

N/A

232 lines (203 loc) 7.33 kB
import jwt from 'jsonwebtoken'; import HttpClient from '@azteam/http-client'; import {ACCOUNT_TYPE} from './constant'; import * as SCOPE from './scope'; import {AUTH_EMAIL_SCOPE, AUTH_PROFILE_SCOPE} from './scope'; const OAUTH2_ENDPOINT = 'https://www.googleapis.com/oauth2/v3'; class GoogleOAuth2API { static async getProfileInApp(appId, token) { const client = new HttpClient({ timeout: 20000, }), res = await client.get(`${OAUTH2_ENDPOINT}/tokeninfo`, { id_token: token, }); if (res.azp && appId) { return { id: res.sub, email: res.email, name: res.name, avatar: res.picture, }; } return null; } constructor(accountType, credential, options = {}) { this.proxy = null; if (options.proxy) { this.proxy = { host: options.proxy.ip, port: options.proxy.port, }; if (options.proxy.username) { this.proxy.auth = { username: options.proxy.username, password: options.proxy.password, }; } } this.accountType = accountType; switch (this.accountType) { case ACCOUNT_TYPE.SERVICE_ACCOUNT: this.serviceAccountCredential = credential; this.token = { expiresAt: 0, tokenType: 'Bearer', accessToken: '', }; break; case ACCOUNT_TYPE.USER_ACCOUNT: default: this.clientId = credential.clientId; this.clientSecret = credential.clientSecret; if (credential.token) { this.token = { expiresAt: 0, tokenType: 'Bearer', ...credential.token, }; } } } getAuthLinkFullScope(redirectURI, state) { const scopeArray = Object.values(SCOPE); return this.getAuthLink(redirectURI, state, scopeArray); } getAuthLink(redirectURI = 'http://localhost', state = null, arrayScope = [AUTH_EMAIL_SCOPE, AUTH_PROFILE_SCOPE]) { const scopes = encodeURIComponent(arrayScope.join(' ')); return `https://accounts.google.com/o/oauth2/v2/auth/auth?access_type=offline&client_id=${this.clientId}&redirect_uri=${redirectURI}&response_type=code&scope=${scopes}&flowName=GeneralOAuthFlow&state=${state}`; } async getToken() { return this.token; } async exchangeCode(code, redirectURI = 'http://localhost') { const client = new HttpClient({ timeout: 20000, }), res = await client.post('https://oauth2.googleapis.com/token', { code, client_id: this.clientId, client_secret: this.clientSecret, redirect_uri: redirectURI, grant_type: 'authorization_code', }); if (res.access_token) { this.token = { accessToken: res.access_token, refreshToken: res.refresh_token, tokenType: res.token_type, expiresAt: Date.now() + Number(res.expires_in) * 1000 - 600, }; return this.token; } if (res.error) { throw new Error(res.error); } return null; } async _getAuthClient() { if (this.token) { await this._refreshToken(); const clientOptions = { timeout: 20000, headers: { Authorization: `${this.token.tokenType} ${this.token.accessToken}`, }, }; if (this.proxy) { clientOptions.proxy = this.proxy; } return new HttpClient(clientOptions); } throw new Error('Empty Init Token'); } async getGoogleServiceAccessToken() { const token = { iss: this.serviceAccountCredential.client_email, iat: Math.round(Date.now() / 1000), exp: Math.round(Date.now() / 1000) + 60 * 60, aud: `${OAUTH2_ENDPOINT}/token`, scope: this.serviceAccountCredential.scope, }, signedJwt = jwt.sign(token, this.serviceAccountCredential.private_key, {algorithm: 'RS256'}), clientOptions = { timeout: 20000, headers: { Authorization: `Bearer ${signedJwt}`, }, }; if (this.proxy) { clientOptions.proxy = this.proxy; } const client = new HttpClient(clientOptions); return client.post( token.aud, { grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', assertion: signedJwt, }, false ); } async getUserInfo() { const client = await this._getAuthClient(), res = await client.get(`${OAUTH2_ENDPOINT}/userinfo`); if (res.error) { throw new Error(res.error); } return res; } async getScope() { const client = await this._getAuthClient(), res = await client.get(`${OAUTH2_ENDPOINT}/tokeninfo`); if (res.error) { throw new Error(res.error); } if (res.scope) { return res.scope.split(' '); } return []; } async hasScope(arrayScope = []) { const scope = await this.getScope(); return scope.some((r) => arrayScope.includes(r)); } async isLive() { const client = await this._getAuthClient(), res = await client.get(`${OAUTH2_ENDPOINT}/tokeninfo`); return !res.error; } async _refreshToken() { if (this.token.expiresAt < Date.now()) { const clientOptions = { timeout: 20000, }; if (this.proxy) { clientOptions.proxy = this.proxy; } const client = new HttpClient(clientOptions); let res = null; switch (this.accountType) { case ACCOUNT_TYPE.SERVICE_ACCOUNT: res = await this.getGoogleServiceAccessToken(); break; case ACCOUNT_TYPE.USER_ACCOUNT: default: res = await client.post(`${OAUTH2_ENDPOINT}/token`, { client_id: this.clientId, client_secret: this.clientSecret, refresh_token: this.token.refreshToken, grant_type: 'refresh_token', }); } if (res.error) { throw new Error(res.error); } this.token.accessToken = res.access_token; this.token.tokenType = res.token_type; this.token.expiresAt = Date.now() + Number(res.expires_in) * 1000 - 600; } return this.token; } } export default GoogleOAuth2API;