Open Id Connect Doubts - Identity Server 4 // How to grant specific users to concrete scopes?
Asked Answered
N

2

6

I have some doubts after reading the Open Id docs. Any help will be much appreciated.

Following any of the oidc flows you end by having an id_token and a access_token.

a)When you send the access_token to a protected api, why or when (example) it would need to retrieve some claims about the user who owns it? Maybe if the protected api wants to use some data about the authenticated user?

b) For obtaining the claims the protected api will need to communicate with the UserEdnpoint endpoint? It sends the access_token? And which claims are contained in the returned id_token?/ What happens if it asks for more claims that the user has consent access?

c)Identity server 4: You define resources to be protected. They could be "identity resources" and "api resources".While defining ApiResources you can define inside fine-grained scopes. For example:

  Scopes =
        {
            new Scope()
            {
                Name = "weather.api.full_access",
                DisplayName = "Full access to WEATHER API",
            },
            new Scope
            {
                Name = "weather.api.read_only",
                DisplayName = "Read only access to WEATHER API"
            }
        }

Also you define the clients , and to which scopes they have access. How do yo you specify which users can access to specific resources? I don 't see how you will map specific permissions of users using the same client.

The steps will be:

  1. Give the client access to both scopes: "weather.api.full_access","weather.api.read_only"
  2. And now.... how I grant, for example "Billy" to have "weather.api.full_access", and "Jhon" to have "weather.api.read_only" ? They are using both the same client.

Many thanks!!

Nikola answered 2/1, 2019 at 12:3 Comment(10)
You are falling into the trap of solving concerns of "Authorization" within IdentityServer which is meant to help solve concerns of "Authentication". Aka, validating that the user/machine is who they say they are. I would recommend to look into "PolicyServer" that the creators of IdentityServer have also produced. They also have a nice youtube video (don't have link at hand) discussing these boundaries between concerns.Planetarium
Ok , many thanks, will you be so nice to answer my questions too? :) :)Nikola
Scopes are only what a client application has been authorized to do. It has no effect on what a user is allowed to do: auth0.com/blog/on-the-nature-of-oauth2-scopes You'll want a different solution for user authorization.Unbridled
@ScottBrady do you recommend me some solution in .net for that purpose?Nikola
You could give PolicyServer a go: policyserver.io Other than that, I typically recommend keeping user authorization decisions within the client apps/protected resources themselvesUnbridled
@ScottBrady you are right, policy server seems to be what i need. Do you know which is the maturity level of policyserver?Nikola
@VidmantasBlazevicius you are right too. Do you have tried Policy server? is mature enough?Nikola
I don't personally have any experience with it yet. I'd get in contact with Dom and Brock.Unbridled
@ScottBrady how? And how have you handled that permissions issue in your business day?Nikola
@Badulake Did you take a look at PolicyServer? There are two versions. I'm certain the commercial version is mature, there is full support. The OSS version is a light, local version, that reads role/permissions from a json file. The middleware adds the permissions as claims to an additional identity and translates it into policies. It is not as extended as the commercial version. But being open source you are free to extend it yourself, as I've done myself. The local version is a good start, as it should be easy to upgrade from local to commercial version.Kickshaw
K
16

IdentityServer has three parts:

  1. ApiResource configuration
  2. IdentityResource configuration
  3. 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:

  1. claims about who the user is, like name, gender, birtday, etc.
  2. 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.

Kickshaw answered 3/1, 2019 at 4:5 Comment(2)
what do you mean by : "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." ?? ThanksNikola
Your help was 10/10 . I still have some doubts , that`s the reason why i oepened a new question. It is more focused in a concrete example. Many thanks!!Nikola
P
1

a)When you send the access_token to a protected api, why or when (example) it would need to retrieve some claims about the user who owns it? Maybe if the protected api wants to use some data about the authenticated user?

For example, if your users can be subdivided into organisations or departments and you want to add a policy within your api to only allow access to certain endpoints for when the user is in one of those subdivided groups - you can add a custom claim "organisationid" or "departmentid" and add that claim with its value to the tokens that are issued. Whether this is applicable or not, it will depend based on the context of your problem and there is a fine line when the concern of authorization can mistakenly be attempted to be solved within authentication.

b) For obtaining the claims the protected api will need to communicate with the UserEdnpoint endpoint? It sends the access_token? And which claims are contained in the returned id_token?/ What happens if it asks for more claims that the user has consent access?

You don't necessarlily need to use UserInfo endpoint. You can set the client property AlwaysIncludeUserClaimsInIdToken to true and all the claims will be included in the id_token by default therefore removing the need of the round trip to UserInfo endpoint. If you do end up taking the round trip, you were correct in saying that you need to send access_token representing the user, however, what is returned is not an id_token, but rather user info. Example from IdentityServer4 docs:

Request: GET /connect/userinfo Authorization: Bearer {access_token}

Response: HTTP/1.1 200 OK Content-Type: application/json { "sub": "248289761001", "name": "Bob Smith", "given_name": "Bob", "family_name": "Smith", "role": [ "user", "admin" ] }

Lastly

How do yo you specify which users can access to specific resources?

This should most likely be solved through Authorization which is not the concern of OAuth2 protocol or IdentityServer4. Authorization is normally handled differently within each application based on problem domain needs. I would recommend to take a look at Policy Server by the creators of Identity Server. They have an excellent video where they delve deeper into this topic and they also have a product to address this problem.

Planetarium answered 2/1, 2019 at 16:21 Comment(3)
@question b // i think you wrote a mistake => access_token sent , right?Nikola
@Badulake yes, good spot. I've seen your other question too - yes, I have some projects using PolicyServer in production so it is definitely worthwhile looking into. In general, I would suggest at looking at default AuthorizationPolicy framework in ASP.NET Core. If that doesn't suit your business needs, you can look into external libs like PolicyServer. If that doesn't work - you need to create your own business logic. It is unlikely you will get a concrete answer on StackOverflow for this as you need to understand the problem domain very well to identify what suits best.Planetarium
@Badulake no problems, happy to help.Planetarium

© 2022 - 2024 — McMap. All rights reserved.