DOTNET CORE JWT Error converting value \"Username\" to type 'jwt_example.Model.LoginModel ERROR - api

I'm trying to write an API with dotnet core 3.1 + JWT.
I created LoginModel and authController when I try to get a token
postman says to me:
"Error converting value \"Username\" to type 'jwt_example.Model.LoginModel"
That is my authController code :
public class AuthController : ControllerBase
{
private UserManager<ApplicationUser> userManger;
public AuthController(UserManager<ApplicationUser> userManger)
{
this.userManger = userManger;
}
[HttpPost]
[Route("login")]
public async Task<IActionResult> Login([FromBody] LoginModel model)
{
var user = await userManger.FindByNameAsync((model.Username).ToString());
//var user = await
if (user != null && await userManger.CheckPasswordAsync(user, model.Password))
{
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
var signinKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("ŞanlıFenerbahçe."));
JwtSecurityToken token = new JwtSecurityToken
(
issuer: "https://localhost:5001",
audience: "https://localhost:5001",
claims: claims,
signingCredentials: new SigningCredentials(signinKey, SecurityAlgorithms.HmacSha256),
expires: DateTime.Now.AddHours(1)
);
return Ok(new
{
token = new JwtSecurityTokenHandler().WriteToken(token),
expiration = token.ValidTo
});
}
else
{
return BadRequest(new
{
message = "Kullanıcı adı veya şifre hatalı!"
});
}
}
}
And, that is loginModel.cs:
public class LoginModel
{
[Required(ErrorMessage = "Kullanıcı adı boş geçilemez.")]
public string Username { get; set; }
[Required(ErrorMessage = "Parola boş geçilemez.")]
public string Password { get; set; }
}

Related

How to create a service for user login in Identity

In order to create a token in jwt, I must first check that the received user password is correct. I want to write a service for login and create its interface and use it in the JWTAuthorizationManager class or anywhere, please correct the codes.
public class JWTAuthorizationManager
{
public JwtFeilds Authenticate(string UserName, string PasswordHash)
{
//ایجاد تاریخ انقضای توکن
var tokenExpireTimeStamp = DateTime.Now.AddHours(Constansts.JWT_TOKEN_EXPIRE_TIME);
//ایجاد متغیر از کلاس مشخص شده برای ایجاد توکن و اطلاعات همراه آن
var jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
//ایجاد آرایه ای از بایت ها به عنوان کلید توکن
var tokenKey = Encoding.ASCII.GetBytes(Constansts.JWT_SECURITY_KEY_FOR_TOKEN);
//از این کلاس برای نگهداری ویژگیها و اطلاعات درون توکن استفاده می شود.
var securityTokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new List<Claim>
{
new Claim("username", UserName),
new Claim(ClaimTypes.PrimaryGroupSid,"User Group 01")
}),
Expires = tokenExpireTimeStamp,
//امضا یا اعتبارنامه یا مجوز ورود
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(tokenKey),SecurityAlgorithms.HmacSha256Signature)
};
var securityToken = jwtSecurityTokenHandler.CreateToken(securityTokenDescriptor);
var token = jwtSecurityTokenHandler.WriteToken(securityToken);
return new JwtFeilds
{
token = token,
user_name = UserName,
expire_time = (int)tokenExpireTimeStamp.Subtract(DateTime.Now).TotalSeconds
};
}
}
public class loginService
{
private readonly SignInManager<User> _signInManager;
public loginService(SignInManager<User> signInManager)
{
_signInManager = signInManager;
}
public async Task<loginService> UserLogin(string UserName, string PasswordHash)
{
var result = await _signInManager.PasswordSignInAsync(UserName, PasswordHash, true,
lockoutOnFailure: false);
if (result.Succeeded)
{
return null;
}
return null;
}
}
interface IuserLogin
{
}
[HttpPost]
public IActionResult Login([FromForm] User model)
{
var jwtAuthorizationManager = new JWTAuthorizationManager();
var result = jwtAuthorizationManager.Authenticate(model.UserName, model.PasswordHash);
if (result == null)
return Unauthorized();
else
return Ok(result);
}
The token creation is done successfully, but I want the user to be identified before creating the token
The easiest way, you can choose to return a boolean Task:
public interface ILoginService
{
//If your model has other attributes, pass them in together
Task<bool> UserLogin(string UserName, string PasswordHash);
}
public class Login : ILogin
{
private readonly SignInManager<IdentityUser> _signInManager;
private readonly UserManager<IdentityUser> _userManager;
public Login(SignInManager<IdentityUser> signInManager,UserManager<IdentityUser> userManager)
{
_signInManager = signInManager;
_userManager = userManager;
}
public async Task<bool> UserLogin(string UserName, string PasswordHash)
{
var user = await _userManager.FindByEmailAsync(UserName);
if (user != null && !user.EmailConfirmed)
{
return false;
}
if (await _userManager.CheckPasswordAsync(user, PasswordHash) == false)
{
return false;
}
var result = await _signInManager.PasswordSignInAsync(UserName, PasswordHash, true);
if (result.Succeeded)
{
return true;
}
else
{
return false;
}
}
}
In the Login Action:
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Login(UserLoginDto model)
{
if (ModelState.IsValid)
{
//Call the ILoginService interface
var result = _loginService.UserLogin(model.Email,model.Password);
if (result.Result)
{
var jwtAuthorizationManager = new JWTAuthorizationManager();
var result = jwtAuthorizationManager.Authenticate(model.UserName, model.PasswordHash);
//do something
}
else
{
ModelState.AddModelError("message", "Invalid login attempt");
return View(model);
}
}
return View(model);
}
Don't forget to sign up for the service:
builder.Services.AddTransient<ILoginService, LoginService>();
You can also return a string or other types of Task, up to you.
Hope this can help you.

