Distinguishing between machines with client credentials flow - oauth-2.0

It seemed like client_credentials flow was appropriate for a machine-to-machine communication between our system and third parties for importing and exporting data.
But if I have two third parties p and q say and users on our system u and v say, then I need to know which of p and q can acces the data of which of u and v.
For example: user u grants access to p (but not to q) and v to q (but not p).
I can give different client secrets to p and q but when they present thir secret to IdentityServer in order to obtain a token I need to know which of p and q it is and add a claim to the token that my controllers can use to determine which of u ad v's data is visible.
I can implement ICustomTokenRequestValidator to intercept the secret and look it up in the configuration context, but the Id column is not in the model class, so I could abuse the Description column as a foreign key to my table of third parties -- seems hacky.
Is there a standard/recommended way to resolve this situation -- that different machines have different data visible to them (different claims in their token)?

If you use client_credentials flow then there is no user-interaction involved.
Each third party should have it's own client with corresponding client credentials. Do not share the same credentials over boundaries, i.e. between different third parties. By having separate clients for separate parties, you minimize the security risk and minimize the impact when having to revoke or change the credentials.
With that said, in the Client Credentials flow the Authorization Server returns a token if the request is valid - it does not know from the request if the user authorized the request or not because the user is not involved. But it authenticates the client. So, if you want to differentiate between several third parties, give them individual clients. If you can't add a claim to the token.
Scopes and claims are the tools to define what a client requests to do. Some authorization can be done in the Authorization Server. For example, an Authorization Server typically only allows a client to request certain scopes.
Use then claims (the data in the token) for a fine grained authorization in the API. It depends on your use case. If the API is supposed to return a filtered result, i.e. only return data from users that are ok with it, then the API needs to be able to look this information up somewhere, as #gary-archer states. Use an applicable claim from the token that enables the API to identify the client or third party. It can then use this data for the lookup. For example, use the client_id claim but it really depends on the Authorization Server which claims it adds to the token.
If the client already knows beforehand which user's data it is going to access, it could add the user-id when requesting the token and - once again - depending on the capabilities of the Authorization Server - get a token with a user_id in the claim set. Then the API can use the client_id and user_id for a lookup.
How you collect the users' consent is out of scope and happens out of bound. It's part of your business logic.
At the end it is all about designing the token. Here is another article on Centralizing Identity Data

The standard option here is to include custom claims from your business data at the time of token issuance. Rather than machine to machine I would describe your scenario as B2B.
EXAMPLE USE CASE
Consider an API called by business partners who act as suppliers of inventory to your system. In this case a useful access token might look like this, and is what I would aim for:
{
client_id: 1hvf367g
supplier_id: 42
exp: ...
}
API requests for stock items could then authorize based on the supplier_id value in the access token. Eg by running a SQL query on the business data, filtering on the supplier ID. All of this keeps your API code simple.
DATA MAPPING
For this to work you need to design onboarding. Eg a button click to create a supplier in your system might create the OAuth client using Identity Server, then save the client_id to a suppliers table in the business data.
Not all identity systems support issuing custom claims in the above manner. An alternative design is to just include the client_id in the access token, and look up the supplier ID from business data when your API receives an access token. This tends to add complexity to API code though.
SUMMARY
I think your question is really about designing business permissions, and OAuth alone cannot solve it. If I'm right then the Claims Best Practices article may be useful.

Related

OAuth2: Client Credentials flow

