Blazor WASM with Azure AD SSO - azure-active-directory

I have a Blazor WASM client and a Web API with working Azure AD authentication and authorization but the users always have to push the button to log in. I want to use SSO for make them easy this process. I tried to find on Internet how I can put this feature in the application but no success. Can someone to show the right direction, please?
I do not insert source code because those who are experienced in this will know what it is about. Thx

• Yes, you can configure SSO in Blazor WASM app very easily by following the below said procedure as shown. Also, you need to determine the authentication method for the Blazor WASM app, i.e., SAML, OAuth 2.0, OpenID as such. Therefore, I am considering SAML as the authentication mechanism in here.
To facilitate the SAML SSO authentication through Azure AD, you will have to install packages related to it in your .NET Core product from its official website. Here, we are using ‘ComponentSpace.SAML.2.0’ package using the NuGet Manager. Once the installation is complete, we need to inject the service of ‘ComponentSpace’ in our project by adding service and cookies in the ‘Program.cs’ file whose code looks like this: -
builder.Services.Configure<CookiePolicyOptions>(options =>
{
// SameSiteMode.None is required to support SAML SSO.
options.MinimumSameSitePolicy = SameSiteMode.None;
});
builder.Services.ConfigureApplicationCookie(options =>
{
// Use a unique identity cookie name rather than sharing the cookie across applications in the domain.
options.Cookie.Name = “StudentPortal.Identity”;
// SameSiteMode.None is required to support SAML logout.
options.Cookie.SameSite = SameSiteMode.None;
});
// Add SAML SSO services.
builder.Services.AddSaml(builder.Configuration.GetSection(“SAML”));
Once done, we can modify the ‘appsettings.json’ file to add to the ‘ComponentSpace’ settings. There are many parts which we can add in the ‘appsettings.json’ file,i.e., ‘schema’, ‘LocalServiceProviderConfiguration’, ‘PartnerIdentityProviderConfigurations’, ‘SingleSignOnServiceUrl’, ‘SingleLogoutServiceUrl’ and ‘ArtifactResolutionServiceUrl’ refers to the IDP server side’s service URL wherein it should be your Azure Cloud’s URL and these URLs are where our applications send sign-on and logout requests which also includes the ‘PartnerName’, i.e., IDP’s name and URL.
To do so, you will have to include the below in your ‘appsettings.json’ file placed under ‘wwwroot’ folder: -
{
"oidc": {
"Authority": "< insert the SSO provider URL here >",
"ClientId": "< unique client id as specified in the SSO provider >",
"ResponseType": "code",
"DefaultScopes": [
"openid",
"profile"
],
"PostLogoutRedirectUri": "authentication/logout-callback",
"RedirectUri": "authentication/login-callback"
}
}
Thus, once done, we will add a new file to the same folder by the name of ‘LoginDisplay.razor’ which is displayed as follows: -
#using Microsoft.AspNetCore.Components.Authorization
#using Microsoft.AspNetCore.Components.WebAssembly.Authentication
#inject NavigationManager Navigation
#inject SignOutSessionStateManager SignOutManager
<AuthorizeView>
<Authorized>
<button class="nav-link btn btn-link" #onclick="BeginSignOut">Log out</button>
</Authorized>
<NotAuthorized>
Register
Log in
</NotAuthorized>
</AuthorizeView>
#code{
private async Task BeginSignOut(MouseEventArgs args)
{
await SignOutManager.SetSignOutState();
Navigation.NavigateTo("authentication/logout");
}
}
Thus, as all the configurations required are done as given above, the program will call ‘SAML/InitiateSingleSignOn’ function and send an SSO sign-in request to IDP, and now our program automatically run into the ‘SAML/AssertionConsumerService’ function and start to wait for IDP's reply, after receiving the response from the IDP. Please find the below snapshots for your reference while following the above steps: -
Certificate import: -
Adding ‘SAML Service Provider’ as a SAMLController as said: -
Adding ‘ComponentSpace.SAML.2.0’ package as below: -
For more detailed information, kindly refer to the below links: -
https://kevinzheng1989.medium.com/build-the-azure-sso-login-page-in-blazor-server-application-with-saml-b0959a50c0a6
https://scientificprogrammer.net/2022/08/12/single-sign-on-user-authentication-on-blazor-webassembly-signalr-client/

Related

How to redirect on backend for Angular application using Itfoxtec to access app through Azure Active Directory

