How to implement multi-tenant JWT Authentication with different AWS Cognito UserPools per tenant with .Net Core - authentication

I have a diifferent AWS Cognito UserPool per Tenant, but I need to be able to validate the token depending on the tenant-specific request.
I'm aware that there is currently no support for this in .Net Core. Can anyone advise a way to handle this? Can you dynamically set the JWT Bearer once the application is running?
To get it working, I've tried to add multiple JWTBearers in the Startup.cs, but I encounter the error: InvalidOperationException: Scheme already exists: Bearer.
services.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(cfg =>
{
cfg.SaveToken = true;
cfg.TokenValidationParameters = <TENANT_1_CONFIG>.TokenValidationParameters();
})
.AddJwtBearer(cfg =>
{
cfg.SaveToken = true;
cfg.TokenValidationParameters = <TENANT_2_CONFIG>.TokenValidationParameters();
});
Ideally I'd like to be able to set this config once the application is running, depending on the request.

Related

Set up authentication in .net-core using ws-federation

I'm trying to set up authentication against ADFS using ws-federation
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme;
})
.AddWsFederation(options =>
{
options.Wtrealm = "https://localhost:44351/";
options.MetadataAddress = "https://xxx/federationmetadata/2007-06/federationmetadata.xml";
})
.AddCookie();
Six claims are returned, but I know that there are other claims which are not returned. For example the claim nameidentifier (http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier).
How can I make ADFS return more claims?
As far as I know, you should set the ADFS server to return the more claims instead of doing it at the client side.
You should have the enough permission to manage your server and follow this article to modify the ADFS server to return more claims or custom claims.
More details, you could refer to this article.

How to version an API such that one version of the api will require a bearer token and the other version does not

I have an asp.net core web api. Say if i want to have two versions of the api such that, version 1 will require a bearer token and version 2 will not require the barer token.
Since the token configuration code resides in the startup.cs file, how do I have two startup.cs files to match my requirement above? i am not even sure if it is legal to have two startup.cs files targeting two different versions of the api because the code to configure versioning of an asp.net core api will also reside in the startup.cs file.
Let me know what options are available to achieve my requirement above.
My current startup.cs file with token authentication enabled look like this..
public void ConfigureServices(IServiceCollection services)
{
services.Configure<AzureADSettings>(Configuration.GetSection("AzureAd"));
var azureADSettings = Configuration.GetSection("AzureAd").Get<AzureADSettings>();
var validAudience = new List<string>
{
azureADSettings.Audience
};
services.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(o =>
{
o.Authority = $"{azureADSettings.Instance}/{azureADSettings.TenantId}/";
o.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = true,
//azureADSettings.Audience
ValidAudiences = new List<string>(validAudience)
};
});
}
The Authorize middleware applies that check if you do not explicitly a controller or action as Anonymous. Maybe what you can do is:
Put the [Anonymous] attribute on top of your controller(s).
Mark your v1 API end-points in your controller(s) as [Authorize].
Leave the v2 API end-points as-is.
This way, the v2 API end-points should work fine with users not having a bearer token but v1 API end-points should expect a valid bearer token.

Not getting .AspNetCore.AzureADCookie when authenticating postman against Azure Active Directory

We are developing a web application communicating with its backend API. API is written in .NET Core and is running in Azure and is using OpenID authentication against Azure Active Directory. Configuration of the authentication process is below (as you can see we're using cookie based authentication):
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = AzureADDefaults.CookieScheme;
options.DefaultChallengeScheme = AzureADDefaults.AuthenticationScheme;
options.DefaultSignInScheme = AzureADDefaults.CookieScheme;
})
.AddAzureAD(options =>
{
configuration.Bind("AzureAd", options);
});
services.Configure<CookieAuthenticationOptions>(AzureADDefaults.CookieScheme, options =>
{
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Lax;
options.Cookie.MaxAge = new TimeSpan(7, 0, 0, 0);
});
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Authority = options.Authority + "/v2.0/";
options.TokenValidationParameters.ValidateIssuer = false;
});
We want to test our application API in Postman and we have set up a request with authentication against AAD (configuration below). Postman is able to make it through authentication and we get the access_token, however the request to API fails.
When we compared Postman cookies and browser cookies we discovered, that browser contains a cookie postman is missing .AspNetCore.AzureADCookie. It's Friday afternoon and we really got into desperation phase and have no clue what may be wrong. How can we make Postman to call AAD in a way it returns such cookie in response and adds it to the API request.
You should be able to use your browser cookies by installing Postman Interceptor extension.
Please try the same and let me know if it works.
https://learning.postman.com/docs/sending-requests/capturing-request-data/interceptor/#syncing-cookies
You can also ref the following ->
Postman is not using cookie

How to set custom token provider for cookies in ASP.NET Core Identity 3.1?

