OAuth 2: authorization_code Grant - Is client_secret param neccesary? - oauth-2.0

With regards to OAuth 2.0, my previous understanding is that client_secret should be used for authorization_code grant, which is supposed to be "more secure" (client_secret was required for some tutorial out here 1 2)
However I saw a library when using authorization_code, didn't brother to check client_secret if not provided. Which makes me wonder the usage of client_secret and dig deeper into the spec of OAuth2.
I then looked into the RFC for OAuth 2 (https://www.rfc-editor.org/rfc/rfc6749#section-4.1), and found that client_secret is not required at all for authorization_code grant flow.
If you scroll down to the required param for authorization_code flow https://www.rfc-editor.org/rfc/rfc6749#section-4.1.1, you will see that client_secret is not even mentioned
So my question is:
Is client_secret required for authorization_code grant type?
If it is suggested to have client_secret instead of required, will there be any official documentation that tell us that client_secret is suggested?
Thanks!

Good question and one of the things I find most annoying about OAuth2.0 - understanding the security protocol around public clients.
To answer your questions as best I can:-
Is client_secret required for authorization_code grant type?
No. If the client is a public client then it should be allowed to use this grant type without authenticating itself (providing it registers a redirection endpoint). The problem is that there seem to be several implementations of OAuth2.0 Servers that don't allow public clients for this grant type.
If it is suggested to have client_secret instead of required, will
there be any official documentation that tell us that client_secret is
suggested?
You probably need to look at the documentation of the actual OAuth2.0 provider you use, rather than the generic IETF specification as they may specify rules around public clients outside of the RFC.
The 6749 RFC pretty much just says that the the Auth Server SHOULD deal with the fact public clients are more insecure, without giving the exact details of how to.
e.g. Section 10.1 says:
The authorization server must consider the security implications of
interacting with unauthenticated clients and take measures to limit
the potential exposure of other credentials (e.g., refresh tokens)
issued to such clients.

Related

Access Token from OIDC vs OAuth2.0

I know that the access token obtained from OAuth2.0 can be used to access protected resources.
OpenID Connect issues ID token and access token after authentication. And the spec says that the access token can be used to access userinfo endpoint to get additional user information.
One thing I'm not able to understand is, is there any difference between the access token obtained in #1 vs #2. If there is no difference then do we need OAuth2.0, if we implement OIDC.
You tend to just implement both OIDC and OAuth 2.0 together as a combined flow, by plugging in an Open Id Connect security library.
Eg For a mobile app it is common to plug in AppAuth Libraries, which would give you this behaviour:
An OAuth 2.0 authorization redirect using response_type=code
The Open Id Connect part is initiated by including scope=openid
You then get an authorization code (OAuth 2.0)
You then swap the authorization code for tokens
You get an access token (the OAuth 2.0 part)
You also get an id token (the OIDC part)
In practical terms OIDC introduces some standardisation that makes developing UI flows and dealing with access tokens in APIs easier, eg:
Metadata endpoint, to tell us where all the other endpoints live
JWKS endpoint, from which we can get the access token's public key
Typically in my own code I do not use the id token much at all. However, it is best practice to receive one, so that libraries such as AppAuth can make extra verification checks against received tokens.
If it helps, my Message Workflow Blog Post summarises some messages and use of endpoints.
This access tokens have different audiences ("aud" claim): the OAuth 2.0 access token is intended for resource server (i.e. API), and OIDC access token is intended for identity server itself.
As for me, they cannot be used interchangebly, but some examples (e.g. IdentityServer4) do that without checking the "aud" claim.
PS. The single access token can be used for both purposes if both audiences are included:
Each principal intended to process the JWT MUST identify itself with a
value in the audience claim.<...> In the general case, the "aud" value
is an array of case-sensitive strings, each containing a StringOrURI
value.
JWT doc

OAuth2.0 and OpenID Connect Confusing

I am confused about the use of OAuth 2.0 as an Authorization method and OpenID Connect as an Authentication method.
Based on my knowledge OAuth 2.0 is only an Authorization method. In other words, this is the process to request an ACCESS_TOKEN and RECEIVE this ACCESS_TOKEN, like depicted in the image below in yellow ellipse: (simplified)
Before an OAuth 2.0 Client retrieves an ACCESS_TOKEN from an Authorization Server this Server should verify if the User allows it and this is an Authentication Process that OAuth 2.0 does not care about.
When OpenID Connect is included in the mix it allows for an Authentication Method as well, but in my knowledge OpenID Connect just adds a "Claim" in the JWT Token that holds information about user that is using the service, like: email, name and others.
My questions are:
Why not ignore OpenID Connect and just add more "claims" in OAuth
2.0 to get information about users?
Is my description of the flows correct?
OpenID Connect does not merely "add a claim in JWT Token" but:
it introduces a completely new token (id_token) with radically different
semantics than the OAuth 2.0 access_token and a standardized format that is understood by the Client as opposed to the access_token which is opaque to the Client
it "twists" the role of the Client, now becoming the "audience" (or: intended recipient) of a token (i.e. the id_token) whilst the audience of the access_token is still a remote entity (aka. Resource Server) and the Client is only the "presenter" of the latter
The 2nd item is the primary source of confusion between OAuth 2.0 and OpenID Connect.
I don't know if your method will work or not but you're totally free to roll your own authentication. After all, that's what Facebook, GitHub and many others did by customizing oauth2. There ended up being so many oauth2 "authentication" methods that it was never plug and play if you wanted to change your provider. I believe that's why OpenID connect was introduced--a common way of connecting and reasoning about authentication while building on the established oauth2 pattern for authorization. Use OpenID connect or don't...but if you don't you'll be reinventing the wheel.
#sdoxee answers explains thing correctly. But I am adding bit more information for OP's understanding.
These days many identity providers (eg:- Azure AD) issue JWT based access tokens. These JWT access tokens do contain claims about end user as well as JWT related validation details (eg:- Token expiration). Here is the link for Azure AD OAuth 2 success response which highlights access token to be a JWT. Also, see JWT claims to see how they explain the claims. Samples are given below,
family_name : User’s last name or surname. The application can display this value.
given_name : User’s first name. The application can display this value.
One could think of building authentication on claims present in access token, but this is not sticking with protocol. And mostly claims and user information will be implementer specific. Also, by protocol definition, these two tokens (id and access) have two different audiences.
ID token is for client, for validation and for authentication.
Access token is for OAuth 2 protected endpoint.
Again, as #sdoxee highlight, use the correct protocol at correct place. Having claims in access token does not necessarily mean you should use them for authentication.

OAuth2 authorization code PKCE without client_secret (wso2 5.3.0 IAM)

I'm currently trying to implement the OAuth 2.0 authorization code grant on a public client/native client (Android App).
Since it is impossible to store the client_secret on the device, I wanted to use this grant type with rfc7636 / Proof Key for Code Exchange by OAuth Public Clients (PKCE).
I'm using wso2 5.3.0 IAM in the backend.
The Authorization step works perfectly fine, but I'm not able to get the Access Token without a client_secret: invalid_request, Missing parameters: client_secret
Did I misunderstand the authorization code grant with PKCE wrong or did I miss some configuration in the IAM?
In comparison: It is possible with auth0.
Best Regards,
Robert
Even if you use the authorization code flow, client_secret is required at the token endpoint if the client type of your application is confidential. "4.1.3. Access Token Request" in RFC 6749 says as follows:
If the client type is confidential or the client was issued client credentials (or assigned other authentication requirements), the client MUST authenticate with the authorization server as described in Section 3.2.1.
So, change the client type of your application to public. I don't know WSO2, but I guess that it provides settings menu to switch the client type like below.
(screenshot of Authlete's web console)
The definitions of confidential clients and public clients are described in "2.1. Client Types" in RFC 6749.
Yes, the client_secret is mandatory in WSO2 IS implementation due to the Apache OLTU library that has been used internally to implement the OAuth2 feature.
Currently there is no way to register an application as a public client as explained.
However that doesn't mean there are necessarily any security pitfalls. Basically what the recommendation says is, not to embed the client_secret in a mobile app, because it makes it vulnerable. It doesn't provide any additional security for protected backend resources, because the client request is anyway not authenticated using client_secret. If you just treat the "Base64(client_id:client_secret)" as one single string it doesn't make any difference in the protocol or security of the protocol.
So when using WSO2 IS with mobile applications, following recommendations need to be followed.
Use authorization code grant type, which requires the client_secret.
Use PKCE (after WSO2 IS 5.2.0)
If you have other type of clients or channels for the same applications, e.g. like web, then register them as a separate service provider in IS and generate a separate pair of client_id, client_secret for them.
Disable "client_credentials" grant type for the particular OAuth2 mobile client you register in WSO2 IS, so that those apps can't get an access token without user authentication.
Going one step further, if you need to have unique client credentials for each instance of the mobile applications then use OAuth2 Dynamic Client Registration (DCR) to generate client credentials on the fly.
By following above 5 recommendations, it gives you the same level of security as recommended in the specification.
For Authorization grant flow you can send the request with empty client_secret. Try putting empty string like this client_secret='' and it should work as expected. You cannot request TOKEN_URI without client_secret parameter.
PKCE is used to protect theft of authorization code, Authorization code is valid for 10 minutes, when auth code is redeemed for access_token we also send code_verifier to make sure the auth code is not stoled by someone. code_verifier and code_challenge are generated together and code_challenge is used while requesting for auth code & code_verifier is used while requesting for access_token

Managing the OAuth2 Authorization Grant lifespan

I'm trying to find anything in the OAuth2 specs that relate to the lifespan of the Authorization Grant. There is mention of the Access Token expiring and needing to be refreshed/renewed but I didn't see anything about the Grant. My impression is that if there is a need to expire or revoke an Authorization Grant, this would be something that would have to be added to the Auth Server and is not really within the scope of the OAuth2 framework.
Am I correct in my understanding or have I overlooked something? Is this even a valid use-case?
RFC 6749 (The OAuth 2.0 Authorization Framework) mentions that:
Authorization codes MUST be short lived and single-use.
RFC 6819 (OAuth 2.0 Threat Model and Security Considerations) provides more detailed description:
Browser-based flows expose protocol parameters to potential attackers via URI query parameters (HTTP referrer), the browser cache, or log file entries, and could be replayed. In order to reduce this threat, short-lived authorization "codes" are passed instead of tokens and exchanged for tokens over a more secure direct connection between the client and the authorization server.
However there is no exact information about grant token lifespan in RFCs. From my experience it may be a minute or several.

How does 2-legged oauth work in OAuth 2.0?

In OAuth 1.0, 2-legged is pretty easily: Simply send the request as usual and omit the access_token header.
Things seems to have changed in OAuth 2.0 (drastically, as I found out today :)). In OAuth 2.0, the request no longer has headers such as the nonce, consumer key, timestamp etc. This is just replaced by:
Authorization: OAuth ya29.4fgasdfafasdfdsaf3waffghfhfgh
I understand how 3 legged authorizations work in OAuth 2.0 and the application flows. But how does 2-legged work in 2.0? Is it possible to design an API that can support both 2-legged and 3-legged OAuth 2.0?
I have been searching for information regarding this, but I have been finding a lot of stuff on 2-legged for 1.0 and almost nothing for 2.0.
After lots of research, I discovered that client_credentials grant type is for this scenario. Once you punch this term into google, you can find loads of very helpful resources.
This is the normal flow for 3-legged OAuth 2.0 (we want the user to sign in):
Assume we have the following endpoints in our app for authentication:
/oauth/auth
/oauth/token
Normally (for authorization code grant), we direct the user to /oauth/auth?state=blah&client_id=myid&redirecturl=mysite.com/blah
Then upon authentication, the user is redirected to mysite.com/blah?code=somecode
We then get somecode and exchange it for a token using /oauth/token?code=somecode&client_id=myid&client_secret=mysecret
We can then use the token to make calls.
This is the application flow for client_credentials to implement 2-legged OAuth 2.0, which is markedly simplier:
In this approach, we do not need to perform any authentication.
We simply POST to /oauth/token with the following form data:
grant_type=client_credentials&scope=view_friends
Note that scope is optional. The endpoint then directly returns an access token for us to use (no refresh token is provided). Since no refresh token is provided, when the token expires, you will need to reauthenticate and ask for a new one.
This leads to the following caveats:
Use this only for (very very) trusted applications such as internal applications.
You need to devise your own way to authenticate. For instance, the RFC's example uses basic auth.
Another solution is to use JWT (JSON web tokens) like the google OAuth API. It is a very complicated process, but there exists numerous libraries for generating your JWT. You then post the following form data (url encoded of course):
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=generated_jwt
This is posted to /oauth/token to get your token.
As for the question of whether you can create an API that supports 2-legged and 3-legged OAuth 2.0, Yes, it is possible.
Then /auth endpoint is only used when users need to authenticate against the service.
In the /token endpoint, simply check the value of grant_type in the GET parameters for urn:ietf:params:oauth:grant-type:jwt-bearer if using JWT or client_credentials for client_credentials.
Note that when generating the client_id and client_secret to give to the user, if you are supporting multiple grant_types, ensure that you have a database column to store what type of grant type the id and secret was generated for. If required to have multiple grant types per user, generate a different set of credentials for each grant type.
You can also check out Google's implementation of 2-legged OAuth2 (I believe this documentation has been published only recently).
The Google Drive SDK delegation docs should also help understanding Google's 2-legged OAuth2 implementation.

Resources