I am new to using ITfoxtec for Azure Active Directory SAML logins. I read the StackOverflow entry for Nuget ITfoxtec SAML & Angular (and other similar entries for CORS issues), but I still do not understand how to adapt the GitHub Angular example from https://github.com/ITfoxtec/ITfoxtec.Identity.Saml2 to my needs. When running the ITfoxtec GitHub example, the Login method of the AuthController.cs file is immediately executed when I launch the test Angular application, and brings up the Azure Active Directory login prompt.
For my application, I need to click a "Login using Azure Active Directory" button on the Angular front end to call a backend method that can then redirect to another method to attempt login.
.NetCore C# code:
SSOController.cs file:
// This method is called by an Angular front end button when the user wishes to log in via Azure Active Directory SSO
[AllowAnonymous]
[Route("AzureAuth")]
[HttpGet]
public IActionResult AzureAuth(string returnUrl = null)
{
var binding = new Saml2RedirectBinding();
Saml2Configuration config = GetSamlConfig();
binding.SetRelayStateQuery(new Dictionary<string, string> { { relayStateReturnUrl, returnUrl ?? Url.Content("https://localhost:44397/api/sso/AssertionConsumerService") } });
//return binding.Bind(new Saml2AuthnRequest(config)).ToActionResult();
// This gives a CORS error, so we have do ensure that we do the redirection at the backend
// so we try redirecting with "RedirectToAction"
return RedirectToAction("https://localhost:44397/api/sso/AssertionConsumerService");
}
My AssertionConsumerService() method (located in Dev at "https://localhost:44397/api/sso/AssertionConsumerService"), which I need to be redirected to:
[Route("AssertionConsumerService")]
[HttpPost]
public async Task<IActionResult> AssertionConsumerService(HttpRequestMessage request)
{
// After user enters AAD SSO information, redirect should point to here.
// This API endpoint is hit if I test from Azure Enterprise Application SSO testing with the redirect API set to this method.
// I do not understand how to do backend redirects from AzureAuth() method to this method, and ensure that the HTTP request data is correct.
}
Just a follow up to my own question. For logging in directly from the Angular front end, I am having success with using "#azure/msal-angular". Once the end user clicks the "Log in with Azure Active Directory" button and is authenticated back to the frontend, I forward the authentication details to the backend for authorization checks.
I am still using ITfoxtec at the backend to process what can be directly sent from the "Azure Enterprise Applications > Set up single sign on > Test single sign-on with ..." for testing purposes. With the Azure "App registrations > Authentication > Platform Configuration" set to "Single-Page Application", I am making good progress in development and testing.
Sounds like you got a solution.
You can load the Angular application before login if it is hosted a place in the ASP.NET application that do not require the user to be authenticated. Then you can start the login process your selv and validate if the user is authenticated.

Get error "login.live.com page can’t be found" when invoking Azure Ad as external IDP in IdentitySrever4

I try to follow this tutorial to add Azure Ad as another external IDP for my IdentityServer4 service (I have gotten Windows and Google working already). I can get the "Azure Ad" button displayed on my IdentityServer login page now, but when I click on it, I get the following error returned:
Here is how I configured Azure Ad in ConfigureServices of my Startup class. I also tried replacing "aad" with "oidc", which is what I used and worked in Google, but no difference here.
and here is how my Azure Ad account configuration looks like. The colors match up with above indicating the values I use in my code:
Can someone tell me what I may be doing wrong here?
Initially please try by deleting history in the browser and use "login.microsoftonline.com/<tenantId>/v2.0/" as authority string.
And options.CallbackPath = "/signin-oidc"; options.ResponseType = "code id_token";
NOTE : The identity platform which is used by Microsoft has a character limit for links. This type of error will appear if the authorization request or link is longer than the said limit,.
Protocols like OpenID Connect, allow state as a parameter in the authorization request, and the identity provider will return that state in the response as you can find that in error page you provided .
Because of which the request URL becomes large as sometimes the state parameter is long.(which might be the possible case here)
Try to call the AddOidcStateDataFormatterCache extension method on the IServiceCollection in startup class which uses the distributed cache in the backend like:
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddTestUsers(Config.GetUsers());
services.AddAuthentication()
.AddOpenIdConnect("aad", "Azure AD", options =>
{
// ...
})
you can write the way as below :
services.AddOidcStateDataFormatterCache(); //Add this line
services.AddAuthentication()
.AddOpenIdConnect("aad", "Azure AD", options =>
{
// ...
})
You may check these similar References for more details :
Sign-in with External Identity Providers — IdentityServer4 1.0.0 documentation
(Or)
See section : 22.4 Handling the callback and signing in the user in IdentityServer4 Documentation
At least check for dns or firewall issues .

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);
...
}

