Integrating ASP.NET with a 3rd Party Security Service

After the recent spate of security issues from large retail companies, especially those that caused me to have to get a new credit card issued, I’ve been thinking more and more about how to secure my ASP.NET web applications. Fortunately, the new OWIN-based security in ASP.NET 4.5.1 can fill that need. In this article, I’m going to show you how I integrated the ASP.NET security model with the security services available in the Telerik Platform.

ASP.NET Identity was released in March 2014, and it provides everything you need to build a highly secure repository of information about your users for authentication. User objects are easy to extend to include extra properties that you want to store with security information in your project. In my case, I already have a repository and simply want to connect that authentication feature to our services so that I can have the same users log in to a mobile application and a web application.

Connecting to my Repository

I will be storing all of my security information, files, and other relational data in Telerik Backend Services. Telerik Backend Services is a cloud storage provider that allows me to connect and operate with a RESTful API, and in my ASP.NET web application I will be using its .NET library to connect to the data source. You can download a copy of the .NET SDK for Telerik Backend Services from the Platform at: https://platform.telerik.com/#downloads Make sure you choose the BackEnd Services option in the middle of the screen, and you will be able to download the .NET SDK from the next screen.

From my Backend Services project on the web, I grabbed the API Key and the Master API key for use in my application. This is important, as we need to master key to retrieve information about roles to which a user belongs. Don’t forget to also grab the application id for your project. You can find this embedded in the URL when viewing your Backend Services Project:

https://platform.telerik.com/#workspace/A GUID YOU CAN IGNORE/backendservices/YOUR APP ID AS A GUID/nav/SOME OTHER NONSENSE

After unpacking and adding the SDK to my ASP.NET project, I added entries to my web.config for the API keys that I downloaded. I took the old school approach and named my keys after the codename for Telerik Backend Services:

<appSettings>
  <add key="Everlive.ApiKey" value="MY API KEY" />
  <add key="Everlive.MasterKey" value="MY MASTER KEY" />
  <add key="Everlive.AppId" value="ANOTHER GUID HERE" />
</appSettings>

This has the added benefit of being able to swap out AppSetting values when I upload this project to an Azure website. In this way, I can have a development repository to work with, and a production repository to utilize that’s managed and locked down by my operations team. Fancy!

I like to use a Repository pattern for interacting with the data repository. I wrote a BaseRepository class that configures the connection and makes it available to inheriting classes:

public abstract class BaseRepository<T> : IRepository<T> where T : DataItem
{

    private static readonly string _EverliveKey;
    private static readonly string _EverliveMaster;
    protected static readonly Guid _AppId;

    static BaseRepository()
    {

      _EverliveKey = ConfigurationManager.AppSettings["Everlive.ApiKey"];
      _EverliveMaster = ConfigurationManager.AppSettings["Everlive.MasterKey"];
      _AppId = Guid.Parse(ConfigurationManager.AppSettings["Everlive.AppId"]);

    }
}

Listing 1 – Acquiring Configuration Keys in a static constructor

The static constructor loads up the configuration keys in memory and then I can write an instance constructor that makes the connection to the repository

public BaseRepository()
{

  var settings = new EverliveAppSettings()
  {
    ApiKey = _EverliveKey,
    OutboundDateTimeProcessing = DateTimeValuesProcessing.ConvertToUtc
  };

  this._Everlive = new EverliveApp(settings);
}

private readonly EverliveApp _Everlive;
protected EverliveApp Everlive { get { return _Everlive; } }

Listing 2 – Make the connection to the Telerik Backend Services

Registering New Users

To make the delivery and management of authenticated users from the platform easier, I’ve subclassed the default User object provided in the Telerik SDK to allow the roles and authorization key for a logged-in user to be passed around our code. My new User class looks like this:

public class TelerikUser : User
{

  public TelerikUser(User from)
  {
    this.Id = from.Id;
    this.Username = from.Username;
    this.Email = from.Email;
  }

  public AccessToken AccessToken { get; set; }

  public Tuple<Guid,string> Role { get; set; }

}

Listing 3 – An extended user class to encapsulate access and role information

I can now configure a registration method in a UserRepository that inherits from BaseRepository and looks like:

public class TelerikUserRepository : BaseRepository<TelerikUser>
  {
    public void RegisterUser(string userName, string email, string password)
    {

          var newUser = new Telerik.Everlive.Sdk.Core.Model.System.User() {
            Username = userName,
            Email = email,
            Password = password
          };
          Everlive.WorkWith().Users().Create(newUser).ExecuteSync();

    }
  }

Listing 4 – User Registration with Telerik Security

This allows me to replace the default register page’s CreateUser_Click event with a very simple call to the new TelerikUserRepository:

protected void CreateUser_Click(object sender, EventArgs e)
{

  var repo = new TelerikUserRepository();
  try
  {
    repo.RegisterUser(Email.Text, Email.Text, Password.Text);
    Response.Redirect("//");
  }
  catch (Exception ex)
  {
    ErrorMessage.Text = ex.Message;
  }
}

Listing 5 – Page Event Handler for Register.aspx

This is a much better practice because it keeps all of the logic of the interaction outside of the webform, somewhere that can be isolated and refactored without changing the web project.

Authentication and Authorization

