ASP.NET MVC 3 HttpContext.Current.User.Identity.IsAuthenticated is always false - asp.net-mvc-3

I have two controllers, AdminController and AccountController with the following code
AccountController:
[HttpPost]
public ActionResult LogOn(LogOnViewModel model)
{
if (ModelState.IsValid)
{
_authenticationService.SetPrincipal(model.UserName);
var exists = _authenticationService.ValidateCredentials(userName, password);
FormsAuthentication.SetAuthCookie(model.UserName, false);
if(exists){
return RedirectToAction("Index", "Admin");
}
}
return RedirectToAction("LogOn");
}
AdminController:
[Authenticate]
public class AdminController : Controller
{
[HttpGet]
public ActionResult Index()
{
return View();
}
}
AuthenticateAttribute is inherited from AuthorizeAttribute and has the following code:
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var authenticated = false;
if (HttpContext.Current.User != null && HttpContext.Current.User.Identity.IsAuthenticated)
{
//some actions
}
else
{
FormsAuthentication.SignOut();
FormsAuthentication.RedirectToLoginPage();
}
return authenticated;
}
_authenticationService is the instance of AuthenticationService class and SetPrincipal() method has the following code:
public void SetPrincipal(string userName)
{
var identity = new GenericIdentity(userName);
var principal = new GenericPrincipal(identity, null);
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
{
var ticket = new FormsAuthenticationTicket(1,
principal.Identity.Name,
DateTime.Now,
DateTime.Now.AddMinutes(30),
false,
String.Empty,
FormsAuthentication.FormsCookiePath);
string encryptedCookie = FormsAuthentication.Encrypt(ticket);
var authenticationCookie = HttpContext.Current.Response.Cookies[FormsAuthentication.FormsCookieName];
if (authenticationCookie != null)
{
authenticationCookie.Value = encryptedCookie;
authenticationCookie.Expires = DateTime.Now.AddMinutes(30);
}
HttpContext.Current.User = principal;
}
}
When I debug and watch AuthenticationService.SetPrincipal() HttpContext.Current.User.Identity.IsAuthenticated is true. But after redirect to Index action of AdminController in AuthenticateAttribute.AuthorizeAttribute() HttpContext.Current.User.Identity.IsAuthenticated is always false. As result I redirected to LogOn view again.
What am I doing wrong?

I don't see anywhere where you actually send the cookie back to the client. In order to be authenticated on each subsequent request, you have to send the encrypted cookie back to the client so that it can pass it back to your site.
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedCookie);
Response.Cookies.Add(cookie);
I see where you try and get the current authentication cookie here:
var authenticationCookie = HttpContext.Current.Response.Cookies[FormsAuthentication.FormsCookieName];
But again, this is a GET, not a SET (or sending the cookie back) function line. At this point in your authentication, if you set a debugger, authenticationCookie is always going to be NULL.
Also, I don't see where you validate the password in any of your actions or functions. Ensure you are not overlooking that step.
One more thought/question/issue with your code. You are setting a variable called userExists in your controller action, but the function you call is a void type, so...you don't need to set that variable, just call the function.
_authenticationService.SetPrincipal(model.UserName);
return RedirectToAction("Index", "Admin");

Related

.net web api AuthorizeAttribute pass parameter to the controller

A AuthorizedAttribute named as JwtAuthenticateAttribute is implemented and did something
It can be triggered for each request of the below API
[HttpGet]
[JwtAuthenticate]
public GetFaqListDS Index([FromUri] GetFaqListModel model)
{
List<MP_faqs> res = Faq.GetFaqList(model.curPage, model.pageSize);
var re = Request;
var hds = re.Headers;
GetFaqListDS ds = new GetFaqListDS();
ds.data = res;
return ds;
}
The question is, how to pass a value to this controller's function from the Attribute JwtAuthenticate.
P.S the value exists at the OnAuthorization of this attribute.
Thanks.
Add the required value in "actionContext.Request.Properties" in Onauthorize method and access these in controller.
Adding UserDetail Model :
actionContext.Request.Properties.Add("__user", new UserDetails(){userid=123, username="ABC"});
Retrieving values :
private static UserDetails GetUserDetailsFromRequest()
{
object tempVal;
try
{
var httpRequestMessage = (HttpRequestMessage)HttpContext.Current.Items["MS_HttpRequestMessage"];
httpRequestMessage.Properties.TryGetValue("__user", out tempVal);
if (tempVal == null) return null;
var user = (UserDetails)tempVal;
return user;
}
catch
{
}
return new UserDetails();
}
Hope this will helps you.

