How can i change authentication type as phone number instead of user name on my web api? - authentication

My authentication is working fine on it is own but i need to use phoneNumber of users instead of user names.
There is my Provider class
using Identity.Infrastructure;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.OAuth;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Identity.Providers
{
public class CustomOAuthProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
return Task.FromResult<object>(null);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = "*";
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
if (!user.EmailConfirmed)
{
context.SetError("invalid_grant", "User did not confirm email.");
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT");
var ticket = new AuthenticationTicket(oAuthIdentity, null);
context.Validated(ticket);
}
}
}
in this class context is coming with only userName and Password,so it cant reach PhoneNumber even i send it as a parameter.I think problem will solve after if i can change
userManager.FindAsync(context.UserName, context.Password)
like this
userManager.FindAsync(context.PhoneNumber, context.Password)
VS doesn't allow me to interfere OAuthGrantResourceOwnerCredentialsContext
using Identity.Infrastructure;
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Web.Http.Routing;
namespace Identity.Models
{
public class ModelFactory
{
private UrlHelper _UrlHelper;
private ApplicationUserManager _AppUserManager;
public ModelFactory(HttpRequestMessage request, ApplicationUserManager appUserManager)
{
_UrlHelper = new UrlHelper(request);
_AppUserManager = appUserManager;
}
public UserReturnModel Create(ApplicationUser appUser)
{
return new UserReturnModel
{
Url = _UrlHelper.Link("GetUserById", new { id = appUser.Id }),
Id = appUser.Id,
UserName = appUser.UserName,
FullName = string.Format("{0} {1}", appUser.FirstName, appUser.LastName),
Email = appUser.Email,
EmailConfirmed = true,
Level = appUser.Level,
JoinDate = appUser.JoinDate,
Roles = _AppUserManager.GetRolesAsync(appUser.Id).Result,
Claims = _AppUserManager.GetClaimsAsync(appUser.Id).Result,
PhoneNumber = appUser.PhoneNumber
};
}
public RoleReturnModel Create(IdentityRole appRole)
{
return new RoleReturnModel
{
Url = _UrlHelper.Link("GetRoleById", new { id = appRole.Id }),
Id = appRole.Id,
Name = appRole.Name
};
}
}
public class RoleReturnModel
{
public string Url { get; set; }
public string Id { get; set; }
public string Name { get; set; }
}
public class UserReturnModel
{
public string Url { get; set; }
public string Id { get; set; }
public string UserName { get; set; }
public string FullName { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public bool EmailConfirmed { get; set; }
public int Level { get; set; }
public DateTime JoinDate { get; set; }
public IList<string> Roles { get; set; }
public IList<System.Security.Claims.Claim> Claims { get; set; }
}
}
As result I stucked on authenticating with phoneNumber instead of userName and set deviceId as password

public override Task<ApplicationUser> FindAsync(string Phone, string password)
{
//Do your Stuff here
//return base.FindAsync(userName, password);
}
Overrride FIndAsync() in the IndentityConfig.cs

Related

ASP.NET Core Web API - How to display users with respective role

I am implementing ASP.NET Identity in ASP.NET Core-6 Web API
Entities:
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<ApplicationUserRole> UserRoles { get; set; }
}
public class ApplicationRole : IdentityRole
{
public ICollection<ApplicationUserRole> UserRoles { get; set; }
}
public class ApplicationUserRole : IdentityUserRole<string>
{
public virtual ApplicationUser User { get; set; }
public virtual ApplicationRole Role { get; set; }
}
Then I have this DTOs:
public class AllRoleListDto
{
public string Id { get; set; }
public string Name { get; set; }
}
public class AllUserListDto
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public string Fullname
{
get { return FirstName + " " + LastName; }
}
}
I did the Mapping using AutoMapper as shown below:
public class AdminMapperProfile: Profile
{
public AdminMapperProfile()
{
CreateMap<ApplicationUser, AllUserListDto>().ReverseMap();
CreateMap<ApplicationRole, AllRoleListDto>().ReverseMap();
}
}
I want to display all the users with there respective roles using:
public async Task<PagedResponse<AllUserListDto>> GetAllUserAsync(int page, int limit)
{
var response = new PagedResponse<AllUserListDto>();
try
{
if (page >= 1 && limit >= 1)
{
var userQueryable = _context.ApplicationUsers.AsQueryable();
var pagedUsers = await userQueryable.ToPagedListAsync(page, limit);
response.Result = _mapper.Map<List<AllUserListDto>>(pagedUsers.ToList());
response.TotalPages = pagedUsers.PageCount;
response.Page = pagedUsers.PageNumber;
response.PerPage = pagedUsers.PageSize;
}
else
{
response.Error = new ErrorResponseDto()
{
ErrorCode = 400,
Message = "The page number and page size must be greater than 1!"
};
}
}
catch (Exception ex)
{
response.Error = new ErrorResponseDto()
{
ErrorCode = 500,
Message = ex.Message
};
}
return response;
}
What I have above only get all the users without the roles.
How do I get all the users with their respective roles as shown below?
UserName | FirstName | LastName | Role
Thanks
You can use _userManager.GetRolesAsync(user) to get the respective roles, here is a simple demo( I have used MVC here to demonstrate it more clearly):
First, Create a viewModel
public class UserRole
{
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public List<string> roles { get; set; } = new List<string>();
}
Then in controller:
public class RegisterController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly RoleManager<ApplicationRole> _roleManager;
public RegisterController(UserManager<ApplicationUser> userManager, RoleManager<ApplicationRole> roleManager)
{
_userManager = userManager;
_roleManager = roleManager;
}
public async Task<IActionResult> show()
{
List<UserRole> userRoles = new List<UserRole>();
//get all users
var user = _userManager.Users.ToList();
foreach (var item in user)
{
UserRole userRole = new UserRole();
userRole.UserName = item.UserName;
userRole.FirstName = item.FirstName;
userRole.LastName = item.LastName;
userRole.Email = item.Email;
//get the user's roles
var roles = await _userManager.GetRolesAsync(item);
foreach (var roleName in roles)
{
userRole.roles.Add(roleName);
}
userRoles.Add(userRole);
}
return View(userRoles);
}
}
View:
#model List<UserRole>
#foreach (var item in Model)
{
<h2>#item.UserName</h2>
<h2>#item.FirstName</h2>
<h2>#item.LastName</h2>
<h2>#item.Email</h2>
#foreach (var role in item.roles)
{
<h3>#role</h3>
}
<h1>==================================</h1>
}
Demo:

