Question adding Bearer Token to BasicAuthentication?

raysefo

Well-known member
Joined
Feb 22, 2019
Messages
287
Programming Experience
10+
Hi,

I have an ASP.Net Web API 2 with BasicAuthenticationAttribute that is working as expected. In my application, there are different controllers and I want to add bearer token-based authentication to one of my controllers. I added those NuGet packages:

NuGet:
Microsoft.AspNet.WebApi.Owin
Microsoft.Owin.Host.SystemWeb
Microsoft.Owin.Security.OAuth

There is no problem with the web API config.

WebApiConfig:
public static class WebApiConfig
    {
       public static void Register(HttpConfiguration config)
       {
                    
           config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
        config.Formatters.JsonFormatter.SerializerSettings.DateTimeZoneHandling =
        DateTimeZoneHandling.Local;
                
        config.MapHttpAttributeRoutes();
      
        config.Routes.MapHttpRoute(
            "DefaultApi",
            "api/{controller}/{id}",
            new {id = RouteParameter.Optional}
        );
        
                          
       config.MessageHandlers.Add(new RequestResponseHandler());
                    
       config.Filters.Add(new CustomExceptionFilter());
        
       var resolver = config.DependencyResolver;
       var basicAuth = resolver.GetService(typeof(BasicAuthenticationAttribute)) as BasicAuthenticationAttribute;
       config.Filters.Add(basicAuth);
   }
}

Here is Owin startup
Owin Startup:
public class Startup
     {
         public void Configuration(IAppBuilder app)
         {
             var configuration = new HttpConfiguration();
             Configure(app);
    
             WebApiConfig.Register(configuration);
             app.UseWebApi(configuration);
         }
    
         private static void Configure(IAppBuilder app)
         {
             var options = new OAuthAuthorizationServerOptions()
             {
                 TokenEndpointPath = new Microsoft.Owin.PathString("/token"),
                 AccessTokenExpireTimeSpan = TimeSpan.FromHours(1),
                 AllowInsecureHttp = true,
                 Provider = new AuthorizationServerProvider()
             };
    
             app.UseOAuthAuthorizationServer(options);
             app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
         }
     }

In the configuration of Owin startup, the program flow goes back to the web API config file.
C#:
WebApiConfig.Register(configuration);

And finally, it gets a null exception for basic authentication.
C#:
config.Filters.Add(basicAuth);

How can I solve this and use both basic authentication attribute and token-based authentication in my web API?

Best Regards.
 
Solution
After long googling, here is how I managed to use both basic authentication and bearer authentication for my different controllers.


