0

I have written WCF Data Service, which runs perfectly well without authentication. Then I wanted to add some authentication code by adding HttpModule in application like below:

public class BasicIdentity : IIdentity
{
    string _name;

    public BasicIdentity(string name)
    {
        this._name = name;
    }

    string IIdentity.AuthenticationType
    {
        get { return "Custom SCHEME"; }
    }

    bool IIdentity.IsAuthenticated
    {
        get { return true; }
    }

    string IIdentity.Name
    {
        get { return _name; }
    }
}

public class BasicPrincipal : IPrincipal
{
    string[] _roles;
    IIdentity _identity;

    public BasicPrincipal(string name, params string[] roles)
    {
        this._roles = roles;
        this._identity = new CustomIdentity(name);
    }

    public IIdentity Identity
    {
        get { return _identity; }
    }

    public bool IsInRole(string role)
    {
        return _roles.Contains(role);
    }
}

public class BasicAuthenticationProvider
{
    public static bool Authenticate(HttpContext context)
    {
        //if (!HttpContext.Current.Request.IsSecureConnection)
        //    return false;

        if (!HttpContext.Current.Request.Headers.AllKeys.Contains("Authorization"))
            return false;

        string authHeader = HttpContext.Current.Request.Headers["Authorization"];

        IPrincipal principal;
        if (TryGetPrincipal(authHeader, out principal))
        {
            HttpContext.Current.User = principal;
            return true;
        }
        return false; 
    }

    private static bool TryGetPrincipal(string authHeader, out IPrincipal principal)
    {
        // 
        // WARNING: 
        // our naive – easily mislead authentication scheme 
        // blindly trusts the caller. 
        // a header that looks like this: 
        // ADMIN username 
        // will result in someone being authenticated as an 
        // administrator with an identity of ‘username’ 
        // i.e. not exactly secure!!! 
        // 
        //var protocolParts = authHeader.Split(' ');
        //if (protocolParts.Length != 2)
        //{
        //    principal = null;
        //    return false;
        //}
        //else if (protocolParts[0] == "ADMIN")
        //{
        //    principal = new CustomPrincipal(
        //       protocolParts[1],
        //       "Administrator", "User"
        //    );
        //    return true;
        //}
        //else if (protocolParts[0] == "USER")
        //{
        //    principal = new CustomPrincipal(
        //       protocolParts[1],
        //       "User"
        //    );
        //    return true;
        //}
        //else
        //{
        //    principal = null;
        //    return false;
        //}
        var creds = ParseAuthHeader(authHeader);
        if (creds != null && TryGetPrincipal(creds, out principal))
            return true;

        principal = null;
        return false; 
    }

    private static string[] ParseAuthHeader(string authHeader)
    {
        // Check this is a Basic Auth header 
        if (
            authHeader == null ||
            authHeader.Length == 0 ||
            !authHeader.StartsWith("Basic")
        ) return null;

        // Pull out the Credentials with are seperated by ':' and Base64 encoded 
        string base64Credentials = authHeader.Substring(6);
        string[] credentials = Encoding.ASCII.GetString(
              Convert.FromBase64String(base64Credentials)
        ).Split(new char[] { ':' });

        if (credentials.Length != 2 ||
            string.IsNullOrEmpty(credentials[0]) ||
            string.IsNullOrEmpty(credentials[0])
        ) return null;

        // Okay this is the credentials 
        return credentials;
    }

    private static bool TryGetPrincipal(string[] creds, out IPrincipal principal)
    {
        if (creds[0] == "Administrator" && creds[1] == "SecurePassword")
        {
            principal = new GenericPrincipal(
               new GenericIdentity("Administrator"),
               new string[] { "Administrator", "User" }
            );
            return true;
        }
        else if (creds[0] == "JoeBlogs" && creds[1] == "Password")
        {
            principal = new GenericPrincipal(
               new GenericIdentity("JoeBlogs"),
               new string[] { "User" }
            );
            return true;
        }
        else
        {
            principal = null;
            return false;
        }
    }
}

public class BasicAuthenticationModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        context.AuthenticateRequest
           += new EventHandler(context_AuthenticateRequest);
    }
    void context_AuthenticateRequest(object sender, EventArgs e)
    {
        HttpApplication application = (HttpApplication)sender;
        if (!BasicAuthenticationProvider.Authenticate(application.Context))
        {
            application.Context.Response.Status = "401 Unauthorized";
            application.Context.Response.StatusCode = 401;
            application.Context.Response.AddHeader("WWW-Authenticate", "Basic");
            application.CompleteRequest();
        }
    }
    public void Dispose() { }
}

Here the BasicAuthenticationModule is registed in web.config as

 <system.web>
    <compilation debug="true" targetFramework="4.0">
      <assemblies>
        <add assembly="System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
      </assemblies>
    </compilation>
    <httpModules>
      <add name="BasicAuthenticationModule"
     type="ODataEmptyWebApp.BasicAuthenticationModule"/>
    </httpModules>
 </system.web>

Now when I run the WCF Data Service in browser it asks me authentication window. When I enter invalid credentials it keeps on asking for correct credentials, when I provide correct credentials i.e. Administrator/SecuredPassword it closes the window and empty page is shown. The page's source code is as follows:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META http-equiv="Content-Type" 
content="text/html; charset=windows-1252"></HEAD>
<BODY></BODY></HTML>

When I debugged code I found It does not call my Initialize method of WCF Data Service which used to get called before I added the HttpModule.

I not getting why the control is not passed to Initialize method. Do I have to explicitely add something to get it run??

MaxRecursion
  • 4,773
  • 12
  • 42
  • 76
  • I am aware I am doing something wrong here, the control should definitely pass through but could not locate the line where control is leaking :( – MaxRecursion Dec 02 '12 at 13:02
  • After looking at the code, i can suggest to use implict authentication modes of WCF . Http module is not a part of the service here and will not run as a single operation. – CodeSpread Dec 02 '12 at 14:36

0 Answers0