AAD: How do you send an "interactive authorization request" to resolve AADSTS65001 when using MSAL if it's not by calling acquireTokenPopup? - azure

I have a single-page application written in TypeScript. It needs to call a middle-tier service, and the middle-tier service will, in turn, call the Microsoft Graph API.
In my SPA, I'm using the MSAL library and I log the user in by calling UserAgentApplication.loginRedirect()
I then call acquireTokenPopup and pass only one scope in the access token request parameter: the scope that is defined by my middle-tier service. Upon doing this, I see the popup but then it disappears (I never see any consent prompt). This successfully gets a token which the SPA then sends as a Bearer token on the Authorization header in an HTTPS request to the middle-tier.
The middle-tier then tries to get an OBO token by calling AAD with properties like this:
const grantType = "urn:ietf:params:oauth:grant-type:jwt-bearer";
const assertion = userAccessToken;
const scope = "https://graph.microsoft.com/Calendars.Read " +
"https://graph.microsoft.com/Calendars.Read.Shared " +
"https://graph.microsoft.com/Calendars.ReadWrite " +
"https://graph.microsoft.com/Calendars.ReadWrite.Shared " +
"openid profile email offline_access";
const requestedTokenUse = "on_behalf_of";
It also has a client secret it sends along that is defined in the AAD registration for the midddle-tier. When this request is made to the AAD endpoint to retrieve an OBO token, it always fails with this error:
{"error":"invalid_grant","error_description":"AADSTS65001: The user or administrator has not consented to use the application with ID '<ID of middle-tier application>' named '<name of middle-tier application>'. Send an interactive authorization request for this user and resource.\r\nTrace ID: 39d9bace-03f0-402b-9310-c713aa990200\r\nCorrelation ID: edfbb88f-5c63-4cef-84c1-0de8457852b7\r\nTimestamp: 2019-05-22 22:25:24Z","error_codes":[65001],"timestamp":"2019-05-22 22:25:24Z","trace_id":"39d9bace-03f0-402b-9310-c713aa990200","correlation_id":"edfbb88f-5c63-4cef-84c1-0de8457852b7","suberror":"consent_required"}
I see the bit about consent being required and how I'm supposed to send an interactive authorization request -- but I'm not sure how to do that. In fact, I thought I was doing that when I called acquireTokenPopup initially (rather than acquireTokenSilent).
If calling acquireTokenPopup in the SPA client with the middle-tier defined scope is not the correct way to send an interactive authorization request, what is?
Possibly helpful information:
The ID of my SPA client application is listed both in the "Authorized client applications" section of AAD registration portal for the middle-tier AND in the "knownClientApplications" section of the manifest.
Thanks!

Firstly your usage of On-Behalf-Of flow for the scenario you describe looks good.
For the admin consent part (to get past AADSTS65001 error) you could try to do consent explicitly, in one of the two ways:
Use the Azure portal (works well for single tenant apps)
Go to Azure Portal > Azure AD > App Registrations > Registration for your app > API Permissions and then click on "Grant admin consent for [your AD]" button.
Use the Admin consent endpoint (works for single tenant or multi-tenant apps)
More info on Microsoft Docs here - Request Permissions from Directory Admin
GET https://login.microsoftonline.com/{tenant}/adminconsent?
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&state=12345
&redirect_uri=http://localhost/myapp/permissions
Only mandatory parameter here is client_id. You can skip providing other parameters and consent should still work.
On a side note, the part that you mentioned about knownClientApplications can help with a more advanced scenario, where you're trying to club the consent for more than one applications in a single interaction. Like multiple layers of the same logical app. If that is what you're trying to achieve, then you won't really need to consent separately for middle-tier API. You could still use approach 2 for Admin Consent endpoint in this case.

Related

What is the flow of Azure AD based authentication in a project having SPA and web api?