Kendo Notification in Code Behind

I have searched and didn't find any solution for my problem. My problem is that I couldn't call notification from my controller and all notification codes are related about razor or js or inside html. I have to write from .cs file and my cs file like that;
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(Kullanici model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
if (string.IsNullOrEmpty(model.KullaniciAdi))
{
//
}
KullaniciDAL kullanicidal = new KullaniciDAL();
Kullanici kullanici = kullanicidal.GetRowByKullaniciAdi(model.KullaniciAdi);
if (string.IsNullOrEmpty(kullanici.KullaniciAdi))
{
//
}
string s = Code.Dev.Crypto.Decrypt(kullanici.Sifre);
if (s == model.Sifre)
{
HttpCookie cookie = new HttpCookie("CustId", kullanici.CustId);
cookie.Expires = DateTime.Now.AddDays(2);
HttpContext.Response.Cookies.Add(cookie);
cookie = new HttpCookie("KullaniciAdi", kullanici.KullaniciAdi);
cookie.Expires = DateTime.Now.AddDays(2);
HttpContext.Response.Cookies.Add(cookie);
cookie = new HttpCookie("KullaniciRol", "Uzman");
cookie.Expires = DateTime.Now.AddDays(2);
HttpContext.Response.Cookies.Add(cookie);
return RedirectToLocal(returnUrl);
}
else
{
//notification will be showed
//Kendo.Mvc.UI.NotificationTemplate notification = new Kendo.Mvc.UI.NotificationTemplate();
//notification
return View("Login");
}
}

WebAPI - Return Response from Initialize (override) method

I have the same code on each API method:
if (user.AspNetRoles.Where(p => p.Name == Models.Roles.SmartphonePhotographer).Count() == 0)
{
return Request.CreateResponse((HttpStatusCode)453, new { Message = "User does not have Smartphone Photographer role" });
//return Request.CreateErrorResponse(HttpStatusCode.NotFound, "User does not have Smartphone Photographer role");
}
if (!user.EmailConfirmed)
{
return Request.CreateResponse((HttpStatusCode)454, new { Message = "User is not confirmed" });
}
and want to return Response directly from override Initialize method. Can I do it?
(Of course, I have moved it to ActionFilter if had not set user variable in Controller)
protected override void Initialize(HttpControllerContext controllerContext)
{
int id = controllerContext.RequestContext.Principal.Identity.GetUserId<int>();
user = controllerContext.Request.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(id);
base.Initialize(controllerContext);
}
Override Initialize method is a void method so you can't return response result,
You should write on the response but in the Initialize method the response still not completed yet.
see this link
You can reroute to your method which return your response like the below
protected override void Initialize(HttpControllerContext controllerContext)
{
var newControllerContext = controllerContext
//edit RouteData to refer to your controller/action
newControllerContext.RouteData.Values["action"] = "YourAction"
base.Initialize(newControllerContext);
}

Forbid users from executing WebApi actions

I have the following WebApi action that deletes an order from the back-end database, only for users that are in the Admin and Order roles. However, if the user is also in the Readonly role the action returns a HTTP 403 Forbidden response.
[Authorize(Roles = "Admin,Order")]
public async Task<IHttpActionResult> Delete(int orderid) {
if(User.IsInRole("Readonly")) { return Forbidden(); }
var order = await _repository.Get(orderid);
if(order != null) {
await _repository.Delete(orderid);
return NoContent();
}
else {
return NotFound();
}
}
What I'd like to know is it possible to prevent actions from being executed if users are in specific roles so that I do not have to put if(User.IsInRole("Readonly")) { return Forbidden(); } at the start of all database update-able action methods, e.g.
[Authorize(Roles = "Admin,Order")]
[NotAuthorized(Roles = "Readonly")]
public async Task<IHttpActionResult> Delete(int orderid) {
var order = await _repository.Get(orderid);
if(order != null) {
await _repository.Delete(orderid);
return NoContent();
}
else {
return NotFound();
}
}
The NotAuthorized action filter will return a HTTP 403 Forbidden response if the user is in the Readonly role.
Is this possible?
This is the code to implement a reverse of the [Authorize()] attribute and forbid users from executing MVC WebApi actions if they are a member of one or more roles.
using System;
using System.Net;
using System.Net.Http;
using System.Security.Principal;
using System.Web.Http;
using System.Web.Http.Controllers;
namespace MyAPI {
[AttributeUsage(AttributeTargets.Method,AllowMultiple = false)]
public class NotAuthorizedAttribute : AuthorizeAttribute {
public override void OnAuthorization(HttpActionContext actionContext) {
IPrincipal user = actionContext.RequestContext.Principal;
if(!user.Identity.IsAuthenticated) {
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
}
else {
bool userInRole = false;
foreach(var role in Roles.Split(',')) {
if(user.IsInRole(role)) {
userInRole = true;
break;
}
}
if(userInRole) {
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
}
}
}
}
}
To use this filter attribute simply decorate any actions that you don't want users to execute if they're a member of a restricted role, e.g. if the user is part of a read-only role they not permitted to update the database:
[Authorize(Roles = "Admin,Order")]
[NotAuthorized(Roles = "Readonly")]
public async Task<IHttpActionResult> Delete(int orderid) {
var order = await _repository.Get(orderid);
if(order != null) {
await _repository.Delete(orderid);
return NoContent();
}
else {
return NotFound();
}
}

