UNPKG

@webda/google-auth

Version:
227 lines 7.29 kB
import { OAuthService, OAuthServiceParameters, WebdaError } from "@webda/core"; import { OAuth2Client } from "google-auth-library"; import * as http from "http"; /** * Credentials to manage Google Auth * https://developers.google.com/identity/protocols/oauth2 */ export class GoogleParameters extends OAuthServiceParameters { constructor(params) { super(params); this.access_type = this.access_type || "online"; } } /** * Manage Google Authentication * * @WebdaModda */ export default class GoogleAuthentication extends OAuthService { /** * Return provider name * @returns */ getName() { return "google"; } /** * Allow every accounts.google. */ getCallbackReferer() { return [/accounts\.google\.[a-z]+$/]; } /** * Get OAuth callback query parameters * @returns */ getCallbackQueryParams() { return [ { name: "code", required: true }, { name: "scope", required: true }, { name: "state", required: true }, { name: "authuser", required: false }, { name: "hd", required: false }, { name: "prompt", required: false } ]; } /** * Expose on /google by default */ getDefaultUrl() { return "/google"; } /** * We manage Google Auth Token */ hasToken() { return true; } /** * @inheritdoc */ loadParameters(params) { return new GoogleParameters(params); } /** * * @param redirect_uri * @param state */ generateAuthUrl(redirect_uri, state, _ctx) { let oauthClient = this.getOAuthClient(redirect_uri); return oauthClient.generateAuthUrl({ access_type: this.parameters.access_type, scope: this.parameters.scope, redirect_uri, response_type: "code", state, ...this.parameters.auth_options }); } /** * Return a google oauth client * @param redirect_uri */ getOAuthClient(redirect_uri) { return new OAuth2Client(this.parameters.client_id, this.parameters.client_secret, redirect_uri); } /** * @inheritdoc */ async handleCallback(ctx) { // Verify state are equal if (ctx.getRequestParameters().state !== ctx.getSession().oauth?.state) { this.log("WARN", `Bad State ${ctx.getRequestParameters().state} !== ${ctx.getSession().oauth?.state}`); throw new WebdaError.Forbidden("Bad State"); } let code = ctx.getRequestParameters().code; let redirect_uri = ctx.getHttpContext().getAbsoluteUrl(`${this.parameters.url}/callback`); let oauthClient = this.getOAuthClient(redirect_uri); let profile, identId; // Now that we have the code, use that to acquire tokens. try { const r = await oauthClient.getToken(code); this.emitSync("GoogleAuth.Tokens", { tokens: r.tokens, context: ctx }); profile = await this.getUserInfo(r.tokens.id_token); identId = profile.sub; } catch (err) { this.log("ERROR", err); throw new WebdaError.Forbidden("OAuth Error"); } return { identId, profile }; } /** * Retrieve the user profile based on the token * @param token * @returns */ async getUserInfo(token) { const oauthClient = this.getOAuthClient(); const ticket = await oauthClient.verifyIdToken({ idToken: token, audience: this.parameters.client_id }); return ticket.getPayload(); } /** * Verify a Google Auth Token */ async handleToken(context) { let tokens = (await context.getRequestBody()).tokens; if (!tokens) { throw new WebdaError.BadRequest("No tokens provided"); } const profile = await this.getUserInfo(tokens.id_token); return { identId: profile.sub, profile }; } /** * Retrieve Google Client * * Redirecting to the webbrowser for the OAuth validation * Store the token in user store afterwards */ async getLocalClient(token, open, storeToken) { if (this._client) { return this._client; } const oAuth2Client = this.getOAuthClient(); if (token) { oAuth2Client.setCredentials(token); this._client = oAuth2Client; return oAuth2Client; } // Generate the url that will be used for the consent dialog. const authorizeUrl = oAuth2Client.generateAuthUrl({ access_type: "offline", scope: this.parameters.scope, redirect_uri: "http://localhost:3000/oauth2callback" }); // Open an http server to accept the oauth callback. In this simple example, the // only request to our webserver is to /oauth2callback?code=<code> return new Promise((resolve, reject) => { const server = http .createServer(async (req, res) => { if (req.url.indexOf("/oauth2callback") > -1) { try { // acquire the code from the querystring, and close the web server. const code = new URL(req.url, `http://localhost:3000`).searchParams.get("code"); if (code) { res.end("Authentication successful! Please return to the console."); } else { res.end("Authentication unsuccessful! Please return to the console."); return reject("Failed"); } // Now that we have the code, use that to acquire tokens. const r = await oAuth2Client.getToken(code); // Make sure to set the credentials on the OAuth2 client. storeToken(r.tokens); oAuth2Client.setCredentials(r.tokens); this._client = oAuth2Client; this.log("INFO", "Google Authentication finished."); return resolve(this._client); } catch (err) { console.log(err); reject(err); } finally { server.close(); } } }) .listen(3000, () => { // open the browser to the authorize url to start the workflow this.log("INFO", "Launching your browser for Google API Permission"); open(authorizeUrl); }); }); } } export { GoogleAuthentication }; //# sourceMappingURL=google-auth.js.map