How to return a cookie from an ASP.NET Core Auth Cookie in a Controller

I currently have a razor page where I return a cookie and it works great. However, I am developing a SPA which uses VueJS so I have created an API to directly communicate with. I have converted my code from the Razor Page to the controller but I am not sure how I actually return the cookie when the user tries to log in. If there is a match I want it to return the cookie and create the cookie. As of now I get a 400 code as if this request is not working. Any thoughts what could be wrong with this?
public class LoginController : Controller
{
private readonly LoginDBContext _context;
private string connectionString;
public IConfiguration Configuration { get; }
public LoginController(LoginDBContext context, IConfiguration configuration)
{
_context = context;
connectionString = configuration["ConnectionStrings:MMCARMSContext"];
}
// GET: HomeController
public ActionResult Index()
{
return Ok(new { Result = "Test" });
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login([FromForm] string Username, [FromForm] string Password, [FromForm] bool RememberMe)
{
if (!String.IsNullOrEmpty(Username) && !String.IsNullOrEmpty(Password))
{
var users = _context.UsersAccountsTbl
.Where(a => a.Username == Username)
.Select(a => new { a.InternalUserNumber, a.Username, a.Password })
.ToArray();
if (users.Length == 1) //if we have more than 1 result we have security issue so do not allow login
{
var passwordHasher = new PasswordHasher<string>();
//To use you need to has with var hashedPassword = passwordHasher.HashPassword(UserName, Password);
//System.Diagnostics.Debug.WriteLine(passwordHasher.HashPassword("Username", "password"));
var user = users.First();
if (passwordHasher.VerifyHashedPassword(user.Username, user.Password, Password) == PasswordVerificationResult.Success)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.InternalUserNumber.ToString())
};
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
if (RememberMe)
{
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
new AuthenticationProperties
{
IsPersistent = RememberMe,
ExpiresUtc = DateTimeOffset.UtcNow.AddHours(2)
});
}
else
{
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity));
}
return Ok(new { Result = "Cookie has been created!" });
}
else
{
return Ok(new { Result = "Password is incorrect!" });
}
}
return Ok(new { Result = "Username or Password does not exist!" });
}
else
{
return Ok(new { Result = "Username or Password invalid!" });
}
}
}
You could set the cookie in the HttpResponse, this way the cookie gets added when the client receives the response from your controller:
HttpCookie MyCookie = new HttpCookie("LastVisit");
DateTime now = DateTime.Now;
MyCookie.Value = now.ToString();
MyCookie.Expires = now.AddHours(1);
Response.Cookies.Add(MyCookie);
https://learn.microsoft.com/en-us/dotnet/api/system.web.httpresponse.cookies?view=netframework-4.8

Getting " InvalidOperationException: Error while validating the service descriptor 'ServiceType: MediatR.IRequestHandler`2"

