Login API method returning 400 Bad request in Xamarin Forms - api

I have a login API method in Xamarin for which I am using a .net core project.The back end login in the web application works perfectly returning the token once I am logged in,but on the client side in Xamarin I am having troubles with it as the response returns a 400 Bad request.I checked the API and everything seems to be fine.I pass the token in the headers for the request along with the credentials and I am checking for the request.
This is the client side API:
public async Task<string> Login(string email, string password)
{
var urlLogin = "http://10.0.2.2:5000/api/Token/";
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("username", email),
new KeyValuePair<string, string>("password", password),
new KeyValuePair<string, string>("grant_type", "password")
});
var httpClient = new HttpClient();
var authData = string.Format("{0}:{1}", email, password);
var authHeaderValue = Convert.ToBase64String(Encoding.UTF8.GetBytes(authData));
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authHeaderValue);
var json = JsonConvert.SerializeObject(formContent);
var content = new StringContent(json, Encoding.UTF8, "application/json");
try
{
CancellationTokenSource cts = new CancellationTokenSource();
httpClient.DefaultRequestHeaders.Accept.Clear();
var responseMessage = httpClient.PostAsync(urlLogin, content).Result;
if(responseMessage.IsSuccessStatusCode)
{
await App.Current.MainPage.DisplayAlert("Login succesful!", "Welcome", "ok", "cancel");
await Xamarin.Forms.Application.Current.MainPage.Navigation.PushModalAsync(new UsersPage());
}
else
{
await App.Current.MainPage.DisplayAlert("Wrong credentials", "Please try again!", "ok", "cancel");
}
}
catch (Exception ex)
{
ex.Message.ToString();
throw;
}
return "";
}
The server-side login api:
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Post(User userData)
{
if (userData != null && userData.Email != null && userData.Password != null)
{
var user = await GetUser(userData.Email, userData.Password);
if (user != null)
{
//create claims details based on the user information
var claims = new[] {
new Claim(JwtRegisteredClaimNames.Sub, config["Jwt:Subject"]),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim("Id", user.Id.ToString()),
new Claim("Name", user.Name),
new Claim("Phone", user.Phone.ToString()),
new Claim("Email", user.Email),
new Claim ("ConfPassword",user.ConfPassword)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["Jwt:Key"]));
var signIn = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(config["Jwt:Issuer"], config["Jwt:Audience"], claims, expires: DateTime.UtcNow.AddDays(1), signingCredentials: signIn);
return Ok(new JwtSecurityTokenHandler().WriteToken(token));
}
else
{
return BadRequest("Invalid credentials");
}
}
else
{
return BadRequest();
}
}
private async Task<User> GetUser(string email, string password)
{
return await _context.User.FirstOrDefaultAsync(u => u.Email == email && u.Password == password);
}
User.cs
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string Password { get; set; }
public string ConfPassword { get; set; }
}
Can someone please help me figure out what is wrong with the API?I am new in working with mobile API so please bear with me.

I have managed to make it work as #Jason suggested.This is the working method on the client side:
public async Task<string> Login(User user)
{
var urlLogin = "http://10.0.2.2:5000/api/Token/";
var httpClient = new HttpClient();
var authData = string.Format("{0}:{1}", user.Email, user.Password);
var authHeaderValue = Convert.ToBase64String(Encoding.UTF8.GetBytes(authData));
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authHeaderValue);
var json = JsonConvert.SerializeObject(user);
var content = new StringContent(json, Encoding.UTF8, "application/json");
try
{
CancellationTokenSource cts = new CancellationTokenSource();
httpClient.DefaultRequestHeaders.Accept.Clear();
var responseMessage = httpClient.PostAsync(urlLogin, content).Result;
if(responseMessage.IsSuccessStatusCode)
{
await App.Current.MainPage.DisplayAlert("Login succesful!", "Welcome", "ok", "cancel");
await Xamarin.Forms.Application.Current.MainPage.Navigation.PushModalAsync(new UsersPage());
}
else
{
await App.Current.MainPage.DisplayAlert("Wrong credentials", "Please try again!", "ok", "cancel");
}
}
catch (Exception ex)
{
ex.Message.ToString();
throw;
}
return "";
}

Related