I have a front end SPA (single page application) and back end api.
Each event in the SPA (like button click) invokes the respective api endpoint, and displays the result in the SPA.
I want to implement Azure AD based authentication so that only my Azure Tenant users are able to use the SPA/api.
Is the following flow correct approach to implementing such a feature:
User opens the SPA
User clicks on login button which opens Microsoft login popup
User enters Microsoft credentials in the popup, and if credentials are correct then user gets the JWT token
For every subsequent api request, the JWT token is placed in the bearer header
The endpoint validates the JWT token using Azure public key and rejects the request if token is missing or validation fails.
Is this flow correct and what is such a flow called?
There are several implementation steps that needs to be performed before you will have the flow that you have described:
User flow needs to be configured (Azure AD) - e.g. selfsignup allowed?
Backend and frontend applications needs to be registered (Azure AD)
Permissions and scopes needs to be added (Azure AD)
Backend API needs to be configured (e.g. API management) in order to validate the JWT token
I highly recommend to configure one of the Azure sample implementations end2end to get and idea of all the needed tasks: https://learn.microsoft.com/en-us/azure/active-directory-b2c/configure-authentication-sample-spa-app
The steps you outlined are correct.
An OAuth 2.0 "flow" outlines the steps to acquire a token from an Identity Provider (IdP). Since you are using a SPA, there are some restrictions on which flows you can use. A SPA can't act as a "Confidential Client" which is required for some flows. (Basically - the Client Secret required for the other flows would be visible in the browser network trace, so it's not "confidential".) The "Implicit Flow" used to be recommended for SPAs but it's less secure, so now the "Authorization code flow (with PKCE)" is recommended. Steps 2 & 3 in the question above are when you are executing the flow to acquire a token.
The authentication flow doesn't really address how you save and send the token to the API (#4 in the question), but the Microsoft Authentication Library (MSAL) helps with that - More information here - https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-spa-overview
In Azure AD, you'll want 2 App Registrations - one for your SPA and one for your API. The API App Registration will need to "Expose an API" which really means to define a scope. Your SPA App Registration will need to Add an "API Permission" to the scope you defined from your API App Registration. (It will show up in My APIs.) This relationship is how #5 in the question is enforced.
Many of the steps for setting up authentication in Azure AD and Azure B2C are similar but Azure AD is designed for authenticating users that are part of your organization. Azure B2C allows you to build a set of users that aren't members of a particular Azure AD organization.

User-Consent screen not showing when using Postman and .Net 6 Web Api

So I am writing a .NET 6 Core Web Api using Azure AD as authentication for the API.
Now when using Graph API as example, you need to setup Graph API scopes in the App Registration. Lets use a delegated "user.read" permission for this example.
I use Postman to receive the access token for the application by authenticating as an user against Azure AD for the API. I would expect to receive a consent-screen so I can consent to the usage of "user.read". This does not happen though.. I get logged in and receive a valid access token. In the Backend though, it will throw an error because the user / admin did not consent to the application.
How do I get around this? Why don't I get asked to consent the permissions set up in the app registration? Neither in Postman, nor in a Swagger oAuth Flow..
My current workaround for this is to use a React application and sign in over the frontend application. Using the frontend application, I get asked to consent to the permissions. After consenting, I can use postman without getting the "user didn't consent" - error.
Any ideas? What did I miss?
Let's focus on the user-consent page first. When we created an azure ad app then add api permission for it, then use this azure ad app to make your .net 6 app/react app integrate azure ad to use azure authentication, and we go to the microsoft sign in page and successfully sign in, we will see a dialog which indicating that this app require you to consent a list of permissions. The permissions are correspond to the api permissions you set for the aad app. After consent once, then it won't ask you to consent again when sign in next time.
This consent only happened when users are signed in. Let's go back to the flows used to generate access token in Azure AD. Since you used delegate permission, then you may used the recommend Auth code flow(Another flow called ROPC flow can also generate delegate access token but not recommended). When we used auth code flow, we need to sign in first, the login url should look like this:
https://login.microsoftonline.com/tenant.onmicrosoft.com/oauth2/v2.0/authorize?client_id=azure_ad_app_id
&response_type=code
&redirect_uri=http://localhost/myapp/
&response_mode=query
&scope=user.read
&state=12345
We need to use it to get the auth code, then we can use the code to generate access token, per my test, I created a new azure ad app and when I directly hit this url in the browser and sign in, it still required me to give the consent. So I'm afraid the reason why you didn't see the dialog when test in post man is that you've consent it when test in react app, or you don't use auth code flow.

