How to login a user with a JWT in asp.net core - asp.net-core

I have an IdentityServer4 instance.
This IdentityServer4 instance is called by a separate asp.net core web app (from an http post login action) to receive a JWT.
What do I do with this JWT to "login" the a user?

Your client application that wants to authenticate should communicate with the Identity Server application on the Open ID Connect (OIDC) protocol. Asp.net core supports this out of the box with AddAuthentication().AddOpenIdConnect() extension that you can use on the IServiceCollection in your Startup.cs file.
In the Configure method, the call to UseAuthetication is what actually "login" the user (it will add the user claims to the incoming requests). So if you are using for example MVC, make sure to add that line before the call to UseMvc().
The Identity Server documentation even provides a sample of this here: http://docs.identityserver.io/en/latest/quickstarts/3_interactive_login.html#creating-an-mvc-client
A very simple example would look something like this:
public void ConfigureServices(IServiceCollection services)
{
// other configuration...
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.Authority = Configuration["auth:oidc:authority"];
options.ClientId = Configuration["auth:oidc:clientid"];
});
// other configuration...
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
// other configuration...
app.UseAuthentication();
// other configuration...
}

Related

Add authorization to header and access [Authorize] controllers

I use Jwt token to login with .NET Core API version 3.1.
I saw token generated but it couldn't access [Authorize] controllers, always returns 401. I've found the way to add authorization to the headers. However, there's no detailed instructions to set up and how to use it to access [Authorize] controllers.
Could anyone tell me how to pass header values by using HttpClient and is there any set up else in Startup.cs? Thank you so much for helping me!
Make sure you are adding JWT authentication to your DI pipeline in Startup.cs like this:
public void ConfigureServices(IServiceCollection services)
{
⋮
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Audience = "http://localhost:5001/";
options.Authority = "http://localhost:5000/";
});
⋮
}
Authorize with a specific scheme in ASP.NET Core
Overview of ASP.NET Core authentication
Also, make sure you are using the Authorization middleware in Startup.cs like this:
public void Configure(IApplicationBuilder app)
{
⋮
app.UseAuthentication();
app.UseAuthorization();
⋮
}
UPDATE:
Here is how you can setup the HttpClient:
var client = new HttpClient();
string token; // your token
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
ASP.NET Core Middleware

how to get the client_id that genereted a bearer token? (.NetCore2.1, IdentityServer4)

i working with netcore 2.1 and identityserver4 with Resource owner password flow
i need to get the client_id that generate the token bearer in one request
exist one way to get the client_id?
exist the relation in database userId,token,client_id?
the problem is that i not know what client_id make the request
i have a API in net core, multiple databases and multiples clients, in function of client_id i get information a database
By default , the access token issued from identity server 4 includes client_id claim:
After the client send request to your web api with access token , on web api side , add the authentication services to DI and the authentication middleware to the pipeline:
1.Add IdentityServer4.AccessTokenValidation NuGet package to your project
2.Update Startup to look like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore()
.AddAuthorization()
.AddJsonFormatters();
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ApiName = "api1";
});
}
public void Configure(IApplicationBuilder app)
{
app.UseAuthentication();
app.UseMvc();
}
Then you can get the claims which include the client id :

OpenIddict - Authorization and authentication with Microservices

