asp.net MVC authentication with Shibboleth - c#

Shibboleth is a SSO Authentication that is added to IIS as a "plugin".
After a user has done a Login there are Headers showing the Shibboleth Session:
ShibSessionID
ShibIdentityProvider
eppn
affiliation
entitlement
unscopedaffiliation
...more
So i can extract username and roles from the Headers.
so far so fine.
Question:
How can I implement a handler that does read the headers and set the status that a user is authorized?
Idea is to use the
[Authorize]
Attribute and the Method
Roles.IsUserInRole.
All from the Headers, no Database, no User Management.
Update
Implementation According to the Answer from #Pharylon
In this Update there is nothing new, just a help for the copy&past friends.
Of course you have to adjust the properties and Header fieldnames according to your Shibboleth Setup.
File: ShibbolethPrincipal.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Security.Principal; //GenericPrincipal
namespace Shibboleth
{
public class ShibbolethPrincipal : GenericPrincipal
{
public string username
{
get { return this.Identity.Name.Replace("#ksz.ch", ""); }
}
public string firstname
{
get { return HttpContext.Current.Request.Headers["givenName"]; }
}
public string lastname
{
get { return HttpContext.Current.Request.Headers["surname"]; }
}
public string phone
{
get { return HttpContext.Current.Request.Headers["telephoneNumber"]; }
}
public string mobile
{
get { return HttpContext.Current.Request.Headers["mobile"]; }
}
public string entitlement
{
get { return HttpContext.Current.Request.Headers["eduzgEntitlement"]; }
}
public string homeOrganization
{
get { return HttpContext.Current.Request.Headers["homeOrganization"]; }
}
public DateTime birthday
{
get
{
DateTime dtHappy = DateTime.MinValue;
try
{
dtHappy = DateTime.Parse(HttpContext.Current.Request.Headers["dateOfBirth"]);
}
finally
{
}
return dtHappy;
}
set {}
}
public ShibbolethPrincipal()
: base(new GenericIdentity(GetUserIdentityFromHeaders()), GetRolesFromHeader())
{
}
public static string GetUserIdentityFromHeaders()
{
//return HttpContext.Current.Request.Headers["eppn"];
return HttpContext.Current.Request.Headers["principalName"];
}
public static string[] GetRolesFromHeader()
{
string[] roles = null;
//string rolesheader = HttpContext.Current.Request.Headers["affiliation"];
string rolesheader = HttpContext.Current.Request.Headers["eduzgEntitlement"];
if (rolesheader != null)
{
roles = rolesheader.Split(';');
}
return roles;
}
}
}
File: ShibbolethController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Shibboleth
{
public class ShibbolethController : Controller
{
protected new ShibbolethPrincipal User
{
get
{
return (base.User as ShibbolethPrincipal) ?? null; //CustomPrincipal.GetUnauthorizedPrincipal();
}
}
}
}
File: Global.asax
void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
var ctx = HttpContext.Current;
var principal = new ShibbolethPrincipal();
HttpContext.Current.User = principal;
}
Using examples:
namespace itservices.Controllers
{
[Authorize] //examples : [Authorize(Roles="Administrators")], [Authorize(Users="Alice,Bob")]
public class PasswordMailController : ShibbolethController
{
if(User.IsInRole("staff"))
{

You'll want to create a method in Global.asax.cs that has the following signature
protected void Application_PostAuthenticateRequest()
{
//Your code here.
}
This will be called automatically before almost anything else is done (MVC will call this method if it exists, you don't have to "turn it on" anywhere), and this is where you need to set the Principal. For instance, let's assume you have a header called RolesHeader that has a comma separated value of roles and another header called UserId that has (duh) the user ID.
Your code, without any error handling, might look something like:
protected void Application_PostAuthenticateRequest()
{
var rolesheader = Context.Request.Headers["RolesHeader"];
var userId = Context.Request.Headers["UserId"];
var roles = rolesheader.Split(',');
var principal = new GenericPrincipal(new GenericIdentity(userId), roles);
Context.User = principal;
}
It's the Principal/Identity that the [Authorize] attribute uses, so setting it here at the beginning of the request lifecycle means the [Authorize] attribute will work correctly.
The rest of this is optional, but I recommend it:
I like to create my own custom classes that implement IPrincipal and IIdentity instead of using the GenericPrincipal and GenericIdentity, so I can stuff more user information in it. My custom Principal and Identity objects then have much more rich information, such as branch numbers or email addresses or whatever.
Then, I create a Controller called BaseController that has the following
protected new CustomPrincipal User
{
get
{
return (base.User as CustomPrincipal) ?? CustomPrincipal.GetUnauthorizedPrincipal();
}
}
This allows me to access all my rich, custom Principal data instead of just what's defined in IPrincipal. All of my real controllers then inherit from BaseController instead of directly from Controller.
Obviously, when using a custom Principal like this, in the Application_PostAuthenticateRequest() method, you'd set the Context.User to be your CustomPrincipal instead of a GenericPrincipal.

Related

Unable to list data from Role-based HttpGet request using ASP.NET Core identity

I have created HttpGet following ASP.NET Core identity but I'm having problem to get data based on role. In my program I have three roles user, manager, admin. And in GET method only login user should be able to list data where admin can list all the company data but user and manager can see/list only those company data where they are associate.
Here is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Web.Data;
using Web.Features.Roles;
using Web.Features.Company;
using Web.Features.Shared;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Web.Controllers
{
[ApiController]
[Route("api/company")]
public class CompanyController : ControllerBase
{
private readonly DataContext dataContext;
public CompanyController(DataContext dataContext)
{
this.dataContext = dataContext;
}
private static Expression<Func<Company, CompanyDto>> MapToDto()
{
return x => new CompanyDto
{
Name = x.Name,
Active = x.Active,
CompanyPopulation = x.CompanyPopulation,
Id = x.Id
};
}
[HttpGet]
[Authorize]
public IEnumerable<CompanyDto> GetAll()
{
var result = dataContext
.Set<Company>()
.Select(MapToDto()).ToList();
if (User.IsInRole("admin")) //I tried to check using IsInRole method but it doesn't work
{
return result;
}
return null;
}
}
}
CompanyDto
public class CompanyDto
{
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public int CompanyPopulation { get; set; }
}
I tried to check using IsInRole method but it doesn't work
Firstly, you can try to get list of role names of current user using following code snippet, and check if you assigned role(s) to current user.
var user = await _userManager.GetUserAsync(User);
var roles = await _userManager.GetRolesAsync(user);
If you do not assign user with specified role, please refer to the following code snippet to add the specified user to the named role.
var user = await _userManager.GetUserAsync(User);
await _userManager.AddToRoleAsync(user, "role_name_here");
Test Result

Authentication in Nancy MVC

I am using a third party authentication procedure to authorize my pages in Nancy. I have tried to do it in MVC and it is successfull but I cannot reproduce the same results in Nancy.
Here is what I am doing:
MVC-Startup:
using Microsoft.Owin;
using Owin;
using Passport.Auth;
[assembly: OwinStartup(typeof(TestAuthorization.Startup))]
namespace TestAuthorization
{
public partial class Startup:StartupBase
{
public void Configuration(IAppBuilder app)
{
base.Configuration(app);
}
public override string IdentityServerUri
{
get { return "https://test.ThirdParty.URL/identity"; }
}
public override string RedirectUri
{
get { return "https://localhost:4443"; }
}
public override string ApplicationClientId
{
get { return "local.fox.company"; }
}
}
}
Nancy-Startup:
using Microsoft.Owin;
using Owin;
using Passport.Auth;
using Nancy.Owin;
[assembly: OwinStartupAttribute(typeof(AgreementManagementTool.Startup))]
namespace AgreementManagementTool
{
public class Startup: StartupBase
{
public void Configuration(IAppBuilder app)
{
app.UseNancy();
base.Configuration(app);
}
public override string IdentityServerUri
{
get { return "https://test.ThirdParty.URL/identity"; }
}
public override string RedirectUri
{
get { return "https://localhost:4443"; }
}
public override string ApplicationClientId
{
get { return "local.fox.company"; }
}
}
}
Now here is my program.cs for Nancy only:
class Program
{
static void Main(string[] args)
{
var uri = "https://+:4443"; //"https://localhost:4443";
Console.WriteLine("Starting Nancy on " + uri);
using (WebApp.Start<Startup>(uri))
{
Console.WriteLine("\n\nServer listening at {0}. Press enter to stop", uri);
Console.ReadLine();
return;
}
}
}
Now all I have to do is write [Authorize] on top of my Nancy module and it should work just like MVC.
MVC-Controller:
using System.Web.Mvc;
namespace TestAuthorization.Controllers
{
[Authorize]
public class HomeController : Controller
{
public ActionResult Index()
{
//this.RequiresMSOwinAuthentication();
return View();
}
}
}
Nancy-Module:
using Nancy;
using AgreementManagementTool.Views.Home.Model;
using System.Web.Mvc;
namespace AgreementManagementTool.Modules
{
[Authorize]
public class HomeModule : NancyModule
{
public HomeModule()
: base("/home")
{
Get["/"] = parameters =>
{
//this.RequiresMSOwinAuthentication(); // Not working
//this.RequiresAuthentication(); // Not working
HomeModel result = new HomeModel();
result.ResultImport = "I am testing AUthentication";
return View["index", result];
};
}
}
}
When I browse to the page after running the MVC application run successfully and authorization work successfull, but nancy doesnot show anything.
I have tried to use this.RequiresAuthentication(); but it throws an exception:
Nancy-Exception
Just to mention that I have no idea how the third party authentication process works, I just have to use it.
In MVC I have recieved the sample and it is working fine, why is it not working the same in nancy.
Nancy does not use [Authorize] attribute.
Have a look sample using Identity server for Nancy specific implementation.
If you are just using Nancy, try this to replace both your startups (using Owin.Security.Cookies):
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
namespace TestOwin
{
public class Startup
{
// OWIN Configuration goes here
public static void Configuration(IAppBuilder app)
{
var cookieOptions = new CookieAuthenticationOptions() { AuthenticationMode = AuthenticationMode.Active };
var nancyCfg = new NancyOptions();
nancyCfg.Bootstrapper = new NBootstrapper();
app.UseStaticFiles();
app.UseCookieAuthentication(cookieOptions);
app.UseNancy(nancyCfg);
}
}
}
And in your NancyBootstrapper (Here is where you can redirect users to a login page):
public class NBootstrapper : DefaultNancyBootstrapper
{
//Nancy Bootstrapper overrides go here
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
// We don't call "base" here to prevent auto-discovery of
// types/dependencies
}
protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context)
{
base.ConfigureRequestContainer(container, context);
// Here we register our user mapper as a per-request singleton.
// As this is now per-request we could inject a request scoped
// database "context" or other request scoped services.
container.Register<IUserMapper, UserValidator>();
}
protected override void RequestStartup(TinyIoCContainer requestContainer, IPipelines pipelines, NancyContext context)
{
// At request startup we modify the request pipelines to
// include forms authentication - passing in our now request
// scoped user name mapper.
//
// The pipelines passed in here are specific to this request,
// so we can add/remove/update items in them as we please.
var formsAuthConfiguration =
new FormsAuthenticationConfiguration()
{
RedirectUrl = "~/login",
UserMapper = requestContainer.Resolve<IUserMapper>(),
};
FormsAuthentication.Enable(pipelines, formsAuthConfiguration);
}
}
And then you can use:
this.RequiresMSOwinAuthentication();
on your modules.
Hope this helps! My problem is trying to also get the same authentication to work inside Nancy and outside too, with SignalR...

Dynamically apply values to the Roles property for the AuthorizedAttribute class

We have an application that have three levels of roles. Depending on which role a user is in determines what they have access to. The users are in activedomain groups. The client has asked that test groups be created for test and the original groups are to be used in production. We're using web.config transforms to distinguish the groups in each environment.
Previous to this the AuthorizedAttribute class was set up for a save method as follows:
[AuthorizeUsers(Roles = "Group A, Administration")]
public SomeInformation Post([FromBody]SomeInformation infodata)
{
return _manager.SaveInfo(infodata);
}
The Group A and Administration are production. In test there is now Group A - Test and Administration - Test.
Is there a way to pull from the web.config file and assign it to the Roles property?
We tried the following:
private string Env = Settings.Default.GroupA + ", " + Settings.Default.Administration
[AuthorizeUsers(Roles = Env)]
But it gacked. The AuthorizeUsers class inherits from AuthorizeAttribute. Any ideas?
You need to make a custom authorize attribute that is read by a custom authorization filter. If you register the filter globally, it will run at the right time and recognize when your controllers and action methods are decorated with the custom authorize attribute.
ConfigAuthorizeAttribute
using System;
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)]
public class ConfigAuthorizeAttribute : Attribute
{
public string RolesConfigurationKey { get; set; }
public string UsersConfigurationKey { get; set; }
}
ConfigAuthorizationFilter
using System;
using System.ComponentModel;
using System.Configuration;
using System.Linq;
using System.Security.Principal;
using System.Web;
using System.Web.Mvc;
public class ConfigAuthorizationFilter : AuthorizeAttribute
{
// Hide users and roles, since we aren't using them here.
[Obsolete("Not applicable in this class.", true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public string Roles { get; set; }
[Obsolete("Not applicable in this class.", true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public string Users { get; set; }
public override void OnAuthorization(AuthorizationContext filterContext)
{
// Pass the current action descriptor to the AuthorizeCore
// method on the same thread by using HttpContext.Items
filterContext.HttpContext.Items["ActionDescriptor"] = filterContext.ActionDescriptor;
base.OnAuthorization(filterContext);
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
var actionDescriptor = httpContext.Items["ActionDescriptor"] as ActionDescriptor;
if (actionDescriptor != null)
{
var authorizeAttribute = this.GetConfigAuthorizeAttribute(actionDescriptor);
// If the authorization attribute exists
if (authorizeAttribute != null)
{
// Ensure the user is logged in
IPrincipal user = httpContext.User;
if (!user.Identity.IsAuthenticated)
{
return false;
}
// Lookup the Web.Config settings
string users = ConfigurationManager.AppSettings[authorizeAttribute.UsersConfigurationKey];
string roles = ConfigurationManager.AppSettings[authorizeAttribute.RolesConfigurationKey];
string[] usersSplit = SplitString(users);
string[] rolesSplit = SplitString(roles);
// Run the authorization based on the configuration values
if (usersSplit.Length > 0 && !usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase))
{
return false;
}
if (rolesSplit.Length > 0 && !rolesSplit.Any(user.IsInRole))
{
return false;
}
return true;
}
}
return true;
}
private ConfigAuthorizeAttribute GetConfigAuthorizeAttribute(ActionDescriptor actionDescriptor)
{
ConfigAuthorizeAttribute result = null;
// Check if the attribute exists on the action method
result = (ConfigAuthorizeAttribute)actionDescriptor
.GetCustomAttributes(attributeType: typeof(ConfigAuthorizeAttribute), inherit: true)
.SingleOrDefault();
if (result != null)
{
return result;
}
// Check if the attribute exists on the controller
result = (ConfigAuthorizeAttribute)actionDescriptor
.ControllerDescriptor
.GetCustomAttributes(attributeType: typeof(ConfigAuthorizeAttribute), inherit: true)
.SingleOrDefault();
return result;
}
internal static string[] SplitString(string original)
{
if (string.IsNullOrEmpty(original))
{
return new string[0];
}
return (from piece in original.Split(new char[] { ',' })
let trimmed = piece.Trim()
where !string.IsNullOrEmpty(trimmed)
select trimmed).ToArray<string>();
}
}
Usage
First, register your global filter.
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new ConfigAuthorizationFilter());
filters.Add(new HandleErrorAttribute());
}
}
Then, decorate your controllers and actions with the ConfigAuthorizeAttribute, specifying where to lookup the users and roles in the configuration file.
[ConfigAuthorize(RolesConfigurationKey = "authorization:groups:home-about")]
public ActionResult About()
{
ViewBag.Message = "Your app description page.";
return View();
}
Then, add the configuration file settings to appSettings in web.config.
<appSettings>
<add key="authorization:groups:home-about" value="Group A, Administration"/>
</appSettings>

