From d093b889d3866e8536de820f8a128550888fd28f Mon Sep 17 00:00:00 2001 From: Sotiria Stefa <72135844+SotiriaSte@users.noreply.github.com> Date: Fri, 21 Oct 2022 14:48:55 +0300 Subject: [PATCH] feat(authentication): bitbucket-provider (#398) --- .../src/config/bitbucket.config.ts | 25 +++++++ modules/authentication/src/config/index.ts | 3 + .../handlers/oauth2/bitbucket/bitbucket.json | 8 +++ .../handlers/oauth2/bitbucket/bitbucket.ts | 69 +++++++++++++++++++ .../oauth2/bitbucket/bitbucket.user.ts | 14 ++++ .../src/handlers/oauth2/index.ts | 1 + 6 files changed, 120 insertions(+) create mode 100644 modules/authentication/src/config/bitbucket.config.ts create mode 100644 modules/authentication/src/handlers/oauth2/bitbucket/bitbucket.json create mode 100644 modules/authentication/src/handlers/oauth2/bitbucket/bitbucket.ts create mode 100644 modules/authentication/src/handlers/oauth2/bitbucket/bitbucket.user.ts diff --git a/modules/authentication/src/config/bitbucket.config.ts b/modules/authentication/src/config/bitbucket.config.ts new file mode 100644 index 000000000..69d57fd84 --- /dev/null +++ b/modules/authentication/src/config/bitbucket.config.ts @@ -0,0 +1,25 @@ +export default { + bitbucket: { + enabled: { + format: 'Boolean', + default: false, + }, + clientId: { + format: 'String', + default: '', + }, + clientSecret: { + format: 'String', + default: '', + }, + redirect_uri: { + format: 'String', + default: '', + }, + accountLinking: { + doc: 'When enabled, if a new bitbucket user matches with an existing email on the database, they will be enriched with bitbucket details', + format: 'Boolean', + default: true, + }, + }, +}; diff --git a/modules/authentication/src/config/index.ts b/modules/authentication/src/config/index.ts index 939296b98..71dcd62b2 100644 --- a/modules/authentication/src/config/index.ts +++ b/modules/authentication/src/config/index.ts @@ -11,8 +11,10 @@ import tokenConfig from './token.config'; import localConfig from './local.config'; import magicLinkConfig from './magicLink.config'; import gitlabConfig from './gitlab.config'; +import bitbucketConfig from './bitbucket.config'; import linkedInConfig from './linkedIn.config'; + const AppConfigSchema = { ...DefaultConfig, ...figmaConfig, @@ -26,6 +28,7 @@ const AppConfigSchema = { ...localConfig, ...magicLinkConfig, ...gitlabConfig, + ...bitbucketConfig, ...linkedInConfig, }; const config = convict(AppConfigSchema); diff --git a/modules/authentication/src/handlers/oauth2/bitbucket/bitbucket.json b/modules/authentication/src/handlers/oauth2/bitbucket/bitbucket.json new file mode 100644 index 000000000..2fe4b32a3 --- /dev/null +++ b/modules/authentication/src/handlers/oauth2/bitbucket/bitbucket.json @@ -0,0 +1,8 @@ +{ + "accessTokenMethod": "POST", + "authorizeUrl": "https://bitbucket.org/site/oauth2/authorize?", + "providerName": "bitbucket", + "tokenUrl": "https://bitbucket.org/site/oauth2/access_token", + "responseType": "code", + "grantType": "authorization_code" +} \ No newline at end of file diff --git a/modules/authentication/src/handlers/oauth2/bitbucket/bitbucket.ts b/modules/authentication/src/handlers/oauth2/bitbucket/bitbucket.ts new file mode 100644 index 000000000..0468b3ab7 --- /dev/null +++ b/modules/authentication/src/handlers/oauth2/bitbucket/bitbucket.ts @@ -0,0 +1,69 @@ +import ConduitGrpcSdk from '@conduitplatform/grpc-sdk'; +import axios from 'axios'; +import { BitbucketUser } from './bitbucket.user'; +import { OAuth2 } from '../OAuth2'; +import { OAuth2Settings } from '../interfaces/OAuth2Settings'; +import * as bitbucketParameters from './bitbucket.json'; +import { ProviderConfig } from '../interfaces/ProviderConfig'; +import { Payload } from '../interfaces/Payload'; +import { ConnectionParams } from '../interfaces/ConnectionParams'; +import { isNil } from 'lodash'; +import { AuthParams } from '../interfaces/AuthParams'; + +export class BitbucketHandlers extends OAuth2 { + constructor(grpcSdk: ConduitGrpcSdk, config: { bitbucket: ProviderConfig }) { + super( + grpcSdk, + 'bitbucket', + new OAuth2Settings(config.bitbucket, bitbucketParameters), + ); + this.defaultScopes = ['account']; + } + + async connectWithProvider(details: ConnectionParams): Promise> { + const bitbucket_access_token = details.accessToken; + const bitbucketProfile = await axios.get('https://api.bitbucket.org/2.0/user', { + headers: { + Authorization: `Bearer ${bitbucket_access_token}`, + Accept: 'application/json', + }, + }); + + let email; + if (isNil(bitbucketProfile.data.email)) { + const bitbucketUserEmail = await axios.get( + 'https://api.bitbucket.org/2.0/user/emails', + { + headers: { + Authorization: `Bearer ${bitbucket_access_token}`, + Accept: 'application/json', + }, + }, + ); + email = bitbucketUserEmail.data.values[0].email; + } else { + email = bitbucketProfile.data.email; + } + return { + id: bitbucketProfile.data.uuid, + email: email, + data: { ...bitbucketProfile.data }, + }; + } + makeRequest(data: AuthParams) { + const requestData: string = Object.keys(data) + .map(k => { + return k + '=' + data[k as keyof AuthParams]; + }) + .join('&'); + + return { + method: this.settings.accessTokenMethod, + url: this.settings.tokenUrl, + data: requestData, + headers: { + Accept: 'application/json', + }, + }; + } +} diff --git a/modules/authentication/src/handlers/oauth2/bitbucket/bitbucket.user.ts b/modules/authentication/src/handlers/oauth2/bitbucket/bitbucket.user.ts new file mode 100644 index 000000000..d4d6e3472 --- /dev/null +++ b/modules/authentication/src/handlers/oauth2/bitbucket/bitbucket.user.ts @@ -0,0 +1,14 @@ +export interface BitbucketUser { + display_name: string; + created_on?: string; + type: string; + uuid: string; + has_2fa_enabled: boolean; + username: string; + is_staff: boolean; + account_id: string; + nickname: string; + account_status: string; + location?: string; + website?: string; +} diff --git a/modules/authentication/src/handlers/oauth2/index.ts b/modules/authentication/src/handlers/oauth2/index.ts index 1abb87263..882548e7e 100644 --- a/modules/authentication/src/handlers/oauth2/index.ts +++ b/modules/authentication/src/handlers/oauth2/index.ts @@ -6,4 +6,5 @@ export * from './microsoft/microsoft'; export * from './slack/slack'; export * from './twitch/twitch'; export * from './gitlab/gitlab'; +export * from './bitbucket/bitbucket'; export * from './linkedIn/linkedin';