Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions app/sagas/__tests__/deepLinking.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -585,3 +585,61 @@ describe('deepLinking saga — handleOAuth dedup guard', () => {
});
});
});

describe('deepLinking saga — handleSaml', () => {
beforeEach(() => {
jest.mocked(loginOAuthOrSso).mockReset();
jest.mocked(loginOAuthOrSso).mockResolvedValue(undefined as any);
});

it('redeems the SAML credential token through the regular saml login', async () => {
const store = setupStore();

store.dispatch(deepLinkingOpen({ type: 'saml', host: HOST, credentialToken: 'saml-fresh-A' } as any));
await flushSagaMicrotasks();
await flushSagaMicrotasks();

expect(jest.mocked(loginOAuthOrSso)).toHaveBeenCalledTimes(1);
expect(jest.mocked(loginOAuthOrSso)).toHaveBeenCalledWith({ saml: true, credentialToken: 'saml-fresh-A' });
});

it('does not call loginOAuthOrSso when the credentialToken is missing', async () => {
const store = setupStore();

store.dispatch(deepLinkingOpen({ type: 'saml', host: HOST } as any));
await flushSagaMicrotasks();
await flushSagaMicrotasks();

expect(jest.mocked(loginOAuthOrSso)).not.toHaveBeenCalled();
});

it('does not redeem the same SAML credentialToken twice', async () => {
const store = setupStore();

store.dispatch(deepLinkingOpen({ type: 'saml', host: HOST, credentialToken: 'saml-dup-B' } as any));
await flushSagaMicrotasks();
await flushSagaMicrotasks();

// The credential token is single use on the server, so a replayed deep link must be suppressed.
store.dispatch(deepLinkingOpen({ type: 'saml', host: HOST, credentialToken: 'saml-dup-B' } as any));
await flushSagaMicrotasks();
await flushSagaMicrotasks();

expect(jest.mocked(loginOAuthOrSso)).toHaveBeenCalledTimes(1);
});

it('redeems a different SAML credentialToken after a previous one was consumed', async () => {
const store = setupStore();

store.dispatch(deepLinkingOpen({ type: 'saml', host: HOST, credentialToken: 'saml-first-C' } as any));
await flushSagaMicrotasks();
await flushSagaMicrotasks();

store.dispatch(deepLinkingOpen({ type: 'saml', host: HOST, credentialToken: 'saml-second-C' } as any));
await flushSagaMicrotasks();
await flushSagaMicrotasks();

expect(jest.mocked(loginOAuthOrSso)).toHaveBeenCalledTimes(2);
expect(jest.mocked(loginOAuthOrSso)).toHaveBeenNthCalledWith(2, { saml: true, credentialToken: 'saml-second-C' });
});
});
19 changes: 19 additions & 0 deletions app/sagas/deepLinking.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,21 @@ const handleOAuth = function* handleOAuth({ params }) {
}
};

let consumedSamlToken;

const handleSaml = function* handleSaml({ params }) {
const { credentialToken } = params;
if (!credentialToken || credentialToken === consumedSamlToken) {
return;
}
consumedSamlToken = credentialToken;
Comment thread
yash-rajpal marked this conversation as resolved.
try {
yield loginOAuthOrSso({ saml: true, credentialToken });
} catch (e) {
log(e);
}
};

const handleShareExtension = function* handleOpen({ params }) {
const server = UserPreferences.getString(CURRENT_SERVER);
const user = UserPreferences.getString(`${TOKEN_KEY}-${server}`);
Expand Down Expand Up @@ -175,6 +190,10 @@ const handleOpen = function* handleOpen({ params }) {
yield handleOAuth({ params });
return;
}
if (params.type === 'saml') {
yield handleSaml({ params });
return;
}

// If there's no host on the deep link params and the app is opened, just call appInit()
let { host } = params;
Expand Down
Loading