How to perform Redirect in helper class for MVC controller?

I have some common code that runs at the beginning of multiple controller actions. I would like to refactor that code into a static class to promote reuse of that code block.
The code checks for a variable, looks for a cookie and if a condition is met, the code should redirect to another page (controller/action).
The problem is that everything works properly (including the cookie lookup) but the Redirect does not fire. The code passes over the redirect and the redirect never happens.
What is the proper way to redirect in a helper class?
Here is the code now:
This line is not working: myController.HttpContext.Response.Redirect(redirectURL);
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MyProject.WebUI
{
public static class SessionValidationHelper
{
// helper class to encapsulate common routines to check for missing session data
public static void SessionIdRequired(string id, Controller myController)
{
if (id == null || id == "")
{
// check cookie
if (myController.ControllerContext.HttpContext.Request.Cookies.AllKeys.Contains("cookiename"))
{
// if a session cookie was found, send to the registration recovery page
string sessionGuidCookieValue = "";
sessionGuidCookieValue = myController.ControllerContext.HttpContext.Request.Cookies["cookiename"].Value;
// check if GUID/SessionID exists in persistent cache
// send to Session Recovery
//myController.HttpContext.Response.RedirectToRoute("SessionRecovery", new { Controller = "SessionRecovery", Action = "Index", id = sessionGuidCookieValue });
string redirectURL = #"~/SessionRecovery/Index/" + sessionGuidCookieValue;
// this code isn't working
myController.HttpContext.Response.Redirect(redirectURL);
}
}
}
}
}
You can create an attribute that is a filter and decorate the action with it:
(I have placed the code you provided in the attribute)
public class SessionValidationAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext.Result == null)
{
var id = filterContext.RouteData.Values["id"] as string;
if (id == null || id == "")
{
// check cookie
if (filterContext.Controller.ControllerContext
.HttpContext.Request.Cookies.AllKeys
.Contains("cookiename"))
{
// if a session cookie was found,
// send to the registration recovery page
string sessionGuidCookieValue = "";
sessionGuidCookieValue = filterContext.Controller
.ControllerContext.HttpContext.Request
.Cookies["cookiename"].Value;
// check if GUID/SessionID exists in persistent cache
// send to Session Recovery
string redirectURL = #"~/SessionRecovery/Index/"
+ sessionGuidCookieValue;
// this code isn't working
filterContext.Result = new RedirectResult(redirectURL);
}
}
}
}
public abstract bool CanAccessResource(User user);
}
And on your action you do this:
[SessionValidationAttribute]
public ActionResult MyAction()
{
// code of the action
}
Or if you want to apply it to all actions inside a class:
[SessionValidationAttribute]
public class MyController : Controller
{
// code of the class, containing all actions
}
Or if you want to apply this GLOBALLY (be carefull with this):
In your Application class (the one inheriting System.Web.HttpApplication, you can do this:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalFilters.Filters.Add(new SessionValidationAttribute());
// register routes
}
}

