I working on an ASP MVC login form.
I have pretty simple codes. A Startup class and an action trying to set the cookie. Below is my code :
Startup
which is located in App_Start (there is also a reference to it in <appSetting> with key="owin:AppStartup")
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "ApplicationCookie",
LoginPath = new PathString("/auth/login"),
});
}
}
The action method that is suppose to authenticate the user is :
[HttpPost]
public ActionResult Login(user model)
{
if(ModelState.IsValid)
{
var identity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Email, "admin#admin.com"),
new Claim(ClaimTypes.Name, "tom"),
new Claim(ClaimTypes.Role, "admin")
});
var ctx = Request.GetOwinContext();
var authManager = ctx.Authentication;
authManager.SignIn(identity);
return RedirectToAction("Index", "Home");
}
return View(model);
}
But this does not get the identity authenticated as #User.Authenticated is false in my _Layout.cshtml when return RedirectToAction("Index", "Home"); and also the debbuger shows that IsAuthenticated property is false (in the controller Login action and in the _Layout.cshtml.
I have checked that IIS is enabled for Anonymous authentication using my windows administrative tools and also I have checked that Startup is set when the application starts...
I seems that authManager.SignIn(identity) is not doing its job.
How can we solve this ?
debugger screenshot
ps : I do not even see the browser popup asking if I want to save the password (I popped only once during my tests even though the user was still not authenticated)
SignIn persists the user for future requests (via cookies), it does not alter the current request. You can directly set HttpContext.User for the current request if you want.
I also recall that you need to set the ClaimsIdentity AuthenticationType to CookieAuthenticationDefaults.AuthenticationType (or whatever auth type you're using to identify your middleware). Otherwise the cookie auth middleware won't activate.
Related
I have a razor pages project that creates a authentication scheme on a login page i hardcoded the name and password for testing purposes.
public async Task<IActionResult> OnPost()
{
if(ModelState.IsValid)
{
if(Credential.Username == "admin" && Credential.Password =="password")
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, Credential.Username),
new Claim(ClaimTypes.Email, "admin#gmail.com"),
new Claim("Tipo", "admin")
};
var identity = new ClaimsIdentity(claims, "CookieAuth");
ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(identity);
HttpContext.Session.SetString("Username", Credential.Username);
await HttpContext.SignInAsync("CookieAuth", claimsPrincipal);
return RedirectToPage("/Index");
}
}
return Page();
}
I have configured a simple authentication/authorization with policies to restrict acess
builder.Services.AddAuthentication("CookieAuth").AddCookie("CookieAuth", options =>
{
options.Cookie.Name = "CookieAuth";
options.LoginPath = "/Login/LoginIndex";
options.AccessDeniedPath = "/Login/AcessDenied";
My login is working fine and the cookie is created sucessfully however when i try to logout the cookie refuses to be removed/cleared from the browser and im not too sure why, heres my logout method, it gets called by a button on a partial view on the nav bar
public async Task<ActionResult> OnPost()
{
await HttpContext.SignOutAsync("CookieAuth");//cookie nao apaga, inspecionar
return RedirectToPage("/Index");
}
i have tried other solutions from previous threads but noone seem to fix my issue, the post method gets called and the cookie persist in the browser making me have to delete it manually for other testing.
I have found the problem. The problem was in the #model reference of the logout page, I had cloned a page view before to save time doing HTML and never REALIZED THE LOGOUT PAGE MODEL WAS DIFFERENT FROM THE LOGOUTMODEL THAT CONTAINED THE METHOD. Anyways, after switching the model it works and the cookie is gone, so all good.
in a non core asp mvc application I had a controller action for signout the user globaly
it looked like this
public ActionResult Logout()
{
Request.GetOwinContext().Authentication.SignOut();
return Redirect("/");
}
now I have a asp core client and want a logout I tried
public async Task<ActionResult> LogOut()
{
if (User.Identity.IsAuthenticated)
{
await HttpContext.Authentication.SignOutAsync("Cookies");
}
return Redirect("/");
}
Update
Now it seems like I get logout, but I'm redirect to a site which requires auth.
I can see that i'm shortly redirected to identity server back again, which automatically sings me again in.
Summarized:
I get logged out in my asp application but not at the identity server.
How can I globally signout ? So that I need to resign in at the identity server?
If you use OpenIdConnect authentication, you also need to remote signout. Try to change your code something like below:
public async Task<ActionResult> LogOut()
{
if (User.Identity.IsAuthenticated)
{
await HttpContext.Authentication.SignOutAsync("Cookies");
await context.Authentication.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties()
{
RedirectUri = "<signed out url>"
});
// if you use different scheme name for openid authentication, use below code
//await context.Authentication.SignOutAsync(<your openid scheme>, new AuthenticationProperties()
//{
// RedirectUri = "/signedout"
//});
}
return Redirect("/");
}
See original sample https://github.com/aspnet/Security/blob/dev/samples/OpenIdConnectSample/Startup.cs
I've came across the same issue and to solve it I had to clear the Response cookies myself in my IdentityServer application.
var cookies = HttpContext.Request.Cookies.Keys;
foreach (var cookie in cookies)
{
HttpContext.Response.Cookies.Delete(cookie, new CookieOptions
{
Domain = "localhost" // Your host name here
});
}
// SignOutAsync depends on IdentityServer4 > Microsoft.AspNetCore.Http.Authentication
await HttpContext.Authentication.SignOutAsync();
What I did was, when I client wants to sign-out, I redirect it to my IdentityServer app which clear the Response Cookies as above.
In this code I'm deleting all the Cookies for localhost, however you can add a filter there and delete only cookies that IdentityServer makes use of to persist the user authentication.
Below you'll find more details on this implementation.
http://benjii.me/2016/04/single-sign-out-logout-identity-server-4/
Here is the official documentation for ASP.NET Identity Server Single Sign-Out:
single sign out documentation
This is a sample tooked from their demo implementation:
public ActionResult Signout()
{
Request.GetOwinContext().Authentication.SignOut();
return Redirect("/");
}
public void SignoutCleanup(string sid)
{
var cp = (ClaimsPrincipal)User;
var sidClaim = cp.FindFirst("sid");
if (sidClaim != null && sidClaim.Value == sid)
{
Request.GetOwinContext().Authentication.SignOut("Cookies");
}
}
I am working on MVC5 Project, it works fine on my system but it's behaving strange after deployment on server. I used OWIN for authentication, it works fine for first login, but after few seconds if I refresh the page, it redirects me back to the login page (This on happens on deployed server).
My Code:
public void ConfigureAuth(IAppBuilder app)
{
// Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login")
});
// Use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);}
I used [Authorize] on my controller.
[Authorize]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
}
Here's my login code:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginUserModel model, string returnUrl)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindAsync(model.userName, model.password);
if (user != null)
{
return RedirectToLocal(returnUrl);
}
}
}
At a glance, its either that the cookie is not being set correctly or at all. Or that the ModelState for some reason is not valid and your redirect is never hit.
Check out the link :-
http://coding.abel.nu/2014/06/understanding-the-owin-external-authentication-pipeline/
It should help with your configuration of OWIN middleware.
I added machineKey and sessionState in my Web.config file and that resolved the issue.
I had a similar issue which was caused by not using SSL. Choose the project in Solution Explorer, hit F4 and change to use SSL. Login requires SSL, a setting you can change but if set wrong it causes this confusing loop.
In my application, all my authentication happens with Google - ie - all my users are Google Accounts.
I don't need users to need to register in my app, just sign in using a Google account. However, I do want to manage Roles for the users with ASP.net Identity (I think)
With that in mind, on successful external authentication, I create an ASP.net Identity user (if one doesn't exist)
So, I've got my ExternalLoginCallback as follows:
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var authenticationManager = Request.GetOwinContext().Authentication;
var loginInfo = await authenticationManager.GetExternalLoginInfoAsync();
//successfully authenticated with google, so sign them in to our app
var id = new ClaimsIdentity(loginInfo.ExternalIdentity.Claims, DefaultAuthenticationTypes.ApplicationCookie);
authenticationManager.SignIn(id);
//Now we need to see if the user exists in our database
var user = UserManager.FindByName(loginInfo.Email);
if (user == null)
{
//user doesn't exist, so the user needs to be created
user = new ApplicationUser { UserName = loginInfo.Email, Email = loginInfo.Email };
await UserManager.CreateAsync(user);
//add the google login to the newly created user
await UserManager.AddLoginAsync(user.Id, loginInfo.Login);
}
return RedirectToLocal(returnUrl);
}
Idea being, I can now manage users, add roles, check if users are in roles, etc....
Firstly, is this a sensible approach? Or have I over complicated it?
One issue I'm having, however, is with logging out of my application
My Logout action looks like:
public ActionResult LogOut()
{
HttpContext.GetOwinContext().Authentication.SignOut();
return RedirectToAction("Index", "Home");
}
My Index action is decorated with the [Authorize] attribute -
However, when I 'logout' - it redirects to Home.Index - but I still seem to be logged in?
According to this ASPNet Identity Work Item, this is by design, and you need to call directly to Google's API in order to log the user out.
completing the post Logout link with return URL (OAuth)
Here is a solution that work for me :
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
return Redirect("https://www.google.com/accounts/Logout?continue=https://appengine.google.com/_ah/logout?continue=https://[url-of-your-site]");
}
I've started a new MVC 5 site, using the new Asp.Net Identity with Owin. In my "account" controller which has the attribute [Authorize], I have fairly standard actions;
// GET: /User/Login
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
// POST: /User/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
try
{
if (ModelState.IsValid)
{
var userApi = new UserService();
var apiUser = await userApi.LogIn(UserManager, model.CardNumber, model.Pin, model.RememberMe);
if (apiUser != null)
{
await SignInAsync(apiUser, model.RememberMe);
if (string.IsNullOrEmpty(returnUrl))
{
return RedirectToAction("UserLoggedIn", "User");
}
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
}
}
}
catch (Exception ex)
{
Trace.TraceError("Cannot login {0}", ex.ToString());
Response.AppendToLog(ex.ToString());
ModelState.AddModelError("", ex.ToString());
}
// If we got this far, something failed, redisplay form
return View(model);
}
My question is in regards to the returnUrl behavior, the code above works in the sense that, if a user is not logged in and calls a action in a controller that has the attribute [Authorize], it gets sent to the login actions above and then returned to the controller/action that was requested. Which is great, BUT how?? And is it safe?
In this article about "Preventing open redirect attacks"(for earlier versions of Asp.Net MVC) the recommendation is to do a check on the returnUrl that it's a local url before doing the redirect, is that something I should still do or is it now handled by the framework?
Cheers,
Ola
You need to check if the url is local indeed using this method (it is not handled by the framework automatically): http://msdn.microsoft.com/en-us/library/system.web.mvc.urlhelper.islocalurl%28v=vs.118%29.aspx
if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
As Sandeep Phadke told, the returnUrl Parameter is filled, because of configuration in startup.Auth.cs.
The CookieAuthenticationOptions has a property ReturnUrlParameter which is by Default set to "returnUrl". That is the reason, why it looks like magic. You can Change it to whatever you want:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
ReturnUrlParameter = "returnTo"
});
Then you can Change the AccountController Login-Action to:
[AllowAnonymous]
public ActionResult Login(string returnTo)
{
ViewBag.ReturnUrl = returnTo;
return View();
}
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Controller");
}
To answer your first question on how the redirect Url is setup, it configured in Startup.Auth.cs which is called from Startup.cs and is marked with an attribute which is probably looked for by the OWIN framework on app startup and both files partial extend a Startup class.
In Startup.Auth.cs there's a class to configure authentication options and usually has the following code
public partial class Startup
{
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
CookieSecure = CookieSecureOption.Always
});
// Use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// ....
// I deleted code which is commented out if you selected "Individual accounts"
// if you created the site project using the VS 2013 wizard
// ...
}
}
I added the CookieSecure option to ensure cookies were signed and that is recommended as a good security practice, other than that its boiler plate code.
More documentation on CookieAuthenticationOptions if you want it.