I've got following setup:
IdentityServer4 using asp.net core identity
Accounts app - where user can edit his account data and users can register
Portal - where users can login and select an organization to which they're connected (same as in azure devops)
API that is used by portal
Does anyone have experience setting up something similar ? My question would be how to authorize the calls at API level to make sure he is only editing entities that belong to his organization.
Thanks !
One way could be to use a tenant claim within each bearer token, so that when a user changes an organization on the portal (in case one user has access to several orgs), you perform relogin to the chosen tenant, or just request a new access token with a chosen tenant_id inside.
You can use this and this answers for the reference on how to pass a custom parameter (such as tenant id) with your request to Identityserver.
Related
How do I best configure Keycloak so that a user needs to have an account for a client to be able to login into that client?
I have to replace a proprietory SSO-Impl. It deals with users, roles and clients much like Keycloak. However, it also knows about accounts. A user is only allowed to login to a client if he has an account for that client.
In Keycloak, if a user simply exists in a realm he may login to a client of that realm. Nothing else is needed. So no "account" is needed. In the old application, he needs an account as well.
What functionality in Keycloak is best suited to overcome this difference?
I have one idea:
Create a client-role in each client namend "HasAccount" and assign it to users. Then, restrict access if that role is missing.
This is discussed here: "Restrict client access in a single realm with keycloak"
It has at least two drawbacks:
It mixes authentication and authorization in the legacy app. I can understand that. But creating a role was already a workaround. That is why I described my initial problem here.
I have clients in 3+ languages/technologies. Adding functionality there seems like more work than in Keycloak.
Last remark:
Before you ask "This is not single sign on" anymore. It is only for administrative purposes. The admin can allow users to login into a client or not by creating an account or not. The user does not have to login a second time. If he is logged in in App A and has an account for App B, accessing App B works without logging in there.
A user is only allowed to login to a client if he has an account for that client. is really not a task for Identity Provider (IdP). It provides only identity and not authorization.
Of course you can ignore that and implement authorization as well. See: User attribute based web service access control by Keycloak
From the design perspective I would add auth reverse proxy in front of legacy app (but it isn't a best solution for SPA apps). Auth proxy will provide authentication via OIDC protocol and also authorization. Legacy apps may keep own OIDC authentication - it will be seamless auth from the user perspective, because SSO will be used.
Account entity - you can use group entity in the Keycloak instead of original account.
CompanyA is integrating with CompanyB where CompanyA's users will be buying devices of CompanyB.
CompanyA wants to show user's device(CompanyB) details on their app by calling
CompanyB's API on each user login.
CompanyA user is authenticated on CompanyA IAM.
CompanyA has to call register device when user tries to add an device first time.
Help me to identify the flow which i can use to query particular loggedin user's device only.
Do i need to create duplicate user account on CompanyB's IAM?
If i use client credential flow for API to API call, access token given by CompanyB is only provides access for API calls but it does not tell that on behalf of correct user only call is invoked.
Assume that CompanyA uses IdentityServer or any other provider as IAM and CompanyB uses Azure AD B2C.
Any other approach?
Please see below diagram,
You should be able to do this by making the Company B API multi-tenant in their Azure AD.
There are other options surely, this is just the first one that came to my mind.
Overview of the multi-tenant pattern
You would have to do admin consent on it to get the API's service principal in your Azure AD tenant.
The Company B API can give you an endpoint for doing this, redirecting you with the proper parameters to the authorization endpoint. How to send a sign-in request
After doing this, you should be able to then require permissions on the API from Company A API in your tenant (configured in Azure AD).
Configure a client application to access web APIs
After doing those things, your API should be able to use On-Behalf-Of grant flow to get an access token for Company B API.
Using Azure AD On-Behalf-Of flow in an ASP.NET Core 2.0 API
Company B API must be configured to accept access tokens from another issuer than their Azure AD of course.
In general multi-tenant scenarios, the issuer validation is commonly turned off.
If Company B wishes to have control over this, currently they will have to explicitly list the valid issuers.
Issuer values look like this: https://sts.windows.net/31537af4-6d77-4bb9-a681-d2394888ea26/, the GUID is your Azure AD tenant id.
The Company B API can extract the tenant id and user object id from the access token, and authorize the user to resources based on them.
I was looking at the AWS side and looks like they have something that could meet the requirements
https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_common-scenarios_federated-users.html
Was wondering if something like this exists in Azure.
Background Explanation
When it comes to Identity Server 4, I come to a road block when i think about the design of user management in the client applications.
At this point, I have Identity Server setup using ASP Identity User Accounts as its user store.
I've built the UI for adding users to the Identity Server User Store.
I've tested setting up a client which is an MVC application, and I'm at the point where i can successfully authenticate with Identity Server and receive the openid identity token in my client application.
Identity Server is providing my client application with authentication.
Now, i need to concentrate on authorization within my app. This is where i get stuck, I need to create users local to the application, where user privileges within the application are stored.
I will need to link / associate the user in Identity Server to the user in the client application.
One way to do this would be to store the sub in the identity token as a user claim in the client application database (Asp Identity). This way, when a user authenticates i can locate them in the local db, based on the sub in the token.
The sub would have to be the user's unique id in the identity server user store. This way, if the user's email is changed, we can still link the two user accounts.
The user accounts in the client application wouldn't require passwords or email addresses, it would be purely claims and roles that are used for authorization across the application, as well as any other application specific information.
Question
Communication between Identity Server and the client application must need to exist when creating users in the client application?
We need to associate the two accounts?
We need to ensure that the client user account being created also has an Identity Server user account to be successfully authenticated?
When in the process should these tasks be complete? Im looking for some guidance in the flow of the communication between the two applications?
EDIT
Is it feasible that there are no User Accounts in the client application at all?
By this i mean that all user claims for a user are stored in the User store of Identity Server.
When a client authenticates with the IDP it requests only the user claims that are specific to the client application.
Example user claims in the User Store :-
"clientA_role" : "administrator"
"clientB_role" : "user"
When Client Application A authenticates, it requests only the scope clientA_role
This feels bad!
Any suggestions?
If you have many client applications then the way I recommend to do the user management is:
User Management Service:
Create a separate service for users management that identityserver will use as user store and applications will use as user repository when user metadata is needed.
Also why would you do something like:
Example user claims in the User Store :-
"clientA_role" : "administrator"
"clientB_role" : "user"
why not
just "roles": "user"? and in your application you will protect your resources using Authorize[Role] annotation.
do not create different fields for different applications, think of it as general user management service, I am pretty sure that standardizing your identity management will make it easier and will gain you maintainability and flexibility.
IdentityServer service handles identity management:
might be a good idea to keep user store inside the same service providing authorization if you feel that your application does not have such deep users management needs.
again in this case, store standard claims and return the claims you need inside an id_token or access-token.
Update:
For a specific user that have different roles in different applications:
let us say we have the following:
1- User1 has user role in first app and admin role in second app, then
User1.Roles{"FirstAppUser","SecondAppAdmin"}
2- User2 has admin role in both apps, then:
User2.Roles{"FirstAppAdmin","SecondAppAdmin"}
we have an existing "private/internal" API (non MS/Azure) that we would like to protect with oAuth2 provided by Azure AD, so, that the user's access to the API could be maintained by the Azure AD administrators.
It seems like the way to do this is to configure the API as a web application in the Azure AD. Then, users can get the authorization token from the Azure oAuth2 server and send it to the api (e.g. from a single page web app).
The API is expected to validate the scope, as per my understanding, received in the token and make a decision regarding the access.
But I can't figure out how to configure the API access scope in the Azure AD against a user. I.e. how to link a particular user and the API scope in Azure AD?
Can someone pls advise?
Thank you.
When you configure the app, you can enable "User assignment required" (in the Enterprise application Properties in the Azure Portal), and then configure which users or groups should have access.
Alternatively, if you need more granularity, you can use the Role based access, where you define roles in the application manifest (https://learn.microsoft.com/en-us/azure/architecture/multitenant-identity/app-roles), and then assign users to the different roles.
The [Authorize] attributes on the API controllers or on actions in them can then be configured with the required roles to access them, like so:
[Authorize(Roles = "Admin, Writer, Approver")] (any of the three named roles have access).
There is a sample which demonstrates this.
I recently learned how OIDC provides user identity on top of OAuth 2, and also managed to create an OIDC login to my webapp. However, when it comes to the authorization part, it is unclear how the application should know what scopes to request for the API?
For example, certain endpoints should be protected with the 'admin' scope, as well as respective UI elements should be hidden from non-admin users. The enforcement part I can implement easily, but when the user logs in, the app needs to know if the user is an admin or not - i.e whether or not to request the 'admin' scope / show the admin-related UI parts.
Is there any standard way of representing app-specific details in the user profile that comes from the IDP? What disturbs me most is that such information - to my knowledge - cannot be added to user profiles that come from e.g Google/Twitter/Facebook, so one needs either a) an extra layer (IdentityServer/OpenAM/etc), or b) extra user profile data in the webapp's DB.
My first thought was to add custom claims by doing a) - but when it comes to integrating with existing IDPs, it might not be possible to adjust all the user profiles (e.g large corporate databases), so I am assuming the app itself should be flexible enough to translate existing user profile data to 'admin/not-admin'.
Is there a better solution?
In my opinion it's not a good practice to base your authorization on the claims returned by your identity providers.
This responsibility should be delegated to the UMA server. It's up to him to decide or not to grant a request access to a protected resource.
For example, image that we have the following scenario :
Context : An e-commerce enterprise has internally developed a tool used by his marketing team, to retrieve information about his most loyal clients.
The application has been developed in WPF and interact with a RESTFUL API to retrieve the clients.
Only this application and users that belong to the marketing group are authorized to retrieve the list.
Problem : How the application can access to the protected operation ?
workflow
Solution : the workflow is made of three big steps :
Identity token : retrieve an identity token with implicit grant-type. The token is returned to the client in a callback parameter
RPT token : the identity and access token (valid for the scopes uma_authorization & uma_protection) are passed in the request to retrieve the RPT one.
When it is received by the WPF application, the token is passed in the Authorization header to retrieve the loyal clients. Both parameters are required by the authorization policy.
Check RPT token : the token is checked against the introspection endpoint, this endpoint is offered by the UMA server.
As you can see internal policies are used by the UMA server to grant / or not access to the resource.
Authorization policy is out of scope of the UMA specification, you have to define it yourself :'(.
I have developed a product (open-source) to easily manage authorization policies. If you are interested you can test the demo here :
http://lokit.westus.cloudapp.azure.com/Demo
Note : If you are not satisfied about claims returned by external identity providers, you can always enrich them (read the use case : "assign role to resource owner" in the documentation)