Review my ASP.NET Authentication code

I have had some problems with authentication in ASP.NET. I'm not used most of the built in authentication in .NET.
I gotten some complaints from users using Internet Explorer (any version - may affect other browsers as well) that the login process proceeds but when redirected they aren't authenticated and are bounced back to loginpage (pages that require authentication check if logged in and if not redirect back to loginpage). Can this be a cookie problem?
Do I need to check if cookies are enabled by the user?
What's the best way to build authentication if you have a custom member table and don't want to use ASP.NET login controls?
Here my current code:
using System;
using System.Linq;
using MyCompany;
using System.Web;
using System.Web.Security;
using MyCompany.DAL;
using MyCompany.Globalization;
using MyCompany.DAL.Logs;
using MyCompany.Logging;
namespace MyCompany
{
public class Auth
{
public class AuthException : Exception
{
public int StatusCode = 0;
public AuthException(string message, int statusCode) : base(message) { StatusCode = statusCode; }
}
public class EmptyEmailException : AuthException
{
public EmptyEmailException() : base(Language.RES_ERROR_LOGIN_CLIENT_EMPTY_EMAIL, 6) { }
}
public class EmptyPasswordException : AuthException
{
public EmptyPasswordException() : base(Language.RES_ERROR_LOGIN_CLIENT_EMPTY_PASSWORD, 7) { }
}
public class WrongEmailException : AuthException
{
public WrongEmailException() : base(Language.RES_ERROR_LOGIN_CLIENT_WRONG_EMAIL, 2) { }
}
public class WrongPasswordException : AuthException
{
public WrongPasswordException() : base(Language.RES_ERROR_LOGIN_CLIENT_WRONG_PASSWORD, 3) { }
}
public class InactiveAccountException : AuthException
{
public InactiveAccountException() : base(Language.RES_ERROR_LOGIN_CLIENT_INACTIVE_ACCOUNT, 5) { }
}
public class EmailNotValidatedException : AuthException
{
public EmailNotValidatedException() : base(Language.RES_ERROR_LOGIN_CLIENT_EMAIL_NOT_VALIDATED, 4) { }
}
private readonly string CLIENT_KEY = "9A751E0D-816F-4A92-9185-559D38661F77";
private readonly string CLIENT_USER_KEY = "0CE2F700-1375-4B0F-8400-06A01CED2658";
public Client Client
{
get
{
if(!IsAuthenticated) return null;
if(HttpContext.Current.Items[CLIENT_KEY]==null)
{
HttpContext.Current.Items[CLIENT_KEY] = ClientMethods.Get<Client>((Guid)ClientId);
}
return (Client)HttpContext.Current.Items[CLIENT_KEY];
}
}
public ClientUser ClientUser
{
get
{
if (!IsAuthenticated) return null;
if (HttpContext.Current.Items[CLIENT_USER_KEY] == null)
{
HttpContext.Current.Items[CLIENT_USER_KEY] = ClientUserMethods.GetByClientId((Guid)ClientId);
}
return (ClientUser)HttpContext.Current.Items[CLIENT_USER_KEY];
}
}
public Boolean IsAuthenticated { get; set; }
public Guid? ClientId {
get
{
if (!IsAuthenticated) return null;
return (Guid)HttpContext.Current.Session["ClientId"];
}
}
public Guid? ClientUserId {
get {
if (!IsAuthenticated) return null;
return ClientUser.Id;
}
}
public int ClientTypeId {
get {
if (!IsAuthenticated) return 0;
return Client.ClientTypeId;
}
}
public Auth()
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
IsAuthenticated = true;
}
}
public void RequireClientOfType(params int[] types)
{
if (!(IsAuthenticated && types.Contains(ClientTypeId)))
{
HttpContext.Current.Response.Redirect((new UrlFactory(false)).GetHomeUrl(), true);
}
}
public void Logout()
{
Logout(true);
}
public void Logout(Boolean redirect)
{
FormsAuthentication.SignOut();
IsAuthenticated = false;
HttpContext.Current.Session["ClientId"] = null;
HttpContext.Current.Items[CLIENT_KEY] = null;
HttpContext.Current.Items[CLIENT_USER_KEY] = null;
if(redirect) HttpContext.Current.Response.Redirect((new UrlFactory(false)).GetHomeUrl(), true);
}
public void Login(string email, string password, bool autoLogin)
{
Logout(false);
email = email.Trim().ToLower();
password = password.Trim();
int status = 1;
LoginAttemptLog log = new LoginAttemptLog { AutoLogin = autoLogin, Email = email, Password = password };
try
{
if (string.IsNullOrEmpty(email)) throw new EmptyEmailException();
if (string.IsNullOrEmpty(password)) throw new EmptyPasswordException();
ClientUser clientUser = ClientUserMethods.GetByEmailExcludingProspects(email);
if (clientUser == null) throw new WrongEmailException();
if (!clientUser.Password.Equals(password)) throw new WrongPasswordException();
Client client = clientUser.Client;
if (!(bool)client.PreRegCheck) throw new EmailNotValidatedException();
if (!(bool)client.Active || client.DeleteFlag.Equals("y")) throw new InactiveAccountException();
FormsAuthentication.SetAuthCookie(client.Id.ToString(), true);
HttpContext.Current.Session["ClientId"] = client.Id;
log.KeyId = client.Id;
log.KeyEntityId = ClientMethods.GetEntityId(client.ClientTypeId);
}
catch (AuthException ax)
{
status = ax.StatusCode;
log.Success = status == 1;
log.Status = status;
}
finally
{
LogRecorder.Record(log);
}
}
}
}
A classic case of over-engineered Authentication mechanism and on top of it the design is bad.
Exceptions should be out of Auth class but reside in same namespace. Can you imagine how .Net framework would look if Microsoft had created exceptions like this. Always keep it simple, stupid (KISS). It seems you need modular code. Try to be simple yet modular.
Your authentication Client-Keys are static magic-values and you're shipping them with your assemblies. Use SecureString instead of readonly string. Anybody can get hold of it using Reflector. How do you sustain change ad security?
Your code directly refers Current HttpContext object when in fact you could have passed the reference of current context object in client-code that will use this.
RequireClientOfType is int[] - why in the world you want to do this ? I believe it could have been an enum or an immutable struct if at all ever needed.
You are already using FormsAuthentication in your Login() and Logout() which is sufficient to replace your entire Auth. Why do you want to re-invent the wheel if ultimately you are going to use FormsAuthnetication to take care of Auth.
And yes if you cannot revise this design please use FxCop/StyleCop at least to avoid spaghetti-code.
Also you could make class Auth as static and expose functionalities like FormsAuthentication does. And also rename it from Auth to Authentication.
This is a prime candidate for http://thedailywtf.com/
Try using built-in asp.net Forms Authentication (Membership).
You can learn from these videos:
Link1 and Link2
If you want to customize it watch this video:
Link

Categories