I have an application in ASP.Net core divided into 3 projects (API, data, services). The API contains the controllers that will be calling the methods in the services project (that's where I'll be processing the data).
Right now I am using the Authentification system ASP provides, so I added those lines in the startup method.
services.AddIdentity<IdentityUser,IdentityRole>().AddEntityFrameworkStores<RHPDbContext>();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = "RHP User",
ValidIssuer = "MYIssuer",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("MyRHPSuperKey"))
};
});
Since I am making an API I will be needing the token-based authentification
And also added this line in the configure method in startup
app.UseAuthentication();
My question now is how can I access the userManager in my services project so I can add users, or get the users, attribute roles to users...etc
Here is the way you can access userManager in your controller class.
public AccountController(UserManager<IdentityUser> userManager)
Related
guys!
Currently, I get a Bearer Token from an API.
What I want to do is:
Get token from external API
Use .NET [Authorize] in controller
Compare token returned from API with token informed in Authorization Header of request
If they are equal, authorize the request
If they are not equal, return 401
What's the best way of doing this?
After using [Authorize]attribute in your controller class; 3rd, 4th and 5th steps automatically performed by .NET built-in functions.
As far as I know, the asp.net core provide the build-in authentication schema to authenticate the JWT bear token.
You could put the right settings inside the registered service with the right secret and issuer settings, then your application will check the token and set the claims into your application to achieve the authentication.
More details about how it works, you could refer to this article.
If JTW is the only way you are authenticating endpoints, it should be as "simple" as;
services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options => {
options.SaveToken = true;
options.RequireHttpsMetadata = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "..TODO..",
ValidateAudience = true,
ValidAudience = "..TODO..",
IssuerSigningKey = new SymmetricSecurityKey(..TODO..);
};
});
From what I have researched, then it's best practice to seperate the API and IdentityServer into 2 projects (also 2 domains-i.e.: https://api.mywebsite.com and https://identity.mywebsite.com), so I did that.
In my IdentityServer/AuthenticationController.cs # login-method it is currently making a JWT-token with a list of Claims(role(s), name, Jti(idk what that is), email, etc, that it puts into the JwtSecurityToken.
How does i.e. [Authorize(Roles = "Admin")] in my WebAPI/ProductController.cs communicate with my IdentityServer and then authorizing it. Is it in "AddAuthentication(...);" in Program.cs services? Or in AddJwtBearer(...)? Or specifically in ValidIssuer?
Where is the connection between API(i.e. https://api.mywebsite.com) and IdentityServer(https://identityserver.mywebsite.com)?
My IdentityServer-project Program.cs:
...
builder.Services.AddIdentityServer().AddAspNetIdentity<IdentityUser>().AddClientStore<InMemoryClientStore>().AddResourceStore<InMemoryResourcesStore>();
builder.Services.AddAuthentication(auth =>
{
auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuerSigningKey = true,
ValidAudiences = builder.Configuration.GetSection("JWT:ValidAudiences").Get<string[]>(),
ValidIssuer = builder.Configuration["JWT:ValidIssuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JWT:Secret"]))
};
});
...
My API-project Program.cs:
...
builder.Services.AddAuthentication(auth =>
{
auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuerSigningKey = true,
ValidAudiences = builder.Configuration.GetSection("JWT:ValidAudiences").Get<string[]>(),
ValidIssuer = builder.Configuration["JWT:ValidIssuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JWT:Secret"]))
};
});
...
Also, are my both codeblocks well-written when it comes to IdentityServer and API authentication and authorization?
AddJwtBearer in the API is in charge of taking the incoming access token and converting it to a "ClaimsPrincipal" user. Then that user is passed with all its claim to the authorization handler that will determine if the user is allowed to pass or not.
For example the [Authorize(Roles = "Admin")] attribute will check the role claims in the user if it contains the Admin role claim or not.
Usually you don't need to specify the IssuerSigningKey in AddJwtBearer and instead let AddJwtBearer to automatically retrieve it from IdentityServer directly at startup. (just remove IssuerSigningKey). You typically add IssuerSigningKey if the API can't talk to IdentityServer.
I have inherited an ASP.NET Core 5 MVC application that has integrated the authentication of the users with a third-party identity server, WSO2.
The authentication is working fine, but I'm not able to understand how to retrieve the username of the user that has logged in.
Here is the code:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSession();
services.AddControllersWithViews();
services.AddTransient<IUserRepository, UserRepository>();
services.AddTransient<ITokenService, TokenService>();
services.AddAuthentication(auth =>
{
auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
//auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
};
});
services.AddHttpClient();
}
in the controller I have added:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "Administrator,Supervisor")]
Is the name and roles found in the JWT token?
If, so then the issue is that OpenIDConnect and Microsoft do not agree about what the name of the 'name' claim should be.
To address this, you need to tell AddJwtBearer, which claim is the 'name' and 'role' claim using:
.AddMyJwtBearer(opt =>
{
...
opt.TokenValidationParameters.RoleClaimType = "roles";
opt.TokenValidationParameters.NameClaimType = "name";
I'm trying to set up an ASP.Net 5 web project with JWTBearer authentication for API Controllers (Controllers decorated with the APIController attribute) and CookieAuthentication for MVC Controllers (Controller classes derived from Controller, to render Views). The relevant part of my ConfigureServices method in the Startup Class looks like this:
services
.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("My Secret Key")),
ValidateAudience = false,
ValidateIssuer = false,
RequireExpirationTime = false,
ValidateLifetime = true
};
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.LoginPath = "/account/login";
options.LogoutPath = "/account/logout";
options.AccessDeniedPath = "/account/accessdenied";
options.Cookie = new CookieBuilder {SameSite = SameSiteMode.Strict};
options.ExpireTimeSpan = TimeSpan.FromMinutes(20);
});
var schemePolicy = new AuthorizationPolicyBuilder(
CookieAuthenticationDefaults.AuthenticationScheme,
JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build();
services.AddAuthorization(o => o.DefaultPolicy = schemePolicy);
In an MCV Controller I have an Action like the below:
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
public IActionResult AnActionForAuthenticatedUsers()
{
// do some stuff...
return View();
}
Now I would expect that when an unauthenticated request for this action comes in the request will be forwarded to /account/login but it is not. Instead a 401 statuscode is returned.
The webserver says:
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
Authorization failed. These requirements were not met:
DenyAnonymousAuthorizationRequirement: Requires an authenticated user.
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[12]
AuthenticationScheme: Cookies was challenged.
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[12]
AuthenticationScheme: Bearer was challenged.
What am I missing here?
The problem here is not in Authentication policy, but in Authorization configuration
Create a separate policy for each type of Authorization and that would work as you are expecting:
services.AddAuthorization(o =>
{
o.AddPolicy(CookieAuthenticationDefaults.AuthenticationScheme, schemePolicy);
o.AddPolicy(JwtBearerDefaults.AuthenticationScheme, schemePolicy);
});
I have created custom middleware class which validates the JWT token. I am calling this method before app.AddMvc() in configure method. ***
I would like to know what are the things that I should add to Configuration services to authenticate my web API using JWT? I have added [Authorize] in my Controller class
Do I need to call my middleware class which validates the JWT token first in Configure method? or I should call App.UseAuthentication()
I am using the following order :
app.UseAuthentication();
app.MessageHandlerMiddleware();
app.UseMvc();
I am new to .net web API implementation. Could you please help me out?
From one of my answers you can see how we pass JWT token and how the code looks for classic .NET (non-core) ASP.NET WebAPI 2.
There are not many differences, the code for ASP.NET Core looks similar.
The key aspect is - when you add JWT config in Startup the app handles validation automatically.
services
.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuerSigningKey = true,
ValidateLifetime = true,
IssuerSigningKey = _configuration.GetSymmetricSecurityKey(),
ValidAudience = _configuration.GetValidAudience(),
ValidIssuer = _configuration.GetValidIssuer()
};
});
(use the above link to see the implementation of GetSymmetricSecurityKey, GetValidAudience, GetValidIssuer ext. methods)
Also very important part:
services.AddAuthorization(auth =>
{
auth
.AddPolicy(
_configuration.GetDefaultPolicy(),
new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser().Build()
);
});