I'm trying to implement refresh token in my api. The api is following CQRS+MediatR pattern and is using JWT for authentication.
After adding a Handler to manage refresh call and a refresh token service I am getting the following error:
Inner Exception 1:
InvalidOperationException: Error while validating the service descriptor 'ServiceType: MediatR.IRequestHandler`2
[UM.Business.Application.Token.Command.Update.UpdateTokenCommand,UM.Business.Application.Common.HandlerResult]
Lifetime: Transient ImplementationType: UM.Business.Application.Token.Command.Update.UpdateTokenHandler':
Unable to resolve service for type 'UM.Infrastructure.Common.Configuration.AuthSettings'
while attempting to activate 'UM.Business.Application.Token.Command.Update.UpdateTokenHandler'.
Inner Exception 2:
InvalidOperationException: Unable to resolve service for type 'UM.Infrastructure.Common.Configuration.AuthSettings'
while attempting to activate 'UM.Business.Application.Token.Command.Update.UpdateTokenHandler'.
My service is doing basic crud operations on the Refresh Token class.
First of all I added dependancy injection in a service module class,
service.AddTransient<IRefreshTokenService, RefreshTokenService>();
This is my controller end point,
[Route("refresh")]
[ProducesResponseType(typeof(LoginResponse), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(IEnumerable<string>), StatusCodes.Status412PreconditionFailed)]
[ProducesResponseType(typeof(string), StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(ConfirmEmail), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(AccountBlocked), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> Refresh([FromBody] UpdateRefreshTokenVM request, CancellationToken ct)
{
var refreshCommand= _mapper.Map<UpdateTokenCommand>(request);
var authenticationResult = await _mediator.Send(refreshCommand, ct);
if (authenticationResult == null)
return Unauthorized();
if (authenticationResult.IsSuccess)
return Ok(authenticationResult.Result);
}
My UpdateRefreshTokenVM,
public class UpdateRefreshTokenVM
{
public string AccessToken { get; set; }
public Core.Domain.Models.RefreshToken RefreshToken { get; set; } }
And UpdateTokenCommand,
public class UpdateTokenCommand : CommandBase<HandlerResult>
{
public string AccessToken { get; set; }
public Core.Domain.Models.RefreshToken RefreshToken { get; set; }
}
Handler getting called is this one,
public UpdateTokenHandler(IUserService userService, IMapper mapper, IRefreshTokenService refreshTokenService, IRoleService roleService, AuthSettings authSetting, IUserClaimsService userClaimService)
{
_userService = userService;
_refreshTokenService = refreshTokenService;
_userClaimService = userClaimService;
_roleService = roleService;
_authSetting = authSetting;
_mapper = mapper;
}
public async Task<HandlerResult> Handle(UpdateTokenCommand request, CancellationToken cancellationToken)
{
var loginResponse = new HandlerResult();
var userIdentity = await _userService.FindByEmailAsync(request.RefreshToken.User.Email);
string accessToken = request.AccessToken;
var refreshToken = request.RefreshToken;
var principal = TokenGenerator.GetPrincipalFromExpiredToken(accessToken);
var username = principal.Identity.Name; //this is mapped to the Name claim by default
var refreshTokenFromDb =await _refreshTokenService.FindByUserId(refreshToken.User.Id);
if (refreshTokenFromDb.RefreshTokenExpiryTime <= DateTime.Now)
{
return null;
}
if (refreshTokenFromDb == null || refreshTokenFromDb != refreshToken )
{
loginResponse.IsSuccess = false;
loginResponse.ErrorMessage="Invalid client request";
loginResponse.Result = null;
return loginResponse;
}
var userClaims = await _userClaimService.GetClaims(userIdentity);
var userRoles = await _roleService.GetUserRoles(userIdentity);
var resultObject = TokenGenerator.GenerateJsonWebToken(userIdentity, userClaims, _authSetting, userRoles);
//var newRefreshToken = TokenGenerator.GenerateRefreshToken();
loginResponse.Result = resultObject;
loginResponse.ErrorMessage = null;
loginResponse.IsSuccess = true;
//user.RefreshToken = newRefreshToken;
//userContext.SaveChanges();
// now save the token variable in db
await _refreshTokenService.UpdateAsync(resultObject.RefreshToken);
return loginResponse;
}
}
Following is the token generator used in the handler,
internal static LoginResponse GenerateJsonWebToken(UM.Core.Domain.Models.User userInfo,IList<System.Security.Claims.Claim> userClaims, AuthSettings authSetting, IList<string> roleNames)
{
userClaims.Add(new System.Security.Claims.Claim(ClaimTypes.Email, userInfo.Email));
userClaims.Add(new System.Security.Claims.Claim(ClaimTypes.NameIdentifier, userInfo.Id));
userClaims.Add(new System.Security.Claims.Claim(ClaimTypes.Name, userInfo.NormalizedUserName));
foreach(string name in roleNames)
{
userClaims.Add(new System.Security.Claims.Claim(ClaimTypes.Role, name));
}
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(authSetting.Key));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha512Signature);
var tokenDescriptor = new SecurityTokenDescriptor()
{
Issuer = authSetting.Issuer,
Audience = authSetting.Audience,
Subject = new ClaimsIdentity(userClaims),
Expires = DateTime.UtcNow.AddMinutes(authSetting.ExpirationTimeInMin),
SigningCredentials = credentials
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
// Now generate refresh token
var refreshToken = new RefreshToken
{
User = userInfo,
//UserId = int.Parse(userInfo.Id),
RefreshTokenString = GenerateRefreshToken(),
RefreshTokenExpiryTime = DateTime.Now.AddDays(14) // will change this later
};
var authenticationResponse = new LoginResponse
{
AccessToken = tokenHandler.WriteToken(token),
RefreshToken = refreshToken,
IsValid = true
};
return authenticationResponse;
}
public static string GenerateRefreshToken()
{
var randomNumber = new byte[32];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(randomNumber);
return Convert.ToBase64String(randomNumber);
}
}
public static ClaimsPrincipal GetPrincipalFromExpiredToken(string token)
{
var tokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false, //you might want to validate the audience and issuer depending on your use case
ValidateIssuer = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("superSecretKey#345")),
ValidateLifetime = false //here we are saying that we don't care about the token's expiration date
};
var tokenHandler = new JwtSecurityTokenHandler();
SecurityToken securityToken;
var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out securityToken);
var jwtSecurityToken = securityToken as JwtSecurityToken;
if (jwtSecurityToken == null || !jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase))
throw new SecurityTokenException("Invalid token");
return principal;
}
Auth settings(which I didn't touch for this implementation)
public class AuthSettings
{
public string Key { get; set; }
public string Issuer { get; set; }
public string Audience { get; set; }
public int ExpirationTimeInMin { get; set; }
}
And my login Response class,
public class LoginResponse
{
public string AccessToken { get; set; }
public RefreshToken RefreshToken { get; set; }
public bool IsValid { get; set; }
public bool VerificationRequired { get; set; }
public bool TwoFactorRequired { get; set; }
public LoginResponse()
{
RefreshToken = new RefreshToken();
}
}

How to extract methods from controller?

I want to extract "what I can" to the service. I have this method in the controller:
[AllowAnonymous]
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
LoginViewModel loginViewModel = new LoginViewModel
{
ReturnUrl = returnUrl,
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList()
};
if (remoteError != null)
{
ModelState.AddModelError(string.Empty, $"Error from external provider: {remoteError}");
return View("Login", loginViewModel);
}
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
ModelState.AddModelError(string.Empty, "Error loading external login information.");
return View("Login", loginViewModel);
}
var signInResult = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true);
if (signInResult.Succeeded)
{
return LocalRedirect(returnUrl);
}
else
{
var email = info.Principal.FindFirstValue(ClaimTypes.Email);
if (email != null)
{
var user = await _userManager.FindByEmailAsync(email);
if (user == null)
{
user = new ApplicationUser
{
UserName = info.Principal.FindFirstValue(ClaimTypes.Email),
Email = info.Principal.FindFirstValue(ClaimTypes.Email)
};
await _userManager.CreateAsync(user);
}
await _userManager.AddLoginAsync(user, info);
await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
ViewBag.ErrorTitle = $"Nie otrzymano informacji o adresie e-mail od dostawcy: {info.LoginProvider}";
ViewBag.ErrorMessage = "Proszę skontaktować się z supportem fryzjer#aplikacjafryzjer.com";
return View("Error");
}
}
Some properties are available only for classes that inherit from Controller, eg. ModelState or Url.
Can I extract these parts of the code too? A service can inherit from a controller, but won't it become a controller then?
------------------------------------------------------------ EDIT ---------------------------------------------------------------
Ok I tried separate my method from the controller. Below my previous code Controller:
public class AccountController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IEmailService _emailService;
public AccountController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IEmailService emailService)
{
_userManager = userManager;
_signInManager = signInManager;
_emailService = emailService;
}
[HttpPost]
public async Task<IActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
//register functionality
var user = new ApplicationUser
{
FirstName = model.FirstName,
LastName = model.LastName,
UserName = model.Email,
Email = model.Email,
};
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
if (_signInManager.IsSignedIn(User) && User.IsInRole("Admin"))
{
return RedirectToAction("ListUsers", "Administrator");
}
//login user
await _signInManager.SignInAsync(user, isPersistent: false);
//generation of the email token
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var link = Url.Action(nameof(VerifyEmail), "Home", new { userId = user.Id, code }, Request.Scheme, Request.Host.ToString());
await _emailService.SendAsync(user.Email, "Weryfikacja adresu e-mail", $"Potwierdź e-mail", true);
return RedirectToAction("EmailVerification");
}
}
return View(model);
}
}
And my Controller now:
public class AccountController : Controller
{
private readonly IUserManager _userManager;
public AccountController(
IUserManager userManager)
{
_userManager = userManager;
}
[HttpPost]
public async Task<IActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
(string action, string controller) = await _userManager.Register(model);
return RedirectToAction(action, controller);
}
return View(model);
}
}
I am sure, something is wrong. I have no idea how separate this logic from the controller, but on the other forums I heard "U have to separate your logic from the controller! Your controller have to be simple - only get request and send response, thats all!". but now, when I started rebuild my project, I am not sure isn't this more complicated...
In my service I return a tuple (???), because I have not better idea...
public async Task<(string, string)> Register(RegisterViewModel model)
{
//register functionality
var user = new ApplicationUser
{
FirstName = model.FirstName,
LastName = model.LastName,
UserName = model.Email,
Email = model.Email,
};
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
if (_signInManager.IsSignedIn(User) && User.IsInRole("Admin"))
{
return ("ListUsers", "Administrator");
}
//login user
await _signInManager.SignInAsync(user, isPersistent: false);
//generation of the email token
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var link = _urlHelper.Action(nameof(VerifyEmail), "Home", new { userId = user.Id, code });
await _emailService.SendAsync(user.Email, "Weryfikacja adresu e-mail", $"Potwierdź e-mail", true);
return ("EmailVerification", "Administrator");
}
return ("Register", "Administrator");
}
As far as I know, if you want to use Url in other custom service which is not inherit from controller. You could inject the IActionContextAccessor and use IUrlHelperFactory to create the it. Besides, if you want to use other controller based property or method, you could refer asp.net core source codes and find it work and then you could write the codes by yourself.
More details, you could refer to below codes:
Firstly you could inject the IActionContextAccessor in the startup.cs ConfigureServices method:
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
Then you could use DI and factory to create the URLhelper like below:
public class GetRoute : IGetRoute
{
private IUrlHelper _urlHelper;
private IActionContextAccessor _IActionContextAccessor;
public IUrlHelper Url
{
get
{
if (_urlHelper == null)
{
_urlHelper = _urlHelperFactory.GetUrlHelper(_IActionContextAccessor.ActionContext);
}
return _urlHelper;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_urlHelper = value;
}
}
private readonly IUrlHelperFactory _urlHelperFactory ;
public GetRoute(IActionDescriptorCollectionProvider actionDescriptorCollectionProvider, IUrlHelperFactory urlHelperFactory, IActionContextAccessor actionContextAccessor)
{
_IActionContextAccessor = actionContextAccessor;
_urlHelperFactory = urlHelperFactory;
}
public string Getlink() {
return Url.Link("default", "aaaa");
}
}

