Search code examples
c#asp.net-web-api2tokenasp.net-apicontroller

Request.GetOwinContext().Get<T> return null in APIControler


I Could not retrieve the data from OwinContext Environment that I store it after authenticate by token.

this is the Code:
[ValidateClientAuthentication] In thins Code I validate the ClientID of the User and then store the data of ApplicationClient in OwinContext in this line

public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        ...
        ApplicationClient App = new ApplicationClient();
        App.Id = clientId;
        App.ClientSecretHash = clientSecret;
        // Storing Client Data
        context.OwinContext.Set<ApplicationClient>("oauth:client", App);
        context.Validated(clientId);
    }

[GrantResourceOwnerCredentials] Here I Validate User Credentials and Add Climes to the Token

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
    ...
    var identity = new ClaimsIdentity(context.Options.AuthenticationType);
    if (Membership.ValidateUser(username, password))
    {          
        identity.AddClaim(new Claim(ClaimTypes.Role, "admin"));
        identity.AddClaim(new Claim(ClaimTypes.Name, username));
        context.Validated(identity);
    }
    else
    {
        context.SetError("Login Field", "Error username or password");
    }
}

[ControlerCode] Now Here is My Problem

[Authorize]
public class MyController : ApiController
{
    public IEnumerable<SelectedMenu> GetAllMenus() // Resturants ID
    {
        // client is Null
        ApplicationClient client = HttpContext.Current.GetOwinContext().Get<ApplicationClient>("oauth:client");
    }
}

Solution

  • You should really only add the clientId inside your token, so you may retrieve it anytime after the login process.

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        // ...
        var identity = new ClaimsIdentity(context.Options.AuthenticationType);
        if (Membership.ValidateUser(username, password))
        {          
            identity.AddClaim(new Claim(ClaimTypes.Role, "admin"));
            identity.AddClaim(new Claim(ClaimTypes.Name, username));
            identity.AddClaim(new Claim("oauth-client", context.ClientId));
            context.Validated(identity);
        }
        else
        {
            context.SetError("Login Field", "Error username or password");
        }
    }
    

    You may also create an extension method to help you in retrieving the clientId:

    public static class PrincipalExtensions
    {
        public static string GetClientId(this IPrincipal principal)
        {
            return (principal.Identity as ClaimsIdentity)?
                .Claims
                .FirstOrDefault(c => c.Type == "oauth-client")?
                .Value;
        }    
    }
    

    And inside your controller:

    [Authorize]
    public class MyController : ApiController
    {
        public IEnumerable<SelectedMenu> GetAllMenus() // Resturants ID
        {
            var clientId = User.GetClientId();
        }
    }
    

    Regarding the token size: if your clientId string is too long I suggest you to use another one and store any other client information (including a longer id if is needed) inside the database. The client identifier should be a small and unique string easy to transmit.