In my MVC app I am trying to store a cookie in one page response and access it another:
So my /account/register controller has an action that calls a method to store a cookie
public void StoreCookie(Guid pid)
{
var userCookie = new HttpCookie("Userid","B6EAF085-247B-46EB-BB94-79779CA44A14");
Response.Cookies.Remove("Userid");
Response.Cookies.Add(userCookie);
}
After a user registers I can see that the response(/account/register ) contains the cookie: Userid:"B6EAF085-247B-46EB-BB94-79779CA44A14
I now wish to access this cookie from another MVC page view - info/paymentsuccess
I tried assigning the value to Viewbag as
Viewbag.userid = #Response.Cookies["Userid"].value
This returns null
how do I access this cookie from another page/MVC view and store it in Viewbag.userid?
In your account register controller set the cookie:
HttpCookie cookie = this.ControllerContext.HttpContext.Request.Cookies["Userid"];
this.ControllerContext.HttpContext.Response.Cookies.Add(cookie);
You can retrieve the cookie like so in your post /account/register controller:
if (this.ControllerContext.HttpContext.Request.Cookies.AllKeys.Contains("Userid"))
{
HttpCookie cookie = this.ControllerContext.HttpContext.Request.Cookies["Userid"];
// retrieve cookie data here
}
You can access cookies value in your view using following:
Viewbag.userid = #Request.Cookies["Userid"].value
After registering, do you create a new session? Maybe the cookie is set to the MinValue for Expires, which makes it a session cookie.
Try this:
public void StoreCookie(Guid pid)
{
var userCookie = new HttpCookie("Userid","B6EAF085-247B-46EB-BB94-79779CA44A14");
userCookie.Expires = DateTime.Now.AddDays(1); //...or something that fit your needs
Response.Cookies.Remove("Userid");
Response.Cookies.Add(userCookie);
}
Related
I use MVC 4 and have moved some logic into an authorize filter. I am trying redirect to an error page based on not being authorized. I would like to set the last page route and a few other properties to catch the error.
Below is my override
// handle unauthorized
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Controller.ViewBag.LastRoute = filterContext.RouteData;
filterContext.Controller.ViewBag.Issue = "put...val here";
var routeValues = new RouteValueDictionary(new
{
controller = "Error",
action = "Oops"
});
filterContext.Result = new RedirectToRouteResult(routeValues);
}
controller
[AllowAnonymous]
public ActionResult Oops()
{
var m = new Core.Models.ErrorModel();
var v = ViewBag.Issue; // == null
return View("~/Views/Error/Oops.cshtml", m);
}
I tried how to set values to viewbag in actionfilterattribute asp mvc 5 for action filters and it works
Any help would be appreciated.
EDIT:
Sorry when I get to the controller the value for:
ViewBag.Issue = null.
I'm not sure how to set the property and have it hold value.
RedirectToRouteResult is going to send a redirect response to the browser and browser will issue a brand new GET request to the url specified. ViewBag data do not survive between 2 http requests.
You may use TempData which will keep the data between 2 seperate http requests. You can set your TempData value in one action method and any action method that is called after this can get values from the TempData object and then use it. TempData use Session behind the scene to store data. The value of TempData persists until it is read or until the session times out. This is ideal for scenarios such as redirection because the values in TempData are available beyond a single request.
So in your action filter you can set the TempData dictionary instead of ViewBag.
filterContext.Controller.TempData["Issue"] = "Robots are laughing non stop";
var routeValues = new RouteValueDictionary(new
{
controller = "Home",
action = "Oops"
});
filterContext.Result = new RedirectToRouteResult(routeValues);
Now in your Oops action method, you may read the TempData value you set
public ActionResult Oops()
{
var issueDetails = TempData["Issue"];
// TO DO : Do something useful with issueDetails :)
return View();
}
Keep in mind that TempData values won't be available after you read it. So if you want to read it in your view again, set it again or better use a view model and set the already read value as the property value of your view model.
[This question relates to ASP.NET MVC4, and it is about best-practice approach - so please, don't suggest hacks.]
I want to authenticate users using an auth token sent in the request URL. It works similarly to a password reset token, except in this case it does not go to a reset page but instead grants access to some portion of the site. The idea is to send the URL with the auth token to a verified email address of the user. Users can click the link and perform some actions without typing their password.
Out-of-the-box, ASP.NET has the [Authorize] attribute and the SimpleMembershipProvider - these seem to work great, but they do some voodoo magic under the hood (like auto-generating database tables), so I don't know how to extend them to add this link-based auth token.
I don't expect an exact answer, but please do point me to the right direction.
Thanks!
Uf, broad question. But I will try at least to direct you to a right direction.
So first if suggest that you use Forms Authentication as a base, but you will have to customize using of it. And I presume that you do not want to use cookies for the authentication as this is native behaviour of the Forms Authentication.
The most important point you should consider to have it you custom query string token based authentication.
Create a login action and in this action you will authorize the user, if he have granted access you ask FormsAuthentication to create AuthCookie. For the further on you just take the httpCookie.Value as your auth token that you will carry in query string.
You need to implement the Application_BeginRequest in the Global.asax that will handle this query string tokens and translate it into the cookie. With this approach you can leverage all the ASP.NET Forms Authentication infrastructure.
This is quite high level picture w/o code. If you need more detail help I can also provide it to you.
You should just use a regular Action that accepts HttpGet.
Upon receiving the token, immediately invalid it so it can't be used again.
Also, only accept tokens that are within your pre-defined range of time period, like 24 or 72 hours.
Thank you Peter for idea.
If smb need to create JWT token authorization for old ASP.NET MVC5.I wrote small example. I don't serialize cookie to JWT. I create a JWT and after I am checking it in the BeginRequest. If everything is ok, I create a cookie and set it to the httpContext.Request. I used authentication mode="Forms" for application and it require cookies.
For create JWT token:
const string secret = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk";
[AllowAnonymous]
[HttpPost]
public ActionResult LoginJWT(LoginViewModel model)
{
ActionResult response = null;
if (ModelState.IsValid)
{
if (true) //todo: check user login&password
{
var payload = new Dictionary<string, object>
{
{ "iss", "subject" },
{ "sub", "api" },
{ "exp", DateTimeOffset.UtcNow.AddHours(2).ToUnixTimeSeconds()},
{ "iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds()},
{ "jti", Guid.NewGuid() },
{ "uid", "64" } //custom field for identificate user
};
IJwtAlgorithm algorithm = new HMACSHA256Algorithm(); // symmetric
IJsonSerializer serializer = new JsonNetSerializer();
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
var token = encoder.Encode(payload, secret);
response = Content(token);
}
else
{
response = new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest, "Login or password are not found");
}
}
else
{
response = new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest, "Errors in Model");
}
return response;
}
For check JWT token in Global.asax:
public override void Init()
{
this.BeginRequest += this.BeginRequestHandler;
base.Init();
}
private void BeginRequestHandler(object sender, EventArgs e)
{
var bearerToken = this.Context.Request.Headers["Authorization"];
if (bearerToken != null)
{
var token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
const string secret = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk";
int userId = 0;
try
{
IJsonSerializer serializer = new JsonNetSerializer();
var provider = new UtcDateTimeProvider();
IJwtValidator validator = new JwtValidator(serializer, provider);
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtAlgorithm algorithm = new HMACSHA256Algorithm(); // symmetric
IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder, algorithm);
var json = decoder.DecodeToObject<IDictionary<string, string>>(token, secret, verify: true);
if (json.TryGetValue("uid", out var uid))
{
userId = Convert.ToInt32(uid);
}
}
catch (TokenExpiredException)
{
Console.WriteLine("Token has expired");
}
catch (SignatureVerificationException)
{
Console.WriteLine("Token has invalid signature");
}
if (userId != 0)
{
// check user by id, if found create cookie.
}
}
}
I used:
jwt-dotnet/jwt library 7.2.1
Im building a website with the new ASP.NET MVC3 framework and using FormsAuth. for securing the website. I'm storing the role of a user in the UserData property of the FormsAuthenticationTicket, (setting the cookie manually), I then call the encrypt method on the ticket before adding it to the cookie(see below a Standard ticket sniplet).
if (Validate(model.UserName, model.Password))
{
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(1,
model.UserName,
DateTime.Now,
DateTime.Now.AddMinutes(30),
false,
UserType.Administrator.ToString());
string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
Response.Cookies.Add(faCookie);
return RedirectToAction("startpage", "mycontroller");
}
}
Now I've made a custom AuthorizeAttribute thats able to check if the user is 1. authenticated and 2. has the admin role (from the ticket). (below)
The AuthorizeCore method of this derived class will be called when an action takes places in a class that has the attribute annotion.
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
IPrincipal user = httpContext.User;
if (!user.Identity.IsAuthenticated)
{
return false;
}
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = httpContext.Request.Cookies[cookieName];
if (authCookie == null)
return false;
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
if (authTicket.UserData != UserType.Administrator.ToString())
return false;
return true;
So here's where im getting confused.
When I follow the code being executed (with valid credentials, in debug), and check the values of the variables made on each line, the encryptedTicket encrypts just fine before adding it to the reponsecookie.
But when I then check the AuthorizeCore method when the controller (of the index page) is being called, the parameter its getting, the HttpContext, contains the ticket with everything unencrypted, so there is no need to decrypt the ticket anymore when reading the cookie.
Why do I see the ticket succesfully being encrypted in the logon controller where I send it back to the client, but then when I receive the httpcontext in the AuthorizeAdministrator class its all unencrypted again.
Sorry for the long question/story, there's probably a simple and short answer for it.
Hope my story is clear.
Thanks.
Forms auth needs to decrypt the cookie early in the page processing pipeline, to determine if the user is authorized -- that's when it fills in the details for User.Identity, etc.
I am using the following code to set a cookie in my asp.net mvc(C#) application:
public static void SetValue(string key, string value, DateTime expires)
{
var httpContext = new HttpContextWrapper(HttpContext.Current);
_request = httpContext.Request;
_response = httpContext.Response;
HttpCookie cookie = new HttpCookie(key, value) { Expires = expires };
_response.Cookies.Set(cookie);
}
I need to delete the cookies when the user clicks logout. The set cookie is not removing/deleting with Clear/Remove. The code is as below:
public static void Clear()
{
var httpContext = new HttpContextWrapper(HttpContext.Current);
_request = httpContext.Request;
_response = httpContext.Response;
_request.Cookies.Clear();
_response.Cookies.Clear();
}
public static void Remove(string key)
{
var httpContext = new HttpContextWrapper(HttpContext.Current);
_request = httpContext.Request;
_response = httpContext.Response;
if (_request.Cookies[key] != null)
{
_request.Cookies.Remove(key);
}
if (_response.Cookies[key] != null)
{
_response.Cookies.Remove(key);
}
}
I have tried both the above functions, but still the cookie exists when i try to check exist.
public static bool Exists(string key)
{
var httpContext = new HttpContextWrapper(HttpContext.Current);
_request = httpContext.Request;
_response = httpContext.Response;
return _request.Cookies[key] != null;
}
What may be problem here? or whats the thing i need to do to remove/delete the cookie?
Clearing the cookies of the response doesn't instruct the browser to clear the cookie, it merely does not send the cookie back to the browser. To instruct the browser to clear the cookie you need to tell it the cookie has expired, e.g.
public static void Clear(string key)
{
var httpContext = new HttpContextWrapper(HttpContext.Current);
_response = httpContext.Response;
HttpCookie cookie = new HttpCookie(key)
{
Expires = DateTime.Now.AddDays(-1) // or any other time in the past
};
_response.Cookies.Set(cookie);
}
The Cookies collection in the Request and Response objects aren't proxies for the cookies in the browser, they're a set of what cookies the browser sends you and you send back. If you remove a cookie from the request it's entirely server side, and if you have no cookies in the response you're just not going to send any thing back to the client, which won't change the set of cookies in the browser at all.
To delete a cookie, make sure that it is in the response cookie collection, but has an expiration time in the past.
Just to add something else I also pass the value back as null e.g.
public static void RemoveCookie(string cookieName)
{
if (HttpContext.Current.Response.Cookies[cookieName] != null)
{
HttpContext.Current.Response.Cookies[cookieName].Value = null;
HttpContext.Current.Response.Cookies[cookieName].Expires = DateTime.Now.AddMonths(-1);
}
}
The best way to implement this is to use a tool like Reflector and see how the System.Web.Security.FormsAuthentication.SignOut method implements removing the authentication cookie.
In Reflector, open up System.Web and navigate to the FormsAuthentication object and find the SignOut method. Right click on it and select "Disassemble" (Choose your language from the menu).
VB.NET
Public Shared Sub SignOut()
FormsAuthentication.Initialize
Dim current As HttpContext = HttpContext.Current
Dim flag As Boolean = current.CookielessHelper.DoesCookieValueExistInOriginal("F"c)
current.CookielessHelper.SetCookieValue("F"c, Nothing)
If (Not CookielessHelperClass.UseCookieless(current, False, FormsAuthentication.CookieMode) OrElse current.Request.Browser.Cookies) Then
Dim str As String = String.Empty
If (current.Request.Browser.Item("supportsEmptyStringInCookieValue") = "false") Then
str = "NoCookie"
End If
Dim cookie As New HttpCookie(FormsAuthentication.FormsCookieName, str)
cookie.HttpOnly = True
cookie.Path = FormsAuthentication._FormsCookiePath
cookie.Expires = New DateTime(&H7CF, 10, 12)
cookie.Secure = FormsAuthentication._RequireSSL
If (Not FormsAuthentication._CookieDomain Is Nothing) Then
cookie.Domain = FormsAuthentication._CookieDomain
End If
current.Response.Cookies.RemoveCookie(FormsAuthentication.FormsCookieName)
current.Response.Cookies.Add(cookie)
End If
If flag Then
current.Response.Redirect(FormsAuthentication.GetLoginPage(Nothing), False)
End If
End Sub
With the above as an example, I was able to create a common method called RemoveCookie() in a shared assembly, code is below:
VB.NET
''' <summary>
''' Method to remove a cookie
''' </summary>
''' <param name="key">Key</param>
''' <remarks></remarks>
Public Shared Sub RemoveCookie(ByVal key As String)
' Encode key for retrieval and remove cookie
With HttpContext.Current
Dim cookie As New HttpCookie(.Server.UrlEncode(key))
If Not IsNothing(cookie) Then
With cookie
.HttpOnly = True
.Expires = New DateTime(&H7CF, 10, 12)
End With
' Remove from server (has no effect on client)
.Response.Cookies.Remove(.Server.UrlEncode(key))
' Add expired cookie to client, effectively removing it
.Response.Cookies.Add(cookie)
End If
End With
End Sub
Having tested this using FireBug and the Cookie add-in for FireBug (in FireFox), I can attest that the cookie immediately gets removed.
Any questions, feel free to message me.
After playing around with this for some time and trying all of the other answers here I discovered that none of the answers here are totally correct.
The part that is correct is that you have to send an expired cookie to effect the deletion. The part that nobody else picked up on (but is demonstrated in the Microsoft code posted by Ed DeGagne) is that the cookie options for the deletion must match exactly the cookie options that were used to set the cookie in the first place.
For example if you originally created the cookie with the HttpOnly option then you must also set this option when deleting the cookie. I expect the exact behavior will vary across browsers and probably over time, so the only safe option that will work long-term is to make sure that all of the cookie options in the deletion response match exactly the cookie options used to create the cookie originally.
Response.Cookies["key"].Expires= DateTime.Now;
Here is My Code To Log In
var expire = DateTime.Now.AddDays(7);
// Create a new ticket used for authentication
var ticket = new FormsAuthenticationTicket(
1, // Ticket version
username, // Username to be associated with this ticket
DateTime.Now, // Date/time issued
expire, // Date/time to expire
true, // "true" for a persistent user cookie (could be a checkbox on form)
roles, // User-data (the roles from this user record in our database)
FormsAuthentication.FormsCookiePath); // Path cookie is valid for
// Hash the cookie for transport over the wire
var hash = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, hash) { Expires = expire };
// Add the cookie to the list for outbound response
Response.Cookies.Add(cookie);
Here Is My Code To Check The Roles. It is a custom IHTTP Module
if (HttpContext.Current.User == null) return;
if (!HttpContext.Current.User.Identity.IsAuthenticated) return;
if (!(HttpContext.Current.User.Identity is FormsIdentity)) return;
// Get Forms Identity From Current User
var id = (FormsIdentity)HttpContext.Current.User.Identity;
// Get Forms Ticket From Identity object
var ticket = id.Ticket;
// Retrieve stored user-data (our roles from db)
var userData = ticket.UserData;
var roles = userData.Split(',');
// Create a new Generic Principal Instance and assign to Current User
Thread.CurrentPrincipal = HttpContext.Current.User = new GenericPrincipal(id, roles);
Here is my Code To Log Out
FormsAuthentication.SignOut();
Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
Session.Clear();
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetExpires(DateTime.Now.AddSeconds(-1));
Response.Cache.SetNoStore();
Response.AppendHeader("Pragma", "no-cache");
return View("SignIn");
This is crazy. I have two bald spots now.
1) shouldn't your call to Response.Cookies.Remove(FormsAuthentication.FormsCookieName); be Response.Cookies.Remove(whatever-the-user-name-is);?
2) try sending an expired cookie back to the browser.
FormsAuthentication.SignOut();
// replace with username if this is the wrong cookie name
Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
Session.Clear();
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetExpires(DateTime.Now.AddSeconds(-1));
Response.Cache.SetNoStore();
Response.AppendHeader("Pragma", "no-cache");
// send an expired cookie back to the browser
var ticketExpiration = DateTime.Now.AddDays(-7);
var ticket = new FormsAuthenticationTicket(
1,
// replace with username if this is the wrong cookie name
FormsAuthentication.FormsCookieName,
DateTime.Now,
ticketExpiration,
false,
String.Empty);
var cookie = new System.Web.HttpCookie("user")
{
Expires = ticketExpiration,
Value = FormsAuthentication.Encrypt(ticket),
HttpOnly = true
};
Response.Cookies.Add(cookie);
return View("SignIn");
You can not directly delete a cookie on a client's computer. When you calls the Cookies.Remove method the cookie is deleted on a server side. To delete the cookie on a client's side it's necessary to set the cookie's expiration date to a past date.
HttpCookie cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (cookie != null)
{
cookie.Expires = DateTime.Now.AddDays(-1);
HttpContext.Current.Response.Cookies.Add(cookie);
}
I hope this helps you.
If you want to apply the "no cache on browser back" behavior on all pages then you should put it in global.asax.
protected void Application_BeginRequest()
{
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetExpires(DateTime.UtcNow.AddHours(-1));
Response.Cache.SetNoStore();
}
hope it helps someone !