I have a mobile (native) and web app (SPA) that talks to backend microservices (developed in core 2.0) for authentication/authorization and other domain related functions, which has configured using Opendidict. Both apps are getting access token. What I'm struggling with is, all microservices should accept bearer access token and authentication/authorization logged in user (a central auth-service), access token generated in auth microservice (OpenIddict 2.*). So what changes I'm missing in microservices, where REST APIs are marked [Authorize]?
Code from Auth Microservice:
public void ConfigureServices(IServiceCollection services)
{
var connection = Configuration.GetConnectionString("DefaultConnection");
services.AddDbContext<IdentityDbContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
options.UseOpenIddict();
});
services.AddAuthentication().AddOAuthValidation();
services.AddOpenIddict(options =>
{
options.AddEntityFrameworkCoreStores<IdentityDbContext>();
options.AddMvcBinders();
options.EnableTokenEndpoint("/connect/token");
// Enable the password flow.
options.AllowPasswordFlow().AllowRefreshTokenFlow();
options.SetRefreshTokenLifetime(TimeSpan.FromHours(1));
options.DisableHttpsRequirement();
});
services.AddDbContext<AuthDbContext>(options => options.UseSqlServer(connection));
services.AddScoped<IUserRepository, UserRepository>();
services.AddAuthentication(options =>
{
options.DefaultScheme = OAuthValidationDefaults.AuthenticationScheme;
});
services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdministratorRole", policy => policy.RequireRole("Administrator"));
});
}
Existing code in Notification Microservice
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MastersDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddAuthentication().AddOAuthValidation();
services.AddAuthentication(options =>
{
options.DefaultScheme = OAuthValidationDefaults.AuthenticationScheme;
});
services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdministratorRole", policy => policy.RequireRole("Administrator"));
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCors(builder =>
builder.WithOrigins("*")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowAnyOrigin()
);
//app.UseAntiforgeryToken();
app.UseMvc();
app.UseAuthentication();
}
Notification Controller:
// POST api/values
[HttpPost]
[Authorize]
public IActionResult Post(Notification notification)
{
//logic
return Ok();
}
For tokens to be correctly decrypted by all your micro-services, you need to make sure that the key ring containing the master keys (that are derived by ASP.NET Core Data Protection to create encryption and validation keys) is correctly synchronized. The procedure is described here: https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview.
Here's an example of how it could be done using a shared folder:
public void ConfigureServices(IServiceCollection services)
{
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(#"\\server\share\directory\"))
}
You'll also need to configure the two applications to use the same "application discriminator":
public void ConfigureServices(IServiceCollection services)
{
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(#"\\server\share\directory\"))
.SetApplicationName("Your application name");
}

ASP .Net Core Google Authentication

I have a problem with google authentication on my .net core web api application.
My use case is simple, get bearer token from google put token in authorization header as "Bearer {token}" and call my web api.
But I cannot make it work. After I get token from google on following url:
https://accounts.google.com/o/oauth2/v2/auth?scope=email%20openid&include_granted_scopes=true&state=some_test_state&redirect_uri=http%3A%2F%2Flocalhost%3A53512&response_type=token&client_id={someClientID}
I will make call to my api with header:
Authorization: Bearer {TokenValue}
But every time I'm getting 401 Unauthorized.
This is my Startup class:
public static IConfigurationRoot Configuration { get; private set; }
// This method gets called by the runtime. Use this method to add services to the container
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// Pull in any SDK configuration from Configuration object
services.AddDefaultAWSOptions(Configuration.GetAWSOptions());
// Add S3 to the ASP.NET Core dependency injection framework.
services.AddAWSService<Amazon.S3.IAmazonS3>();
IocConfig.Configure(services);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddLambdaLogger(Configuration.GetLambdaLoggerOptions());
var googleOptions = new GoogleOptions
{
AuthenticationScheme = "Google",
ClientId = "clientid",
ClientSecret = "cs",
SignInScheme = "Google"
};
app.UseGoogleAuthentication(googleOptions);
app.UseDeveloperExceptionPage();
app.UseMvc();
}
It's because your authentication scheme is "Google", but if you want to use bearer token you need to add it to your startup.cs
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
// here's your options
})
And use this authentication scheme instead of "Google"

Why .Net Core has its own claim types?