IdentityServer4 requesting a JWT / Access Bearer Token using the password grant in asp.net core

I've searched all over on requesting a JWT / Access Bearer Token using the password grant using IdentityServer4 in asp.net core, but I cant seem to find the right way to do it.
Below is the POST Request from which I register my user.
http://localhost:52718/account/register
Below is the Bearer Token GET Request from which I can get JWT Token using IdentityServer4
http://localhost:52718/connect/token
Below is the POST Request from which I Login my user
http://localhost:52718/account/signin
Now, what I'm trying to do is when I login my user then I want a JWT / Bearer Token same as I get from here http://localhost:52718/connect/token. When I hit this URL.
Here is my AccountController Code:
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Server.Models;
using Server.Models.AccountViewModels;
using Server.Models.UserViewModels;
namespace Server.Controllers
{
public class AccountController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly RoleManager<IdentityRole> _roleManager;
public AccountController(
UserManager<ApplicationUser> userManager,
RoleManager<IdentityRole> roleManager
)
{
_userManager = userManager;
_roleManager = roleManager;
}
[HttpPost]
public async Task<IActionResult> Register([FromBody]RegisterViewModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = new ApplicationUser { UserName = model.UserName, FirstName = model.FirstName, LastName = model.LastName, Email = model.Email };
var result = await _userManager.CreateAsync(user, model.Password);
string role = "Basic User";
if (result.Succeeded)
{
if (await _roleManager.FindByNameAsync(role) == null)
{
await _roleManager.CreateAsync(new IdentityRole(role));
}
await _userManager.AddToRoleAsync(user, role);
await _userManager.AddClaimAsync(user, new System.Security.Claims.Claim("userName", user.UserName));
await _userManager.AddClaimAsync(user, new System.Security.Claims.Claim("firstName", user.FirstName));
await _userManager.AddClaimAsync(user, new System.Security.Claims.Claim("lastName", user.LastName));
await _userManager.AddClaimAsync(user, new System.Security.Claims.Claim("email", user.Email));
await _userManager.AddClaimAsync(user, new System.Security.Claims.Claim("role", role));
return Ok(new ProfileViewModel(user));
}
return BadRequest(result.Errors);
}
public async Task<IActionResult> Signin([FromBody]LoginViewModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var result = await _userManager.FindByNameAsync(model.UserName);
if (result != null && await _userManager.CheckPasswordAsync(result, model.Password))
{
return Ok(new ProfileViewModel(result));
}
return BadRequest("Invalid username or password.");
}
}
}
When I hit signin method I successfully get the data of user.
But I also need a jwt / access token when user login my app.
Now my actual question is:
What can I do in my signin method so when user login it returns me token along with other user data. I hope I briefly explain my question.
Thanks
I've found my own question answer. Before starting I show you my that code where I'm Defining the client.
public static IEnumerable<Client> GetClients()
{
// client credentials client
return new List<Client>
{
// resource owner password grant client
new Client
{
ClientId = "ro.angular",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.Address,
"api1"
}
}
};
}
Now what I do in my Signin Method is to use the TokenClient class to request the token. To create an instance you need to pass in the token endpoint address, client id and secret.
Next I'm using Requesting a token using the password grant to allows a client to send username and password to the token service and get an access token back that represents that user.
Here is my Signin Code which I need to modify:
public async Task<IActionResult> Signin([FromBody]LoginViewModel model)
{
var disco = await DiscoveryClient.GetAsync("http://localhost:52718");
if (disco.IsError)
{
return BadRequest(disco.Error);
}
var tokenClient = new TokenClient(disco.TokenEndpoint, "ro.angular", "secret");
var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync(model.UserName, model.Password, "api1 openid");
if (tokenResponse.IsError)
{
return BadRequest(tokenResponse.Error);
}
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = _userManager.FindByNameAsync(model.UserName);
var result = await _userManager.FindByNameAsync(model.UserName);
if (result != null && await _userManager.CheckPasswordAsync(result, model.Password))
{
return Ok(new ProfileViewModel(result, tokenResponse));
}
return BadRequest("Invalid username or password.");
}
Also I modify ProfileViewModel Class and add two new Token & Expiry:
public class ProfileViewModel
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Token { get; set; }
public int Expiry { get; set; }
public ProfileViewModel()
{
}
public ProfileViewModel(ApplicationUser user, TokenResponse UToken = null)
{
Id = user.Id;
FirstName = user.FirstName;
LastName = user.LastName;
Email = user.Email;
Token = UToken.AccessToken;
Expiry = UToken.ExpiresIn;
}
public static IEnumerable<ProfileViewModel> GetUserProfiles(IEnumerable<ApplicationUser> users)
{
var profiles = new List<ProfileViewModel> { };
foreach (ApplicationUser user in users)
{
profiles.Add(new ProfileViewModel(user));
}
return profiles;
}
}
Now Here is my desire output. Hope this answer help others.