Set up SMS for Two-factor authentication with Twilio

Adding two-factor authentication (2FA) to your web application increases the security of your user’s data. Multi-factor authentication determines the identity of a user in two steps:

  • First we validate the user with an email and password
  • Second we validate the user using his or her mobile device, by sending a one-time verification code

Once our user enters the verification code, we know they have received the SMS, and indeed are who they say they are. This is a standard SMS implementation. Continue reading “Set up SMS for Two-factor authentication with Twilio”

Advertisements

How to fix 4625: An account failed to log on on Windows Azure VMs

You have probably seen the error below while scanning your Event Viewer in Windows Azure VMs:

An account failed to log on. Continue reading “How to fix 4625: An account failed to log on on Windows Azure VMs”

Single Sign-On Implementation in VB.NET for ZenDesk

If you would like your users to get automatically authenticated with ZenDesk, why not create a single sign-on method using VB.NET (ASP.NET) and the JWT – web authentication method?

zendesk-sso-1

Here are the steps of the single sign-on authentication process:

  1. An unauthenticated user (not already logged in) navigates to your Zendesk URL (for example, https://mycompany.zendesk.com/).
  2. The Zendesk SSO mechanism recognizes that SSO is enabled and that the user is not authenticated.
  3. The user is redirected to the remote login URL configured for the SSO settings (for example, https://mycompany.com/zendesk/sso).
  4. A script on your side authenticates the user using your proprietary login process.
  5. Your script builds a JWT request that contains the relevant user data.
  6. You redirect the customer to the Zendesk endpoint at https://mycompany.zendesk.com/access/jwt with the JWT payload.
  7. Zendesk parses the user detail from the JWT payload and then grants the user a session.

https://support.zendesk.com/hc/en-us/articles/203663816-Setting-up-single-sign-on-with-JWT-JSON-Web-Token-

First, create an ASHX handler which will handle all sign-in request. I have named mine SSO.ASHX. The link to direct your users to Zendesk should be: https://website/SSO.ashx.

Your SSO.ashx class:

<%@ WebHandler Language="VB" Class="SSO" %>

Imports System
Imports System.Web

Public Class SSO : Implements IHttpHandler, System.Web.SessionState.IRequiresSessionState


    Private Const SHARED_KEY As String = "your token"
    Private Const SUBDOMAIN As String = "your zendesk subdomain"

    Public Sub ProcessRequest(ByVal context As  _
            System.Web.HttpContext) Implements _
            System.Web.IHttpHandler.ProcessRequest
        Dim t As TimeSpan = (DateTime.UtcNow - New DateTime(1970, 1, 1))
        Dim timestamp As Double = CDbl(t.TotalSeconds)

        Dim payload As Dictionary(Of String, Object) = New Dictionary(Of String, Object)()

        ''get user name and email based on session id
        Dim d As New DataLayer
        Dim strEmail As String = d.GetUserEmail(context.Session("ClientID"), context.Session("ID"))
        Dim strName As String = d.GetUserFullName(context.Session("ClientID"), context.Session("ID"))
        Dim strClientName As String = d.GetClientName(context.Session("ClientID"))
        payload.Add("iat", timestamp)
        payload.Add("jti", System.Guid.NewGuid().ToString())
        payload.Add("name", strName)
        payload.Add("email", strEmail)
        payload.Add("external_id", context.Session("ID"))
        payload.Add("organization", strClientName)
        payload.Add("role", "user")


        Dim token As String = JWT.JsonWebToken.Encode(payload, SHARED_KEY, JWT.JwtHashAlgorithm.HS256)
        Dim redirectUrl As String = "https://" + SUBDOMAIN + ".zendesk.com/access/jwt?jwt=" + token

        Dim returnTo As String = context.Request.QueryString("return_to")

        If (returnTo IsNot Nothing) Then
            redirectUrl += "&return_to=" + HttpUtility.UrlEncode(returnTo)
        End If

        context.Response.Redirect(redirectUrl)
    End Sub

    Public ReadOnly Property IsReusable() As Boolean _
            Implements System.Web.IHttpHandler.IsReusable
        Get
            Return False
        End Get
    End Property
End Class

Securing Web Services With Username and Password (Custom SoapExtension)

Have you ever wanted to intercept a web service method because you wanted to maybe log it or even as we are about to do authenticate a user?  Did you know you can intercept any incoming SoapMessage that is sent to your web service?  This is all possible because of the SoapExtension class in .Net. Not only can you intercept the incoming message but you can do it within one of four stages:

  1. AfterDeserialize
  2. AfterSerialize
  3. BeforeDeserialize
  4. BeforeSerialize

This allows us a lot of flexibility obviously.  In our example we are going to work with the AfterDeserialize stage which is after the message has been sent across the wire and serialized into a SoapMessage object.  Since we will have a full blown SoapMessage object, we can inspect the headers of the SoapMessage and take care of authentication then.  Our end goal with taking this approach is to allow us to authenticate a user with a WebMethod simply by adding an authentication attribute to the WebMethod like this highlighted in bold.

    [WebMethod]
[SoapHeader(“CustomSoapHeader”)]
[AuthenticatonSoapExtensionAttribute] (magic here)

public int AddTwoNumbers(int x, int y)
{
return x + y;
}

You’ll notice in this example we do not have to remember to call the code to authenticate the user manually as we saw in the previous article.  Instead by adding the attribute to the method it knows to call the authentication method.  To do this we need to first create a custom object that extends SoapExtensionAttribute and then another that extends SoapExtension.

Create a Custom SoapExtensionAttribute

In order to have the method call our custom SoapExtension we need to create an object that extends SoapExtensionAttribute.  It is a farily simple class with two overridden properties.  Here’s the code:

 [AttributeUsage(AttributeTargets.Method)]
public class AuthenticatonSoapExtensionAttribute : SoapExtensionAttribute
{
private int _priority;
public override int Priority
{
get { return _priority; }
set { _priority = value; }
}

public override Type ExtensionType
{
get { return typeof(AuthenticatonSoapExtension); }
}
}

You’ll notice about the only thing of real substance is the Extension type property which simply returns to us our custom extension.

Create a Custom SoapExtension

The last piece to pull this all together is a custom class which extends the SoapExtension class.  In this class we are going to write the code that does the actual authentication.  We are going to check for the AfterDeserialize stage and then first make sure we have a valid SoapHeader.   Once we do that we are going to call the static validation method and pass in the SoapHeader as we did above.

 /// <summary>
/// Custom SoapExtension that authenticates the method being called.
/// </summary>

public class AuthenticatonSoapExtension : SoapExtension
{
/// <summary>
/// When overridden in a derived class, allows a SOAP extension to initialize data specific to an XML Web service method using an attribute applied to the XML Web service method at a one time performance cost.
/// </summary>
/// <param name=”methodInfo”></param>
/// <param name=”attrib”></param>

public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attrib)
{
return null;
}
/// <summary>
/// When overridden in a derived class, allows a SOAP extension to initialize data specific to a class implementing an XML Web service at a one time performance cost.
/// </summary>
/// <param name=”WebServiceType”></param>

public override object GetInitializer(Type WebServiceType)
{
return null;
}

/// <summary>
/// When overridden in a derived class, allows a SOAP extension to initialize itself using the data cached in the GetInitializer method.
/// </summary>
/// <param name=”initializer”></param>

public override void Initialize(object initializer)
{
}

/// <summary>
/// After the message is deserialized we authenticate it.
/// </summary>
/// <param name=”message”></param>
public override void ProcessMessage(SoapMessage message)
{
if (message.Stage == SoapMessageStage.AfterDeserialize)
{
Authenticate(message);
}
}

public void Authenticate(SoapMessage message)
{
ServiceAuthHeader header = message.Headers[0] as ServiceAuthHeader;
if (header != null)
{
ServiceAuthHeaderValidation.Validate(header);
}
else
{
throw new ArgumentNullException(“No ServiceAuthHeader was specified in SoapMessage.”);
}
}

}

The method that we are really concerned with is the ProcessMessage which checks for the stage and then calls the Authenticate method.  This in turn calls our static validation method which checks for authentication.  At this point light bulbs should be going off!   Since we have a SoapMessage object do we not know which method is being called?  Yes!  Could we modify the ServiceAuthHeaderValidation to check for a database instead of hard coding things?  Yes!  Now you are starting to see where this could really go.   SoapExtensions are powerful and only limited to your imagination.

When I Test It, It Doesn’t Work, Why?

Once you get your SoapExtension in your solution setup and press F5 to debug it within Visual Studio  it will launch a new web server on a random port and bring you to your service.  You enter the parameters and submit the form and it by passes your validation.  Why!?

This is suppose to happen and here is why. If you go to the service invoked from VS through the browser interface it will not invoke the authentication and it isn’t suppose to either. The reason is you are not invoking the service via SOAP but rather just a standard POST from a form. Therefore, the SOAP extension is never going to fire. This should be disabled when you publish your web service to production as only SOAP messages should be allowed. If you have a case where you need to allow GET and POST calls then the method of a custom SoapExtension isn’t going to work.

As a benefit, Visual Studio builds the form for you automatically when you press F5 and allows you to pass parameters to the web method, but it does it via POST. If you invoke the web method from a console application or a real client making a SOAP call, you have to pass in the username and password.  I actually consider this behavior a feature.  If we didn’t use the SoapExtension to secure the method, we’d be forced to pass in username and password all the time which would mean we’d have to always call the secured web method from a test client.   Speaking from experience this isn’t fun.  Of course you should have Unit Tests for each web method anyway but it is really easy to pass in the params to a web form while debugging.

I hope you find this useful and now don’t feel so daunted because your team leader asked you how you were going to authenticate web service methods via a database.  The only thing left is for you to implement your required features.  Of course, if you are on an Intranet, instead of using username and password as we did in the previous post you could at the point of Authenticate(SoapMessage message) use the user’s integrated credentials and check for various groups in Active Directory or even using Enterprise Library Security Block.

Digital Certificates & Encryption

This is a white paper dedicated to Digital Certificates & Encryption, how they work and apply to Internet Commerce

The Need for Security
On the Internet, information  you send from one computer to another passes through numerous systems before it reaches its destination. Normally, the users of these intermediary systems don’t monitor the Internet traffic routed through them, but someone who’s determined can intercept and eavesdrop on your private conversations or credit card exchanges. Worse still, they might replace your information with their own and send it back on its way.

Due to the architecture of the Internet and intranets, there will always be ways for unscrupulous people to intercept and replace data in transit. Without security precautions, users can be compromised when sending information over the Internet or an intranet. This has serious implications for Internet Commerce. For Internet Commerce to exist, there has to be a means to secure data sent over the Internet. Without a secure means of communication, commerce cannot exist.

How do I protect my data?

Encryption & Digital Certificates are the solution for Internet Commerce. Used together, they protect your data as it travels over the Internet.
Encryption is the process of using a mathematical algorithm to transform information into a format that can’t be read (this format is called cipher text). Decryption is the process of using another algorithm to transform encrypted information back into a readable format (this format is called plain text).
Digital Certificates are your digital passport, an Internet ID. They are verification of you who you are and the integrity of your data.

Combined, encryption and digital certificates protect and secure your data in the following four ways:.
* Authentication: This is digital verification of who you are, much in the same way your driver’s license proves your identity. It is very easy to send spoofed email. I can email anyone in the world pretending I am the President of the United States. Using standard email, there is no way to verify who the sender is, i.e. if it is actually the President. With digital signatures and certificates, you digitally encode verifiable proof of your identity into the email.
* Integrity: This is the verification that the data you sent has not been altered. When email or other data travels across the Internet, it routes through various gateways (way stations). It is possible for people to capture, alter, then resend the message. Example, your boss emails the company president stating that you should be fired. It is possible for you to intercept that email and change it saying you deserve a $10,000 raise. With digital certificates, your email cannot be altered without the recipient knowing.
* Encryption: This ensures that your data was unable to be read or utilized by any party while in transit. Your message is encrypted into incomprehensible gibberish before it leaves your computer. It maintains it encrypted (gibberish) state during it’s travel through the Internet. It is not de-crypt until the recipient receives it. Because of the public-key cryptography used (discussed later) only the recipient can decipher the received message, no one else can.
* Token verification: Digital tokens replace your password which can be easily guessed. Tokens offer a more secure way of access to sensitive data. The most common way to secure data or a web site is with passwords. Before anyone access the data, they are prompted with their user login id and password. However, this is easily cracked using various security software (such as Crack 5.0, etc.). Also, passwords can be found with other means, such as social engineering. Passwords are not secure. Token verification is more secure. Your digital certificate is an encrypted file that sits on your hardrive. When you need access to a system, that systems asks you for your digital certificate instead of a password. Your computer would then send the certificate, in encrypted format, through the Internet, authorizing you for access. For this to be compromised, someone would have to copy this file from your computer, AND know your password to de-crypt the file.
How does it all work?

Encryption

To understand how this all works, we need to start with the basics. Encryption has been around for centuries, Julius Caesar used encrypted notes to communicate with Rome thousands of years ago. This traditional cryptography is based on the sender and receiver of a message knowing and using the same secret key: the sender uses the secret key to encrypt the message, and the receiver uses the same secret key to decrypt the message. For Caesar, the letter A was represented by the letter D, B by the letter E, C by the letter F, etc. The recipient would know about this sequence, or key, and decrypt his message. This method is known as secret-key or symmetric cryptography. Its main problem is getting the sender and receiver to agree on the key without anyone else finding out. Both sides must find some “secure” way to agree or exchange this common key. Because all keys must remain secret, secret-key cryptography often has difficulty providing secure key management, especially in open systems with a large numbers of users, such as the Internet.
21 years ago, a revolution happened in cryptography that changed all this, public-key cryptography. In 1976, Whitfield Diffie and Martin Hellman, introduced this new method of encryption and key management. A public-key cryptosystem is a cryptographic system that uses a pair of unique keys (a public key and a private key). Each individual is assigned a pair of these keys to encrypt and decrypt information. A message encrypted by one of these keys can only be decrypted by the other key in the pair:
* The public key is available to others for use when encrypting information that will be sent to an individual. For example, people can use a person’s public key to encrypt information they want to send to that person. Similarly, people can use the user’s public key to decrypt information sent by that person.
* The private key is accessible only to the individual. The individual can use the private key to decrypt any messages encrypted with the public key. Similarly, the individual can use the private key to encrypt messages, so that the messages can only be decrypted with the corresponding public key.

What does this mean?

Exchanging keys is no longer a security concern. I have my public key and private key. I send my public key to anyone on the Internet. With that public key, they encrypt their email. Since the email was encrypted with my public key, ONLY I can decrypt that email with my private key, no one else can. If I want to encrypt my email to anyone else on the Internet, I need their public key. Each individual involved needs their own public/private key combination.

Now, the big question is, when you initially receive someone’s public key for the first time, how do you know it is them? If spoofing someone’s identity is so easy, how do you knowingly exchange public keys, how do you TRUST the user is really who he says he is? You use your digital certificate. A digital certificate is a digital document that vouches for the identity and key ownership of an individual, a computer system (or a specific server running on that system), or an organization. For example, a user’s certificate verifies that the user owns a particular public key. Certificates are issued by certificate authorities, or CAs. These authorities are responsible for verifying the identity and key ownership of the individual before issuing the certificate, such as Verisign.

Authentication & Integrity

We now have a secure means of encrypting data, one of the four methods of securing data on the Internet. Two others, authentication and data integrity, are combined in what is called a digital signature. A digital signature works as follows:
* Authentication: a specific individual sent a message (in other words, no impersonator claiming to be the individual sent the message).
* Integrity: this particular message was sent by the individual (in other words, no one altered the message before it was received).
When you email someone, your public/private key combination creates the digital signature. It does this using the following format:
1. The sender uses a message-digest algorithm to generate a shorter version of the message that can be encrypted. This shorter version is called a message digest. Message digests and message-digest algorithms are explained in the next section.
2. The sender uses their private key to encrypt the message digest.
3. The sender transmits the message and the encrypted message digest to the recipient.
4. Upon receiving the message, the recipient decrypts the message digest.
5. The recipient uses the hash function on the message to generate the message digest.
6. The recipient compares the decrypted message digest against the newly generated message digest.
* If the message digests are identical, the recipient knows that the message was indeed sent by the person claiming to be the sender and that the message was not modified during transmission.
* If the message digests differ, the recipient knows that either the message was sent by someone else claiming to be the sender or that the message was modified or damaged during transmission.
The encrypted message digest serves as a digital signature for the message. The signature verifies the identity of the sender and the contents of the message.

If the message is sent by someone claiming to be the sender, this person does not have access to the sender’s private key. The person claiming to be the sender must use a different private key to encrypt the message digest.

Because the recipient uses the sender’s public key to decrypt the message digest (and not the actual public key corresponding to the private key used to encrypt the message digest), the decrypted message digest will not match the newly generated message digest.
If the message was modified during transmission, the hash function will generate a different message digest when applied after the transmission.

Tokens

Tokens represent the fourth security option by replacing passwords. Tokens are simply your digital certificate residing on your hardrive. When a computer prompts you for your password,  your computer sends your certificate over the Internet instead. Your certificate verifies your identity instead of the password. This is a more secure (and easier) means of verification.

How Secure is all This?

Just how secure is encryption. The strength of encryption is measured in bits, or how big the key is. The bigger the key, the stronger the encryption. There are currently 3 commonly used key sizes used commercially, 40, 56, and 128 bit. Originally, the government allowed only 40 bit keys for exportation. However, this proved far to weak for security. In February of 1997, a college student was able to crack 40 bit encrypted data within 4  hours .
Berkeley — It took UC Berkeley graduate student Ian Goldberg only three and a half hours to crack the most secure level of encryption that the federal government allows U.S. companies to export.

Yesterday (1/28) RSA Data Security Inc. challenged the world to decipher a message encrypted with its RC5 symmetric stream cipher, using a 40-bit key, the longest keysize allowed for export. RSA offered a $1,000 reward, designed to stimulate research and practical experience with the security of today’s codes.

Goldberg succeeded a mere 3 1/2 hours after the contest began, which provides very strong evidence that 40-bit ciphers are totally unsuitable for practical security.

In June of 1997, a organized group of people were able to crack 56 bit DES encryption in 140 days. This group shared their resources throughout the Internet utilizing software called DESCHALL. With a possible 72 quadrillion keys to test, this distributed attack would require an incredibly large amount of computing power. And compute the DESCHALL team did, at some points testing almost seven billion keys per second.

In the end, the DESCHALL effort solved the DES challenge after only searching 24.6% of the key space. (about 18 quadrillion keys!) The winning key was determined by Michael Sanders, using a Pentium 90 MHz desktop PC with 16 megs of RAM.

Many believe this security is good enough. By the time your data can be compromised, (3 months) it is of little value because it took so long.  However, to truly ensure the security of your data, most Internet Commerce uses 128 bit encryption. Keep in mind, key strength increases exponentially, making 128 bit encryption thousands of times more difficult to compromise. Because of its strength, the government has prohibited its exportation, it can only be used within the United States. At this time, no one has cracked this encryption. 128 bit encryption is expected to remain secure well past the year 2000.

What it Looks Like

Below is an example of a message that has been encrypted and signed, but intercepted before the recipient has received it.   Notice how the body of the entire message is “gibberish”, i.e., the message cannot be read.  That is what encryption looks like.

Below is an example of the same message, but received by the intended recipient.  The recipient has decrypted the message and verified the message’s integrity & /authenticity.  The protocol or Internet standard used for Digital Certificates is X.509 & S/MIME.  Any email system that has these open based standards can use Digital Certifcates for Internet Commerce.  The image below is of Netscape  Navigator, which is both X.509 and S/MIME compliant.

Conclusion

Utilizing digital certificates and encryption, users can easily and securely communicate on the Internet. This combination of ease of use and security lays the foundation for commerce. As users gain confidence and experience using these tools, Internet Commerce, much like encryption, will grow exponentially.

ASP.NET Forms Authentication – Security

ASP.NET Forms authentication allows users to identify themselves by entering credentials (a user name and password) into a Web Form. Upon receipt of these credentials, the Web application can authenticate the user by checking the user name and password combination against a data source.

This How To describes how to authenticate users against the Microsoft Active Directory directory service by using the Lightweight Directory Access Protocol (LDAP). It also describes how to retrieve a list of security groups and distribution lists to which the user belongs, how to store this information in a GenericPrincipal object, and how to store this into the HttpContext.Current.User property that flows with the request through the ASP.NET Web application. This can subsequently be used for .NET role-based authorization.

Summary of Steps

This How To includes the following steps:

  • Step 1. Create a Web Application with a Logon Page
  • Step 2.Configure the Web Application for Forms Authentication
  • Step 3.Develop LDAP Authentication Code to Look Up the User in Active Directory
  • Step 4.Develop LDAP Group Retrieval Code to Look Up the User’s Group Membership
  • Step 5.Authenticate the User and Create a Forms Authentication Ticket
  • Step 6.Implement an Authentication Request Handler to Construct a GenericPrincipal Object
  • Step 7.Test the Application

Step 1. Create a Web Application with a Logon Page

This procedure creates a simple C# Web application that contains a logon page that allows a user to enter a user name and password and a default page that displays the identity name and group membership information associated with the current Web request.

To create a Web application with a logon page

  1. Start Microsoft Visual Studio® .NET and create a new C# ASP.NET Web Application named FormsAuthAD.
  2. Use Solution Explorer to rename WebForm1.aspx as Logon.aspx.
  3. Add a new assembly reference to System.DirectoryServices.dll. This provides access to the System.DirectoryServices namespace that contains managed types to help with Active Directory querying and manipulation.
  4. Add the controls listed in Table 1 to Logon.aspx to create a simple logon form.Table 1. Logon.aspx controls
    Control Type Text ID
    Label Domain Name:
    Label User Name:
    Label Password
    Text Box txtDomainName
    Text Box txtUserName
    Text Box txtPassword
    Button Log On btnLogon
    Label lblError
  5. Set the TextMode property of txtPassword to Password.
  6. In Solution Explorer, right-click FormsAuthAd, point to Add, and then click Add Web Form.
  7. In the Name field, type default.aspx, and then click Open.
  8. In Solution Explorer, right-click default.aspx, and then click Set As Start Page.
  9. Double-click default.aspx to display the page load event handler.
  10. Add the following code to the event handler to display the identity name associated with the current Web request.
    Response.Write( HttpContext.Current.User.Identity.Name );

Step 2. Configure the Web Application for Forms Authentication

This procedure edits the application’s Web.config file to configure the application for Forms authentication.

To configure the Web application for forms authentication

  1. Use Solution Explorer to open Web.config.
  2. Locate the <authentication> element and change the mode attribute to Forms.
  3. Add the following <forms> element as a child of the authentication element and set the loginUrl, name, timeout, and pathattributes as shown in the following.
    <authentication mode="Forms">
      <forms loginUrl="logon.aspx" name="adAuthCookie" timeout="60"
        path="/">
      </forms>
    </authentication>
  4. Add the following <authorization> element beneath the <authentication> element. This will allow only authenticated users to access the application. The previously establish loginUrl attribute of the <authentication> element will redirect unauthenticated requests to the logon.aspx page.
    <authorization>
      <deny users="?" />
      <allow users="*" />
    </authorization>
  5. Save Web.config.
  6. Start the IIS Microsoft Management Console (MMC) snap-in.
  7. Right-click the application’s virtual directory, and then click Properties.
  8. Click the Directory Security tab, and then click the Edit button in the Anonymous access and authentication control group.
  9. Select the Anonymous access check box and clear the Allow IIS to control password check box.
  10. Because the default anonymous account IUSR_MACHINE does not have permission to access Active Directory, create a new least privileged account and enter the account details in the Authentication Methods dialog box.
  11. Click OK, and then click OK again to close the Properties dialog box.
  12. Return to Visual Studio .NET and add an <identity> element beneath the <authorization> element in Web.config and set the impersonate attribute to true. This causes ASP.NET to impersonate the anonymous account specified earlier.
    <identity impersonate="true" />

    As a result of this configuration, all requests to the application will run under the security context of the configured anonymous account. The user will provide credentials through the Web form to authenticate against Active Directory, but the account used to access Active Directory will be the configured anonymous account.

Step 3. Develop LDAP Authentication Code to Look Up the User in Active Directory

This procedure adds a new helper class to the Web application to encapsulate the LDAP code. The class will initially provide an IsAuthenticated method to validate a supplied domain, user name, and password against an Active Directory user object.

To develop LDAP authentication code to look up the user in Active Directory

  1. Add a new C# class file called LdapAuthentication.cs.
  2. Add a reference to the System.DirectoryServices.dll assembly.
  3. Add the following usingstatements to the top of LdapAuthentication.cs.
    using System.Text;
    using System.Collections;
    using System.DirectoryServices;
  4. Rename the existing namespace as FormsAuthAD.
  5. Add two private strings to the LdapAuthenticationclass; one to hold the LDAP path to Active Directory and the other to hold a filter attribute used for searching Active Directory.
    private string _path;
    private string _filterAttribute;
  6. Add a public constructor that can be used to initialize the Active Directory path.
    public LdapAuthentication(string path)
    {
      _path = path;
    }
  7. Add the following IsAuthenticated method that accepts a domain name, user name and password as parameters and returns bool to indicate whether or not the user with a matching password exists within Active Directory. The method initially attempts to bind to Active Directory using the supplied credentials. If this is successful, the method uses the DirectorySearcher managed class to search for the specified user object. If located, the _path member is updated to point to the user object and the _filterAttributemember is updated with the common name attribute of the user object.
    public bool IsAuthenticated(string domain, string username, string
      pwd)
    {
      string domainAndUsername = domain + @"" + username;
      DirectoryEntry entry = new DirectoryEntry( _path,
                                                 domainAndUsername,
                                                   pwd);
    
      try
      {
        // Bind to the native AdsObject to force authentication.
        Object obj = entry.NativeObject;
        DirectorySearcher search = new DirectorySearcher(entry);
        search.Filter = "(SAMAccountName=" + username + ")";
        search.PropertiesToLoad.Add("cn");
        SearchResult result = search.FindOne();
        if(null == result)
        {
          return false;
        }
        // Update the new path to the user in the directory
        _path = result.Path;
        _filterAttribute = (String)result.Properties["cn"][0];
      }
      catch (Exception ex)
      {
        throw new Exception("Error authenticating user. " + ex.Message);
      }
      return true;
    }

Step 4. Develop LDAP Group Retrieval Code to Look Up the User’s Group Membership

This procedure extends the LdapAuthentication class to provide a GetGroups method, which will retrieve the list of groups that the current user is a member of. The GetGroups method will return the group list as a pipe separated string, as in the following.

"Group1|Group2|Group3|"

To develop LDAP group retrieval code to look up the user’s group membership

  1. Add the following implementation of the GetGroups method to the LdapAuthenticationclass.
    public string GetGroups()
    {
      DirectorySearcher search = new DirectorySearcher(_path);
      search.Filter = "(cn=" + _filterAttribute + ")";
      search.PropertiesToLoad.Add("memberOf");
      StringBuilder groupNames = new StringBuilder();
      try
      {
        SearchResult result = search.FindOne();
        int propertyCount = result.Properties["memberOf"].Count;
        String dn;
        int equalsIndex, commaIndex;
    
        for( int propertyCounter = 0; propertyCounter < propertyCount;
             propertyCounter++)
        {
          dn = (String)result.Properties["memberOf"][propertyCounter];
    
          equalsIndex = dn.IndexOf("=", 1);
          commaIndex = dn.IndexOf(",", 1);
          if (-1 == equalsIndex)
          {
            return null;
          }
          groupNames.Append(dn.Substring((equalsIndex + 1),
                            (commaIndex - equalsIndex) - 1));
          groupNames.Append("|");
        }
      }
      catch(Exception ex)
      {
        throw new Exception("Error obtaining group names. " +
          ex.Message);
      }
      return groupNames.ToString();
    }

Step 5. Authenticate the User and Create a Forms Authentication Ticket

This procedure implements the btnLogon_Click event handler to authenticate users. For authenticated users, you will then create a Forms authentication ticket that contains the user’s group list. You will then redirect the user to the original page that they requested (before being redirected to the logon page).

To authenticate the user and create a forms authentication ticket

  1. Return to the Logon.aspx form and double-click the Log On button to create an empty btnLogon_Click event handler.
  2. At the top of the file add the following using statement beneath the existing using statements. This provides access to the FormsAuthenticationmethods.
    using System.Web.Security;
  3. Add code to create a new instance of the LdapAuthenticationclass initialized to point to your LDAP Active Directory, as shown in the following code. Remember to change the path to point to your Active Directory server.
    // Path to you LDAP directory server.
    // Contact your network administrator to obtain a valid path.
    string adPath =
      "LDAP://yourCompanyName.com/DC=yourCompanyName,DC=com";
    LdapAuthentication adAuth = new LdapAuthentication(adPath);
  4. Add the code that follows to perform the following steps:
    1. Authenticate the caller against Active Directory.
    2. Retrieve the list of groups that the user is a member of.
    3. Create a FormsAuthenticationTicket that contains the group list.
    4. Encrypt the ticket.
    5. Create a new cookie that contains the encrypted ticket.
    6. Add the cookie to the list of cookies returned to the user’s browser.
    try
    {
      if(true == adAuth.IsAuthenticated(txtDomainName.Text,
                                        txtUserName.Text,
                                        txtPassword.Text))
      {
        // Retrieve the user's groups
        string groups = adAuth.GetGroups();
        // Create the authetication ticket
        FormsAuthenticationTicket authTicket =
            new FormsAuthenticationTicket(1,  // version
                                          txtUserName.Text,
                                          DateTime.Now,
                                          DateTime.Now.AddMinutes(60),
                                          false, groups);
        // Now encrypt the ticket.
        string encryptedTicket =
          FormsAuthentication.Encrypt(authTicket);
        // Create a cookie and add the encrypted ticket to the
        // cookie as data.
        HttpCookie authCookie =
                     new HttpCookie(FormsAuthentication.FormsCookieName,
                                    encryptedTicket);
        // Add the cookie to the outgoing cookies collection.
        Response.Cookies.Add(authCookie);
    
        // Redirect the user to the originally requested page
        Response.Redirect(
                  FormsAuthentication.GetRedirectUrl(txtUserName.Text,
                                                     false));
      }
      else
      {
        lblError.Text =
             "Authentication failed, check username and password.";
      }
    }
    catch(Exception ex)
    {
      lblError.Text = "Error authenticating. " + ex.Message;
    }

Step 6. Implement an Authentication Request Handler to Construct a GenericPrincipal Object

This procedure implements the Application_AuthenticateRequest event handler within global.asax and creates a GenericPrincipal object for the currently authenticated user. This will contain the list of groups that the user is a member of, retrieved from the FormsAuthenticationTicket contained in the authentication cookie. Finally, you will associate the GenericPrincipal object with the current HttpContext object that is created for each Web request.

To implement an authentication request handler to construct a GenericPrincipal object

  1. Use Solution Explorer to open global.asax.cs.
  2. Add the following usingstatements to the top of the file.
    using System.Web.Security;
    using System.Security.Principal;
  3. Locate the Application_AuthenticateRequest event handler and add the following code to obtain the cookie that contains the encrypted FormsAuthenticationTicket, from the cookie collection passed with the request.
    // Extract the forms authentication cookie
    string cookieName = FormsAuthentication.FormsCookieName;
    HttpCookie authCookie = Context.Request.Cookies[cookieName];
    
    if(null == authCookie)
    {
      // There is no authentication cookie.
      return;
    }
  4. Add the following code to extract and decrypt the FormsAuthenticationTicketfrom the cookie.
    FormsAuthenticationTicket authTicket = null;
    try
    {
      authTicket = FormsAuthentication.Decrypt(authCookie.Value);
    }
    catch(Exception ex)
    {
      // Log exception details (omitted for simplicity)
      return;
    }
    
    if (null == authTicket)
    {
      // Cookie failed to decrypt.
      return;
    }
  5. Add the following code to parse out the pipe separate list of group names attached to the ticket when the user was originally authenticated.
    // When the ticket was created, the UserData property was assigned a
    // pipe delimited string of group names.
    String[] groups = authTicket.UserData.Split(new char[]{'|'});
  6. Add the following code to create a GenericIdentity object with the user name obtained from the ticket name and a GenericPrincipalobject that contains this identity together with the user’s group list.
    // Create an Identity object
    GenericIdentity id = new GenericIdentity(authTicket.Name,
                                             "LdapAuthentication");
    
    // This principal will flow throughout the request.
    GenericPrincipal principal = new GenericPrincipal(id, groups);
    // Attach the new principal object to the current HttpContext object
    Context.User = principal;

Step 7. Test the Application

This procedure uses the Web application to request the default.aspx page. You will be redirected to the logon page for authentication. Upon successful authentication, your browser will be redirected to the originally requested default.aspx page. This will extract and display the list of groups that the authenticated user belongs to from the GenericPrincipal object that has been associated with the current request by the authentication process.

To test the application

  1. On the Build menu, click Build Solution.
  2. In Solution Explorer, right-click default.aspx, and then click View in Browser.
  3. Enter a valid domain name, user name, and password and then click Log On.
  4. If you are successfully authenticated, you should be redirected back to default.aspx. The code on this page should display the user name of the authenticated user.To see the list of groups the authenticated user is a member of, add the following code at the end of the Application_AuthenticateRequest event handler in the global.aspx.cs file.
    Response.Write("Groups: " + authTicket.UserData + "<br>");