I have 2 web applications.
The web APIs in both sites are the same. The authentication is the same.
Test 1: When I ask Site A to use its own API to get the data (with Site A's authentication turned on) it works great.
Test 2: When I ask Site A to use Site B to get the data (with Site B's authentication turned OFF) it works great.
Test 3: When I ask Site A to use Site B to get the data (with Site B's authentication turned ON) it fails with unauthorised error 401.
Here is the code I am using to authenticate...
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(o =>
{
o.MetadataAddress = String.Format("https://login.microsoftonline.com/{0}/v2.0/.well-known/openid-configuration", Configuration["AzureAd:TenantId"]);
o.Audience = Configuration["AzureAd:ClientId"];
});
So...
I know the tokens are getting passed correctly (because of Test A).
I know the Site B API being called is accessible and correct (because of Test B).
Therefore, I assume, the issue is with the values being passed to Azure AD. I assume Azure doesn't think the tokens being obtained in Site A and validated in Site B (that have different URIs) are not "the same" and therefore not authorised.
What do I need to pass to Azure to get it to work?
Thanks
There are 3 bits of validation that will happen against the token
1) It will validate that it was signed by the public keys of the open id provider (e.g. retrieved from here https://login.microsoftonline.com/common/discovery/keys)
2) It will validate the issuer of the token (the iss claim) is valid for the API
3) It will validate the audience of the token (the aud claim) is valid for the API
(there's some additional lifetime validation as well, but these 3 are important at configuration time)
Likely, the token you are sending to the API isn't passing either issuer or audience validation. Use a JWT decoder like http://jwt.ms/ to inspect the token and understand the claims that are being sent to the API.
Does the aud claim of your token match the audience you are specifying in Configuration["AzureAd:ClientId"]?
If it's the same issuer for both tokens and you are happy for 'Site B' to accept tokens from 'Site A', then you can amend token validation parameters to accept multiple options here, have a look at this answer for an example
https://stackoverflow.com/a/47072385/1538039
Investigate the claims in the token, then amend the config of Site B to validate the claims accordingly, by allowing the audiences/issuers you want to be able to communicate with it.
Thanks for this for very clear feedback. I will look into this and report back. Also, the JWT decoder will be very useful.
So, I have checked the JWT token generated by Site A, and it looks like the iss and aud values are correct (and the same as Site B) is comparing against.
Is it possible to get some info about what the authentication failure is caused by? The event log just says the authentication failed. Not the most useful debugging insight.
If I debug both API requests, Site A has a populated ClaimsIdentity, Site B doesn't. This doesn't appear to be an issue with the Azure token claim values at all. Ummm. I'll accept your answer for now (as it answers my original hypothesis).
You won't have a ClaimsIdentity if it's failed token validation parameters. Have a look at OnAuthenticationFailed event, which you can wire up like so stackoverflow.com/a/48890853/1538039. The context passed to that method should have the Exception details after doing token validation (as per github.com/aspnet/Security/blob/…).