Security

In this section, you will learn about the security features of Gazel.

Authentication Flow

Authentication is enabled by default. All requests from outside of your host project require an authentication token. When you run your AppHost.Service project, you will see an Authorization text box. It takes an authentication token in an HTTP authorization header.

If you want to disable authentication, you will need to configure it from Global.asax.

public class Global : ServiceApplication { protected override AuthenticationFeature Authentication(AuthenticationConfigurer configure) { return configure.NoAuthentication(); } }

Implementing Authentication Mechanism

When authentication is enabled, Gazel requires an implementation of ISessionManager interface. Once you implement it in any module, it will be registered automatically.

ISessionManager interface

ISessionManager is basically responsible for finding an ISession instance from a given AppToken. An example implementation is as follows;

public class SessionManager : ISessionManager { ... public ISession GetSession(AppToken appToken) { return context.Query<Sessions>().SingleByAppToken(appToken); } ... }

If you don't want GetSession method to be public, you can implement ISessionManager explicitly.

public class SessionManager : ISessionManager { ... private ISession GetSession(AppToken appToken) { ... } ... ISession ISessionManager.GetSession(AppToken appToken) { return GetSession(appToken); } ... }

ISession interface

Just like a web session, ISession represents a session of a user. Unlike a web session it is not stored in cookies nor in a file. ISessionManager creates it before every request and puts it in the request scope. After every request, session instances are destroyed along with its request.

public interface ISession { AppToken Token { get; } // The unique identifier of the session string Host { get; } // A text to represent client's host if needed IAccount Account { get; } // The account that this session is attached to void Validate(); // The method that is invoked before every request }

Here is an example implementation of ISession interface;

public class Session : ISession { private readonly IRepository<Session> repository; private readonly IModuleContext context; protected Session() { } public Session(IRepository<Session> repository, IModuleContext context) { this.repository = repository; this.context = context; } public virtual int Id { get; protected set; } public virtual AppToken Token { get; protected set; } public virtual Account Account { get; protected set; } public virtual string Host { get; protected set; } public virtual DateTime ExpireTime { get; protected set; } protected internal virtual Session With(Account account) { Account = account; Token = context.System.NewAppToken(); Host = context.Request.Host.ToString(); ExpireTime = context.System.Now.AddMinutes(30); repository.Insert(this); return this; } protected virtual void Validate() { if(ExpireTime < context.System.Now) { throw new AuthenticationRequiredException(); } } //implemented explicitly to return IAccount IAccount ISession.Account => Account; //implemented explicitly to make Validate protected void ISession.Validate() => Validate(); } public class Sessions : Query<Session> { public Sessions(IModuleContext context) : base(context) { } internal Session SingleByAppToken(AppToken appToken) { return SingleBy(s => s.AppToken == appToken); } }

IAccount interface

IAccount represents an account in your application.

public interface IAccount { int Id { get; } // Id of the account string DisplayName { get; } // A text to represent the account // The method that is invoked before every request if authorization is enabled bool HasAccess(IResource resource); }

Here is an example implementation of IAccount interface;

public class Account : IAccount { private readonly IRepository<Account> repository; private readonly IModuleContext context; protected Account() { } public Account(IRepository<Account> repository, IModuleContext context) { this.repository = repository; this.context = context; } public virtual int Id { get; protected set; } public virtual string FullName { get; protected set; } protected internal virtual Account With(string fullName) { FullName = fullName; repository.Insert(this); return this; } //implemented explicitly to map DisplayName on FullName string IAccount.DisplayName => FullName; //give access to everything for this sample bool IAccount.HasAccess(IResource resource) => true; } public class Accounts : Query<Account> { ... }

Accessing User Session

When you finish setting up your authentication mechanism, you can access user session using IModuleContext.Session property.

public class CompanyManager { private readonly IModuleContext context; public CompanyManager(IModuleContext context) { this.context = context; } public Company CreateCompany(string name) { return context.New<Company>().With( name: name, createdBy: context.Session.Account.DisplayName ); } }

Overriding Current Session

You may need to override the session in some specific cases. You can use IModuleContext.OverrideSession method as shown below.

public class Company { private readonly IRepository<Company> repository; private readonly IModuleContext context; protected Company() { } public Company(IRepository<Company> repository, IModuleContext context) { this.repository = repository; this.context = context; } protected internal virtual Company With(string name, string address) { //this level uses current session //assume we need an admin session here var adminSession = context.New<Session>().WithAdminRights(); context.OverrideSession(adminSession, () => { //here context.Session returns adminSession //do some admin stuff }); //back to current session Name = name; Address = address; repository.Insert(this); return this; } }

OverrideSession can be used nested. After each OverrideSession block ends, previous session becomes accessible.

... protected internal virtual Company With(string name, string address) { //this level uses current session context.OverrideSession(innerSession, () => { //this level uses inner session context.OverrideSession(nestedSession, () => { //this level uses nested session }); }); //this level uses current session return this; } ...