UNPKG

open-collaboration-server

Version:

Open Collaboration Server implementation, part of the Open Collaboration Tools project

204 lines 8.55 kB
// ****************************************************************************** // Copyright 2024 TypeFox GmbH // This program and the accompanying materials are made available under the // terms of the MIT License, which is available in the project root. // ****************************************************************************** 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; }; import { inject, injectable, postConstruct } from 'inversify'; import { Emitter, Info } from 'open-collaboration-protocol'; import passport from 'passport'; import { Strategy as GithubStrategy } from 'passport-github'; import { Strategy as GoogleStrategy } from 'passport-google-oauth20'; import { Logger } from '../utils/logging.js'; import { Configuration } from '../utils/configuration.js'; import { URL } from 'url'; export const oauthProviders = Symbol('oauthProviders'); export const ThirdParty = { code: Info.Codes.ThirdParty, message: 'Third-party', params: [] }; let OAuthEndpoint = class OAuthEndpoint { loginRedirectRequests = new Map(); logger; configuration; scope; baseURL; authSuccessEmitter = new Emitter(); onDidAuthenticate = this.authSuccessEmitter.event; initialize() { this.baseURL = this.configuration.getValue('oct-base-url'); } onStart(app, hostname, port) { passport.use(this.id, this.getStrategy(hostname, port)); app.get(this.path, async (req, res) => { const token = req.query.token; if (!token) { this.logger.error('missing token parameter in request'); res.status(400); res.send('Error: Missing token parameter in request'); return; } if (req.query.redirect) { this.loginRedirectRequests.set(token.toString(), req.query.redirect.toString()); } passport.authenticate(this.id, { state: `${token}`, scope: this.scope })(req, res); }); const loginSuccessURL = this.configuration.getValue('oct-login-success-url'); const redirectUriWhitelist = this.configuration.getValue('oct-redirect-url-whitelist')?.split(','); app.get(this.redirectPath, async (req, res) => { const token = req.query.state; if (!token) { this.logger.error('missing token in request state'); res.status(400); res.send('Error: Missing token in request state'); return; } passport.authenticate(this.id, { state: token, scope: this.scope }, async (err, userInfo) => { if (err || !userInfo) { this.logger.error('Error retrieving user info', err); res.status(400); res.send('Error retrieving user info'); return; } try { await Promise.all(this.authSuccessEmitter.fire({ token, userInfo })); } catch (err) { this.logger.error('Error during login', err); res.status(500); res.send('Internal server error occured during Login. Please try again'); return; } const redirectRequest = this.loginRedirectRequests.get(token); if (redirectRequest) { this.loginRedirectRequests.delete(token); if (!redirectUriWhitelist?.includes(redirectRequest)) { this.logger.error(`Redirect URI ${redirectRequest} not in whitelist`); res.status(400); res.send('Error: Redirect URL not in whitelist'); } else { const url = URL.canParse(redirectRequest) ? new URL(redirectRequest) : undefined; if (!url) { res.status(400); res.send('Error: Invalid redirect URL'); return; } url.searchParams.append('token', token); res.redirect(url.toString()); } } else if (loginSuccessURL) { res.redirect(loginSuccessURL); } else { res.status(200); res.send('Login Successful. You can close this page'); } })(req, res); }); } createRedirectUrl(host, port, path) { const baseURL = this.baseURL ?? `http://${host === '0.0.0.0' ? 'localhost' : host}:${port}`; return new URL(path, baseURL).toString(); } }; __decorate([ inject(Logger) ], OAuthEndpoint.prototype, "logger", void 0); __decorate([ inject(Configuration) ], OAuthEndpoint.prototype, "configuration", void 0); __decorate([ postConstruct() ], OAuthEndpoint.prototype, "initialize", null); OAuthEndpoint = __decorate([ injectable() ], OAuthEndpoint); export { OAuthEndpoint }; let GitHubOAuthEndpoint = class GitHubOAuthEndpoint extends OAuthEndpoint { id = 'github'; path = '/api/login/github'; redirectPath = '/api/login/github-callback'; shouldActivate() { return Boolean(this.configuration.getValue('oct-oauth-github-clientid') && this.configuration.getValue('oct-oauth-github-clientsecret')); } getProtocolProvider() { return { type: 'web', name: 'github', endpoint: this.path, label: { code: Info.Codes.GitHubLabel, message: 'GitHub', params: [] }, group: ThirdParty }; } getStrategy(hostname, port) { return new GithubStrategy({ clientID: this.configuration.getValue('oct-oauth-github-clientid'), clientSecret: this.configuration.getValue('oct-oauth-github-clientsecret'), callbackURL: this.createRedirectUrl(hostname, port, this.redirectPath), }, (accessToken, refreshToken, profile, done) => { const userInfo = { name: profile.displayName, email: profile.emails?.[0]?.value, authProvider: 'Github' }; done(undefined, userInfo); }); } }; GitHubOAuthEndpoint = __decorate([ injectable() ], GitHubOAuthEndpoint); export { GitHubOAuthEndpoint }; let GoogleOAuthEndpoint = class GoogleOAuthEndpoint extends OAuthEndpoint { id = 'google'; path = '/api/login/google'; redirectPath = '/api/login/google-callback'; scope = 'email profile'; shouldActivate() { return Boolean(this.configuration.getValue('oct-oauth-google-clientid') && this.configuration.getValue('oct-oauth-google-clientsecret')); } getProtocolProvider() { return { type: 'web', name: 'google', endpoint: this.path, label: { code: Info.Codes.GoogleLabel, message: 'Google', params: [] }, group: ThirdParty }; } getStrategy(hostname, port) { return new GoogleStrategy({ clientID: this.configuration.getValue('oct-oauth-google-clientid'), clientSecret: this.configuration.getValue('oct-oauth-google-clientsecret'), callbackURL: this.createRedirectUrl(hostname, port, this.redirectPath), }, (accessToken, refreshToken, profile, done) => { const userInfo = { name: profile.displayName, email: profile.emails?.find(mail => mail.verified)?.value, authProvider: 'Google' }; done(undefined, userInfo); }); } }; GoogleOAuthEndpoint = __decorate([ injectable() ], GoogleOAuthEndpoint); export { GoogleOAuthEndpoint }; //# sourceMappingURL=oauth-endpoint.js.map