2
votes

I have an ASP.NET Web API 2 application that uses Windows Authentication for all the controllers. I have a need now for some controllers to use Basic Authentication.

I know it is not possible to enable both Anonymous and Windows Authentications, but is it possible to enable Windows Authentication for some controllers and Basic Authentication for some others?

UPDATE: Implemented a filter as shown on the article EdSF shared

Here is what I have so far:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class BasicAuthenticationFilter : AuthorizationFilterAttribute
{
    private bool _active = true;
    private const string ValidUsername = @"Test";
    private const string ValidPassword = @"T3st";

    public BasicAuthenticationFilter()
    {
    }

    public BasicAuthenticationFilter(bool active)
    {
        _active = active;
    }

    public override void OnAuthorization(HttpActionContext actionContext)
    {
        if (_active)
        {
            var identity = ParseAuthorizationHeader(actionContext);
            if (identity == null)
            {
                Challenge(actionContext);
                return;
            }


            if (!OnAuthorizeUser(identity.Name, identity.Password, actionContext))
            {
                Challenge(actionContext);
                return;
            }

            var principal = new GenericPrincipal(identity, null);

            Thread.CurrentPrincipal = principal;

            if (HttpContext.Current != null)
            {
                HttpContext.Current.User = principal;
            }

            base.OnAuthorization(actionContext);
        }
    }

    protected virtual bool OnAuthorizeUser(string username, string password, HttpActionContext actionContext)
    {
        if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password) ||
            !username.Equals(ValidUsername) || !password.Equals(ValidPassword))
        {
            return false;
        }

        return true;
    }

    protected virtual BasicAuthenticationIdentity ParseAuthorizationHeader(HttpActionContext actionContext)
    {
        string authHeader = null;
        var auth = actionContext.Request.Headers.Authorization;
        if (auth != null && auth.Scheme == "Basic")
        {
            authHeader = auth.Parameter;
        }

        if (string.IsNullOrEmpty(authHeader)) return null;

        authHeader = Encoding.Default.GetString(Convert.FromBase64String(authHeader));

        var tokens = authHeader.Split(':');
        if (tokens.Length < 2) return null;

        return new BasicAuthenticationIdentity(tokens[0], tokens[1]);
    }

    private void Challenge(HttpActionContext actionContext)
    {
        var host = actionContext.Request.RequestUri.DnsSafeHost;
        actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
        actionContext.Response.Headers.Add("WWW-Authenticate", string.Format("Basic realm=\"{0}\"", host));
    }
}

BasicAuthenticationIdentity Class:

public class BasicAuthenticationIdentity : GenericIdentity
{
    public BasicAuthenticationIdentity(string name, string password)
        : base(name, "Basic")
    {
        Password = password;
    }

    public string Password { get; set; }
}

Also, decorated my controller with the Basic Authentication Filter:

[BasicAuthenticationFilter]
[RoutePrefix("api/BasicAuth")]
public class BasicAuthController : ApiController
{
    //[BasicAuthenticationFilter]
    [HttpGet]
    public IHttpActionResult TestBasicAuth()
    {
        return Ok("success");
    }
}

When I make a call to api/BasicAuth from Fiddler I got back 401, but it only returns the following challenges:

WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM

At this point Fiddler tries it again but this time instead of passing Basic Authorization Scheme, it passes Negotiate. This one fails also.

Then, when Fiddler finally tries the third time, my filter actually gets the request, but since authorization scheme is Negotiate instead of Basic, my filter returns Unauthorized also.

Is there a way to force the controller to just use the BasicAuthenticationFilter?

Thanks in advance

1
HttpContext.Current.User will return you the Identity of the calling user when impersisation i set to true . on the controller you can get this information and check weather use is valid win user .Yashveer Singh
Or a base controler like WinAuthContrler which will do this for you and then you can inherit this WinAuthContrler on other mvc controler . Your WinAuthControler should inhert from Controler classYashveer Singh
Yes, if the user is using Windows, that object gets set, but my problem is users will be calling from a Linux box using basic auth. I need to be able to capture the username and password from that call.Jail
have you tried this HttpContext.Current.Request.LogonUserIdentity.Name ?Yashveer Singh

1 Answers

1
votes

You can - because BASIC AUTH credentials are sent in HTTP Headers (base64 encoded only). You don't have to "enable" anything at the application level and handle HTTP requests to your API endpoints "manually" (by inspecting headers).

e.g. Basic Auth Header: Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=

See this example that creates an AuthorizationFilter that can be applied to Controller or Action, or even globally if needed...

Hth.