diff --git a/package-lock.json b/package-lock.json index 21bb1ae..2073a97 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "jwk-to-pem": "^2.0.5", "passport": "^0.6.0", "passport-facebook": "^3.0.0", + "passport-forcedotcom": "^0.1.5", "passport-google-oauth20": "^2.0.0", "passport-hubspot-oauth2": "^1.0.3", "passport-mailchimp": "^1.1.0", @@ -8640,6 +8641,30 @@ "node": ">= 0.4.0" } }, + "node_modules/passport-forcedotcom": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/passport-forcedotcom/-/passport-forcedotcom-0.1.5.tgz", + "integrity": "sha512-raDRQqBJtTEHDxLkxLG+9sYE0xpPy9PqriMPbMy9jmpIcU2+mOq9PgczvUPrU8UjfcZp3gqkDHqujcUWiGOVNQ==", + "dependencies": { + "passport-oauth2": "1.3.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/passport-forcedotcom/node_modules/passport-oauth2": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.3.0.tgz", + "integrity": "sha512-AisRXcC7Vx8WSx5C9O8UNib/Y91/IOphs5tImlOMs7T+QTEJdmS+OxgXSskbnDdGQT/8mU5T3yMT6136rBjYXg==", + "dependencies": { + "oauth": "0.9.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/passport-google-oauth20": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz", @@ -16932,6 +16957,26 @@ "passport-oauth2": "1.x.x" } }, + "passport-forcedotcom": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/passport-forcedotcom/-/passport-forcedotcom-0.1.5.tgz", + "integrity": "sha512-raDRQqBJtTEHDxLkxLG+9sYE0xpPy9PqriMPbMy9jmpIcU2+mOq9PgczvUPrU8UjfcZp3gqkDHqujcUWiGOVNQ==", + "requires": { + "passport-oauth2": "1.3.x" + }, + "dependencies": { + "passport-oauth2": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.3.0.tgz", + "integrity": "sha512-AisRXcC7Vx8WSx5C9O8UNib/Y91/IOphs5tImlOMs7T+QTEJdmS+OxgXSskbnDdGQT/8mU5T3yMT6136rBjYXg==", + "requires": { + "oauth": "0.9.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x" + } + } + } + }, "passport-google-oauth20": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz", diff --git a/package.json b/package.json index 16dfcc7..78c597c 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "jwk-to-pem": "^2.0.5", "passport": "^0.6.0", "passport-facebook": "^3.0.0", + "passport-forcedotcom": "^0.1.5", "passport-google-oauth20": "^2.0.0", "passport-hubspot-oauth2": "^1.0.3", "passport-mailchimp": "^1.1.0", @@ -67,11 +68,11 @@ "@types/jest": "27.0.2", "@types/jsonwebtoken": "^8.5.8", "@types/jwk-to-pem": "^2.0.1", + "@types/multer": "^1.4.7", "@types/node": "^16.11.41", "@types/passport-facebook": "^2.1.11", "@types/passport-google-oauth20": "^2.0.11", "@types/passport-oauth2": "^1.4.11", - "@types/multer": "^1.4.7", "@types/supertest": "^2.0.12", "@typescript-eslint/eslint-plugin": "^5.29.0", "@typescript-eslint/parser": "^5.29.0", diff --git a/src/app.module.ts b/src/app.module.ts index f418d7c..1fc2dd8 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -38,6 +38,7 @@ import { AuthenticationGuard } from './authentication/authentication.guard'; import { APP_GUARD } from '@nestjs/core'; import { GoogleStrategy } from './modules/oauth/passport-strategies/google-strategy'; import { MailChimpStrategy } from './modules/oauth/passport-strategies/mailchimp-strategy'; +import { SalesforceStrategy } from './modules/oauth/passport-strategies/salesforce-strategy'; import { ConnectorClientConfiguration } from './clients/connector/client.config'; import { ConnectorController } from './modules/connector/connector.controller'; import { ConnectorClientService } from './clients/connector/client.service'; @@ -77,14 +78,11 @@ const connectorClient = new ConnectorClientConfiguration(); AuthClientService, PermissionsClientService, CatalogService, - { - provide: APP_GUARD, - useClass: AuthenticationGuard, - }, HubspotStrategy, FacebookStrategy, GoogleStrategy, MailChimpStrategy, + SalesforceStrategy, { provide: APP_GUARD, useClass: AuthenticationGuard, diff --git a/src/modules/oauth/oauth.controller.ts b/src/modules/oauth/oauth.controller.ts index 7feebae..fd262c1 100644 --- a/src/modules/oauth/oauth.controller.ts +++ b/src/modules/oauth/oauth.controller.ts @@ -89,6 +89,19 @@ export class OauthController { return await this.callback(req, 'mailchimp'); } + @Get('salesforce') + @UseGuards(AuthGuard('salesforce')) + async oauthSalesforce() { + return true; + } + + @Get('salesforce/callback') + @UseGuards(AuthGuard('salesforce')) + @Redirect() + async oauthSalesforceCallback(@Req() req) { + return await this.callback(req, 'salesforce'); + } + async callback(@Req() req: Request, plugin) { const { error, state: customer_id } = req.query; diff --git a/src/modules/oauth/passport-strategies/facebook-strategy.ts b/src/modules/oauth/passport-strategies/facebook-strategy.ts index f8e5ec5..8829824 100644 --- a/src/modules/oauth/passport-strategies/facebook-strategy.ts +++ b/src/modules/oauth/passport-strategies/facebook-strategy.ts @@ -1,12 +1,15 @@ import { Profile, Strategy, - StrategyOptionWithRequest, + StrategyOption, VerifyFunction, } from 'passport-facebook'; import { PassportStrategy } from '@nestjs/passport'; import { Inject, Injectable } from '@nestjs/common'; import { OauthSecrets } from 'src/utils/OauthSecrets'; +import { Request } from 'express'; +import { ParamsDictionary } from 'express-serve-static-core'; +import { ParsedQs } from 'qs'; @Injectable() export class FacebookStrategy extends PassportStrategy(Strategy) { @@ -14,19 +17,29 @@ export class FacebookStrategy extends PassportStrategy(Strategy) { @Inject('OAUTH_SECRETS') private readonly oauthSecrets: OauthSecrets, ) { - const options: StrategyOptionWithRequest = { + const options: StrategyOption = { clientID: oauthSecrets.facebook.client_id, clientSecret: oauthSecrets.facebook.client_secret, callbackURL: oauthSecrets.facebook.redirect_uri, - passReqToCallback: true, }; const verify: VerifyFunction = ( accessToken: string, refreshToken: string, profile: Profile, done: (error: any, user?: any, info?: any) => void, - ) => done(null, profile, { accessToken, refreshToken }); + ) => { + return done(null, profile, { accessToken, refreshToken }); + }; super(options, verify); } + authenticate( + req: Request>, + options?: any, + ): void { + const { customer_id } = req.query; + options.state = customer_id; + options.scope = 'ads_read ads_management'; + super.authenticate(req, options); + } } diff --git a/src/modules/oauth/passport-strategies/hubspot-strategy.ts b/src/modules/oauth/passport-strategies/hubspot-strategy.ts index 2e33567..fe752ec 100644 --- a/src/modules/oauth/passport-strategies/hubspot-strategy.ts +++ b/src/modules/oauth/passport-strategies/hubspot-strategy.ts @@ -14,7 +14,6 @@ export class HubspotStrategy extends PassportStrategy(Strategy) { clientID: oauthSecrets.hubspot.client_id, clientSecret: oauthSecrets.hubspot.client_secret, callbackURL: oauthSecrets.hubspot.redirect_uri, - redirectUri: oauthSecrets.hubspot.redirect_uri, scope: 'automation business-intelligence oauth forms integration-sync sales-email-read crm.lists.read crm.objects.contacts.read crm.schemas.contacts.read crm.objects.companies.read crm.objects.deals.read crm.schemas.companies.read crm.schemas.deals.read crm.objects.owners.read crm.objects.quotes.read crm.schemas.quotes.read crm.objects.line_items.read crm.schemas.line_items.read', passReqToCallback: true, diff --git a/src/modules/oauth/passport-strategies/salesforce-strategy.ts b/src/modules/oauth/passport-strategies/salesforce-strategy.ts new file mode 100644 index 0000000..e47a462 --- /dev/null +++ b/src/modules/oauth/passport-strategies/salesforce-strategy.ts @@ -0,0 +1,47 @@ +import { Strategy } from 'passport-forcedotcom'; +import { PassportStrategy } from '@nestjs/passport'; +import { Inject, Injectable } from '@nestjs/common'; +import { OauthSecrets } from 'src/utils/OauthSecrets'; + +@Injectable() +export class SalesforceStrategy extends PassportStrategy( + Strategy, + 'salesforce', +) { + constructor( + @Inject('OAUTH_SECRETS') + private readonly oauthSecrets: OauthSecrets, + ) { + const options = { + clientID: oauthSecrets.salesforce.client_id, + clientSecret: oauthSecrets.salesforce.client_secret, + callbackURL: oauthSecrets.salesforce.redirect_uri, + scope: [ + 'wave_api', + 'chatter_api', + 'chatbot_api', + 'cdp_ingest_api', + 'cdp_profile_api', + 'api', + 'pardot_api', + 'cdp_segment_api', + 'cdp_query_api', + 'refresh_token', + 'offline_access', + ], + }; + const verify = ({ params }, refreshToken: string, profile: any, done) => { + return done(null, profile, { + accessToken: params.access_token, + refreshToken, + }); + }; + super(options, verify); + } + + authenticate(req, options) { + const { customer_id } = req.query; + options.state = customer_id; + super.authenticate(req, options); + } +} diff --git a/src/utils/OauthSecrets.ts b/src/utils/OauthSecrets.ts index a5786fa..d882c76 100644 --- a/src/utils/OauthSecrets.ts +++ b/src/utils/OauthSecrets.ts @@ -14,6 +14,7 @@ export class OauthSecrets { google = new OauthSecretsObject(); mailchimp = new OauthSecretsObject(); facebook = new OauthSecretsObject(); + salesforce = new OauthSecretsObject(); } export async function getOauthSecrets() { const secrets = new OauthSecrets();