Skip to content

Commit

Permalink
SamlConfig: defer the initial logic to _processInitialRequest
Browse files Browse the repository at this point in the history
Now we use the results of the utility function to determine what to do
with the SAML request. This in particular will now handle
IdP-initiated requests that are missing a `RelayState`.
  • Loading branch information
jordigh committed Feb 3, 2025
1 parent a882901 commit f279a52
Showing 1 changed file with 10 additions and 16 deletions.
26 changes: 10 additions & 16 deletions app/server/lib/SamlConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,17 +161,10 @@ export class SamlConfig {

// Assert endpoint for when the login completes as POST.
app.post("/saml/assert", express.urlencoded({extended: true}), expressWrap(async (req, res, next) => {
const relayState: string = req.body.RelayState;
if (!relayState) { throw new Error('Login or logout failed to complete'); }
const permitStore = this._gristServer.getExternalPermitStore();
const state = await permitStore.getPermit(relayState);
if (!state) { throw new Error('Login or logout is stale'); }
await permitStore.removePermit(relayState);

const redirectUrl = state.url!;
const samlResponse: any = await fromCallback((cb) => sp.post_assert(idp, {request_body: req.body}, cb));

if (state.action === 'login') {
const {redirectUrl, sessionId, unsolicited, action} = await this._processInitialRequest(req);
if (action === 'login') {
const samlResponse: saml2.SAMLAssertResponse =
await fromCallback((cb) => sp.post_assert(idp, { request_body: req.body }, cb));
const samlUser = samlResponse.user;
if (!samlUser || !samlUser.name_id) {
log.warn(`SamlConfig: bad SAML response: ${JSON.stringify(samlUser)}`);
Expand All @@ -181,19 +174,20 @@ export class SamlConfig {
// An example IdP response is at https://github.com/Clever/saml2#assert_response. Saml2-js
// maps some standard attributes as user.given_name, user.surname, which we use if
// available. Otherwise we use user.attributes which has the form {Name: [Value]}.
const fname = samlUser.given_name || samlUser.attributes.FirstName || '';
const lname = samlUser.surname || samlUser.attributes.LastName || '';
const email = samlUser.email || samlUser.name_id;
const fname = (samlUser as any).given_name || samlUser.attributes?.FirstName || '';
const lname = (samlUser as any).surname || samlUser.attributes?.LastName || '';
const email = (samlUser as any).email || samlUser.name_id;
const profile = {
email,
name: `${fname} ${lname}`.trim(),
};

const samlSessionIndex = samlUser.session_index;
const samlNameId = samlUser.name_id;
log.info(`SamlConfig: got SAML response for ${profile.email} (${profile.name}) redirecting to ${redirectUrl}`);
log.info(`SamlConfig: got SAML response${unsolicited ? ' (unsolicited)' : ''} for ` +
`${profile.email} (${profile.name}) redirecting to ${redirectUrl}`);

const scopedSession = sessions.getOrCreateSessionFromRequest(req, {sessionId: state.sessionId});
const scopedSession = sessions.getOrCreateSessionFromRequest(req, {sessionId});
await scopedSession.operateOnScopedSession(req, async (user) => Object.assign(user, {
profile,
samlSessionIndex,
Expand Down

0 comments on commit f279a52

Please sign in to comment.