2

Authentication should not be a difficult thing for my code to accomplish, as I am handing that responsibility off to the Telerik service. Therefore, I delegate authentication to the Telerik service in a simple Authenticate method in the TelerikUserRepository:

public TelerikUser Authenticate(string userName, string password)
{

  // Login to the service and capture the access token if successful
  var thisAccessToken = Everlive.WorkWith().Authentication().Login(userName, password).ExecuteSync();
  if (thisAccessToken == null) return null;

  // Get the user object
  var result = Everlive.WorkWith().Users().GetMe().ExecuteSync();

  // Get the roles of the user
  var role = EverliveMaster.WorkWith().Application(_AppId).Roles().GetById(result.RoleId).ExecuteSync();

  // Format the TelerikUser for use on the web
  var outUser = new TelerikUser(result);
  outUser.AccessToken = thisAccessToken;
  outUser.Role = new Tuple<Guid, string>(role.Id, role.Name);

  return outUser;

}

Listing 6 – Authentication method to retrieve Telerik User Information

The first call in this method performs the actual authentication and returns an access key that can be used to sign further interactions with the repository. Next, the code retrieves the actual user object and then reaches through an EverliveMaster object to get the role the user is assigned to. The EverliveMaster object is where that extra MasterKey comes into play. We need to reach in and read the role information using elevated permissions, and I wrapped that access in a simple property like so:

protected EverliveAccount EverliveMaster
{
  get
  {
    if (_EverliveMasterApp == null)
    {
      _EverliveMasterApp = new EverliveAccount(
        new EverliveAccountSettings(
          AuthorizationStrategy.MasterKey, _EverliveMaster
        )
      );
    }
    return _EverliveMasterApp;
  }
}

Listing 7 – Getting elevated access to the Telerik Security Repository

I can now change the Login method in the /Account/Login.aspx page of my ASP.NET application to authenticate using this technique:

protected void LogIn(object sender, EventArgs e)
{
  if (IsValid)
  {

    // Validate the user password
    Models.TelerikUser outUser = null;
    var repo = new Models.TelerikUserRepository();
    outUser = repo.Authenticate(Email.Text, Password.Text);
    if (outUser != null)
    {

      var id = ConfigureClaims(outUser);

      var ctx = Request.GetOwinContext();
      var authenticationManager = ctx.Authentication;
      authenticationManager.SignIn(id);
      RedirectToReturnUrl(Request.QueryString["ReturnUrl"], Response);
    }
    else
    {
      FailureText.Text = "Invalid username or password.";
      ErrorMessage.Visible = true;
    }

  }
}

Listing 8 – Login event handler on the Login.aspx page

After verifying that the data submitted is valid, I call my repository’s authenticate method. If a user object is not returned, authentication failed. Otherwise, I configure the ClaimsIdentity appropriately with the name and Role assigned to the user. Finally, I grab the Owin AuthenticationManager.aspx) and SignIn with the identity created. The claims for this identity are as follows:

private ClaimsIdentity ConfigureClaims(TelerikUser outUser)
{
  var claims = new List<Claim>();
  claims.Add(new Claim(ClaimTypes.Name, outUser.Username));
  claims.Add(new Claim(ClaimTypes.Email, outUser.Email));
  claims.Add(new Claim(ClaimTypes.UserData, outUser.AccessToken.Token));
  claims.Add(new Claim(ClaimTypes.Role, outUser.Role.Item2));

  var id = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
  return id;
}

Listing 9 – Security claims for a user

Now I have my access token and roles available to any other method that can access the OWIN Context and they can verify authorization to resources. For example, I can now authorize all data requests back to the Telerik repository with this syntax:

var user = HttpContext.Current.Request.GetOwinContext().Authentication.User;
AccessToken myToken = null; 
if (user != null && user.Claims.Any(c => c.Type == ClaimTypes.UserData))
{
  myToken = new AccessToken(user.Claims.First(c => c.Type == ClaimTypes.UserData).Value);
  _Everlive.AccessToken = myToken;
}

return _Everlive.WorkWith().Data<T>();

Listing 10 – Authorizing access to data

If the user is not authenticated, then no access token is appended to the request. If the user is authenticated, then the access token is passed along to the Telerik Repository where an authorization check is made to ensure the user is allowed access to the data.

Additionally, by passing the Role information in as a claim to the OWIN identity object, I can resume using the ASP.NET 1.0 technique with web.config authorization markup to protect folders and file locations in my application:

<location path="Admin">
  <system.web>
    <authorization>
      <deny users="?" />
      <allow roles="Admin" />
    </authorization>
  </system.web>
</location>

Listing 11 – Web.Config markup to only allow Admin users access to the Admin folder

Rocking it old school

With that, our website is secure and my markup for authorization is old school and just as cool as ever.

Summary

In this post, I showed you how to use Telerik Security Services to secure an ASP.NET website with the new OWIN security model. By swapping out the inherent logic for calls to the remote service, I was able to centralize the authentication and authorization functionality for my user base. I hope this serves as a template so that you can easily integrate your existing security repositories with the new OWIN security model.

Are you having problems with OWIN and the new pipeline features in ASP.NET? Sound off in the discussion below and we’ll bring you more about the future of ASP.NET in future posts.

Comments