Was this page helpful?
Your feedback about this content is important. Let us know what you think.
Additional feedback?
1500 characters remaining
Export (0) Print
Expand All

Login

Visual Studio .NET 2003

The Login page uses a familiar logon process to verify a user's identity before he or she is allowed to enter the Fitch and Mather 7.0 application. The logon process is accomplished using a unique e-mail alias and a password with ASP.NET cookie-based authentication.

Implementation

The Login page uses the following ASP.NET file and C# code behind files:

Process Overview

The Login process is initiated in the User Services Layer (USL). A user enters an e-mail address and password (credentials). The credentials are then passed to the Business Logic Layer (BLL) via the FMStocks7.BLL.Account.VerifyLogin method (shown below) using a credentials object. The BLL then passes the credentials object to the Data Access Layer (DAL). The DAL then calls the Account_VerifyLogin stored procedure (SPROC) in the SQL Server database. The SPROC verifies the e-mail address against the accounts table. If the SPROC finds a matching account, it returns the password, Account ID, First Name, and Last Name. The hashed password is then verified against the salted hashed password retrieved from the database via the HashHelper.ComparePasswords method. The user is then authenticated using a cookie and is taken to the home page. If a match is not found in the database or the password is not valid, the user is prompted to reenter the login information. For further information on hashing and salting the password see, Password Credential Protection.

Architectural Decisions

Since Fitch and Mather 7.0 uses the common cookie-based Forms Authentication, user information can be stored and managed within a SQL Server database. Other potential options include:

  • Windows

    Windows authentication involves authenticating users against a Windows domain authority using Windows NT Challenge/Response (NTLM). It is commonly used on intranets.

  • Passport

    Passport authentication is a centralized authentication service provided by Microsoft that offers a single sign-in and core profile services for member sites.

To find source code that applies to specific layers in this topic, click the appropriate link:

BLL Source Code | DAL Source Code | SPROC

USL Source Code

The default location for the USL source code is the [Drive letter where Visual Studio .NET is installed]:\Program Files\Microsoft Visual Studio .NET 2003\Enterprise Samples\FMStocks7\Web\Login.aspx.cs.

Key Points

The SetAuthCookie method creates a Forms Authentication ticket that is added to the cookies collection. The user is then redirected to the home page. This could be done in a single step using the RedirectFromLoginPage method as it is in Duwamish 7.0. For more information, see Logon.

   if ( ! account.VerifyLogin( credentials, out firstName, out lastName, out accountID ) )
      ErrorMessage = LoginAttemptFailed + "<br>";
   else
      {
      // Save minimal state info and redirect to home page
      FormsAuthentication.SetAuthCookie( accountID.ToString(), false );
      Page.Response.Redirect("Home.aspx");
      }
Security Note   A deployed Internet application would post back login credentials using SSL to an HTTPS URL. Fitch and Mather 7.0 passes clear text passwords that anyone could obtain. Prior to deployment, it would be essential to address this serious security flaw. For further information see, Forms-Based Security.

BLL Source Code

The default location for the FMStocks7.BLL.Account.VerifyLogin method source code is the [Drive letter where Visual Studio .NET is installed]:\Program Files\Microsoft Visual Studio .NET 2003\Enterprise Samples\FMStocks7\BLL\Account.cs.

public bool VerifyLogin( Credentials credentials, out string firstName, out string lastName, out int accountID )
         {             
         DAL.Account account = new DAL.Account();

         try   {
            return account.VerifyLogin( credentials, out firstName, out lastName, out accountID );
            }
         finally
            {
            ServicedComponent.DisposeObject( account );
            }
         }

DAL Source Code

The default location for the FMStocks7.DAL.Account.VerifyLogin method source code is the [Drive letter where Visual Studio .NET is installed]:\Program Files\Microsoft Visual Studio .NET 2003\Enterprise Samples\FMStocks7\DAL\Account.cs.

