plugin/auth-backend: add gitlab oauth
This commit is contained in:
@@ -39,6 +39,7 @@
|
||||
"morgan": "^1.10.0",
|
||||
"passport": "^0.4.1",
|
||||
"passport-github2": "^0.1.12",
|
||||
"passport-gitlab2": "^5.0.0",
|
||||
"passport-google-oauth20": "^2.0.0",
|
||||
"passport-oauth2": "^1.5.0",
|
||||
"passport-okta-oauth": "^0.0.1",
|
||||
|
||||
@@ -137,7 +137,9 @@ export const executeRefreshTokenStrategy = async (
|
||||
params: any,
|
||||
) => {
|
||||
if (err) {
|
||||
reject(new Error(`Failed to refresh access token ${err}`));
|
||||
reject(
|
||||
new Error(`Failed to refresh access token: ${JSON.stringify(err)}`),
|
||||
);
|
||||
}
|
||||
if (!accessToken) {
|
||||
reject(
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import Router from 'express-promise-router';
|
||||
import { createGithubProvider } from './github';
|
||||
import { createGoogleProvider } from './google';
|
||||
import { createGitlabProvider } from './gitlab';
|
||||
import { createSamlProvider } from './saml';
|
||||
import { createOktaProvider } from './okta';
|
||||
import { AuthProviderFactory, AuthProviderConfig } from './types';
|
||||
@@ -26,6 +27,7 @@ import { TokenIssuer } from '../identity';
|
||||
const factories: { [providerId: string]: AuthProviderFactory } = {
|
||||
google: createGoogleProvider,
|
||||
github: createGithubProvider,
|
||||
gitlab: createGitlabProvider,
|
||||
saml: createSamlProvider,
|
||||
okta: createOktaProvider,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { createGitlabProvider } from './provider';
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import express from 'express';
|
||||
import { Strategy as GitlabStrategy } from 'passport-gitlab2';
|
||||
import {
|
||||
executeFrameHandlerStrategy,
|
||||
executeRedirectStrategy,
|
||||
makeProfileInfo,
|
||||
} from '../../lib/PassportStrategyHelper';
|
||||
import {
|
||||
OAuthProviderHandlers,
|
||||
OAuthPrivateInfo,
|
||||
AuthProviderConfig,
|
||||
RedirectInfo,
|
||||
EnvironmentProviderConfig,
|
||||
OAuthProviderOptions,
|
||||
OAuthProviderConfig,
|
||||
OAuthResponse,
|
||||
PassportDoneCallback,
|
||||
} from '../types';
|
||||
import { OAuthProvider } from '../../lib/OAuthProvider';
|
||||
import {
|
||||
EnvironmentHandlers,
|
||||
EnvironmentHandler,
|
||||
} from '../../lib/EnvironmentHandler';
|
||||
import { Logger } from 'winston';
|
||||
import { TokenIssuer } from '../../identity';
|
||||
import passport from 'passport';
|
||||
|
||||
export class GitlabAuthProvider implements OAuthProviderHandlers {
|
||||
private readonly _strategy: GitlabStrategy;
|
||||
private static _defaultExpiresInSeconds = 2 * 3600;
|
||||
|
||||
static transformPassportProfile(rawProfile: any): passport.Profile {
|
||||
const profile: passport.Profile = {
|
||||
id: rawProfile.id,
|
||||
username: rawProfile.username,
|
||||
provider: rawProfile.provider,
|
||||
displayName: rawProfile.displayName,
|
||||
};
|
||||
if (rawProfile.emails && rawProfile.emails.length > 0) {
|
||||
profile.emails = rawProfile.emails;
|
||||
}
|
||||
if (rawProfile.avatarUrl) {
|
||||
profile.photos = [{ value: rawProfile.avatarUrl }];
|
||||
}
|
||||
return profile;
|
||||
}
|
||||
|
||||
static transformOAuthResponse(
|
||||
accessToken: string,
|
||||
rawProfile: any,
|
||||
params: any = {},
|
||||
): OAuthResponse {
|
||||
const passportProfile = GitlabAuthProvider.transformPassportProfile(
|
||||
rawProfile,
|
||||
);
|
||||
const profile = makeProfileInfo(passportProfile, params.id_token);
|
||||
return {
|
||||
providerInfo: {
|
||||
accessToken,
|
||||
scope: params.scope,
|
||||
expiresInSeconds:
|
||||
params.expires_in || GitlabAuthProvider._defaultExpiresInSeconds,
|
||||
},
|
||||
profile,
|
||||
};
|
||||
}
|
||||
|
||||
constructor(options: OAuthProviderOptions) {
|
||||
this._strategy = new GitlabStrategy(
|
||||
{ ...options },
|
||||
(
|
||||
accessToken: any,
|
||||
_: any,
|
||||
params: any,
|
||||
rawProfile: any,
|
||||
done: PassportDoneCallback<OAuthResponse, OAuthPrivateInfo>,
|
||||
) => {
|
||||
const oauthResponse = GitlabAuthProvider.transformOAuthResponse(
|
||||
accessToken,
|
||||
rawProfile,
|
||||
params,
|
||||
);
|
||||
done(undefined, oauthResponse);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async start(
|
||||
req: express.Request,
|
||||
options: Record<string, string>,
|
||||
): Promise<RedirectInfo> {
|
||||
return await executeRedirectStrategy(req, this._strategy, options);
|
||||
}
|
||||
|
||||
async handler(
|
||||
req: express.Request,
|
||||
): Promise<{ response: OAuthResponse; refreshToken: string }> {
|
||||
const { response } = await executeFrameHandlerStrategy<
|
||||
OAuthResponse,
|
||||
OAuthPrivateInfo
|
||||
>(req, this._strategy);
|
||||
|
||||
return { response };
|
||||
}
|
||||
}
|
||||
|
||||
export function createGitlabProvider(
|
||||
{ baseUrl }: AuthProviderConfig,
|
||||
providerConfig: EnvironmentProviderConfig,
|
||||
logger: Logger,
|
||||
tokenIssuer: TokenIssuer,
|
||||
) {
|
||||
const envProviders: EnvironmentHandlers = {};
|
||||
|
||||
for (const [env, envConfig] of Object.entries(providerConfig)) {
|
||||
const config = (envConfig as unknown) as OAuthProviderConfig;
|
||||
const { secure, appOrigin, baseUrl: oauthBaseUrl } = config;
|
||||
const callbackURLParam = `?env=${env}`;
|
||||
|
||||
const opts = {
|
||||
clientID: config.clientId,
|
||||
clientSecret: config.clientSecret,
|
||||
callbackURL: `${baseUrl}/gitlab/handler/frame${callbackURLParam}`,
|
||||
baseURL: oauthBaseUrl,
|
||||
};
|
||||
|
||||
if (!opts.clientID || !opts.clientSecret) {
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
throw new Error(
|
||||
'Failed to initialize Gitlab auth provider, set AUTH_GITLAB_CLIENT_ID and AUTH_GITLAB_CLIENT_SECRET env vars',
|
||||
);
|
||||
}
|
||||
|
||||
logger.warn(
|
||||
'Gitlab auth provider disabled, set AUTH_GITLAB_CLIENT_ID and AUTH_GITLAB_CLIENT_SECRET env vars to enable',
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
envProviders[env] = new OAuthProvider(new GitlabAuthProvider(opts), {
|
||||
disableRefresh: true,
|
||||
providerId: 'gitlab',
|
||||
secure,
|
||||
baseUrl,
|
||||
appOrigin,
|
||||
tokenIssuer,
|
||||
});
|
||||
}
|
||||
return new EnvironmentHandler(envProviders);
|
||||
}
|
||||
@@ -47,6 +47,10 @@ export type OAuthProviderConfig = {
|
||||
* to the window that initiates an auth request.
|
||||
*/
|
||||
appOrigin: string;
|
||||
/**
|
||||
* Base URL of the auth provider.
|
||||
*/
|
||||
baseUrl: string;
|
||||
/**
|
||||
* Client ID of the auth provider.
|
||||
*/
|
||||
|
||||
@@ -76,6 +76,15 @@ export async function createRouter(
|
||||
clientSecret: process.env.AUTH_GITHUB_CLIENT_SECRET!,
|
||||
},
|
||||
},
|
||||
gitlab: {
|
||||
development: {
|
||||
appOrigin: 'http://localhost:3000',
|
||||
secure: false,
|
||||
baseUrl: process.env.GITLAB_BASE_URL || 'https://gitlab.com',
|
||||
clientId: process.env.AUTH_GITLAB_CLIENT_ID!,
|
||||
clientSecret: process.env.AUTH_GITLAB_CLIENT_SECRET!,
|
||||
},
|
||||
},
|
||||
saml: {
|
||||
development: {
|
||||
entryPoint: 'http://localhost:7001/',
|
||||
@@ -89,8 +98,8 @@ export async function createRouter(
|
||||
clientId: process.env.AUTH_OKTA_CLIENT_ID!,
|
||||
clientSecret: process.env.AUTH_OKTA_CLIENT_SECRET!,
|
||||
audience: process.env.AUTH_OKTA_AUDIENCE,
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user