-
Notifications
You must be signed in to change notification settings - Fork 11
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
[EVAKA-HOTFIX] Support Single Logout w/o 3rd party cookies #738
Conversation
8d97975
to
39017b2
Compare
83d6d6b
to
5fe222c
Compare
3f36d94
to
f20406e
Compare
req, | ||
res, | ||
config.sessionType, | ||
profile?.nameID && createLogoutToken(profile.nameID, profile.sessionIndex) |
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.
Replacing this with undefined
should effectively simulate the situation in master
when running the regression tests: if nothing is parsed from the SAML message, only cookies are used (and the tests don't currently care about SameSite
in this context, so evaka.eugw.session
will be available in the "IdP-initiated logout" test case).
- Express still actually uses the same body-parser package/methods behind the scenes but as the library user we should use the official wrapper methods `express.json()` and `express.urlencoded()` - Options stay as-is as the implementations stay as-is
- Allows separate instation of the `SAML` class (from `passport-saml`) used for parsing/verifying SAML messages - Will later be used to parse logout tokens from SAML LogoutRequest messages where cookies aren't available
- No SAML error will ever be present when starting the logout flow from the SP (eVaka itself) -> don't waste time attempting to parse SAML errors and clarify the error description
- Many browsers are starting to disable 3rd party cookies by default (even SameSite None) which breaks Single Logout with passport-saml that currently only supports logging out with a cookie - Browser details: https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ and https://blog.google/products/chrome/more-intuitive-privacy-and-security-controls-chrome/ - passport-saml issues: node-saml/passport-saml#419 and node-saml/passport-saml#221 - This is actually a real security issue as passport-saml will give a successful looking response from `passport.authenticate()` in the logout callback which results in IdPs showing "successfully logged out" messages to the user -- even though the user is still fully logged in! - This also causes an usability issue when the user tries to initate SLO from another application, fails to end the eVaka session, attempts a logout from eVaka and gets an ugly JSON error message as a response when APIGW attempts to make a LogoutRequest to the IdP that already ended the session -> will be fixed separately - Other systems like Shibboleth get around the 3rd party cookie issue with Single Logout by not utilizing cookies at all for this but instead use the SAML nameID (and sessionIndex) properties presented in all SAML LogoutRequest messages - Source: https://wiki.shibboleth.net/confluence/display/DEV/IdP+SameSite+Testing#IdPSameSiteTesting-SameSiteandSingleLogout - When logouts are always only done through SAML there's no need for the logout cookie itself but the idea is actually useful as an effective "secondary index" for Redis: - By storing a nameID + sessionIndex keyed item pointing to the session ID, we effectively create a second index that can be used with just the SAML LogoutRequest's nameID and sessionIndex properties
- Not relevant for e.g. mobile logouts where the request will never have a SAMLRequest query parameter or body -> make it optional - Add debug logging for easier understanding of login/logout flow events
- Just for easier debugging and makes key collisions slightly less likely
- Instead of re-generating the token from session details (if no SAMLRequest Profile was available), use the logoutToken's value directly - Trying to avoid having too much SAML-specific stuff in the session implementation - Also unify code style for generating session and logout token Redis keys
- Instead of having anything specifically SAML-related in the session module, take the logout token in as just a string and move the parsing and nameID+sessionIndex logic into more SAML-specific modules - This also allows dropping unnecessary arguments from all other routes than logout callback - But requires the login callback route to have logic for generating the token initially before saving it
- Disallow floating Promises to prevent unexpected order of operations in async functions where an async function is called but not awaited - Already enabled in frontend projects
- Enables temporarily removing and re-adding the same cookies - Useful for simulating missing cookies e.g. in 3rd party SAML request cases - Augment "tough-cookie" types to add missing `sameSiteContext` property for `setCookie` options object - Missing from `@types/tough-cookie@4.0.0` but implemented in library - Extremely relevant for 3rd party cookie testing - Also return the actual Cookie instance from getCookie (all current usage just checks for truthyness/not-undefined which is still valid) -> enables new setCookie usage
- Match frontends config
- For test use only, named accordingly
- To allow testing cases where using the actual full Strategy instead of a dummy strategy is needed, allow overriding the `sfiMock` config with an argument that defaults to the config to not break current usage
- Allows using the tester with non-dummy passport strategies
…-mock - Unfortunately redis-mock currently has an open issue (yeahoffline/redis-mock#135) and an un-merged fix for it (yeahoffline/redis-mock#178) that means it doesn't support multiple arguments (i.e. spread) for the `.del()` method -> to allow using the mock library in tests add support to AsyncRedisClient for using an array of keys instead of argument-per-key and always provide the keys to the underlying redis client as an array instead of using the spread operator
- As passport-saml and SAML authentication as a whole is very deeply integrated into the application, tests for it need to unfortunately set up its own instances of almost everything for the app in order to override important configs - Session store must use Redis as the Single Logout token system relies on it being available (instead of the default MemoryStore for local development) -> need to override client setup to use a in-memory mock-Redis - SAML configuration must use "real" certificates but obviously nothing from a real environment - SAML configuration needs additional adjustments to simplify testing - As simulating real 3rd party cookie situations would be really complex, deleting and re-adding cookies is used to replicate the situation - Also implement a reference test case where cookies are available - Add necessary XML + crypto dependencies for creating and parsing SAML messages - Although passport-saml has the SAML class (with missing official types...), it doesn't really provide useful methods/abstractions -- they go too far into request handling instead of just parsing and generating messages -- so decided to impelement these manually - Based on the wonderful examples from node-saml/passport-saml#419
f20406e
to
dc56a6c
Compare
- Axios eats the stack traces for failed requests -> explicitly expect specific response status codes outside of axios by always validating status to `true`
- Globally mock redis with redis-mock in all Jest tests for APIGW - Allows removal of test-specific logic from apps regarding Redis clients - Merge Suomi.fi SAML configs into one dynamic config that is once again controlled with `config.sfiMock` - No reason to duplicate when fallbacks and "if test then" logic can be integrated into the configs/constants - Still return an empty config object when Suomi.fi mocking is enabled (i.e. local dev, other tests than SAML tests) to prevent fallout from missing required props - Utilize dynamic imports and brute force to temporarily disable Suomi.fi mocking for SAML tests ONLY - Not pretty but allows use of the actual enduser app instance (instead of faking it) and configs - Slightly less brittle against changes in the implementation
d416dbb
to
6a62fcd
Compare
Summary of SLO issue / vulnerability
passport-saml
issues: passport-saml's IdP initiated LogoutRequest handling doesn't always close sessions node-saml/passport-saml#419 and IdP-initiated SLO node-saml/passport-saml#221LogoutRequest
s via the User's browserSummary
Strategy
creationSAML
class (frompassport-saml
) used for parsing SAML messages for SLOpassport-saml
either supported hooking intopassport.authenticate()
before it automatically redirects onSAMLRequest
or if it had separate methods for first parsing the request and then doing everything else (redirects etc.)body-parser
directlyexpress.json()
andexpress.urlencoded()
GatewayTester
to allow further cookie manipulation and non-dummy authentication.editorconfig
forapigw/
(matchesfrontend/.editorconfig
)@typescript/no-floating-promises
rule for ESLint (can't forget toawait
aPromise
, already enabled in frontend projects)Testing instructions
On master version (e.g. staging):
SAMLRequest
query parameter)SAMLResponse
indicating success{"message":"Internal server error"}
+ an alertOn dev (once this branch is deployed):