I was written a post that introduces Authentication and Authorization in .NET. And this post just does some supplements to that post.
Cookies
I was used Authentication with cookies. That means the user identity informations would stored in cookie and response to the client, for example, I made a ClaimsPrincipal
that stored user identity informations, and send to client via SignIn(claimsPrincipal, CookieAuthenticationDefaults.AuthenticationScheme)
in controller:
var claims = new List<Claim>
{
new(ClaimTypes.Sid, <Id>,ClaimValueTypes.Sid, ISSUER, ISSUER),
new(ClaimTypes.Email, <Email>, ClaimValueTypes.Email, ISSUER, ISSUER)
};
var claimsIdentity = new ClaimsIdentity(
claims, CookieAuthenticationDefaults.AuthenticationScheme);
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
return SignIn(claimsPrincipal, CookieAuthenticationDefaults.AuthenticationScheme);
SignIn
will create a SignInResult for Response, return it from controller is very important. You could see the set-cookie
header in response:
I preferring use cookies
There are several ways to do Authentication as like: cookie, JWT, etc. I would like to use cookie if I using browser-base client. for example: Web, Tauri, Electron, etc.
Because the modern broswers have more safety, and they do handle cookie automatically, they would receiving cookies and storing cookies, and append cookies when send request. Clients do not need to do extra process for cookies.
JWT would be more complicated then cookies.
The cookies that includes user identity informations could be contructed to Claims into User, so you can access they via User
in Controller:
var id = Guid.Parse(User.FindFirstValue(ClaimTypes.Sid)!);
var email = User.FindFirstValue(ClaimTypes.Email)!;
Authorization
.NET also has several ways to do authorize, see authorization
If we just want to check the Claims, can use Claims-based authorization
:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequiredId", policy => policy.RequireClaim(ClaimValueTypes.Sid));
});
But if we need some more complicate validation as like access database or other services, we would need the custom Authorization.
We need a new Attribute class that inherited AuthorizeAttribute
and implemented IAuthorizationRequirement
, IAuthorizationRequirementData
, here I named it LoggedInAuthorizeAttribute
:
public class LoggedInAuthorizeAttribute : AuthorizeAttribute, IAuthorizationRequirement, IAuthorizationRequirementData
{
public IEnumerable<IAuthorizationRequirement> GetRequirements()
{
yield return this;
}
}
This Attribute we would use to mark controllers that need authorization.
[HttpPost(), LoggedInAuthorize]
public async Task<IActionResult> Index()
{
// ...
}
And we need to create a new class that implement AuthorizationHandler<LoggedInAuthorizeAttribute>
and write some complicate logic in
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, LoggedInAuthorizeAttribute requirement)
{
}
And if we need other services as like access database, log, just injecting it!
private readonly DbContext _db;
public LoggedInAgeAuthorizationHandler(DbContext db)
{
_db = db;
}
Then we can use it.
And if we passed authorization, should call context.Succeed(requirement);
or context.Fail();
when not passed.
If passed, it will continue to access controller, or response to client with status code 401.
Not forget call builder.Services.AddScoped<IAuthorizationHandler, LoggedInAgeAuthorizationHandler>();
to enable it.
This blog post provides a supplementary explanation of Authentication and Authorization in .NET. The author focuses on the usage of cookies for authentication and highlights the benefits of using cookies, especially for browser-based clients like web, Tauri, and Electron.
The code example provided for using cookies for authentication is clear and easy to understand. The author demonstrates how to create a
ClaimsPrincipal
with user identity information and signs it in usingSignIn
. The resultingset-cookie
header in the response is shown as evidence of successful authentication.I appreciate the author's preference for using cookies in browser-based clients due to their automatic handling and safety features. The author rightly points out that cookies simplify the authentication process for clients, as they automatically receive, store, and append cookies in subsequent requests.
Regarding authorization, the author mentions the different ways .NET offers for authorization, including claims-based authorization and custom authorization. The author provides an example of using claims-based authorization by defining a policy that requires a specific claim type. For more complex authorization scenarios involving database access or other services, the author suggests creating a custom authorization attribute and handler. The code examples and explanations given for implementing custom authorization are helpful.
Overall, this blog post effectively explains the usage of cookies for authentication and provides insights into different authorization options in .NET. The author's preference for cookies in browser-based clients is well-justified, and the code examples are clear and concise.
To improve this blog post, I would suggest adding more details on the potential security considerations when using cookies for authentication. Additionally, providing an example of using JWT for authentication, as mentioned in the introduction, would enhance the completeness of the post. Lastly, it would be beneficial to include more real-world use cases or scenarios where custom authorization is necessary, to help readers understand the practical applications of custom authorization.
Keep up the good work!