Web API 2 OAuth Bearer Tokens with Custom Authentication

10,750

I worked in a similar scenario and had implemented an authentication filter (IAuthenticationFilter) and a customized class inherited from OAuthAuthorizationServerProvider. In my case, I needed to authenticate the request with OAuth and a legacy token. I believe that in your case, you will need customize the AuthenticationFilter. See below an example of the AuthenticationFilter:

public class MyAuthenticationFilter : IAuthenticationFilter
{
    private readonly string _authenticationType;

    /// <summary>Initializes a new instance of the <see cref="HostAuthenticationFilter"/> class.</summary>
    /// <param name="authenticationType">The authentication type of the OWIN middleware to use.</param>
    public MyAuthenticationFilter(string authenticationType)
    {
        if (authenticationType == null)
        {
            throw new ArgumentNullException("authenticationType");
        }

        _authenticationType = authenticationType;
    }

    /// <summary>Gets the authentication type of the OWIN middleware to use.</summary>
    public string AuthenticationType
    {
        get { return _authenticationType; }
    }

    /// <inheritdoc />
    public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }

        HttpRequestMessage request = context.Request;

        if (request == null)
        {
            throw new InvalidOperationException("Request mut not be null");
        }


        //In my case, i need try autenticate the request with BEARER token (Oauth)
        IAuthenticationManager authenticationManager = GetAuthenticationManagerOrThrow(request);

        cancellationToken.ThrowIfCancellationRequested();
        AuthenticateResult result = await authenticationManager.AuthenticateAsync(_authenticationType);
        ClaimsIdentity identity = null;

        if (result != null)
        {
            identity = result.Identity;

            if (identity != null)
            {
                context.Principal = new ClaimsPrincipal(identity);
            }
        }
        else
        {
            //If havent success with oauth authentication, I need locate the legacy token
//If dont exists the legacy token, set error (will generate http 401)
            if (!request.Headers.Contains("legacy-token-header"))
                context.ErrorResult = new AuthenticationFailureResult(Resources.SAUTH_ERROR_LEGACYTOKENNOTFOUND, request);
            else
            {
                try
                {
                    var queryString = request.GetQueryNameValuePairs();
                    if (!queryString.Any(x => x.Key == "l"))
                        context.ErrorResult = new AuthenticationFailureResult(Resources.SAUTH_ERROR_USERTYPENOTFOUND, request);
                    else
                    {
                        var userType = queryString.First(x => x.Key == "l").Value;
                        String token = HttpUtility.UrlDecode(request.Headers.GetValues("tk").First());

                        identity = TokenLegacy.ValidateToken(token, userType);
                        identity.AddClaims(userType, (OwinRequest) ((OwinContext)context.Request.Properties["MS_OwinContext"]).Request);
                        if (identity != null)
                        {
                            context.Principal = new ClaimsPrincipal(identity);
                        }
                    }

                }
                catch (Exception e)
                {
                    context.ErrorResult = new AuthenticationFailureResult(e.Message, request);
                }
            }
        }
    }


    /// <inheritdoc />
    public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }

        HttpRequestMessage request = context.Request;

        if (request == null)
        {
            throw new InvalidOperationException("Request mut not be null");
        }

        IAuthenticationManager authenticationManager = GetAuthenticationManagerOrThrow(request);

        // Control the challenges that OWIN middleware adds later.
        authenticationManager.AuthenticationResponseChallenge = AddChallengeAuthenticationType(
            authenticationManager.AuthenticationResponseChallenge, _authenticationType);

        return TaskHelpers.Completed();
    }

    /// <inheritdoc />
    public bool AllowMultiple
    {
        get { return true; }
    }

    private static AuthenticationResponseChallenge AddChallengeAuthenticationType(
        AuthenticationResponseChallenge challenge, string authenticationType)
    {
        Contract.Assert(authenticationType != null);

        List<string> authenticationTypes = new List<string>();
        AuthenticationProperties properties;

        if (challenge != null)
        {
            string[] currentAuthenticationTypes = challenge.AuthenticationTypes;

            if (currentAuthenticationTypes != null)
            {
                authenticationTypes.AddRange(currentAuthenticationTypes);
            }

            properties = challenge.Properties;
        }
        else
        {
            properties = new AuthenticationProperties();
        }

        authenticationTypes.Add(authenticationType);

        return new AuthenticationResponseChallenge(authenticationTypes.ToArray(), properties);
    }

    private static IAuthenticationManager GetAuthenticationManagerOrThrow(HttpRequestMessage request)
    {
        Contract.Assert(request != null);

        var owinCtx = request.GetOwinContext();
        IAuthenticationManager authenticationManager = owinCtx != null ? owinCtx.Authentication : null;

        if (authenticationManager == null)
        {
            throw new InvalidOperationException("IAuthenticationManagerNotAvailable");
        }

        return authenticationManager;
    }
}

In WebApiConfig.cs, you need the add the authentication filter like this:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services
        // Configure Web API to use only bearer token authentication.
        config.SuppressDefaultHostAuthentication();

        config.Filters.Add(new MyAuthenticationFilter(OAuthDefaults.AuthenticationType));
     }
}

I recommend reading the official WEB API poster:

https://www.asp.net/media/4071077/aspnet-web-api-poster.pdf

Share:
10,750
puri
Author by

puri

Updated on June 27, 2022

Comments

  • puri
    puri almost 2 years

    I have the following requirements for my C# Web API 2 Service:

    The service authenticates Users by the combination of Email and a temporary Passcode that gets sent to their inbox, as a factor of authentication. I need to blend in this authentication mechanism with producing OAuth bearer tokens to secure the service and use standard ASP.NET Authorisation mechanism to check each request against the token via some kind of [Authorize] attribute.

    I have successfully implemented these steps

    1. User requests Passcode
    2. System generates and emails Passcode to User with 30 days expiration
    3. User authenticates with Email + Passcode
    4. System checks validity of Passcode

    But I am not sure how to begin implementing the remaining steps

    1. If Passcode valid, system generates OAuth bearer token
    2. OAuth bearer token lasts as long as Passcode expiration date
    3. Use ASP.NET Identity authorization attributes to perform authentication and authorisation checks
    4. Use OWIN Security and OAuth Middleware to create token
    5. Use claims based authorisation and serialise claims into token

    The cited process only describes using ASP.NET Identity Individual User accounts as a means to authenticate which is not how I want to authenticate.

    http://www.asp.net/web-api/overview/security/individual-accounts-in-web-api

    I actually need to authenticate by checking Email and Passcode.