Based OpenidConnect specification the standard types for role claim and name claim is role and name. However in .net core System.Security.Claims.ClaimsIdentity.NameClaimType is set to "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" and System.Security.Claims.ClaimsIdentity.RoleClaimType is set to "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
My issue here is with role.
My ASP.NET core application is using OpenIdConnect for authentication. After successful authentication the OpenIdConnect provider sends back the role as a part of claims collection with Cliam.Type is set to role which is correct as per the OpenId specs.
However since .Net Core has its own type for role, IsInRole() method always returns false. Because I think IsInRole() method uses microsoft's role type for comparison.
Why .net is using differ types for claims instead of using standard convention? and how do I solve IsInRole() issue
Update 1
Well I tried configuring claim types during startup but it didn't work.
startup.cs
public class Startup
{
public Startup(IHostingEnvironment env)
{
// some stuff here that is not related to Identity like building configuration
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddAuthorization();
services.AddMvc(config =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
});
// Add Kendo UI services to the services container
services.AddKendo();
// Transform Microsoft cliam types to my claim type
services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
{
options.ClaimsIdentity.RoleClaimType = "role";
options.ClaimsIdentity.UserNameClaimType = "name";
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime appLifetime)
{
loggerFactory.AddSerilog();
appLifetime.ApplicationStopped.Register(Log.CloseAndFlush);
app.UseExceptionHandler("/Home/Error");
app.UseApplicationInsightsRequestTelemetry();
app.UseApplicationInsightsExceptionTelemetry();
app.UseStaticFiles();
app.UseIdentityServer(Configuration["Identity:Authority"], Configuration["Identity:ClientId"], Configuration["Identity:PostLogoutRedirectUri"]);
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
// Configure Kendo UI
app.UseKendo(env);
}
}
UseIdentityServer extension method
public static void UseIdentityServer(this IApplicationBuilder app, string authority, string clientId, string postlogoutRedirectUri)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
AuthenticationScheme = CookieAuthenticationDefaults.AuthenticationScheme,
LoginPath = IdentityConstant.CallbackPath,
AccessDeniedPath = new PathString(IdentityConstant.AccessDeniedPath),
CookieName = IdentityConstant.AuthenticationCookieName,
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap = new Dictionary<string, string>();
var connectOptions = new OpenIdConnectOptions()
{
AutomaticChallenge = true,
Authority = authority,
ClientId = clientId,
ResponseType = IdentityConstant.ResponseType,
AuthenticationScheme = IdentityConstant.OpenIdAuthenticationScheme,
SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme,
PostLogoutRedirectUri = postlogoutRedirectUri,
CallbackPath = IdentityConstant.CallbackPath,
Events = new OpenIdConnectEvents()
{
OnTokenValidated = async context =>
{
var userInfoClient = new UserInfoClient(context.Options.Authority + IdentityConstant.UserInfoEndpoint);
var response = await userInfoClient.GetAsync(context.ProtocolMessage.AccessToken);
var claims = response.Claims;
//We will create new identity to store only required claims.
var newIdentity = new ClaimsIdentity(context.Ticket.Principal.Identity.AuthenticationType);
// keep the id_token for logout
newIdentity.AddClaim(new Claim(IdentityConstant.IdTokenClaim, context.ProtocolMessage.IdToken));
// add userinfo claims
newIdentity.AddClaims(claims);
// overwrite existing authentication ticket
context.Ticket = new AuthenticationTicket(
new ClaimsPrincipal(newIdentity),
context.Ticket.Properties,
context.Ticket.AuthenticationScheme);
await Task.FromResult(0);
}
}
};
connectOptions.Scope.Add(IdentityConstant.OpenIdScope);
connectOptions.Scope.Add(IdentityConstant.ProfileScope);
connectOptions.Scope.Add("roles");
app.UseOpenIdConnectAuthentication(connectOptions);
}
Update 2
I use IdentityServer3 for authentication for all our applications. If the client application is developed using classic ASP.NET MVC then ASP.Net's JWT handler will transform incoming role claim type to http://schemas.microsoft.com/ws/2008/06/identity/claims/role (More details can be found here under Claims Transformation section)
However same is not true when client application is developed using ASP.NET Core. The .net core WILL NOT transform claimtypes to .Net claim type, and that is correct. However .Net Core internally uses .Net claim type to find is user's role claims.
That means I need to Transform .Net claim types to desired claim type, but not sure where?
What are the standard conventions? You're only thinking of it from the context of the OpenId Connect specification which is not the only identity standard out there. Microsoft have made it generic enough to support all identity systems.
The fault here seems to be in the OpenId Connect authentication implementation for not providing a ClaimsPrincipal that uses the correct claim type for role.
Having said that you can fix it by implementing your own ClaimsPrincipal and override the IsInRole() method to use the correct claim type.
Alternatively you might consider putting in a place some middleware to apply the appropriate role claims based on the OpenId claims coming back?
You can configure the claim types during application startup.
services.AddIdentity<ApplicationUser, IdentityRole>(options => {
options.ClaimsIdentity.RoleClaimType = "http://yourdesiredclaimtype";
options.ClaimsIdentity.UserNameClaimType = "http://yourdesiredclaimtype";
});
You can see the claim options on GitHub.