IdentityServer has three parts:
- ApiResource configuration
- IdentityResource configuration
- User information (claims)
There are two tokens: the access token and the identity token. The access token is used to access resources (api and also UserInfo endpoint), the identity token contains information about the user.
While IdentityServer is about authenticating the user, it also contains configuration for authorization. You can use something like a PolicyServer to take the authorization out of IdentityServer, but for basic authorization you don't have to.
There are two types of user claims:
- claims about who the user is, like name, gender, birtday, etc.
- claims about what the user is authorized to, like roles, employeeId, etc.
The first set of claims are part of the identity token and tell the client about WHO the user is, the other claims are part of the access token and tell the API what the user is allowed to do.
About the Identity Token, the only thing you can count on is the fact that it always contains the sub
claim (the Id from the user table). The short version of the filosophy here is to keep the token small because all you need is that claim. Additional information can be requested at the UserInfo endpoint. The content of the identity tokencan vary. E.g. you can force to add all claims to the first token (AlwaysIncludeUserClaimsInIdToken) in order to prevent an additional call.
On the other hand, if the user chooses to not give consent then the identity token will remain empty (except for the sub claim, because scope=openid is required). Also when requested from the UserInfo endpoint.
So the question is, how are you going to use the identity token? How much value does the token have? You shouldn't use it to access resources (that's where the access token is for) and it may not contain the requested information.
About the access token, this is the token that actually make it possible for the client to access a resource (api). In the client_credentials flow, there is no user (so there won't be an identity token as well), while in other flwos the client will act on behalf of the user. The difference between the first flow and the others is the sub
claim. That tells the Api on who's behalf the client requests access.
For access tokens counts the same, if the user doesn't give consent then the client will not be able to access the resource.
About resources and scopes. A resource is a logical name of a resource. By that I mean that it is possible that you have multiple Api's that are part of the same resource. That's fine. Because one resource can have many scopes. In the examples it is 1:1, but when you are really using scopes, then you'll see that a scope actually defines a certain piece of functionality within the resource. Like micro services.
Back to IdentityServer. An important job of IdentityServer is to filter claims. Because the user can have many claims, but you only want them when you need them. So per token the requested claims are added.
The Identity Token claims tell something about who the user is. But there is more, these claims are not context dependent. They are always true. Your name is everywhere the same, so is your birthday, etc. Use the IdentityResource table to define the scopes, e.g. openid, profile, email.
The claims in the access token are context dependent. Because only the API knows what a claim means. Use namespace types to distinct between claims.
It doesn't really matter how the Api resource is setup, just remember that when a scope is requested all claims from the resource (including the other scopes in that resource) are added to the request filter, while when a scope is requested, only the claims for that scope are added to the request filter. The request filter that is used to add all matching claims from the user to the token.
So now you have an access token. The token must contain information about the scopes. Why? Because you don't want clients to access resources that are not allowed. Limit access by defining the scopes per client. As a scope is a piece of functionality, you know which scopes the client needs. In the IdentityServer you configure the same scopes in order to match the client. Never trust a client.
The access token contains all claims from the requested scope/resources. That means that with that token all api's within the resource/scope can be accessed with that token, without having to obtain a new token per api.
This also means that the token can become quite large. Something you should avoid before hitting limits. That is why you don't want permissions in the token. If you want permissions, then implement your own authorization server.
On the other hand, do you need permissions? Given a design where the Api (resource) knows what a user is allowed to access, use policies (sort of business rules) and resource based authorization. You can implement a local user table that contains information about the permissions.
Also, do you really need CRUD permissions on every object? With policies you have the chance to define more complex authorization than just comparing string values.
As for the user, the user is the resource for the identity. But knows nothing about scopes. So you can't bind scopes to users. The client has scopes, which opens the door to the api, but the api decides whether the user actually has access.
And now to answer your questions:
a) All claims from the user that match the requested claim types are added to the access token. Where requested claim types are taken from the requested scopes. Where all ApiResource claim types that are related to the requested scopes are added and the claim types of the requested scopes themselves.
If the Api needs information about the user, then it can use the access token to call the UserInfo endpoint. The default identity token only contains the sub
claim (something the user can't deny or revoke).
b) The tokens are requested by adding scopes to the client. By default the openid, profile scopes are added. The client credentials will not receive an identity token and there is one additional token, the refresh token. Please note that this token is not available to all flows. To request the refresh token add the offline_access
scope. Where you can take offline access literal. This is a token that allows the client to request new access tokens, without requiring user input. So in case the user is offline, the service can still proceed.
When user consent is required then it is possible that certain information or options are not available. E.g. if the user doesn't want offline access, then the service (e.g. some synchronization service) will not be able to run. This also occurs when the user revokes a given consent. Requesting new access token will result in an unauthorized response.
In order to bypass missing user information, simply ask the user for information and store it locally. You are most likely going to need it there anyway.
c) Forget permissions, use policies and resource based authorization. Implement this in the Api. That's the place where the exact context is known. The client can open the door to the api, but it depends on the user (claims, local authorization info) if the resource can be accessed.
Suppose you have the resource 'weather' and scopes 'weather.api.full_access' and 'weather.api.read_only' (as part of the resource 'weather').
Please note that the name 'weather.api.full_access' doesn't say anything about the level of access, only about the expected functionality.
The actual access level should be based on local information from the resource or policies, e.g. user has a read subscription or an admin role.
In order to give Billy full access add the http://api/admin (value: true) claim to Billy. And for Jhon add a record in the subscriptions table.
In order to divide a resource over multiple api's (per scope), use events to validate the scope. You don't want a client to access the full_access functionality when only the read scope was requested.
I hope this makes sense to you. It's hard to give a short and complete answer. Let me know if you have questions about this answer.
In short, IdentityServer can implement it all. If you want permissions then take a look at the policy server.