background image

Forms Authentication in SharePoint Products and Technologies (Part 
2): Membership and Role Provider Samples 

Summary: Explore details of developing a custom membership and role provider for Microsoft Office SharePoint 
Server (MOSS) 2007 and Windows SharePoint Services 3.0, including the minimum required interfaces and how to 
register and debug your custom provider. This article is part 2 of 3. (26 printed pages) 

Steve Peschka, Microsoft Corporation

 

December 2007

 

Applies to: Microsoft Office SharePoint Server 2007, Windows SharePoint Services 3.0

 

Contents 

 

z

Developing Custom Membership and Role Providers

 

 

z

Applying the XML File for the Data Source

 

 

z

Inheriting from the Membership and Role Base Classes

 

 

z

Minimum Interfaces Required by MOSS and Windows SharePoint Services

 

 

z

Registering the Custom Provider

 

 

z

Debugging the Custom Provider

 

 

z

Writing a Custom Forms Logon Page

 

 

z

Using Web Services with a Site Protected by Forms Authentication

 

 

z

Additional Resources

 

 

Read part 1 and part 3: 

 

Forms Authentication in SharePoint Products and Technologies (Part 1): Introduction

 

[ http://msdn2.microsoft.com/en-us/library/bb975136(printer).aspx ]  

Forms Authentication in SharePoint Products and Technologies (Part 3): Forms Authentication vs. Windows 
Authentication

 [ http://msdn2.microsoft.com/en-us/library/bb977430(printer).aspx ]  

Developing Custom Membership and Role Providers

 

Applying the XML File for the Data Source

 

©2008 Microsoft Corporation. All rights 

reserved.

Microsoft Office SharePoint Server (MOSS) 2007 and Windows SharePoint Services 3.0 (in this article series, 
collectively referred to as SharePoint Products and Technologies) are built upon the ASP.NET 2.0 Framework. As 
such, support for forms authentication extends not only to the membership and role providers that are included 
with ASP.NET, MOSS, and Windows SharePoint Services, but also to custom membership and role providers. The 
custom provider is required to inherit only from the ASP.NET membership or role base class respectively, and to 
implement a limited set of interfaces on those classes.  

Note: 

Addressing this part of the Microsoft .NET Framework extensively is beyond the scope of this article. For 
more information about the membership and role provider base classes, see 

System.Web.Security.Membership

 [ http://msdn2.microsoft.com/en-us/library/dazakw52

(printer).aspx ] and 

System.Web.Security.RoleProvider

 [ http://msdn2.microsoft.com/en-

us/library/kkdz3641(printer).aspx ] . 

We now describe how to write a custom provider for users and roles that uses an XML file for the directory 
information. The format of the XML file looks like the following code. 

Important: 

Strona 1 z 24

Forms Authentication in SharePoint Products and Technologies (Part 2): Membership...

2008-02-22

http://msdn2.microsoft.com/en-us/library/bb975135(printer).aspx

background image

Inheriting from the Membership and Role Base Classes

 

It is NOT a safe or secure practice to include unencrypted passwords in a clear text file, as displayed in 
the following code example. We are using it in this case only to simplify the explanation of developing 
custom providers. This is not an acceptable design for a production application. When storing sensitive 
information in a configuration file for an application, you should encrypt the sensitive values by using 
Protected Configuration. For more information, see 

Encrypting Configuration Information Using 

Protected Configuration

 [ http://msdn2.microsoft.com/en-us/library/53tyfkaw(printer).aspx ] and 

Securing Membership

 [ http://msdn2.microsoft.com/en-us/library/ms178398(printer).aspx ] . If you 

take the approach used as an example in this article, you are responsible for encrypting and decrypting 
the data within your provider. 

<

Users

>

 

  

<

User

 name="user1" email="user1@microsoft.com" password="test" created="7/15/2007"

>

 

    

<

Groups

>

 

      

<

Group

>

Administrators

</

Group

>

 

      

<

Group

>

Authors

</

Group

>

 

      

<

Group

>

Readers

</

Group

>

 

    

</

Groups

>

 

  

</

User

>

 

  

<

User

 name="user2" email="user2@microsoft.com" password="test" created="7/31/2007"

>

 

    

<

Groups

>

 

      

<

Group

>

Readers

</

Group

>

 

    

</

Groups

>

 

  

</

User

>

 

  

<

User

 name="user3" email="user3@microsoft.com" password="test" created="8/15/2007"

>

 

    

<

Groups

>

 

      

<

Group

>

Designers

</

Group

>

 

      

<

Group

>

Readers

</

Group

>

 

    

</

Groups

>

 

  

</

User

>

 

</

Users

>

 

Xml

 Copy Code

The base class that is used for membership is 

System.Web.Security.MembershipProvider

 

[ http://msdn2.microsoft.com/en-us/library/sfka4yf8(printer).aspx ] ; if you are writing a custom membership 
provider you must inherit from this class. 

The base class that is used for roles is System.Web.Security.RoleProvider; if you are writing a custom role 
provider you must inherit from this class. 

using

 System.Web; 

using

 System.Web.Security; 

using

 System.Xml; 

using

 System.Configuration; 

 

namespace

 Microsoft.IW 


   

public

 

class

 customUser : MembershipProvider 

   { 
      

// This is not necessary for every provider; we are doing  

 

      

// it for this one provider so that we can ensure our XML 

 

      

// file is set up correctly.

 

      

private

 XmlDocument xDoc = 

null

       
      

// Code in here.

 

   } 
   

public

 

class

 customRole : RoleProvider 

   { 
      

// This is not necessary for every provider; we are doing  

 

      

// it for this one provider so that we can ensure our XML 

 

      

// file is set up correctly.

 

      

private

 XmlDocument xDoc = 

null

       
   

//Code in here.

 

   } 

C#

 Copy Code

Imports

 System.Web 

Imports

 System.Web.Security 

Imports

 System.Xml 

Visual Basic

 Copy Code

Strona 2 z 24

Forms Authentication in SharePoint Products and Technologies (Part 2): Membership...

2008-02-22

http://msdn2.microsoft.com/en-us/library/bb975135(printer).aspx

background image

Minimum Interfaces Required by MOSS and Windows SharePoint Services

 

Imports

 System.Configuration 

 

Namespace

 Microsoft.IW 

   

Public

 

Class

 customUser 

      

Inherits

 MembershipProvider 

 
      

' This is not necessary for every provider; we are doing  

 

      

' it for this one provider so that we can ensure our XML 

 

      

' file is set up correctly.

 

      

Private

 xDoc 

As

 XmlDocument = 

Nothing

 

 
      

' Code in here.

 

   

End

 

Class

 

   

Public

 

Class

 customRole 

      

Inherits

 RoleProvider 

 
      

' This is not necessary for every provider; we are doing  

 

      

' it for this one provider so that we can ensure our XML 

 

      

' file is set up correctly.

 

      

Private

 xDoc 

As

 XmlDocument = 

Nothing

 

 
      

' Code in here.

 

   

End

 

Class

 

End

 

Namespace

 

Both the MembershipProvider class and the RoleProvider class include several methods and properties. To 
use the provider, only a subset of those is required by MOSS and Windows SharePoint Services. 

To use the MembershipProvider, you must implement the following methods:

 

z

System.Web.Security.MembershipProvider.GetUser

 [ http://msdn2.microsoft.com/en-us/library/ms152043

(printer).aspx ]  

z

System.Web.Security.MembershipProvider.GetUserNameByEmail(System.String)

 

[ http://msdn2.microsoft.com/en-us/library/57hsxfsd(printer).aspx ]  

z

System.Web.Security.MembershipProvider.ValidateUser(System.String,System.String)

 

[ http://msdn2.microsoft.com/en-us/library/05d03b82(printer).aspx ]  

z

System.Web.Security.MembershipProvider.FindUsersByEmail
(System.String,System.Int32,System.Int32,System.Int32)

 [ http://msdn2.microsoft.com/en-

us/library/96e54ch9(printer).aspx ]  

z

System.Web.Security.MembershipProvider.FindUsersByName
(System.String,System.Int32,System.Int32,System.Int32)

 [ http://msdn2.microsoft.com/en-

us/library/89e8kx21(printer).aspx ]  

To use the RoleProvider, you must implement the following methods: 

 

z

System.Web.Security.RoleProvider.GetRolesForUser(System.String)

 [ http://msdn2.microsoft.com/en-

us/library/wzdyk8sx(printer).aspx ]  

z

System.Web.Security.RoleProvider.RoleExists(System.String)

 [ http://msdn2.microsoft.com/en-

us/library/5t22kc66(printer).aspx ]  

In addition to the methods that SharePoint Products and Technologies require, the class contract requires you to 
implement some additional methods and properties. In those cases, you can either implement the functionality or 
choose to throw a 

System.NotSupportedException

 [ http://msdn2.microsoft.com/en-us/library/8a7a4e64

(printer).aspx ] . For example, the MembershipProvider requires a 

System.Web.Security.MembershipProvider.ChangePassword(System.String,System.String,System.String)

 

[ http://msdn2.microsoft.com/en-us/library/bdt44e91(printer).aspx ] method; if you do not want to support that 
functionality, your override of that method should resemble the following code. 

Note: 

The following section contains several code examples meant to demonstrate only the implementation of 
the minimum interfaces required to have a custom membership and role provider that is usable with 
MOSS 2007 and Windows SharePoint Services 3.0. They are not meant to demonstrate coding best 
practices. 

Strona 3 z 24

Forms Authentication in SharePoint Products and Technologies (Part 2): Membership...

2008-02-22

http://msdn2.microsoft.com/en-us/library/bb975135(printer).aspx

background image

As with most custom providers, this example includes special code to work with the data store: in this case, an 
XML file. A simple helper class was developed to read the file from a hard-coded location on disk and store it in 
cache. The item was added into cache with a dependency so that if the file on disk changes, such as when a user 
is added or removed, the XML file is purged from cache. Following is the code for the helper class. 

public

 override 

bool

 ChangePassword(

string

 username, 

string

 oldPassword, 

string

 

newPassword) 

  throw 

new

 NotSupportedException(); 

C#

 Copy Code

Public

 

Overrides

 

Function

 ChangePassword(

ByVal

 username 

As

 

String

ByVal

 oldPassword 

As

 

String

ByVal

 newPassword 

As

 

String

As

 

Boolean

 

  

Throw

 

New

 NotSupportedException() 

End

 

Function

 

Visual Basic

 Copy Code

using

 System; 

using

 System.Collections.Generic; 

using

 System.Text; 

using

 System.Xml; 

using

 System.Diagnostics; 

using

 System.Web; 

 

namespace

 Microsoft.IW 


  internal 

class

 Helper 

  { 
    

private

 

const

 

string

 XML_PATH =  

    

"c:\\inetpub\\wwwroot\\userdata\\users.xml"

    

private

 

const

 

string

 XML_CACHE = 

"xmlUserDocFile"

 
    

public

 

static

 XmlDocument GetXmlFile() 

    { 
      

// This helper function looks for the XML document 

 

      

// in cache; if it is there,

 

      

// it pulls it out of cache. Otherwise, it loads 

 

      

// it into an XML document

 

      

// and stores it in cache with a file cache

 

      

// dependency, so that if the

 

      

// the XML file changes it will be flushed out 

 

      

// of cache and have to be reread and reloaded.

 

 
      XmlDocument xDoc = 

null

 
      

try

 

      { 
        

// Look for the item in cache.

 

        

if

 (HttpContext.Current.Cache[XML_CACHE] == 

null

        { 
          

// Create a new document.

 

          xDoc = 

new

 XmlDocument(); 

 
          

// Load it from disk.

 

          xDoc.Load(XML_PATH); 
 
          

// Save it to cache.

 

          HttpContext.Current.Cache.Insert(XML_CACHE, 
            xDoc, 

new

  

            System.Web.Caching.CacheDependency(XML_PATH)); 
        } 
        

else

 

          xDoc =  
           (XmlDocument)HttpContext.Current.Cache[XML_CACHE]; 
      } 
    

catch

 (Exception ex) 

    { 
      

// Try writing to the event log.

 

      

try

 

      { 
        EventLog.WriteEntry(

"fbaSharp Custom Provider"

          

"Error loading user xml file: "

 + 

          ex.Message, EventLogEntryType.Error); 
      } 

C#

 Copy Code

Strona 4 z 24

Forms Authentication in SharePoint Products and Technologies (Part 2): Membership...

2008-02-22

http://msdn2.microsoft.com/en-us/library/bb975135(printer).aspx

background image

Functions Required in a Custom Membership Provider

 

      

catch

 

      { 
        

// Ignore.

 

      } 
    } 
 
    

return

 xDoc; 

    } 
  } 

Imports

 System.Xml 

Imports

 System.Diagnostics 

Imports

 System.Web 

 

Namespace

 Microsoft.IW 

Friend

 

Class

 Helper 

 

Private

 

Const

 XML_PATH 

As

 

String

 = _ 

"c:\inetpub\wwwroot\userdata\users.xml"

 

Private

 

Const

 XML_CACHE 

As

 

String

 = 

"xmlUserDocFile"

 

 

Public

 

Shared

 

Function

 GetXmlFile() 

As

 XmlDocument 

 
  

' This helper function looks for the XML document 

 

  

' in cache; if it is there, it pulls it out of cache.

 

  

' Otherwise, it loads it into an XML document and

 

  

' stores it in cache with a file cache dependency, 

 

  

' so that if the XML file changes it will be

 

  

' flushed out of cache and have to be reread

 

  

' and reloaded.

 

 
  

Dim

 xDoc 

As

 XmlDocument = 

Nothing

 

 
  

Try

 

    

' Look for the item in cache.

 

    

If

 HttpContext.Current.Cache(XML_CACHE) 

Is

 

Nothing

 

Then

 

      

' Create a new document.

 

      xDoc = 

New

 XmlDocument 

 
      

' Load it from disk.

 

      xDoc.Load(XML_PATH) 
 
      

' Save it to cache.

 

      HttpContext.Current.Cache.Insert(XML_CACHE, xDoc, _ 
        

New

 System.Web.Caching.CacheDependency(XML_PATH)) 

    

Else

 

      xDoc = HttpContext.Current.Cache(XML_CACHE) 
    

End

 

If

 

  

Catch

 ex 

As

 Exception 

    

' Try writing to event log.

 

    

Try

 

      EventLog.WriteEntry(

"fbaVB Custom Provider"

, _ 

        

"Error loading user xml file: "

 & _ 

        ex.Message, EventLogEntryType.Error) 
    

Catch

 logEx 

As

 Exception 

      

' Ignore.

 

    

End

 

Try

 

  

End

 

Try

 

  

Return

 xDoc 

End

 

Function

 

End

 

Class

 

End

 

Namespace

 

Visual Basic

 Copy Code

The following sections describe the functions (methods) required in a custom membership provider.

 

GetUser Method

 

The GetUser function has two overridden implementations and returns a 

System.Web.Security.MembershipUser

 

[ http://msdn2.microsoft.com/en-us/library/d1b506ez(printer).aspx ] object based on user name and a flag that 
indicates whether a user is online. As with any class, the author must determine whether and how to use the 
parameters that are passed to it. For example, in most cases it is unlikely that you would change the return value 
from this function based only on whether the user is online. Notice that the providerUserKey is useful only if 
you are using a single Membership database to store users for multiple applications and you need to distinguish 

Strona 5 z 24

Forms Authentication in SharePoint Products and Technologies (Part 2): Membership...

2008-02-22

http://msdn2.microsoft.com/en-us/library/bb975135(printer).aspx

background image

in which of those applications the user exists. You can find more detail about this parameter on MSDN in the 
Membership classes materials that are referenced at the beginning of this article. This parameter is controlled in 
the applicationName attribute of the add element in the web.config file that is used to define a membership 
provider. 

public

 override MembershipUser GetUser(object providerUserKey,  

bool

 userIsOnline) 


  MembershipUser ret = 

null

  XmlNode xNode = 

null

 
  

try

 

  { 
    

// Get the XML document.

 

    xDoc = Helper.GetXmlFile(); 
 
    

// Proceed if it contains something.

 

    

if

 (xDoc != 

null

    { 
      

// Look for a user with a name that matches.

 

      xNode = xDoc.SelectSingleNode(

"/Users/User[@name='"

 +  

        providerUserKey.ToString() + 

"']"

); 

 
      

// Determine whether there are any matches.

 

      

if

 (xNode != 

null

      { 
        

// Create a new membershipusercollection.

 

        ret = 

new

 MembershipUser(Membership.Provider.Name, 

        xNode.Attributes[

"name"

].Value.ToString(), 

        xNode.Attributes[

"name"

].Value.ToString(), 

        xNode.Attributes[

"email"

].Value.ToString(), 

        

string

.Empty, 

string

.Empty, 

true

false

        DateTime.Parse(xNode.Attributes[

"created"

].Value.ToString()), 

        DateTime.Today, DateTime.Today, DateTime.Today,  
        DateTime.MinValue); 
      } 
    } 
  } 

catch

 


  

// Take appropriate action.

 


 
  

// Return the results.

 

  

return

 ret; 


 

public

 override MembershipUser GetUser(

string

 username,  

bool

 userIsOnline) 


  MembershipUser ret = 

null

  XmlNode xNode = 

null

 
  

try

 

  { 
    

// Get the XML document.

 

    xDoc = Helper.GetXmlFile(); 
 
    

// Proceed if it contains something.

 

    

if

 (xDoc != 

null

    { 
 
    

// Look for a user with a name that matches.

 

    xNode = xDoc.SelectSingleNode(

"/Users/User[@name='"

 +  

      username + 

"']"

); 

 
    

// See if there are any matches.

 

    

if

 (xNode != 

null

    { 
      

// Create a new membershipusercollection.

 

      ret = 

new

 MembershipUser(Membership.Provider.Name, 

      xNode.Attributes[

"name"

].Value.ToString(), 

      xNode.Attributes[

"name"

].Value.ToString(), 

      xNode.Attributes[

"email"

].Value.ToString(), 

      

string

.Empty, 

string

.Empty, 

true

false

      DateTime.Parse(xNode.Attributes[

"created"

].Value.ToString()), 

      DateTime.Today, DateTime.Today, DateTime.Today, 
      DateTime.MinValue); 

C#

 Copy Code

Strona 6 z 24

Forms Authentication in SharePoint Products and Technologies (Part 2): Membership...

2008-02-22

http://msdn2.microsoft.com/en-us/library/bb975135(printer).aspx

background image

    } 
  } 

catch

 


  

// Take appropriate action.

 


 
  

// Return the results.

 

  

return

 ret; 

Public

 

Overloads

 

Overrides

 

Function

 GetUser(_  

ByVal

 providerUserKey 

As

 

Object

ByVal

 userIsOnline 

As

 

Boolean

) _ 

As

 System.Web.Security.MembershipUser 

 

Dim

 ret 

As

 MembershipUser = 

Nothing

 

Dim

 xNode 

As

 XmlNode = 

Nothing

 

 

Try

 

  

' Get the XML document.

 

  xDoc = Helper.GetXmlFile() 
 
  

' Proceed if it contains something.

 

  

If

 xDoc IsNot 

Nothing

 

Then

 

 
    

' Look for a user with a name that matches.

 

    xNode = xDoc.SelectSingleNode("/Users/User[@name=

'" & _

 

      providerUserKey.ToString() & "

']")

 

 
    

' See if there are any matches.

 

    

If

 xNode IsNot 

Nothing

 

Then

 

      

'Create a new membershipusercollection.

 

        ret = 

New

 MembershipUser(Membership.Provider.Name, _ 

        xNode.Attributes(

"name"

).Value.ToString(), _ 

        xNode.Attributes(

"name"

).Value.ToString(), _ 

        xNode.Attributes(

"email"

).Value.ToString(), _ 

        

String

.Empty, 

String

.Empty, 

True

False

, _ 

        

Date

.Parse(xNode.Attributes(

"created"

).Value.ToString()), _ 

        

Date

.Today, 

Date

.Today, 

Date

.Today, 

Date

.MinValue) 

    

End

 

If

 

  

End

 

If

 

 

Catch

 ex 

As

 Exception 

  

'Take appropriate action.

 

End

 

Try

 

 

' Return the results.

 

Return

 ret 

End

 

Function

 

 

Public

 

Overloads

 

Overrides

 

Function

 GetUser( _ 

ByVal

 username 

As

 

String

,ByVal userIsOnline 

As

 

Boolean

) _ 

As

 System.Web.Security.MembershipUser 

 

Dim

 ret 

As

 MembershipUser = 

Nothing

 

Dim

 xNode 

As

 XmlNode = 

Nothing

 

 

Try

 

  

' Get the XML document.

 

  xDoc = Helper.GetXmlFile() 
 
  

' Proceed if it contains something.

 

  

If

 xDoc IsNot 

Nothing

 

Then

 

 
  

' Look for a user with a name that matches.

 

  xNode = xDoc.SelectSingleNode("/Users/User[@name=

'" & _

 

    username & "

']")

 

 
  

' See if there are any matches.

 

  

If

 xNode IsNot 

Nothing

 

Then

 

    

' Create a new membershipusercollection.

 

    ret = 

New

 MembershipUser(Membership.Provider.Name, _ 

    xNode.Attributes(

"name"

).Value.ToString(), _ 

    xNode.Attributes(

"name"

).Value.ToString(), _ 

    xNode.Attributes(

"email"

).Value.ToString(), _ 

    

String

.Empty, 

String

.Empty, 

True

False

, _ 

                 

Visual Basic

 Copy Code

Strona 7 z 24

Forms Authentication in SharePoint Products and Technologies (Part 2): Membership...

2008-02-22

http://msdn2.microsoft.com/en-us/library/bb975135(printer).aspx

background image

GetUserNameByEmail Method 

The GetUserNameByEmail function takes an e-mail address as a parameter and looks for a user with that e-
mail address. If it finds such a user, it returns the user name. 

    

Date

.Parse(xNode.Attributes(

"created"

).Value.ToString()), _ 

    

Date

.Today, 

Date

.Today, 

Date

.Today, 

Date

.MinValue) 

    

End

 

If

 

  

End

 

If

 

 

Catch

 ex 

As

 Exception 

  

'Take appropriate action.

 

End

 

Try

 

 

' Return the results.

 

Return

 ret 

End

 

Function

  

public

 override 

string

 GetUserNameByEmail(

string

 email) 


  

string

 ret = 

string

.Empty; 

  XmlNode xNode = 

null

 
  

try

 

  { 
    

// Get the XML document.

 

    xDoc = Helper.GetXmlFile(); 
 
    

// Proceed if it contains something.

 

    

if

 (xDoc != 

null

    { 
      

// Look for the user.

 

      xNode = xDoc.SelectSingleNode(

"/Users/User[@email='"

 +  

        email + 

"']"

); 

 
      

// See if it found a match.

 

      

if

 (xNode != 

null

)  

        ret = xNode.Attributes[

"name"

].Value.ToString(); 

    } 
  } 
  

catch

  

  { 
    

// Take appropriate action.

 

  } 
 
  

// Return the results.

 

  

return

 ret; 

C#

 Copy Code

Public

 

Overrides

 

Function

 GetUserNameByEmail(

ByVal

 email 

As

 

String

) _ 

As

 

String

 

 

Dim

 ret 

As

 

String

 = 

String

.Empty 

Dim

 xNode 

As

 XmlNode = 

Nothing

 

 

Try

 

  

' Get the XML document.

 

  xDoc = Helper.GetXmlFile() 
 
  

' Proceed if it contains something.

 

  

If

 xDoc IsNot 

Nothing

 

Then

 

 
    

' Look for the user.

 

    xNode = xDoc.SelectSingleNode("/Users/User[@email=

'" & _

 

      email & "

']")

 

 
    

' See if it found a match.

 

    

If

 xNode IsNot 

Nothing

 

Then

 _  

      ret = xNode.Attributes(

"name"

).Value.ToString 

  

End

 

If

 

 

Catch

 ex 

As

 Exception 

  

' Take appropriate action.

 

End

 

Try

 

 

' Return the results.

 

Visual Basic

 Copy Code

Strona 8 z 24

Forms Authentication in SharePoint Products and Technologies (Part 2): Membership...

2008-02-22

http://msdn2.microsoft.com/en-us/library/bb975135(printer).aspx

background image

ValidateUser Method 

The ValidateUser function takes a user name and password and verifies whether it is correct.

 

Return

 ret 

End

 

Function

 

public

 override 

bool

 ValidateUser(

string

 username, 

string

 password) 


 
  

bool

 ret = 

false

  XmlNode xNode = 

null

 
  

try

 

  { 
    

// Get the XML document.

 

    xDoc = Helper.GetXmlFile(); 
 
    

// Proceed if it contains something.

 

    

if

 (xDoc != 

null

    { 
      

// Look for the user.

 

      xNode = xDoc.SelectSingleNode(

"/Users/User[@name='"

 +  

        username + 

"']"

); 

 
      

// See if it found a match.

 

      

if

 (xNode != 

null

      { 
        

// Look for the password attribute to see if it matches.

 

        

if

 (xNode.Attributes[

"password"

].Value.ToString() == password)  

          ret = 

true

      } 
    } 
  } 
  

catch

 

  { 
    

// Take appropriate action.

 

  } 
 
  

// Return the results.

 

  

return

 ret; 

C#

 Copy Code

Public

 

Overrides

 

Function

 ValidateUser(

ByVal

 username 

As

 

String

, _ 

ByVal

 password 

As

 

String

As

 

Boolean

 

 

Dim

 ret 

As

 

Boolean

 = 

False

 

Dim

 xNode 

As

 XmlNode = 

Nothing

 

 

Try

 

  

' Get the XML document.

 

  xDoc = Helper.GetXmlFile() 
 
  

' Proceed if it contains something.

 

  

If

 xDoc IsNot 

Nothing

 

Then

 

 
    

' Look for the user.

 

    xNode = xDoc.SelectSingleNode("/Users/User[@name=

'" & _

 

      username & "

']")

 

 
    

' See if it found a match.

 

    

If

 xNode IsNot 

Nothing

 

Then

 

      

' Look for the password attribute to see if it matches.

 

      

If

 xNode.Attributes(

"password"

).Value.ToString = password 

Then

 _ 

        ret = 

True

 

    

End

 

If

 

  

End

 

If

 

 

Catch

 ex 

As

 Exception 

  

' Take appropriate action.

 

End

 

Try

 

 

' Return the results.

 

Return

 ret 

End

 

Function

  

Visual Basic

 Copy Code

Strona 9 z 24

Forms Authentication in SharePoint Products and Technologies (Part 2): Membership...

2008-02-22

http://msdn2.microsoft.com/en-us/library/bb975135(printer).aspx

background image

FindUsersByEmail Method 

The FindUsersByEmail function takes an e-mail address as a parameter and finds all users whose e-mail 
addresses start with that value. It returns a 

System.Web.Security.MembershipUserCollection

 

[ http://msdn2.microsoft.com/en-us/library/3xe386wc(printer).aspx ] object. 

public

 override MembershipUserCollection FindUsersByEmail(

string

  

emailToMatch, 

int

 pageIndex, 

int

 pageSize, out 

int

 totalRecords) 


  MembershipUserCollection ret = 

null

  XmlNodeList xList = 

null

 
  

// Initialize the number of records found.

 

  totalRecords = 0; 
 
  

try

 

  { 
 
    

// Get the XML document.

 

    xDoc = Helper.GetXmlFile(); 
 
    

// Proceed if it contains something.

 

    

if

 (xDoc != 

null

    { 
      

// Look for users with matching e-mail addresses.

 

      xList = xDoc.SelectNodes(

"/Users/User[starts-with(@email, '"

 +  

        emailToMatch + 

"')]"

); 

 
      

// See if there are any matches.

 

      

if

 ((xList != 

null

) && (xList.Count > 0)) 

      { 
        

// Set the number of records found.

 

        totalRecords = xList.Count; 
 
        

// Create a new membershipusercollection.

 

        ret = 

new

 MembershipUserCollection(); 

 
        

// Enumerate each match and add it to the collection.

 

        

foreach

 (XmlNode xNode 

in

 xList) 

        { 
          ret.Add(

new

 MembershipUser(Membership.Provider.Name, 

          xNode.Attributes[

"name"

].Value.ToString(),  

          xNode.Attributes[

"name"

].Value.ToString(),  

          xNode.Attributes[

"email"

].Value.ToString(),  

          

string

.Empty, 

string

.Empty, 

true

false

,  

          DateTime.Parse(xNode.Attributes[

"created"

].Value.ToString()), 

          DateTime.Today, DateTime.Today, DateTime.Today, 
          DateTime.MinValue)); 
        } 
      } 
    } 
  } 
  

catch

 

  { 
    

// Take appropriate action.

 

  } 
 
  

// Return the results.

 

  

return

 ret; 

C#

 Copy Code

Public

 

Overrides

 

Function

 FindUsersByEmail(

ByVal

 emailToMatch 

As

 _ 

String

ByVal

 pageIndex 

As

 

Integer

ByVal

 pageSize 

As

 

Integer

, _ 

ByRef

 totalRecords 

As

 

Integer

As

 _ 

System.Web.Security.MembershipUserCollection 
 

Dim

 ret 

As

 MembershipUserCollection = 

Nothing

 

Dim

 xList 

As

 XmlNodeList = 

Nothing

 

 

' Initialize the number of records found.

 

totalRecords = 0 
 

Try

 

  

' Get the XML document.

 

  xDoc = Helper.GetXmlFile() 
 

Visual Basic

 Copy Code

Strona 10 z 24

Forms Authentication in SharePoint Products and Technologies (Part 2): Members...

2008-02-22

http://msdn2.microsoft.com/en-us/library/bb975135(printer).aspx

background image

FindUsersByName Method 

The FindUserByName function works the same way that the FindUserByEmail function does; however, it takes 
a user name (full or partial) as a parameter. 

  

' Proceed if it contains something.

 

  

If

 xDoc IsNot 

Nothing

 

Then

 

 
    

' Look for users with matching e-mail addresses.

 

    xList = xDoc.SelectNodes("/Users/User[starts-with(@email, 

'" & _

 

      emailToMatch & "

')]")

 

 
    

' See if there are any matches.

 

    

If

 xList IsNot 

Nothing

 

AndAlso

 xList.Count > 0 

Then

 

      

' Set the number of records found.

 

      totalRecords = xList.Count 
 
      

' Create a new membershipusercollection.

 

      ret = 

New

 MembershipUserCollection() 

 
      

' Enumerate each match and add it to the collection.

 

      

For

 

Each

 xNode 

As

 XmlNode 

In

 xList 

        ret.Add(

New

 MembershipUser(Membership.Provider.Name, _ 

        xNode.Attributes(

"name"

).Value.ToString(), _ 

        xNode.Attributes(

"name"

).Value.ToString(), _ 

        xNode.Attributes(

"email"

).Value.ToString(), _ 

        

String

.Empty, 

String

.Empty, 

True

False

, _ 

        

Date

.Parse(xNode.Attributes(

"created"

).Value.ToString()), _ 

        

Date

.Today, 

Date

.Today, 

Date

.Today, 

Date

.MinValue)) 

      

Next

 

    

End

 

If

 

  

End

 

If

 

 

Catch

 ex 

As

 Exception 

  

' Take appropriate action.

 

End

 

Try

 

 

' Return the results.

 

Return

 ret 

End

 

Function

 

public

 override MembershipUserCollection FindUsersByName(

string

  

usernameToMatch, 

int

 pageIndex, 

int

 pageSize, out 

int

 totalRecords) 


 
  MembershipUserCollection ret = 

null

  XmlNodeList xList = 

null

 
  

// Initialize the number of records found.

 

  totalRecords = 0; 
 
  

try

 

  { 
 
    

// Get the XML document.

 

    xDoc = Helper.GetXmlFile(); 
 
    

// Proceed if it contains something.

 

    

if

 (xDoc != 

null

    { 
      

// Look for users with matching e-mail addresses.

 

      xList = xDoc.SelectNodes(

"/Users/User[starts-with(@name, '"

 +  

        usernameToMatch + 

"')]"

); 

 
      

// See if there are any matches.

 

      

if

 ((xList != 

null

) && (xList.Count > 0)) 

      { 
        

// Create a new membershipusercollection.

 

        ret = 

new

 MembershipUserCollection(); 

 
        

// Enumerate each match and add it to the collection.

 

        

foreach

 (XmlNode xNode 

in

 xList) 

        { 
          ret.Add(

new

 MembershipUser(Membership.Provider.Name, 

          xNode.Attributes[

"name"

].Value.ToString(), 

          xNode.Attributes[

"name"

].Value.ToString(), 

          xNode.Attributes[

"email"

].Value.ToString(), 

          

string

.Empty, 

string

.Empty, 

true

false

C#

 Copy Code

Strona 11 z 24

Forms Authentication in SharePoint Products and Technologies (Part 2): Members...

2008-02-22

http://msdn2.microsoft.com/en-us/library/bb975135(printer).aspx

background image

Functions Required in a Custom Role Provider

 

          DateTime.Parse(xNode.Attributes[

"created"

].Value.ToString()), 

          DateTime.Today, DateTime.Today, DateTime.Today,  
          DateTime.MinValue)); 
        } 
      } 
    } 
  } 
  

catch

 

  { 
    

// Take appropriate action.

 

  } 
 
  

// Return the results.

 

  

return

 ret; 

Public

 

Overrides

 

Function

 FindUsersByName( _ 

ByVal

 usernameToMatch 

As

 

String

ByVal

 pageIndex 

As

 

Integer

, _ 

ByVal

 pageSize 

As

 

Integer

ByRef

 totalRecords 

As

 

Integer

) _ 

As

 System.Web.Security.MembershipUserCollection 

 

Dim

 ret 

As

 MembershipUserCollection = 

Nothing

 

Dim

 xList 

As

 XmlNodeList = 

Nothing

 

 

' Initialize the number of records found.

 

totalRecords = 0 
 

Try

 

  

' Get the XML document.

 

  xDoc = Helper.GetXmlFile() 
 
  

' Proceed if it contains something.

 

  

If

 xDoc IsNot 

Nothing

 

Then

 

 
    

' Look for users with matching e-mail addresses.

 

    xList = xDoc.SelectNodes("/Users/User[starts-with(@name, 

'" & _

 

      usernameToMatch & "

')]")

 

 
    

' See if there are any matches.

 

    

If

 xList IsNot 

Nothing

 

AndAlso

 xList.Count > 0 

Then

 

      

'create a new membershipusercollection

 

      ret = 

New

 MembershipUserCollection() 

 
      

' Enumerate each match and add it to the collection.

 

      

For

 

Each

 xNode 

As

 XmlNode 

In

 xList 

        ret.Add(

New

 MembershipUser(Membership.Provider.Name, _ 

          xNode.Attributes(

"name"

).Value.ToString(), _ 

          xNode.Attributes(

"name"

).Value.ToString(), _  

          xNode.Attributes(

"email"

).Value.ToString(), _ 

          

String

.Empty, 

String

.Empty, 

True

False

, _ 

          

Date

.Parse(xNode.Attributes(

"created"

).Value.ToString()), _ 

          

Date

.Today, 

Date

.Today, 

Date

.Today, 

Date

.MinValue)) 

      

Next

 

    

End

 

If

 

  

End

 

If

 

 

Catch

 ex 

As

 Exception 

  

' Take appropriate action.

 

End

 

Try

 

 

' Return the results.

 

Return

 ret 

End

 

Function

 

Visual Basic

 Copy Code

The following sections describe functions (methods) that are required in a custom role provider.

 

GetRolesForUser Method

 

The GetRolesForUser function takes a user name as a parameter and returns a string array that contains the 
names of all groups to which the user belongs. 

Important: 

Strona 12 z 24

Forms Authentication in SharePoint Products and Technologies (Part 2): Members...

2008-02-22

http://msdn2.microsoft.com/en-us/library/bb975135(printer).aspx

background image

You must understand one limitation when you are writing code for the GetRolesForUser method. The 
role provider has a property named 

System.Web.Security.Roles.CacheRolesInCookie

 

[ http://msdn2.microsoft.com/en-us/library/f46tewk7(printer).aspx ] . Unfortunately, because of a bug 
that exists in ASP.NET at the time this article was written, that attribute is not honored, and the role 
provider's GetRolesForUser method is called every time. In practice, the GetRolesForUser method is 
called at least once for every user, for every page that he or she visits in the SharePoint site. Because 
of this, you should implement your own caching mechanism in the GetRolesForUser method in any 
custom role provider. 

public

 override 

string

[] GetRolesForUser(

string

 username) 


 
  

string

[] ret = 

null

  XmlNode xNode = 

null

  XmlNodeList xList = 

null

 
  

try

 

  { 
    

// Get the XML document.

 

    xDoc = Helper.GetXmlFile(); 
 
    

// Proceed if it contains something.

 

    

if

 (xDoc != 

null

    { 
      

// Look for the user.

 

      xNode = xDoc.SelectSingleNode(

"/Users/User[@name='"

 +  

        username + 

"']"

); 

 
      

// See if it found a match.

 

      

if

 (xNode != 

null

      { 
        

// Get the collection of group nodes.

 

        xList = xNode.ChildNodes[0].ChildNodes; 
 
        

// Resize the array based on number of child nodes.

 

        ret = 

new

 

string

[xList.Count]; 

 
        

// Enumerate all the groups in the Groups/Group subnodes 

 

        

// and add to return value.

 

        

for

 (

int

 cnt = 0;cnt < xList.Count;cnt++) 

        { 
          ret[cnt] = xList.Item(cnt).InnerText; 
        } 
      } 
    } 
  } 
  

catch

 

  { 
    

// Take appropriate action.

 

  } 
   
  

//Return the results.

 

  

return

 ret; 

C#

 Copy Code

Public

 

Overrides

 

Function

 GetRolesForUser(

ByVal

 username 

As

 

String

) _ 

As

 

String

() 

 

Dim

 ret 

As

 

String

() = 

Nothing

 

Dim

 xNode 

As

 XmlNode = 

Nothing

 

Dim

 xList 

As

 XmlNodeList = 

Nothing

 

 

Try

 

  

' Get the XML document.

 

  xDoc = Helper.GetXmlFile() 
 
  

' Proceed if it contains something.

 

  

If

 xDoc IsNot 

Nothing

 

Then

 

 
    

' Look for the user.

 

    xNode = xDoc.SelectSingleNode("/Users/User[@name=

'" & _

 

      username & "

']")

 

 
    

' See if it found a match.

 

    

If

 xNode IsNot 

Nothing

 

Then

 

Visual Basic

 Copy Code

Strona 13 z 24

Forms Authentication in SharePoint Products and Technologies (Part 2): Members...

2008-02-22

http://msdn2.microsoft.com/en-us/library/bb975135(printer).aspx

background image

RoleExists Method 

The RoleExists function takes a role name as a parameter. If the role exists, the function returns true
otherwise, it returns false

      

' Get the collection of group nodes.

 

      xList = xNode.ChildNodes(0).ChildNodes 
 
      

' Resize the array based on number of child nodes.

 

      

ReDim

 ret(xList.Count - 1) 

 
      

' Enumerate all the groups in the Groups/Group subnodes and 

 

      

' add to return value.

 

      

For

 cnt 

As

 

Integer

 = 0 

To

 xList.Count - 1 

        ret(cnt) = xList.Item(cnt).InnerText 
      

Next

 

    

End

 

If

 

  

End

 

If

 

 

Catch

 ex 

As

 Exception 

  

' Take appropriate action.

 

End

 

Try

 

 

' Return the results.

 

Return

 ret 

End

 

Function

 

public

 override 

bool

 RoleExists(

string

 roleName) 


  

bool

 ret = 

false

  XmlNode xNode = 

null

 
  

try

 

  { 
    

// Get the XML document.

 

    xDoc = Helper.GetXmlFile(); 
 
    

// Proceed if it contains something.

 

    

if

 (xDoc != 

null

    { 
      

// Look for the role.

 

      xNode = xDoc.SelectSingleNode(

"/Users/User[Groups/Group='"

 +  

        roleName + 

"']"

); 

 
      

// Return true if it found a match.

 

      

if

 (xNode != 

null

) ret = 

true

    } 
  } 
  

catch

 

  { 
    

// Take appropriate action.

 

  } 
 
  

// Return the results.

 

  

return

 ret; 

C#

 Copy Code

Public

 

Overrides

 

Function

 RoleExists(

ByVal

 roleName 

As

 

String

) _ 

As

 

Boolean

 

 

Dim

 ret 

As

 

Boolean

 = 

False

 

Dim

 xNode 

As

 XmlNode = 

Nothing

 

 

Try

 

  

' Get the XML document.

 

  xDoc = Helper.GetXmlFile() 
 
  

' Proceed if it contains something.

 

  

If

 xDoc IsNot 

Nothing

 

Then

 

 
    

' Look for the role.

 

    xNode = xDoc.SelectSingleNode("/Users/User[Groups/Group=

'" & _

 

      roleName & "

']")

 

 
    

' Return true if it found a match.

 

    

If

 xNode IsNot 

Nothing

 

Then

 ret = 

True

 

Visual Basic

 Copy Code

Strona 14 z 24

Forms Authentication in SharePoint Products and Technologies (Part 2): Members...

2008-02-22

http://msdn2.microsoft.com/en-us/library/bb975135(printer).aspx

background image

Registering the Custom Provider

 

  

End

 

If

 

 

Catch

 ex 

As

 Exception 

  

' Take appropriate action.

 

End

 

Try

 

 

' Return the results.

 

Return

 ret 

End

 

Function

 

Now that you understand the minimum required interfaces for a custom Membership and Role provider to work 
with MOSS 2007 and Windows SharePoint Services 3.0. your next step is to register the custom provider. 

To be able to use a custom provider, you must provide a strong name for the assembly it uses, and then register 
it in the global assembly cache. 

To strong name the assembly

 

1. In Microsoft Visual Studio, right-click the project name, and then click Properties

 

2. Click the Signing tab, and then select Sign the assembly

 

3. In the Choose a strong name key file list, click <New…>.

 

4. In the Create Strong Name Key dialog box, type a name for the key file, optionally type a password for the 

key file, and then click OK

When you compile the assembly, it builds with the strong name you provided.

 

You can now add your strong-named assembly to the global assembly cache in several ways, but the most 
common way is to use the 

gacutil.exe

 [ http://msdn2.microsoft.com/en-us/library/ex0ss12c(printer).aspx ] utility 

with the /i option. For more information about installing an assembly in the global assembly cache, see 

How to: 

Install an Assembly into the Global Assembly Cache

 [ http://msdn2.microsoft.com/en-us/library/dkkx7f79

(printer).aspx ] . 

After you register the assembly, you can extend a Web application into a zone and configure it to use your 
custom provider (described in the "Setting Up Forms Authentication" section of 

Forms Authentication in 

SharePoint Products and Technologies (Part 1): Introduction

 [ http://msdn2.microsoft.com/en-

us/library/bb975136(printer).aspx ] ).  

Following are two different example entries for the custom provider, reflecting that the Microsoft Visual C# and 
Microsoft Visual Basic assemblies were created with different names. In the examples, when you configure the 
authentication provider for the zone, the authentication method should be Forms, the membership provider 
name should be fbaUser, and the role manager name should be fbaRole

For the Visual C# assembly, use the following code.

 

For the Visual Basic assembly, use the following code.

 

Note: 

Because the strong name for an assembly that you compile on your computer will differ from the 
following example, the entries for your custom provider will also differ slightly; specifically the 
PublicKeyToken value will be different. 

    

<

membership

 defaultProvider="fbaUser"

>

 

      

<

providers

>

 

        

<

add

 name="fbaUser" type="Microsoft.IW.customUser, fbaSharp, Version=1.0.0.0, 

Culture=neutral, PublicKeyToken=b74d22b2d68547b5" 

/>

 

      

</

providers

>

 

    

</

membership

>

 

    

<

roleManager

 enabled="true" defaultProvider="fbaRole"

>

 

      

<

providers

>

 

        

<

add

 name="fbaRole" type="Microsoft.IW.customRole, fbaSharp, Version=1.0.0.0, 

Culture=neutral, PublicKeyToken=b74d22b2d68547b5" 

/>

 

      

</

providers

>

 

    

</

roleManager

>

 

Xml

 Copy Code

Strona 15 z 24

Forms Authentication in SharePoint Products and Technologies (Part 2): Members...

2008-02-22

http://msdn2.microsoft.com/en-us/library/bb975135(printer).aspx

background image

Debugging the Custom Provider

 

    

<

membership

 defaultProvider="fbaUser"

>

 

      

<

providers

>

 

        

<

add

 name="fbaUser" type="Microsoft.IW.customUser, fbaVB, Version=1.0.0.0, 

Culture=neutral, PublicKeyToken=ddd85720e6ace42b" 

/>

 

      

</

providers

>

 

    

</

membership

>

 

    

<

roleManager

 enabled="true" defaultProvider="fbaRole"

>

 

      

<

providers

>

 

        

<

add

 name="fbaRole" type="Microsoft.IW.customRole, fbaVB, Version=1.0.0.0, 

Culture=neutral, PublicKeyToken=ddd85720e6ace42b" 

/>

 

      

</

providers

>

 

    

</

roleManager

>

 

Xml

 Copy Code

Now that you have compiled the assembly, registered it, and configured a new zone to use the custom provider, 
you can use and (optionally) debug the provider. You would debug the custom provider in the same way that you 
would debug any other assembly that is used by another process. In this case, the process that is consuming the 
assembly is the w3wp.exe process. 

To debug the provider, you set the breakpoints you want in the code, and then attach to the w3wp.exe process.

 

To attach to the w3wp.exe

 

1. In Visual Studio, on the Tools menu, click Attach to Process to open the Attach to Process dialog box. 

 

2. In Available Processes, select the w3wp.exe process, and then click Select.

 

z

If you do not see any w3wp processes, select Show processes from all users

 

z

Also, if you have recently performed an iisreset, you must navigate to a page in the SharePoint site to 
create a new w3wp.exe process. Conversely, you might also find multiple w3wp.exe processes running. 
In such a case, select all w3wp.exe processes. This ensures that you attach to the process that is 
running your provider assembly. 

After you attach the w3wp.exe process, try logging on to the site. If you attached a breakpoint on the 
ValidateUser method of your membership provider, the breakpoint should be hit at the time you log on. If the 
breakpoint is not hit, you should try reattaching to the w3wp.exe process; another process might have started in 
connection with logging on to the site. The processes that are already attached appear dimmed (they are 
disabled); any new w3wp.exe processes appear black and are enabled to allow attachments. 

As you log on and navigate to pages, you should see breakpoints being hit in both the membership and role 
providers. To test your providers further, try using the People Picker to search for users and roles to add to 
SharePoint groups. This triggers breakpoints set in any of the membership and role provider methods. The 
number of times the breakpoints are hit varies on what is being done. When you use the People Picker, some 
methods such as FindUserByEmail can be called more than once. The number of times the role provider is 
called for any given page can vary also. The role provider is called for each item on the page that requires 
authentication. Some examples of items are navigation, list view Web Parts, or parts that are targeted at 
audiences. As a result, the GetRolesForUser method in the role provider is called one or more times for every 
user on each page view. 

Figure 1 and Figure 2 show the People Picker, configured to use the custom membership and role providers. 
Notice the account name for the entities matches the name attribute of the membership and role provider, as 
described earlier. 

Figure 1. Select People and Groups - membership provider name

 

 

Strona 16 z 24

Forms Authentication in SharePoint Products and Technologies (Part 2): Members...

2008-02-22

http://msdn2.microsoft.com/en-us/library/bb975135(printer).aspx

background image

Writing a Custom Forms Logon Page

 

 

Figure 2. Select People and Groups - role provider name

 

 

 

You may have scenarios that have special logon requirements that cannot be addressed by the default SharePoint 
forms logon page. For example, you may need to implement a second authentication factor such as Secure ID. 
Fortunately, because SharePoint Products and Technologies are built on top of ASP.NET 2.0, you can create a 
custom logon page with your own logon logic, and integrate it directly into MOSS or Windows SharePoint 
Services. 

Creating a Standard ASP.NET Web Site

 

Strona 17 z 24

Forms Authentication in SharePoint Products and Technologies (Part 2): Members...

2008-02-22

http://msdn2.microsoft.com/en-us/library/bb975135(printer).aspx

background image

The easiest way to create a custom logon page is to build a standard ASP.NET Web application. This enables you 
to create a site and easily debug the code behind for your forms logon page to ensure that it is working correctly. 
When you do this, you should configure your site to use the same membership and role provider that you intend 
to use with MOSS or Windows SharePoint Services. 

In the scenario for this article, we built a custom forms logon page for two reasons:

 

z

To show a custom policy condition that all site users must agree to before using the site.

 

z

To force certain users to use two-factor authentication, where the second factor is Secure ID.

 

We have created an ASPX page named customLogin.aspx and added it to the site. It contains the following:

 

z

Edit boxes for user name, password, and Secure ID number

 

z

A check box for a persistent authorization cookie 

 

z

A button to perform the logon process

 

Figure 3 shows how the page should look.

 

Figure 3. customLogin.aspx page

 

 

 

The web.config file was modified to do the following:

 

z

Use forms authentication and the fbaVB membership and role provider described earlier in this article, in 
the section 

Registering the Custom Provider

z

Deny access to anonymous users. 

 

Default.aspx was also added to the site when the project was created, and it is used in the site for testing.

 

Adding Code Behind the Logon Page

 

In the code behind the logon page, you should first validate the credentials. You can use the ValidateUser 
method of the Membership class to do this. If the credentials are valid, you can redirect the user to the site; 
otherwise, prompt them to reenter their credentials. 

if

 (Membership.ValidateUser(UserTxt.Text, PwdTxt.Text)) 


  

// Do Secure ID thing here. 

 

  

// Redirect the user to the requested page.

 

  FormsAuthentication.RedirectFromLoginPage(UserTxt.Text, 
    SaveChk.Checked); 

else

  

  StatusLbl.Text = 

"The credentials you entered are not valid. "

 +  

  

"Please try again."

C#

 Copy Code

If

 Membership.ValidateUser(UserTxt.Text, PwdTxt.Text) 

Then

 

Visual Basic

 Copy Code

Strona 18 z 24

Forms Authentication in SharePoint Products and Technologies (Part 2): Members...

2008-02-22

http://msdn2.microsoft.com/en-us/library/bb975135(printer).aspx

background image

For the Secure ID component, this example simulates only the process that a user or another system that 
requires additional authentication processing would go through. In this example, the page is hard-coded to 
prompt a specific user for a Secure ID number; in practice you could query a database or Web service (or 
whatever is appropriate) to determine how to process users. After prompting the user for the Secure ID 
credentials, it allows them to pass. 

Compiling the Application into an Assembly 

After you complete the simple ASP.NET application and have the forms logon page working correctly, your next 
step is to compile it and use it in MOSS or Windows SharePoint Services. You can use the Web site project that is 
new with Visual Studio 2005 to create the Web application. Alternatively, with Service Pack 1 of Visual Studio you 
can also create the Web project model that is similar to Visual Studio 2003, in that you can compile the project 
into a single assembly (.dll file).  

For the remainder of this section, we assume that you have compiled the project into a single assembly. If you 
use the Visual Studio 2005 style of Web site project, you can still make the application work, but you must use 
the 

ASP.NET Compilation Tool (Aspnet_compiler.exe)

 [ http://msdn2.microsoft.com/en-us/library/ms229863

(printer).aspx ] to precompile it. If you use that approach, ensure that you include the -u parameter when you 
compile so that you can update the assembly later. 

Copying Files and Registering the Assembly

 

Following are the steps to make the custom logon page available to MOSS or Windows SharePoint Services. 

 

To enable the use of the custom logon page by SharePoint Products and Technologies

 

1. Copy the logon page to the _layouts directory. The default path is C:\Program Files\Common 

Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS

2. Create a bin directory on the file system at the top-level (root) of your SharePoint Web application that will 

use your custom logon page. For the examples used in this article, the default directory would be in the path 
C:\Inetpub\wwwroot\wss\VirtualDirectories\ www.contoso.com80

. Then, copy the compiled assembly 

for the custom forms application to the bin directory that you created. 

3. Register the compiled assembly for the custom forms application in the global assembly cache. You can do 

this by using the 

Global Assembly Cache Tool (Gacutil.exe)

 [ http://msdn2.microsoft.com/en-

us/library/ex0ss12c(printer).aspx ] with the /i option. For more details about installing an assembly in the 
global assembly cache, see 

How to: Install an Assembly into the Global Assembly Cache

 

  

' Do Secure ID thing here. 

 

  

' Redirect the user to the requested page.

 

  FormsAuthentication.RedirectFromLoginPage(UserTxt.Text, _ 
    SaveChk.Checked) 

Else

 

  StatusLbl.Text = 

"The credentials you entered are not valid. "

 & _ 

    Please 

try

 again." 

End

 

If

 

// Instead of redirecting user3, ask for a Secure ID number. 

 

if

 (UserTxt.Text.ToLower() == 

"user3"


  

// Update UI.

 

  StatusLbl.Text = 

"Please enter your Secure ID number"

  LogPnl.Visible = 

false

  SecurePnl.Visible = 

true

C#

 Copy Code

' Instead of redirecting user3, ask for a Secure ID number. 

 

If

 UserTxt.Text.ToLower() = 

"user3"

 

Then

 

  

' Update UI.

 

  StatusLbl.Text = 

"Please enter your Secure ID number"

 

  LogPnl.Visible = 

False

 

  SecurePnl.Visible = 

True

 

End

 

If

 

Visual Basic

 Copy Code

Note: 

It is often easier to debug integrated solutions such as this if you use the latter approach and compile 
the Web application into a single DLL. 

Strona 19 z 24

Forms Authentication in SharePoint Products and Technologies (Part 2): Members...

2008-02-22

http://msdn2.microsoft.com/en-us/library/bb975135(printer).aspx

background image

[ http://msdn2.microsoft.com/en-us/library/dkkx7f79(printer).aspx ] . 

You must also update the web.config file for the Web application that is going to use the custom logon page. 

 

To update the web.config file for the Web application

 

1. Open the web.config file. 

 

2. In the authentication section, locate the forms element. 

 

3. In the forms element, change the loginUrl attribute to point to your new custom forms logon page; for 

example, _layouts/customLogin.aspx.  

4. Perform an iisreset.

 

You should now be able to use the custom logon page. 

 

Following is an example of what the page looks like when used with the Contoso site described earlier in this 
article.  

Figure 4. customLogin.aspx page on Contoso site

 

 

 

The interface is obviously not quite what one would expect from a SharePoint site. Fortunately, you can copy 
elements from the login.aspx page that is included with SharePoint Products and Technologies and paste them 
into the custom logon page to give it the SharePoint appearance.  

To modify the custom logon page to give it the SharePoint appearance

 

1. In the @ Page directive at the top of the page, add the following master page attribute.

 

2. Remove the following lines from near the top of the page.

 

3. Add the following lines directly below the @ Page directive.

 

Note: 

This step is important; things might not work correctly if you do not perform an iisreset

MasterPageFile="~/_layouts/simple.master" 

Html

 Copy Code

<!

DOCTYPE

 html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"  

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"

>

 

<

html

  

>

 

<

head

 id="Head1" runat="server"

>

 

    

<

title

>

Untitled Page

</

title

>

 

</

head

>

 

<

body

>

 

    

<

form

 id="form1" runat="server"

>

 

Html

 Copy Code

Html

 Copy Code

Strona 20 z 24

Forms Authentication in SharePoint Products and Technologies (Part 2): Members...

2008-02-22

http://msdn2.microsoft.com/en-us/library/bb975135(printer).aspx

background image

4. Remove the following lines from the bottom of the page.

 

5. Add the following line to the bottom of the page.

 

Try logging on to the site again; you may need to refresh the page. After you refresh, the page should look like 
Figure 5 and Figure 6. 

Figure 5. Updated customLogin.aspx on Contoso site - enter user name and password

 

 

 

Figure 6. Updated customLogin.aspx on Contoso site - enter Secure ID

 

 

<%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %>

  

<%

@ Register Tagprefix="SharePoint"  

Namespace="Microsoft.SharePoint.WebControls"  

Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral,  

PublicKeyToken=71e9bce111e9429c" 

%>

  

<%

@ Register Tagprefix="Utilities"  

Namespace="Microsoft.SharePoint.Utilities"  

Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, 

PublicKeyToken=71e9bce111e9429c" 

%>

  

<%@ Import Namespace="Microsoft.SharePoint" %>

 

<asp:Content ContentPlaceHolderId="PlaceHolderPageTitle" runat="server">

 

    

<

SharePoint:EncodedLiteral

 runat="server" text="

<%

$Resources:wss,login_pagetitle

%

>

" EncodeMethod='HtmlEncode'

/>

 

</asp:Content>

 

<asp:Content ContentPlaceHolderId="PlaceHolderTitleBreadcrumb" runat="server">

 

&nbsp; 

</asp:Content>

 

<asp:Content ContentPlaceHolderId="PlaceHolderPageTitleInTitleArea" runat="server">

 

    

<

SharePoint:EncodedLiteral

 runat="server" text="

<%

$Resources:wss,login_pagetitle

%

>

" EncodeMethod='HtmlEncode'

/>

 

</asp:Content>

 

<asp:Content ContentPlaceHolderId="PlaceHolderSiteName" runat="server"/>

 

<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">

 

</

form

>

 

</

body

>

 

</

html

>

 

Html

 Copy Code

</asp:Content>

 

Html

 Copy Code

Strona 21 z 24

Forms Authentication in SharePoint Products and Technologies (Part 2): Members...

2008-02-22

http://msdn2.microsoft.com/en-us/library/bb975135(printer).aspx

background image

Using Web Services with a Site Protected by Forms Authentication

 

 

 

Using the SharePoint Web services with a site secured with forms authentication works; however, the process 
differs from what you would do to use Web services for a site that is secured with Windows authentication. The 
primary difference is that you must obtain an authentication cookie and then use that cookie when accessing the 
Web services in the site protected by forms authentication. 

Fortunately, SharePoint Products and Technologies provide a new Web service that makes it easier to work in this 
scenario: the 

Authentication Web service

 [ http://msdn2.microsoft.com/en-us/library/aa979750(printer).aspx ] . 

It has a method named 

Login

 [ http://msdn2.microsoft.com/en-us/library/aa979746(printer).aspx ] that, when 

called, places an authentication cookie in the proxy's 

System.Web.Services.Protocols.HttpWebClientProtocol.CookieContainer

 [ http://msdn2.microsoft.com/en-

us/library/yta0fyx9(printer).aspx ] collection. That cookie can then be used in subsequent requests to other Web 
services in the site that is protected by forms authentication to authenticate the request. 

For purposes of creating your Web service proxies (such as 

adding Web references in Visual Studio

 

[ http://msdn2.microsoft.com/en-us/library/8dcbc50t(printer).aspx ] ), use a Windows authentication–protected 
site. In most cases, the Visual Studio Add Web Reference wizard does not work with a SharePoint site that is 
protected by forms authentication. For this example, we retrieve all of the lists in the site by using the 

Lists Web 

service

 [ http://msdn2.microsoft.com/en-us/library/ms774654(printer).aspx ] .  

To retrieve all lists in the site by using the Lists Web services

 

1. Start Visual Studio, and create a Windows Application project.

 

2. Add a button and text box to Form1. 

 

3. Change the text box properties so that Multiline is True and Scrollbars is Vertical

 

4. Resize the text box to fill the form under the button.

 

5. Add a Web reference to the Authentication Web service in the site that is protected by forms 

authentication; the Authentication Web service can be found in the path 
http://siteCollectionName/_vti_bin/authentication.asmx

. Name this Web reference fbaAuth.  

6. Add a second Web reference to the Lists Web service; it can be found in the path 

http://siteCollectionName/_vti_bin/lists.asmx

. Name this Web reference fbaLists

7. On the form, double-click Button1 to switch to code view and create an event handler for the click event. 

Create two variables for the Web service proxies, as shown in the following code. 

Note: 

By default, a zone that is configured to use forms authentication does not enable Client Integration 

features. This option, which is found on the Authentication Provider page in SharePoint Central 
Administration, must be turned on if you want to use the SharePoint Web services. When this option is 
turned off, SharePoint Products and Technologies also turn off support for remote interfaces, such as 
Web services. 

fbaAuth.Authentication auth = 

new

 fbaAuth.Authentication(); 

fbaLists.Lists lists = 

new

 fbaLists.Lists(); 

C#

 Copy Code

Strona 22 z 24

Forms Authentication in SharePoint Products and Technologies (Part 2): Members...

2008-02-22

http://msdn2.microsoft.com/en-us/library/bb975135(printer).aspx

background image

Additional Resources

 

8. Create a CookieContainer collection object for the Authentication class proxy; the authentication cookie 

will be stored in this container after calling the Login method.  

9. Call the Login method, and check the result from that call, as shown in the following code.

 

If the Login method succeeds, the proxy for the Authentication Web service has a valid authentication cookie in 
its CookieContainer collection. To reuse this cookie, just set the CookieContainer property for the Lists Web 
service proxy equal to the CookieContainer property of the Authentication Web service proxy. You can then 
make calls to the Lists Web service, and the authentication cookie will be used to authenticate and authorize the 
request. That means that when you make a Web service request, the cookie with authenticate and authorize the 
request based on the permissions of whatever account was used to create the authentication cookie. 

Following is the remainder of the code sample.

 

This concludes part 2 of this article series.

 

Next step: 

Forms Authentication in SharePoint Products and Technologies (Part 3): Forms Authentication vs. 

Windows Authentication

 [ http://msdn2.microsoft.com/en-us/library/bb977430(printer).aspx ] . 

Dim

 auth 

As

 

New

 fbaAuth.Authentication() 

Dim

 lists 

As

 

New

 fbaLists.Lists() 

Visual Basic

 Copy Code

auth.CookieContainer = 

new

 System.Net.CookieContainer(); 

auth.AllowAutoRedirect = 

true

fbaAuth.LoginResult lr = auth.Login(

"myUserName"

"myUserPassword"

); 

 

if

 (lr.ErrorCode == fbaAuth.LoginErrorCode.NoError) 

  

//Now we can talk to the Lists Web service.

 

C#

 Copy Code

auth.CookieContainer = 

New

 System.Net.CookieContainer() 

auth.AllowAutoRedirect = 

True

 

Dim

 lr 

As

 fbaAuth.LoginResult = auth.Login(

"myUserName"

, _ 

  

"myUserPassword"

 

If

 lr.ErrorCode = fbaAuth.LoginErrorCode.NoError 

Then

 

  

'Now we can talk to the Lists Web service.

 

End

 

If

 

Visual Basic

 Copy Code

if

 (lr.ErrorCode == fbaAuth.LoginErrorCode.NoError) 


   

// Now we can talk to the Lists Web service.

 

   lists.CookieContainer = auth.CookieContainer; 
   XmlNode xData = lists.GetListCollection(); 

C#

 Copy Code

If

 lr.ErrorCode = fbaAuth.LoginErrorCode.NoError 

Then

 

   

' Now we can talk to the Lists Web service.

 

   lists.CookieContainer = auth.CookieContainer 
   

Dim

 xData 

As

 XmlNode = lists.GetListCollection() 

End

 

If

 

Visual Basic

 Copy Code

For more information, see the following resources:

 

Strona 23 z 24

Forms Authentication in SharePoint Products and Technologies (Part 2): Members...

2008-02-22

http://msdn2.microsoft.com/en-us/library/bb975135(printer).aspx

background image

z

Forms Authentication in SharePoint Products and Technologies (Part 1): Introduction

 

[ http://msdn2.microsoft.com/en-us/library/bb975136(printer).aspx ]  

z

Forms Authentication in SharePoint Products and Technologies (Part 3): Forms Authentication vs. Windows 
Authentication

 [ http://msdn2.microsoft.com/en-us/library/bb977430(printer).aspx ]  

z

What Every SharePoint Administrator Needs to Know About Alternate Access Mappings Part 1 of 3

 

[ http://blogs.msdn.com/sharepoint/archive/2007/03/06/what-every-sharepoint-administrator-needs-to-
know-about-alternate-access-mappings-part-1.aspx ]  

z

What Every SharePoint Administrator Needs to Know About Alternate Access Mappings Part 2 of 3

 

[ http://blogs.msdn.com/sharepoint/archive/2007/03/19/what-every-sharepoint-administrator-needs-to-
know-about-alternate-access-mappings-part-2-of-3.aspx ]  

z

What Every SharePoint Administrator Needs to Know About Alternate Access Mappings Part 3 of 3

 

[ http://blogs.msdn.com/sharepoint/archive/2007/04/18/what-every-sharepoint-administrator-needs-to-
know-about-alternate-access-mappings-part-3-of-3.aspx ]  

z

Configuring Multiple Authentication Providers for SharePoint 2007

 

[ http://blogs.msdn.com/sharepoint/archive/2006/08/16/702010.aspx ]  

z

SharePoint Server 2007 Developer Portal

 [ http://msdn2.microsoft.com/en-us/office/aa905503.aspx ] 

 

z

How to Use ADFS to Turn MOSS 2007 into a Claims-Aware Application

 

[ http://blogs.msdn.com/sharepoint/archive/2007/02/15/how-to-use-adfs-to-turn-moss-2007-into-a-
claims-aware-application.aspx ]  

z

Plan Authentication Settings for Web Applications in Office SharePoint Server

 

[ http://technet2.microsoft.com/Office/en-us/library/d3e0e0fc-77b6-4109-87d6-53ad088db01d1033.mspx?
mfr=true ]  

z

How To: Encrypt Configuration Sections in ASP.NET 2.0 Using DPAPI

 [ http://msdn2.microsoft.com/en-

us/library/ms998280(printer).aspx ]  

z

How To: Encrypt Configuration Sections in ASP.NET 2.0 Using RSA

 [ http://msdn2.microsoft.com/en-

us/library/ms998283(printer).aspx ]  

z

User Security Attributes

 [ http://msdn2.microsoft.com/en-us/library/ms677943(printer).aspx ] 

 

z

Office Developer Center

 [ http://msdn.microsoft.com/office ] 

 

z

Windows SharePoint Services Developer Center

 [ http://msdn.microsoft.com/wss ] 

 

  

Community Content

  

Strona 24 z 24

Forms Authentication in SharePoint Products and Technologies (Part 2): Members...

2008-02-22

http://msdn2.microsoft.com/en-us/library/bb975135(printer).aspx