Problem: I am currently working on making a REST Api available to clients (the client in this case is not the normal end user who orders the articles, but the client's Web Api that communicates with my system). In order to order different products in my system, each customer may have several accounts for each country separately. Authentication is done by authenticating the client's WebApi application to my system (machine to machine). So it looks like this should be done using OAuth2 Client Credentials Flow based on the article https://learn.microsoft.com/en-us/azure/active-directory/develop/authentication-flows-app-scenarios#scenarios- and-supported-authentication-flows, but I have doubts about the issue of client accounts for each country separately.
Question: This should be solved by creating a ClientId and ClientSecret for each client account per country separately or, however, one client account should be created, while the country should be sent by the client in each request to the Api or before adding the country as a scope or claim to access token.
Additionally, I am not sure if Client Credentials Flow is a good choice in this situation, so I would be very grateful for any suggestions.
CLIENTS
Ideally each client company should have a single client credential for getting access tokens. In sone cases, such as when there are different legal subdivisions, this can be extended. By default use a single value, but you need to understand your clients.
A client credentials flow between companies can involve stronger credentials if needed, such as JWT client assertions or Mutual TLS - as in this advanced tutorial.
CLAIMS
In your system you should map domain specific data needed for authorization against each client ID. This might include country specific access to products or whatever makes sense for your scenario.
This data can then be either included in access tokens at the time of issuance, or looked up when an access token is first received, then cached for subsequent requests with the same token. Step 3 of my Authorization Blog Post explains a couple of design patterns here.
API REQUESTS
How the client gets data in API requests is interesting:
Is data for all countries owned by the caller? If so let them select the data they want via a country parameter during API requests.
If the API client shoild never be able to see data for a country, that suggests that in at least some cases you need different clients per country.
SUMMARY
Define clients in terms of what makes sense for those companies. Avoid an explosion of clients in order to manage access rights for the same data owner. Exact solutions depend on your domain specific requirements. OAuth is a framework that is meant to be adapted.
If your entire existing data-model silos 'countries' by a concept of an account, then a set of credentials per account might be the easiest.
But it sounds to me that your data-model doesn't fully capture your actual business well. It sounds to me like you have a concept of a 'customer/client' that has access to one of more 'accounts', each representing a country.
So a more correct way to model this might be to structure your API so that a single API client can access all of the related accounts, and your API should perhaps be structured so that the idea of an accountId is somehow passed (usually in the URL for REST apis). For example, each API endpoint can be prefixed with /account/123.
To me this is more of a data-modelling and API architecture question than anything OAuth2-specific.

How to secure an API with OIDC/OAuth

I'm trying to better understand how to make use of OIDC/OAuth in securing a restful API, but I keep getting lost in terminology. Also when I research this question most of the answers are for Single Page Apps, so for purposes of this question assume the API will not be used for an SPA.
Assumptions:
Customers will access a restful API to interact with <Service>.
It is expected that customers will create automated scripts, or custom application in their own system to call the API.
Once setup it is not expected that there will be a real person who can provide credentials every time the API is called.
<Service> uses a 3rd party IDP to store and manage users.
3rd part IDP implements OIDC/Oauth and that is how it should be integrated into <Service>
Questions:
What OIDC/OAuth flow should be used in this situation?
What credentials should be provided to the customer? client-id/client-secret or something else?
What tokens can/should be used to communicate information about the "user"? E.g. Who they are/what they can do.
How should those tokens be validated?
Can you point me to any good diagrams/resources that explain this specific use case?
Am I missing anything important in the workflow?
It sounds like these are the requirements, if I am not misunderstanding you. The solution contains not just your own code and is more of a data modelling question than an OAuth one.
R1: Your company provides an API to business partners
R2. Business partners call it from their own applications, which they can develop however they see fit
R3. User authentication will be managed by each business partner, resulting in a unique ID per user
R4. You need to map these user IDs to users + resources in your own system
OAUTH
Partner applications should use the client credentials flow to get an access token to call the API. Each business partner would use a different credential for their set of users.
Using your own IDP to store users does not seem to make sense, since you do not seem to have an authentication relationship with the actual end users.
Access tokens issued to business partners would not be user specific by default. It is possible that a custom claim to identify the user could be included in access tokens - this would have to be developed in a custom manner such as via a custom header, since it is not part of the client credentials flow.
Access tokens would be verified in a standard OAuth manner to identify the partner - and possibly the end user.
DATA
Model users in your own system to have these fields, then store resources (such as orders) mapped against the User ID:
User ID (your generated value)
Partner ID (company the user is from)
External User ID (an ID that is easy for partners to supply)
Typically each partner would also have an entry in one of your database tables that includes a Client ID, name etc.
If you can't include a custom User ID claim in access tokens, partners have to tell you what user they are operating on when they call the API, supplying the external user ID:
POST /users/2569/orders
Your API authorization needs to ensure that calls from Partner A cannot access any resources from Partner B. In the above data you have all the fields you need to enable this.
SUMMARY
So it feels like you need to define the interface for your own APIs, based on how they will be called from the back end of partner apps. Hopefully the above hints help with this.

Securing URL with user owned resources in OAuth2

I'm aware of how OAuth2 and OIDC can use custom scopes and token introspection to secure an URL like this:
/users/me/documents
I can give this URL the documents:view scope and when receiving the token from the authenticated user, I can ask the authorization server if this user has the correct permissions. Then I can use the preferred_username claim or similar to see who /me actually is.
But what if I have a resource which is accessible by multiple users? Let's say a user has documents but they can be viewed by his direct manager. To retrieve the employee's documents as a manager, I'd need to have an url like this:
/users/${userId}/documents
How could I enforce it in a way that only the resource owner and direct manager can view this resource? I don't want everyone to access everyone's documents by knowing the userId. I could grant access as a whole to all users with the manager role, but that's not specific enough.
I'm aware there's the UMA extension where users can grant access to resources on his behalf to other users, but it's not the user who grants permission. It's the system who states in this case that managers can access their employees documents.
Would it make sense to write a custom policy which extracts the ${userId} and performs the check? Or should this not be done by the authorization server at all and be done by the resource server instead? Perhaps a different approach to reach the same goal?
Finer grained authorization like this is done with claims rather than scopes. There may be business rules around which docs a user can see, eg:
A user can access their own docs
An admin has view access to all docs
A manager can view docs for people they manage
In an access token this might be represented by these claims:
userId
role
Claims are often domain specific like this and the preferred option is to add them to tokens during token issuance. At Curity we have some good resources on this topic:
Claims Best Practices
CLAIMS AND AUTHORIZATION
The Authorization Server issues access tokens and then APIs (resource servers) verify the access token and use the token data to apply authorization rules (which are often domain specific) on every single request.
Claims are often used when dynamic behaviour is needed - they are runtime values that derive from the user identity, whereas scopes are fixed design time values. In your example an API might also need to vary SQL to retrieve documents based on the user identity.
There are more complex variations on this theme, such as an API calling a system such as Open Policy Agent, so that documents returned are determined by rules configured by a security administrator. That policy would still involve using claims from the access token though.
EXAMPLE CODE
If it helps, here is some sample code of mine that show the type of approach when enforcing domain specific authorization rules. Typically you need to filter collections and check access to individual items.

Using Auth Tokens to grant access to a specific item

I have an application which provides authenticated users with views into data about various objects in a database. There's another application in our ecosystem that provides different views into some of the same objects, using its own permission model. We trust that other application's permission model, and would like to allow them to issue access tokens to users who haven't been authenticated through our application's usual method, so those users can only view specific objects that the other application has verified they have access to.
Rather than coming up with our own spec for the communication between these two applications, I was wondering if there's already a standard approach available via something like OpenID Connect. OIDC seems to handle most of the gnarly details we'd have to consider in a case like this, but the one aspect where it doesn't seem to fit is that its access tokens seem to be general-purpose, rather than calling out a specific object that the user has access to. It says "Here's a user who can access your application", but not "Here's a user who can access Item 123".
Is there a standard for using an access token to grant access to a specific item, preferably using OAuth 2 and/or OpenID Connect? Am I correct in assuming that using an item's ID as a scope on the access token would be an inappropriate use of OAuth scopes?
I've always found the best design for most real world apps to be like this:
OAuth 2.0 based tech identifies the user
You then lookup user details at an application level to enforce authorization
OAuth 2.0 scopes etc cannot handle things like this:
You don't have access to account 123
You don't have access to region US
So I tend to look them up from the user id in the token after login. This tends also to be much easier to extend, if for example the items and user rights in the external app grows over time.
For more concrete info see my write up on API Claims Caching.
Also here is an example of the coded algorithm in a Rest API, resulting in a claims object that can be injected into logic classes.
In your case the custom claims provider would be the external app, and you could query claims from it, for data that does not really fit well into OAuth tokens.
Just my thoughts - not sure if it will fully work for you - but I've found this to be quite an adaptable solution, which often puts responsibilities in the right places.

How is the correct way to add specific information for Resource Server inside OAuth2 access token?

I have a scenario where my Resource Server (RS) can connect to multiple databases and it can be multi-tenant. It has an environment configuration where I can say: Env A points to database A and belongs to tenant A; Env B points to database B and belongs to tenant B;
Our desktop apps store that information in a data structure that is sent to RS on every call and our web apps store it inside ASP.NET Session. The user select the environment at login.
Now we are moving our APIs completely to REST services and protecting them with oAuth2 using IdentityServer4.
I need to send that environment value to the APIs and I think that should be part of the access token.
The first question is: Is that correct? Can a access token have that kind of information?
The second is: Which is the best IdentityServer4 service that I should extend to inject that value as a claim inside access token and consequently inside ClaimsPrincipal.
The third is: Sometimes I don't have a user to select the environment at login (client credentials grant, for example). In this case, is the correct treat environment as a client claim? There is a way to have dynamic client claims?
Sorry about the long question!
Regards,
Diogo
If that claim is about the users (or clients) identity - yes - it is a candidate for the token.
Add the claim to the resource scopes that represent your API - this way the claim type will be requested in the profile service and you can add it to the token.
Client claims are not dynamic though.

Resources