I have the following setup:
IdenetityServer3 for auth (OAUth / OpenID)
ASP.NET WebApi back end
Ember-cli UI
I have the auth flow working nicely - I haven't managed to get the admin UI working yet but I can prepopulate users, scopes and clients so that's fine.
When the user auths against IdentityServer3 they are redirected back to the UI and the UI uses the oidc-client to retrieve the users info from the JWT - the client also uses the bearer token to send to the API to auth requests - all good.
My problem is that the IddentityServer is in charge of authentication / authorization - but the API doesn't yet have any notion of a user - but it needs that.
What is the best way of syncing user info between IdentityServer and my API? How can I best manage things like roles and user hierarchy? Is there a way for the API to query IdentityServer for this? It seems silly holding a copy of the user info locally to the API when we have an identity server that manages all of this.
IdentityServer exposes a UserInfo endpoint (https://identityserver.github.io/Documentation/docsv2/endpoints/userinfo.html) which you can call to retrieve additional information about a user.
However, wherever possible, try to achieve what you need to by passing a token that has the relevant amount of claims so that you can make AuthZ decisions without requiring a call to Identity Server. This reduces coupling, and means you have less outbound calls from your API.
E.g. When you sign in, a JWT token could be created that contains the roles the user is a member of plus the users unique id (sub claim)
{
"iss": "https://my.api.com/trust",
"aud": "https://my.api.com",
"exp": 1512748805,
"nbf": 1481212805,
"scope": "openid",
"sub": "83b0451a718b4d54b930d6fe9cb7b442",
"idp": "site",
"roles": [
"role1",
"role2"
]
}
Your API can now just check the claims presented to it and say 'To call this API endpoint, the token presented to me must have role2 in the roles claim'.
You can also do this with the scopes, using the scope attribute
A well designed JWT token will contain the right amount of information to make AuthZ decisions without requiring lots of additional calls, whilst keeping the overall size of the token as small as possible - remember, it's included on every request.
Related
We have a scenario where we have different apps in the backend that needs to do some operation between them. Those apps are registered in the Okta console and have their own workflow. They both allow access to users that authenticate through an access token that they will get through a process the widget on the web. The two services needs to perform operations between them. For example, on one service we need information about products on another service. But it's not a "user" request, it's a service to service.
As far as my understanding goes, we still need to send a request as it would a normal user, so we need a bearer token to authenticate the request.
I cannot find in the docs a way to request an access token on the backend. I only found some libraries that can help providing a callback uri and multiple step process where you need to have a window to pop up to interact to insert your Okta login.
Is there any way to request an access token as an API call? In the backend services we won't have windows to pop up and authenticate?
We tried to use Okta-auth-js but seems to be more "front-end" oriented as most of the methods are "browser-only".
Yes, you need to create OIDC application of type "OAuth service", which will created "client credential" flow app for you. It supports calls to /token endpoint using client_id and client_secret available after the creation of the app in Okta. As a result of your call to /token directly from the backend, you will obtain an access token for machine-to-machine communication (no user context)
We have an application that loads information(user specific) from the external system upon successful authentication, to avoid round trips to the external system for each api call, we are planning create a custom JWT token with user specific information for the first time user authenticated, then the token is send to user in each response header using http interceptor and in the front-end we are including the custom token in every request along with authorization token. Is this correct approach? Do you have any alternative suggestions?
I have looked into other distributed caching techniques like redis but not so appealing for a small usecase. Our payload length does not exceed 4 to 5K hence inclined towards the JWT option
it is ok to include user information that allows you to handle the user authorization inside the access token. Just beware of the privacy implication and perhaps not include personal information like social security number or date-of-birth or other identifiable information.
Also, make sure the token size does not get to big. The other option is to lookup and cache the user information in the API's when it receives a new access token.
Some systems including ASP.NET Core do store the token inside the session cookie in an encrypted form, so that the end user can't see or access the stored tokens.
If you are developing a SPA application, the using the BFF pattern is one approach.
I'm creating an API server which will be consumed by a mobile app that I will work on later. I have yet to see any reference of API best practices related to user flow and returned data even after searching for several hours.
My question is whether the login response of an API should return the a personal access token with the refresh token along with the user info? Or should I just return the token and make another API call for getting the user info.
I could just do what I have in mind but I'm trying to learn the best practices so that I don't have to adjust a lot of things later.
I need suggestions as well as good references related to my question.
Thank you.
It depends on what you are using for your authentication. If you are using libraries like Laravel Passport or JWT, you can have the token endpoint which returns the access token, refresh token, validity period and the token type (Bearer). You can then have an authenticated endpoint which will be used to get a user's profile based of the token passed in the request header.
However, if you go through the documentation for those libraries, in most there is an allowance to manually generate a token. You can use this in a custom endpoint that will return the token as well as the user profile Passport Manually Generate Token.
If you are using JWT, you can also embed a few user properties in the token itself. The client can the get the profile info from the JWT itself without having to make a round trip to the server. Passport ADD Profile to JWT
If you have a custom way in which you are handling authentication, you can pass the token as well as the user profile in the same response.
In the end, it's up to you to decide what suits you best.
Have you looked at OpenID Connect? It's another layer on top of OAuth 2.0 and provides user authentication (OAuth 2.0 does not cover authentication, it just assumes it happens) and ways to find information about the current user.
It has the concept of an ID_token, in addition to the OAuth access token, and also provides a /userinfo endpoint to retrieve information about the user.
You could put user information in your access token, but security best practice is to NOT allow your access token to be accessible from JavaScript (i.e. use HTTP_ONLY cookies to store your access token).
I am planning to change the ASP.NET Web API 2.0 which includes Authentication and Authorization and all the services into Microservices architecture.
My Question if I create a central microservice to handle authentication and authorization. How do I authorize the users sending the request with their tokens to other services?
To elaborate the question:
Let'say I have three microservices.
1 ) ASP NET framework handling authentication and authorization, Which will authenticate a user and sends a token.
2 ) Orders service, Which will receive the requests with the token in their headers. (ASP NET core)
3 ) Accounting service, which will receive the requests with the token in their headers. (ASP NET core)
How do we authorize the users when they call service 2 or 3?
And Is this an ideal approach?
Instead of authenticating external requests at each microservice (you may want to do that for internal microservice communications), I would install a gateway (for example Ocelot which can handle the external "upstream" authentication for you using whatever system you're using, for example for Jwt bearer:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication()
.AddJwtBearer("TestScheme", x => ...
}
Then in Ocelot you can decide which routes require this scheme as follows
"Routes": [{
"DownstreamHostAndPorts": [...],
"DownstreamPathTemplate": "/",
"UpstreamPathTemplate": "/",
"AuthenticationOptions": {
"AuthenticationProviderKey": "TestScheme", //<--here decide to use this scheme
"AllowedScopes": []
}
}]
If Authentication is successful you can use Ocelot's method of "claims to claims transformation" from your upstream to downstream this method - I personally wanted customise this and build a new Jwt token for internal authentication so used my own downstream handler, like this:
services
.AddHttpClient<IMyService, MyService>(client => ...)
.AddHttpMessageHandler<MyJwtDownstreamHandler>();
//then in the MyJwtDownstreamHandler
request.Headers.Authorization = new AuthenticationHeaderValue(
"bearer",
TokenGenerator.Generate( //<--generate your own Symmetric token using your own method
identity: myIdentity, //<--claims for downstream here
)
);
Based on comments Above
External Identity provider
You may need to use external identity provider e.g. identiyserver4 , azure ad or auth0 etc. Since the token may be generated is JWT token you will have to validate the token.
Validate Token
You need to validate the token in the .Net core Middle ware. Every token issued has a payload and your app middleware will verify every incoming token and reject if it's not able to validate. Your middle ware will fill the claims principle which can be used in your application to validate the authorization as well e.g. roles (if user has authorization to access particular api). You would put "authorize" attribute on top of controller and it will do the job.
You can validate the token manually or some identity provider gives automatic validation e.g. Azure Ad will validate the token and fill the claims principle without doing much effort by simply adding Azure ad nuget package.
There are heaps of example if you simply google. Tokens can be confusing so i would suggest you understand tokens e.g. id_token , access_token , refresh token . Authentication flows and claims. It would become easier if you understand the token types and flows. I am attaching very simple example just to give you idea.
Example
I have a website (WebApp) from where I make Ajax calls to my WebAPI layer that accepts JWT Bearer Token auth. I have integrated Adal.js into my front end script layer and the config values look like this.
var config = {
instance: "https://login.microsoftonline.com/",
tenant: "tenant.com",
clientId: "CLIENT_ID OF THE PORTAL APP", // Web-Portal app
redirectUri: "http://localhost:8241/",
popUp: true,
cacheLocation: "localStorage" };
The WebApiConfig looks like this:
var webApiConfig = {
resourceId: "CLIENT_ID OF API APP", // Web-API app
resourceBaseAddress: "http://localhost:9020/"
};
It all works fine, I am able to authenticate/authorize and call my webapi via an access token via Implicit oauth flow.
Now I would like to be able to retrieve the User's security group membership values for the logged in user so that at the WebAPI layer, I can make sure the user belongs to a particular security group as part of the authorization logic. So I set the GroupMembershipClaims: "SecurityGroup" in the App's manifest xml in Azure AD (I did this for the WebApi App manifest first, but then also in the WebApp app manifest).
However, looks like GroupMembershipClaims are only included in the idtoken and not the accessToken. And given it is the accessToken that is sent to the WebAPI, I am unable to do this check at that layer. I guess I could make the check at the WebApp layer, but given this WebApi layer will be called from many other frontend apps (which are not owned by me, I am primarily an "API provider"), that is not a secure soln.
So how do I solve this? I guess one way is to use the "on-behalf-of" flow on the WebApi layer to make calls to Graph API to find this out? I am afraid that will require more permissions than available at User scope.
Thanks!
It's a good question.
My immediate first thought was "well you can get them from Graph API", but I see you already thought of that :)
If you want to do that, you can use On-behalf-of like you said, and use this operation on MS Graph API: https://developer.microsoft.com/en-us/graph/docs/api-reference/beta/api/user_getmembergroups.
You would need these delegated permissions: User.Read and Group.Read.All.
So you would need the permission to read all groups in addition to the basic User.Read.
There are a couple other options.
You can define roles in your API.
I wrote an article on how to do this: Defining permission scopes and roles offered by an app in Azure AD.
So if you define a role like this in the API's manifest:
{
"appRoles": [
{
"allowedMemberTypes": [
"User"
],
"displayName": "Administrator",
"id": "179c1dc0-4801-46f3-bc0d-35f059da1415",
"isEnabled": true,
"description": "Administrators can access advanced features.",
"value": "admin"
}
]
}
You'll need to make the id a unique GUID for each role, you can use online generators or PowerShell for that [System.Guid]::NewGuid().
These roles can then be assigned to users, and if you have at least Azure AD Basic, you can assign them to groups.
Roles are included in access tokens, so you can check these quite easily in your API.
The other idea I had was to have the API and front-end use the same registered app in Azure AD.
This way you could pass the Id token to the API instead of an access token, and you'd get the groups.
But since you might have other front-ends too, I doubt this would work for you.