Suppose we have some RESTful API whose resources we want to expose. End users will work with this API through client applications like mobile apps and Javascript based clients that run on web browsers.
With OAuth 2.0 this RESTful API will lie on the Resource Server and we will have one Authorization Server on which the client applications are registered. The users will then be registered at the authorization server and will be able to grant permission to those applications access resources on their behalf or not.
So when the user access one client application he will be redirected to the Authorization Server and be prompted to grant permissions to said client app. After that an access token is issued and the client is able to make requests to the Resource Server.
All of this is quite clear to me. There's just one missing piece: the protection of each resource might be user-dependent. To be more precise it might be claims-dependent. What I mean by that is we can have the following situation:
The resource http://resourceserver.com/api/first-resource should only be accessible to users with claim "ExampleClaim" with value 123.
The resource http://resourceserver.com/api/second-resource should only be accessible to users with claim "AnotherClaim" with value 123.
The resource http://resourceserver.com/api/third-resource should be accessible to any users.
When I first heard of OAuth was dealing with ASP.NET WebAPI and I dealt with that in the following way: when the request was sent with the Authorization: Bearer [token]
header, on server side the thread principal was set and I thought that this meant the user was authenticated with the API. So I used [Authorize]
attributes in order to verify if the user could access the resource.
After studying OAuth more deeply I saw this was a terrible misuse of the protocol. As I've learned, OAuth authorizes applications and not users. When the request is made with the Authorization header, as I've learned, the access token shouldn't contain information about the user, just about the application being allowed to make the request.
Considering that, sending the Authorization header with the request doesn't identify the user and does don't say if the user can or cannot access said resource.
In that case, how does one perform this kind of authorization? I mean, not authorization of the client app performing the request, but the authorization of the user accessing the resource based on his claims? I believe this is where OpenID Connect and it's ID tokens come in, but I'm unsure. How does one manage this?
An access token does not contain a user's claims, but it contains the subject of the user who has granted permissions to the client application. "Subject" is a technical term and it means a unique identifier. Simply saying, "subject" is a user ID in your database.
At a protected resource endpoint, you will do:
- Extract an access token from the request. (RFC 6750)
- Get detailed information about the access token from the authorization sever. (RFC 7662)
- Validate the access token. The validation includes (a) whether the access token has expired or not, and (b) whether the access token covers scopes (permissions) that are required by the protected resource endpoint.
The steps above from 1 to 3 are an access control against client applications. OAuth 2.0 (RFC 6749) is for this. See "Protected Resource" by Authlete (by me) for details about these steps.
After the steps above, then you will do:
- Extract the subject from the access token. Again, "subject" is a unique identifier of the user.
- Retrieve claims of the user from your database.
- Validate the claims as you like.
The steps above from 4 to 6 are an access control against users. OAuth 2.0 is NOT for this.
The primary purpose of OpenID Connect is to get an ID token in a verifiable manner. You can confirm that an ID token has been issued by a right party by verifying the signature attached to the ID token. See JSON Web Signature (JWS) (RFC 7515) for details about signature.
An ID token itself is not a technology to protect Web APIs. But you may be able to use it for that purpose if you use at_hash
claim in an ID token properly (see "3.1.3.6. ID Token" in OpenID Connect Core 1.0). However, at a protected resource endpoint, it will be much easier to get claims directly from your database than to parse an ID token.
[ Additional answer #1 for the comment ]
In your use case, you don't need ID tokens. It's because an access token already contains information about the subject of the user. In normal cases, the information is equivalent to the value of sub
claim in an ID token.
Therefore, you don't need an ID token to get the subject of the user. See the description of the step 4, and you can find "extract the subject from the access token."
[ Additional answer #2 for the comment ]
So is there anything wrong in extracting the subject from the access token like that and verify the claims? Or this is the right way of doing things?
There is nothing wrong. For example, suppose you define a Web API, https://api.example.com/profile
, which returns profile information of a user. In normal cases, such an API would accept an access token and then extract the subject from the access token to determine which user to refer to. On the other hand, if the API did not extract the subject from the access token, it would have to require "subject" as a request parameter to determine which user to refer to (or require an ID token that contains "sub" claim). Even in such a case, the API must check whether the subject specified by the request parameter and the subject associated with the access token are identical because otherwise it would become a security issue.
Checking claims after extracting the subject is also a normal step. For example, you may want to restrict functionalities of your service based on the plan that the user has paid for (Free plan, Lite plan, Enterprise plan, or whatever). In this case, you would have to refer to plan
claim. Of course, checking such a claim can be done only after extracting the subject from the access token.
Therefore, (1) extracting the subject from an access token and then (2) verifying the claims of the user are normal and even typical steps in implementations of protected resource endpoints.
You are right, OAuth is NOT an authentication protocol but rather a delegation protocol.
OpenID Connect adds two notable identity constructs to OAuth 2.0's token issuance model.
an Identity Token - the delivery of which from one party to another
can enable a Federated Identity SSO user experience
a standardized identity attribute API - at which a client can
retrieve desired identity attributes for a given user.
The ID TOken can be presented to the userinfo_endpoint to obtain the information and provides level of assurance that the user has been authenticated by the OpenID Provider.
BTW: The "sub" ie only unique within the context of the Authorization Server. It is recommended IF you store the sub you also store something like iss-sub. The thoughts are tsmith at Google may not be tsmith at Twitter