How to authenticate a frontend SPA app and API when both use Azure Easy Auth

The situation
I have a backend API and frontend SPA app both hosted in Azure. I have protected both these app services with Azure AD Easy Auth using the same app registration. This works - if I try and visit the frontend application I am redirected to the Microsoft login page where I can login and if I visit the API I am returned a 401 (I can login to it interactively via /.auth/login/aad). In both applications, I can visit /.auth/me to retrieve the id_token and access_token among other things. My SPA app will query this endpoint to retrieve the id_token. It then uses this token to communicate with the API via a Bearer token. This communication works and I can successfully call endpoints in this way.
The problem I am facing is that after some time, the id_token stored and sent by the frontend becomes invalid. I assumed the frontend would ask the use to login again which would refresh the id_token but this does not seem to be the case. I did some digging and apparently I should be sending the access_token up instead of the id_token. However, my API rejects that with:
{
"code": 401,
"message": "IDX10511: Signature validation failed. Keys tried: '[PII is hidden]'. \nkid: '[PII is hidden]'. \nExceptions caught:\n '[PII is hidden]'.\ntoken: '[PII is hidden]'."
}
https://jwt.io also fails to verify the access token.
I am having a lot of trouble in finding resources/documentation for an SPA app and API which both use Easy Auth and how to get them to talk to each other.
Things already tried
Removing any Microsoft Graph permissions from the app registration
Changing the manifest "accessTokenAcceptedVersion" to 2 for the app registration
Enabling both "ID tokens" and "Access tokens" for "Implicit grant and hybrid flows" for the app registration in the Azure portal
Using cookie auth (sending up the cookie with requests from the SPA)
Exposing an API and adding a permission for it
What is the standard way of authenticating a frontend and a backend when both are protected by Azure Easy Auth? Should I be using the access_token or id_token to authenticate? If the latter, how do I refresh it?
Looks like you are receiving access token for graph .
Please make sure scope is created for the backend web api .
Note:User.Read is for graph api access and may have nonce in token
generated which may make the signature invalidated for you web api.
So please create a new scope in the portal that identifies your backendapi i.e ; modify your app to acquire an access token for your API.
Your spa App > Expose an API > Add Scope.
(Create a new scope: say name = access_as_user)
In API permissions: Add a new permission for registered application and scope access_as_user. something like api://<app id>/ scope name
Make sure you place this name of scope where ever you are using in your app scope ( remove user.read if present and add scope of your api) and grant admin consent .
Now try again and check the token in https://jwt.io .
If there is further error saying audience invalid:
Go to Manifest: Change entry "accessTokenAcceptedVersion" from (null or 1) to 2
And try .If not successful ,try by removing api:// prefix from your app configuration scope.
References:
Tutorial: Authenticate users E2E - Azure App Service | Microsoft
Docs
azure - Setting additionalLoginParams with auth v2 - Stack Overflow
IDX10511 -issue (github.com)

Is there a way to Authorize Net Core 3.0 APIs with JWT and also Azure AD Tokens

