What I'm trying to do is set cookie to Response after some Validation on backend.
Here is my code:
Controller
public class MyController : Controller
{
public ActionResult Index()
{
var cookie = new HttpCookie("cookie-key", "true")
{
Expires = DateTime.Now.AddDays(30)
};
System.Web.HttpContext.Current.Response.SetCookie(cookie);
}
}
But after that in System.Web.HttpContext.Current.Request.Cookies there is no cookie with key "cookie-key".
I've added a <sessionState cookieless="UseCookies" /> to my web.config file but it's doesn't help.
How can I make it work as it should? Am I missing something?
Edit:
I've changed SetCookie to Cookies.Add but it doesn't helped.
Updated code:
public class MyController : Controller
{
public ActionResult Index()
{
var cookie = new HttpCookie("cookie-key", "true")
{
Expires = DateTime.Now.AddDays(30)
};
System.Web.HttpContext.Current.Response.Cookies.Add(cookie);
}
}
Try this code:
HttpCookie cookie = new HttpCookie("cookie-key","true");
cookie.Expires = DateTime.Now.AddDays(30);
cookie.Path = "/";
Response.Cookies.Add(cookie);
Response.SetCookie(cookie);
1) Probably you need write location(path)
2) Sometimes good do Cookies.Add AND SetCookies
SetCookie is for already existing Cookies.
You need to use Response.Cookies.Add(...)
That is all that is needed for your code to work.
Here is some code that I have in production that work 100%:
public ActionResult Login(string username, string password)
{
var userService = new UserService();
var loginResult = userService.ValidatePassword(username, password);
if (loginResult == null) return Redirect("/");
Response.Cookies.Add(new HttpCookie("tok", Uri.EscapeDataString(userService.CreateToken(loginResult)))
{
Expires = DateTime.Now.AddYears(1)
});
return Redirect("/admin");
}
Try this.
using System.Net;
var response = System.Web.HttpContext.Current.Response;
foreach( Cookie cook in response.Cookies)
{
// set your expiration here
cook.Expires = DateTime.MinValue;
}
Found reason why this doesn't work. It was error with javascript framework reactjs. There was 500 internal server error but because of ajax call couldn't diagnose this.
Thanks guys for answers :)
Related
This simple code has left me perplexed.
From this controller action method, I create a cookie , give it an expiration and set it to be HttpOnly. The cookie gets created correctly, added to the Response, looks correct on the browser debugger , however when returned back into the same code after refresh , loses expiration and HttpOnly flag. The cookie itself is still there , but the values are lost. If I watch Request.Cookies["mycookie"] back into the same controller/action method after a trip to the browser, the values are gone - the cookie itself is not deleted though.
If somebody understands this behaviour please explain, what might be happening here-
public class HomeController : Controller
{
public ActionResult Index()
{
if (this.ControllerContext.HttpContext.Request.Cookies["mycookie"] == null)
{
HttpCookie cookie = Response.Cookies["mycookie"];
cookie["mycookie"] = "test";
cookie.Expires = DateTime.Now.AddDays(90);
cookie.HttpOnly = true;
this.ControllerContext.HttpContext.Response.SetCookie(cookie);
}
return View();
}
The problem is this line: return View();
The cookie cannot be set and then read again (server-side) in the same round trip to the server. So, you need to create a second request for the cookie to be available. The simplest way is to force a second request by calling RedirectToAction, although you could use some clever AJAXy way of doing it so it appears to be the same request.
See this post for a working example - here is the part where the cookie is written and deleted.
public class CookieController : Controller
{
public ActionResult Create()
{
HttpCookie cookie = new HttpCookie("Cookie");
cookie.Value = "Hello Cookie! CreatedOn: " + DateTime.Now.ToShortTimeString();
this.ControllerContext.HttpContext.Response.Cookies.Add(cookie);
return RedirectToAction("Index", "Home");
}
public ActionResult Remove()
{
if (this.ControllerContext.HttpContext.Request.Cookies.AllKeys.Contains("Cookie"))
{
HttpCookie cookie = this.ControllerContext.HttpContext.Request.Cookies["Cookie"];
cookie.Expires = DateTime.Now.AddDays(-1);
this.ControllerContext.HttpContext.Response.Cookies.Add(cookie);
}
return RedirectToAction("Index", "Home");
}
}
Ashiquizzaman is also correct in that you are not setting the value of the cookie, but that is only half of the problem.
Please see this code below.
var request=this.ControllerContext.HttpContext.Request;
var response =this.ControllerContext.HttpContext.Response;
//OR
// var request=System.Web.HttpContext.Current.Request;
//var response =System.Web.HttpContext.Current.Response;
if (request.Cookies["mycookie"] == null)
{
HttpCookie cookie= new HttpCookie("mycookie");
cookie.Value = "test";//your problem hear.
cookie.Expires = DateTime.Now.AddDays(90);
cookie.HttpOnly = true;
response.Cookies.Add(cookie);
}
else//need to update your cookies then use this block or not
{
HttpCookie cookie=Request.Cookies["mycookie"];
cookie.Value = "test";//your problem hear.
cookie.Expires = DateTime.Now.AddDays(90);
cookie.HttpOnly = true;
//response.Cookies.SetCookie(cookie);
response.Cookies.Set(cookie);//To update a cookie, you need only to set the cookie again using the new values.
}
Hopefully it's help you.
I have a web system developed with ASP.NET MVC 4.
We have a user management that allows users to edit/delete other users.
On delete function, currently i'm doing only a delete on database.
So here is my login controller/method:
[HttpPost]
public ActionResult Login(LoginViewModel loginViewModel)
{
if (_loginService == null)
_loginService = new LoginService();
var result = _loginService.Login(loginViewModel.User, loginViewModel.Password);
if (!result.Error)
{
var userData = JsonConvert.SerializeObject(result.User);
FormsAuthentication.SetAuthCookie(result.User.Id, false);
var ticket = new FormsAuthenticationTicket(1, result.Id, DateTime.Now, DateTime.Now.AddMinutes(9999), true, userData, FormsAuthentication.FormsCookiePath);
var encryptedCookie = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedCookie) { Expires = DateTime.Now.AddHours(14) };
Response.Cookies.Add(cookie);
}
return new JsonResult
{
Data = result
};
}
And I treat that return on client side with some javascript. This is working fine by now.
For every Controller that user must be authenticated, I have [Authorize] attribute.
Lets say that I just logged in with user ABC. As long as ABC cookie is alive he can navigate fine.. the problem is when some user (lets say ZXC) deletes user ABC, he will still navigate fine until the cookie expires.
Is there a way to drop ABC session on IIS in the moment ZXC deletes him from database?
I don't know.. force a cookie expire. I just don't wanna implement a consult for each action done in navigation to check if the user is still "alive" in database.
Any ideas, suggestions?
Firstly, no. There is no way to access cookies in another session as they only exist for the lifetime of the request/response. However, you could store a static List of all current authenticated users and invalidate them that way.
This is a bit problematic because in the case that the App Pool recycles - all users will be 'logged out'. If this is not an issue for you (i.e. the app pool recycles at 2am and it is for a business system that does not operate at 2 am) then you can try this...
Code provided is untested
source: https://msdn.microsoft.com/en-us/library/system.web.security.formsauthenticationmodule.authenticate
EDIT:
I was not removing the cookie from the request and expiring it in the response.
In the Global.asax
private static List<string> _authenticatedUsers = new List<string>();
public static AuthenticateUser (MyApplicationUser user)
{
if(!_authenticatedUsers.ContainsKey(user.Username))
{
_authenticatedUsers.Add(user.Username);
}
}
public static DeauthenticateUser (MyApplicationUser user)
{
if(_authenticatedUsers.ContainsKey(user.Username))
{
_authenticatedUsers.Remove(user.Username);
}
}
public void FormsAuthentication_OnAuthenticate(object sender, FormsAuthenticationEventArgs args)
{
if (FormsAuthentication.CookiesSupported)
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
try
{
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(
Request.Cookies[FormsAuthentication.FormsCookieName].Value);
MyApplicationUser user = JsonConvert.DeserializeObject(ticket.UserData);
if(user == null || !_authenticatedUsers.Any(u => u == user.Username))
{
// this invalidates the user
args.User = null;
Request.Cookies.Remove(FormsAuthentication.FormsCookieName);
HttpCookie myCookie = new HttpCookie(FormsAuthentication.FormsCookieName);
DateTime now = DateTime.Now;
myCookie.Value = "a";
myCookie.Expires = now.AddHours(-1);
Response.Cookies.Add(myCookie);
Response.Redirect(FormsAuthentication.LoginUrl);
Resonpse.End();
}
}
catch (Exception e)
{
// Decrypt method failed.
// this invalidates the user
args.User = null;
Request.Cookies.Remove(FormsAuthentication.FormsCookieName);
HttpCookie myCookie = new HttpCookie(FormsAuthentication.FormsCookieName);
DateTime now = DateTime.Now;
myCookie.Value = "a";
myCookie.Expires = now.AddHours(-1);
Response.Cookies.Add(myCookie);
Response.Redirect(FormsAuthentication.LoginUrl);
Resonpse.End();
}
}
}
else
{
throw new HttpException("Cookieless Forms Authentication is not " +
"supported for this application.");
}
}
In your login action
public ActionResult Login(LoginViewModel loginViewModel)
{
...
if (!result.Error)
{
...
MvcApplication.AuthenticateUser(result.User);
...
}
...
}
In your logout action
public ActionResult Logout(...)
{
...
MvcApplication.DeauthenticateUser(user);
...
}
In your delete method
...
MvcApplication.DeauthenticateUser(user);
...
I'm working on a REST API Project where users log in and make calls. In order to do that, i create a cookie where i encrypt the username. My server is deployed and something really weird is going on. From time to time i just don't receive the cookies in the response. In this case I just have to make any modification in the web.config file and it starts working again... I really don't understand why... Any ideas ?
Here's my login code :
[Route("login", Order = 1)]
[HttpPost]
[HttpGet]
public async Task<HttpResponseMessage> Login([FromUri] string userId, [FromUri] string userPassword)
{
try
{
Tuple<string, string> result = userService.Authenticate(userId, userPassword);
string sessionIds = result.Item1;
string message = result.Item2;
CookieHeaderValue cookie = CreateSessionsCookie(sessionIds);
cookie.Secure = true;
// Store username for later use
CookieHeaderValue userCookie = new CookieHeaderValue(Strings.Id, Encryption.Protect(userId, Strings.Id));
userCookie.Secure = true;
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, JsonConvert.DeserializeObject(message));
response.Headers.AddCookies(new CookieHeaderValue[] { cookie, userCookie });
return response;
}
catch (Exception ex)
{
return HandleException(ex);
}
}
It is a bit of an anti-pattern to use a cookie for a restful web service. Just include the username in the header instead.
As to why this is timing out I suspect it has to do with your session timing out.
I've got an existing C# project with the following POST method:
// POST: /Account/ExternalLogin
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
{
return new ExternalLoginResult(provider, Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
}
Which uses:
OAuthWebSecurity.Login(provider, ...);
I'm kinda new to OAuth, and haven't done anything with it in this C# project. I do however implemented Google-authorization on an Android App, and I want to use the following class/method for the HttpPost:
public class TaskPostAPI extends AsyncTask<String, Void, String>
{
GoogleApiClient googleAPI;
public TaskPostAPI(GoogleApiClient googleAPI){
this.googleAPI = googleAPI;
}
#Override
protected String doInBackground(String... urls){
String response = "";
for(String url : urls){
DefaultHttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(url);
try{
List<NameValuePair> nvPairs = new ArrayList<NameValuePair>(2); //(3);
//nvPairs.add(new BasicNameValuePair("personName", Plus.PeopleApi.getCurrentPerson(googleAPI).getDisplayName()));
//nvPairs.add(new BasicNameValuePair("personGooglePlusProfile", Plus.PeopleApi.getCurrentPerson(googleAPI).getUrl()));
//nvPairs.add(new BasicNameValuePair("personEmail", Plus.AccountApi.getAccountName(googleAPI)));
nvPairs.add(new BasicNameValuePair("provider", ???));
URL u = new URL(url);
nvPairs.add(new BasicNameValuePair("returnUrl", u.getProtocol() + "://" + u.getHost()));
post.setEntity(new UrlEncodedFormEntity(nvPairs));
HttpResponse execute = client.execute(post);
InputStream content = execute.getEntity().getContent();
BufferedReader buffer = new BufferedReader(new InputStreamReader(content));
String s = "";
while((s = buffer.readLine()) != null)
response += s;
}
catch(Exception ex){
ex.printStackTrace();
}
}
return response;
}
}
So my question: What should I put at the POST-method's ???. So what is the Provider? In my Android App I log in using Google, and got the GoogleApiClient and Person (com.google.android.gms.plus.model.people.Person).
At this website I see the following url: https://accounts.google.com/o/oauth2/auth. Is this the url I should use as provider? Or should I add parameters to this url? Or should I use a completely different string as provider?
PS: If someone can send me a link to a list of provider-urls (like Google's, Facebook's, Twitter's, etc.) and how to use them for the C# OAuthWebSecurity I would appreciate it.
Thanks in advance for the responses.
Ok it turns out it was very simple.. The provider is just "Google", nothing more, nothing less.. This provider name I found at the RegisterClient method in de C# project:
OAuthWebSecurity.RegisterClient(client, "Google", extraData);
Now the POST works, except for the message "The required anti-forgery cookie "__RequestVerificationToken" is not present.", but at least I can continue. And adding a Cookie to a HttpPost isn't that hard I believe when using a CookieManager.
I am trying to get the grip on the Facebook SDK and at the same time transitioning from ASP.NET forms to MVC (finally). So please bear with me ..
I have created two controller actions:
FBLogon is execetued when the user clicks on the FB login button on the form.
He is then redirected to the FB login page.
Afterwards he gets sent back to the FBAuthorize page, which is supposed to parse the returned url for the access token. I get something like:
http://localhost:5000/account/FBAuthorize#access_token=199143326771791|827213759889396d5408fee6-100001815992604|BmYchAOMqSoZ2L0TYgCrtpoKP3M&expires_in=0
The problem I see, is that as the access_token is passed behind a #, asp.net cannot parse it on the server. Am I doing something fundamentaly wrong?
Code follows:
public ActionResult FBLogon()
{
var settings = ConfigurationManager.GetSection("facebookSettings");
IFacebookApplication current = null;
if (settings != null)
{
current = settings as IFacebookApplication;
if (current.AppId == "{app id}" || current.AppSecret == "{app secret}")
{
return View();
}
}
string[] extendedPermissions = new[] { "publish_stream", "offline_access" };
var oauth = new FacebookOAuthClient { ClientId = current.AppId, RedirectUri = new Uri("http://localhost:5000/account/FBAuthorize") };
var parameters = new Dictionary<string, object>
{
{ "response_type", "token" },
{ "display", "page" }
};
if (extendedPermissions != null && extendedPermissions.Length > 0)
{
var scope = new StringBuilder();
scope.Append(string.Join(",", extendedPermissions));
parameters["scope"] = scope.ToString();
}
var loginUrl = oauth.GetLoginUrl(parameters);
return Redirect(loginUrl.ToString());
}
public ActionResult FBAuthorize()
{
FacebookOAuthResult result;
if (FacebookOAuthResult.TryParse(Request.Url, out result))
{
if (result.IsSuccess)
{
var accesstoken = result.AccessToken;
}
else
{
var errorDescription = result.ErrorDescription;
var errorReason = result.ErrorReason;
}
}
return View();
}
Ok. The facebook docs say it quite clearly:
Because the access token is passed in
an URI fragment, only client-side code
(such as JavaScript executing in the
browser or desktop code hosting a web
control) can retrieve the token. App
authentication is handled by verifying
that the redirect_uri is in the same
domain as the Site URL configured in
the Developer App
from http://developers.facebook.com/docs/authentication/ ---> Client-side Flow Section.
So I'm sending the token back to my server to complete the authentication..
Update:
The sending back to the server I do using Javascript something like this:
var appId = "<%: Facebook.FacebookContext.Current.AppId %>";
if (window.location.hash.length > 0) {
accessToken = window.location.hash.substring(1);
var url = window.location.href.replace(/#/, '?');
window.location = url;
}
On the server then I have the following action. Not very nice but it works..
public ActionResult FBAuthorize()
{
FacebookOAuthResult result = null;
string url = Request.Url.OriginalString;
/// hack to make FacebookOuthResult accept the token..
url = url.Replace("FBAuthorize?", "FBAuthorize#");
if (FacebookOAuthResult.TryParse(url, out result))
{
if (result.IsSuccess)
{
string[] extendedPermissions = new[] { "user_about_me", "offline_access" };
var fb = new FacebookClient(result.AccessToken);
dynamic resultGet = fb.Get("/me");
var name = resultGet.name;
RegisterModel rm = new Models.RegisterModel();
rm.UserName = name;
rm.Password = "something";
rm.Email = "somethig";
rm.ConfirmPassword = "23213";
//Label1.Text = name;
//Response.Write(name);
//return RedirectToAction("register", "Account", rm);
ViewData["Register"] = rm;
return RedirectToAction("Register");
}
else
{
var errorDescription = result.ErrorDescription;
var errorReason = result.ErrorReason;
}
}
return View();
}
I found this post http://facebooksdk.codeplex.com/discussions/244568 on codeplex. I think this is what you need.
Note that instead of using the client-side flow, you need to use the server-side flow.
This is what you should do
Create a login link for server-side flow. After Authorization, facebook will return an url containing a code instead of a access token.
Then you request for a token from facebook using the code. this is my example
public ActionResult FBAuthorize()
{
FacebookOAuthClient cl = new FacebookOAuthClient(FacebookContext.Current);
FacebookOAuthResult result = null;
string url = Request.Url.OriginalString;
// verify that there is a code in the url
if (FacebookOAuthResult.TryParse(url, out result))
{
if (result.IsSuccess)
{
string code = result.Code;
// this line is necessary till they fix a bug *see details below
cl.RedirectUri = new UriBuilder("http://localhost:5000/account/FBAuthorize").Uri;
var parameters = new Dictionary<string, object>();
//parameters.Add("permissions", "offline_access");
Dictionary<String, Object> dict = (Dictionary<String, Object>)cl.ExchangeCodeForAccessToken(code, new Dictionary<string, object> { { "redirect_uri", "http://localhost:5000/account/FBAuthorize" } });
Object Token = dict.Values.ElementAt(0);
TempData["accessToken"] = Token.ToString();
return RedirectToAction ("ShowUser");
}
else
{
var errorDescription = result.ErrorDescription;
}
}
else
{
// TODO: handle error
}
return View();
}
*There is bug when using IIS in localhost, see the original post for details (the redirect uri when asking for the token must be the same as the one used asking for the code)
It is highly recommended to use IIS and not visual studio web server. There are many things that wont work in visual studio web server.
I am in the same spot you are at the moment.
We never get the Request.QueryString populated becasue of the "fragment" or # in the url.
Love to know if you solved this and how.
It does not look like the FacebookOAuthResult class was written to be used in web applications of any sort.
you can change the response type in you scope paramas to be "code" then it will send back a code in the querystring in which you can swap for a token.