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

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

Related

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)

Azure Active Directory Oauth 2.0 Client Credentials Flow with API Management Access Token issue

I have had been struggling to make my Azure Active Directory Oauth 2.0 Client Credentials Flow work with API Management. but I get authenticated via postman too. But in return I do not get any access token just a bunch of HTML. How can I fix this? The settings of the applications are exactly as per the documents including the validation of JWT Policy.
Basically I want my client apps to connect with my azure API's using Oauth 2.o without any consent using provided client id/secret. I'm trying to set this up for now with ECHO API provided out of the box with API Management console.
thanks
Postman Access token Error Screen
To use application permissions with your own API (as opposed to Microsoft Graph), you must first expose the API by defining scopes in the API's app registration in the Azure portal. Then, configure access to the API by selecting those permissions in your client application's app registration. If you haven't exposed any scopes in your API's app registration, you won't be able to specify application permissions to that API in your client application's app registration in the Azure portal.
For an example, if I sent scope parameter with custom name like https://testwebapp.in/.default without configuring same as application ID URI in Azure AD then is an expected behavior and you will get error AADSTS500011.
scope parameter in the request should be the resource identifier (application ID URI) of the resource you want, affixed with the .default suffix. For the Microsoft Graph example, the value is https://graph.microsoft.com/.default. This value tells the Microsoft identity platform that of all the direct application permissions you have configured for your app, the endpoint should issue a token for the ones associated with the resource you want to use.
Reference: https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow#application-permissions

Is this a right flow for React app on frontend and Express app on backend to authenticate and authorize users with Azure AD?

I have React app on the frontend, I have registered it at Azure AD as REACT_AZURE and I use #azure/msal-react npm package to authenticate the user.
In order to protect my Express routes, I have registered another app at Azure AD as API_AZURE, and in "Expose an API" section I have added scope 'access_as_user'.
In REACT_AZURE app in "API permissions" section I have added permission for 'access_as_user'.
Now I can acquire access token for 'access_as_user' scope and make an API call to my express server.
In my protected route, I am using passport-azure-ad BearerStrategy to validate the access token, if it is valid I am authorized to get resources.
Is this the right flow? Do I have to register two apps with Azure AD? if not, how do I do it right?
Is this the right flow? Do I have to register two apps with Azure AD?
if not, how do I do it right?
Of course, your process is absolutely correct. You need to register two applications in Azure, one representing the client application and the other representing the api application. Then expose the api of the api application and add the client application to the api application. Then let the user log in to the client application to complete the authenticate and obtain the token, and use the token to call the api. I have answered similar questions before, you can refer to it.
But I’m not sure if you want to control which users can access the api based on the user role. If you only want certain users in the tenant to access the api, then you can add a step that is to create an app role and grants users who you wish to have access to the api. Then users in the tenant who are not granted the app role will not have permission to access the api. see more detailed answer.

Securing shared APIs with Azure AD