How to properly deserialize asp.net core Model State errors to object in xamarin forms

I am having a silly problem trying to deserialize asp.net core model state errors to an object. My code is like this
For backend register method
[HttpPost("register-user")]
[ValidateModel]
public async Task<IActionResult> Index(RegisterDto registerDto)
{
try
{
Data.Models.User user = mapper.Map<RegisterDto, Data.Models.User>(registerDto);
user.LockoutEnd = DateTimeOffset.Now;
user.Warehouse = configuration["Config:Warehouse"];
user.SiteId = Convert.ToInt32(configuration["Config:SiteId"]);
IdentityResult result = await userManager.CreateAsync(user, registerDto.Password);
if (result.Succeeded)
{
AddLogInformation(logger, "User created a new account with password.");
string token = await userManager.GenerateEmailConfirmationTokenAsync(user);
token = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(token));
string confirmationUrl = Url.Action("Index", "EmailConfirmation",
new {userId = user.Id, code = token}, Request.Scheme);
emailSender.SendEmailAsync(registerDto.Email, "Confirm your email",
GetEmailConfirmationTemplate(registerDto.FirstName, registerDto.LastName,
confirmationUrl));
ApplicationRole retailPersonRole =
await roleManager.FindByNameAsync(RoleHelper.GetRetailUserRoleName());
if (retailPersonRole != null) await userManager.AddToRoleAsync(user, retailPersonRole.Name);
if (userManager.Options.SignIn.RequireConfirmedAccount)
{
AddLogInformation(logger, "Sent email confirmation email to user");
return Ok(SuccessResult(null));
}
//If confirm account is set to false
await signInManager.SignInAsync(user, false);
return Ok(SuccessResult(null));
}
// If we got this far, something failed, redisplay form
return Ok(FailedMessage(logger, "Cannot register user at this time. Please try again later."));
}
catch (Exception e)
{
return ServerErrorJsonResult(logger, "Error while trying to register user. Error message is: " + e.Message);
}
}
And I am catching the model state error in action filter and returning a response as below.
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
var modelState = context.ModelState;
if (!modelState.IsValid)
context.Result = new BadRequestObjectResult(new JsonResult(new {modelError = true, Errors = modelState}));
}
}
Now in the front-end (xamarin), I have a model to where the error should be deserialised so that I can display a proper error to user.
My register model in the front end is like this
public class RegisterDto: BaseDto
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string ConfirmPassword { get; set; }
public AddressDto BillingAddress { get; set; }
public RegisterDto Errors { get; set; }
}
My Address Dto is like this
public class AddressDto
{
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string PostCode { get; set; }
public string State { get; set; }
}
I am creating the post request in xamarin like this.
public async Task<TResult> PostAsync<TResult>(string uri, TResult data, string token = "", string header = "")
{
try
{
HttpClient httpClient = CreateHttpClient(token);
if (!string.IsNullOrEmpty(header))
{
AddHeaderParameter(httpClient, header);
}
var content = new StringContent(JsonConvert.SerializeObject(data));
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpResponseMessage response = await httpClient.PostAsync(uri, content);
await HandleResponse(response);
string serialized = await response.Content.ReadAsStringAsync();
TResult result = await Task.Run(() =>
JsonConvert.DeserializeObject<TResult>(serialized, serializerSettings));
return result;
}
catch (Exception e)
{
return default;
}
}
And finally, in the view model, I am doing this
--
userToBeRegistered is an instance of RegisterDto
await something.PostAsync(UrlHelper.RegisterUrl, userToBeRegistered);
The serialized string output is like this
{
"errors":{
"Email":[
"Email is required"
],
"LastName":[
"Last name is required"
],
"Password":[
"Password is required"
],
"FirstName":[
"First name is required"
],
"PhoneNumber":[
"Phone number is required"
],
"BillingAddress.City":[
"Suburb is required"
],
"BillingAddress.State":[
"State is required"
],
"BillingAddress.Address1":[
"Street address is required"
],
"BillingAddress.PostCode":[
"Postcode is required"
]
},
"type":"https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title":"One or more validation errors occurred.",
"status":400,
"traceId":"|3c2d7d70-49a6eceecbeedab8."
}
My question is how can I deserialise it to an errors object. If I keep running the above code, then I get this error.
"Unexpected character encountered while parsing value: [. Path 'errors.Email', line 1, position 20."
Can anyone help me with this?
Try the below method.
Create ErrorInfor class:
class ErrorInfor
{
public MyError errors { get; set; }
public string type { get; set; }
public string title { get; set; }
public int status { get; set; }
public string traceId { get; set; }
public class MyError
{
public List<string> FirstName { get; set; }
public List<string> LastName { get; set; }
public List<string> PhoneNumber { get; set; }
public List<string> Email { get; set; }
public List<string> Password { get; set; }
[JsonProperty("BillingAddress.City")]
public List<string> Citiy { get; set; }
[JsonProperty("BillingAddress.State")]
public List<string> State { get; set; }
[JsonProperty("BillingAddress.Address1")]
public List<string> Address1 { get; set; }
[JsonProperty("BillingAddress.PostCode")]
public List<string> PostCode { get; set; }
}
}
then you could get the data from your above json string.
ErrorInfor errorInfor = JsonConvert.DeserializeObject<ErrorInfo>(json);

