@webda/google-auth
Version:
Webda Google Authentication
227 lines • 7.29 kB
JavaScript
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