ASP.NET Core Web API - How to send Registration Notification Email without link and token

In my ASP.NET Core-6 Web API, I am sending email notification to users when his user registration account is created.
MailService:
public class MailService : IMailService
{
private readonly MailSettings _mailSettings;
private readonly ILogger _logger;
public MailService(MailSettings mailSettings, ILogger logger)
{
_logger = logger;
_mailSettings = mailSettings;
}
public async Task<bool> SendEmailAsync(MailRequest mailRequest)
{
var email = new MimeMessage { Sender = MailboxAddress.Parse(_mailSettings.Mail) };
email.To.Add(MailboxAddress.Parse(mailRequest.ToEmail));
email.Subject = mailRequest.Subject;
var builder = new BodyBuilder();
if (mailRequest.Attachments != null)
{
foreach (var file in mailRequest.Attachments.Where(file => file.Length > 0))
{
byte[] fileBytes;
await using (var ms = new MemoryStream())
{
file.CopyTo(ms);
fileBytes = ms.ToArray();
}
builder.Attachments.Add((file.FileName + Guid.NewGuid().ToString()), fileBytes, ContentType.Parse(file.ContentType));
}
}
builder.HtmlBody = mailRequest.Body;
email.Body = builder.ToMessageBody();
try
{
using var smtp = new SmtpClient();
smtp.Connect(_mailSettings.Host, _mailSettings.Port, SecureSocketOptions.StartTls);
smtp.Authenticate(_mailSettings.Mail, _mailSettings.Password);
await smtp.SendAsync(email);
smtp.Disconnect(true);
return true;
}
catch (Exception e)
{
_logger.Error(e, e.Source, e.InnerException, e.Message, e.ToString());
return false;
}
}
}
MailRequest:
public class MailRequest
{
public string ToEmail { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
public List<IFormFile> Attachments { get; set; }
}
GetEmailBody:
public static async Task<string> GetEmailBody(string emailTempPath, string token, string email)
{
var link = $"https://myapp.com/Manager/RegisterManager?email={email}&token={token}";
var temp = await File.ReadAllTextAsync(Path.Combine(Directory.GetCurrentDirectory(), emailTempPath));
var emailBody = temp.Replace("**link**", link);
return emailBody;
}
Register:
public async Task<Response<string>> Register(RegisterUserDto model)
{
var user = _mapper.Map<AppUser>(model);
user.IsActive = true;
var response = new Response<string>();
using (var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await _userManager.AddToRoleAsync(user, UserRoles.Customer);
var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var encodedToken = TokenConverter.EncodeToken(token);
var userRole = await _userManager.GetRolesAsync(user);
var mailBody = await EmailBodyBuilder.GetEmailBody(user, userRole.ToList(), emailTempPath: "StaticFiles/Html/ConfirmEmail.html", linkName: "ConfirmEmail", encodedToken, controllerName: "Authentication");
var mailRequest = new MailRequest()
{
Subject = "Registration Notification",
Body = mailBody,
ToEmail = model.Email
};
bool emailResult = await _mailService.SendEmailAsync(mailRequest);
if (emailResult)
{
_logger.Information("Mail sent successfully");
var customer = new Customer
{
AppUser = user
};
await _unitOfWork.Customers.InsertAsync(customer);
await _unitOfWork.Save();
response.StatusCode = (int)HttpStatusCode.Created;
response.Succeeded = true;
response.Data = user.Id;
response.Message = "User created successfully! Please check your mail to verify your account.";
transaction.Complete();
return response;
}
_logger.Information("Mail service failed");
transaction.Dispose();
response.StatusCode = (int)HttpStatusCode.BadRequest;
response.Succeeded = false;
response.Message = "Registration failed. Please try again";
return response;
}
response.Message = GetErrors(result);
response.StatusCode = (int)HttpStatusCode.BadRequest;
response.Succeeded = false;
transaction.Complete();
return response;
};
}
I want to send the Username and Password to the user as Email notification. This will not include link and token. And the will not confirm, but just to know his Username and password.
How do I modify the code above to achieve this?
Thanks
It seems you are using Microsoft.AspNetCore.Identity for the functionality.
You can disable email verification as you intend to not require the user functionality.
services.AddIdentity<ApplicationUser, IdentityRole>(config =>
{
config.SignIn.RequireConfirmedEmail = false;
})
And you can have your Username information from var user = _mapper.Map<AppUser>(model); and password from RegisterUserDto model, just construct the email body with these value and put it in mailBody variable.

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

