Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

  1. Do we really need client_secret to get access_token on PKCE flow?
  2. Why is it recommended to use "PKCE Flow" if it will just expose the client_secret?
  3. 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

like image 793
dcangulo Avatar asked Dec 23 '22 17:12

dcangulo


2 Answers


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.

Using 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 sense 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 static client secret (like in the original authorization code flow) 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.

like image 195
afh Avatar answered Dec 28 '22 07:12

afh


  1. Do we really need client_secret to get access_token on PKCE flow?

It depends. Originally PKCE was introduced to protect public clients (a client which cannot protect a secret). But recently, with practices, PKCE became a recommendation for authorization code grant (source)

2.1.1. Authorization Code Grant

Clients MUST prevent injection (replay) of authorization codes into
the authorization response by attackers. The use of PKCE [RFC7636]
is RECOMMENDED to this end. The OpenID Connect "nonce" parameter and ID Token Claim [OpenID] MAY be used as well. The PKCE challenge or
OpenID Connect "nonce" MUST be transaction-specific and securely
bound to the client and the user agent in which the transaction was
started.

Note: although PKCE so far was designed as a mechanism to protect
native apps, this advice applies to all kinds of OAuth clients,
including web applications.

  1. Why is it recommended to use "PKCE Flow" if it will just expose the client_secret?

In short, to avoid authorization code replay attacks (spec - introduction). And this happens inside end user's device and not in the transmission of data. TLS is mandatory for OAuth 2.0 token request.

  1. How is it different from "Client Credentials Grant Flow" which also exposes the client_secret?

No grant will expose credentials as token requests are done via TLS.

I think in your case, the client is a confidential client (spec - client types). So I would recommend to check this aspect in authorization server.

like image 42
Kavindu Dodanduwa Avatar answered Dec 28 '22 07:12

Kavindu Dodanduwa