"authentication failed due to: jwt audience is invalid" with Azure AD - azure

First I'm describing how I setup my applications then I will describe how I'm using the APIs.
Setup
In my Azure Active Directory, I have two applications registered: UI and Backend
UI has the client ID clientId1 and backend has client ID clientId2 (it's a GUID, but for simplicity)
Both are under the same tenant tentant1 (single tenant)
Backend (Web API)
Backend has an exposed API with scope "api://clientId2/access_as_user" and authorized client "clientId1" with the scope just mentioned selected
I'm using passport and passport-azure-ad (I pretty much copied https://github.com/Azure-Samples/active-directory-javascript-nodejs-webapi-v2).
My config:
const config = {
identityMetadata: "https://login.microsoftonline.com/tenant1/v2.0/.well-known/openid-configuration",
clientID: "clientId2",
validateIssuer: false,
loggingLevel: 'info',
passReqToCallback: false,
loggingNoPII: false
};
I get this message when starting the server:
{"name":"AzureAD: Bearer Strategy","hostname":"DESKTOP-NCVLN56","pid":16052,"level":40,"msg":"Production environments should always validate the issuer.","time":"2020-04-11T13:25:44.283Z","v":0}
{"name":"AzureAD: Bearer Strategy","hostname":"DESKTOP-NCVLN56","pid":16052,"level":30,"msg":"In BearerStrategy constructor: created strategy with options {\"identityMetadata\":\"https://login.microsoftonline.com/tenant1/v2.0/.well-known/openid-configuration\",\"clientID\":\"clientId2\",\"validateIssuer\":false,\"loggingLevel\":\"info\",\"passReqToCallback\":false,\"loggingNoPII\":false,\"clockSkew\":300,\"allowMultiAudiencesInToken\":false,\"audience\":[\"clientId2\",\"spn:clientId2\"]\"isB2C\":false,\"_isCommonEndpoint\":false}","time":"2020-04-11T13:25:44.285Z","v":0}
Listening on port 5000
UI (Angular SPA)
UI has permissions was granted automatically permission to access Microsoft Graph (profile, user.read, user.read.all -- last one I think I granted). The permissions are in "API permissions"
I went ahead and also granted access to the Backend access_as_user
For the UI code I'm using the MSAL library and again I pretty much copied the repo (https://github.com/Azure-Samples/active-directory-javascript-singlepageapp-angular)
In the protectedResourceMap field I added the following
['https://graph.microsoft.com/v1.0/me', ['user.read']],
['http://localhost:5000', ['api://clientId2/access_as_user']],
I am able to log in and read my user profile, but when trying to access http://localhost:5000/hello (protected), I'm getting the error the title of this question
{"name":"AzureAD: Bearer Strategy","hostname":"DESKTOP-NCVLN56","pid":20720,"level":30,"msg":"authentication failed due to: jwt audience is invalid","time":"2020-04-11T13:38:08.700Z","v":0}
--
I can see the Bearer Token coming (in the UI and backend), the server decodes the token (I can see all my profile info in the server logs), but it's saying the JWT is invalid?!
I'm not defining an audience, yet I can see in the token when it gets decoded the audience with aud: 'api://clientId2'.
I can also see when the backend starts it shows the audience as [clientId2, sps:clientId2] by default (step4 on the backend). When I define in the config audience: 'api://clientId2', I get a 403 with the message:
{"name":"AzureAD: Bearer Strategy","hostname":"DESKTOP-NCVLN56","pid":12644,"level":30,"msg":"In Strategy.prototype.jwtVerify: We did not pass Req back to Callback","time":"2020-04-11T16:19:30.398Z","v":0}
Any help would be appreciated. Thank you.

Turns out their code in the repository is not using proper configuration to verify the scope access...
https://github.com/Azure-Samples/active-directory-javascript-nodejs-webapi-v2/blob/master/index.js#L41
if (req.authInfo['scp'].split(" ").indexOf("demo.read") >= 0) {
I needed to change the scope from "demo.read" to "access_as_user".

In my case it was just that the clock of my VM where the application was running on was 15mins behind. So the time of the token was created was in the future...

Related

Replicate Postman Oauth 2.0 using Python

I have this Authorization request that works.
How can I replicate it in Python?
I am using an Azure AD to authenticate the access.
Since you are working with python, your case is a : Oauth2 login for SSR web applications with Microsoft
Goal
Get an access_token from interactive login using the oauth2 authorization code grant
Steps
Here I will list all the steps required to do it with any language
Create a web with session with at least these endpoints
/ : home page
/callback : server route or path able to receive query params like /callback?code=123456. This along with your base domain will be called redirect_uri. Sample : http://localhost:8080/callback or http://acme.com/callback
Create and configure an app in Azure Dev Console. Same process is in Google, Facebook, Linkedin, etc. As a result you should have a clientId, clientSecret and a redirect url
Create a simple web with classic session in which if user is not logged-in, redirect (302) to this url:
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=foo&response_type=code&redirect_uri=foo&response_mode=query&scope=offline_access%20user.read%20mail.read
clientid and redirect_uri are important here and should be the same of previous step
After that, browser should redirect the user to the platform login
If user enters valid credentials and accepts the consent warning, Microsoft will perform another redirect (302) to the provided redirect_uri but with special value: The auth code
http://acme.com/callback?code=123456798
In the backend of /callback get the code and send it to this new endpoint
Add a client_id & client_secret parameters
Add a code parameter with the code sent by microsoft
Add a redirect_uri parameter with previously used and registered on azure. Sample http://acme.com/callback or http://localhost:8080/callback
Add a grant_type parameter with a value of authorization_code
Issue the HTTP POST request with content-type: application/x-www-form-urlencoded
You should get a response with the precious access_token:
{
token_type: 'Bearer',
scope: 'Mail.Read User.Read profile openid email',
expires_in: 5020,
ext_expires_in: 5020,
access_token: 'eyJ0oVlKhZHsvMhRydQ',
refresh_token: 's_Rcrqf6xMaWcPHJxRFwCQFkL_qUYqBLM71UN6'
}
You could do with this token, whatever you configured in azure. Sample: If you want to access to user calendar, profile, etc on behalf of the user, you should have registered this in the azure console. So the clientid is related to that and human user will be prompted with something like this
Libraries
There is some libraries provided by microsoft (c#, nodejs) which will save you a little work. Anyway the previous explanation are very detailed.
Advice
Read about oauth2 spec: https://oauth.net/2/
Read about oauth2 authorization code flow login before the implementation with python
https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2
https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow
https://github.com/msusdev/microsoft_identity_platform_dev/blob/main/presentations/auth_users_msalnet.md
Check this to understand how configure the azure web console: https://learn.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app
Check my gist https://gist.github.com/jrichardsz/5b8ba730978fce7a7c585007d3fd06b4

Azure B2C React SPA dose not providing access token

I am working on my first project of connecting Azure B2C with MERN App. I wanted to Sign In using Azure B2C and authorise my web API using the Access Token.
I configured everything and applied configurations to this sample React tutorial provide by their documentation.
The problem arises while calling the Web API. The Web API call are sending without any token. When I check the code, acquireTokenSilent function returning empty accessToken from response.
instance.acquireTokenSilent({
scopes: protectedResources.apiHello.scopes,
account: account
}).then(async (response) => {
console.log(response)
The Request is:
Even though I looked many forums and Microsoft technical forums, no answer is not working.
But what I noticed is, it is requesting for grant_type: authorization_code but am not seeing access token in the response. Posting here the API call, request and response.
The Response is producing id_token but not access token,
I gave grant permission in the SPA App permission for task.read scope. I tried everything but I am still receiving the access token as empty. How could I fix this issue?
I tried to reproduce the same in my environment and got below results:
I registered one Azure AD B2C application for app1 and added scopes(task.read) as below:
Now I created one SPA registration and added API permissions by granting consent like this:
I created Sign up and sign in policy and ran the user flow as below:
Please Check authentication and access token and id token:
I signed in as user it gave me auth code in address bar.
https://<tenant name >.b2clogin.com/<tenant name> .onmicrosoft.com/oauth2/v2.0/authorize?p=B2C_1_susi&client_id=<app id>&nonce=defaultNonce&redirect_uri=https://jwt.ms&scope=openid%20https%3A%2F%2F<tenant name>.onmicrosoft.com tasks.read&response_type=code&prompt=login&code_challenge_method=S256&code_challenge=<challenge paramater>
I generated the access token via Postman with commands like this:
POST https://tenant.b2clogin.com/tenant.onmicrosoft.com/policy/oauth2/v2.0/token
grant_type: authorization_code
client_id: SPA_appid
scope: https://tenant.onmicrosoft.com/app1/task.read
redirect_uri: redirect_uri
code: code
code_verifier: code_verifier
Postman
When I decode the token i getting scp in jwt .ms

msal-node error trying to resolve endpoints

I have been using MSAL in my React app for some time with success. One of the tokens that my app requests is for scope 'https://management.core.windows.net/user_impersonation'. I have a nodeJS server that I want to push that token acquisition to so I installed msal-node (1.12.1) and tried using the OBO flow:
const pca = new msal.ConfidentialClientApplication({
auth: {
clientId: settings.config.azure.clientId,
clientSecret: settings.config.azure.clientSecret,
authority: "https://login.microsoftonline.com/<tenantid>",
knownAuthorities: ["https://login.microsoftonline.com/<tenantid>"],
}
});
const request = {
scopes: ['https://management.core.windows.net//user_impersonation'],
oboAssertion: <token_extracted_from_auth_header>
}
const response = await pca.acquireTokenOnBehalfOf(request);
return response.accessToken;
However the above code results in the following error:
ClientAuthError: endpoints_resolution_error: Error: could not resolve endpoints. Please check network and try again. Detail: ClientAuthError: openid_config_error: Could not retrieve endpoints. Check your authority and verify the .well-known/openid-configuration endpoint returns the required endpoints. Attempted to retrieve endpoints from: https://login.microsoftonline.com/tenantid/v2.0/.well-known/openid-configuration
If I visit the URL it complains about I do get back some metadata so not really sure why it is failing.
Anybody have a suggestion?
Also in regards to OBO flow:
For my nodeJS app I have added that permission to the required API list
I presume the oboAssertion field is the token that is passed to my nodeJS app by the client? I simply extracted it from the Auth header
The actual error message there means that the URL that we are trying to contact is wrong. And it is wrong https://login.microsoftonline.com/tenantid/v2.0/.well-known/openid-configuration returns an error.
A coorrect one is: https://login.microsoftonline.com/19d5f71f-6c9a-4e7f-b629-2b0c38f2b167/v2.0/.well-known/openid-configuration
Notice how I used an actual teanant_id there. You can get yours from the Azure Portal - it's the "directory id"
If your web api is single tenant, i.e. it is only meant for the people in 1 organization, then the is the tenant id of that organization. It is also known as "directory id". You get it from the Azure Portal.
However, if your api is multi-tenant, i.e. it's a bit more complicated, and the "correct" answer is to use the tenant id of the incoming assertion. It's the tid claim in it.

Getting access denied when accessing Azure Web App via token

We are testing locking down an API hosted on Azure Web Apps by using the built-in Azure Web App Authentication/Authorization with Azure Active Directory.
We are calling POST https://login.microsoftonline.com/{tenantIDhere}/oauth2/v2.0/token with the following parameters in Postman:
grant_type: password
client_id: {ID}
username: {username}#{tenenat}.onmicrosoft.com
password: {password}
scope: https://{webappname}.azurewebsites.net/.default
client_secret: {secret}
Below is the response I get:
"token_type":"Bearer","scope":"https://{webappname}.azurewebsites.net/user_impersonation https://{webappname}.azurewebsites.net/.default","expires_in":3599,"ext_expires_in":3599,"access_token":"{TOKEN HERE}"}
So I take that token and try and access the webpage by passing the token in the header and I get the below error using POST:
https://{webappname}.azurewebsites.net/api/url
Authorization: Bearer {TOKEN HERE}
Response (401 Unauthorized)
You do not have permission to view this directory or page.
I have spent days trying everything I can find. What am I missing?? As soon as I turn off authorization needed for the web app, I get the expected result. But using the grant_type: password, I CANNOT get in! I have a feeling its related to the scope, but I cannot find documentation on what I need here for this. Any ideas?
The user is able to manually login to the webpage and pull the data, so it is not a permission issue. It has something to do with the token I believe.
If you are using v2.0 endpoint, the scope should be {your_client_id}/.default. Replace https://{webappname}.azurewebsites.net with the client id/application id for your API app in Azure AD.
Edit:
As Wayne said, you could also set scope as {your_client_id}/User.Read to get access token.

Microsoft Graph API - cannot refresh access token

I have registered a multitenant app at https://apps.dev.microsoft.com since the "admin consent" prompt wasn't available in the Azure AD apps. Admin consent is required for our app to retrieve info about users and their calendars.
I can provide admin consent from a completely different tenant than what this app is registered from and use the provided access token to retrieve all necessary information, however that obviously expires after an hour and we need offline access.
I have tried using the tenantId instead of 'common' in the https://login.windows.net/common/oauth2/token endpoint, however receive the same message as below.
The following is the data being submitted to the token endpoint in json format (converted within node to form encoded format before submitting):
{
grant_type: 'refresh_token',
client_id: 'e5c0d59d-b2c8-4916-99ac-3c06d942b3e3',
client_secret: '(redacted)',
refresh_token: '(redacted)',
scope: 'openid offline_access calendars.read user.read.all'
}
When I try to refresh the access token I receive an error:
{
"error":"invalid_grant",
"error_description":"AADSTS65001: The user or administrator has not consented to use the application with ID 'e5c0d59d-b2c8-4916-99ac-3c06d942b3e3'. Send an interactive authorization request for this user and resource.\r\nTrace ID: 2bffaa08-8c56-4872-8f9c-985417402e00\r\nCorrelation ID: c7653601-bf96-46c3-b1ff-4857fb25b7dc\r\nTimestamp: 2017-03-22 02:17:13Z",
"error_codes":[65001],
"timestamp":"2017-03-22 02:17:13Z",
"trace_id":"2bffaa08-8c56-4872-8f9c-985417402e00",
"correlation_id":"c7653601-bf96-46c3-b1ff-4857fb25b7dc"
}
This error occurs even when standard consent is used. I have also tried using the node-adal library instead of raw http requests which produces the exact same result.
I note that "offline_access" isn't a permission I am able to set within the MS apps portal, however I would guess the fact that I am getting a refresh token back means that I can refresh the access token?
For the record, the following is the node-adal code I used to see if I was doing something wrong:
var self = this;
var authenticationContext = new AuthenticationContext('https://login.windows.net/common');
authenticationContext.acquireTokenWithRefreshToken(
self.refreshToken,
self.clientId,
self.clientSecret,
'https://graph.microsoft.com/',
function(a) {
console.log(a);
}
);
Any help in getting this refresh process working is appreciated!
Please ensure that the tenant that you using for refreshing token is same as the tenant that you requesting for the access_token.
The refresh token request works well for me unless in the scenario of below:
register the app from protal using Microsoft account
user1 is in tenant1
add user1 as the external users to tenant2
request the access_token/refresh_token from tenant1(OK)
try to refresh the token using tenant1 in the request(OK)
try to refresh the token using tenant2 in the request(same error message)

Resources