Closed bellebaum closed 6 days ago
Perhaps you understand the grant type, or confidential clients.
This grant type is where the client authenticates directly to the AS and gets an access token. No user is involved. By definition, only confidential clients can keep a secret, and authenticate with a client credential.
Hence only a confidential client can use the client_credential
grant type
Hello @dickhardt :)
Correct, there is no user. According to the draft, client_credentials
are a used
when the authorization scope is limited to the protected resources under the control of the client, or to protected
resources previously arranged with the authorization server. Client credentials are used when the client is requesting
access to protected resources based on an authorization previously arranged with the authorization server.
OAuth clients may even be used in scenarios where they represent the only resource owners in the system. I.e. there are no users. For example, OAuth may be (and is) used as a central management protocol for device authorization.
My question relates to the first part of the quote I omitted above:
The client credentials or other forms of client authentication (e.g., a private key used to sign a JWT, as described in
{{RFC7523}}) can be used as an authorization grant [...]
What about use cases where we want to manage permissions of unauthenticated (i.e. public) clients, which authenticate by the mere act of trying? Essentially, the client credentials of public clients are not nonexistent, they are the empty string. These clients also need a way to access resources under their control, so apart from the form of client authentication (which is otherwise not a grant type specific token endpoint extension), they need exactly what the client credentials grant offers.
If there are no credentials, it is not a protected resource
I'm very confused about the language you're using here. These clients with no credentials are needing a way to access resources "under their control"? How do you enforce any access control without any credentials?
If you were to make a "client credentials" request with no credentials, why bother even having an access token in the first place, why not just let the apps request the public API?
Imagine wanting to temporarily restrict access to a diverse range of resources to particular clients. So the resources are public by default, but may be temporarily restricted at will. In such a case, access by public clients can be restricted at a single point (the authorization server), while access by unauthenticated clients has to be restricted at each resource server individually.
Furthermore, the authorization server may additionally centrally enforce further access restrictions such as IP-based rate limiting.
These are just some use cases where omitting this restriction can simplify the design of an ecosystem (I have also outlined above how it may simplify the design of an authorization server).
The main question is what interoperability or security arguments were considered against this. According to RFC 2119:
Imperatives of the type defined in this memo must be used with care and sparingly. In particular, they MUST only be used where it is actually required for interoperation or to limit behavior which has potential for causing harm (e.g., limiting retransmisssions) For example, they must not be used to try to impose a particular method on implementors where the method is not required for interoperability.
If you're wanting to temporarily restrict access to "particular" clients, you need to authenticate those clients somehow, which means they have credentials, which means they are not public clients.
Correct, the particular clients I want to restrict access to need to have credentials, but the ones having their access revoked might not.
Maybe an example setup might help:
Say I have clients A,B and C. A and B have credentials. They represent critical services which need to have access to my APIs at all time. C is for public access and has no credentials. Its client_id can be found on my website, but its access to my APIs might get revoked or limited during times of high load.
I can configure all APIs to require access tokens, but then C needs a way to acquire any (and posting credentials online is bad practice, should probably not be encouraged). The most interoperable way is the client_credentials
grant.
Alternatively, I could make my APIs publicly accessible, but then restrictions require coordination with all API managers rather than just a simple switch at the authorization server. In complex environments, this might not always be an option.
I don't think OAuth would be a good way to enforce rate limiting or a central way to turn off your public API temporarily. Unless you make the tokens extremely short-lived (e.g., a TTL of one minute) or even single-use, the extra step of having an authorization server will be ineffective. Moreover, if the authorization server needs to be contacted for each request anyway, moving this part of the access control to your resource servers (by having them contact the authorization server) would yield similar results. At that point, an internal protocol using optional access tokens might be easier to implement.
If you're concerned about your application's complexity, you could expose your API through some form of authenticating proxy. This way, your resource servers won't have to implement authentication, but you can still achieve global rate limiting and access control.
OAuth is a framework for granting access to protected resources, so adding a grant type to the specification that doesn't require authentication seems dangerous. Outside your use case of using OAuth to enforce rate limiting, granting access to a protected resource without authentication is a security problem, and by having (public) client credentials involved in the access procedure, implementing services might get a false sense of security here.
OAuth is a framework for granting access to protected resources, so adding a grant type to the specification that doesn't require authentication seems dangerous.
Resource Servers should never make any assumptions based merely on the fact that an authorization server is "using OAuth". Indeed, how the server authenticates e.g. users is not at all specified for a reason. The purpose of OAuth is to centralize authorization within an ecosystem.
OAuth is a framework for granting access to protected resources, so adding a grant type to the specification that doesn't require authentication seems dangerous.
Sure, if you assume that anyone can take all their APIs and expose them via a single proxy. This falls apart quickly if your ecosystem spans multiple organizations.
I don't think OAuth would be a good way to enforce rate limiting or a central way to turn off your public API temporarily.
Rate limiting is just one example :)
An authorization server is free to enforce an arbitrary policy based on peer address, time of day, phase of the moon or /dev/random
, if need be. The centralized management options OAuth provides can be quite helpful in some cases.
Yet another use case:
Access Tokens are JWTs, every client is simultaneously a resource server and willing to talk to other clients based on attributes in the (non-bearer) access token (i.e. the access token grants access to other clients, hence OAuth). For some clients, the authorization server attests special properties, but arbitrary clients may participate without being attested any special properties. Essentially, the authorization server is used as a kind of CA issuing short-lived (ca. 1h) certificates in JWT-format. In a real-world use case, the clients are essentially devices unable to use a browser.
Again, the question is not whether or not this is a reasonable thing to allow: It is being used. The question is whether security or interoperability concerns justify the use of an RFC 2119 reserved word to restrict these use cases.
https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-10#section-2.4.2
The authorization server MAY support any suitable authentication scheme matching its security requirements.
You can authenticate clients however you want, it doesn't have to use a preregistered client secret as the authentication method.
The client credentials grant uses whatever client authentication you've defined.
Saying that public clients can use the client credentials grant is a contradiction because public clients have no way to authenticate themselves.
Or another way to look at it, let's say you're using the client IP address to authenticate the client, so that means the client credentials request would contain the client_id and nothing else, it still falls under the "confidential client" definition because you're saying that the client IP address is the client credential.
What if I am using the empty string as a client credential and do not send it, as public clients do? This might match my authorization server's security requirements (e.g. for some clients, there are none, such as in my previous comment), hence, by this quote, it should suffice.
But then, if clients never sending any form of credential are suddenly counted as confidential clients, what is the purpose of the distinction anyway?
My point is saying public clients can use the client credentials grant is a contradiction because by definition public clients don't authenticate to the AS. As soon as the client authenticates somehow it's no longer a public client so it can use its authentication in the client credentials request. You can be as creative as you want about the client authentication method.
Yes, but taking this to the extreme, public clients are confidential clients, just that their authentication check reduces to a mere return true
. Therefore, they are authenticating to the fullest extend required by the authorization server using their non-existent credentials.
I don't think changing the language in the spec is going to help clarify that
Sure, but it does have real implications if there is an explicit statement (with an RFC 2119 keyword!) forbidding such clients from using this particular grant type. In particular, it has an effect on the following claim:
saying public clients can use the client credentials grant is a contradiction
It is not a contradiction, even if a bit unintuitive at first sight. And it can be helpful. Hence this issue. Currently there is no way (any auditor would accept) to both implement the spec-to-be and facilitate these valid use cases. This is potentially hindering interoperability, which need not be bad per se, but should be motivated by security or another form of interoperability rather than the use cases seeming nonsensical.
Maybe let me ask from a different perspective: If the client credentials grant could be used by any client authenticating to the token endpoint and requesting access to resources it is allowed to access, where would you see problems?
Resource Servers should never make any assumptions based merely on the fact that an authorization server is "using OAuth". Indeed, how the server authenticates e.g. users is not at all specified for a reason. The purpose of OAuth is to centralize authorization within an ecosystem.
Resource servers and authorization servers are regularly operated by the same entity (or at least operated in a way where the resource server is aware of the security an access token issued by the authorization server provides). As such, all resource servers that verify the token somehow (e.g., by checking a cryptographic property against a known key or secret) have at least some understanding of the level of security provided by this token. I.e. you cannot rely on OAuth providing some level of security on its own, but you cannot use OAuth without being aware of the level of security it provides in your use case.
Access Tokens are JWTs, every client is simultaneously a resource server and willing to talk to other clients based on attributes in the (non-bearer) access token (i.e. the access token grants access to other clients, hence OAuth). For some clients, the authorization server attests special properties, but arbitrary clients may participate without being attested any special properties. Essentially, the authorization server is used as a kind of CA issuing short-lived (ca. 1h) certificates in JWT-format. In a real-world use case, the clients are essentially devices unable to use a browser.
At this point, you're using OAuth as an alternative to existing structures with a CA issuing X.509 certificates. However, the common practice in this case is that clients without any attested properties don't use a certificate at all. I'm not saying that OAuth would be an inherently flawed way to distribute JWT-formatted certificates. However, not using a certificate if no property is attested is a perfectly valid implementation. Without unauthenticated tokens, you wouldn't need this grant type for public clients in the first place.
Currently there is no way (any auditor would accept) to both implement the spec-to-be and facilitate these valid use cases.
This will always be a limitation if you want to use a security-related specification for a project with parts implementing the standard that opt out of any security. The problem I see in changing the specification to allow (intentionally) insecure implementations to pass a security audit is that this weakening of the standard also applies to projects that don't try to opt out of security. The vast majority of applications using the client credential grant type should not pass a security audit when allowing unauthenticated authorization grants. In other words, the harm of enabling the security implications of your use case to pass an audit would outweigh the benefits, IMHO.
at least some understanding of the level of security provided by this token
I agree, and OAuth is purposefully fuzzy there. Maybe that level of understanding is best categorized as "the level of security that the RS operator trusts the configured AS to provide". After all, it is the RS operator configuring the RS to trust tokens issued by the AS. So what matters is their understanding of the security guarantees after consultation with the AS operator (which might or might not be the same person). This is where the simplicity of OAuth comes from.
This implies that assumptions like "only clients with suitable methods of authentication should use the RS to access resources for them" belong to the security considerations for implementations and deployments, not the core protocol. In fact, that would be a good place to bring this up.
However, the common practice in this case is that clients without any attested properties don't use a certificate at all.
Yes, although the AS agreeing to the communication by issuing the token is a kind of attestation in itself. This is where the centralization comes into play.
Currently there is no way (any auditor would accept) to both implement the spec-to-be and facilitate these valid use cases.
This will always be a limitation if you want to use a security-related specification for a project with parts implementing the standard that opt out of any security. The problem I see in changing the specification to allow (intentionally) insecure implementations to pass a security audit is that this weakening of the standard also applies to projects that don't try to opt out of security. The vast majority of applications using the client credential grant type should not pass a security audit when allowing unauthenticated authorization grants. In other words, the harm of enabling the security implications of your use case to pass an audit would outweigh the benefits, IMHO.
Could you please specify the exact security implications?
Client metadata already includes both a token_endpoint_auth_method
and a grant_types
attribute. So without any code to fulfill this MUST
, it is already possible to achieve the same effect by configuration. Deviating from a sane default configuration should come with appropriate context specific security considerations. Even if an AS has no concept of client metadata, it can still be written in a way to enforce arbitrary policies. Restricting it at a protocol level thus seems to only restrict use cases, and not provide additional security over proper configuration or implementation. This is what security considerations are for.
By allowing public clients to use the client credentials grant, one is not opting out of any security. One is in fact using OAuth's security model to its fullest extend. If the AS deems "unauthenticated" as secure enough and the RS is configured to blindly trust the AS, then any client should get access to a resource. Anything beyond that may only make OAuth more complicated and even harder to understand than it already is.
How about something like this in the security considerations:
Issuing access tokens as a response to public clients using the client credentials grant type implies that anyone may acquire such tokens without authentication. Authorization servers SHOULD limit the scope of such tokens to resources for which they expect this to be acceptable.
Does this address the security concerns?
This implies that assumptions like "only clients with suitable methods of authentication should use the RS to access resources for them" belong to the security considerations for implementations and deployments, not the core protocol. In fact, that would be a good place to bring this up.
The resource server's awareness of the security an authentication server provides doesn't necessarily imply that (all) security implementation details don't belong in the specification. E.g., the specification contains a requirement that
OAuth URLs MUST use the https scheme except for loopback interface redirect URIs, which MAY use the http scheme.
There are legitimate use cases where HTTP would be "enough security" (e.g. if the authorization server is only available on the loopback interface). However, for the vast majority of implementations, using anything but https
for the authorization endpoint would be unacceptable. Similarly, you can come up with a use case where issuing access tokens to an unauthenticated client is acceptable. However, for the vast majority of implementations providing the client credentials grant type, this behaviour would be insecure. By including a requirement for clients that authenticate with their credentials to actually have credentials, the specification helps to prevent faulty implementations where the developer implementing the authorization server might not notice that it's incorrect to assume every client that's been issued a client secret can use that secret to authenticate. This requirement would indeed prevent some uncommon use cases, but I believe it's better to switch to another standard for those use cases than try to generalize OAuth until it works for every possible use case.
Yes, although the AS agreeing to the communication by issuing the token is a kind of attestation in itself. This is where the centralization comes into play.
If the AS agrees based on some condition, which would be the only case where the agreement could be interpreted as an attestation, that condition becomes the client credential. E.g. if it is appropriate for your application's security, it could be a valid client credential that the user has a specific IP address or that the user is connecting at a specific phase of the moon. At this point, if I understand @aaronpk
's point correctly, you can declare your clients confidential and create a compliant implementation.
Could you please specify the exact security implications? Client metadata already includes both a
token_endpoint_auth_method
and agrant_types
attribute. So without any code to fulfill thisMUST
, it is already possible to achieve the same effect by configuration. Deviating from a sane default configuration should come with appropriate context specific security considerations. Even if an AS has no concept of client metadata, it can still be written in a way to enforce arbitrary policies. Restricting it at a protocol level thus seems to only restrict use cases, and not provide additional security over proper configuration or implementation. This is what security considerations are for.
The paragraph you quoted was about changing the specification with the goal of passing a security audit. My point is that dropping this grant type's requirement for clients to be confidential will allow implementations for regular use cases to incorrectly assume that client authentication of public clients provides any form of security. Again, if there's any verifiable way to authenticate a client (e.g. by using the IP address), that client can be considered confidential. Otherwise, there is no verifiable attribute, and issuing a token for a protected resource based on a grant type built around client authentication is counterintuitive.
If the client credentials grant type doesn't provide the flexibility you need, you can always define a new custom "minimal access" grant type that's available to any registered client. This does limit interoperability, but if the client doesn't know about your authorization endpoint's custom grant type, it might not know about the lack of authentication for the AS' client credentials grant type implementation either.
I feel that this discussion is slowly turning into bikeshedding over the definition of "client credential". It might be a good idea to wait for another opinion, as I'm unsure which definition is "more intuitive".
@bellebaum you are free to implement your AS however you want. There are no standards police that will take you away and lock you up for not following the standard to the letter. What is motivating you to want a change that has not been expressed by anyone else to the authors since OAuth 2.0 was started 15 years ago?
I don't think this discussion is making any progress, and I don't think it will help anyone to say "public clients can use the client credentials grant" because that meanse the client credentials grant is being used without credentials.
Hi,
I was looking through the current draft when I found this:
I was wondering where it came from. I believe the reasoning might be that resources accessible by public clients are public resources in the first place? I believe that some APIs can be designed much simpler if they always require an access token. This then allows to manage access for public clients centrally at the authorization server (e.g. consider the case where you want to temporarily restrict access to a diverse set of APIs to some known clients).
There are other reasons for why omitting this restriction makes for a cleaner design. Consider this typical OAuth token endpoint flow with many omitted details:
client_id
grant_type
in the request and determine whetherclient_id
is allowed to use it based on itsgrant_types
metadata value.grant_type
-specific code to determine the scope of the access token (including where it may be used, a resource owner, etc.)Currently, point three has to look at authentication information of the client again despite this information being primarily conceived as facilitating the first point. This can make code more complicated. Also, policy decisions as to which client may use a grant are generally perceived as being handled by the second point.
I would appreciate it if anyone could give me some insight into why this restriction was considered. Thanks in advance :)