Separate UserStore By Client - identityserver4

I'm using Identityserver4 and ASPIdentity for user management. I want users dont have access to all applications(clients). I'm going to add table like UserClients, which specifies allowed applications for user. I'm interested in your advice, whether it is possible and does not conflict with the principles of SSO?

IdentityServer takes care of client access configuration and user authentication, but not of authorization at client/user level.
Users are not bound to a client. Thus, as you've noticed, all users have access to a client. IdentityServer authenticates the user, regardless which client is used or whether the user logs in at IdentityServer directly.
So it is the client (and also api) that has to authorize the user.
It doesn't matter if the user is already authenticated (SSO) or not. It is the responsibility of the client / api to determine whether the user is authorized. In order to solve this 'problem' they've build the PolicyServer, to decouple authentication and authorization.
By adding middleware (as done in PolicyServer) you can grant or deny a user access.
You can add some discriminator in IdentityServer, e.g. a specific claim type / role, use a seperate authorization server or use resource based authorization, e.g. when a (business) user is present then the user has access.
In the end, authorization is done by the resource that needs to be protected, because only the resource can interpret the claims and business rules.
All you have to do is short-circuit the pipeline in case the authenticated user has no access.
It's perfectly fine if you want to 'disable' SSO. Simply ignore the IdentityServer cookie (in the client), thus forcing the user to login (for that client).
But you should not deny authentication (e.g. return invalid login) when the user has no access to the client / resource.
That's why there is http status 401 Unauthorized and 403 Forbidden. So you can notify the user that it is not authorized to use this client.
Regarding your setup, in your case you can also add claims to the UserClaims table (instead of creating an additional table) to configure the allowed apps, e.g. user A:
http://mycompany/appname = app1
http://mycompany/appname = app2
http://mycompany/appname = app3
Add the ClaimType to the IdentityClaims table and the ApiClaims table in order to make sure the claims make it to the access / identity token.
Add middleware in the client / resource or add business rules (policies) where you check the claimtype and required value. If not present then deny access. E.g. app1 should look for the required claim type http://mycompany/appname with the value 'app1'.

Related

How do I check if the user from a non-Graph Azure AD token is a member of an email distribution list in an APIM policy

