diff --git a/apps/api/src/app/controllers/oauth.controller.ts b/apps/api/src/app/controllers/oauth.controller.ts index 58b0e6a5..0e9250e7 100644 --- a/apps/api/src/app/controllers/oauth.controller.ts +++ b/apps/api/src/app/controllers/oauth.controller.ts @@ -15,6 +15,10 @@ export const routeDefinition = { validators: { query: z.object({ loginUrl: z.string().min(1), + addLoginParam: z + .enum(['true', 'false']) + .nullish() + .transform((val) => val === 'true'), }), hasSourceOrg: false, }, @@ -34,8 +38,8 @@ export const routeDefinition = { * @param res */ const salesforceOauthInitAuth = createRoute(routeDefinition.salesforceOauthInitAuth.validators, async ({ query }, req, res, next) => { - const loginUrl = query.loginUrl; - const { authorizationUrl, code_verifier, nonce, state } = oauthService.salesforceOauthInit(loginUrl); + const { loginUrl, addLoginParam } = query; + const { authorizationUrl, code_verifier, nonce, state } = oauthService.salesforceOauthInit(loginUrl, { addLoginParam }); req.session.orgAuth = { code_verifier, nonce, state, loginUrl }; res.redirect(authorizationUrl); }); diff --git a/apps/api/src/app/services/oauth.service.ts b/apps/api/src/app/services/oauth.service.ts index 9a399dd9..ce1ee05e 100644 --- a/apps/api/src/app/services/oauth.service.ts +++ b/apps/api/src/app/services/oauth.service.ts @@ -1,6 +1,6 @@ import { ENV } from '@jetstream/api-config'; import { SalesforceUserInfo } from '@jetstream/types'; -import { CallbackParamsType, Issuer, generators } from 'openid-client'; +import { AuthorizationParameters, CallbackParamsType, Issuer, generators } from 'openid-client'; function getSalesforceAuthClient(loginUrl: string) { const { Client } = new Issuer({ @@ -29,7 +29,10 @@ function getSalesforceAuthClient(loginUrl: string) { /** * Get redirectUrl and authData for Salesforce OAuth */ -export function salesforceOauthInit(loginUrl: string, loginHint?: string) { +export function salesforceOauthInit( + loginUrl: string, + { loginHint, addLoginParam = false }: { addLoginParam?: boolean; loginHint?: string } = {} +) { // https://login.salesforce.com/.well-known/openid-configuration const nonce = generators.nonce(); @@ -39,7 +42,7 @@ export function salesforceOauthInit(loginUrl: string, loginHint?: string) { const authClient = getSalesforceAuthClient(loginUrl); - const authorizationUrl = authClient.authorizationUrl({ + const params: AuthorizationParameters = { code_challenge_method: 'S256', code_challenge, login_hint: loginHint, @@ -47,7 +50,13 @@ export function salesforceOauthInit(loginUrl: string, loginHint?: string) { prompt: 'login', scope: 'api web refresh_token', state, - }); + }; + + if (addLoginParam) { + params['login'] = 'true'; + } + + const authorizationUrl = authClient.authorizationUrl(params); return { code_verifier, nonce, state, authorizationUrl }; } diff --git a/libs/shared/ui-core/src/orgs/AddOrg.tsx b/libs/shared/ui-core/src/orgs/AddOrg.tsx index ec23b088..0d9bd719 100644 --- a/libs/shared/ui-core/src/orgs/AddOrg.tsx +++ b/libs/shared/ui-core/src/orgs/AddOrg.tsx @@ -1,6 +1,6 @@ import { addOrg } from '@jetstream/shared/ui-utils'; import { SalesforceOrgUi } from '@jetstream/types'; -import { Grid, GridCol, Icon, Input, Popover, PopoverRef, Radio, RadioGroup } from '@jetstream/ui'; +import { Checkbox, CheckboxToggle, Grid, GridCol, Icon, Input, Popover, PopoverRef, Radio, RadioGroup } from '@jetstream/ui'; import classNames from 'classnames'; import { FunctionComponent, useEffect, useRef, useState } from 'react'; import { useRecoilState } from 'recoil'; @@ -34,6 +34,8 @@ export const AddOrg: FunctionComponent = ({ className, label = 'Add const [orgType, setOrgType] = useState('prod'); const [customUrl, setCustomUrl] = useState(''); const [loginUrl, setLoginUrl] = useState(null); + const [advancedOptionsEnabled, setAdvancedOptionsEnabled] = useState(false); + const [addLoginTrue, setAddLoginTrue] = useState(false); const [applicationState] = useRecoilState(applicationCookieState); useEffect(() => { @@ -46,19 +48,30 @@ export const AddOrg: FunctionComponent = ({ className, label = 'Add setLoginUrl(url); }, [orgType, customUrl]); - // FIXME: we should have a way to know what org was being "fixed" and always replace it in the DB and here function handleAddOrg() { loginUrl && - addOrg({ serverUrl: applicationState.serverUrl, loginUrl }, (addedOrg: SalesforceOrgUi) => { - popoverRef.current?.close(); - onAddOrg(addedOrg, true); - }); + addOrg( + { serverUrl: applicationState.serverUrl, loginUrl, addLoginTrue: advancedOptionsEnabled && addLoginTrue }, + (addedOrg: SalesforceOrgUi) => { + popoverRef.current?.close(); + onAddOrg(addedOrg, true); + } + ); + } + + function handleReset() { + setOrgType('prod'); + setCustomUrl(''); + setLoginUrl(null); + setAdvancedOptionsEnabled(false); + setAddLoginTrue(false); } return ( // TODO: figure out way to close this once an org is added - this was fixed, but it caused the component to fully re-render each time! !isOpen && handleReset()} // placement="bottom-end" header={
@@ -116,6 +129,24 @@ export const AddOrg: FunctionComponent = ({ className, label = 'Add /> )} +
+ + {advancedOptionsEnabled && ( + + )} +
} footer={ diff --git a/libs/shared/ui-utils/src/lib/shared-ui-utils.ts b/libs/shared/ui-utils/src/lib/shared-ui-utils.ts index 9d250340..deee9c16 100644 --- a/libs/shared/ui-utils/src/lib/shared-ui-utils.ts +++ b/libs/shared/ui-utils/src/lib/shared-ui-utils.ts @@ -931,14 +931,17 @@ function handleWindowEvent(event: MessageEvent) { } } -export function addOrg(options: { serverUrl: string; loginUrl: string }, callback: (org: SalesforceOrgUi) => void) { - const { serverUrl, loginUrl } = options; +export function addOrg(options: { serverUrl: string; loginUrl: string; addLoginTrue?: boolean }, callback: (org: SalesforceOrgUi) => void) { + const { serverUrl, loginUrl, addLoginTrue } = options; addOrgCallbackFn = callback; window.removeEventListener('message', handleWindowEvent); const strWindowFeatures = 'toolbar=no, menubar=no, width=1025, height=700'; - let url = `${serverUrl}/oauth/sfdc/auth?`; - url += `loginUrl=${encodeURIComponent(loginUrl)}`; - url += `&clientUrl=${encodeURIComponent(document.location.origin)}`; + const url = new URL(`${serverUrl}/oauth/sfdc/auth`); + url.searchParams.set('loginUrl', loginUrl); + url.searchParams.set('clientUrl', document.location.origin); + if (addLoginTrue) { + url.searchParams.set('addLoginParam', 'true'); + } windowRef = window.open(url, 'Add Salesforce Org', strWindowFeatures); window.addEventListener('message', handleWindowEvent, false); }