locked
Basic Authentication + Bearer Jwt Authentication RRS feed

  • Question

  • User381809404 posted

    Hi all,

    I am developing API using .net core 2.2. I wanted to understand about the Basic Authentication as well as Jwt Authentication.
    My questions are :

    1. Can we use Basic as well as Bearer token based authentication in a single project?
    2. Some API's with basic authentication and few API's with Jwt authentication, Is it possible to have two different controller with different type of authentication in single project?
    3. Can we add two different attributes for different controller? e.g [Authorization] and [BasicAuthentication] on top of controller name?

    Please send me documentation, examples, github code, etc..

    Sorry for my questions (if you find it silly) because i am too much confuse here.

    Thanks

    Monday, July 29, 2019 8:17 PM

Answers

  • User475983607 posted

    You can Google this stuff...

    The link has a Basic Authentication handler.

    https://jasonwatmore.com/post/2018/09/08/aspnet-core-21-basic-authentication-tutorial-with-example-api

    There are tons of JWT examples that a quick Google search will find.  I used this one...

    https://wildermuth.com/2018/04/10/Using-JwtBearer-Authentication-in-an-API-only-ASP-NET-Core-Project

    This one if from the same author as above.

    https://github.com/cornflourblue/aspnet-core-jwt-authentication-api/blob/master/Startup.cs

    You'll add both authentication schemes to your startup.  Then specify the authentication scheme in the [Authorize] attribute.  Keep in mind that you can only have one default scheme.

    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
        public static class BasicAuthenticationDefaults
        {
            public const string AuthenticationScheme = "BasicAuthentication";
        }
        [Authorize(AuthenticationSchemes = BasicAuthenticationDefaults.AuthenticationScheme)]
        public class IndexModel : PageModel

    Anyway, I tested this in a Razor Page where the Razor Page uses Basic Authentication and Web API uses JWT.   You'll need to modify the BasicAuthenticationHandler slightly if you want a browser prompt.  It is import to understand that the browser instance will remember the credentials.

    public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
        {
            private readonly IUserService _userService;
            private bool Challenge { get; set; } = false;
    
    
            public BasicAuthenticationHandler(
                IOptionsMonitor<AuthenticationSchemeOptions> options,
                ILoggerFactory logger,
                UrlEncoder encoder,
                ISystemClock clock,
                IUserService userService)
                : base(options, logger, encoder, clock)
            {
                _userService = userService;
            }
    
            protected override Task HandleChallengeAsync(AuthenticationProperties props)
            {
                if(Challenge)
                {
                    Response.Headers.Add("WWW-Authenticate", "Basic realm=\"localhost\", charset=\"UTF-8\"");
                    Response.StatusCode = 401;
                }
                
                return Task.CompletedTask;
            }
    
            protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
            {
                if (!Request.Headers.ContainsKey("Authorization"))
                {
                    Challenge = true;
                    return AuthenticateResult.Fail("Missing Authorization Header");
                }
                    
    
                User user = null;
                try
                {
                    var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
                    var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
                    var credentials = Encoding.UTF8.GetString(credentialBytes).Split(':');
                    var username = credentials[0];
                    var password = credentials[1];
                    user = await _userService.Authenticate(username, password);
                }
                catch
                {
                    Challenge = true;
                    return AuthenticateResult.Fail("Invalid Authorization Header");
                }
    
                if (user == null)
                {
                    Challenge = true;
                    return AuthenticateResult.Fail("Invalid Username or Password");
                }
                   
    
                var claims = new[] {
                    new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
                    new Claim(ClaimTypes.Name, user.Username),
                };
                var identity = new ClaimsIdentity(claims, Scheme.Name);
                var principal = new ClaimsPrincipal(identity);
                var ticket = new AuthenticationTicket(principal, Scheme.Name);
    
                return AuthenticateResult.Success(ticket);
            }
        }

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, July 31, 2019 3:13 PM