setting HttpContext.Current.User

I am developing an asp.net mvc 3.0 application which has a simple authentication process. User fills a form which is sent to server by ajax call and gets response, but the problem here is that using the following method :
FormsAuthentication.SetAuthCookie(person.LoginName,false);
is not enough to fill 'HttpContext.Current.User' and it needs the below method to be run :
FormsAuthentication.RedirectFromLoginPage("...");
Problem here is that as i mentioned, the loggin form uses an ajax form, and get responses with json, so redirecting is not possible.
How could I fill 'HttpContext.Current.User' ?
Thanks.
Update :
Here is register method :
[HttpPost]
public ActionResult Register(Person person)
{
var q = da.Persons.Where(x => x.LoginName == person.LoginName.ToLower()).FirstOrDefault();
if (q != null)
{
ModelState.AddModelError("", "Username is repettive, try other one");
return Json(new object[] { false, this.RenderPartialViewToString("RegisterControl", person) });
}
else
{
if (person.LoginName.ToLower() == "admin")
{
person.IsAdmin = true;
person.IsActive = true;
}
da.Persons.Add(person);
da.SaveChanges();
FormsAuthentication.SetAuthCookie(person.LoginName,false);
return Json(new object[] { true, "You have registered successfully!" });
}
}
FormsAuthentication doesn't support immediate setting of user's identity, but you should be able to fake it by something like this:
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(
new System.Security.Principal.GenericIdentity(person.LoginName),
new string[] { /* fill roles if any */ } );
Here is the version I ended up using, which is based on the answer by #AdamTuliper-MSFT. It is only meant to be used right after logging in, but before redirect, to allow other code to access HttpContext.User.
Don't do anything if already authenticated
Doesn't modify the cookie, since this should only be used for the lifetime of this request
Shorten some things, and a little safer with userdata (should never be null, but...)
Call this after you call SetAuthCookie(), like below:
// in login function
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
AuthenticateThisRequest();
private void AuthenticateThisRequest()
{
//NOTE: if the user is already logged in (e.g. under a different user account)
// then this will NOT reset the identity information. Be aware of this if
// you allow already-logged in users to "re-login" as different accounts
// without first logging out.
if (HttpContext.User.Identity.IsAuthenticated) return;
var name = FormsAuthentication.FormsCookieName;
var cookie = Response.Cookies[name];
if (cookie != null)
{
var ticket = FormsAuthentication.Decrypt(cookie.Value);
if (ticket != null && !ticket.Expired)
{
string[] roles = (ticket.UserData as string ?? "").Split(',');
HttpContext.User = new GenericPrincipal(new FormsIdentity(ticket), roles);
}
}
}
Edit: Remove call to Request.Cookies, as #AdamTuplier-MSFT mentioned.
You need to manually set it. Rather than reinventing the wheel, note the section here on updating the current principal for the request - thats your option here.
How to set Request.IsAuthenticated to true when not using FormsAuthentication.RedirectFromLoginPage?
public void RenewCurrentUser()
{
System.Web.HttpCookie authCookie =
System.Web.HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = null;
authTicket = FormsAuthentication.Decrypt(authCookie.Value);
if (authTicket != null && !authTicket.Expired)
{
FormsAuthenticationTicket newAuthTicket = authTicket;
if (FormsAuthentication.SlidingExpiration)
{
newAuthTicket = FormsAuthentication.RenewTicketIfOld(authTicket);
}
string userData = newAuthTicket.UserData;
string[] roles = userData.Split(',');
System.Web.HttpContext.Current.User =
new System.Security.Principal.GenericPrincipal(new FormsIdentity(newAuthTicket), roles);
}
}
}

Resources