app.UseWindowsAzureActiveDirectoryBearerAuthentication stopped working after upgrade from ADAL to MSAL - angularjs

I'm running some tests with MSAL but unfortunately it's not working as expected.
I had all configured for an ASP.NET MVC (.net 4.6) + Angular 1.6 SPA application with ADAL and ADAL Angular. All worked just fine but then I decided to try MSAL.
My configured provider's OnValidateIdentity handler in Startup.Auth.cs was being hit correctly with ADAL and I could add additional claims:
Provider = new OAuthBearerAuthenticationProvider
{
OnValidateIdentity = async context =>
{
Now that I changed to MSAL for Angular JS, I'm getting the ID Token and the Access Token but my OnValidateIdentity handler is not being hit anymore.
Is using app.UseWindowsAzureActiveDirectoryBearerAuthentication still valid when using MSAL?
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
.
.
.

MSAL is meant to be used with converged/v2.0 application registrations, while ADAL is usually meant to be used with v1.0 App registrations,
You should create a new application using the new portal in portal.azure.com if you're trying to migrate to the v2 endpoint. In addition to that these docs go over creating a v2.0 App Registration : https://learn.microsoft.com/en-us/graph/auth-register-app-v2
Please refer to this resource for more information on migrating from v1 to the v2 endpoint. https://azure.microsoft.com/en-gb/resources/samples/active-directory-dotnet-v1-to-v2/
In regards to the specifics of using app.UseWindowsAzureActiveDirectoryBearerAuthentication
// NOTE: The usual
WindowsAzureActiveDirectoryBearerAuthenticaitonMiddleware uses a
// metadata endpoint which is not supported by the v2.0 endpoint. Instead, this
// OpenIdConenctCachingSecurityTokenProvider can be used to fetch & use the OpenIdConnect
// metadata document.
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
AccessTokenFormat = new JwtFormat(tvps, new OpenIdConnectCachingSecurityTokenProvider("https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration")),
});
This is referenced from the startup.cs : https://github.com/AzureADQuickStarts/AppModelv2-NativeClient-DotNet/blob/a69a4cb41e821f0ea8dddc937ea401a03e2f49fe/TodoListService/App_Start/Startup.Auth.cs
Some more good reading that does a bit of a comparison between the v1/v2 sample apps can be found here : https://simonlamb.codes/2017/02/27/net332-introduction-to-authentication-on-azure-active-directory/

Related

ADAL to MSAL react migration givening token unauthorization error