I'm working with a client to define a security strategy and have got stuck trying to get something working. I'm new to Azure AD so this may actually not be possible.
Consider the following application landscape.
I have 4 "API" applications:
API-A, requires interactive user and role based permissions
API-B, access via service demon, client_credential grant
API-C, must not be authenticated against directly
API-D, access via service demon, client_credential grant
A user / demon authenticated against API-A or API-B should be able to access API-C as well. However the demon authenticated against API-D must not be able to access API-C.
I was expecting to be able to use the "Expose an API" and "API Permissions" of the App Registrations to be able to control to "roles" returned in the JWT, I cannot seem to get it to work or find any decent guide on how this can be achieved.
EDIT: For clarity the API applications are not hosted within Azure, I am just looking to use Azure AD to provide authentication
It may be helpful for you to distinguish between client apps and API apps (or resource servers in OAuth2 lingo). Each of them has to be registered separately. Your list above seems to merge them together, which is a likely source of confusion for you.
The former (client apps) acquire tokens, the latter receive them from the clients with the service request. Authentication is only only involved when client apps acquire tokens. APIs do not authenticate - they use tokens to authorize access to their services. Clients acquire tokens either on behalf of a user - and the user authenticates and consents as part of the process, or on their own behalf (client creds). In AAD an API app may expose/define scopes/permissions which may be included in one or both of these token types. An API may decide not to require any tokens (sounds like your API-C). You Expose (available) Permissions on API apps, you specify (required) API Permissions on client apps. At runtime (if using the AAD V2 endpoint) a client may request fewer scopes than it is is configured with as Required. That applies only if the client is using delegated tokens (user based). (Note that an API app may also be a client app to another API app (common in multi-tier systems).
BTW, where the clients or APIs are deployed is totally immaterial to the above. At most deployment affects the value of the reply url you need to specify for some client apps (not APIs).

Azure client app accessing Azure api secured by AD

I have an Angular 5 app and a web api app, both of which are hosted in Azure.
They have been secured with Azure AD at the website level e.g. no anonymous access is allowed.
When browsing the Angular site, it asks me to log in fine and I can access .auth/me which uses the local cookie to get token/claim information.
I now want to call the separate api but not sure how to go about it.
Both sites have an application in Azure AD, and I've set the client to have delegated permissions of 'Access to API'.
I've tried accessing the api using both the local cookie from the client (not sure if this would work) and the token returned .auth/me but neither work.
In my client manifest I have the following:
"resourceAppId": "3cddd33c-2624-4216-b686-7f8fa48f38cf", // api id
"resourceAccess": [
{
"id": "c2712c68-ea93-46d2-9874-61b807b19241",
"type": "Scope"
}
]
but haven't seen any additional scopes added to the claims, should it?
According to your description, you have both created the separate AAD application for your Angular app and your web api app, and configured the delegated permissions for your Angular AAD app to access the web api AAD app.
Based on my understanding, you are using the build-in App Service Authentication / Authorization for authentication, at this point you could do not need to change code on your app backend. You may have set Action to take when request is not authenticated to Log in with Azure Active Directory instead of allowing anonymous access, at this time your app service would directly redirect the user for authentication. After logged, your client could access https://{your-angular-app-name}.azurewebsites.net/.auth/me for retrieving the logged user info. For accessing your web api website, you could just send the request as follows in your angular client:
GET https://{your-webapi-app-name}.azurewebsites.net/api/values
Header Authorization:Bearer {id_token or access_token of AAD}
UPDATE:
That is exactly the route I'm trying to implement. One thing missing though, I had to add the client application id to the allowed token audience of the api app in Azure.
For retrieving the access_token, you need to set additional settings for the AAD provider in your Angular web app as follows:
"additionalLoginParams": [
"response_type=code id_token",
"resource=<AAD-app-id-for-your-webapi-webapp>"
]
Details you could follow this similar issue.
Use the EasyAuth server flow for logging, you would get the access_token, and you could leverage https://jwt.io/ to decode your token as follows:
Pass the access_token as the bearer token to your webapi web app, at this time you do not need to specific the ALLOWED TOKEN AUDIENCES.
At this time, you could invoke .auth/refresh against your Angular web app for refreshing the access_token, then you could use the new access_token to access your webapi web app.
I want roles included in the token so might have to stick with id?
If you want your Web API exposing access scopes to your Angular application which would be contained in the access_token as the scp property, you could follow the Configuring a resource application to expose web APIs section in this tutorial. Moreover, you could also follow Application roles.
UPDATE2:
You could follow Authorization in a web app using Azure AD application roles & role claims for detailed tutorial and code sample.
The usual approach would be to use ADAL.JS (or MSAL.JS with AAD v2 endpoint/B2C) to get an access token for the API.
ADAL.JS uses a hidden iframe to get an access token using the user's active session in Azure AD.
You can find an example Angular app here: GitHub.
An especially important part of the ADAL.JS configuration is here:
var endpoints = {
// Map the location of a request to an API to a the identifier of the associated resource
"https://myapi.azurewebsites.net/": "https://myaadtenant.onmicrosoft.com/MyApi"
};
The property name/key should be the URL for your API. ADAL-Angular detects calls to URLs starting with that, and attaches the correct access token to them.
The value should be the App ID URI of the API. You can find it from your API's App Registration from Azure Active Directory -> App registrations -> All Apps -> Your API -> Settings -> Properties.
You do need to enable implicit grant flow on the Angular app from the app registration for the SPA. You can find it from the Manifest.

Resources