In a WinForms application I want to keep the currently logged user in memory through out the applications life. So that in subsequent user actions I could check the permission against the user. Another option is to store the user information in a text file locally but it seems not safe.
User Log In verification code
private void ValidateUser()
{
var hashEntered = Encryption.GetHash(_View.UserID, _View.Password); //hash of the salted password entered by user.
var User = _DataService.GetUser(_View.UserID); //user trying to log in
if (user != null)
{
var hashInDB = user.PassWord;
if (hashEntered != hashInDB)
{
MessageBox.Show("Invalid password");
}
else
{
_MainView.show();
}
}
else
{
MessageBox.Show("Invalid user name");
}
}
So for the MainView, the currently logged user should be available.
How to keep the current user object in memory until the program exits?
I would suggest a singleton.
public class UserSession{
private static volatile User currentUser;
private static object syncRoot = new Object();
private UserSession() {}
public static User GetUser(){
if (currentUser == null) throw new Exception("Not logged in.");
return currentUser;
}
public static void Login(User user){
if (currentUser != null) throw new Exception("Already logged in");
lock(syncRoot){
currentUser = user;
}
}
public static void Logout(){
lock(syncRoot){
currentUser = null;
}
}
}
You can store the user data in System.Threading.Thread.CurrentPrincipal of type IPrincipal which also has a property called Identity (which is of type IIdentity). The difference between those two is that you just store security associated data of user (suppose permission or roles ) in principal and other data in Identity. you can use Microsoft's already existing implementation of those two interfaces or you can build them by your own. here is an example
class CustomPrincipal : IPrincipal
{
public IEnumerable<string> Roles { get; set; }
public IIdentity Identity { get; set; }
public bool IsInRole(string role)
{
// check user for appropriate roles
return false;
}
}
class CustomIdentity : IIdentity
{
public int UserId { get; set; }
public string AuthenticationType { get; set; }
public bool IsAuthenticated
{
get { return !string.IsNullOrWhiteSpace(Name); }
}
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
CustomIdentity identity = new CustomIdentity
{
UserId = 1,
Name = "user1"
};
CustomPrincipal principal = new CustomPrincipal
{
Identity = identity,
Roles = new List<string> { "admin", "superAdmin" }
};
System.Threading.Thread.CurrentPrincipal = principal;
}
}
Principal is the part of ExecutionContext so it will be copied from thread to thread. so even if you were to start a new thread or task or any asynchronous job that would try to get the Principal it would be there
you would use this code than to retrieve the user principal
System.Threading.Thread.CurrentPrincipal as CustomPrincipal
and this to get user identity
System.Threading.Thread.CurrentPrincipal.Identity as CustomIdentity
Related
I am trying to create a Session Wrapper class for ASP.NET Core Razor Pages.
In traditional ASP.NET web forms my singleton session wrapper class used to work normally, but i am not sure if in .net core it will work the same or the same class will be shared across all requests.
I want to store specific information for each request (session) and not to be shared across all requests.
i created the following wrapper :
public class MySession
{
//the ISession interface mandatory
public ISession Session { get; set; }
private static MySession instance;
private MySession() {
}
public static MySession Instance
{
get
{
if (instance == null)
{
instance = new MySession();
}
return instance;
}
}
//properties
public User User
{
get => SessionHelper.SessionExtensions.Get<User>(this.Session, "User");
set => SessionHelper.SessionExtensions.Set<User>(this.Session, "User", value);
}
public bool LoggedIn
{
get => SessionHelper.SessionExtensions.Get<bool>(this.Session, "LoggedIn");
set => SessionHelper.SessionExtensions.Set<bool>(this.Session, "LoggedIn", value);
}
}
and from my page model (Login.cshtml.cs) i am doing the following :
public void OnGet()
{
MySession.Instance.Session = HttpContext.Session;
}
and i am accessing the session and storing and retrieving information perfectly as expected for example :
public ActionResult OnPostAuthenticate()
{
string username = Request.Form["username"];
string password = Request.Form["password"];
User user = this.userDataManager.GetUserAuthentication(username, password);
if (user != null)
{
//authentication success
//save session variables
MySession.Instance.User = user;
MySession.Instance.LoggedIn = true;
return new JsonResult(new { success = true });
}
else
{
return new JsonResult(new { success = false });
}
}
And as i said everything work perfect, but i want to know if it is SAFE to use the session this way or my requests will be messed up and the same session information will be shared across all requests ?
There are many questions about custom authentication in ASP.NET, but none of them answers how to fully integrate it with ASP.NET mechanics.
I want to make a web application for system which already exists and have users.
Let's create new ASP.NET MVC project. I choose "No authentication" to make it from scratch (because I need to read from custom tables, custom hashing function etc, so I'll go this way).
I'll use IIdentity and IPrincipal interfaces to carry logged in user in HttpContext.
public class Identity : IIdentity
{
public Identity(string name)
{
Name = name;
IsAuthenticated = true;
}
public string Name { get; private set; }
public string AuthenticationType { get; set; }
public bool IsAuthenticated { get; set; }
}
public class Principal : IPrincipal
{
public Principal(string email)
{
Identity = new Identity(email);
}
public IIdentity Identity { get; private set; }
public bool IsInRole(string role)
{
return false;
}
}
I'll create SessionsController which will create and destroy Session. Session will contain Id of logged in user.
public class UserManager
{
public bool Authenticate(WorkerDTO user, string password)
{
using (var context = new DatabaseContext())
{
var user = context.Users.SingleOrDefault(w => w.Email == user.Email);
if (user != null)
{
// compute SHA512 of password with salt
var hash = Hash(user.Password);
if (user.HashPassword == hash)
return true;
}
return false;
}
}
}
public class SessionsController : Controller
{
private IDatabaseManager _dbManager;
private UserManager _userManager;
public SessionsController(IDatabaseManager dbManager, UserManager userManager)
{
_dbManager = dbManager;
_userManager = userManager;
}
[HttpGet]
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(UserLoginViewModel model)
{
var user = _dbManager.Give(new WorkerByEmail(model.Email));
if(user != null && _userManager.Authenticate(user, model.Password))
{
// create session
Session["UserId"] = worker.Id;
// assign User property
User = new Principal(worker.Email);
return RedirectToAction("Index", "Home");
}
return RedirectToAction("New");
}
public ActionResult Logout()
{
Session.Remove("UserId");
User = null;
return View();
}
}
Application won't remember that User = new Principal(worker.Email);.
So, if I want to make use of sessions, I want to tell my ASP.NET application that User behind UserId carried by session (probably a cookie) each request is the logged in user. In Global.asax I have available events with reasonable names:
AuthenticateRequest
PostAuthenticateRequest.
Unfortunately, Session is unavailable in these events.
How to make use of ASP.NET in my application? Maybe Session isn't the way to go?
Should I create new IPrincipal object and attach to User prop each request?
And why User property isn't null at start? Should I set it to null on logout?
My project got pages with [Authorize] where user have to log in to visit those pages.
Upon successful login with same userid and password as in database, the current users id get stored in session. But how do I do I authenticate/allow user to visit pages with [Authorize]?
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(User u)
{
if (ModelState.IsValid) //this is check validity
{
using (UserEntities db = new UserEntities())
{
var v = db.Users.Where(a=>a.UserName.Equals(u.UserName) && a.Password.Equals(u.Password)).FirstOrDefault();
if (v != null)
{
Session["LoggedUserID"] = u.Id.ToString();
Session["UserFullname"] = u.Name.ToString();
return RedirectToAction("AfterLogin");
}
}
}
return View(u);
}
Any help is much appreciate. Thanks.
If you absolutely want to manage login and security yourself using Session, You can create your own action filter which checks whether session has a user id set to it.
Something like this
public class AuthorizeWithSession : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.HttpContext.Session == null ||
context.HttpContext.Session["LoggedUserID"]==null)
{
context.Result =
new RedirectToRouteResult(new RouteValueDictionary(
new {controller = "Account", action = "Login"}));
}
base.OnActionExecuting(context);
}
}
Now decorate this action filter on your secure actions/controllers
[AuthorizeWithSession]
public class TeamController : Controller
{
}
You should have your own role management if you want to control what the users can do.
Each user should have one or more roles, each role can have a set of permissions and you can create an action filter that inherits from AuthorizeAttribute to make sure it is executed as early as possible.
Inside the AuthorizeCore method of the AuthorizeAttribute , you will see if the user is authenticated or not, and if he is authenticated then you can read his identity, read his roles and permissions from the database and compare it to a value passed to the role.
ex:
public class RequireRoleAttribute : AuthorizeAttribute
{
public RoleEnum[] RequiredRoles { get; set; }
public RequireRoleAttribute()
{
}
public RequireRoleAttribute(params RoleEnum[] roles)
: this()
{
RequiredRoles = roles;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var principle = httpContext.User;
if (principle == null || principle.Identity == null || !principle.Identity.IsAuthenticated)
{
return false;
}
if (RequiredRoles != null)
{
if (!HasRole(RequiredRoles))
{
httpContext.Response.Redirect("/AccessDenied");
}
}
return base.AuthorizeCore(httpContext);
}
public bool HasRole(RoleEnum[] roles)
{
foreach (var role in roles)
{
if (HasRole(role))
return true;
}
return false;
}
public bool HasRole(RoleEnum role)
{
return true if the user role has the role specified (read it from database for example)
}
}
Then in your controller, just annotate the controller or action with the attribute
[RequireRole(RoleEnum.Administator)]
public class MySecureController : Controller
{
}
I have an MVC5 app that uses Individual Authentication, and of course ASP.NET Identity. The point is that I had extended I have a model that inherits from ApplicationUser, it is simply defined like this:
public class NormalUser : ApplicationUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
So, the point is that, first of all I want to check whether there is a logged-in user, and if there is, I want to get his/her FirstName, LastName and Email fields. How can I achieve it?
I think I need to use something like this to check whether there is a logged-in user:
if (Request.IsAuthenticated)
{
...
}
But, how can I get those specific fields' values for the current user?
In MVC5 the user data is stored by default in the session and upon request the data is parsed into a ClaimsPrincipal which contains the username (or id) and the claims.
This is the way I chose to implement it, it might not be the simplest solution but it definitely makes it easy to use.
Example of usage:
In controller:
public ActionResult Index()
{
ViewBag.ReverseDisplayName = this.User.LastName + ", " + this.User.FirstName;
}
In view or _Layout:
#if(User.IsAuthenticated)
{
<span>#User.DisplayName</span>
}
1. Replace ClaimsIdentityFactory
using System.Security.Claims;
using System.Threading.Tasks;
using Domain.Models;
using Microsoft.AspNet.Identity;
public class AppClaimsIdentityFactory : IClaimsIdentityFactory<User, int>
{
internal const string IdentityProviderClaimType = "http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider";
internal const string DefaultIdentityProviderClaimValue = "My Identity Provider";
/// <summary>
/// Constructor
/// </summary>
public AppClaimsIdentityFactory()
{
RoleClaimType = ClaimsIdentity.DefaultRoleClaimType;
UserIdClaimType = ClaimTypes.NameIdentifier;
UserNameClaimType = ClaimsIdentity.DefaultNameClaimType;
SecurityStampClaimType = Constants.DefaultSecurityStampClaimType;
}
/// <summary>
/// Claim type used for role claims
/// </summary>
public string RoleClaimType { get; set; }
/// <summary>
/// Claim type used for the user name
/// </summary>
public string UserNameClaimType { get; set; }
/// <summary>
/// Claim type used for the user id
/// </summary>
public string UserIdClaimType { get; set; }
/// <summary>
/// Claim type used for the user security stamp
/// </summary>
public string SecurityStampClaimType { get; set; }
/// <summary>
/// Create a ClaimsIdentity from a user
/// </summary>
/// <param name="manager"></param>
/// <param name="user"></param>
/// <param name="authenticationType"></param>
/// <returns></returns>
public virtual async Task<ClaimsIdentity> CreateAsync(UserManager<User, int> manager, User user, string authenticationType)
{
if (manager == null)
{
throw new ArgumentNullException("manager");
}
if (user == null)
{
throw new ArgumentNullException("user");
}
var id = new ClaimsIdentity(authenticationType, UserNameClaimType, RoleClaimType);
id.AddClaim(new Claim(UserIdClaimType, user.Id.ToString(), ClaimValueTypes.String));
id.AddClaim(new Claim(UserNameClaimType, user.UserName, ClaimValueTypes.String));
id.AddClaim(new Claim(IdentityProviderClaimType, DefaultIdentityProviderClaimValue, ClaimValueTypes.String));
id.AddClaim(new Claim(ClaimTypes.Email, user.EmailAddress));
if (user.ContactInfo.FirstName != null && user.ContactInfo.LastName != null)
{
id.AddClaim(new Claim(ClaimTypes.GivenName, user.ContactInfo.FirstName));
id.AddClaim(new Claim(ClaimTypes.Surname, user.ContactInfo.LastName));
}
if (manager.SupportsUserSecurityStamp)
{
id.AddClaim(new Claim(SecurityStampClaimType,
await manager.GetSecurityStampAsync(user.Id)));
}
if (manager.SupportsUserRole)
{
user.Roles.ToList().ForEach(r =>
id.AddClaim(new Claim(ClaimTypes.Role, r.Id.ToString(), ClaimValueTypes.String)));
}
if (manager.SupportsUserClaim)
{
id.AddClaims(await manager.GetClaimsAsync(user.Id));
}
return id;
}
2. Change the UserManager to use it
public static UserManager<User,int> Create(IdentityFactoryOptions<AppUserManager> options, IOwinContext context)
{
var manager = new UserManager<User,int>(new UserStore<User,int>(new ApplicationDbContext()))
{
ClaimsIdentityFactory = new AppClaimsIdentityFactory()
};
// more initialization here
return manager;
}
3. Create a new custom Principal
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Security.Claims;
public class UserPrincipal : ClaimsPrincipal
{
public UserPrincipal(ClaimsPrincipal principal)
: base(principal.Identities)
{
}
public int UserId
{
get { return FindFirstValue<int>(ClaimTypes.NameIdentifier); }
}
public string UserName
{
get { return FindFirstValue<string>(ClaimsIdentity.DefaultNameClaimType); }
}
public string Email
{
get { return FindFirstValue<string>(ClaimTypes.Email); }
}
public string FirstName
{
get { return FindFirstValue<string>(ClaimTypes.GivenName); }
}
public string LastName
{
get { return FindFirstValue<string>(ClaimTypes.Surname); }
}
public string DisplayName
{
get
{
var name = string.Format("{0} {1}", this.FirstName, this.LastName).Trim();
return name.Length > 0 ? name : this.UserName;
}
}
public IEnumerable<int> Roles
{
get { return FindValues<int>(ClaimTypes.Role); }
}
private T FindFirstValue<T>(string type)
{
return Claims
.Where(p => p.Type == type)
.Select(p => (T)Convert.ChangeType(p.Value, typeof(T), CultureInfo.InvariantCulture))
.FirstOrDefault();
}
private IEnumerable<T> FindValues<T>(string type)
{
return Claims
.Where(p => p.Type == type)
.Select(p => (T)Convert.ChangeType(p.Value, typeof(T), CultureInfo.InvariantCulture))
.ToList();
}
}
4. Create an AuthenticationFilter to use it
using System.Security.Claims;
using System.Web.Mvc;
using System.Web.Mvc.Filters;
public class AppAuthenticationFilterAttribute : ActionFilterAttribute, IAuthenticationFilter
{
public void OnAuthentication(AuthenticationContext filterContext)
{
//This method is responsible for setting and modifying the principle for the current request though the filterContext .
//Here you can modify the principle or applying some authentication logic.
var principal = filterContext.Principal as ClaimsPrincipal;
if (principal != null && !(principal is UserPrincipal))
{
filterContext.Principal = new UserPrincipal(principal);
}
}
public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
{
//This method is responsible for validating the current principal and permitting the execution of the current action/request.
//Here you should validate if the current principle is valid / permitted to invoke the current action. (However I would place this logic to an authorization filter)
//filterContext.Result = new RedirectToRouteResult("CustomErrorPage",null);
}
}
5. Register the auth filter to load globally in the FilterConfig
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new AppAuthenticationFilterAttribute());
}
By now the Principal is persisted and all we have left to do is expose it in the Controller and View.
6. Create a controller base class
public abstract class ControllerBase : Controller
{
public new UserPrincipal User
{
get { return HttpContext.User as UserPrincipal; }
}
}
7. Create a WebViewPage base class and modify the web.config to use it
public abstract class BaseViewPage : WebViewPage
{
public virtual new UserPrincipal User
{
get { return base.User as UserPrincipal; }
}
public bool IsAuthenticated
{
get { return base.User.Identity.IsAuthenticated; }
}
}
public abstract class BaseViewPage<TModel> : WebViewPage<TModel>
{
public virtual new UserPrincipal User
{
get { return base.User as UserPrincipal; }
}
public bool IsAuthenticated
{
get { return base.User.Identity.IsAuthenticated; }
}
}
And the web.config inside the Views folder:
<pages pageBaseType="MyApp.Web.Views.BaseViewPage">
Important!
Do not store too much data on the Principal since this data is passed back and forth on each request.
Yes, in Identity, if you need additional user information, you just pull the user from the database, since this is all stored on the actual user object now.
if (Request.IsAuthenticated)
{
var user = UserManager.FindById(User.Identity.GetUserId());
}
If GetUserId isn't on User.Identity, add the following to your usings:
using Microsoft.AspNet.Identity;
I am attempting to make a Windows Forms application that plugs into some services exposed by ASP.NET MVC WebAPI, but am having a great deal of trouble with the authentication/login part.
I cannot seem to find an example that just demonstrates how to do this from Windows Forms, everything I find seems to be very convoluted and includes a lot of very deep plumbing, or seems targeted to other ASP.NET websites, and not windows forms.
Is there something I am missing? Is this just not possible? Or is it just not intended? I've looked at things like this .NET WebApi Authentication that claim to do it, but I don't see how to use cookies from a Windows Forms standpoint. I've also gone over http://blogs.msdn.com/b/webdev/archive/2012/08/26/asp-net-web-api-and-httpclient-samples.aspx and still have had very little luck.
Just create authentication token on server-side and store it in your database or even in cache. Then send this token with requests from your win forms application. WebApi should check this token all the time. It's good enough and you have full control over your auth process.
Let me share, how it works for me:
Object with Auth details:
public class TokenIdentity
{
public int UserID { get; set; }
public string AuthToken { get; set; }
public ISocialUser SocialUser { get; set; }
}
Web API Auth Controller:
public class AuthController : ApiController
{
public TokenIdentity Post(
SocialNetwork socialNetwork,
string socialUserID,
[FromUri]string socialAuthToken,
[FromUri]string deviceRegistrationID = null,
[FromUri]DeviceType? deviceType = null)
{
var socialManager = new SocialManager();
var user = socialManager.GetSocialUser(socialNetwork, socialUserID, socialAuthToken);
var tokenIdentity = new AuthCacheManager()
.Authenticate(
user,
deviceType,
deviceRegistrationID);
return tokenIdentity;
}
}
Auth Cache Manager:
public class AuthCacheManager : AuthManager
{
public override TokenIdentity CurrentUser
{
get
{
var authToken = HttpContext.Current.Request.Headers["AuthToken"];
if (authToken == null) return null;
if (HttpRuntime.Cache[authToken] != null)
{
return (TokenIdentity) HttpRuntime.Cache.Get(authToken);
}
return base.CurrentUser;
}
}
public int? CurrentUserID
{
get
{
if (CurrentUser != null)
{
return CurrentUser.UserID;
}
return null;
}
}
public override TokenIdentity Authenticate(
ISocialUser socialUser,
DeviceType? deviceType = null,
string deviceRegistrationID = null)
{
if (socialUser == null) throw new ArgumentNullException("socialUser");
var identity = base.Authenticate(socialUser, deviceType, deviceRegistrationID);
HttpRuntime.Cache.Add(
identity.AuthToken,
identity,
null,
DateTime.Now.AddDays(7),
Cache.NoSlidingExpiration,
CacheItemPriority.Default,
null);
return identity;
}
}
Auth Manager:
public abstract class AuthManager
{
public virtual TokenIdentity CurrentUser
{
get
{
var authToken = HttpContext.Current.Request.Headers["AuthToken"];
if (authToken == null) return null;
using (var usersRepo = new UsersRepository())
{
var user = usersRepo.GetUserByToken(authToken);
if (user == null) return null;
return new TokenIdentity
{
AuthToken = user.AuthToken,
SocialUser = user,
UserID = user.ID
};
}
}
}
public virtual TokenIdentity Authenticate(
ISocialUser socialUser,
DeviceType? deviceType = null,
string deviceRegistrationID = null)
{
using (var usersRepo = new UsersRepository())
{
var user = usersRepo.GetUserBySocialID(socialUser.SocialUserID, socialUser.SocialNetwork);
user = (user ?? new User()).CopyFrom(socialUser);
user.AuthToken = System.Guid.NewGuid().ToString();
if (user.ID == default(int))
{
usersRepo.Add(user);
}
usersRepo.SaveChanges();
return new TokenIdentity
{
AuthToken = user.AuthToken,
SocialUser = user,
UserID = user.ID
};
}
}
}
Global Action Filter:
public class TokenAuthenticationAttribute : System.Web.Http.Filters.ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (actionContext.Request.RequestUri.AbsolutePath.Contains("api/auth"))
{
return;
}
var authManager = new AuthCacheManager();
var user = authManager.CurrentUser;
if (user == null)
{
throw new HttpResponseException(HttpStatusCode.Unauthorized);
}
//Updates the authentication
authManager.Authenticate(user.SocialUser);
}
}
Global.asax registration:
GlobalConfiguration.Configuration.Filters.Add(new AuthFilterAttribute());
The idea is that AuthCacheManager extends AuthManager and decorates it's methods and properties. If there is nothing inside cache then go check database.
You could use token based authentication. Here's a great article illustrating how you could write a custom action filter that uses RSA public/private cryptography.