Deriving from AuthenticationHandler
I will come to the code straightway because this is a mere re-write of the basic authentication scheme that we discussed in the previous tutorial. You might want to go through the previous tutorial for a recap.
The source code for the project can be obtained from the downloads attached to this video. Open the solution explorer and locate the file BasicAuthHandler.cs. Double click to open this file.
// BasicAuthHandler.cs class using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Options; using System.Net.Http.Headers; using System.Security.Claims; using System.Text; using System.Text.Encodings.Web; public class BasicAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions> { // override protected override Task<AuthenticateResult> HandleAuthenticateAsync() { // parse the header if ( AuthenticationHeaderValue.TryParse( Request.Headers["Authorization"], out var authValue) && ("basic".Equals(authValue.Scheme, StringComparison.OrdinalIgnoreCase)) ) { String []uidPwd = Encoding.UTF8.GetString( Convert.FromBase64String(authValue.Parameter ?? "")) .Split(":"); // check userid password against database // and get the role of the user if("hoven".Equals(uidPwd[0]) && "1234".Equals(uidPwd[1])) { // construct claims principal var claims = new List<Claim>(); claims.Add(new Claim(ClaimTypes.Name, uidPwd[0])); claims.Add(new Claim(ClaimTypes.Role, "Admin")); // construct claims principal var principal = new ClaimsPrincipal(new ClaimsIdentity(claims) ); var authTicket = new AuthenticationTicket(principal, Scheme.Name); return Task.FromResult( AuthenticateResult.Success(authTicket)); } } Response.Headers.Add( "WWW-Authenticate", "Basic realm=web api access"); return Task.FromResult( AuthenticateResult.Fail("invalid credentials")); } // ctor public BasicAuthHandler( IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } }
At the start we have the various namespace directives. You can always have a look at them if you get compilation errors when you write your own code.
The class BasicAuthHandler has been derived from AuthenticationHandler. This class contains most of the boilerplate code - and so the only thing that needs to be done now is override the HandleAuthenticateAsync function as you see here.
The if condition parses the authorization header, and checks if the scheme is basic authentication.
The header is then base64 decoded, and the user-id and password segregated from the colon separated string.
After that a database or identity based check of the credentials can be made, and an authentication ticket be created, and a success result sent.
If some failure occurs, then a www-authenticate header is added to the response stream and a failure result sent. We have discussed www-authenticate header in the previous tutorial, so you can have a look at that, in case you find it a bit new.
This is all we need to do.
Video Explanation (see it happen!)
Please watch the following youtube video:
The Program.cs File
And now we can examine the program.cs file.
Open the solution explorer, and locate the program.cs file. Double-click and open it!
// Program.cs file using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using MyRazorApp.Utility; var builder = WebApplication.CreateBuilder(); builder.Services.AddRazorPages(); const String BASIC_SCHEME_NAME = "Basic"; // register authentication builder.Services.AddAuthentication() .AddScheme<AuthenticationSchemeOptions, BasicAuthHandler> (BASIC_SCHEME_NAME, null); // register authorization builder.Services.AddAuthorization(); var app = builder.Build(); // add middleware app.UseAuthentication(); app.UseAuthorization(); app.MapRazorPages(); // decorate with [Authorize] app.MapGet("/token", [Authorize(AuthenticationSchemes =BASIC_SCHEME_NAME)]() => { return Results.Ok(new { access_token = "response from the server" }); }); app.Run();
Let's see the code line by line. The first few lines are the usual boilerplate code. After that we register the authentication services. Then we have the AddScheme method which connects the BasicAuthHandler class that we added just now.
And next we register the authorization services, and then the Authentication and Authorization middleware are added to the request pipeline.
Finally, our GET WebApi is now decorated with the Authorize attribute. This handler for this api will execute only if authentication succeeds. Look! how neat is the web api now.
Run the Project
Add a breakpoint in the web api handler. This will help us determine the flow of the program.
Now run the project and make a request to the /token end point. The browser presents a credentials box.
Type wrong credentials and hit enter. We verify that the breakpoint in the api handler is not hit, and the credentials box appears again.
Next enter the correct credentials - userid = hoven and password = 1234. Hit the enter.
We verify that the breakpoint is hit now, and the web api responds with a 200 OK response. Thanks!
Similar Posts
This Blog Post/Article "(C# ASP.NET Core) HandleAuthenticateAsync and Basic Authentication in WebApi" by Parveen is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.