'Do we really need client_secret to get access_token on PKCE flow?
I am building 2 apps; a front-end, and a back-end.
The back-end will be built using Rails API + Doorkeeper Gem (oauth2 provider) while the front-end will be built using React Native.
Currently, I am using "Client Credentials Grant Flow" which works just fine at the moment. But after researching for a while, this flow shouldn't be used in a client-only app as it exposes the client_secret if ever someone decompiles the app.
I have also read about "Implicit Grant Flow" which only requires client_id. But this flow seems old now?
And according to this: https://auth0.com/docs/api-auth/which-oauth-flow-to-use#is-the-client-a-single-page-app-
It is recommending to use "Authorization Code Grant with PKCE" over "Implicit Grant Flow". I am able to make it work but the problem is that it still needs the client_secret in order to get an access_token, is this how it should be?
Here is the sample flow I am doing:
curl -X POST 'http://localhost:3000/oauth/authorize?client_id=xxxx&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&scope=public&code_challenge=testchallenge&code_challenge_method=plain'
This will give me the following response:
{
"status": "redirect",
"redirect_uri": {
"action": "show",
"code": "8quZ-EAiKKG2EKnQiSYs3xeFRCgsIwcTbaWNdjnpyFg"
}
}
And then I will use the code above to get an access token using the request below:
curl -i http://localhost:3000/oauth/token \
-F grant_type="authorization_code" \
-F client_id="xxxx" \
-F client_secret="xxxx" \
-F code="8quZ-EAiKKG2EKnQiSYs3xeFRCgsIwcTbaWNdjnpyFg" \
-F redirect_uri="urn:ietf:wg:oauth:2.0:oob" \
-F code_verifier="testchallenge"
Now here is the problem, in order to exchange code to an access_token I will still be needing the client_secret. How is it different from "Client Credentials Grant Flow" if both will just expose my client_secret?
The above command will return the following:
{
"access_token": "nQoorBqLxQH4qFpmlx3mGG6Cd_TfX4d3L3gAGOTwrFs",
"token_type": "Bearer",
"expires_in": 7200,
"scope": "public",
"created_at": 1595517643
}
If I don't include the client_secret in the request here is the response:
{
"error": "invalid_client",
"error_description": "Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method."
}
So here are my questions:
- Do we really need
client_secretto getaccess_tokenon PKCE flow? - Why is it recommended to use "PKCE Flow" if it will just expose the
client_secret? - How is it different from "Client Credentials Grant Flow" which also exposes the
client_secret?
Doorkeeper PKCE documentation: https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-PKCE-flow
Solution 1:[1]
Update
In the meantime recommendations have changed (see also Kavindu's answer for references) and I want to make sure this is also emphasized in this answer. Including PKCE as an additional layer of security supplementary to the client secret also makes sense for confidential clients. The client secret allows the authorization server (identity provider) to determine the identity of the client.
While PKCE allows to make sure that the party trying to exchange the authorization code for the token is the one that actually requested the authorization code in the first place. So to avoid interception scenarios it also makes to add PKCE on top even for confidential clients.
But keep in mind that not all identity providers might support using PKCE and a client secret at the same time (yet).
As for the problem stated with Doorkeeper this behaviour might in the meantime also have changed.
Original Answer
Authorization code flow with PKCE was invented for setups where the client is not able to securely protect a secret. So when using authorization code flow with PKCE you don't need a secret or to be more precise a client secret would make no sense.
It seems like what you are experiencing is a Doorkeeper bug (see https://github.com/doorkeeper-gem/doorkeeper/issues/1089). So I'm afraid until they fixed it you will have to use some dummy client secret.
But as long as Doorkeeper implements the rest of the PKCE flow correctly this should not be an issue as this flow is not dependent on any static client secret but instead uses the dynamically created code verifier as you already pointed out.
I'm not sure if I correctly understand what kind of client you are using that handles the login. If it is a SPA or mobile app you should use authorization code flow with PKCE but if you are implementing a "classic" web application where the login is handled in some backend service you should go with the normal authorization code flow using a client secret as backends can be trusted to protect the secret.
By the way, I just checked the code in one of my projects I'm working on where I build some Angular SPA which authenticates via Auth0 as identity provider. I'm using the authorization code flow with PKCE there and I definitely don't send any client secret to Auth0 because obviously the flow is implemented as intended. So this really has to be an issue with Doorkeeper.
One other thing: I don't know if the requests you provided are just examples but instead of using the method plain for transforming the code verifier into the code challenge I would rather use a secure method such as S256 instead as recommended in the RFC which you referenced in your question.
Solution 2:[2]
I am creating a Doorkeeper::Application like this in the Rails console:
Doorkeeper::Application.create :name => 'Test App', :uid => 'xxxx', :secret => 'xxxx', :redirect_uri => 'urn:ietf:wg:oauth:2.0:oob'
It seems like I need to make the Doorkeeper::Application's confidential field to false to be able to get access_token without client_secret.
So the above code will become:
Doorkeeper::Application.create :name => 'Test App', :uid => 'xxxx', :secret => 'xxxx', :redirect_uri => 'urn:ietf:wg:oauth:2.0:oob', :confidential => false
I found the solution in: https://github.com/doorkeeper-gem/doorkeeper/blob/master/spec/requests/flows/authorization_code_spec.rb#L348
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | dcangulo |
