Search code examples
authenticationasp.net-web-apioauthcastle-windsorclaims-based-identity

OAuth: ASP.NET Web API User.Identity doesn't load claims set by authentication token provider


I am using OAuth bearer authentication, configured like this in Startup.cs:

        OAuthBearerAuthenticationOptions oAuthBearerOptions = 
            new OAuthBearerAuthenticationOptions
            {
                AccessTokenProvider = new AccessTokenProvider(),
                AuthenticationMode = AuthenticationMode.Active
            };
        app.UseOAuthBearerAuthentication(oAuthBearerOptions);

... where AccessTokenProvider is implemented as:

public class AccessTokenProvider : AuthenticationTokenProvider
{
    public override async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
    {
        // Internal logic to get data needed for building identity...

        // Create claims identity
        ClaimsIdentity identity = new ClaimsIdentity(identityName);
        identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, nameIdentifier));
        // Add other claims

        // Set claims identity
        context.SetTicket(new AuthenticationTicket(identity, new AuthenticationProperties()));
    }
}

If I set a breakpoint at the end of ReceiveAsync, I can verify that the identity is built correctly (has claims) and that SetTicket is reached.

But when I try to access the identity from a Web API controller:

public abstract class BaseStorageController : ApiController
{
    protected IStorageService StorageService;

    protected BaseStorageController(IStorageServiceFactory storageServiceFactory)
    {
        StorageService = storageServiceFactory.CreateStorageService(User.Identity as ClaimsIdentity);
    }

}

... the list of claims on the identity is empty!

What can be causing this?

Side note: I don't know if this is related, but I am using Castle Windsor as an IOC container to inject dependencies into my controllers (in the above case, IStorageServiceFactory). The above seemed to work (claims were not empty) before I added that. However, I'm not using CW to manage anything related to authentication. Here is my CW installer for api controllers:

public class ApiControllerInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Classes.FromThisAssembly().BasedOn<ApiController>().LifestylePerWebRequest());
    }
}

Solution

  • I found the answer. It was not related to dependency injection/inversion of control. I'm not sure how I thought it was working prior to adding that.

    The issue is similar to what is described here (but in my case the solution is different): User (IPrincipal) not avaliable on ApiController's constructor using Web Api 2.1 and Owin

    Basically IPrincipal is not accessible from the constructor of the api controller, which is why there are no claims (the user is not yet authenticated). User.Identity is only accessible from the controller's actions, not the constructor. I changed my base controller implementation to the following to get around this issue:

    public abstract class BaseStorageController : ApiController
    {
        private readonly IStorageServiceFactory _storageServiceFactory;
        private IStorageService _storageService;
    
        protected BaseStorageController(IStorageServiceFactory storageServiceFactory)
        {
            _storageServiceFactory = storageServiceFactory;
        }
    
        protected IStorageService StorageService
        {
            get
            {
                if (_storageService == null)
                {
                    _storageService = _storageServiceFactory.CreateStorageService(User.Identity as ClaimsIdentity);
                }
                return _storageService;
            }
        }
    }
    

    Since StorageService is only accessed from controller actions, User.Identity is authenticated and has claims populated by the time that the StorageService getter gets called.

    Hope this helps someone!