Migrate Identity Authentication from ASP.NET MVC to ASP.NET Web API - asp.net

I have a ASP.NET MVC web application and I want to register and login using angular. I'm calling a Login and Register Method on my WebAPI when the user wants to Login/Register. The problem is that I don't know how to transfer my MVC Login and Register in the AccountController to my WebAPI.
My current Register method looks like this:
// POST api/RegisterApi
public async Task<HttpResponseMessage> Post([FromBody]RegisterViewModel model)
{
if (!ModelState.IsValid)
{
return await this.BadRequest(this.ModelState).ExecuteAsync(new CancellationToken());
}
var user = new ApplicationUser
{
Email = model.Email,
UserName = model.Email
};
IdentityResult result = await this.UserManager.CreateAsync(user, model.Password);
if (!result.Succeeded)
{
return await this.GetErrorResult(result).ExecuteAsync(new CancellationToken());
}
// Auto login after register (successful user registration should return access_token)
var loginResult = this.LoginUser(new LoginViewModel()
{
Email = model.Email,
Password = model.Password
});
return await loginResult;
}
My main issue is the following line:
IdentityResult result = await this.UserManager.CreateAsync(user, model.Password);
Somehow i can't call the CreateAsync-Method and I don't really know why.
I get the following error:
Task' does not contain a definition for 'CreateAsync'
and no extension method 'CreateAsync' accepting a first argument of
type 'Task
Do you guys can give my any input on doing this properly? Everything I've seen so far seemed far too complicated for my problem.
EDIT: Okay the error is solved, but now there's a new problem. The CreateAsync method does not Create a User and returns to the error function in my AngularJs. Do you guys have any suggestion why CreateAsync could fail?

Related

HttpContext.SignOutAsync() Method not deleting my authentication cookie

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.

Null Reference Exception for ClaimsType in IdentityCore, Getting claims as null

Hi guys I am trying out Identity Core,
I need to get the details of the user who has logged in.
I am trying the following:
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: true);
if (result.Succeeded)
{
string userRole=_signInManager.Context.User.FindFirst(ClaimTypes.Role).Value;
}
I am getting a Null reference exception when getting the user roles
However I tried the same in a different controller then the Login Page of Identity Core and it is working.
What am I doing wrong?
Thank you:)
This code doesn't work as you expected because calling _signInManager.PasswordSignInAsync only generates and sets authentication cookie for user and doesn't log in the user right now, thus there is no logged user in this request and that's why _signInManager.Context.User returns null. User is considered to be logged in only on next request where request contains that authentication cookie. So in general code for log in looks like this
_signInManager.PasswordSignInAsync(...);
if (result.Succeeded)
{
return Redirect(...);
}
But if you want to get user information right after login you can do it like following
if (result.Succeeded)
{
var user = await _signInManager.UserManager.FindByEmailAsync(Input.Email);
IList<Claim> userClaims = await _signInManager.UserManager.GetClaimsAsync(user);
string userRole = userClaims.First(c => c.Type == ClaimTypes.Role).Value;
//...
}
Or if you want to get only roles you may use this code
var user = await _signInManager.UserManager.FindByEmailAsync(Input.Email);
IList<string> roles = await _signInManager.UserManager.GetRolesAsync(user);
string userRole = roles.First();

Show only one Register error instead of two

I have an ASP.Net MVC site that uses ASP.Net Identity for managing users.
I use the default mechanism that the Email of the user is also his username. When a user wants to register with an Email which is already taken two error messages are shown:
Name myEmail#gmail.com is already taken.
Email 'myEmail#gmail.com' is already taken.
The structure of the Register form:
HttpPost Register Method of AccountController:
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser {UserName = model.Email, Email = model.Email};
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
string callbackUrl =
await SendEmailConfirmationTokenAsync(user.Id, "Confirm your account");
return View("Info");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
How can I only show the Email Error message to the user?
Replace your AddErrors() method with
ModelState.AddModelError("", String.Format("{0} is already taken", model.Email))
or, if you have included #Html.ValidationMessageFor(m => m.Email) in the view and want the message to be associated with the form control, rather than in the summary
ModelState.AddModelError("Email", String.Format("{0} is already taken", model.Email))
Side note: You might also want to consider using a RemoteAttribute so that you get client side validation before submitting the form - refer How to: Implement Remote Validation in ASP.NET MVC (although you should also keep the code above in (the rare) case 2 users submit the same Email at the same time).

How to set up two-factor authentication in ASP.NET MVC 5.2.3 and Katana correctly?