All replies

  • User711641945 posted

    Hi Pritesh,

    Can we use Basic as well as Bearer token based authentication in a single project?

    Yes,you can.You need to configure the authentication in Startup.cs by using services.AddAuthentication().

    Some API's with basic authentication and few API's with Jwt authentication, Is it possible to have two different controller with different type of authentication in single project?

    Yes,it is.And you could select the scheme with the Authorize attribute to specify the controller.

    Can we add two different attributes for different controller? e.g [Authorization] and [BasicAuthentication] on top of controller name?

    You could use one attribute and use '+' to combine different scheme you want to authenticate.

    Reference: https://stackoverflow.com/a/49148749/11398810

    https://docs.microsoft.com/en-us/aspnet/core/security/authorization/limitingidentitybyscheme?view=aspnetcore-2.2&tabs=aspnetcore2x

    Best Regards,

    Rena

    Tuesday, July 30, 2019 7:31 AM
  • User381809404 posted

    Hi Rena,

    Thank you for your reply. I have gone through the Reference link: https://stackoverflow.com/a/49148749/11398810 but didn't able to implement it. It is showing following error :

    'AuthenticationBuilder' does not contain a definition for 'AddBasicAuthentication' and no accessible extension method 'AddBasicAuthentication' accepting a first argument of type 'AddBasicAuthentication' could be found. 

    Is 'AuthenticationBuilder' still available in .net core 2.2 ??

    I will appreciate you can send full working source code.

    Thank you.

    Tuesday, July 30, 2019 7:49 PM
  • User711641945 posted

    Hi Pritesh,

    Is 'AuthenticationBuilder' still available in .net core 2.2 ??

    AuthenticationBuilder is still available in .net core 2.2. But the package Bazinga.AspNetCore.Authentication.Basic which is needed by AddBasicAuthentication support .net core 2.0.

    Best Regards,

    Rena

    Wednesday, July 31, 2019 10:02 AM
  • User381809404 posted

    Dear Rena,

    AuthenticationBuilder is still available in .net core 2.2. But the package Bazinga.AspNetCore.Authentication.Basic which is needed by AddBasicAuthentication support .net core 2.0.

    I did not able to find official document on Microsoft website regarding .net core 2.2 Basic Authentication. I have gone through the documentation available on GitHub (https://github.com/bruno-garcia/Bazinga.AspNetCore.Authentication.Basic) . It says :

    Microsoft doesn't ship a Basic Authentication package with ASP.NET Core Security for a good reason. While that doesn't stop us needing such implementation for testing, this is not advised for production systems due to the many pitfalls and insecurities.

    It means Basic Authentication is completed pulled out from .net core? What do you say? 

    Thanks,

    Pritesh.

    Wednesday, July 31, 2019 1:08 PM
  • User-474980206 posted
    If you google for building a custom provider, it’s almost always a basic provider.
    Wednesday, July 31, 2019 2:03 PM
  • User475983607 posted

    You can Google this stuff...

    The link has a Basic Authentication handler.

    https://jasonwatmore.com/post/2018/09/08/aspnet-core-21-basic-authentication-tutorial-with-example-api

    There are tons of JWT examples that a quick Google search will find.  I used this one...

    https://wildermuth.com/2018/04/10/Using-JwtBearer-Authentication-in-an-API-only-ASP-NET-Core-Project

    This one if from the same author as above.

    https://github.com/cornflourblue/aspnet-core-jwt-authentication-api/blob/master/Startup.cs

    You'll add both authentication schemes to your startup.  Then specify the authentication scheme in the [Authorize] attribute.  Keep in mind that you can only have one default scheme.

    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
        public static class BasicAuthenticationDefaults
        {
            public const string AuthenticationScheme = "BasicAuthentication";
        }
        [Authorize(AuthenticationSchemes = BasicAuthenticationDefaults.AuthenticationScheme)]
        public class IndexModel : PageModel

    Anyway, I tested this in a Razor Page where the Razor Page uses Basic Authentication and Web API uses JWT.   You'll need to modify the BasicAuthenticationHandler slightly if you want a browser prompt.  It is import to understand that the browser instance will remember the credentials.

    public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
        {
            private readonly IUserService _userService;
            private bool Challenge { get; set; } = false;
    
    
            public BasicAuthenticationHandler(
                IOptionsMonitor<AuthenticationSchemeOptions> options,
                ILoggerFactory logger,
                UrlEncoder encoder,
                ISystemClock clock,
                IUserService userService)
                : base(options, logger, encoder, clock)
            {
                _userService = userService;
            }
    
            protected override Task HandleChallengeAsync(AuthenticationProperties props)
            {
                if(Challenge)
                {
                    Response.Headers.Add("WWW-Authenticate", "Basic realm=\"localhost\", charset=\"UTF-8\"");
                    Response.StatusCode = 401;
                }
                
                return Task.CompletedTask;
            }
    
            protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
            {
                if (!Request.Headers.ContainsKey("Authorization"))
                {
                    Challenge = true;
                    return AuthenticateResult.Fail("Missing Authorization Header");
                }
                    
    
                User user = null;
                try
                {
                    var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
                    var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
                    var credentials = Encoding.UTF8.GetString(credentialBytes).Split(':');
                    var username = credentials[0];
                    var password = credentials[1];
                    user = await _userService.Authenticate(username, password);
                }
                catch
                {
                    Challenge = true;
                    return AuthenticateResult.Fail("Invalid Authorization Header");
                }
    
                if (user == null)
                {
                    Challenge = true;
                    return AuthenticateResult.Fail("Invalid Username or Password");
                }
                   
    
                var claims = new[] {
                    new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
                    new Claim(ClaimTypes.Name, user.Username),
                };
                var identity = new ClaimsIdentity(claims, Scheme.Name);
                var principal = new ClaimsPrincipal(identity);
                var ticket = new AuthenticationTicket(principal, Scheme.Name);
    
                return AuthenticateResult.Success(ticket);
            }
        }

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, July 31, 2019 3:13 PM