-
-
Notifications
You must be signed in to change notification settings - Fork 277
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support nonce and acr with OIDC + Tests #883
base: main
Are you sure you want to change the base?
Support nonce and acr with OIDC + Tests #883
Conversation
e78e2f3
to
8b90c90
Compare
post_logout_redirect_uri: redirectUrl.href, | ||
state: mreq.session.oidc?.state, | ||
id_token_hint: mreq.session.oidc?.idToken, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to the reviewers: I don't clear either the state
or the idToken
from the session in this method, as I feel like it's more secure to secure the logout if the first attempt fails (the following attempts may succeed).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes sense, but I can't see where this info is cleaned up. Can you point me to that?
Opening the PR for checking whether the tests work in the CI |
b3ca7fe
to
56706b3
Compare
56706b3
to
1bf114a
Compare
eb8331b
to
0115474
Compare
Looks good to me |
763caf6
to
a992efa
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @CamilleLegeron!
Hi @fflorent, sorry for the delay in reviewing this! Dmitry just hasn't had time, and likely won't for a while. @SleepyLeslie has offered to take it on. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @fflorent for the work. I left some thoughts. I am new to these security features so I had to learn before commenting - sorry for the delay!
Also, feel free to point out any mistakes I make and any misunderstandings I have - I've just joined Grist Labs last week and I'm still largely unfamiliar with this codebase.
document.title = t("Signin failed{{suffix}}", {suffix: getPageTitleSuffix(getGristConfig())}); | ||
return pagePanelsError(appModel, t("Signin failed{{suffix}}", {suffix: ''}), [ | ||
cssErrorText(message ?? | ||
t("Failed to login.{{separator}}Please try again or contact the support.", { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"contact support" - no need for "the".
@@ -1849,7 +1849,9 @@ export class FlexServer implements GristServer { | |||
} | |||
|
|||
public resolveLoginSystem() { | |||
return process.env.GRIST_TEST_LOGIN ? getTestLoginSystem() : (this._getLoginSystem?.() || getLoginSystem()); | |||
return isAffirmative(process.env.GRIST_TEST_LOGIN) ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not call allowTestLogin()
?
.omitBy(_.isFunction) | ||
.mapValues((value, key) => { | ||
const showValueInClear = ['token_type', 'expires_in', 'expires_at', 'scope'].includes(key); | ||
return showValueInClear ? value : 'REDACTED'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just show these 4 values?
res.redirect(targetUrl ?? '/'); | ||
} catch (err) { | ||
log.error(`OIDC callback failed: ${err.stack}`); | ||
if (Object.prototype.hasOwnProperty.call(err, 'response')) { | ||
log.error(`Response received: ${err.response?.body ?? err.response}`); | ||
} | ||
// Delete the session data even if the login failed. | ||
// This way, we prevent several login attempts. | ||
// | ||
// Also session deletion must be done before sending the response. | ||
delete mreq.session.oidc; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comments here should be updated. It's no longer "even if the login failed" - if I understand correctly, OIDC-related session data now needs to be preserved until logout if login succeeds.
I don't quite understand the "we prevent several login attempts" part - can you explain that a bit more?
// Delete the session data even if the login failed. | ||
// This way, we prevent several login attempts. | ||
// | ||
// Also session deletion must be done before sending the response. | ||
delete mreq.session.oidc; | ||
res.status(500).send(`OIDC callback failed.`); | ||
await this._sendAppPage(req, res, { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice UX improvement.
{ | ||
itMsg: 'should fulfill when the end_session_endpoint is not known ' + | ||
'and GRIST_OIDC_IDP_SKIP_END_SESSION_ENDPOINT=true', | ||
end_session_endpoint: undefined, | ||
env: { | ||
GRIST_OIDC_IDP_SKIP_END_SESSION_ENDPOINT: 'true' | ||
} | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Identical tests?
const promise = OIDCConfigStubbed.buildWithStub(client.asClient()); | ||
if (ctx.errorMsg) { | ||
await assert.isRejected(promise, ctx.errorMsg); | ||
assert.isFalse(logInfoStub.calledOnce); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this test making sure log.info(`OIDCConfig: initialized with issuer ${issuerUrl}`)
wasn't called?
assert.isFalse(config.supportsProtection("STATE")); | ||
}); | ||
|
||
it('if omitted, should defaults to "STATE,PKCE"', async function () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: remove "s" - "should default to"
} | ||
}, | ||
expectedErrorMsg: /Login is stale/, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it's helpful for long-term maintenance to explicitly specify GRIST_OIDC_IDP_ENABLED_PROTECTIONS
here?
}); | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very comprehensive testing. Thanks!
By the way, can you provide some examples for these IdPs? |
Context
Proposed solution
GRIST_OIDC_IDP_ENABLED_PROTECTIONS
variable who can contain comma-separated values with either:STATE
,NONCE
andPKCE
, and defaults toSTATE,PKCE
;GRIST_OIDC_IDP_ACR_VALUES
variable with space separated values;