-
Notifications
You must be signed in to change notification settings - Fork 25k
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
Oidc additional client auth types #58708
Oidc additional client auth types #58708
Conversation
The OpenID Connect specification defines a number of ways for a client (RP) to authenticate itself to the OP when accessing the Token Endpoint. We currently only support `client_secret_basic`. This change introduces support for 2 additional authentication methods, namely `client_secret_post` (where the client credentials are passed in the body of the POST request to the OP) and `client_secret_jwt` where the client constructs a JWT and signs it using the the client secret as a key. Support for the above, and especially `client_secret_jwt` in our integration tests meant that the OP we use ( Connect2id server ) should be able to validate the JWT that we send it from the RP. Since we run the OP in docker and it listens on an ephemeral port we would have no way of knowing the port so that we can configure the ES running via the testcluster to know the "correct" Token Endpoint, and even if we did, this would not be the Token Endpoint URL that the OP would think it listens on. To alleviate this, we run an ES single node cluster in docker, alongside the OP so that we can configured it with the correct hostname and port within the docker network.
Pinging @elastic/es-security (:Security/Authentication) |
@mark-vieira added you for a +1 for the docker-compose changes since we chatted about this |
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.
I still need go through the tests more carefully (they are new to me). Here are a few comments/questions to start. Thanks!
} else { | ||
tokensListener.onFailure(new ElasticsearchSecurityException("Failed to exchange code for Id Token using Token Endpoint." + | ||
"Expected client authentication method to be one of " + OpenIdConnectRealmSettings.CLIENT_AUTH_METHODS + " but was " + | ||
rpConfig.getClientAuthenticationMethod())); |
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.
Nit: The name or value is usually placed inside a pair of brackets in error messages, .e.g ... but was [xxx]
.
for (Map.Entry<String, List<String>> entry : clientSecretJWT.toParameters().entrySet()) { | ||
// Both client_assertion and client_assertion_type are singleton lists | ||
params.add(new BasicNameValuePair(entry.getKey(), entry.getValue().get(0))); | ||
} |
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 I am missing something obvious. But client_id
is not added as one of the request parameters? The map returned from ClientSecretJWT.toParameters()
only contains client_assertion
and client_assertion_type
if I read the code correctly.
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.
But client_id is not added as one of the request parameters?
It's not, it doesn't need to be. It is added as the iss
claim of the JWT and validated from there.
only contains client_assertion and client_assertion_type
yes, these are the two parameters needed, see https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication
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.
You are right. For a moment, I forgot how JWT is organized. It's too late for me ...
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.
I know that feeling !!! :) No need to go through this today, it can definitely wait. I raised the PR because I had it ready since last week
@@ -841,6 +842,8 @@ private RelyingPartyConfiguration getDefaultRpConfig() throws URISyntaxException | |||
new ResponseType("id_token", "token"), | |||
new Scope("openid"), | |||
JWSAlgorithm.RS384, | |||
ClientAuthenticationMethod.CLIENT_SECRET_BASIC, | |||
JWSAlgorithm.HS384, | |||
new URI("https://rp.elastic.co/successfull_logout")); | |||
} | |||
private RelyingPartyConfiguration getRpConfig(String alg) throws URISyntaxException { |
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.
Nit: need a blank line before this method.
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.PRINCIPAL_CLAIM.getClaim()), "sub") | ||
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.RP_REDIRECT_URI), "https://rp.my.com") | ||
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.RP_CLIENT_ID), "rp-my") | ||
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.RP_CLIENT_AUTH_METHOD), "none") |
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.
"none", "None", "noNe", "nOnE", "NonE" ... 👍 that we will not have this issue.
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 would be testing the OP though, not our implementation, as it is the OP that should not validate a client JWT with none algorithm. We do not disallow none
specifically, we use an allowlist that only allows "HS256", "HS384", "HS512"
I like the suggestion though, we can use it in OpenIdConnectAuthenticatorTests.testImplicitFlowFailsWithNoneAlgorithm
which tests our implementation
+ "/c2id/clients"; | ||
private static final String LOGIN_API = "http://127.0.0.1:" + getEphemeralTcpPortFromProperty("oidc-provider", "8080") | ||
+ "/c2id-login/api/"; | ||
private static final String ES_PORT = getEphemeralTcpPortFromProperty("elasticsearch-node", "9200"); |
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.
I don't see test.fixtures.elasticsearch-node.9200
get set anywhere. How does this work?
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.
elasticsearch-node
is the name of the elasticsearch service in https://github.com/elastic/elasticsearch/pull/58708/files#diff-2be63811028f90650408e594c29099b9R3 and 9200 is defined there in the ports
section. I assume it is the gradle docker compose plugin that handles this magic or something in our buildSrc , Mark can probably share more details
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.
Ah awesome! buildSrc magic. TIL
I assume this PR will close #54582 ? Or do you plan to add more auth methods after this one? Anyway, I am tagging it here for cross-reference. |
we can wait until I return, this is not urgent, no worries. |
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.
One comment, otherwise LGTM.
x-pack/test/idp-fixture/build.gradle
Outdated
@@ -1,4 +1,36 @@ | |||
import org.elasticsearch.gradle.VersionProperties | |||
import org.elasticsearch.gradle.Architecture | |||
|
|||
apply plugin: 'elasticsearch.build' |
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.
I wonder if we can remove elasticsearch.build
plugin. This project contains no code or tests, it's simply a test fixture.
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.
LGTM. I left a few questions about the c2id-server setup for my learning purpose. Thanks!
@@ -40,11 +153,13 @@ services: | |||
- ./idp/shib-jetty-base/start.d/ssl.ini:/opt/shib-jetty-base/start.d/ssl.ini | |||
|
|||
oidc-provider: | |||
image: "c2id/c2id-server:7.8" | |||
image: "c2id/c2id-server:9.5" |
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 an image that we maintain? If so, where can I find the Dockerfile that builds the image?
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.
Nope, we use the official one that the folks from connect2id ( who implement the OP ) publish in Dockerhub. I dont think they publish the Dockerfile, some details are in https://connect2id.com/products/server/docs/guides/docker
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! I checked the image on Dockerhub and it has zero documentation. I found it hard to believe it was the official image ... apparently I am wrong.
httpPost3.setEntity(new StringEntity(postClient, ContentType.APPLICATION_JSON)); | ||
httpPost3.setHeader("Accept", "application/json"); | ||
httpPost3.setHeader("Content-type", "application/json"); | ||
httpPost3.setHeader("Authorization", "Bearer 811fa888f3e0fdc9e01d4201bfeee46a"); |
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.
Not sure where this Bearer token is picked from. I guess it has something to do with the docker image?
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.
We define this in override.properties which we bind mount to the container, see the docker compose file and it is used as a static credential to talk to the OPs API , I'll add a comment here when I get to merge this
op.issuer=http://oidc-provider:8080/c2id | ||
op.authz.endpoint=http://oidc-provider:8080/c2id-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.
Same here. Would be great to learn how these configurations work.
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.
See https://connect2id.com/products/server/docs/guides/docker for some details. Issuer is the OPs issuer string that it will use to populate the iss claim in the ID Token. The OP also uses this to determine the token endpoint and userinfo endpoint it expects to be listening on by adding the proper suffix.
The authz.endpoint is an API endpoint that we use to programmatically authenticate users with the OP.
We use the docker compose service name oidc-provider
so that es which also runs in docker can access it via it (docker networking magic :) )
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.
Sorry for not being clear here. I wasn't asking about oidc-provider
, which I understand it's docker's doing. Was more about property names, e.g. op.authz.endpoint
and what do they mean to the OP. The guide you link should answer all my questions. Thanks!
@elasticmachine run elasticsearch-ci/2 |
|
Hey looking at the
I need some more time to narrow down the proper fix. @jkakavas As a workaround, for now running |
@breskeby any updates on this ? Did you find sometime to look into the composeUp task? Anything I could do to help with this? Thanks! |
@jkakavas I pushed a fix on task dependencies setup for test clusters earlier today. Can you rebase against master and see if that fixes your issue? |
@elasticmachine update branch |
@elasticmachine run elasticsearch-ci/packaging-sample-windows |
Still:
|
@mark-vieira I looked briefly into this but couldn't find a root cause for this. do you have an idea what's going wrong here? |
The PR targets |
The OpenID Connect specification defines a number of ways for a client (RP) to authenticate itself to the OP when accessing the Token Endpoint. We currently only support `client_secret_basic`. This change introduces support for 2 additional authentication methods, namely `client_secret_post` (where the client credentials are passed in the body of the POST request to the OP) and `client_secret_jwt` where the client constructs a JWT and signs it using the the client secret as a key. Support for the above, and especially `client_secret_jwt` in our integration tests meant that the OP we use ( Connect2id server ) should be able to validate the JWT that we send it from the RP. Since we run the OP in docker and it listens on an ephemeral port we would have no way of knowing the port so that we can configure the ES running via the testcluster to know the "correct" Token Endpoint, and even if we did, this would not be the Token Endpoint URL that the OP would think it listens on. To alleviate this, we run an ES single node cluster in docker, alongside the OP so that we can configured it with the correct hostname and port within the docker network.
Backport #62289 |
The OpenID Connect specification defines a number of ways for a client (RP) to authenticate itself to the OP when accessing the Token Endpoint. We currently only support `client_secret_basic`. This change introduces support for 2 additional authentication methods, namely `client_secret_post` (where the client credentials are passed in the body of the POST request to the OP) and `client_secret_jwt` where the client constructs a JWT and signs it using the the client secret as a key. Support for the above, and especially `client_secret_jwt` in our integration tests meant that the OP we use ( Connect2id server ) should be able to validate the JWT that we send it from the RP. Since we run the OP in docker and it listens on an ephemeral port we would have no way of knowing the port so that we can configure the ES running via the testcluster to know the "correct" Token Endpoint, and even if we did, this would not be the Token Endpoint URL that the OP would think it listens on. To alleviate this, we run an ES single node cluster in docker, alongside the OP so that we can configured it with the correct hostname and port within the docker network. Co-authored-by: Ioannis Kakavas <[email protected]>
@tvernum You are right. They are corrected now. |
Support for additional Client authentication methods was added in the OIDC realm in elastic#58708. This change adds the `rp.client_auth_method` in the realm settings reference doc.
Support for additional Client authentication methods was added in the OIDC realm in #58708. This change adds the `rp.client_auth_method` and `rp.client_auth_signature_algorithm` settings in the realm settings reference doc.
Support for additional Client authentication methods was added in the OIDC realm in elastic#58708. This change adds the `rp.client_auth_method` and `rp.client_auth_signature_algorithm` settings in the realm settings reference doc.
Support for additional Client authentication methods was added in the OIDC realm in elastic#58708. This change adds the `rp.client_auth_method` and `rp.client_auth_signature_algorithm` settings in the realm settings reference doc.
Support for additional Client authentication methods was added in the OIDC realm in elastic#58708. This change adds the `rp.client_auth_method` and `rp.client_auth_signature_algorithm` settings in the realm settings reference doc.
Support for additional Client authentication methods was added in the OIDC realm in elastic#58708. This change adds the `rp.client_auth_method` and `rp.client_auth_signature_algorithm` settings in the realm settings reference doc.
The OpenID Connect specification defines a number of ways for a
client (RP) to authenticate itself to the OP when accessing the
Token Endpoint. We currently only support
client_secret_basic
.This change introduces support for 2 additional authentication
methods, namely
client_secret_post
(where the client credentialsare passed in the body of the POST request to the OP) and
client_secret_jwt
where the client constructs a JWT and signsit using the the client secret as a key.
Support for the above, and especially
client_secret_jwt
in ourintegration tests meant that the OP we use ( Connect2id server )
should be able to validate the JWT that we send it from the RP
(which contains the Token Endpoint as the
aud
claim ).Since we run the OP in docker and it listens on an ephemeral port
we would have no way of knowing the port so that we can configure
the ES running via the testcluster to know the "correct" Token
Endpoint, and even if we did, this would not be the Token Endpoint
URL that the OP would think it listens on. To alleviate this, we
run an ES single node cluster in docker, alongside the OP so that
we can configured it with the correct hostname and port within
the docker network.