In Custom basic authentication Attribute I used dependency and requestScope.GetService.
Custom Basic Authentication:
public class CustomBasicAuthenticationAttribute : AuthorizationFilterAttribute
    {
        [Dependency] public static IUserValidate authentication { get; set; }

        private const string Realm = "My Realm";
        

        public override void OnAuthorization(HttpActionContext actionContext)
        {
            var requestScope = actionContext.Request.GetDependencyScope();

            //If the Authorization header is empty or null
            //then return Unauthorized
            if...

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,712
Location
Chesapeake, VA
Programming Experience
10+
Are you getting the exception on line 25 of your WebApiConfig.cs? If so that means that the resolver on line 24 could not find the
BasicAuthenticationAttribute.

I am guessing that the call to app.UseOAuthBearerAuthentication() maybe removing the registration of the basic auth from your resolver. That or you accidentally over wrote the code that sets up basic auth with your new code that sets up Oauth.
 
Last edited:

raysefo

Well-known member
Joined
Feb 22, 2019
Messages
287
Programming Experience
10+
At first, when the web API config starts, Unity Dependency Resolver is set. But after owin startup, it is an Empty Resolver.

WebApiConfig:
var resolver = config.DependencyResolver;
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,712
Location
Chesapeake, VA
Programming Experience
10+
If you revert back to the version without OAuth, is it also null?
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,712
Location
Chesapeake, VA
Programming Experience
10+
I was going to point you to the same thing...
 

raysefo

Well-known member
Joined
Feb 22, 2019
Messages
287
Programming Experience
10+
I changed Owin startup.cs like this but this time I am not getting 401 Authorization has been denied for this request.

Owin Startup:
public void Configuration(IAppBuilder app)
        {
            var configuration = GlobalConfiguration.Configuration;
            
            //WebApiConfig.Register(configuration);
            app.UseWebApi(configuration);

            Configure(app);
        }

What I am missing?
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,712
Location
Chesapeake, VA
Programming Experience
10+
Did you read the first answer in that link? It looks like you are trying to implement the second answer.
 

raysefo

Well-known member
Joined
Feb 22, 2019
Messages
287
Programming Experience
10+
I don't understand his logic. He seems using ProviderService but there is no clue what is inside the class. So I need some guidance. Is there any tutorial about this specific issue?
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,712
Location
Chesapeake, VA
Programming Experience
10+
I'm sorry, I don't know enough about the plumbing that goes into the ASP.NET authentication/authorization pipeline. I believe that it is documented, but I just never had to dig into it. Just speculating: Perhaps order matters on whether you setup webapi first or oath first?

Don't get hung up on his
C#:
ProviderService[/icode] and [icode]IProviderService[/icode]. I think it's his private code to help drive the code behind his [icode]ServiceController[/icode].
 

raysefo

Well-known member
Joined
Feb 22, 2019
Messages
287
Programming Experience
10+
I am searching and trying to find how to use both basic authentication and token-based authentication for different controllers. Here is my status update.

There is no code in the Global.asax

Owin Startup:
public class Startup

    {

        public void Configuration(IAppBuilder app)

        {


            var config = GlobalConfiguration.Configuration;

            WebApiConfig.Register(config);

            app.UseWebApi(config);

            Configure(app, config.DependencyResolver);           

            config.EnsureInitialized();

        }


        private static void Configure(IAppBuilder app, IDependencyResolver resolver)

        {

            var options = new OAuthAuthorizationServerOptions()

            {


                TokenEndpointPath = new Microsoft.Owin.PathString("/token"),

                AccessTokenExpireTimeSpan = TimeSpan.FromHours(1),

                AllowInsecureHttp = true,

                Provider = new AuthorizationServerProvider((IUserValidate)resolver.GetService(typeof(IUserValidate)))

                

            };


            

            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

            app.UseOAuthAuthorizationServer(options);


        }

    }

AuthorizationServerProvider:
public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
    {
        private readonly IUserValidate _userValidate;
        public AuthorizationServerProvider(IUserValidate userValidate)
        {
            _userValidate = userValidate;
        }
        public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            if (!context.TryGetBasicCredentials(out var clientId, out var clientSecret))
            {
                context.SetError("Error", "Error...");
            }

            if (_userValidate.Login(clientId, clientSecret))
            {
                context.Validated();
            }
        }

        
        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {

            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Headers", new[] { "Content-Type" });


            if (_userValidate.Login(context.UserName, context.Password))
            {
                
                var identity = new ClaimsIdentity(context.Options.AuthenticationType);
                identity.AddClaim(new Claim("sub", context.UserName));
                identity.AddClaim(new Claim("role", "admin"));

                context.Validated(identity);
            }
            else
            {
                
                context.SetError("Error", "Error...");
            }
        }
    }

The rest is the same as the previous code samples.


When I call ...api/v2/game/abc101/purchase I am getting 401, it is progress. But when I call http://localhost:52908/token I am getting unsupported_grant_type. I am sending requests via Postman and I am sending a POST requests with content-type x-www-form-urlencoded. Grant-Type is password and username/password is also correct. When I call another controller http://localhost:52908/api/v2/game/purchase basic authentication does NOT work!
 

raysefo

Well-known member
Joined
Feb 22, 2019
Messages
287
Programming Experience
10+
Now I am getting the token, one step at a time :) How can I also use Basic authentication for another controller?

Owin Startup:
public class Startup
    {
        public void Configuration(IAppBuilder app)
        {

            var config = GlobalConfiguration.Configuration;
            Configure(app, config.DependencyResolver);
            WebApiConfig.Register(config);
            app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
            app.UseWebApi(config);
            config.EnsureInitialized();

        }

        private static void Configure(IAppBuilder app, IDependencyResolver resolver)
        {
            var options = new OAuthAuthorizationServerOptions()
            {
                AllowInsecureHttp = true,
                TokenEndpointPath = new Microsoft.Owin.PathString("/token"),
                AccessTokenExpireTimeSpan = TimeSpan.FromHours(1),
                Provider = new AuthorizationServerProvider((IUserValidate)resolver.GetService(typeof(IUserValidate)))
                
            };

            app.UseOAuthAuthorizationServer(options);
            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

        }
    }

AuthorizationServerProvider:
public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
    {
        private readonly IUserValidate _userValidate;
        public AuthorizationServerProvider(IUserValidate userValidate)
        {
            _userValidate = userValidate;
        }
        public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            if (!context.TryGetBasicCredentials(out var clientId, out var clientSecret))
            {
                context.SetError("invalid_grant", "The user name or password is incorrect.");
            }

            if (_userValidate.Login(clientId, clientSecret))
            {
                context.Validated();
            }
        }

        
        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {

            
            if (_userValidate.Login(context.UserName, context.Password))
            {
                
                var identity = new ClaimsIdentity(context.Options.AuthenticationType);
                identity.AddClaim(new Claim("sub", context.UserName));
                identity.AddClaim(new Claim("role", "admin"));

                context.Validated(identity);
            }
            else
            {
                
                context.SetError("invalid_grant", "The user name or password is incorrect.");
            }
        }
    }

