Question adding Bearer Token to BasicAuthentication?

raysefo

Well-known member
Joined
Feb 22, 2019
Messages
361
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...
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:
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;
 
If you revert back to the version without OAuth, is it also null?
 
I was going to point you to the same thing...
 
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?
 
Did you read the first answer in that link? It looks like you are trying to implement the second answer.
 
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?
 
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].
 
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!
 
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.
 
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.
 
Back
Top Bottom