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

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)

Related

Incomplete bearer token when using authentication-managed-identity tag in azure api management

I've been trying to expose an API through azure API Management and I can't figure out what I'm doing wrong. Here's the situation:
My API is going to be called from an external application
They don't have an Azure Account in the same tenant
I want to enable external calls for my API by just using a subscription key (hence, why I'm using API Management), but also want to keep my actual API secured with Azure AD.
I have an API which is secured with Azure AD using OAuth2 and published into a Windows AppService
I have an App registration for that API, which i use to authenticate (it works from postman, for example)
app registration
I have Managed Identities turned on and permissions set.
I have added the API in API management
I added the authentication-managed-identity inbound rule, used the API Id Uri of the app registration as the resource value for it.
Api Management Config
When testing an endpoint from the APIM interface, I can successfully get a bearer token, but I get a 500 exception from the API which says: Neither scope or roles claim was found in the bearer token
bearer response
Here is the decoded bearer token, it doesn't have a scp attribute
bearer decoded
I'm not sure where I can specify a scope. If I use the full scope uri (api://guid/access.api.management) it will fail when trying to get a bearer token (The resource principal named api://guid/access.api.management was not found in the tenant).
I've even tried adding the Owner role to the APIM Identity for the AppService.
Maybe I'm not using this correctly, I'm pretty new at using Azure cloud and API Management so any suggestions are welcome.
Thanks.
You have expose an api protected by Azure, and currently you have an api application. Next, you need to create another application that represents the client, and then add the client application to the api application.
Next, go to the client application.
Under 'API permissions' click on 'Add permission', then click on the 'My APIs' tab.
Find your api 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.
1.Request an authorization code in the browser.
https://login.microsoftonline.com/{tenant id}/oauth2/v2.0/authorize?
client_id={client app client id}
&response_type=code
&redirect_uri={redirect_uri}
&response_mode=query
&scope=api://{api app client id}/{scope name}
&state=12345
2.Redeem token.
Parse the token:
I managed to get it working using the client credentials flow and storing the client secret in key vault.

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

Azure App Service Easy Auth with Client Credentials Grant flow in AAD B2C

Is it possible to secure a Web API in an App Service within Azure, using the built in Authentication/Authorization options and at the same time access it using the OAuth Client Credential Grant flow?
I have an app service being authenticated with Azure AD B2C. In that B2C tenant I have the web app/API registered and authentication working fine for using the web app.
I added a scope in the Published Scopes section of that AD B2C app and also added an App ID URI for that scope.
Then I created another App (ClientApp) in the tenant to represent another service I want to provide access to the first API. I generated a Secret key for it and also in the API access section added the scope I had created in the other app.
Then in postman I get the token using the next
Grant type: Client credentials
Access token URL: https://login.microsoftonline.com/mytenantname.onmicrosoft.com/oauth2/v2.0/token (for some reason I have to use login.microsoftonline as the b2clogin domain doesn't work for this)
Client ID: The ID that appears in the app registered for the ClientApp
Client secret: The secret generated under the ClientApp
Scope: the App ID URI I added the the web app registration.
I successfully get the token, but when I try to access the web site using the bearer token with postman, I just get a 401.
It is not supported to use client_credentials flow against AAD B2C application registrations. Therefore you have to instead create an App Registration through the normal Azure AD Blade instead.
https://learn.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-apps#current-limitations
Then the App Service must accept tokens from 2 different authorities, which App Services cannot do with EasyAuth (Authentication enabled at the App Service configuration menu).
You would instead need to use a library that can accept tokens from multiple authorities. There is an example here:
Use multiple JWT Bearer Authentication
I've spent hours to find a proper solution.
This solution: https://stackoverflow.com/a/48657826/11721142 doesn't work with mine B2C setup (is almost ideal :) ). I had to change original:
"additionalLoginParams": [
"response_type=code id_token",
"resource=<AAD-app-id-for-your-webapi-webapp>"
]
To:
"additionalLoginParams": [
"scope="scope=https://xxx.onmicrosoft.com/{Guid}/all+openid+offline_access""
]
Where
https://xxx.onmicrosoft.com/{Guid}/all is your scope identifier defined in B2C API permisions
Then... finally I can call {frontend-app-domain}/.auth/login/aad and... see: access-token, id-token and refresh-token after {frontend-app-domain}/.auth/me
You can also provide required scopes like that: {frontend-app-domain}/.auth/login/aad?scope=<see scopes above>
Not sure if I understand it correctly, but it seems you are using a URL as scope. It works if you set the scope to the application id of the app service (you can find an enterprise application with the name of your app service in azure AD) instead of using the URL. (format: /.default)
I am still trying to figure out if it is possible to use the URL as scope...

Authenticate Azure app service with AAD custom login in mobile app

I have created app service for mobile app. Then i have added Authentication to the app service. Then Selected Authentication type as "Log on with Azure AD". It is working fine.
Is it possible to have custom login page instead of browser based login screen?
I was able to get the token by using https://login.microsoftonline.com//oauth2/token. But not able to authorize the app service with this bearer token.
Is it possible to have custom login page instead of browser based
login screen?
This page is the authentication endpoint of AzureAD. Though it can be configured by Company branding, I think it cannot be customlized by yourself for Moblie APP.
I was able to get the token by using
https://login.microsoftonline.com//oauth2/token. But not able to
authorize the app service with this bearer token.
Authencation/Authorization for Web App is a feature that securing Web App behind those IDPs, NOT just like other azure resources you can use REST API to access it. I understand what you want to do . But this action is not recommended or supported.
I was able to get the token by using https://login.microsoftonline.com//oauth2/token. But not able to authorize the app service with this bearer token.
As juunas answered, your token may does not match the AAD provider you configured on Azure Portal. Details you could follow here to check your configuration. Moreover, you could use https://jwt.io/ to decode your access_token and validate the related properties (e.g. the aud should be the clientId you configured on Azure Portal,etc.).
As App Service Authentication / Authorization (EasyAuth) states as follows:
Users who interact with your application through a web browser will have a cookie set so that they can remain authenticated as they browse your application. For other client types, such as mobile, a JSON web token (JWT), which should be presented in the X-ZUMO-AUTH header, will be issued to the client. The Mobile Apps client SDKs will handle this for you. Alternatively, an Azure Active Directory identity token or access token may be directly included in the Authorization header as a bearer token.
For Azure Web App or Azure Mobile App, you could just access your endpoint as follows:
https://{your-app-name}.azurewebsites.net/api/values
Header: Authorization:Bearer {the id_token or access_token of AAD}
Or
https://{your-app-name}.azurewebsites.net/api/values
Header: x-zumo-auth:{authenticationToken}
Moreover, if you retrieve the access_token in your mobile app, you could also use it to retrieve the authenticationToken and use the authenticationToken for communicating with the backend endpoint.
POST https://{your-app-name}.azurewebsites.net/.auth/login/{provider-name,for your scenario, it would be AAD}
Body: {"access_token":"<your-access-token>"}
For your mobile client, you could use the client for Azure Mobile Apps, details you could follow here. Also, you could follow Authenticate users to understand the client-flow and server-flow authentication for App Service Authentication.
As Wayne Yang said, customization of the login page is limited to logos and some text.
I'm not sure if you can use the "Easy Auth" for APIs.
You might need to actually implement the authentication in your app.
In that case your API would validate the incoming JSON Web Token so that its signature is valid and that the audience and issuer are what is expected.
Most frameworks have JWT authentication available, so it mostly comes down to configuring that properly.

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