Re use jwt to call API from another API - asp.net-web-api

I have a single tenant scenario in Azure Active Directory where an API1 needs to call another API2 using authentication, and where the API1 was called from a SPA.
Would it be correct to just pass on the user's JWT received in API1 from the SPA to call API2 for authentication?
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
TokenValidationParameters = new TokenValidationParameters() {
SaveSigninToken = true,
...
like in: https://github.com/Azure-Samples/active-directory-dotnet-webapi-onbehalfof
All APIs are implemented with ASP.NET Web API and the SPA using Active Directory Authentication Library (ADAL) for JavaScript.

Passing the token you got from the first step would NOT be correct for the second step. Every access token has a specific audience that the token is intended for, and because API 1 and API 2 are different, the audience value of that token can only match one of the two.
You link to the correct sample, which is the On-Behalf-Of flow. This flow allows API 1 to exchange the first token for a brand new token where the client becomes API 1 and the resource becomes API 2.
You can find a description of the protocol here
// line breaks for legibility only
POST /oauth2/token HTTP/1.1
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer
&client_id=625391af-c675-43e5-8e44-edd3e30ceb15
&client_secret=0Y1W%2BY3yYb3d9N8vSjvm8WrGzVZaAaHbHHcGbcgG%2BoI%3D
&resource=https%3A%2F%2Fgraph.windows.net
&assertion=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6InowMzl6ZHNGdWl6cEJmQlZLMVRuMjVRSFlPMCIsImtpZCI6InowMzl6ZHNGdWl6cEJmQlZLMVRuMjVRSFlPMCJ9.eyJhdWQiOiJodHRwczovL2Rkb2JhbGlhbm91dGxvb2sub25taWNyb3NvZnQuY29tLzE5MjNmODYyLWU2ZGMtNDFhMy04MWRhLTgwMmJhZTAwYWY2ZCIsImlzcyI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0LzI2MDM5Y2NlLTQ4OWQtNDAwMi04MjkzLTViMGM1MTM0ZWFjYi8iLCJpYXQiOjE0OTM0MjMxNTIsIm5iZiI6MTQ5MzQyMzE1MiwiZXhwIjoxNDkzNDY2NjUyLCJhY3IiOiIxIiwiYWlvIjoiWTJaZ1lCRFF2aTlVZEc0LzM0L3dpQndqbjhYeVp4YmR1TFhmVE1QeG8yYlN2elgreHBVQSIsImFtciI6WyJwd2QiXSwiYXBwaWQiOiJiMzE1MDA3OS03YmViLTQxN2YtYTA2YS0zZmRjNzhjMzI1NDUiLCJhcHBpZGFjciI6IjAiLCJlX2V4cCI6MzAyNDAwLCJmYW1pbHlfbmFtZSI6IlRlc3QiLCJnaXZlbl9uYW1lIjoiTmF2eWEiLCJpcGFkZHIiOiIxNjcuMjIwLjEuMTc3IiwibmFtZSI6Ik5hdnlhIFRlc3QiLCJvaWQiOiIxY2Q0YmNhYy1iODA4LTQyM2EtOWUyZi04MjdmYmIxYmI3MzkiLCJwbGF0ZiI6IjMiLCJzY3AiOiJ1c2VyX2ltcGVyc29uYXRpb24iLCJzdWIiOiJEVXpYbkdKMDJIUk0zRW5pbDFxdjZCakxTNUllQy0tQ2ZpbzRxS1MzNEc4IiwidGlkIjoiMjYwMzljY2UtNDg5ZC00MDAyLTgyOTMtNWIwYzUxMzRlYWNiIiwidW5pcXVlX25hbWUiOiJuYXZ5YUBkZG9iYWxpYW5vdXRsb29rLm9ubWljcm9zb2Z0LmNvbSIsInVwbiI6Im5hdnlhQGRkb2JhbGlhbm91dGxvb2sub25taWNyb3NvZnQuY29tIiwidmVyIjoiMS4wIn0.R-Ke-XO7lK0r5uLwxB8g5CrcPAwRln5SccJCfEjU6IUqpqcjWcDzeDdNOySiVPDU_ZU5knJmzRCF8fcjFtPsaA4R7vdIEbDuOur15FXSvE8FvVSjP_49OH6hBYqoSUAslN3FMfbO6Z8YfCIY4tSOB2I6ahQ_x4ZWFWglC3w5mK-_4iX81bqi95eV4RUKefUuHhQDXtWhrSgIEC0YiluMvA4TnaJdLq_tWXIc4_Tq_KfpkvI004ONKgU7EAMEr1wZ4aDcJV2yf22gQ1sCSig6EGSTmmzDuEPsYiyd4NhidRZJP4HiiQh-hePBQsgcSgYGvz9wC6n57ufYKh2wm_Ti3Q
&requested_token_use=on_behalf_of
&scope=openid

Related

How to silently renew Id Token using AddMicrosoftIdentityWebAppAuthentication to Call Downstream API

I am trying to implement the BFF-Gateway pattern (no tokens in the browser) to be used with a React SPA. The BFF is using AddMicrosoftIdentityWebAppAuthentication to handle login and issue a cookie to the SPA. And it is using YARP to proxy api requests to a downstream api. I'm using Azure B2C. Everything works perfectly until the BFF id_token expires in 1 hour. At that point, fetching the downstream api access token via GetAccessTokenForUserAsync (which is called in a piece of middleware) fails:
var scope = _configuration["CallApi:ScopeForAccessToken"];
var accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(new[] { scope });
ctx.Request.Headers.Add("Authorization", "Bearer " + accessToken);
Exception:
IDW10502: An MsalUiRequiredException was thrown due to a challenge for the user. See https://aka.ms/ms-id-web/ca_incremental-consent.
ResponseBody: {"error":"invalid_grant","error_description":"AADB2C90085: The service has encountered an internal error. Please reauthenticate and try again.\r\nCorrelation ID: 622d6bd6-d06e-4142-86f2-b30a7a17b3b5\r\nTimestamp: 2022-11-25 09:31:23Z\r\n"}
This is effectively the same as Call Downstream API Without The Helper Class example and this sample, except that I'm acquiring the access token in middleware, not a controller, so the downstream YARP requests contain the access token. BTW I get the same error if I do this inside a controller per this example. And I see no soluton to this in the sample.
There is a similar question here which references the sample referenced above, but for the B2C sample I see no solution to this problem.
I also found this sample and this explanation. But this uses Microsoft.Owin to configure auth, not AddMicrosoftIdentityWebAppAuthentication. This looks promising, but is a departure from most examples I see that use Microsoft.Identity.Web.
Can you please point to the correct soluton? I need call to be able to call _tokenAcquisition.GetAccessTokenForUserAsync after the id token expires without asking the user to reauthenticate and/or the SPA to having to reload.
At the moment I am handling this issue in the SPA by catching the exception from MSAL and redirecting back to the login endpoint in the BFF which initiates the challenge. This gets me a new id_token and cookie, but this is just a temp workaround as it's very disruptive to user to be redirected away from the SPA.

Authentication and authorization as a central MicroService ASP.NET

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

Proper way to authenticate WebApi backend

I am building a WebApi for external consumers. I want to lock it down using Azure AD B2C token authentication. .NET Framework 4.7.2 using Owin middleware.
The workflow (as I understand it) goes like this:
Successful path:
GET request on secure endpoint, contains a header with a valid auth token
API returns expected result
Invalid/missing token path:
GET request on secure endpoint, contains an invalid/missing token in the header(s)
API returns 401
This all looks correct from a security point of view, but my poor consumers won't know how to get a new token. Should I be returning something with the 401 to assist in them getting a new token? (i.e. the URL for the Azure AD B2C endpoint) - does this break conventional rules on returning objects with a 401? Am I expecting too much for my consumers to know how to interact with my chosen 3rd-party auth provider?
My question is more about design but would appreciate any technical examples.

Google Social Login with Auth0 Tokens for [Authorized] API Calls

Anyone here implemented social login through Google for Auth0? I have an issue with the tokens (access and id) being returned after validating with Google.
Here's my code:
var waGoogle = new auth0.WebAuth({
domain: 'testApplication.auth0.com',
clientID: '************',
redirectUri: 'http://localhost:8080/'
})
waGoogle.authorize({
connection: 'google-oauth2',
responseType: 'id_token token'
}, function(err, authResult){
if(err){
console.log('Google Login Error')
console.log(err)
}
});
Google screen shows up, I log in and I am redirected back to my application. From the application, I parse the URL so that I can get the access and id tokens.
let getParameterByName = (name) => {
var match = RegExp('[#&]' + name + '=([^&]*)').exec(window.location.hash);
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}
var access_token = getParameterByName('access_token')
var id_token = getParameterByName('id_token')
Issue I am having is that none of the tokens allow me to call my APIs (asp.net web api) which are decorated with the [Authorize] attribute. It returns a:
401 (Unauthorized)
I know that my API is working, as using the normal
Username-Password-Authentication
method where I also obtain an access token, my api calls are just pulling through.
Are there any next steps which I need to do after obtaining the access and id_token from Google? Do I need to make an additional call to Auth0 to obtain the proper access token to be able to call my web api?
Thanks.
The token you are looking for is called an IdP (Identity Provider) Token. This is different from the one issued to you after logging in. There are pretty good instructions on the Auth0 site that walk you through the process of getting that token.
Here is the overview of IdP tokens
Here is a step-by-step guide to calling the Identity Provider
The tl;dr:
To access the IdP token you need to call the Auth0 management API from your server. For that, your server will need a management token. Then use that token to access the endpoint /api/v2/users/[USER_ID]. In the object sent back from Auth0, look for the google identity and extract that token.
Also note, you should probably keep that token on your server if you can. If you can keep those power tokens away from your client your users will be happy.

Authenticating an API request with a Token

Suppose I make an API request to the following URL:
https://myapi.com/data
This is an API I built and have full control over.
I would like to limit access to this API to only the apps I authorize.
I see many services will provide you with an API key which you can append to your URL to give you access.
Suppose I have:
https://myapi.com/data?key=a6reallly7long2string9of0numbers2and4letters
Then on the backend I have something like:
class REST {
public $ACCESS_TOKEN = 'a6reallly7long2string9of0numbers2and4letters',
public function Auth($token){
if($token===$this->ACCESS_TOKEN) return true;
return false;
}
}
If the values match, I allow access.
But all someone would have to do is look at the request the app is making on the client side and they have the token.
Even if I encrypt the token or use one-way hashing, they'll still have the value that decrypts to the correct result.
How does one approach good authentication via URL token for an API?
I would like to limit access to this API to only the apps I authorize.
What are you looking for is "access authorization". Indeed, an access token seems to be a good way, but there are some aspects missing
** Authorization header **
By default the token should be sent as an HTTP header (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization) not in the url
Commonly used Authorization headers types are Basic (for basic authentication) and Bearer (for OAuth authentication and authorization)
The token should not be a hardcoded constant. It should be created/generated based on the application (and optionally user) authentication. And still better if the token is temporary
Now you can ask - how can an application keep its credentials secret? Each application can have their own server services (end user should.not access application credentials) or pure web application should be comtrolled by the CORS headers
Just search for OAuth 2.0 protocol and JWT token (jwt should be self-contained and signed).
IMHO the URL token may be an option when there is no other alternative, as URL is often cached, resent, logged,...
** API Manager **
If you have resources (server) to do so, you can deploy an API manager (there are open source, commercial or cloud options, just search for some). API manager will handle the application enrollment, authorization and enforcement.

Resources