The sample app can be found here --> https://github.com/Azure-Samples/active-directory-dotnet-webapp-wsfederation.
The application is using Microsoft.Owin and this is what I am expecting:
Users navigate to your application.
Your application redirects anonymous users to authenticate at Azure AD, sending a WS-Federation protocol request that indicates the application URI for the realm parameter. The URI should match the App ID URI shown in the single sign-on settings.
The request is sent to your tenant WS-Federation endpoint, for example: https://login.windows.net/solexpaad.onmicrosoft.com/wsfed
The user is presented with a login page, unless he or she already has a valid cookie for the Azure AD tenant.
When authenticated, a SAML token is returned in the HTTP POST to the application URL with a WS-Federation response. The URL to use is specified in the single sign-on settings as the Reply URL.
The application processes this response, verifies the token is signed by a trusted issuer (Azure AD), and confirms that the token is still valid.
My Question:
After the authentication a SAML token is returned via the HTTP POST. How can I view the SAML response? Currently when I view the HttpContext after POST there is nothing in it.
Thanks for any help.
In the App_Start/Startup.Auth.cs, you should be able to get access to the token.
I've added the SecurityTokenReceived Func:
app.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
Wtrealm = realm,
MetadataAddress = metadata,
Notifications = new WsFederationAuthenticationNotifications
{
AuthenticationFailed = context =>
{
context.HandleResponse();
context.Response.Redirect("Home/Error?message=" + context.Exception.Message);
return Task.FromResult(0);
},
SecurityTokenReceived = context =>
{
// Get the token
var token = context.ProtocolMessage.GetToken();
return Task.FromResult(0);
}
}
});
Related
Is there a good example or a walkthrough of a Blazor standalone app calling a function app via an Azure Active Directory B2C passing in a claim with an identity to the function?
I have been working my way through the documentation,
Secure an ASP.NET Core Blazor WebAssembly standalone app with Azure Active Directory B2C and
ASP.NET Core Blazor WebAssembly additional security scenarios but cannot get past 404 result.
So what am I trying to achieve? I want to put together a couple of POCs. The first is a Blazor standalone client app, authenticating via B2C, then passing an authorisation token claims token to an azure function, where I can unpack the user id and email address. The second is same as above, but with Api Management between the Blazor client and the functions api. Because of the nature of the project I am using the consumption plan for both the functions and api management.
So just concentrating on the first case (Blazor - B2C - Function), on the assumption if I get that to work, the api will follow…
I have a B2C client tenant with 2 applications: a front end app authorised for the scopes of a 2nd B2C application for the api. My Function app has authentication/authorisation set to 'Login with Active Directory' with the client ID set to the Front end app's client id, the issuer uri set to the B2C's pocsignupsignin Userflow and the 'Allowed Token Audiences' set to the client id of the Api in B2C.
I can get a JWT token via a browser and using postman successfully call a function in my function app passing a bearer token.
My Blazor app can log in to B2C. If I have no authorisation configured for the web app, then my function call is successful.
But once I turn authorisation I run into CORS 404 - origin 'https://localhost:44389' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. But I have CORS configured with 'Enable Access-Control-Allow-Credentials' and my client url configured.
What am I missing?
I also suspect that the token with the id will not be passed correctly.
From Program.cs
builder.Services.AddHttpClient("ServerAPI",
client => client.BaseAddress = new Uri(functionURI))
.AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAdB2C", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add(readScopeURI);
options.ProviderOptions.DefaultAccessTokenScopes.Add(writeScopeURI);
Console.WriteLine($"options.ProviderOptions.DefaultAccessTokenScopes {options.ProviderOptions.DefaultAccessTokenScopes}");
});
From CustomAuthorizationMessageHandler
public class CustomAuthorizationMessageHandler : AuthorizationMessageHandler
{
public CustomAuthorizationMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { B2CClientUrl },
scopes: new[] { "read", "write" });
}
}
From appsettings
"AzureAdB2C":
{
"Authority": B2C signupsignin uri,
"ClientId": B2C frontend client,
"ValidateAuthority": false
}
Function call
async Task GetFromDedicatedFunctionClient(string subUrl)
{
try
{
var client = ClientFactory.CreateClient("ServerAPI");
Console.WriteLine($"client.BaseAddress {client.BaseAddress}");
result =
await client.GetStringAsync(subUrl);
}
catch …
}
I would like to understand the authentication process for Azure B2C. It is my understanding that up to 50k authentication per months are free and you pay more if it goes over 50k.
I am developing two applications. One for front-end and another for back-end that will use Azure B2C to authenticate users.
Here is my scenario.
Users will go to UI portal to login with their social accounts.
Users can access back-end resources by making API call based on their permissions.
So it is my understanding that when you login to UI using your social account you receive an access token, which is one authentication in B2C. If they want to access back-end resources from API, it requires you to acquire access token by making another request as shown here. Below is the example code that acquires token silently.
// Retrieve the token with the specified scopes
var scope = AzureAdB2COptions.ApiScopes.Split(' ');
string signedInUserID = HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
TokenCache userTokenCache = new MSALSessionCache(signedInUserID, this.HttpContext).GetMsalCacheInstance();
ConfidentialClientApplication cca = new ConfidentialClientApplication(AzureAdB2COptions.ClientId, AzureAdB2COptions.Authority, AzureAdB2COptions.RedirectUri, new ClientCredential(AzureAdB2COptions.ClientSecret), userTokenCache, null);
AuthenticationResult result = await cca.AcquireTokenSilentAsync(scope, cca.Users.FirstOrDefault(), AzureAdB2COptions.Authority, false);
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, AzureAdB2COptions.ApiUrl);
// Add token to the Authorization header and make the request
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage response = await client.SendAsync(request);
My question is, when login in to UI using your social account and accessing back-end resources with another silent request. Does it count as 1 authentication? Or it counts as 2 authentication in Azure B2C?
After your web application handles the authentication response, the ConfidentialClientApplication.AcquireTokenByAuthorizationCodeAsync method retrieves the access token from Azure AD B2C and then writes it to a token cache, which is implemented by the MSALSessionCache class.
Before your web application invokes your web API, the ConfidentialClientApplication.AcquireTokenSilentAsync method reads the existing access token from the token cache and only requests a new access token from Azure AD B2C if:
The access token doesn't exist in the token cache;
It is about to expire; or
It has expired.
If the existing access token is read from the token cache, then you aren't charged for the issued token.
If a new access token is requested from Azure AD B2C, then you are charged for the issued token.
I have setup MSAL to fetch tokens from Azure AD B2C, setup dotnet core WebAPI to accept JWT tokens. Pointed WebApi at the Authority Endpoint:
services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(jwtOptions =>
{
string tenant = Configuration["AzureAdB2C:Tenant"], policy = Configuration["AzureAdB2C:Policy"], clientId = Configuration["AzureAdB2C:ClientId"];
jwtOptions.Authority = $"https://login.microsoftonline.com/tfp/{tenant}/{policy}/v2.0/";
jwtOptions.Audience = clientId;
jwtOptions.Events = new JwtBearerEvents
{
OnAuthenticationFailed = AuthenticationFailed
};
});
as per the samples. MSAL is configured to use the same policy and same client Id and receives token.
MSAL Authority - https://login.microsoftonline.com/tfp/{tenant}.onmicrosoft.com/{policy}/v2.0.
However, that AuthFailed event handler just returns
IDX10501: Signature validation failed. Unable to match keys.
and bounces the auth as a 401.
I went looking for signing keys and the kid of the token is not the same as the kid listed at the discovery endpoint.
https://login.microsoftonline.com/tfp/{tenant}/{policy}/discovery/v2.0/keys
Any ideas?
Seems that I had not selected the correct Issuer claim setting. MSAL was grabbing its token using the https://login.microsoftonline.com/{guid}/v2.0 endpoint whereas WebAPI was using the https://login.microsoftonline.com/tfp/{guid}/{policy}/v2.0/ issuer.
As per the docs this isn't an openid compatible endpoint, but works fine for B2C. Pays to check over the two different claim sets!
I have browsed all the tutorials regarding using Oauth to protect WebAPI in Azure active directory online. But unfortunately, none of them can work.
I am using VS 2017 and my project is .net core.
So far what I have tried is:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
ervices.AddAuthentication(); // -----------> newly added
}
In "Configure", I added:
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
Authority = String.Format(Configuration["AzureAd:AadInstance"], Configuration["AzureAD:Tenant"]),
Audience = Configuration["AzureAd:Audience"],
});
Here is my config:
"AzureAd": {
"AadInstance": "https://login.microsoftonline.com/{0}",
"Tenant": "tenantname.onmicrosoft.com",
"Audience": "https://tenantname.onmicrosoft.com/webapiservice"
}
I have registered this "webapiservice" (link is: http://webapiservice.azurewebsites.net) on my AAD.
Also, to access this web api service, I created a webapi client "webapiclient" which is also a web api and also registered it on my AAD and requested permission to access "webapiservice". The webapi client link is: http://webapiclient.azurewebsites.net
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://webapiservice.azurewebsites.net/");
//is this uri correct? should it be the link of webapi service or the one of webapi client?
HttpResponseMessage response = client.GetAsync("api/values").Result;
if (response.IsSuccessStatusCode)
{
var result = response.Content.ReadAsAsync<IEnumerable<string>>().Result;
return result;
}
else
{
return new string[] { "Something wrong" };
}
So theoretically, I should receive the correct results from webapiservice. but I always received "Something wrong".
Am I missing anything here?
You need an access token from Azure AD.
There are plenty of good example apps on GitHub, here is one for a Daemon App: https://github.com/Azure-Samples/active-directory-dotnet-daemon/blob/master/TodoListDaemon/Program.cs#L96
AuthenticationResult authResult = await authContext.AcquireTokenAsync(todoListResourceId, clientCredential);
This app fetches an access token with its client id and client secret for an API. You can follow a similar approach in your case. You can just replace todoListResourceId with "https://graph.windows.net/" for Azure AD Graph API, or "https://graph.microsoft.com/" for Microsoft Graph API, for example. That is the identifier for the API that you want a token for.
This is the way it works in AAD. You want access to an API, you ask for that access from AAD. In a successful response you will get back an access token, that you must attach to the HTTP call as a header:
Authorization: Bearer accesstokengoeshere......
Now if you are building a web application, you may instead want to do it a bit differently, as you are now accessing the API as the client app, not the user. If you want to make a delegated call, then you will need to use e.g. the Authorization Code flow, where you show the user a browser, redirect them to the right address, and they get sent back to your app for login.
To call web api protected by azure ad , you should pass this obtained access token in the authorization header using a bearer scheme :
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
I am stuck with Azure Active Directory OpenId.
I Have a ReactJs Web Client that does Authentication to Azure AD, When signed in a token_id is return representing a JWT token. This will be passed back to a ASP.Net Core Web Api and then Authenticated with the JwtSecurityTokenHandler in System.IdentityModel.Tokens.Jwt.
This is wired up with the Middelware when the application starts. When you access the OpenIdConnectOption Events on TokenValidated call, the token is validated already in the back end. A TokenValidatedContext get passed in to the Event Method which contains the JwtSecurityToken and I have access to the whole JWT Token.
I created a separate validation service that takes in the token string.
public ClaimsPrincipal ValidateToken(string token)
{
SecurityToken validatedToken;
var sectoken = TokenHandler.ReadJwtToken(token);
var tokenValidationParams = new TokenValidationParameters()
{
ValidAudiences = sectoken.Audiences,
ValidIssuer = sectoken.Issuer,
};
return TokenHandler.ValidateToken(token, tokenValidationParams, out validatedToken);
}
Is that the SigningKey object is null and the ValidateToken fails. If I do the same validation in the Event call then the SigningKey is valid and the cert is set to Microsoft Cert that seems to be issues by the Azure AD Service.
The main objective is to have a separate Token Validation Service.
Thanks in advance.