Blazor Client side get CORS error when accessing Azure Function using Azure Active directory

I've been trying to deploy my Blazor PWA for 2 days without any success so far, if someone has an idea of what I’m doing wrong I would be really grateful.
hello
I could resolve most of my issues by myself but I'm now stuck on a CORS problem using AAD.
Here's my project setup:
Blazor webassembly client hosted on Static Website Storage (works
great), Net 5
AzureFunctions connected to an Azure Sql Server database (works great
with anonymous authentication in Blazor)
Azure Active Directory I want to use to authenticate the users.
(protecting both the blazor app and the functions)
So I’ve created an App registration, added my static web site address as SPA uri and uncheck both implicit.
In my blazor client, program.cs, I’ve added the following code to connect to AAD:
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication); //contains clientId, Authority
options.ProviderOptions.DefaultAccessTokenScopes.Add("https://graph.microsoft.com/User.Read");
options.ProviderOptions.LoginMode = "redirect";
});
That works great too, I can login, authorize view works as expected.
The problem is when I try to authenticate Azure functions with «Login with Azure Active Directory»,
I' tried with both express and advanced configurations (using clientId, tenantId) but when I
Access to fetch at 'https://login.windows.net/tenantid/oauth2/authorize ... (redirected from 'https://myfunctionstorage.azurewebsites.net/api/client/list') from origin 'https://*****9.web.core.windows.net' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
I have of course enabled CORS for my Blazor Client Address on Azure Functions configuration but the problem seems to be about the login windows uri...
Also, if I enable the token id implicit flow in the App registration and access the login url in the browser it works perfectly fine.
All the examples I could find so far are about msal 1.0 and App registration using implicit flow instead of SPA so it doesn't help...
Thank you for your time and your help.
UPDATE:
I’ve done more researches since yesterday and I think it could by related to my HTTPClient, currently I use the basic one (with just a base adress).
But I’ve seen on some example that when using the Client using AAD it needs more parameters, for example:
builder.Services.AddHttpClient("companiesAPI", cl => { cl.BaseAddress = new Uri("https://localhost:5001/api/"); }) .AddHttpMessageHandler(sp => { var handler = sp.GetService<AuthorizationMessageHandler>() .ConfigureHandler( authorizedUrls: new[] { "https://localhost:5001" }, scopes: new[] { "companyApi" } ); return handler; });
Is that AuthorizationMessageHandler needed ?
Also I see some references to the need of using the «use_impersonation» scope.
Are those changes (on HttpClient and the use_impersonation scope) also required when using msal2/SPA app registration ?
Thank you
If want to call the Azure function projected by Azure AD in Blazor webassembly client, please refer to the following steps
If you want to call Azure function projected by Azure AD in angular application, please refer to the following code
Create Azure AD application to protect function
Register Azure AD application. After doing that, please copy Application (client) ID and the Directory (tenant) ID
Configure Redirect URI. Select Web and type <app-url>/.auth/login/aad/callback.
Enable Implicit grant flow
Define API scope and copy it
Create client secret.
Enable Azure Active Directory in your App Service app
Configure CORS policy in Azure Function
Create Client AD application to access function
Register application
Enable Implicit grant flow
configure API permissions. Let your client application have permissions to access function
Code
Create Custom AuthorizationMessageHandler class
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
public class CustomAuthorizationMessageHandler : AuthorizationMessageHandler
{
public CustomAuthorizationMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { "<your function app url>" },
scopes: new[] { "<the function app API scope your define>" });
}
}
Add the following code in Program.cs.
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");
// register CustomAuthorizationMessageHandler
builder.Services.AddScoped<CustomAuthorizationMessageHandler>();
// configure httpclient
// call the following code please add packageMicrosoft.Extensions.Http 3.1.0
builder.Services.AddHttpClient("ServerAPI", client =>
client.BaseAddress = new Uri("<your function app url>"))
.AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
// register the httpclient
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
.CreateClient("ServerAPI"));
// configure Azure AD auth
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add("<the function app API scope your define>");
});
await builder.Build().RunAsync();
}
Call the API
#page "/fetchdata"
#using Microsoft.AspNetCore.Components.WebAssembly.Authentication
#inject HttpClient Http
<h1>Call Azure Function</h1>
<p>This component demonstrates fetching data from the server.</p>
<p>Result: #forecasts</p>
<button class="btn btn-primary" #onclick="CallFun">Call Function</button>
#code {
private string forecasts;
protected async Task CallFun()
{
forecasts = await Http.GetStringAsync("api/http");
}
}