getting 400 error on webapi call blazorserver

i am trying to setup a blazor server app, calling a webapi.
I keep getting a 400 error returned, when I call the API.
I have 3 Projects, projectserver and projectapi. projectserver is where the Blazor app sits and Project API is where the API sits.
I don't know if the apicall can find the API as it does not hit any breakpoints in the API section, I am totally confused, as if it cannot find the API then it should return a 404 or other error and not 400 ?
thank you for your efforts.
this is my code,
Projectserver, this is where I post the Register Model to the API
public string message { get; set; }
public RegisterModel r = new RegisterModel();
private async Task Create(MouseEventArgs e)
{
var json = Newtonsoft.Json.JsonConvert.SerializeObject(r);
var client = clientfactory.CreateClient("ServerApi");
var result = await client.PostAsJsonAsync("/Account/Register",json); // check the Startup file and check base address for the Full route.
message = result.StatusCode.ToString();
}
}
the ClientFactory returns the base address of what is defined in startup.cs
services.AddHttpClient("ServerApi", client => client.BaseAddress = new Uri("https://localhost:44302/"));
the API is Projectserver and defined as follows.
[Route("[controller]")]
[ApiController]
public class AccountContoller : ControllerBase
{
private readonly ApplicationDbContext _context;
private readonly SecurityOptions _securityOptions;
private readonly JwtIssuerOptions _jwtOptions;
// GET: api/<Account>
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/<Account>/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
// POST api/<Account>
[HttpPost]
public void Post([FromBody] string value)
{
}
// POST api/<Account>
[HttpPost("Register")]
public async Task<ActionResult<RegisterResult>> Register(RegisterModel model)
{
RegisterResult r = new RegisterResult();
var Exisits = await _context.Users.Where(r => r.EmailAddress == model.Email).FirstOrDefaultAsync();
if(Exisits != null)
{
r.Sucsess = false;
r.ErrorMessage = "Email - Already Exisits";
return r;
}
else
{
try
{
User newuser = new User();
newuser.CreatedDateTime = DateTime.UtcNow;
newuser.UserID = Guid.NewGuid();
newuser.MobileNumber = model.MobileNumber;
newuser.Password = model.Password;
newuser.FirstName = model.FirstName;
newuser.Surname = model.LastName;
_context.Users.Add(newuser);
await _context.SaveChangesAsync();
r.Sucsess = true;
return r;
}
catch(Exception e)
{
r.Sucsess = false;
r.ErrorMessage = e.ToString();
return r;
}
}
}
the Model classes are defined as Serializable
[Serializable]
public class RegisterResult
{
public bool Sucsess { get; set; }
public string ErrorMessage { get; set; }
}
[Serializable]
public class RegisterModel
{
public string UserName { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string RoleID { get; set; }
public string EntityID { get; set; }
public string MobileNumber { get; set; }
}
Can you please modify your code as below and give it a try:-
var serializedBody = JsonConvert.SerializeObject(r);
var jsonRequestBodyContent = new StringContent(serializedBody, Encoding.UTF8,"application/json");
var client = clientfactory.CreateClient("ServerApi");
var result = await client.PostAsync("/Account/Register",jsonRequestBodyContent);

MediatR 3.0.1 possible bug? Cannot get IAsyncRequestHandler working

I am getting the following error message when executing IRequest with IAsyncRequestHandler.
System.InvalidOperationException: 'No service for type 'MediatR.IRequestHandler`2[TestProject.Domain.Requests.Users.CreateUserRequest,TestProject.Domain.Requests.Users.CreateUserResponse]' has been registered.'
This is how i register it in the startup class
// Add framework services.
services.AddMvc();
services.AddMediatR(typeof(CreateUserRequest).GetTypeInfo().Assembly);
CreateUserRequest and Response
public class CreateUserRequest : IRequest<CreateUserResponse>
{
public string EmailAddress { get; set; }
public int OrganisationId { get; set; }
public string Password { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class CreateUserResponse
{
public int UserId { get; set; }
public string EmailAddress { get; set; }
}
Request handler
public class CreateUserRequestHandler : IAsyncRequestHandler<CreateUserRequest, CreateUserResponse>
{
private readonly UserManager<User> _userManager;
public CreateUserRequestHandler()
{
}
public async Task<CreateUserResponse> Handle(CreateUserRequest request)
{
//create the user and assign it to the organisation
var user = new User
{
Email = request.EmailAddress,
OrganisationUsers = new List<OrganisationUser> { new OrganisationUser { OrganisationId = request.OrganisationId } }
};
//create new user with password.
await _userManager.CreateAsync(user, request.Password);
//create response.
var response = new CreateUserResponse{UserId = user.Id, EmailAddress = user.Email};
return response;
}
}
Controller class
public class UserController : Controller
{
private readonly IMediator _mediator;
public UserController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost]
public async Task<CreateUserResponse> Post(CreateUserRequest request)
{
return await _mediator.Send(request);
}
}
the error occurs inside the controller class it does not hit the async request handler.
Is there anything wrong with the DI registration? I have looked at the examples but could not find anything specific to aspnet core.

How to log in my custom user table in MVC 4?

I want to use my custom User table in MVC 4 code first application. I defined advance User table in my database context:
public class MyDatabase : DbContext
{
public DbSet<User> UserSet { get; set; }
public DbSet<News> NewsSet { get; set; }
...
}
Model is like:
public class User
{
[Key]
public int Id{ get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string Name { get; set; }
public string SurName { get; set; }
...
}
When application start, it calls this:
WebSecurity.InitializeDatabaseConnection("MyDatabase", "Users", "Id", "UserName", autoCreateTables: true);
In controller I use Add(entity) to save user entity. After saving I want to log in user. But it does not work:
[HttpPost]
public ActionResult Register(User user)
{
var result = MyService.SaveUser(user);
WebSecurity.Login(result.UserName, result.Password, true);
return RedirectToAction("Index", "Profile", new { id = result.Id });
}
After saving user, it's data stored in my database, but it can not log in. How should I do?
Edit:
Is it right to save User entity with my business method? Or I must do it only with
WebSecurity.CreateUserAndAccount()?
If I can use my own save method, how to save password in database?
You could just use forms authentication directly.
[HttpPost]
public ActionResult Register(User user)
{
var result = MyService.SaveUser(user);
SignIn(result.Id, "");
return RedirectToAction("Index", "Profile", new { id = result.Id });
}
public void SignIn(string accountId, string roles)
{
var authTicket = new FormsAuthenticationTicket(
1,
accountId,
DateTime.Now,
DateTime.Now.AddMinutes(20),
false,
roles
);
string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
HttpContext.Current.Response.Cookies.Add(authCookie);
}
Here is a user class that will help you with password issue. It relies on BCrypt
public class UserAccount
{
public string Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public string Password
{
get { return PasswordHash; }
set { PasswordHash = HashPassword(value); }
}
public string PasswordHash { get; private set; }
public List<string> Roles { get; set; }
public string AuthenticationRoles
{
get { return Roles == null ? "" : String.Join(",", Roles.Select(x => x.ToString())); }
}
public bool IsActive { get; set; }
public string Name { get; set; }
public bool PasswordIsValid(string password)
{
bool matches = BCrypt.Net.BCrypt.Verify(password, Password);
return matches;
}
private string HashPassword(string value)
{
return BCrypt.Net.BCrypt.HashPassword(value);
}
}