My ASP.NET MVC web application allows administrators to change their own, or other users' usernames.
Users are logged in by calling FormsAuthentication.SetAuthCookie(userName [string], createPersistentCookie [bool]). They are logged out by calling FormsAuthentication.SignOut(). I understand that after updating the username I'd need to sign them out and back in again. But how do I retrieve the existing value of createPersistentCookie? e.g. how do I retain their original 'remember me' setting when signing them back in?
var cookieName = FormsAuthentication.FormsCookieName;
var request = HttpContext.Current.Request;
var cookie = request.Cookies.Get(cookieName);
if (cookie == null)
return;
try
{
var ticket = FormsAuthentication.Decrypt(cookie.Value);
//This should give you what you want...
bool isPersistent = ticket.IsPersistent;
}
catch (Exception ex)
{
//Logging
}
Related
we have a site in which few pages travels in https and few in http and we have implemented a Generic http handler(ashx) for login and auth.
We are using Forms authentication in our site and manually setting Auth cookie in Handler
We have set the access-control-allow-origin to support the mixed of http and https
For security needs we tried to access the handler in https protocol and facing the below issue
When the handler (https) is getting called through AJAX from the secure page (https) everything is working fine
But when the same handler (https) is getting called thorugh AJAX from the non secure page (http) it is not working as in previous case.
In the AJAX success call we are just refreshing the page in javascript.
In 1st case the user name is coming
In 2nd case the same login link will be there.
I am not sure of what is the problem here ? and don't know where to look out ?
Kindly help me out
Thanks
public void ProcessRequest (HttpContext context)
{
string Message = "";
try
{
string EmailId = HttpContext.Current.Request["emailid"].ToString();
string Password = HttpContext.Current.Request["Password"].ToString();
string User = "";
Entities.UserTable user = null;
UserDAL userDAL = new UserDAL();
user = userDAL.UserLogin(EmailId, Password);
if (user != null)
{
int userId = user.Id;
string roles = "User";
bool rememberUserName = true;
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1, // Ticket version
user.Id.ToString(),// Username to be associated with this ticket
DateTime.Now, // Date/time ticket was issued
DateTime.Now.AddYears(20), // Date and time the cookie will expire
rememberUserName, // if user has chcked rememebr me then create persistent cookie
roles, // store the user data, in this case roles of the user
FormsAuthentication.FormsCookiePath); // Cookie path specified in the web.config file in <Forms> tag if any.
string hashCookies = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, hashCookies); // Hashed ticket
cookie.Expires = DateTime.Now.AddYears(20);
HttpContext.Current.Response.Cookies.Add(cookie);
Message = "success";
}
else
{
Message = "Please check your Mailid or password.";
}
}
catch(Exception ex)
{
Message = "Please contact Support";
}
context.Response.ContentType = "text/plain";
context.Response.Write(Message);
}
I'm using the latest sample code for MVC5.2 with Asp.Identity and Two Factor authentication.
With 2FA enabled, when a user logins, the get prompted for a code (sent by phone or email) and they have the option to "Remember Browser" - so that they don't get ask for codes again on that browser.
This is handled in the VerifyCode action
var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, isPersistent: model.RememberMe, rememberBrowser: model.RememberBrowser);
Note that model.RememberMe is not used in the default templates so it is false.
I find when I do this the .AspNet.TwoFactorRememberBrowser that gets set, expires on session end (so it does not remember the browser)
Now if I set isPersistent = true, .AspNet.TwoFactorRememberBrowser gets an expiration of 30 days which is great, but the .AspNet.ApplicationCookie also gets a 30 day expiration - which means that when I close the browser and re-open, I am automatically logged in.
I want it so that it doesn't persist my login, but that it will persist my choice of remembering the 2FA code. Ie the user should always have to login, but they should not be asked for a 2fa code if they have already save it.
Has anybody else seen this, or am I missing something?
It doesn't seem like this code was designed to set more than one identity cookie in the same request/response because the OWIN cookie handlers end up sharing the same AuthenticationProperties. This is because the AuthenticationResponseGrant has a single principal, but the principal can have multiple identities.
You can workaround this bug by altering and then restoring the AuthenticationProperties in the ResponseSignIn and ResponseSignedIn events specific to the 2FA cookie provider:
//Don't use this.
//app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
//Set the 2FA cookie expiration and persistence directly
//ExpireTimeSpan and SlidingExpiration should match the Asp.Net Identity cookie setting
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationType = DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie,
AuthenticationMode = AuthenticationMode.Passive,
CookieName = DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie,
ExpireTimeSpan = TimeSpan.FromHours(2),
SlidingExpiration = true,
Provider = new CookieAuthenticationProvider
{
OnResponseSignIn = ctx =>
{
ctx.OwinContext.Set("auth-prop-expires", ctx.Properties.ExpiresUtc);
ctx.OwinContext.Set("auth-prop-persist", ctx.Properties.IsPersistent);
var issued = ctx.Properties.IssuedUtc ?? DateTimeOffset.UtcNow;
ctx.Properties.ExpiresUtc = issued.AddDays(14);
ctx.Properties.IsPersistent = true;
},
OnResponseSignedIn = ctx =>
{
ctx.Properties.ExpiresUtc = ctx.OwinContext.Get<DateTimeOffset?>("auth-prop-expires");
ctx.Properties.IsPersistent = ctx.OwinContext.Get<bool>("auth-prop-persist");
}
}
});
Make sure to set the same ExpireTimeSpan and SldingExpiration as your main Asp.Net Identity cookie to preserve those settings (since they get merged in the AuthenticationResponseGrant).
This still appears to be an issue in Identity 2.2.1 (It may be fixed in Asp.Net Identity 3.0 - but that is currently pre-released and requires a later version of .Net framework that 4.5)
The following work around seems ok for now:
The cookie is getting set on the SignInManager.TwoFactorSignInAsync with the wrong values, so on Success of the VerifyCode action, I reset the cookie to be persistent and give it the expiry date that I wish (in this case I set it to a year)
public async Task<ActionResult> VerifyCode(VerifyCodeViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
} var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, isPersistent: model.RememberMe, rememberBrowser: model.RememberBrowser);
switch (result)
{
case SignInStatus.Success:
// if we remember the browser, we need to adjsut the expiry date as persisted above
// Also set the expiry date for the .AspNet.ApplicationCookie
if (model.RememberBrowser)
{
var user = await UserManager.FindByIdAsync(await SignInManager.GetVerifiedUserIdAsync());
var rememberBrowserIdentity = AuthenticationManager.CreateTwoFactorRememberBrowserIdentity(user.Id);
AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTime.UtcNow.AddDays(365) }, rememberBrowserIdentity);
}
return RedirectToLocal(model.ReturnUrl);
What you can do is assign your own CookieManager class that modifies the expiration time of the TwoFactorRememberBrowserCookie. This seems better than modifing the cookie in Application_PostAuthenticateRequest.
This works around the problem that you can either persist all or none of the authentication cookies.
Put this in your ConfigureAuth, the last line sets your custom cookie manager.
public void ConfigureAuth(IAppBuilder app)
{
// left out all but the modified initialization of the TwoFactorRememberBrowserCookie
var CookiePrefix = ".AspNet.";
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationType = DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie,
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Passive,
CookieName = CookiePrefix + DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie,
ExpireTimeSpan = TimeSpan.FromDays(14),
CookieManager = new TwoFactorRememberBrowserCookieManager()
});
}
Use this CookieManager class only for the TwoFactorRememberBrowserCookie.
When you do not persist cookies in TwoFactorSignInAsync, unfortunately the ExpirationTimeout is ignored.
So just set it again in the CookieManager (This is a modified version of the cookie manager coming from Microsoft.Owin.Infrastructure.CookieManager):
public class TwoFactorRememberBrowserCookieManager : Microsoft.Owin.Infrastructure.ICookieManager
{
string CookiePrefix = ".AspNet.";
Microsoft.Owin.Infrastructure.ICookieManager cm = new Microsoft.Owin.Infrastructure.ChunkingCookieManager();
public void AppendResponseCookie(IOwinContext context, string key, string value, CookieOptions options)
{
if (key == CookiePrefix + DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie) {
options.Expires = DateTime.UtcNow.AddDays(14);
}
cm.AppendResponseCookie(context, key, value, options);
}
public void DeleteCookie(IOwinContext context, string key, CookieOptions options)
{
cm.DeleteCookie(context, key, options);
}
public string GetRequestCookie(IOwinContext context, string key)
{
return cm.GetRequestCookie(context, key);
}
}
This is what you will get:
Works for me that way.
Here, I am trying to authenticate user via login and after that I want to skip permissions dialog. But I am unable to achieve this, as it always asking for permissions for app to the user. My intention is if user is not logged into the facebook he/she should be prompted for facebook login and then I will fetch public information by using method Get("/me"). Let me know what I am doing wrong here.
public string GetFBAccessToken(string strAppID, string strAppSecret, string strUrl)
{
// Declaring facebook client type
var vFB = new FacebookClient();
string strAccessTok = string.Empty;
try
{
if (!string.IsNullOrEmpty(strAppID) && !string.IsNullOrEmpty(strAppSecret) && !string.IsNullOrEmpty(strUrl))
{
// Getting login url for facebook
var loginUrl = vFB.GetLoginUrl(new
{
client_id = strAppID,
client_secret = strAppSecret,
redirect_uri = strUrl,
response_type = "code",
state = "returnUrl",
//scope = "",
display = "popup"
});
// Redirecting the page to login url
if (HttpContext.Current.Request.QueryString["code"] == null)
{
HttpContext.Current.Response.Redirect(loginUrl.AbsoluteUri);
}
// Fetching the access token from query string
if (HttpContext.Current.Request.QueryString["code"] != null)
{
dynamic result = vFB.Post("oauth/access_token", new
{
client_id = strAppID,
client_secret = strAppSecret,
redirect_uri = strUrl,
code = HttpContext.Current.Request.QueryString["code"]
});
// Getting access token and storing in a variable
strAccessTok = result.access_token;
}
}
return strAccessTok;
}
catch (Exception ex)
{
//if (HttpContext.Current.Request.QueryString["response_type"] == "code")
//{
// var fb = new FacebookClient();
// var details = fb.Get("/me");
//}
return strAccessTok;
}
}
Regardless to the platform/ language you are using; solution can be as follows.
check use's logged in status. https://developers.facebook.com/docs/reference/javascript/FB.getLoginStatus/
based on Response status, forcefully call your action (i.e. Log in, Get Permission or any additional action if user is already connected). For Log in check this reference document from FB. https://developers.facebook.com/docs/facebook-login/login-flow-for-web/
No. You cannot skip the Login Dialog.
In fact, it is really important for an APP owner to build a trust relationship with your users. I would recommend you to follow the Login Best Practices while authenticating the users using your APP.
I am a PHP guy but am in the process of making a log-in page in ASP.NET MVC4. I am expecting to store the ID, Username and Roles of the user in session. So far what I am doing is as follows. If I am correct it saves the cookie with the username.
[HttpPost]
public ActionResult Login(Models.UserLoginModel user)
{
if (ModelState.IsValid)
{
Models.User u = new Models.User();
if (u.IsValid(user.Username, user.Password))
{
FormsAuthentication.SetAuthCookie(user.Username, user.RememberMe);
return RedirectToAction("Index", "Accounts");
}
else
{
ModelState.AddModelError("", "Login data is incorrect!");
}
}
return View(user);
}
My interest is to store more information and control validation time. I was advised and asked to use FormAuthenticationTicket class. I replaced FormsAuthentication.SetAuthCookie(user.Username, user.RememberMe); with
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket
(
1,
user.Username,
DateTime.Now,
DateTime.Now.AddMinutes(30),
false,
"Some User Data",
FormsAuthentication.FormsCookiePath
);
Response.Cookies.Add
(
new HttpCookie
(
FormsAuthentication.FormsCookieName,
FormsAuthentication.Encrypt(ticket)
)
);
It looks cool, I didn't test it though, has flexibility. But problem is how I could receive this information.
How can I get these information back and determine if the user is logged in and also other necessary information saved inside the FormsAuthenticationTicket.
Thanks in advance.
Like you would any ticket:
var cookie = Request.Cookies[FormsAuthentication.FormsCookieName];
var ticketInfo = FormsAuthentication.Decrypt(cookie.Value);
Since it's a security ticket, if you don't need to access the information from client JavaScript, also set HttpOnly to true. This means the cookie is only accessible on the server.
My login code, after authentication:
var authTicket = new FormsAuthenticationTicket(
1,
userName,
DateTime.Now,
DateTime.Now.AddMinutes(20), // expiry
false,
roles,
"/");
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(authTicket));
Response.Cookies.Add(cookie);
and, thanks to Darin Dimitrov, I have a custom Authorize attribute:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class TJAuthorize : AuthorizeAttribute {
public override void OnAuthorization(AuthorizationContext filterContext) {
string cookieName = FormsAuthentication.FormsCookieName;
if (!filterContext.HttpContext.User.Identity.IsAuthenticated ||
filterContext.HttpContext.Request.Cookies == null || filterContext.HttpContext.Request.Cookies[cookieName] == null) {
HandleUnauthorizedRequest(filterContext);
return;
}
var authCookie = filterContext.HttpContext.Request.Cookies[cookieName];
var authTicket = FormsAuthentication.Decrypt(authCookie.Value);
string[] roles = authTicket.UserData.Split(',');
var userIdentity = new GenericIdentity(authTicket.Name);
var userPrincipal = new GenericPrincipal(userIdentity, roles);
filterContext.HttpContext.User = userPrincipal;
base.OnAuthorization(filterContext);
}
This all works beautifully when I'm working in a browser session. But now I am working with a Flash/Adobe Air client, and the authentication attribute is causing a failure. By putting debug statements into the code, I can tell that:
filterContext.HttpContext.User.Identity.IsAuthenticated
is false - even after a successful login!
Why should there be any difference between using a browser client and an Air client? And how do I fix this?
EDIT: Another clue: after putting in some more debug statements, I have found that the filterContext.HttpContext.User.Identity is not correctly set when making the call from Air - the Name property comes out blank! Session ID is correct, cookie ID is correct - but the User.Identity is not set. Any ideas why this might be happening?
Perhaps HttpCookieMode (http://msdn.microsoft.com/en-us/library/system.web.httpcookiemode.aspx) is set to the wrong value?
Default is UseDeviceProfile ... what happens when you force it to UseCookies ?
It's a longshot, but IsAuthenticated depends on client's ASPXAUTH cookie (or whatever you've named id) being sent with request. Make sure that flash/air is sending that cookie (by wireshark or any other network tool)
Does the HttpContext.User.Identity show up in the Application_AuthorizeRequest in global.asax?