tl;dr: Is there way how to set custom TokenProvider or something similar e.g. (jwt tokens has IssuerSigningKey) to cookies?
I have a .Net Core 3.1 backend with identity Authentication. I had a problem with confirming generated email token.. “Invalid Token” error. From this SO answer I found out there can be problem with my hosting. I have shared hosting for my application and my application was often restarted. I believe this was causing my problems. So as answer suggested I created my own TokenProvider:
services.AddIdentity<AppUser, AppRole>()
.AddEntityFrameworkStores<MyContext>()
.AddDefaultTokenProviders()
.AddTokenProvider<AesDataProtectorTokenProvider<AppUser>>(TokenOptions.DefaultProvider);
This helps. For authentication I was using JwtBearer tokens with custom IssuerSigningKey
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
})
I found out that SignInManager from Identity is creating Cookies by default. So I tough it will be better to use this cookies instead of my jwt tokens saved in localstorage. So I set up expiration time for my cookies to 30 days.
services.ConfigureApplicationCookie(options =>
{
// Cookie settings
options.ExpireTimeSpan = TimeSpan.FromDays(30);
});
Everything worked great.. until I hosted my application to server. I believe that when my app is restarted, cookie validation fail and I am getting 401 from server. So..finally to my question.. Is there way how to set custom TokenProvider or something similar e.g. (jwt tokens has IssuerSigningKey) to cookies?
There are no existing API (class, method) for you customize cookies in ASP.NET Core Identity.
You can build yourself without ASP.NET Core Identity: https://learn.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-3.1
Reference: https://github.com/dotnet/aspnetcore/blob/master/src/Http/Http.Abstractions/src/CookieBuilder.cs
https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.cookiebuilder?view=aspnetcore-3.0
https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.cookieoptions?view=aspnetcore-3.0

Asp.Net Core 2.0 and Azure AD B2C for authentication on WebApp and API

I have an existing small app that I use for test, it is in Asp.Net Core 1.1 for both the Web App and the API, the authentication is done using Azure AD B2C.
I am trying to move it to .Net Core 2.0 but I can't figure how to get it working, I tried using both sample from GitHub Azure-Samples for Web App and API, but I have either an unauthorized or 500 error when trying to access the api, if you have a working example for calling a web api from a web app using 2.0 and protected by AD B2C it will be greatly appreciated.
Edit:
The sample I use to test are :
Web App : WebApp-OpenIDConnect-DotNet core2.0
Web Api : B2C-WebApi core2.0
, I changed the appsettings values to match my b2c directory.
For my asp.net core 1.1 test app I use the same samples as above but from the master branch, with the same value for appsettings.
Edit 2
by default, in startup.cs I have this :
services.AddAuthentication()
.AddJwtBearer(option => new JwtBearerOptions
{
Authority = string.Format("https://login.microsoftonline.com/tfp/{0}/{1}/v2.0/",
Configuration["Authentication:AzureAd:Tenant"], Configuration["Authentication:AzureAd:Policy"]),
Audience = Configuration["Authentication:AzureAd:ClientId"],
Events = new JwtBearerEvents
{
OnAuthenticationFailed = AuthenticationFailed
}
});
which gives me the following error:
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://localhost:44352/api/values/5
Microsoft.AspNetCore.Server.Kestrel:Error: Connection id "0HL89JHF4VBLM", Request id "0HL89JHF4VBLM:00000001": An unhandled exception was thrown by the application.
System.InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found.
if modified services.AddAuthentication like that
services.AddAuthentication(sharedOption =>
{
sharedOption.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
the error is now
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler:Information: Failed to validate the token xxx.
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidSignatureException: IDX10500: Signature validation failed. No security keys were provided to validate the signature.
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.d__6.MoveNext()
I saw a pull request on the sample which correct this issue (Link), the services.AddAuthentication must be change to:
services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(jwtOptions =>
{
jwtOptions.Authority = $"https://login.microsoftonline.com/tfp/{Configuration["Authentication:AzureAd:Tenant"]}/{Configuration["Authentication:AzureAd:Policy"]}/v2.0/";
jwtOptions.Audience = Configuration["Authentication:AzureAd:ClientId"];
jwtOptions.Events = new JwtBearerEvents
{
OnAuthenticationFailed = AuthenticationFailed
};
});
I got this example working both for Core 1.1 and Core 2.0, please add the Oath Authentication as below,
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddAzureAdB2C(options => Configuration.Bind("Authentication:AzureAdB2C", options))
You configuration options will be defined inside of the class "AzureAdB2CAuthenticationBuilderExtensions", which is found inside of the
azure b2c project
Looks like your token is not being update it from the Azure, are you able to get the token from your web app? could you please verify that you are not getting null
Did you register your api scopes on your azure b2c tenant web app?
"ApiScopes": "https://fabrikamb2c.onmicrosoft.com/demoapi/demo.read"
you have to set scope in your web api and allows to be read on the web app, please follow click the link in order to set it up