Swaggerbuckle with AAD --Error AADSTS500013: Resource identifier is not provided

I'm trying to use AzureAD to get a token in swagger and test my .netCore2.1 API with it.(using Swashbuckle.AspNetCore 4.0.1)
To do this I completed the following steps
1. Created a Web API project(asp.net Core2.1)
2. Register an Azure AD (AAD) app for the Web API
3. Updated the Web API project to use Azure AD authentication
4. Register an AAD app for the Swagger web site https://localhost:5001/swagger
5. Granted permissions for the Swagger AAD app to access the Web API AAD app
6. Generated a Client Secret for the Swagger AAD app
7. Enabled OAuth2 implicit flow on the Swagger AAD app
8. Added Swagger to the Web API project
When I authenticate, I am getting the following error.
Error AADSTS500013: Resource identifier is not provided .
When I click authorize
I am getting error AADSTS500013: Resource identifier is not provided.
My WebAPI project's startup.cs file goes here
Please help me understand which resource Identifier I am missing here or how to resolve this error.
Or any pointers on how to use Swaggerbuckle with ASP.NET Core webAPI, especially with AAD authenticated WebAPI ?
UPDATES
I copied my webAPI's APPID URI from azure portal. ie Dashboard>>Microsoft - App registrations>>MY_API_APP>>Settings>>Properties>>App ID URI
But I am still getting the same error when I try to authenticate
Error AADSTS500013: Resource identifier is not provided
Usually resource identifier which uniquely identifies your application. You can easily found your resource Identifier Like below:
When logged into the Azure portal, Azure Active Directory > App Registrations > [App Name] > Settings> Properties, then it's the App ID URI
See the screen shot:
In the orignal OAuth 2.0 specification, there is no resource parameter in the authorization request. It use scope parameter. The authorization and token endpoints allow the client to specify the scope of the access request using the "scope" request parameter.
As document explains, when Azure AD implement the OAuth 2.0(1.0 endpoint), the resource is used to specify the access_token you request for which resource. And the Azure AD will issue the token which the scp based on the permission you config on the portal for the resource.
And in the v2.0 endpoint, the Azure AD also use the scope to support the dynamic permission request. More detail you can have a look here.
Usual Reason for Error
Make sure you have configured below steps accordingly.
Like below:
Application Manifest
Reply URI
Request Endpoint
For V1.0 refer this URI https://login.microsoft.com//oauth2/authorize
For V2.0 refer this URI https://login.microsoft.com//oauth2/v2.0/authorize
Application Consent
Need to confirm your application required resource access permission you could refer here
I believe this step can lead you to sort out your problem.
With Azure AD V1.0 app you need set resource to identify which api you want to access during the authentication&access token requests :
Code flow :
https://learn.microsoft.com/en-us/azure/active-directory/develop/v1-protocols-oauth-code
Implicit flow :
https://learn.microsoft.com/en-us/azure/active-directory/develop/v1-oauth2-implicit-grant-flow
In Swashbuckle.AspNetCore 4.0.1 , you need to config the resource as parameter if you want to get access token for accessing your web api :
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "MySite API V1");
options.OAuthClientId("19c73866-562f-482a-bafb-89d9fe9b0aaa");
options.OAuthAppName("Swagger Api Calls");
// add resource
options.OAuthAdditionalQueryStringParams(new Dictionary<string, string>() { { "resource", "f479db30-9b62-431b-98c2-bcaae52203cf" } });
});
For us Nan Yu's response got us one step closer.
We added the resource in the AdditionalQuerystringParams like so:
options.OAuthAdditionalQueryStringParams(new Dictionary<string, string>() { { "resource", "{ResourceIDHere}" } });
We are now able to get passed the Auth step in Swagger, but trying routes out doesn't seem to pass the token to the api called and so we get a 401 Error.
change { "scope", API_AppID_URI} to { "resource", API_AppID_URI}

Resources