I am migrating from ADAL to MSAL then getting the Unauthorized error when passing token into my api. (using React and .NET core ( .NET core 2.1 framework)
UIClient application is already registered then I have added SPA and add redirecturl.
Registered API application has added UIClient ClientId into
Expose API -> Authrozied client application
MSAL is able to generate the token.
Sending token to API (using .Net core 2.1 framework)
Failed in authorizing the token, getting 401 unauthorized error from API code.
appsetting.json file contain setting
AzureAd{
Authority: "https://login.microsoftonline.com/common",
ValidClientIds: ["Guid1","Guid2"],
"AllowedIssuers: ["stsurl1","stsurl2"]
}
Startup.cs code
services.AddAuthentication(opt => {opt.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;}).AddAzureAdBearer(opt2 => configuration.Bind("AzureAd", opt2)); ```
Please check if the audience is matching the clientId of the
application of the token is decoded in https://jwt.ms/. Audience
mmatch the client iD and sometimes it maybe AppId URI of the app i.e;
something like api://<clientId>.So please try checking for the same
by changing or also including clientId as both clientId and appId
uri or allowing both audiences.
Also if thats not the issue try changing accessTokenAcceptedVersion
property of the in the Web API app manifest in azure ad portal to 1
or null if it is 2 or change to 2 if it is already 1 or null.
Also please make sure that the scope of your web api
(api:///) which is exposed after giving api permissions is granted admin consent either from portal
itself or check with the admin .

Azure Active Directory - UI > API - 401 Error

Good Day,
Currently I have a single tenent with a React UI and .NET Core Apis secured by Azure Active Directory without any problems.
We have recently moved to a new Azure Tenent, new Active Directory etc. I have create two new App Registrations, one single App Service for UI and one for API. I have linked the App Service to AAD (UI = UI App Registration, API = API App Registration).
The problem is the API is getting a 401 error and I think see that in the original tenent the Bearer token is in a JWT format but in the new instance it's not, I believe it my be a graph api access key.
New Tenent:
Authorization: Bearer PAQABAAAAAAD--DLA3VO7QrddgJg7WevrQvEQVbZEMD8su-tIp9k2bTFUTort7SZgeDI52P6KRYefHgtmj4YrecgUKZJ2wylGuhvIzIz642n7Sg0VMU1RwKtrzWlaMqK62CaSoJcstxiEf6 *****
Orginal Tenent:
Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Im5PbzNaRHJPRFhFSzFqS1doWHNsSFJfS1hFZyIsImtpZCI6Im5PbzNaRHJPRFhFSzFqS1doWHNsSFJfS1hFZyJ9.eyJhdWQiOiI3OThkN2ZkOC0zODk2LTQxOGMtOTQ0Ny0wNGFlNTQ2OGFkNDIiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC83ZDE3NTU3Ni03Y2Y3LTQyMDctOTA5My0wNmNiNmQyZDIwNjAvIiwiaWF0IjoxNjE2NDUyNzExLCJuYmYiOjE2MTY0NTI3MTEsImV4cCI6MTYxNjQ1NjYxMSwiYWNyIjoiMSIsImFpbyI6IkFTUUEyLzhUQUFBQU9mejhPZHp *****
Please someone kindly enought to provide some guidance / input where I am going wrong.
Regards
Paul.
When using Azure AD to obtain an access token, an additional resource parameter is required. Otherwise, the access token is not a JWT.
For example, if your web API's application ID URI is https://contoso.com/api and the scope name is Employees.Read.All, then with oidc-client the client configuration should be :
scope: 'openid profile email Employees.Read.All',
extraQueryParams: {
resource: 'https://contoso.com/api'
}
In App Service auth configuration, you can use additionalLoginParams
"additionalLoginParams": ["response_type=code", "resource=https://contoso.com/api"]
If you did not use a custom application ID URI, it may look like
api://868662dd-3e28-4c7f-b7d5-7ec02ac9c601
Quickstart: Configure an application to expose a web API
Firstly, the scope is incorrect.
You should Expose an API in your API App Registration and then add it as a permission in your UI App Registration. You can refer to this document.
And when you try to call the 'https://login.windows.net/{tenant}/oauth2/authorize endpoint, you need to specify the scope to include api://{app id of the API App Registration}. For example: api://{app id of the API App Registration} openid profile email. Then the access token would be for calling your API.
At last, for CORS issue, please configure the CORS as * in your web app to see if it helps.
Try to follow this step: Configure App Service to return a usable access token
In my experience, this problem occurs, when you try to authorize against version 1 of the endpoint.
Instead of calling
https://login.microsoftonline.com/{tenant}/oauth2/authorize
call
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize
You might be required to set something like "metadata URL" in you authorization library to:
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/.well-known/openid-configuration
Make sure your builder follows this order...lifted from our API program.cs
These must be in order of
UseRouting -> UseAuthentication -> UseAuthorisation -> MapControllers
> app.UseRouting()
> app.UseAuthentication()
> app.UseAuthorization()
> app.MapControllers()
If app.UseAuthentication and app.UseAuthorization are not in this order in statement position you Will get 401 Unauthorised as at 01/2023 .Net 6 Core.

Blazor WASM calling Azure AAD secured Functions API

I have an Azure Functions API which uses Azure Active Directory authentication. I can test locally and deployed using a browser and curl calls in a process of:
Get a code
Use the code to get a token
Pass the token to authenticate and get the function result.
I now want to call this API from my Blazor WASM app but I'm sure there must be a nice MSAL call to do all the authentication but I cannot find any documentation on what that might be.
Does anyone have a code snippet to illustrate what needs to happen?
Further Information
My Azure Functions App and Blazor WASM client are not part of the same project and are hosted on different sub-domains of Azure hypotheticalapi.azurewebsites.net and hypotheticalweb.azurewebsites.net.
The web client application registration has API Permissions for the API and the API has an application registration which exposes itself with the scope that the client app has permissions for.
Again, the API and Web app work individually. I just don't seem able to get them to talk.
I have been following the "ASP.NET Core Blazor WebAssembly additional security scenarios" documentation but after several attempts I keep coming back to the error:
Microsoft.JSInterop.JSException: invalid_grant: AADSTS65001:
The user or administrator has not consented to use the application with ID 'e40aabb0-8ed5-4833-b50d-ec7ca4e07996' named 'BallerinaBlazor5Wasm'.
Send an interactive authorization request for this user and resource.
Even though I have revoked/deleted the client's permissions on the API, it has never repeated asking for consent. Is there a way I should clear the consent I previously gave? No idea how I might do that.
This GitHub Issue appears to be relevant.
I was stuck for the last two weeks with the same error code in the same setting: Blazor WASM talking to an AAD secured Azure Functions app.
What appeared to be a problem in my case was the scopes that I was listing in the http request when contacting AAD identification provider endpoints. Almost all examples I came across use Microsoft Graph API. There, User.Read is the scope that is given as an example. My first though was that even when I am contacting my own API I have to include the User.Read scope in the request because I was reasoning that this scope is necessary to identify the user. However, this is not the case and the only scope that you have to list when you call the authorize and token endpoints is the one that you exposed under the "Expose an API blade" in your AAD app registration.
I am using the OAuth2 authorization code in my example and not the implicit grant. Make sure that in the manifest of your API registration you have set "accessTokenAcceptedVersion": 2 and not "accessTokenAcceptedVersion": null. The latter implies the use of implicit flow as far as I know.
The scope the I exposed in my API is Api.Read. You can expose more scopes if you need but the point is that you only ask for scopes that you exposed.
I also have both following options unticked (i.e. no implicit flow). However, I tried with selecting "ID token" and it still worked. Note that the "ID token" option is selected by default if you let the Azure Portal create your AAD app registration from your function app Authentication blade.
Blazor code
Program.cs
This code has to be added.
builder.Services.AddScoped<GraphAPIAuthorizationMessageHandler>();
builder.Services.AddHttpClient("{NAME}",
client => client.BaseAddress = new Uri("https://your-azure-functions-url.net"))
.AddHttpMessageHandler<GraphAPIAuthorizationMessageHandler>();
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
.CreateClient("{NAME}"));
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
// NOTE: no "api://" when providing the scope
options.ProviderOptions.DefaultAccessTokenScopes.Add("{you API application id}/{api exposed scope}");
});
appsetting.json
"AzureAd": {
"Authority": "https://login.microsoftonline.com/{aad tenant id}",
"ClientId": "{application id of your blazor wasm app}",
"ValidateAuthority": true
}
GraphAPIAuthorizationMessageHandler.cs
Note that this class can have a different name. you'll then also reference a different name in Program.cs.
public class GraphAPIAuthorizationMessageHandler : AuthorizationMessageHandler
{
public GraphAPIAuthorizationMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { "https://your-azure-functions-url.net" },
// NOTE: here with "api://"
scopes: new[] { "api://{you API application id}/{api exposed scope}" });
}
}
I hope this works. If not, let me know.
At least you need to get the access token, then use the token to call the function api. In this case, if you want to get the token in only one step, you could use the client credential flow, MSAL sample here, follow every part on the left to complete the prerequisites.
The following are the approximate steps(for more details, you still need to follow the sample above):
1.Create a new App registration and add a client secret.
2.Instantiate the confidential client application with a client secret
app = ConfidentialClientApplicationBuilder.Create(config.ClientId)
.WithClientSecret(config.ClientSecret)
.WithAuthority(new Uri(config.Authority))
.Build();
3.Get the token
string[] scopes = new string[] { "<AppId URI of your function related AD App>/.default" };
result = await app.AcquireTokenForClient(scopes)
.ExecuteAsync();
4.Call the function API
httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
// Call the web API.
HttpResponseMessage response = await _httpClient.GetAsync(apiUri);
...
}

How to access a web api secured with Azure AD B2C using react js

I am having a .net core web api which is secured with Azure AD B2C and I am trying to access the api using react js msal library.
new Msal.UserAgentApplication(config.applicationId,
authority,
authCallback,
{ logger: logger,
cacheLocation: config.cacheLocation,
postLogoutRedirectUri: config.postLogoutRedirectUri,
redirectUri: config.redirectUri }
);
How to call a web api by passing the JWT token by using react js
It looks like you are using MSAL js v0.2.3 or v0.2.4, the new MSAL v1.0.0 release takes a configuration object in the constructor for the UserAgentApplication. Make sure to refer to the documentation for correct usage for your version:
https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki
Regardless - you'll need to initiate a login after initialization by calling loginPopup or loginRedirect on the MSAL app. After successful credentials are entered and you return to your web app, you can call MSAL's getUser() ( or getAccount() if using v1.0.0 )
After that, you'll need to get an access token by calling acquireTokenSilent(scopes), where the scopes array is defined by your B2C application.
( In v1.0.0 you'd pass a parameter object like this: { scopes: [scopes] } )
Once you have an access token - you'll need to pass it as the Authorization header on your REST request, and then validate it in your WebAPI by calling back to B2C before processing the request.
This is an example of the validation process:
https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-webapi-manual-jwt-validation/

With ADAL.js use of ClientId as Audience in Endpoints for SPA config

I am creating an AngularJS client to interact with two ASP.Net Core (v2.0) APIs, all of which are to be secured with Azure AD. Because of a requirement to use roles and groups we will be using the v1 endpoint and therefore ADAL.js. The UI client must be a separate project from each API, additionally the UI project is not a Visual Studio project, rather VSCode with npm. In the UI project I am using:
AngularJS 1.6.9
UI-Router 1.0.15
ADAL.js 1.0.17
After a long time of trial and error, I finally got the UI to authenticate to the API after I took the following steps:
In the UI project I included endpoints in the Adal init() function:
var endpoints = {
'http://localhost:8000/api0/': '<API_0_CLIENT_ID HERE>',
'http://localhost:8001/api1/': '<API_1_CLIENT_ID HERE>',
};
adalAuthenticationServiceProvider.init(
{
tenant: 'slurm.onmicrosoft.com',
clientId: '00000000-0000-0000-0000-XXXXXXXXXX',
endpoints: endpoints
},
$httpProvider
);
There are two endpoints, one is the base url for each API, and each has the corresponding clientId for that API as assigned when each api was registered in Azure AD. Also, the UI project, when registered in Azure AD, is granted appropriate permissions to each api. Once I did set these endpoints in the init() function, and the UI had each clientId of the APIs, the UI was able to authenticate properly to both APIs. This is the SO question that finally clued me in to try this: 32352325
If I do not provide the endpoints in the UI project, a token is not even passed back to the API and therefor authentication fails.
What I am not sure of (there is no clear documentation), is whether the UI clientId should be set as the audienceId in each API or keep each API client id embedded in the UI.
Questions:
1) For an AngularJS UI project that is separate from each API, and each project is registered separately in Azure AD, do we register the UI clientId as an audience with each API or allow the client to know each API clientId?
2) Why are endpoints seemingly required to be specified in the ADAL init() function on the client? Or am I using them incorrectly?
Based on the ReadMe of this Azure-Samples project, it would appear the UI should know the clientId of each API (under Step 3, Configure the WebApp it says):
In the TodoListWebApp project, //...// Find the TodoListResourceId property and replace the value with the Application ID of the TodoListService app
But, this example is not an SPA example and therefore does not use the implicit flow.
Much thanks in advance!
This is what I eventually deduced (after lots of digging and trial and error). Again the example assumes the UI is a separate project from any APIs. For the original questions above:
1) For an AngularJS UI project that is separate from each API, and
each project is registered separately in Azure AD, do we register the
UI clientId as an audience with each API or allow the client to know
each API clientId?
The Client UI knows about each API but uses the App ID URI
2) Why are endpoints seemingly required to be specified in the ADAL
init() function on the client? Or am I using them incorrectly?
Read on.
For each API your UI will access, there needs to be an endpoint declared in the endpoints map structure. For example, say I am trying to access 2 separate APIs registered in AAD with the following relevant info:
Tenant: slurm.onmicrosoft.com
API_0
Home Page URL: 'https://localhost:8000'
App ID URI: 'https://slurm.onmicrosoft.com/00000000-0000-0000-0000-aaaaaaaaaaaa'
API_1
Home Page URL: 'https://localhost:4000'
App ID URI: 'https://slurm.onmicrosoft.com/00000000-0000-0000-0000-bbbbbbbbbbbb'
adalAuthenticationServiceProvider.init(
{
tenant: 'slurm.onmicrosoft.com',
clientId: '00000000-0000-0000-0000-XXXXXXXXXX',
endpoints: {
'localhost:8000': 'https://slurm.onmicrosoft.com/00000000-0000-0000-0000-aaaaaaaaaaaa',
'localhost:4000': 'https://slurm.onmicrosoft.com/00000000-0000-0000-0000-bbbbbbbbbbbb',
}
},
$httpProvider
);
The following are the references:
Go to http://www.cloudidentity.com/blog/2015/02/19/introducing-adal-js-v1
Scroll to section Calling API via CORS
Follow the link to the github example:
https://github.com/Azure-Samples/active-directory-angularjs-singlepageapp-dotnet-webapi
On the github example readme
Go to Step 3: Configure the To Go API to use your Azure Active
Directory tenant
See step #7 which states:
Enter a mapping of the To Go API endpoint location to its resource
identifier, or App ID URI. The name of the property of the endpoints
object should be the location of the To Go API.
Also if you look into the code example you will see in the web.config of the ToGoAPI it indicates the 'Audience' value is set to the App ID URI of the ToGoAPI
Note: The App Id URIs used above are intentionally left in a format similar to the default values Azure AD will
provide when you register an application. These can be changed (just make sure you change it everywhere).
Note 2: In the endpoints map you see that the keys do not include the scheme and to not fully match the corresponding Home Page URL. When I included the scheme i.e https:// I got 401 responses from the API

Resources