public bool VerifyLogin( Credentials credentials, out string firstName, out string lastName, out int accountID )
{
   Debug.Assert( sproc == null );

   // Create parameter array

   SqlParameter[] parameters =
   {
      new SqlParameter( "@Email",     SqlDbType.NVarChar, 50 ), // 0
      new SqlParameter( "@Password",  SqlDbType.Binary,   24 ), // 1
      new SqlParameter( "@FirstName", SqlDbType.NVarChar, 30 ), // 2
      new SqlParameter( "@LastName",  SqlDbType.NVarChar, 30 ), // 3
      new SqlParameter( "@AccountID", SqlDbType.Int,       4 ), // 4
   };

   // Set parameter values and directions

   parameters[ 0 ].Value     = credentials.Email;
   parameters[ 1 ].Direction = ParameterDirection.Output;
   parameters[ 2 ].Direction = ParameterDirection.Output;
   parameters[ 3 ].Direction = ParameterDirection.Output;
   parameters[ 4 ].Direction = ParameterDirection.Output;

   // Run the stored procedure

   sproc = new StoredProcedure(  "Account_VerifyLogin", parameters );
   bool isValid = sproc.Run() == 0;

   if ( isValid )
   {

      // verify the salted password
      byte[] password = ( byte[] )parameters[ 1 ].Value;
      HashHelper hashHelper = new HashHelper();
      if (hashHelper.ComparePasswords(password, credentials.Password))
      {
         firstName = ( string )parameters[ 2 ].Value;
         lastName  = ( string )parameters[ 3 ].Value;
         accountID = ( int )parameters[ 4 ].Value;
      }
      else
      {
         isValid = false;
         firstName = string.Empty;
         lastName  = string.Empty;
         accountID = 0;
      }
   }
   else
   {
      firstName = string.Empty;
      lastName  = string.Empty;
      accountID = 0;
   }
   return isValid;
}


///   <summary>
///   Helper Class for retrieving working with the salted hashed password
///   </summary>
sealed internal class HashHelper 
{
private const int saltLength = 4;

// create salted password to save in Db
internal byte [] CreateDbPassword(byte[] unsaltedPassword)
{
   //Create a salt value
   byte[] saltValue = new byte[saltLength];
   RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
   rng.GetBytes(saltValue);
   
   return CreateSaltedPassword(saltValue, unsaltedPassword);
}

// create a salted password given the salt value
internal byte[] CreateSaltedPassword(byte[] saltValue, byte[] unsaltedPassword)
{
   // add the salt to the hash
   byte[] rawSalted  = new byte[unsaltedPassword.Length + saltValue.Length]; 
   unsaltedPassword.CopyTo(rawSalted,0);
   saltValue.CopyTo(rawSalted,unsaltedPassword.Length);
   
   //Create the salted hash         
   SHA1 sha1 = SHA1.Create();
   byte[] saltedPassword = sha1.ComputeHash(rawSalted);

   // add the salt value to the salted hash
   byte[] dbPassword  = new byte[saltedPassword.Length + saltValue.Length];
   saltedPassword.CopyTo(dbPassword,0);
   saltValue.CopyTo(dbPassword,saltedPassword.Length);

   return dbPassword;
}

// compare the hashed password against the stored password
internal  bool ComparePasswords(byte[] storedPassword, byte[] hashedPassword)
{
   if (storedPassword == null || hashedPassword == null || hashedPassword.Length != storedPassword.Length - saltLength)
      return false;

   // get the saved saltValue
   byte[] saltValue = new byte[saltLength];
   int saltOffset = storedPassword.Length - saltLength;
   for (int i = 0; i < saltLength; i++)
      saltValue[i] = storedPassword[saltOffset + i];

   byte[] saltedPassword = CreateSaltedPassword(saltValue, hashedPassword);

   // compare the values
   return CompareByteArray(storedPassword, saltedPassword);
}

// compare the contents of two byte arrays
private bool CompareByteArray(byte[] array1, byte[] array2)
{
   if (array1.Length != array2.Length)
      return false;
   for (int i = 0; i < array1.Length; i++)
   {
      if (array1[i] != array2[i])
         return false;
   }
   return true;
}
}

SPROC

The default location for the SQL stored procedure is the [Drive letter where Visual Studio .NET is installed]:\Program Files\Microsoft Visual Studio .NET 2003\Enterprise Samples\FMStocks7\Database\SQLScripts\Sprocs.Sql.

---------------------------------------------------------------------------------
-- Name:    Account_VerifyLogin
--
-- Purpose: Verify that supplied login credentials are valid,
--          return account number to caller
--
-- Returns: Zero if the credentials provided by the user are valid; otherwise -1.
---------------------------------------------------------------------------------

create procedure Account_VerifyLogin
   @EMail     nvarchar( 50 ),
   @Password  binary( 24 ) output,
   @FirstName nvarchar( 30 ) output,
   @LastName  nvarchar( 30 ) output,
   @AccountID int output
as
   select   @AccountID = [AccountID],
         @FirstName = [FirstName],
         @LastName  = [LastName],
         @Password = [Password]
         
   from    Accounts
   where   Email = @EMail
   
   if @@ROWCOUNT = 0
      return -1

   return 0
go

See Also

Behind the Scenes of Fitch and Mather 7.0 | Architectural Overview | Behind the Scenes of Duwamish 7.0

Show:
© 2015 Microsoft