Add support for OpenID Connect implicit authentication flow.#42069
Add support for OpenID Connect implicit authentication flow.#42069azasypkin merged 7 commits intoelastic:masterfrom
Conversation
|
Pinging @elastic/kibana-security |
e30d427 to
653d4e8
Compare
There was a problem hiding this comment.
note: that the main thing I need to get rid of (hopefully) before this PR is ready for review
There was a problem hiding this comment.
Alternatively we could just render tiny HTML page outside of Kibana context with inline "nonced" onliner script.... That way we won't have rison that garbles URL fragment right now.
There was a problem hiding this comment.
note: I was thinking about using POST here, but couldn't convince myself enough since all these args are already came through URL fragment 🤷♂️ Would be glad to hear any thoughts on this.
There was a problem hiding this comment.
note: actually, it probably makes sense to have a dedicated endpoint for implicit flow, haven't thought thoroughly about this.
There was a problem hiding this comment.
I'll defer to your opinion on this one. I don't think the current implementation is un-readable, but if you think it makes it easier to have a separate end-point, that seems reasonable also.
There was a problem hiding this comment.
I don't think the current implementation is un-readable
Great, I don't have strong opinion on that. Let's keep it as is for now then and split whenever we feel we really need to split.
There was a problem hiding this comment.
note: we should find a better way of solving the underlying problem, but not in this PR likely.
653d4e8 to
f82980a
Compare
There was a problem hiding this comment.
note: using CSP here feels like an overkill here, but it doesn't hurt to have it I think.
There was a problem hiding this comment.
Do we need to do anything about server.customResponseHeaders explicitly, or does the global hapi configuration apply automatically?
I've been trying to think of potential issues that we get from rendering all of the HTML ourselves, but I haven't been able to think of any.
There was a problem hiding this comment.
Do we need to do anything about server.customResponseHeaders explicitly, or does the global hapi configuration apply automatically?
Hapi's onPreResponse hooks do everything for us "automagically". I actually was surprised that CSP isn't done via onPreResponse too... But I admit I don't know much about all the variables we had to account for when we were implementing it.
There was a problem hiding this comment.
note: we can just pass entire window.location.href instead, it will also work and make this code a tad easier. Now when I look at this I can't think of any reason to not use entire window.location.href 🙈 What do you think?
There was a problem hiding this comment.
That seems easy enough to me, and potentially less fragile.
There was a problem hiding this comment.
Yep, will switch to window.location.href.
f82980a to
a47f882
Compare
There was a problem hiding this comment.
note: It seems it'd be better to include <!DOCTYPE html> at least, will do.
There was a problem hiding this comment.
The w3c validator makes it seem like we should include <title>foo</title> as well: https://validator.w3.org/nu/#textarea
Borrowing from https://mathiasbynens.be/notes/minimal-html who found whatwg/html@5c7cf94, the title can be omitted when dealing with e-mails, but it sounds like we should be using it here.
There was a problem hiding this comment.
Huh, would never thought we may need title :) Let me read through this, thanks for sharing!
There was a problem hiding this comment.
Yeah, the spec agrees with you: https://html.spec.whatwg.org/multipage/semantics.html#the-title-element: Neither tag is omissible. 🙂
kobelb
left a comment
There was a problem hiding this comment.
I think the approach is fine. Perhaps long-term we can add the ability to render custom HTML content without all of the other "cruft" in the core application, but what we have now seems reasonable.
There was a problem hiding this comment.
I'll defer to your opinion on this one. I don't think the current implementation is un-readable, but if you think it makes it easier to have a separate end-point, that seems reasonable also.
++, that's what we'll need to have for sure - custom HTML with only the absolutely necessary stuff (CSP, xsrf, HSTS, CORS etc. etc.). Not a blocker for now, but at some point managing this manually and outside of core's control in every such case will hurt us. |
a47f882 to
dcfabee
Compare
This comment has been minimized.
This comment has been minimized.
dcfabee to
776cda2
Compare
💚 Build Succeeded |
|
@kobelb this PR should be ready for review whenever you have time 🙂 Thanks! |
| async handler(request, h) { | ||
| try { | ||
| const query = request.query || {}; | ||
| const payload = request.payload || {}; |
There was a problem hiding this comment.
It predates this PR, but should we considering add payload validation to this route?
There was a problem hiding this comment.
I don't know to be honest, I was even planning to eventually get rid of query validation as well since we (Kibana) neither parse nor try to interpret them and just blindly pass to Elasticsearch anyway (and we allow unknown parameters as well). If it happens so that these args are malformed, imo, it's better to allow ES to parse them and log more detailed error in their log than having this validation in Kibana.
What do you think?
There was a problem hiding this comment.
What do you think?
We likely get little benefit from doing it ourselves because we end up passing it directly to Elasticsearch. It could potentially help us if we received the wrong "data-type" for a specific parameter. So if we received a payload.iss which was an Object as opposed to a string, we could potentially have more informative error messages instead of something breaking in a somewhat obscure manner downstream.
There was a problem hiding this comment.
I'm fine with it either way, I defer to your opinion :)
There was a problem hiding this comment.
I've just tried to add a payload validation and was hit by this limitation 😢 That means we can't have payload validation if route defines multiple methods and some of them don't support payload. The only alternative is to split this route into two very similar ones. Since you're fine with the current implementation I'll keep it simple and leave it as is for now. Let's get back to it when we migrate this route to NP (I leave a code comment).
I'll add a validation for authenticationResponseURI query parameter as you suggested below though.
|
|
||
| const nonce = await generateCSPNonce(); | ||
| const cspRulesHeader = createCSPRuleString(legacyConfig.get('csp.rules'), nonce); | ||
| return h.response(` |
There was a problem hiding this comment.
This is likely a naive question. When reading through the OIDC spec, section 3.2.2.5 states
When using the Implicit Flow, all response parameters are added to the fragment component of the Redirection URI, as specified in OAuth 2.0 Multiple Response Type Encoding Practices [OAuth.Responses], unless a different Response Mode was specified.
And section 2.1 of the OAuth2.0 Response Type Encoding Practices makes it appear that we could potentially use the query response mode https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#rfc.section.2.1 to get around needing to use this page to parse the fragment.
There was a problem hiding this comment.
This is likely a naive question.
And what is the question? 🙂 So, I assume you're asking why we can't just say OP and RP (ES) to use response_mode=query for the implicit flow. Per OAuth 2.0 Multiple Response Type Encoding Practices it seems that query is forbidden for id_token and token response types:
The default Response Mode for this Response Type is the fragment encoding and the query encoding MUST NOT be used.
cc @jkakavas
There was a problem hiding this comment.
Okta seems to obey this rule too https://developer.okta.com/docs/reference/api/oidc/#request-parameters
There was a problem hiding this comment.
Ah, thanks for the clarification. I stopped reading the specs too soon.
There was a problem hiding this comment.
fragment is used on purpose so that the access token and the id token are not sent as part of any requests to a back-end server ( The assumption was that the implicit flow would be best used by javascript clients running in the browser ) and thus don't get leaked in logs or be available more than needed on the wire. We (and many other clients) have the validation on the server side so we end up making a request with it to the kibana server eitherway so there goes that.. In any case as @azasypkin mentioned we're not allowed to use anything else for response_type
| // fragment and put it into `authenticationResponseURI` query string parameter for this endpoint. See more details | ||
| // at https://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth | ||
| let loginAttempt; | ||
| if (query.authenticationResponseURI) { |
There was a problem hiding this comment.
Should we add authenticationResponseURI to the route's query validation?
There was a problem hiding this comment.
Replied above, but let me know if you think it makes sense to treat our own authenticationResponseURI differently.
💔 Build Failed |
💚 Build Succeeded |
💚 Build Succeeded |
|
7.x/7.4.0: 4c396f8 |
💚 Build Succeeded |
Kibana OIDC authentication provider currently supports Authentication using the Authorization Code Flow only. The main reason is that it is a recommended authentication flow and should be used by default whenever it's possible.
The authorization code flow implies that Elasticsearch has access to the Token endpoint of the OpenID Connect Provider (OP), but it's neither always possible nor desired. So in the scenarios like this having support for another, so called Implicit Flow would be beneficial.
This PR adds support for the OpenID Connect Authentication using the Implicit Flow. The tricky part here is that OP passes arguments required for the authentication using the implicit flow via URL fragment. That means we can't directly receive these arguments on the server side and have to provide a client-side proxy page that UA will be forwarded to and where we'll be extracting URL fragment and only then sending them to the Kibana Server OIDC endpoint to perform actual authentication.
Since this PR modifies OIDC authentication provider we should consider enabling OIDC integration tests first, that's blocked by #36959(tests were re-enabled).Notes to myself:
oidc/implicitURL into ES Docs (Document support of OIDC Implicit flow in Kibana. elasticsearch#45693)oidc/implicitURL into Cloud Docs (only when this PR is merged/backported)How to test
client_idandclient_secret):$ yarn es snapshot --license trial \ -E xpack.security.authc.token.enabled=true \ -E xpack.security.authc.realms.native.native1.order=0 \ -E xpack.security.authc.realms.oidc.oidc1.order=1 \ -E xpack.security.authc.realms.oidc.oidc1.rp.client_id=xxxx \ -E xpack.security.authc.realms.oidc.oidc1.rp.client_secret=xxxx \ -E xpack.security.authc.realms.oidc.oidc1.rp.response_type="id_token token" \ -E xpack.security.authc.realms.oidc.oidc1.rp.requested_scopes=profile \ -E xpack.security.authc.realms.oidc.oidc1.rp.redirect_uri=https://localhost:5601/api/security/v1/oidc/implicit \ -E xpack.security.authc.realms.oidc.oidc1.op.authorization_endpoint=https://elastic-dev.auth0.com/authorize \ -E xpack.security.authc.realms.oidc.oidc1.op.token_endpoint=https://elastic-dev.auth0.com/oauth/token \ -E xpack.security.authc.realms.oidc.oidc1.op.userinfo_endpoint=https://elastic-dev.auth0.com/userinfo \ -E xpack.security.authc.realms.oidc.oidc1.op.issuer=https://elastic-dev.auth0.com/ \ -E xpack.security.authc.realms.oidc.oidc1.op.jwkset_path=https://elastic-dev.auth0.com/.well-known/jwks.json \ -E xpack.security.authc.realms.oidc.oidc1.claims.principal=sub \ -E xpack.security.authc.realms.oidc.oidc1.claims.groups=https://myapp.example.com/kbn_groups \ -E xpack.security.authc.realms.oidc.oidc1.claims.name=namexpack.security.authc.providers: [oidc, basic]Blocked by: #36959Fixes: #41984
"Release Note: Adding support for the OpenID Connect Authentication using the Implicit Flow"
/cc @jkakavas