Asp.net Core Object reference not set to an instance of an object

ASP.NET CORE API
The logged in user gives an error in the code below while adding a photo. Can anybody help?
var currentUserId = int.Parse(User.FindFirst(ClaimTypes.NameIdentifier).Value)
This code gives an error. Help me
Object reference not set to an instance of an object
PhotosController.cs
[HttpPost]
public ActionResult AddPhotoForCity(int cityId,[FromForm]PhotoForCreationDto photoForCreationDto)
{
var city = _appRepository.GetCityById(cityId);
if (city == null)
{
return BadRequest("Could not find the city.");
}
var currentUserId = int.Parse(User.FindFirst(ClaimTypes.NameIdentifier).Value);
karşılaştırmak gibi
if (currentUserId != city.UserId)
{
return Unauthorized();
}
var file = photoForCreationDto.File;
var uploadResult = new ImageUploadResult();
if (file.Length > 0)
{
using (var steam = file.OpenReadStream())
{
var uploadParams = new ImageUploadParams()
{
File = new FileDescription(file.Name,steam)
};
uploadResult = _cloudinary.Upload(uploadParams);
}
}
photoForCreationDto.Url = uploadResult.Url.ToString();
photoForCreationDto.PublicId = uploadResult.PublicId;
var photo = _mapper.Map<Photo>(photoForCreationDto);
photo.City = city;
if (!city.Photos.Any(p => p.IsMain))
{
photo.IsMain = true;
}
city.Photos.Add(photo);
if (_appRepository.SaveAll())
{
//eklenen fotoğrafı döndürüyoruz
var photoToRetun = _mapper.Map<Photo>(photoForCreationDto);
return CreatedAtRoute("GetPhoto", new {id = photo.Id}, photoToRetun);
}
return BadRequest("Cloud not add the photo");
}
AuthController.cs
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
private IAuthRepository _authRepository;
private IConfiguration _configuration;
public AuthController(IAuthRepository authRepository, IConfiguration configuration)
{
_authRepository = authRepository;
_configuration = configuration;
}
[HttpPost("register")]
public async Task<IActionResult> Register([FromBody] UserForRegisterDto userForRegisterDto)
{
if (await _authRepository.UserExists(userForRegisterDto.UserName))
{
ModelState.AddModelError("UserName", "Username already exists");
}
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var userToCreate = new User
{
UserName = userForRegisterDto.UserName
};
var createdUser = await _authRepository.Register(userToCreate, userForRegisterDto.Password);
return StatusCode(201);
}
[HttpPost("login")]
public async Task<ActionResult> Login([FromBody] UserForLoginDto userForLoginDto)
{
var user = await _authRepository.Login(userForLoginDto.UserName, userForLoginDto.Password);
if (user == null)
{
return Unauthorized();
}
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_configuration.GetSection("AppSettings:Token").Value);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.UserName)
}),
Expires = DateTime.Now.AddDays(1),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key)
, SecurityAlgorithms.HmacSha512Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);
return Ok(tokenString);
}
}
From the above code snippet you have shared, they mostly likely reason you are getting this error is because the user is not logged in, and hence this line of code
User.FindFirst(ClaimTypes.NameIdentifier).Value
is throwing an exception.
You could either do it like this.
User.FindFirst(ClaimTypes.NameIdentifier)?.Value
Or
[HttpPost]
[Authorize] // make sure you authorize your action method by adding this attribute and
// only allow logged in user to access it.
public ActionResult AddPhotoForCity(int cityId,[FromForm]PhotoForCreationDto photoForCreationDto)
{
}
try this one:
var currentUserId = int.Parse(User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier).Value);

ASP.NET Core application http client request to a web api