Have an API in Azure API Management (APIM).
The API operation validates a JWT generated by Azure Active Directory (AAD) using a scope from a backend app registration (the scope is NOT User.Read). Note: client id is another app registration which is an authorized app of that backend scope.
After the JWT is validated, am I able take that token, extract user info out of it and verify if the user is part of a email distribution list (DL)? If so, how to do it within an APIM policy?
I am aware of MS Graph APIs. Using Postman I can confirm the DL is listed in the tenant's groups and can get its group ID. I can also confirm the user is a member of the group. The bit I'm stuck with for Graph API is that it needs a different token to the one supplied by the client application (due to he scopes being from different domains custom app registration vs graph) and I'm stuck at this point. Should I make the client app also get a graph token and pass it in a separate header, or is there way to orchestrate things from within APIM or something else?
The non-APIM part of this solution is provided by a Microsoft article. I've summarised those and combined with the APIM parts in the following steps:
In Azure, create a new Azure App Registration (note the client id for later)
Under "Certificates and secrets", add a client secret (note the secret for later)
Under "API Permissions", add a new MS Graph Application Permission (can be User.Read.All, Group.Read.All, GroupMember.Read.All depending on your situation). MS Graph's "groups" includes both AD groups and Distribution Lists (DL). Note: don't use Delegated permission.
Application permissions allow the authorized app to enquire about any user/group. You will need an Azure Admin to Grant Admin Consent for the App Registration to have the chosen Application Permission.
Now in Azure APIM, go to your API and edit the inbound policy.
Validate the JWT from the user making the call (See validate-jwt or newer validate-azure-ad-token) to ensure the User is authorized to call this API.
Extract the oid claim from the JWT (this is the user ID I'll use for the graph call) and save it in a variable using set-variable policy
Add a send-request policy request an auth token for MS Graph using client-credentials flow (this is when you'll need the client id and secret from earlier App registration). Note: secrets should be stored in a secure store like KeyVault but that is outside the scope of this answer.
Extract the access_token field from the JSON response body and put it in a variable using set-variable policy.
Create another send-request policy, but this time to the MS Graph endpoint. For User.Read.All permission you'd use /users/<userIdFromJwtOidClaim>/memberof/<groupId>. MS Graph v1.0 API Reference, and pass the access_token in the Authorization header using <set-header> element.
A status code of 200 indicates the user is a member of the group. IIRC A status code of 403 indicates the user isn't a member of the group.
Use a choose policy to perform logic depending on the user's group membership.
Use return-response policy to send a response back to the user.

Azure AD: How to get both security groups and application roles into access token

I am facing a scenario where my .NET Core app (Azure Web API) will be accessed in two ways:
client_credentials flow
delegated permissions (user) flow
I am using Microsoft Identity Web to authorize and authenticate requests via AD on middleware level. Then I would like to do additional authorization inside my controller methods to check the following:
In case of application call (client credentials), check that the provided access_token contains a specific roles claim that matches with the application role defined in app registrations -> app roles.
In case of signed-in user call, check that the provided access_token contains specific AD Groups (security groups) assigned to that user in Azure AD.
Flow #1 works, but if I enable flow #2 by clicking on token configuration -> add groups claim -> Security Groups -> emit groups as role claims in access token, then the app roles are no longer available in the client credentials flow inside the access token (as in the below screenshot), presumably because it overwrites the roles claim with the security groups (which do not exist for applications).
What is the correct way to do this, or achieve an equivalent situation in a different way?
The requirement is to differentiate controller method access where application A can call e.g. a read-only endpoint 1, but cannot call write endpoint 2, whereas application B is able to call write endpoint 2. The same differentiation should be done also for users on AD-group basis.
You can include a groups claim in your token. You just need to modify the "groupMembershipClaims" field in application manifest:
"groupMembershipClaims": "SecurityGroup"
Then the token will contain the Ids of the groups that the use belongs to like below :
{
"groups": ["{group_id}"]
}
This method won't overwrite the roles claim. So for delegated permissions (user) flow, you need to check the groups claim instead now.

How can we impersonate a user in Identity Server 4

I have a requirement where Customer care people login with their credentials and have to impersonate the actual users.
You can implement service account flow for same. Using it you can get token of respective client with all allowed scopes to client (This client has all the scope required to access customer care resource).

Microservice Authentication/Authorisation architecture

ARCHITECTURE
Need to know how to authorise a user for each microservice in the given architecture. I.e. deny requests to each microservice if they are not authorised to have that module as per user data in the main db.
I also need to share the user from the main db between each microservice db to maintain data integrity.
Is there a way to do this? Or do I need to re-create a user table in each microservice? Basically meaning that everytime a user wants access to a microservice I have to create a new user (POST from main api) in the microservice (at the loss of data integrity).
I haven't tried anything yet looking for suggestions/a solid and SECURE architecture. Each microservice will be hosted on a subdomain on same PAAS as to avoid transfer attacks and reduce delay.
deny requests to each microservice if they are not authorised to have that module as per user data in the main db.
Assuming you are using OAuth2.0 with JWT access and refresh tokens, you could use the "aud" claim of JWT token to carry the authorizations or entitlements or permissions to access individual microservices. In the "aud" claim, you could provide the individual service URL, or service id that the user is authorized to access. At the individual micro service (or resource server in OAuth terminology) you need to check if the JWT carries the particular microservice UR or id in its "aud" claim. If not, access will be denied.
I also need to share the user from the main db between each microservice db to maintain data integrity.
That's not usually how this is done.
You usually have an authorization server. That is the master of all the user information. Any unauthenticated user trying to access any service will be redirected to the authorization server and asked to provide their authorizations after authenticating themselves. Once the authentication is done and authorizations are given, the user will be redirected to the service page with appropriate access and refresh tokens which will thereafter accompany all user requests. Even when the request goes from User A -> Service B -> Service C. Service B and C would then need to check:
Is the JWT valid (not tampered by anybody)? (using the digital signature in the JWT - usually HMACSHA256)
Is the access token (usually a JWT) still valid as per the expiry date and time set in the "exp" claim of the JWT by the authorization server?
Is the service URL or id present in the "aud" claim?
If all the 3 questions are answered as "yes", access is granted and each service can find out the user name from the "sub" claim of the JWT. The user name can be the logged for audit
I also recommend you read this answer.

Adding custom claims into AAD token

Is it possible, while acquiring an access_token (Client Credentials grant), to instruct AAD to inject certain custom claims with certain values into the access_token being issued?
I need it to avoid sending extra context information to my service through such a "disconnected" means as HTTP Header for instance. Instead I want the token signed by AAD and containing everything AAD stamps into it by default plus some small pieces of information controlled by the application acquiring the token. All this will help my service to apply proper authorization once this token is received by the service.
I looked at the above, and I am clear that you are not looking for claims augmentation as it was described in the blog.
As I understood, you are looking for the right way to authorized your application using AAD tokens.
If my understanding is correct here is my answer.
It took me quite sometime to remember how I did it before and the caveat was missing the graph permissions for:
Directory.AccessAsUser.All
Directory.Read.All
Directory.ReadWrite.All
Now let me type down the steps one by one, but care less to the order of these steps may not be correct, just do the steps in any order you want.
Step 1: In AD, in the App registration
Register your Web Application,
Copy the Client_ID
Step 2: Go to Expose an API
Add a scope or more (This is what you are going to see as a claim and role in the token)
Add the client Client_ID
Note: this is basically for 2 applications one calling another, but in this example and your case, you have one web application that needs to authorize on itself.
Next: In the API permissions
THIS IS A MUST grant admin consent delegated permissions for MicrosoftGraph
Directory.AccessAsUser.All
Directory.Read.All
Directory.ReadWrite.All
Additionally: Give permission to the scope that you added.
Then: In the App roles:
Add the Application roles
Then: In the Enterprise Applications:
Assign that role to the users or groups that you want to access this.
Finally: In the application configuration file
Update the Client id
You are done.
I hope that was what you were looking for.

Resources