I am creating a basic authentication system with MVC 4 that uses custom logic. I have a UserModel class (which inherits from IPrincipal) that stores data I need to be persistent. To do this I am using the following code in my Controller:
if (ModelState.IsValid)
{
if (userModel.IsValidUser())
{
FormsAuthentication.SetAuthCookie(userModel.Username, false);
HttpContext.User = userModel;
// User is now logged in; send them to Index method to load MyeMan data
return RedirectToAction("Index", "Login");
}
}
Then, in the Index action:
if (IsUserLoggedIn())
{
UserModel userModel = (UserModel) HttpContext.User; // This line throws error
}
The error I get is:
Unable to cast object of type 'System.Web.Security.RolePrincipal' to
type 'MyProj.UserModel'.
When I look in debug, the HttpContext.User accepts the UserModel without complaint. Only when it redirects to the different action does its datatype change.
Am I doing something wrong or is this the complete wrong way to go about storing UserModel persisently without Sessions? Sessions will expire independently of the AuthCookie; I was told HttpContext.User is the way to go.
Assigning the user will work, but this assignment will not persist between requests. You have to make sure to set up the user at the beginning of each request, perhaps in a custom AuthorizeAttribute or IHttpModule. For example, you might have logic like:
Retrieve the relevant cookie from the request
Verify that the cookie corresponds to a valid session (e. g. by querying a database containing this information)
Retrieve the session information based on the cookie and store it in the User property
Also, when you assign HttpContext.Current.User consider assigning Thread.CurrentPrincipal as well.
Read Passing Data in an ASP.NET MVC Application
You can use TempData to pass the data between the action. It will be available for only subsequent requests
TempData["User"] = userModel;
// User is now logged in; send them to Index method to load MyeMan data
return RedirectToAction("Index", "Login");
In Index
UserModel user= (UserModel)TempData["User"];
Related
I am using Asp.net core 2.0 MVC with Individual User Account enabled. The automatically-generated ManageController class is attributed by [Authorize].
I find there are some action methods with the following code snippet.
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
Question
In my mental model, being authorized guarantees being a registered user. So such a null checking in authorized classes seems to be unnecessary. I want to know whether or not UserManager.GetUserAsync(User) can return null in a class with Authorize attribute?
I want to know whether or not UserManager.GetUserAsync(User) can return null in a class with Authorize attribute?
It can, if the user entry was removed from the database after the user logged in (by default, cookies are validated after 30 minutes so they can still be "valid" even after the corresponding user was removed from the database).
Upon logging in, I am saving a Session variable for that user who just logged in. This Session variable is very very important for every single thing the user then see's (see this question MVC Individual User Accounts login value persisting)
I see there being a potential problem where the Session variable is not tied in with the user being logged in, it has its own expiry (unless someone can give a 100% fool proof way of that NOT happening, for example, when I restart debugging, I am still logged in but the session variable has gone, regardless of expiry).
What I do then, is to check the Session variable exists, and sign them out and redirect if its null. However the redirect needs to be done within the Action, and would need to happen for EVERY get request, so there will be a lot of duplicate code.
var customerIdSession = Session["CustomerId"];
if (customerIdSession == null)
{
// Then sign out which needs the AuthenticationManager property to be defined in the controller too!
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
return RedirectToAction("Login", "Account");
}
var customerId = Convert.ToInt32(customerIdSession);
Is there a way I could tidy this up? And not have to do this on EVERY get method. Make it a global check somehow, like Authorize does for login
I found the answer on my own in the end. I took the idea I had of it being similar to the Authorize so found a way to make my own Authrorize attribute. This way, I can put the attribute above every Controller then assume the Session variable exists in every action. If not, the user is logged out and redirected automatically.
public class AuthorizeSessionAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var customerId = httpContext.Session["CustomerId"];
if (customerId == null)
{
var lo = httpContext.GetOwinContext().Authentication;
lo.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
return false;
}
return true;
}
}
I find lots of information about Identity but nothing specifically addressing this very common scenario.
I have a controller named ShowAccount() that should display the account data of the currently logged in user, and prevent him from seeing anything but its own account.
Also unauthenticated users should not be able to access this functionality at all.
How do I achieve this?
Thanks
Unauthenticated Users
K, I'll start with the simpler request, to block unauthenticated user from having access at all to your controller just add this attribute:
[Authorize]
above your controller, or if you want to allow some\disable some functions in the controller you can place it above the specific function.
In case you want to block your entire controller and allow just a few functions you can use this attribute:
[AllowAnonymous]
Limit user access to his own data
I'm doing something similar in one of my project so I thought it might help, nothing fancy, I would love to hear a better option myself.
For your 2nd issue, I assume that you have a model that stores data and that data has some kind relation to the UserID (foreign key maybe?).
What you can do is in your controler - filter the data you send back to the user, i.e on the view instead of returning:
return View(db.MyDB.ToList());
return:
MyDBClass data = db.MyDB.Where(u => u.UserID == GetUserID()).ToList();
return View(data);
Assume GetUserID() is a function that gives you the current user ID, in case you use the default authentication in MVC I can share it here as well.
This solution tho is not complete, you need to continue enforcing it in any other actions such as edit\delete\create or what ever other actions you support, you need to always check that the user is accessing only his data by comparing between the userID saved in the DB to the one in the request.
Hope this helps.
I had a similar challenge but I got mine
public ActionResult Create()
{
return View();
}
// POST: ArtistGig/Create
[HttpPost]
public ActionResult Create(ArtistGig artistGig)
{
var userid = User.Identity.GetUserId();
///
var artist = db.ArtistHubs.SingleOrDefault(a => a.ApplicationUserId == userid).Id;
artistGig.ArtistHubId = artist;
db.ArtistGigs.Add(artistGig);
db.SaveChanges();
return RedirectToAction("Index");
}
User.Identity.GetUseId is to query for the loged in user's Id according to the DbContext you are using
I am new to MVC and I have very simple problem.
When user login to my application I need to create a specific object (model) for the user for eg UserObject.
This object is unique to current logged in user and should only be disposed when user click on logout.
I don’t know how to maintain the lifetime of the object. As if I create object in Action method of controller class then as soon as the request is finished I lose the reference of the object.
How this should have been done?
The lifetime of your models are only going to be as long as the request. So each time the user goes to another page or refreshes, the MVC framework is going to instantiate a new controller (and model within). Otherwise your server would have a ton of static objects floating around in memory which would use up a lot of resources and wouldn't scale.
In order to manage state, you are going to need to use other methods such as sessions/cookies and a database.
So let's say the user logs in via /User/Login. This routes the request to an action named UserController.Login().
Inside this action, it instantiates a UserModel.
public ActionResult Login(string username, string password) {
var userModel = new UserModel();
if (userModel.Authenticate(username, password)) {
// Setup your session to maintain state
Session["username"] = username;
} else {
return View("Login");
}
return View("LoginComplete");
}
You might want the user model to actually create the session, but I've shown it here for clarity.
The user model authenticates the user, and then you create a session just like you would in a traditional non-MVC site.
Then in subsequent requests, you will want to authorize the user, and use any session data you have to retrieve state information.
public ActionResult SuperSecretPlace() {
var userModel = new UserModel();
string username = Session["username"]
var user = userModel.GetUserByUsername(username);
if (user == null) throw new HttpException(401, "User is not authorized.");
return View("SuperSecretPlace", user);
}
In the action above, the UserModel might do something like query a database to retrieve the user's data so you can pass it in to the corresponding view.
If you want to make life easier, you might want to just use .NET's built in forms authentication:
http://www.codeproject.com/Articles/578374/AplusBeginner-splusTutorialplusonplusCustomplusF
For more info about the lifecycle of MVC:
http://www.dotnet-tricks.com/Tutorial/mvc/TbR0041112-Asp.net-MVC-Request-Life-Cycle.html
http://www.asp.net/mvc/overview/getting-started/lifecycle-of-an-aspnet-mvc-5-application
Actually what you are trying to achieve is passing model from controller to controller which is not possible. When an action is executed the context of the model object is disposed at the view and it can cannot be passed from controller to controller. You have to create a new object repopulate it and use it to achieve the goal in different controller.If you need the data to be persisted you can use sessions but still you need to create an object of the model in every controller.
The following image is for your reference as to see what to use when passing data between model-view-controller. Please feel free to ask if you need more information on this.
As opposed to the other aswers I would not use session as it has quite some disadvantages (scalability, pessimistic concurrency which blocks concurrent calls, app pool recycling...). Why you should not use session is documented in a lot of places like here or here.
Instead, I would store it in a cookie.
However, be sure to not store confidential or sensitive data. Whatever you use (cookies or session), it can be tampered with or stolen. If you are dealing with sensitive information, you need other solutions. Read also more about secure cookie solution here.
I'm having problem with getting ServiceStack [Authentication] attribute to work in ASP.Net MVC4 controller, pages / action methods with the attribute keep redirecting Users to the login page even after the login details are submitted correctly.
I've followed the SocialBootstrapApi example, with the difference being that all the authentication web service calls are made from the controllers:
this.CreateRestClient().Post<RegistrationResponse>("/register", model);
Other things that I've done so far:
Use my own user session implementation subclassing AuthUserSession (not too different from the example, but using my own implementation of User table)
Inherit ServiceStackController on my BaseController, overriding the default login URL
Enable Auth feature in AppHost with my user session implementation
Registration does work, user auth logic works (even though the session does not persist), and I can see the ss-id and ss-pid cookies in the request.
So my complete list of questions:
How do I make the [Authenticate] attribute work (or, what did I do wrong)?
How do I save and reuse the user session in an MVC controller? At the moment this.UserSession is always null.
How do I logout a user? this.CreateRestClient().Get<AuthResponse>("/auth/logout"); does not seem to work.
Update 1:
The session cookies (ss-id and ss-pid) gets created when I attempt to load the secured page (ones with [Authenticate] attribute), before any credentials get submitted. Is this the expected behaviour?
Update 2:
I can see that the session is saved in MemoryCacheClient, however trying to retrieve it in the base controller via this.Cache.Get<CustomUserSession>(SessionKey) returns null (where SessionKey is like: urn:iauthsession:1)
After much fiddling around, apparently the way to hook ServiceStack authentication is to call the AuthService via:
try {
authResponse = AuthService.Authenticate(new Auth{ UserName = model.UserName, Continue = returnUrl, Password = model.Password });
} catch (Exception ex) {
// Cut for brevity...
}
and NOT authResponse = this.CreateRestClient().Post<AuthResponse>("/auth/credentials", model);!
Where AuthService is defined in the base controller as:
public AuthService AuthService
{
get
{
var authService = ServiceStack.WebHost.Endpoints.AppHostBase.Instance.Container.Resolve<AuthService>();
authService.RequestContext = new HttpRequestContext(
System.Web.HttpContext.Current.Request.ToRequest(),
System.Web.HttpContext.Current.Response.ToResponse(),
null);
return authService;
}
}
Everything else (incl. session) works correctly now.
You can find how it could be done in the ServiceStack Use Cases repository. The following example is based on MVC4 but works perfectly for MVC3 either: CustomAuthenticationMvc.