Wire IIS7 Basic Authentication to Authenticate against custom provider

The best custom basic authentication out there is at http://custombasicauth.codeplex.com/

But many people have told me that they don’t want to write iis extensions modules etc. So here is the simplest one, just implement ihttpmodlue

1.    Create a class library project called it Company.HttpModules
2.    I add the a class called RevBasicAuthenticationModule
3.    Below is the Code for the class

Imports System.Web
Imports System.Security.Principal
Imports System.Text

Public Class RevBasicAuthenticationModule
Implements IHttpModule

Public Sub Dispose() Implements System.Web.IHttpModule.Dispose

End Sub

Public Sub Init(ByVal context As System.Web.HttpApplication) Implements System.Web.IHttpModule.Init
AddHandler context.AuthenticateRequest, AddressOf OnEnter
End Sub

Private Sub OnEnter(ByVal sender As Object, ByVal e As EventArgs)
Dim application As HttpApplication = DirectCast(sender, HttpApplication)
If Not Authenticate(application.Context) Then
application.Context.Response.Status = “401 Unauthorized”
application.Context.Response.StatusCode = 401
application.Context.Response.AddHeader(“WWW-Authenticate”, “Basic realm=My Company”)
application.CompleteRequest()
End If
End Sub

Public Shared Function Authenticate(ByVal context As HttpContext) As Boolean
‘Enable this if you are using ssl. Which you should
‘If Not HttpContext.Current.Request.IsSecureConnection Then
‘    Return False
‘End If

If Not HttpContext.Current.Request.Headers.AllKeys.Contains(“Authorization”) Then
Return False
End If

Dim authorizationHeader As String = HttpContext.Current.Request.Headers(“Authorization”)

Dim principal As IPrincipal = Nothing
If GetPrincipal(authorizationHeader, principal) Then
HttpContext.Current.User = principal
Return True
End If
Return False
End Function

Private Shared Function GetPrincipal(ByVal authorizationHeader As String, ByVal principal As IPrincipal) As Boolean
Dim credentials = GetAuthorizationHeader(authorizationHeader)
If credentials IsNot Nothing AndAlso ValidateUser(credentials, principal) Then
Return True
End If

principal = Nothing
Return False
End Function

Private Shared Function GetAuthorizationHeader(ByVal authorizationHeaderInfo As String) As String()
‘Get Header info.
If authorizationHeaderInfo Is Nothing OrElse authorizationHeaderInfo.Length = 0 OrElse Not authorizationHeaderInfo.StartsWith(“Basic”) Then
Return Nothing
End If

‘ The Credentials are seperated by ‘:’ and are Base64 encoded
Dim EncodedCredentials As String = authorizationHeaderInfo.Substring(6)
Dim credentials As String() = Encoding.ASCII.GetString(Convert.FromBase64String(EncodedCredentials)).Split(New Char() {“:”c})

If credentials.Length <> 2 OrElse String.IsNullOrEmpty(credentials(0)) OrElse String.IsNullOrEmpty(credentials(0)) Then
Return Nothing
End If

Return credentials
End Function

Private Shared Function ValidateUser(ByVal creds As String(), ByVal principal As IPrincipal) As Boolean
If creds(0) = “Rev” AndAlso creds(1) = “Rev” Then
principal = New GenericPrincipal(New GenericIdentity(“Rev”), New String() {“Administrator”, “User”})
Return True
ElseIf creds(0) = “Ram” AndAlso creds(1) = “Ram” Then
principal = New GenericPrincipal(New GenericIdentity(“Ram”), New String() {“User”})
Return True
Else
principal = Nothing
Return False
End If
End Function

End Class

4.    Added a empty asp.net website to the solution

5.    Add reference to the Company.httpModules

6.    Edit web.config

<system.webServer>
<modules>
<add name=”Company.httpModules” type=”Company.httpModules.RevBasicAuthenticationModule”/>
</modules>
<security>
<authorization>
<add accessType=”Allow” users=”” roles=””/>
</authorization>
</security>-
</system.webServer>

7.    Important make sure Anonymous Authentication is enabled and the rest are disabled for that application

image

8.    That is it you have a successful custom basic authentication. You can implement your own validate user how you want.
9.    But remember the authentication will take place for every request. So you have to use a mechanism to create a token or  cache credentials
10.    In the Custom Basic Authentication code on codeplex created by Dominick Baier has a cache mechanism which is good and can be used here

Custom Basic Authentication for IIS7

Installing and Using Custom Basic Authentication for IIS7.
1.    Download the code from http://custombasicauth.codeplex.com/.
2.    Open visual studio command prompt by right clicking run as administrator.
3.    Cd to the folder where you unzipped the downloaded source.
4.    In the output folder there is Install command file. Edit this file in notepad.
5.    Remove “rem” from line 16 rem iisschema.exe /install CustomBasicAuthentication_schema.xml.
6.    Remove “rem” from line 20 i.e rem IisRegMgmt CustomBasicAuth LeastPrivilege.CustomBasicAuthentication.Management.CustomBasicAuthenticationModuleProvider LeastPrivilege.CustomBasicAuthentication.Management.dll
7.    Save the file
8.    Run Install.cmd from VS cmd prompt

