2

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

4
  • 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 . Commented Jan 18, 2017 at 20:39
  • 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 class Commented Jan 18, 2017 at 20:41
  • 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. Commented Jan 18, 2017 at 20:42
  • have you tried this HttpContext.Current.Request.LogonUserIdentity.Name ? Commented Jan 18, 2017 at 20:44

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

6 Comments

When I implement the filter on that article and decorate my controller with the [BasicAuthenticationFilter], it first tries to go through the Windows Auth and gets 401, hence the second call passes Negotiate instead of Basic. When it gets to the Basic Authentication Filter, since the Authetication scheme is Negotiate, it returns. Updating the question with what I've implemented so far.
@Jail when debugging is the filter hit at all? If not, do you have a global setting that handles all requests?
It gets called once as part of AreaRegistration.RegisterAllAreas(); and once after first two requests fail to authenticate as I explained in my update. I haven't configured a global handler.
You did say you had Windows auth enabled (in web.config?) so is it locking down the location path to api? If so, try setting your "public" controllers to a different route (other than api).
It actually does not. I don't know whether it is related or not but there is Role Provider configure for the application. It's for authorization rather than authentication, hence don't think that is the issue.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.