0

I'm subclassing the AuthorizeAttribute so I can implement token authentication whereby the token is passed in the request header. I'm also using Ninject for IoC. The overriden OnAuthorization method gets called and validates the token but I still receive a 401.

Any ideas on why this is happening?

TokenAuthorisationAttribute.cs

public class TokenAuthorisationAttribute : AuthorizeAttribute
{
    private readonly IRepository _repository;


    public TokenAuthorisationAttribute(IRepository repository)
    {
        _repository = repository;
    }

    public override void OnAuthorization(
        HttpActionContext actionContext)
    {

        if (!ValidateToken(actionContext.ControllerContext.Request))
            HandleUnauthorizedRequest(actionContext);

        base.OnAuthorization(actionContext);
    }

    private bool ValidateToken(HttpRequestMessage request)
    {
        const string authenticationToken = "Authentication-Token";

        var token = request.Headers.GetValues(authenticationToken).FirstOrDefault();

        if (token == null)
            return false;

        var device = _repository.FindSingleOrDefault<Device>(x => x.Id.Equals(token));

        if (device == null || !token.Equals(device.Id))
            return false;

        return true;

    }
}

NinjectWebCommon.cs

 public static class NinjectWebCommon 
{
    private static readonly Bootstrapper Bootstrapper = new Bootstrapper();

    /// <summary>
    /// Starts the application
    /// </summary>
    public static void Start() 
    {
        DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
        DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
        Bootstrapper.Initialize(CreateKernel);
    }

    /// <summary>
    /// Stops the application.
    /// </summary>
    public static void Stop()
    {
        Bootstrapper.ShutDown();
    }

    /// <summary>
    /// Creates the kernel that will manage your application.
    /// </summary>
    /// <returns>The created kernel.</returns>
    private static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
        kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

        RegisterServices(kernel);

        GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
        return kernel;
    }

    /// <summary>
    /// Load your modules or register your services here!
    /// </summary>
    /// <param name="kernel">The kernel.</param>
    private static void RegisterServices(IKernel kernel)
    {
        var connectionString = ConfigurationManager.ConnectionStrings["MONGOHQ_URL"].ConnectionString;
        var databaseName = ConfigurationManager.AppSettings["Database"];

        kernel.Bind<IRepository>().To<MongoRepository>()
            .WithConstructorArgument("connectionString", connectionString)
            .WithConstructorArgument("databaseName", databaseName);

        kernel.BindHttpFilter<TokenAuthorisationAttribute>(FilterScope.Global);

    }        
Click Ahead
  • 2,782
  • 6
  • 35
  • 60

2 Answers2

2

I managed to resolve this issue by overriding the IsAuthorized method instead of OnAuthorization. Not 100% sure if this is the right approach? Any opinions ??

public class TokenAuthorisationAttribute : AuthorizeAttribute
{
    private readonly IRepository _repository;


    public TokenAuthorisationAttribute(IRepository repository)
    {
        _repository = repository;
    }

    protected override bool IsAuthorized(HttpActionContext actionContext)
    {
        if (actionContext.Request.Headers.Authorization == null)
            return false;

        var authToken = actionContext.Request.Headers.Authorization.Parameter;
        var decodedToken = Encoding.UTF8.GetString(Convert.FromBase64String(authToken));

        var deviceToken = new DeviceToken(decodedToken);


        var device = _repository.FindSingleOrDefault<Device>(x => x.Id.Equals(deviceToken.GetDeviceId()));

        if (device != null)
        {
            HttpContext.Current.User = new GenericPrincipal(new ApiIdentity(device), new string[] {});
            return true;
        }

        return base.IsAuthorized(actionContext);
    }
}
Click Ahead
  • 2,782
  • 6
  • 35
  • 60
  • According to http://stackoverflow.com/questions/15148050/custom-authorizations-in-web-api, yes, that is correct. – Colin Young Nov 05 '13 at 20:28
0

The ASP.NET WebAPI is an open source project. So you can read the correspondingly codes here:

There are two facts an AuthorizationFilterAttribute takes in to consider when it is making decision:

  1. Is OnAuthorization throw exception; or
  2. Is the actionContext's Response field is filled;

Either one is fulfilled, the remaining action filters and action are shortcut.

Based on your code, I'm curious if the function HandleUnauthorizedRequest does either of the operation above.

The reason why overriding IsAuthorized works, is because it works at AuthorizeAttribute level. The OnAuthorization call to the IsAuthorized overload and set value to actionContext's Request property.

Thanks, Troy

Troy Dai
  • 2,071
  • 1
  • 13
  • 7