I have a running Web API and I try to get a bearer token back from it. Starting the request from Postman is working and I get the token back. Once doing from my application I always get an http 400 Bad Request error.
What am I missing here?
public async Task<string> GetToken(string userName, string passWord)
{
var request = new HttpRequestMessage(HttpMethod.Post, "api/auth/login");
request.Headers.Authorization = new AuthenticationHeaderValue(
"Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{ userName}:{ passWord}")));
request.Headers.Host = "api.my-host.com";
request.Headers.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
using var responseStream = await response.Content.ReadAsStreamAsync();
var authResult = await JsonSerializer.DeserializeAsync<AuthResult>(responseStream);
return authResult == null ? "" : authResult.Access_Token;
}
As requested here's a screenshot of the Postman result:
I created a HttpGet request and added the bearer token from Postman in the code and I receive data. Just the token request seems to have a problem.
And my controller:
namespace AmsAPI.Controller
{
[Produces("application/json")]
[Route("api/auth")]
[ApiController]
[Authorize]
public class AuthenticationController : ControllerBase
{
private readonly IAuthenticationManager _authenticationManager;
public AuthenticationController(IAuthenticationManager authenticationManager)
{
_authenticationManager = authenticationManager;
}
[HttpPost("login"), AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<ActionResult> Login([FromHeader] byte[] basic)
{
if (!ModelState.IsValid) return BadRequest();
string Basic = Encoding.UTF8.GetString(basic);
var splitBasic = Basic.Split(':');
AuthCredentials credentials = new()
{
UserName = splitBasic[0],
Password = splitBasic[1]
};
return await _authenticationManager.SignInCheck(credentials) ?
Ok(new
{
message = string.Format("User {0} successfully logged in.", credentials.UserName),
access_token = await _authenticationManager.CreateToken(),
token_type = "bearer",
expires_in = "3600"
}) :
Unauthorized();
}
[HttpGet("user")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<List<User>> GetUser() => await _authenticationManager.GetUser();
}
}
Well I have successfully reproduce your issue.
You are getting 400 because its searching for SSL credentials but
by default we don't have certificate bind with our request and its not
required. So to handle this 400 exception you have to use
HttpClientHandler which will bypass certificate error. So you can
try below way to get the token response successfully.
HTTP Request:
public async Task<string> GetToken()
{
var user = new UserCred();
user.user_name = "admin";
user.password = "123456";
var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; };
var client = new HttpClient(handler);
var json = JsonConvert.SerializeObject(user);
var data = new StringContent(json, Encoding.UTF8, "application/json");
var url = "http://localhost:21331/api/Authentication/login";
var response = await client.PostAsync(url, data);
string result = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(result);
return result;
}
Output:
Note: You need to add below code snippet to resolve your issue. But I like to call HTTP POST request in above ways to make it little
clean. You can continue with your code just adjust what I suggest.
var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; };
var client = new HttpClient(handler);
Hope above steps resolve your problem accordingly.
It appeared that Postman is sending the Basic Authorization Header as Content of the Http request and not in the Header, but my Web App was implementing the Authorization correctly in the Header.
So in the WebApi it looks like
[Authorize]
[Route("api/auth")]
[ApiController]
public class AuthenticationController : ControllerBase
{
private readonly IAuthenticationManager _authenticationManager;
public AuthenticationController(IAuthenticationManager authenticationManager)
{
_authenticationManager = authenticationManager;
}
[HttpPost("login"), AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<ActionResult> Login()
{
//check if header has Authorization
if (!Request.Headers.ContainsKey("Authorization")) return BadRequest();
try
{
AuthenticationHeaderValue authenticationHeaderValue = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
if (authenticationHeaderValue == null) throw new Exception();
var bytes = Convert.FromBase64String(authenticationHeaderValue.Parameter ?? throw new Exception());
string[] creds = Encoding.UTF8.GetString(bytes).Split(':');
AuthCredentials credentials = new()
{
UserName = creds[0],
Password = creds[1]
};
return await _authenticationManager.SignInCheck(credentials) ?
Ok(new
{
message = string.Format("User {0} successfully logged in.", credentials.UserName),
access_token = await _authenticationManager.CreateToken(),
token_type = "bearer",
expires_in = "3600"
}) :
Unauthorized();
}
catch (Exception)
{
return BadRequest();
}
}
}
and my request like following:
public async Task<string> GetToken(string userName, string passWord)
{
//set request
var request = new HttpRequestMessage(HttpMethod.Post, "api/auth/login");
//set Header
request.Headers.Authorization = new AuthenticationHeaderValue(
"Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{ userName}:{ passWord}")));
//get response
var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
using var responseStream = await response.Content.ReadAsStreamAsync();
var authResult = await JsonSerializer.DeserializeAsync<AuthResult>(responseStream);
var token = authResult.Access_Token;
return authResult == null ? "Authorization failed!" : "Bearer token successfully created!";
}
and the httpClient is outsourced into a Service
public static void ConfigureServices(this IServiceCollection services)
{
var url = Environment.GetEnvironmentVariable("amsApiUrl");
var host = Environment.GetEnvironmentVariable("amsHostUrl");
//set HttpClient
services.AddHttpClient<IAmsAccountService, AmsAccountService>(c =>
{
c.BaseAddress = new Uri(url ?? "");
c.DefaultRequestHeaders.Accept.Clear();
c.DefaultRequestHeaders.Host = host;
c.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
c.Timeout = TimeSpan.FromSeconds(10);
})
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
CookieContainer = new CookieContainer(),
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) =>
{
return true;
}
};
});
}