As I mentioned before, I have Basic Authentication Attribute and somehow I have to use it in my other controller.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,712
Location
Chesapeake, VA
Programming Experience
10+
Yay! Progress!

Sorry for all the hand waving, but as I understand ASP.NET AuthN/AuthZ, they use a chain of responsibility. If one provider says it doesn't know how to handle something, it gets passed in down the chain. I wish I could tell you more to guide you.
 

raysefo

Well-known member
Joined
Feb 22, 2019
Messages
287
Programming Experience
10+
How can I use OverrideAuthenticationand my basic authentication attribute?

Basic Authentication Attribute:
public class BasicAuthenticationAttribute : AuthorizationFilterAttribute
    {
        private const string Realm = "My Realm";
        private readonly Func<IUserValidate> _factory;

        public BasicAuthenticationAttribute(Func<IUserValidate> factory)
        {
            _factory = factory;
        }
        ...
 
Last edited by a moderator:

raysefo

Well-known member
Joined
Feb 22, 2019
Messages
287
Programming Experience
10+
After long googling, here is how I managed to use both basic authentication and bearer authentication for my different controllers.


In Custom basic authentication Attribute I used dependency and requestScope.GetService.
Custom Basic Authentication:
public class CustomBasicAuthenticationAttribute : AuthorizationFilterAttribute
    {
        [Dependency] public static IUserValidate authentication { get; set; }

        private const string Realm = "My Realm";
        

        public override void OnAuthorization(HttpActionContext actionContext)
        {
            var requestScope = actionContext.Request.GetDependencyScope();

            //If the Authorization header is empty or null
            //then return Unauthorized
            if (actionContext.Request.Headers.Authorization == null)
            {
                actionContext.Response = actionContext.Request
                    .CreateResponse(HttpStatusCode.Unauthorized);
                // If the request was unauthorized, add the WWW-Authenticate header
                // to the response which indicates that it require basic authentication
                if (actionContext.Response.StatusCode == HttpStatusCode.Unauthorized)
                    actionContext.Response.Headers.Add("WWW-Authenticate",
                        $"Basic realm=\"{Realm}\"");
            }
            else
            {
                //Get the authentication token from the request header
                var authenticationToken = actionContext.Request.Headers
                    .Authorization.Parameter;
                try
                {
                    //Decode the string
                    var decodedAuthenticationToken = Encoding.UTF8.GetString(
                        Convert.FromBase64String(authenticationToken));
                    //Convert the string into an string array
                    var usernamePasswordArray = decodedAuthenticationToken.Split(':');
                    //First element of the array is the username
                    var username = usernamePasswordArray[0];
                    //Second element of the array is the password
                    var password = usernamePasswordArray[1];

                    authentication = requestScope.GetService(typeof(IUserValidate)) as IUserValidate;

                    if (authentication != null && authentication.Login(username, password))
                    {
                        var identity = new GenericIdentity(username);
                        IPrincipal principal = new GenericPrincipal(identity, null);
                        Thread.CurrentPrincipal = principal;
                        if (HttpContext.Current != null) HttpContext.Current.User = principal;
                    }
                    else
                    {
                        actionContext.Response = actionContext.Request
                            .CreateResponse(HttpStatusCode.Unauthorized);
                    }
                }
                catch
                {
                    actionContext.Response = actionContext.Request
                        .CreateResponse(HttpStatusCode.Unauthorized);
                }
            }
        }
    }

In one of my controller, I added those attributes
Controller:
[OverrideAuthentication]
        [CustomBasicAuthentication]

        [HttpPost, Route("purchase")]
        public async Task<IHttpActionResult> PurchaseGame(RequestDto game)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
...
Now I can use bearer token authentication for ...api/v2/game/abc101/purchase and basic authentication for ...api/v2/game/purchase.

Hope this solution helps for others.
 
Solution

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,712
Location
Chesapeake, VA
Programming Experience
10+
How important is that OverrideAuthentication attribute to the overall solution? Is the CustomBasicAuthentication ignored without it?
 

raysefo

Well-known member
Joined
Feb 22, 2019
Messages
287
Programming Experience
10+
The vital part is the dependency and actionContext.Request.GetDependencyScope();. Without OverrideAuthentication it is working as expected.
 
Top Bottom