SignInManager.SignInPasswordAsync is not logging in the User in ApiController - asp.net

I have an ApiController that handles ajax login requests. Below is what is happening in the code:
// I tried with HttpContext.Current.GetOwinContext().Get... and it didn't work either
private ApplicationUserManager userManager => Request.GetOwinContext().Get<ApplicationManager>();
private ApplicationSignInManager signInManager => Request.GetOwinContext().Get<ApplicationSignInManager>();
public async Task<IHttpActionResult> Login(LoginJson request)
{
var user = await userManager.FindByEmailAsync(request.Email);
var result = await signInManager.PasswordSignInAsync(user.UserName, request.Password, true, true);
if (result == SignInStatus.Success)
{
if (!User.Identity.IsAuthenticated)
{
throw new Exception("Why are you not authenticating!!!");
}
}
return Ok();
}
That exception is always thrown (i.e. the result is Success and yet IPrincipal reports that user is not authenticated yet).
What is even weirder is that if I reload the page I am taken to the dashboard page (Dashboard is the default home page for authenticated users), meaning the previous request actually did log the user in. But then why would User.Identity.IsAuthenticated return false in my first login request? Any ideas?
Note: this is only happening for ApiController, a normal MVC Controller logs the user in correctly

Authentication cookie is only set when your controller send a reply to a client (browser). And User.Identity.IsAuthenticated is checking if the cookie is set. But you are trying to check if the cookie is set within the same request as where you set the cookie.
In other words you can only check if user is authenticated only on the following request after you call PasswordSignInAsync. So remove that throw new Exception... and you'll be fine.

Related

MVC5 Authentication redirects to Login Page even after login

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.

ASP.net identity - external login - won't log out

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]");
}

login/ logout implementation using cache and interceptor in spring

I want to implement login/logout in my web application, I am using Spring MVC , and for login / logout i am using Ehcache and HandlerInterceptorAdapter. The basic idea is:
when user login -> authenticate with DB and if all the credentials
are correct then store the user object in cache with a key and store
the key in request object.
From the next request get the key from request object in preHandle()
method of HandlerInterceptorAdapter and check in cache if exist, or
redirect to login page.
code for storing in cache is:
if(validateUserFromDB()){
/* if a valid user */
userDtlForm = iHomePageService.getUserDetails(emailIdOfUser);
String token = generateSomeUniqueTokenForTheUser();
/* put the user in cache : [ key --> token || value--> userDtlForm ] */
storeTheTokenInCache();
request.setAttribute("token ", token );
}
now for every other request i am checking the cache against the token in my interceptor.
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getAttribute("token");
/* isValid == true when user exists inthe cache */
boolean isValid = checkCacheForTheToken(token);
if (!isValid) {
//redirect to login page
return false;
}
return true;
}
but the problem is I am getting null value by request.getAttribute("token") in my interceptor.what i am doing wrong
when user login -> authenticate with DB and if all the credentials
are correct then store the user object in cache with a key and store
the key in request object.
From the next request get the key from request object in preHandle() method of HandlerInterceptorAdapter and check in cache if exist, or redirect to login page.
In postHandle() again set the token back to client.
maintain the token in a common jsp, thus it can be attached with all request.
What is the way to implement it, please suggest ..../

ASP.NET Web API how to authenticate user