I'm creating a web service that contains authentication (no identity), and I protected the APIS with JWT (the token is returned when the user logs in or registers by email, password, name, etc..) but I also have a microsoft login/register using MSAL, I also created an Azure App. How do I validate the login with microsoft to return the JWT or how could I implement the API authorization using both JWT and Azure AD.
You have currently registered an Azure application, you can set it as a client application, and then you need to create a back-end application representing the api, and then let the user log in to your client application to complete authorization and obtain an access token, and finally use the the access token calls the api application.
The operation process is as follows:
First expose the api of the back-end application and add the client application.
Next,under 'API permissions', give your front-end application access to your backend api:
Under 'API permissions' click on 'Add permission', then click on the 'My APIs' tab.
Find your backend application and select the appropriate scope.
Click 'Add permissions'.
Grant admin consent for your APIs.
Next, you need to use the auth code flow to obtain an access token,which requires you to log in to the user and obtain the authorization code, and then use the authorization code to redeem the access token.
Parse the token:
Finally, you can use the token to call the api.
The complete sample is for your reference.

Azure AD On-Behalf-Of authentication with separate frontend and backend applications

I feel like I may be going a little mad here.
I have basic architecture that features a frontend react app (SPA Auth) which communicates with a backend GraphQL Nodejs API service (Protected Web API Auth), hosted in Azure and authenticating with Azure AD.
Frontend access token requires User.Read access to azure graph, and access to Backend exposed scope
Backend exposes an API and single scope for access
Backend also requires User.Read access to azure graph on behalf of the user
I have been trying to configure the authentication to use the On-Behalf-Of Flow.
The react app successfully retrieves an access token using it's own app registration details
The access token is supplied to the backend service with each GraphQL request
The backend service verifies the access token provided to it
The backend service requests it's own access token via the On-Behalf-Of grant_type urn:ietf:params:oauth:grant-type:jwt-bearer
This all works EXCEPT I cannot get past this issue -
The user or administrator has not consented to use the application with ID '9b56c153-be42-499a-a41a-20176ed2ce69' named 'service-cbcity-api'. Send an interactive authorization request for this user and resource.
Basically I have not been able to successfully configure the app registrations and token requests to ensure that when the backend requests it's token it is allowed to call User.Read on behalf of the originally authenticated user.
In the On-Behalf-Of documentation it states the following regarding using /.default scope -
/.default and combined consent
The middle tier application adds the client to the known client applications list in its manifest, and then the client can trigger a combined consent flow for both itself and the middle tier application. On the Microsoft identity platform endpoint, this is done using the /.default scope. When triggering a consent screen using known client applications and /.default, the consent screen will show permissions for both the client to the middle tier API, and also request whatever permissions are required by the middle-tier API. The user provides consent for both applications, and then the OBO flow works.
I have tried all sorts of combinations of configuration in the App Registrations as well as different combinations of scope requests and I simply cannot get this to function as expected; the prompt doesn't seem to include the combined consent.
The only way I have gotten it to function is by manually providing admin consent to the Backend app for User.Read, this just seems like a hack and I would prefer to correctly configure this to ask for users consent.
If anyone has configured something similar before (seems like an expected use case) please let me know how you got it working, including configuration like
App registration config for Frontend service (eg api permissions set)
App registration config for Backend service (eg exposed scope, api permissions, authorized client applications)
Scopes requested on various auth requests
At this stage I am going to have to revert to possibly using the one App Registration and sharing the same access token between frontend and backend, even though personally this seems like a poorer solution to me.
Figured it out, my main issue was that I confused known client applications list with Authorized client applications.
The Authorized client applications is present in the UI and is configurable from the Expose an API area -
However this is different from known client applications which is a setting only found if you edit the manifest -
The key pieces to this puzzle are -
Add your frontend app client id to the knownClientApplications in your backend app registration manifest
When doing login with the frontend app your scope needs to be {{api_clientid}}/.default, where {{api_clientid}} is your the client id of your backend app registration
This will present the API Permissions you have configured in the backend app registration to the user at time of consent, and it will permit your backend process to retrieve an AccessToken using the OBO flow.
For what it's worth, this is the tutorial that helped me realise I needed to update the manifest and gave me guidance on the exact OAUTH request format -
https://github.com/Azure-Samples/active-directory-dotnet-native-aspnetcore-v2/tree/master/2.%20Web%20API%20now%20calls%20Microsoft%20Graph#how-to-deploy-this-sample-to-azure

Resources