In the code that you get in the ASP.NET MVC 5.2.3 templates with Visual Studio 2015 Community RC, if you run them as they came, and if you register with your email address (and not with an external service provider such as Facebook or Google or Linked In or Twitter), and then if you login into the website by entering your user name and password, it straight-away lets you login and does not trigger two-factor authentication. It just logs you in successfully.
Specifically, the PasswordSignInAsync method on the SignInManager always returns a SignInStatus of Success if you enter your correct user name and password. It never evaluates to SignInStatus.RequiresVerification.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model,
string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout,
// change to shouldLockout: true
var result = await SignInManager.PasswordSignInAsync(
model.Email, model.Password,
model.RememberMe, shouldLockout: true);
switch (result)
{
case SignInStatus.Success:
// if I sign-in with my correct user name
// and password, the flow-of-control always
// comes here. The SignInStatus never evaluates
// to RequiresVerification
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
// the flow-of-control never reaches here
return RedirectToAction("SendCode",
new
{
ReturnUrl = returnUrl,
RememberMe = model.RememberMe
});
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
This happens even though the default code has got two-factor authentication enabled and set up as indicated by the following snippets of code.
In Startup.ConfigureAuth
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie,
TimeSpan.FromMinutes(5));
app.UseTwoFactorRememberBrowserCookie(
DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
In ApplicationUserManager.Create, the factory method.
// Register two factor authentication providers. This application uses Phone
// and Emails as a step of receiving a code for verifying the user
// You can write your own provider and plug it in here.
manager.RegisterTwoFactorProvider("Phone Code",
new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Your security code is {0}"
});
manager.RegisterTwoFactorProvider("Email Code",
new EmailTokenProvider<ApplicationUser>
{
Subject = "Security Code",
BodyFormat = "Your security code is {0}"
});
var container = Unity.Container;
manager.EmailService = container.Resolve<EmailService>();
manager.SmsService = container.Resolve<SmsService>();
I've got my EmailService and SmsService set up in a Unity container and they're configured properly.
What else do I need to do to set it up correctly? I have read this article and a few pieces of documentation from the MSDN, and a few forums posts on other websites about setting this up, but I am not very certain if I am missing something.
This guy isn't called / redirected to from anywhere. I guess this is what's missing.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> EnableTwoFactorAuthentication()
{
await UserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(),
true);
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false,
rememberBrowser: false);
}
return RedirectToAction("Index", "Manage");
}
It does seem that I am missing the part where I have to specifically have the user call the EnableTwoFactorAuthentication action as there is currently no call to it, but I can't be sure how that should integrate with the rest of the login workflow.
Click f12 from your browser and delete application Cookies

LiveAuthClient broken?

It seems very much that the current version of LiveAuthClient is either broken or something in my setup/configuration is. I obtained LiveSDK version 5.4.3499.620 via Package Manager Console.
I'm developing an ASP.NET application and the problem is that the LiveAuthClient-class seems to not have the necessary members/events for authentication so it's basically unusable.
Notice that InitializeAsync is misspelled aswell.
What's wrong?
UPDATE:
I obtained another version of LiveSDK which is for ASP.NET applications but now I get the exception "Could not find key with id 1" everytime I try either InitializeSessionAsync or ExchangeAuthCodeAsync.
https://github.com/liveservices/LiveSDK-for-Windows/issues/3
I don't think this is a proper way to fix the issue but I don't have other options at the moment.
I'm a little late to the party, but since I stumbled across this trying to solve what I assume is the same problem (authenticating users with Live), I'll describe how I got it working.
First, the correct NuGet package for an ASP.NET project is LiveSDKServer.
Next, getting user info is a multi-step process:
Send the user to Live so they can authorize your app to access their data (the extent of which is determined by the "scopes" you specify)
Live redirects back to you with an access code
You then request user information using the access code
This is described fairly well in the Live SDK documentation, but I'll include my very simple working example below to put it all together. Managing tokens, user data, and exceptions is up to you.
public class HomeController : Controller
{
private const string ClientId = "your client id";
private const string ClientSecret = "your client secret";
private const string RedirectUrl = "http://yourdomain.com/home/livecallback";
[HttpGet]
public ActionResult Index()
{
// This is just a page with a link to home/signin
return View();
}
[HttpGet]
public RedirectResult SignIn()
{
// Send the user over to Live so they can authorize your application.
// Specify whatever scopes you need.
var authClient = new LiveAuthClient(ClientId, ClientSecret, RedirectUrl);
var scopes = new [] { "wl.signin", "wl.basic" };
var loginUrl = authClient.GetLoginUrl(scopes);
return Redirect(loginUrl);
}
[HttpGet]
public async Task<ActionResult> LiveCallback(string code)
{
// Get an access token using the authorization code
var authClient = new LiveAuthClient(ClientId, ClientSecret, RedirectUrl);
var exchangeResult = await authClient.ExchangeAuthCodeAsync(HttpContext);
if (exchangeResult.Status == LiveConnectSessionStatus.Connected)
{
var connectClient = new LiveConnectClient(authClient.Session);
var connectResult = await connectClient.GetAsync("me");
if (connectResult != null)
{
dynamic me = connectResult.Result;
ViewBag.Username = me.name; // <-- Access user info
}
}
return View("Index");
}
}

Resources