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
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.
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
Jeffrey T. Fritz is a Microsoft MVP in ASP.Net and an ASPInsider with more than a decade of experience writing and delivering large scale multi-tenant web applications. After building applications with ASP, ASP.NET and now ASP.NET MVC, he is crazy about building web sites for all sizes on any device. You can read more from Jeffrey on his personal blog or on Twitter at @csharpfritz.