Appendix

Validate username and password against a database

When the username and password are passed from the application to the built-in OAuth server, the OAuth server will by default authenticate them against the users predefined in DefaultUserStore.cs. For security concern, you might want the OAuth server to authenticate against the users stored in a database instead of DefaultUserStore.cs.

To do that, you will need to modify the UserValidator.cs file so that every time when a token is requested, the OAuth server will connect to the database and authenticate the user. (Another option is to populate and cache users from the database to the user list of the DefaultUserStore.cs file. See this section for details.)

Step 1: Add namespaces. Suppose a SQL Server database will be connected. The following namespaces need to be added.

using System;
using Microsoft.Data.SqlClient;
using SnapObjects.Data;
using SnapObjects.Data.SqlServer;

To connect with a database type different from SQL Server, add the following namespace accordingly.

using SnapObjects.Data.MySql;
using SnapObjects.Data.Oracle;
using SnapObjects.Data.PostgreSql;
using SnapObjects.Data.Odbc;

Step 2: Add the connection string to connect to the database and authenticate the username and password against the users in the database.

Below is the complete scripts of the UserValidator.cs file (suppose the users are stored in the "users" table in a SQL Server database).

using System.Threading.Tasks;
using IdentityServer4.Models;
using IdentityServer4.Validation;
using System;
using Microsoft.Data.SqlClient;
using SnapObjects.Data;
using SnapObjects.Data.SqlServer;

namespace ServerAPIs
{
    
    public class UserValidator : IResourceOwnerPasswordValidator
    {
        public Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
        {
            //To validate username and password against a SQLServer database, set the connection string as below
            String Constr = @"Data Source=172.16.1.10,1433;Initial Catalog=pb_cloud;Integrated Security=False;User ID=sa;Password=1234;Pooling=True;Min Pool Size=0;Max Pool Size=100;MultipleActiveResultSets=False;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite";
            SqlServerDataContext _context = new SqlServerDataContext(new SqlConnection(Constr));
            string sql = "select username from users where isValid = 1 and username = '" + context.UserName + "' and password = '" + context.Password + "'";
            var users = _context.SqlExecutor.Select<DynamicModel>(sql);
            
            if (users.Count >= 1)
            {
                //If validation is successful, returns the user
                context.Result = new GrantValidationResult(subject: context.UserName, authenticationMethod: "custom");
            }
            else
            {
                //If validation failed, returns the error
                context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Incorrect username or password.");
            }
            
            return Task.CompletedTask;
        }
    }
}

Validate username and password against an LDAP server

When the username and password are passed from the application to the built-in OAuth server, the OAuth server will by default authenticate them against the users predefined in DefaultUserStore.cs. For security concern, you might want the OAuth server to authenticate against the users stored in an LDAP server instead of DefaultUserStore.cs.

To do that,

Step 1: Install the Microsoft.Windows.Compatibility NuGet package first, as the following sample scripts make references to this package.

Step 2: Modify the UserValidator.cs file.

Here is a sample script of the UserValidator.cs class that connects to an LDAP server to authenticate the user credentials.

using IdentityServer4.Models;
using IdentityServer4.Validation;
using System;
using System.DirectoryServices;
using System.Threading.Tasks;
namespace ServerAPIs
{
    public class UserValidator: IResourceOwnerPasswordValidator
    {
        public Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
        {
            string strError = string.Empty;
            bool lb_succes = false;
            string ls_server = "ldap.appeon.com";
            string ls_user = context.UserName;
            string ls_pass = context.Password;
            using (DirectoryEntry adsEntry = new DirectoryEntry("LDAP://" + ls_server, ls_user, ls_pass, AuthenticationTypes.Secure))
            {
                using (DirectorySearcher adsSearcher = new DirectorySearcher(adsEntry))
                {
                    adsSearcher.Filter = "(SAMAccountName=" + ls_user + ")";
                    adsSearcher.PropertiesToLoad.Add("cn");
                    try
                    {
                        SearchResult adsSearchResult = adsSearcher.FindOne();
                        if (adsSearchResult == null)
                        {
                            lb_succes = false;
                        }
                    }
                    catch (Exception ex)
                    {
                        strError = ex.Message;
                    }
                    finally
                    {
                        adsEntry.Close();
                    }
                }
            }
            if (strError.Length == 0)
            {
                lb_succes = true;
            }
            
            if (lb_succes)
            {
                context.Result = new GrantValidationResult(subject: context.UserName, authenticationMethod: "custom");
            }
            else
            {
                context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Incorrect username,password or server.");
            }
            return Task.CompletedTask;
        }
        
    }
}

Test the OAuth server

Test the OAuth server by sending a request which includes the grant type, scope, client ID, client secret, and user credentials.

  1. Right click in the code block of a method, and select Run Test(s) from the popup menu.

    The Web API Tester is launched.

  2. In the Web API Tester, click the plus (+) sign to create a new request:

    URL: http://localhost:5000/connect/token

    HTTP method: POST

    Content-Type: application/x-www-form-urlencoded

    Request (when grant type is client credentials):

    grant_type=client_credentials&scope=scope.readaccess&client_id=YourClientIdThatCanOnlyRead&client_secret=yoursecret1

    Or request (when grant type is resource owner password):

    grant_type=password&scope=scope.readaccess&client_id=YourClientIdThatCanOnlyRead&client_secret=yoursecret1&username=user&password=pass
  3. Click Send to send the request, and the OAuth server returns the token information if validation is successful.