What happens when you run the install.cmd
1.    The 3 dlls will be added to the GAC
2.    When the command “iisschema.exe /install CustomBasicAuthentication_schema.xml” is executed 2 things happen.
•    CustomBasicAuthentication_schema will be copied to C:\Windows\System32\inetsrv\config\schema folder
•    <section name=”customBasicAuthentication” overrideModeDefault=”Allow” allowDefinition=”Everywhere” /> will be added to
<sectionGroup name=”system.webServer”>
<sectionGroup name=”security”>
<sectionGroup name=”authentication”>
Section in applicationHost.config file
3.    When IisRegMgmt is run it edit administration.config file.
a.    <add name=”CustomBasicAuth” type=”LeastPrivilege.CustomBasicAuthentication.Management.CustomBasicAuthenticationModuleProvider, LeastPrivilege.CustomBasicAuthentication.Management, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f20dc168dfd54966″ />  to the  </moduleProviders>

b.    To    <location path=”.”><modules><add name=”CustomBasicAuth” /></modules>

4.    Open an application in inetmgr. Double click Authentication

image

5.    You should see Custom Basic Authentication

How to use it

1.    Create an empty asp.net website.
2.    Edit web.config, Add
<system.webServer>
<validation validateIntegratedModeConfiguration=”false”/>
<modules>
<remove name=”ScriptModule”/>
<add name=”ScriptModule” preCondition=”managedHandler” type=”System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/>
<add name=”CustomBasicAuthentication” type=”LeastPrivilege.CustomBasicAuthentication.CustomBasicAuthenticationModule, LeastPrivilege.CustomBasicAuthenticationModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=F20DC168DFD54966″/>
</modules>
To System.webserver,modules section
3.    Add
<security>
<authentication>
<customBasicAuthentication enabled=”true” realm=”Your Company Name” providerName=”default” cachingEnabled=”true” cachingDuration=”15″ requireSSL=”false”/>
</authentication>
</security>
</system.webServer>
4.    You will need to hook up your custom provider. To Test I created a class library project. Called Company.Security.Provider. Added a class calledMemberProvider.vb
5.    Add refrence to system.web
6.    In the class Imports System.Web
7.    And Inherits System.Web.Security.MembershipProvider

image

8.    The only method I implement was ValidateUser
Public Overrides Function ValidateUser(ByVal username As String, ByVal password As String) As Boolean
If username = “rev” And password = “rev” Then
Return True
Else
Return False
End If
End Function
9.    Here I intend to hook up to my active directory module
10.    In the web project add reference to this class library.
11.    Edit the web.config file, add the default  provider in system.web section

<system.web>
<membership defaultProvider=”Company.Security.Provider”>
<providers>
<clear/>
<add name=”Company.Security.Provider” type=”Company.Security.Provider.MemberProvider”/>
</providers>
</membership>

12.    That’s it, you have custom basic authentication working

Accessing File Resource on a UNC share from IIS7 running .Net 4.0 win 2008

Today we had a situation where a page was trying to load a string from another file in the UNC share.

The asp.net page looked like this

<%@ Page EnableSessionState=”False”%>

 <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”  “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd“>

 <html xmlns=”http://www.w3.org/1999/xhtml”&gt;

<head runat=”server”>

    <title></title>

</head>

<body>

    <form runat=”server”>

<div>

                 <asp:PlaceHolder runat=”server” Visible=”true”>

                    <%Response.WriteFile(“/Common/breadcrumb.htm”)%>

                </asp:PlaceHolder>

            </div>

    <div>

    My Test Page

    </div><asp:Literal runat=”server”></asp:Literal>

    <asp:Button runat=”server” Text=”test”/>

    </form>

</body>

</html>

Breadcrum.htm file just as a string. When this is executed on IIS7 in Asp.Net v2 works fine. But when run 0n IIS7 and Asp.Net v4 this generates System.UnauthorizedAccessException: Access to the path “\\unshare\common\breadcrumb.htm” is denied

Fix

<%@ Page EnableSessionState=”False”%>

<%@ Import Namespace=”System.IO” %>

<%@ Import Namespace=”System.Security.Permissions” %>

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”&gt;

<script runat=”server”>

    Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs)

        Dim oFp As FileIOPermission = New  _

        FileIOPermission(FileIOPermissionAccess.Read, Server.MapPath(“/Common/breadcrumb.htm”))

        oFp.Assert()

        Dim data As String = File.ReadAllText(Server.MapPath(“/Common/breadcrumb.htm”))

        Literal1.Text = data

    End Sub

</script>

<html xmlns=”http://www.w3.org/1999/xhtml”&gt;

<head runat=”server”>

    <title></title>

</head>

<body>

    <form runat=”server”>

    <div>

    My Test Page

    </div><asp:Literal runat=”server”></asp:Literal>

 <asp:Button ID=”Button1″ runat=”server” Text=”test” OnClick=”Button1_Click”/>

    </form>

</body>

</html>

My application pool was running under Network Services. I could have either changed the identity of the pool to use a user that had access to unc share or I could just programmatically give permission to file.  I used the FileIOPermission to grant access to the file and then read it.

Deploying Asp.Net Code to Production with compilation debug=”true”

This is an extract for my reference from ScottGu article on Don’t run production ASP.NET Applications with debug=”true” enabled

What happens when you deploy web application to production with debug=”true” in the web.config file

1) The compilation of ASP.NET pages takes longer (since some batch optimizations are disabled)

2) Code can execute slower (since some additional debug paths are enabled)

3) Much more memory is used within the application at runtime

4) Scripts and images downloaded from the WebResources.axd handler are not cached

How to avoid accidentally deploys with <compilation debug=”true”/>

Do the below setting in machine.config file:

<configuration>

<system.web>

<deployment retail=”true”/>

</system.web>

</configuration>