Set Authenticated User Globally for Testing in ASP.NET Core

I am working on an ASP.NET Core 2.2 with ASP.Net Core Identity project.
I would like to set the authenticated User, with its UserId, globally for testing.
It this possible?
For Integration Test, you could progammly login the application, save the cookies and then attach the cookies for sub-requests.
Try to implement custom WebApplicationFactory like
public class CustomWebApplicationFactory<TEntryPoint> : WebApplicationFactory<TEntryPoint> where TEntryPoint : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
});
base.ConfigureWebHost(builder);
}
public new HttpClient CreateClient()
{
var cookieContainer = new CookieContainer();
var uri = new Uri("https://localhost:44344/Identity/Account/Login");
var httpClientHandler = new HttpClientHandler
{
CookieContainer = cookieContainer
};
HttpClient httpClient = new HttpClient(httpClientHandler);
var verificationToken = GetVerificationToken(httpClient, "https://localhost:44344/Identity/Account/Login");
var contentToSend = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("Email", "test#outlook.com"),
new KeyValuePair<string, string>("Password", "1qaz#WSX"),
new KeyValuePair<string, string>("__RequestVerificationToken", verificationToken),
});
var response = httpClient.PostAsync("https://localhost:44344/Identity/Account/Login", contentToSend).Result;
var cookies = cookieContainer.GetCookies(new Uri("https://localhost:44344/Identity/Account/Login"));
cookieContainer.Add(cookies);
var client = new HttpClient(httpClientHandler);
return client;
}
private string GetVerificationToken(HttpClient client, string url)
{
HttpResponseMessage response = client.GetAsync(url).Result;
var verificationToken =response.Content.ReadAsStringAsync().Result;
if (verificationToken != null && verificationToken.Length > 0)
{
verificationToken = verificationToken.Substring(verificationToken.IndexOf("__RequestVerificationToken"));
verificationToken = verificationToken.Substring(verificationToken.IndexOf("value=\"") + 7);
verificationToken = verificationToken.Substring(0, verificationToken.IndexOf("\""));
}
return verificationToken;
}
}
And then
public class IntegrationTestWithIdentityTest : IClassFixture<CustomWebApplicationFactory<Startup>>
{
private readonly HttpClient _client;
private readonly CustomWebApplicationFactory<Startup> _factory;
public IntegrationTestWithIdentityTest(CustomWebApplicationFactory<Startup> factory)
{
_factory = factory;
_client = factory.CreateClient();
}
[Fact]
public async Task IndexRendersCorrectTitle()
{
var response = await _client.GetAsync("https://localhost:44344/About");
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
Assert.Contains("Send Email", responseString);
}
}
Source Code: IntegrationTestWithIdentityTest.
If you want to mock a user which is not exist in the Identity Table, you need to define a new endpoint which will sign the user with
public async Task<IActionResult> Login()
{
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "edward"));
identity.AddClaim(new Claim(ClaimTypes.Name, "edward zhou"));
//add your own claims from jwt token
var principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, new AuthenticationProperties { IsPersistent = true });
return View();
}