Before I explain my problem I want to share some insights about what I am trying to do. I am developing a serverless application in Azure. My azure app service plan contains multiple azure function application and each function app contains multiple azure functions(HTTP triggers/API) in it. Now I want to authenticate all the serverless API. I am using firebase to authenticate my users. How I can achieve it in a minimal and reusable manner.
I already use firebase in asp.net core for authentication in a monolithic manner but for a situation like this, I am not sure How to do.
In a Monolithic manner. In our ConfigureServices method, we need to register the services necessary to handle authentication and to specify the parameters of our Firebase project.
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://securetoken.google.com/my-firebase-project";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "https://securetoken.google.com/my-firebase-project",
ValidateAudience = true,
ValidAudience = "my-firebase-project",
ValidateLifetime = true
};
});
Then in our Configure method, we have to do one simple call to register the actual middleware that will handle the authentication.
app.UseAuthentication();
Related
I have a standalone blazor WASM app that needs to send requests to an external app. I need to validate the authentication that I receive from the WASM app in the API.
I can see the Bearer token being sent and I checked it up in jwt.io, the data seems to make sense.
The client configuration looks like so :
"AzureAd": {
"Authority": "https://login.microsoftonline.com/common",
"ClientId": "****",
"ValidateAuthority": true
}
I've tried the following TokenValidationParameters without success:
var stsDiscoveryEndpoint = String.Format(CultureInfo.InvariantCulture, $"https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration");
var configManager = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint, new OpenIdConnectConfigurationRetriever());
var config = await configManager.GetConfigurationAsync();
var validationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = true,
IssuerSigningKeys = config.SigningKeys,
ValidIssuer = config.Issuer,
ValidateLifetime = false
};
I've loosened up the "Validate", hoping to find what's wrong, but I keep getting "Signature validation failed"
Any idea how I can validate the token?
There're 2 methods to validate the token. The first is writing a custom filter to intercept all the incoming requests and as you know there's a bearer token in the request header, then you can use jwt decode library to decode the token and validate the claims.
But I still recommend following official sample to protect your api via Azure AD directly. And here's a tutorial. You need to expose an API in Azure AD, then configure your app.
By the way, you've had the access token, I think you should have had your exposed api in AAD, so it mostly like that you only need to add authentication in your api project. Then In asp.net core web API project, you need to modify Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
And appsetting.json
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"ClientId": "37xxxxxxxb2d7",
"TenantId": "21xxxxxxd93",
"Audience": "api://37xxxxxb2d7"
},
Then in the controller, add [Authorize] attribute.
When I deploy a NET 6 blazor web app (server and client) into IIS but I got this error when I run it, http://blazorecommerceapi.findingsteve.net
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
Authorization failed. These requirements were not met:
DenyAnonymousAuthorizationRequirement: Requires an authenticated user.
When I run its api, http://blazorecommerceapi.findingsteve.net/api/product
But when I run it locally, it works as expected. I can run the blazor client page.
I can see the api data.
I am using Sqlite to persist the data and for the authentication, these are some codes in program.cs
builder.Services.AddScoped<IAuthService, AuthService>();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey =
new SymmetricSecurityKey(System.Text.Encoding.UTF8
.GetBytes(builder.Configuration.GetSection("AppSettings:Token").Value)),
ValidateIssuer = false,
ValidateAudience = false
};
});
app.UseAuthentication();
app.UseAuthorization();
I am working on updating our API (core 3.1) auth to use the latest Microsoft Identity nuget for use with MSAL for an Angular UI application. We have Azure Functions that will call into our API's using a Managed Service Identity and have setup several new app registrations for each API to use with MSAL in Angular. The same API's we call from an Azure function will also be called from the Angular UI. The problem I am running into is that I need to accept up to four different audiences in order not to break auth for everything.
Audiences needed:
client id of the API
https://management.azure.com/ for MSI
https://management.core.windows.net/ for MSI
client id of another app registration we use to generate tokens for automation testing
I am attempting to set the audiences in a list as follows:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(options => { }, options =>
{
options.Authority = Configuration["Authentication:Jwt:Authority"];
options.Instance = Configuration.GetSection("AzureAd")["Instance"];
options.ClientId = Configuration.GetSection("AzureAd")["ClientId"];
options.Domain = Configuration.GetSection("AzureAd")["Domain"];
options.TenantId = Configuration.GetSection("AzureAd")["TenantId"];
options.TokenValidationParameters.ValidateAudience = true;
var audience = new List<string>();
audience.Add(Configuration["AzureAd:ClientId"]);
audience.AddRange(new string[] {"https://management.azure.com/",
"https://management.core.windows.net/", "other api client id"});
options.TokenValidationParameters.ValidAudiences = audience;
});
When I attempt to call an endpoint via Swagger using a token created by another app (#4), I get this error:
IDX10214: Audience validation failed. Audiences: 'System.String'. Did not match: validationParameters.ValidAudience: 'System.String' or validationParameters.ValidAudiences: 'System.String'.
I also noticed when looking at the context in the events that none of the audience values I setup at runtime are present when the events trigger. ValidAudience and ValidAudiences are both null.
I need to figure out how to persist these settings in the events as my guess is that is why the audience validation is failing.
I have an ASP Net core API app with Identity Core for user and claim management and Microsoft.AspNetCore.Authentication as an Authentication middle-ware. I am using JWT middleware to issue bearer tokens.
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(cfg =>
{
cfg.RequireHttpsMetadata = false;
cfg.SaveToken = true;
cfg.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = Configuration["JwtIssuer"],
ValidAudience = Configuration["JwtIssuer"],
IssuerSigningKey = issuerSigningKey,
ClockSkew = TimeSpan.Zero // remove delay of token when expire
};
});
This works fine for Username/Password scenarios. Now I want to use another API (Azure Logic App) to make some REST calls to the ASP Core API, which has protected endpoints.
I am looking for guidance on how to achieve this. I have a couple of ideas:
Create a user in the identity table which will act as a service/automation account, same as a normal human user. Advantage - no change required. Downside - sounds a bit hacky.
Create some sort of an encrypted string/token, save it in API app.settings, and ask the consumer APIs to pass it in the header. Advantage - Easy to implement, downside - not sure how it will work with JWT bearer authentication pipeline. Also, sounds kind of static and insecure.
Implement something like an App ID and Secret. Advantage - Seems like a good practice, is scale-able for multiple Apps (consumers). Downside - I have no idea how to implement it using ASP core Identity and that too along with JWT pipeline.
I would really appreciate some guidance.
I have two .net applications.
Both applications have WebAPI 2.O APIs using C#.
Let's say one is parent application another one is a child.
Parent application has Owin authentication and all APIs working as expected with Authorization.
In child application, I want to use same Authorization provider used in the parent application. I don't want to use authentication for child application again.
Two things I have tried:
Use of same machine keys in both the applications
Tried to create a third independent .net application which will provide authentication and authorization for both the applications.
First one didn't work. I am not sure how I can achieve the second one.
Any help would be appreciated.
Thanks.
So, if I understood correctly, you want a way to authenticate a child service, based on the parent service authentication passing authentication between the services.
We just need the same thing here, to authenticate the microservices behind our front service (parent service).
We used JWT for that, using it we can solve that, because on the child services (in our case microservices) they trust the parent authentication.
The services work like this, the Parent Service or maybe another Authentication service creates the valid JWT to be used on the Parent Service.
When the Parent Service, receveives the JWT they will validate everything that's need to ensure the client is corret. When the Parent Service need to call the Child Service, it'll send the same JWT, but on the Child Service the JWT will be not the same, in our case we just validate the Lifetime and Issuer Sign Key.
We end up with a code like this on our Startup.cs file on our child services, our parent service/auth service was kept the same.
public static void ConfigureAuth(IServiceCollection services)
{
services
.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(o =>
{
o.SaveToken = true;
o.TokenValidationParameters = new TokenValidationParameters
{
// Must validate the signing key
ValidateIssuerSigningKey = true,
// Must validate the life time
ValidateLifetime = true,
// The issuer may vary in a multitenant scenario,
// that's why we not valid the issuer.
ValidateIssuer = false,
ValidIssuer = o.ClaimsIssuer,
// Allowing passing a token among multiple services (audiences).
ValidateAudience = false,
ValidAudience = "",
// Does not require expiration
RequireExpirationTime = false,
ClockSkew = TimeSpan.Zero
};
});
}
If you still have doubts I recommend you to look for Authentication Between Microservice, maybe that can help.
Store the generated authentication token (along with user identity info if needed) from the Parent application in a secure Redis cache.
You can then get the token from subsequent requests on the Parent API's authorized endpoints, and append it on any calls to your Child API:
public class ValuesController : ApiController
{
[Authorize]
public IHttpActionResult Get()
{
var authToken = Request.Headers.Authorization;
// send authToken with requests to child endpoints
}
}
Then on the Child API you can get the auth token in a similar manner, and lookup & validate it against the stored Redis tokens.
Extra points if you're getting the token in middleware.