I'm trying to create a simple user authentication function but I just can't get it to work.
Here is the code I'm working on:
public class LoginController : ApiController
{
private void SetPrincipal(IPrincipal principal)
{
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
}
public bool Login(string token)
{
//Check token
if (.....)
{
//Authenticate user
var identity = new GenericIdentity("Test user");
SetPrincipal(new GenericPrincipal(identity, new string[]{"Test role"}));
}
}
[Authorize]
public string TestFun()
{
return "Hello " + User.Identity.Name;
}
}
So, if I try to call method TestFun() first, it returns error code 401 like it should.
However when I call method Login() it should somehow save user credentials, but this is where I get lost, I just can't get it to work.
TestFun() always returns error code 401 even if I call Login() first.
If I try to put return "Hello " + User.Identity.Name; in the Login() function it returns correct username, but in the TestFun() the user is not available.
I've even tried using Sessions and FormsAuthentication but I just can't get it to work, even on this really simple example.
Can someone please tell me what am I missing?
Thanks!
The Login method sets the principal for current request only. Just after the request completes, the principal context is wiped out so that the server can handle other requests for other users. When a new request comes, eons later from the server perspective, the principal context no longer exists and if nothing restores it, the request is unauthenticated.
To fix this you have to return something from your login method to the client. Not only bool but rather - an authentication token. Something the client could use to authenticate further requests.
It could be anything. Forms cookie would be fine as long as the client remembers to append it to further requests. Another common practice is to have a custom authentication token returned to the client and then appended by the client in a custom authentication header. And as forms cookies are handled by the Forms Authentication module, custom headers would need a custom mvc authentication filter or custom asp.net authentication module so that the token is readed, the identity is extracted and restored just before the request is about to execute.
If you don't like to bake your own token infrastructure, I would also recommend OAuth2 tokens. There is a great book that contains easy to follow examples on this and other possible authentication methods:
http://www.amazon.com/Pro-ASP-NET-Web-API-Security/dp/1430257822/ref=sr_1_1?ie=UTF8&sr=8-1&keywords=web+api+security
I just got the same issue, yes, I agreed we need to save that principal into somewhere (cookie, session) for other action to use, so, in SetPrincipal function I added
HttpContext.Current.Session["user"] = HttpContext.Current.User;
Now, the issue is how to get it back for other action, the idea popups in my mind is to extend AuthorizeAttribute and override IsAuthrized function, it will read the session first and if it found the session, it will return true, otherwise it will return false.
namespace BinZ
{
public class MyAuthorizeAttribute:AuthorizeAttribute
{
protected override bool IsAuthorized(HttpActionContext actionContext) {
HttpContext.Current.User = HttpContext.Current.Session["user"] as IPrincipal;
return HttpContext.Current.User != null;
}
}
}
Please remember to replace [Authorize] to [MyAuthorizeAttribute] in WebApi controller.
It works for me very well.
Cheers

Passing the username/password from client to web API using GET

for example I have a web API : http://example.com/api/product.
I have a C# client to consume this web API. Something like that to get whole list of product.
// List all products.
HttpResponseMessage response = client.GetAsync("api/products").Result; // Blocking call!
if (response.IsSuccessStatusCode)
{
// Parse the response body. Blocking!
var products = response.Content.ReadAsAsync<IEnumerable<Product>>().Result;
foreach (var p in products)
{
Console.WriteLine("{0}\t{1};\t{2}", p.Name, p.Price, p.Category);
}
}
else
{
Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
}
How do I pass the username and password from C# client to server's API? What I want is when the C# client to get whole product list from web API.
The client will send the username and password to the server's API. if the server's web API checks whether it is authorized user from database, if not don't let it get product list.
I used the following approach in a proof of concept some time ago, I hope it helps you.
I wrote something like this, an "AuthenticationController" with 2 methods:
public bool Login(string username, string password, bool rememberMe)
{
if (Membership.ValidateUser(username, password))
{
FormsAuthentication.SetAuthCookie(username, rememberMe);
return true;
}
return false;
}
public void Logout()
{
FormsAuthentication.SignOut();
}
The Login method creates a cookie that will be sent to the client; then, in each request, you need to send it back to the server. You can use the [Authorize] attribute in your controller actions to validate allowed roles and rights.
My recommendation is to use have an authentication routine that will assign a token to the client. The client would then cache that token and pass that token in subsequent requests. The authentication routine should be via SSL to prevent sniffing on the wire and shouldn't be stored on the device at all (the token can be cached to the device).
This will give you a fair bit of control over the client. Your service is then in a position where it can preemptively deactivate the client (kill the token and force a re-auth - essentially a timemout situation). You are also in a position to protect your application on the client (if the application is compromised on the device the user credentials won't be passed around).
You could use DotNetOpenAuth to get you started along this path.
[System.Web.Mvc.AcceptVerbs(HttpVerbs.Post)]
public ActionResult LogOn(string loginIdentifier)
{
if (!Identifier.IsValid(loginIdentifier))
{
ModelState.AddModelError("loginIdentifier",
"The specified login identifier is invalid");
return View();
}
else
{
var openid = new OpenIdRelyingParty();
IAuthenticationRequest request = openid.CreateRequest(
Identifier.Parse(loginIdentifier));
// Require some additional data
request.AddExtension(new ClaimsRequest
{
BirthDate = DemandLevel.NoRequest,
Email = DemandLevel.Require,
FullName = DemandLevel.Require
});
return request.RedirectingResponse.AsActionResult();
}
}
Source: Sample Code

Resources