Skip to content

Commit

Permalink
Allow users to add the login=true param when going through oauth
Browse files Browse the repository at this point in the history
This allows bypassing SSO if configured by an org administrator
  • Loading branch information
paustint committed Aug 24, 2024
1 parent c880ac1 commit bf3739c
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 17 deletions.
8 changes: 6 additions & 2 deletions apps/api/src/app/controllers/oauth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand All @@ -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);
});
Expand Down
17 changes: 13 additions & 4 deletions apps/api/src/app/services/oauth.service.ts
Original file line number Diff line number Diff line change
@@ -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({
Expand Down Expand Up @@ -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();
Expand All @@ -39,15 +42,21 @@ 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,
nonce,
prompt: 'login',
scope: 'api web refresh_token',
state,
});
};

if (addLoginParam) {
params['login'] = 'true';
}

const authorizationUrl = authClient.authorizationUrl(params);

return { code_verifier, nonce, state, authorizationUrl };
}
Expand Down
43 changes: 37 additions & 6 deletions libs/shared/ui-core/src/orgs/AddOrg.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -34,6 +34,8 @@ export const AddOrg: FunctionComponent<AddOrgProps> = ({ className, label = 'Add
const [orgType, setOrgType] = useState<OrgType>('prod');
const [customUrl, setCustomUrl] = useState<string>('');
const [loginUrl, setLoginUrl] = useState<string | null>(null);
const [advancedOptionsEnabled, setAdvancedOptionsEnabled] = useState(false);
const [addLoginTrue, setAddLoginTrue] = useState(false);
const [applicationState] = useRecoilState(applicationCookieState);

useEffect(() => {
Expand All @@ -46,19 +48,30 @@ export const AddOrg: FunctionComponent<AddOrgProps> = ({ 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!
<Popover
ref={popoverRef}
onChange={(isOpen) => !isOpen && handleReset()}
// placement="bottom-end"
header={
<header className="slds-popover__header">
Expand Down Expand Up @@ -116,6 +129,24 @@ export const AddOrg: FunctionComponent<AddOrgProps> = ({ className, label = 'Add
/>
</Input>
)}
<div className="slds-m-top_small">
<CheckboxToggle
id="advanced-settings-toggle"
checked={advancedOptionsEnabled}
label="Advanced"
labelPosition="right"
onChange={setAdvancedOptionsEnabled}
/>
{advancedOptionsEnabled && (
<Checkbox
id="advanced-settings-login-true"
label={`Add "login=true" to url`}
labelHelp="Allows bypassing SSO if your admin has enabled this option."
checked={addLoginTrue}
onChange={setAddLoginTrue}
/>
)}
</div>
</div>
}
footer={
Expand Down
13 changes: 8 additions & 5 deletions libs/shared/ui-utils/src/lib/shared-ui-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down

0 comments on commit bf3739c

Please sign in to comment.