Password reset token generation fails - asp.net-mvc

I'm trying to create password reset feature in my ASP.NET Core MVC project. The validation process is done by jQuery. The method for this purpose is:
[HttpPost]
public async Task<JsonResult> ResetPassword(string username, string newPassword)
{
var user = await _userManager.FindByNameAsync(username);
var token = await _userManager.GeneratePasswordResetTokenAsync(user);
if(user != null)
{
var result = await _userManager.ResetPasswordAsync(user, token, newPassword);
if (result.Succeeded)
{
return Json(new { success = true});
}
else
{
return Json(new { success = false });
}
}
else
{
return Json(new { success = false });
}
}
The problem is that _userManager.GeneratePasswordResetTokenAsync(user) causes the context to be disposed and the process stops there. How can I solve this?

I solved this problem by registering token:
builder.Services.AddIdentity<AppUser, AppRole>(options =>
{
options.User.RequireUniqueEmail = false;
options.Password.RequireDigit = true;
options.Password.RequiredUniqueChars = 0;
options.Password.RequireUppercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequiredLength = 6;
options.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultProvider;
}).AddEntityFrameworkStores<IdentityAppContext>().AddDefaultTokenProviders();

Related

Xamarin iOS Oidc downloads webpage after login

I have an app that uses Oidc to login. This works fine in most cases. But there is one iphone 8+ running iOS 15.6.1 that keeps downloading the return url from the login request.
i use Xamarin forms v 5.0.0.2515, Xamarin.Essentials 1.7.3, Xamarin.CommunityToolkit 2.0.5
IdentityModel.OidcClient 3.1.2
The problem is that it works on all other devices and i can't replicate it in a dev environment.
public class WebauthenticatorBrowser : IBrowser
{
private readonly string callbackUrl;
public WebauthenticatorBrowser(string? callbackUrl = null)
{
this.callbackUrl = callbackUrl;
}
public async Task<BrowserResult> InvokeAsync(BrowserOptions options, CancellationToken cancellationToken = default)
{
try
{
var callBack = string.IsNullOrWhiteSpace(callbackUrl) ? options.EndUrl : callbackUrl;
var authResult = await WebAuthenticator.AuthenticateAsync(new WebAuthenticatorOptions
{
Url = new Uri(options.StartUrl),
CallbackUrl = new Uri(callBack),
PrefersEphemeralWebBrowserSession = false
});
var authorizeResponse = ToRawIdentityUrl(options.EndUrl, authResult);
return new BrowserResult()
{
Response = authorizeResponse
};
}
catch (Exception ex)
{
return new BrowserResult()
{
Error = ex.Message
};
}
}
private string ToRawIdentityUrl(string redirectUrl, WebAuthenticatorResult result)
{
IEnumerable<string> parameters = result.Properties.Select(x => $"{x.Key}={x.Value}");
var values = string.Join("&", parameters);
return $"{redirectUrl}#{values}" ;
}
}
In my oidc class where i create my client
private async Task createOidcClient() {
document = await httpClient.GetDiscoveryDocumentAsync(idsUrl);
if (document.IsError) {
alertPresenter.Alert("Connection issue", "please try again");
return;
}
var options = new OidcClientOptions {
Authority = document.Issuer,
ClientId = "---clientID-",
Scope = "-- all the scopes --",
RedirectUri = "-- callback url--",
PostLogoutRedirectUri = " -- callback url--",
Browser = new WebauthenticatorBrowser(),
ResponseMode = OidcClientOptions.AuthorizeResponseMode.Redirect
};
client = new OidcClient(options);
}
the login call in the OicdClass
public async Task<LoginResult> Login() {
await createOidcClient();
var result = await client.LoginAsync(new LoginRequest());
if (result.IsError) return result;
setAuthHeader(result.AccessToken);
await accessTokenUpdater.SetAllTokens(result.AccessToken, result.RefreshToken, result.IdentityToken);
return result;
}

.NET CORE 3.1, MVC Async method not updating DB

AI am just moving to ASYNC methods and trying to get my data to update. I can select just find so I know the repository is working.
Action
[HttpPost]
public async Task<IActionResult> EditTeam(EmployeeVm empVm)
{
if (!ModelState.IsValid)
{
ModelState.AddModelError("", _errorUpdateMsg);
}
else
{
if (await _teamRepository.UpdateEmployee(empVm.Employee))
{
return RedirectToAction("Index");
}
ModelState.AddModelError("", _errorUpdateMsg);
}
return View(empVm);
}
My Constructor in repo
public TeamRepository(EnvisionDbContext envisionDbContext)
{
_envisonDbContext = envisionDbContext;
}
Here is my Update that does not save
public async Task<bool> UpdateEmployee(Employee employee)
{
var result = await _envisonDbContext.Employees.FirstOrDefaultAsync<Employee>(e => e.Id == employee.Id);
if (result != null)
{
result.FirstName = employee.FirstName;
result.LastName = employee.LastName;
result.Phone = employee.Phone;
result.IsActive = employee.IsActive;
await _envisonDbContext.SaveChangesAsync();
return true;
}
return false;
}
Thanks in advance for the help.
UPDATED: If I add this, it works. Is this because the two await calls are disconnected?
result.IsActive = employee.IsActive;
_envisonDbContext.Entry(result).State = EntityState.Modified;
Seems like you forgot update-method before savingchanges
if (result != null)
{
result.FirstName = employee.FirstName;
result.LastName = employee.LastName;
result.Phone = employee.Phone;
result.IsActive = employee.IsActive;
_envisionDbContext.Update(result); //paste it before you save changes
await _envisonDbContext.SaveChangesAsync();
return true;
}

JWT Authentication ASP.NET Core MVC application

I've seen numerous examples of how to use JWT authentication with Angular, React, Vue etc... clients but can't find any examples of using JWT authentication with ASP.NET Core (specifically 2.2) Web App Mvc.
Does anyone have any examples or advice on how to do this?
Thanks,
You can use this class based on nuget package JWT 3.0.3
using JWT;
using JWT.Algorithms;
using JWT.Serializers;
using Newtonsoft.Json;
using System;
namespace Common.Utils
{
public class JwtToken
{
private IJwtEncoder encoder;
private IJwtDecoder decoder;
/// <remarks>
/// This requires a key value randomly generated and stored in your configuration settings.
/// Consider that it is a good practice use keys as at least long as the output digest bytes
/// length produced by the hashing algorithm used. Since we use an HMAC-SHA-512 algorithm,
/// then we can provide it a key at least 64 bytes long.
/// <see cref="https://tools.ietf.org/html/rfc4868#page-7"/>
/// </remarks>
public string SecretKey { get; set; }
public JwtToken()
{
IJwtAlgorithm algorithm = new HMACSHA512Algorithm();
IJsonSerializer serializer = new JsonNetSerializer();
IDateTimeProvider datetimeProvider = new UtcDateTimeProvider();
IJwtValidator validator = new JwtValidator(serializer, datetimeProvider);
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
decoder = new JwtDecoder(serializer, validator, urlEncoder);
SecretKey = "";
}
public JwtToken(string secretKey) : this()
{
SecretKey = secretKey;
}
public bool IsTokenValid(string token)
{
return !string.IsNullOrWhiteSpace(DecodeToken(token));
}
public string GetToken(object payload)
{
try
{
return encoder.Encode(payload, SecretKey);
}
catch (Exception)
{
return encoder.Encode(new DataModel(payload), SecretKey);
}
}
public string DecodeToken(string token)
{
try
{
if (string.IsNullOrWhiteSpace(token) || token == "null")
{
return null;
}
return decoder.Decode(token, SecretKey, true);
}
catch (TokenExpiredException)
{
return null;
}
catch (SignatureVerificationException)
{
return null;
}
}
public T DecodeToken<T>(string token) where T : class
{
try
{
if (string.IsNullOrWhiteSpace(token))
{
return null;
}
return decoder.DecodeToObject<T>(token, SecretKey, true);
}
catch (TokenExpiredException)
{
return null;
}
catch (SignatureVerificationException)
{
return null;
}
catch (Exception)
{
var data = decoder.DecodeToObject<DataModel>(token, SecretKey, true).Data;
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(data));
}
}
}
public class DataModel
{
public DataModel(object data)
{
Data = data;
}
public object Data { get; set; }
}
}
Then in your Startup class Configure method set the jwt middleware
for check authentication status of each request:
app.Use((context, next) =>
{
// verify app access token if not another service call
var appAccessToken = context.Request.Headers["Authorization"];
if (appAccessToken.Count == 0)
{
context.Items["User"] = null;
}
else
{
var token = appAccessToken.ToString().Replace("Bearer ", "");
var jwtToken = new JwtToken(config.JwtTokenSecret); //you need a secret (with requirements specified above) in your configuration (db, appsettings.json)
if (string.IsNullOrWhiteSpace(token) || !jwtToken.IsTokenValid(token))
{
context.Response.StatusCode = 401;
return Task.FromResult(0);
}
dynamic user = jwtToken.DecodeToken<dynamic>(token);
var cachedToken = cache.Get(user.Id); //you need some cache for store your token after login success and so can check against
if (cachedToken == null || cachedToken.ToString() != token)
{
context.Response.StatusCode = 401;
return Task.FromResult(0);
}
context.Items["User"] = new Dictionary<string, string>() {
{ "FullName",user.Name?.ToString()},
{ "FirstName",user.FirstName?.ToString()},
{ "LastName",user.LastName?.ToString()},
{ "Role",user.Role?.ToString()},
{ "Email",user.Email?.ToString()}
};
}
return next();
});
And finally you need generate the token and return it after
authentication:
[AllowAnonymous]
public IActionResult Login(string username, string password)
{
User user = null; //you need some User class with the structure of the previous dictionary
if (checkAuthenticationOK(username, password, out user)) //chackAuthenticationOk sets the user against db data after a succesfull authentication
{
var token = new JwtToken(_config.JwtTokenSecret).GetToken(user); //_config is an object to your configuration
_cache.Set(user.id, token); //store in the cache the token for checking in each request
return Ok(token);
}
return StatusCode(401, "User is not authorized");
}
Add following code to startup
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Issuer"],
ValidAudience = Configuration["Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["SigningKey"]))
};
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env,, ILoggerFactory loggerFactory)
{
app.UseAuthentication();
}
Code for login action in AccountController
[Route("api/[controller]")]
public class AccountController : Controller
{
[AllowAnonymous]
[HttpPost]
[Route("login")]
public IActionResult Login([FromBody]LoginViewModel loginViewModel)
{
if (ModelState.IsValid)
{
var user = _userService.Authenticate(loginViewModel);
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, loginViewModel.Username),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
var token = new JwtSecurityToken
(
issuer: _configuration["Issuer"],
audience: _configuration["Audience"],
claims: claims,
expires: DateTime.UtcNow.AddDays(10),
notBefore: DateTime.UtcNow,
signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["SigningKey"])),
SecurityAlgorithms.HmacSha256)
);
return Ok(new
{
access_token = new JwtSecurityTokenHandler().WriteToken(token),
expires_in = (int)token.ValidTo.Subtract(DateTime.UtcNow).TotalSeconds,// TimeSpan.FromTicks( token.ValidTo.Ticks).TotalSeconds,
sub = loginViewModel.Username,
name = loginViewModel.Username,
fullName = user.FullName,
jobtitle = string.Empty,
phone = string.Empty,
email = user.EmailName,
});
}
}
}
I assume you have implemented JWT on the server side. To handle this on client side, first you have to add token to web browser local storage. Add to your main layout javascript (let's named it AuthService.js)
below code adds token to local storage after login button clicked. gettokenfromlocalstorage() retrieve token from local storage.
<script>
var token = "";
function Loginclick() {
var form = document.querySelector('form');
var data = new FormData(form);
var authsevice = new AuthService();
authsevice.LogIn(data.get("username").toString(), data.get("password").toString());
}
function gettokenfromlocalstorage() {
var authserv = new AuthService();
var mytoken = authserv.getAuth();
authserv.LogOut();
}
var AuthService = /** #class */ (function () {
function AuthService() {
this.authKey = "auth";
}
AuthService.prototype.LogIn = function (username, password) {
this.username = username;
this.password = password;
this.grant_type = "password";
this.client_id = "MyClientId";
var loginurl = "/api/Token/Auth";
var xhr = new XMLHttpRequest();
xhr.open("POST", loginurl, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(this));
xhr.onreadystatechange = function () {
console.log("onreadystatechange");
};
xhr.onerror = function () {
var aaa = this.responseText;
};
xhr.onload = function () {
var data = JSON.parse(this.responseText);
var auth = new AuthService();
auth.setAuth(data);
};
};
AuthService.prototype.LogOut = function () {
this.setAuth(null);
return true;
};
AuthService.prototype.setAuth = function (auth) {
if (auth) {
localStorage.setItem(this.authKey, JSON.stringify(auth));
}
else {
localStorage.removeItem(this.authKey);
}
return true;
};
AuthService.prototype.getAuth = function () {
var i = localStorage.getItem(this.authKey);
return i === null ? null : JSON.parse(i);
};
AuthService.prototype.isLoggedIn = function () {
return localStorage.getItem(this.authKey) !== null ? true : false;
};
return AuthService;
}());
var aa = new AuthService();
var gettoken = aa.getAuth();
if (gettoken !== null) {
token = gettoken.token;
}
</script>
To add token to the header of each anchor tag put below script also to
your main layout.
<script>
var links = $('a');
for (var i = 0; i < links.length; i++) {
links[i].onclick = function check() {
addheader(this.href);
return false;
}
}
function addheader(object) {
let xhr = new XMLHttpRequest();
xhr.open("GET", object, true);
xhr.setRequestHeader('Authorization', 'Bearer ' + token);
xhr.send(null);
xhr.onload = function () {
window.history.pushState("/", "", xhr.responseURL);
//mycontainer is a div for parialview content
$("#mycontainer").html(xhr.responseText);
window.onpopstate = function (e) {
if (e.state) {
$("html").html = e.state;
document.title = e.state.pageTitle;
}
};
};
}
</script>
Remember that using of this approach, each view has to be loaded as a partial view.
If you insert url address in a web browser bar directly this solution doesn't work. I haven't figured it out yet. That's why to manage token authentication is better using single page application, not multipage application.
You can use this boilerplate to understand how to implement JWT tokenization with .Net Core. In the project you can find JWT, Swagger and EF features.

HttpClient returns nothing even if the request is successfull

I am trying to call an MVC Controller action from an API Controller (both in different applications), I can successfully make a call to the API Controller and returns the result but the controll never gets back to the calling method and PostMan shows the request still pending.
I have this method in my WebApi Controller
[HttpGet]
public BaseModel GetUserId(string email)
{
if (string.IsNullOrWhiteSpace(email))
{
return new BaseModel
{
success = false,
message = "invalid username or email."
};
}
var result = _security.GetUserId(email);
var baseModel = JsonConvert.DeserializeObject<BaseModel>(result.Result);
return baseModel;
}
_security.GetUserById
public async Task<string> GetUserId(string userName)
{
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string,string>("email",userName)
});
var response = await httpClient.PostAsync(baseUri + "Account/GetUserId", content);
if (response.IsSuccessStatusCode)
{
var result = response.Content.ReadAsStringAsync().Result;
return result;
}
return "";
}
Here is My API Controller action
[HttpPost]
[AllowAnonymous]
public async Task<BaseModel> GetUserId(string email)
{
var user = await _userManager.FindByEmailAsync(email);
if (user == null)
{
return new BaseModel
{
success = false,
message = "No user exists with the specified email"
};
}
user = await _userManager.FindByNameAsync(email);
if (user == null)
{
return new BaseModel
{
success = false,
message = "No user exists with the specified username"
};
}
return new BaseModel
{
success = true,
data = user.Id
};
}

How can I reduce the time (TTFB) of the post request?

I'm working on a web site.I use both MVC 5 and Web api.
The TTFB is too long (6~10 s) for the web api request, but the mvc view's ttfb(1~2 s) is acceptable.
And when I post many request to the same api, only the first request has the problem.
this is my code in Login method
public HttpResponseMessage Login(LoginInfo loginInfo)
{
string result = JsonConvert.SerializeObject(new { status = "example" });
//check the whether user is already login
if (HttpContext.Current.User.Identity.IsAuthenticated
&& loginInfo.UserName == (string)HttpContext.Current.Session["user"])
{
result = JsonConvert.SerializeObject(new { status = (string)HttpContext.Current.Session["role"] });
return new HttpResponseMessage { Content = new StringContent(result, Encoding.UTF8, "application/json") };
}
Student student = studentRepo.GetById(loginInfo.UserName);
if (student == null)
{
result = JsonConvert.SerializeObject(new { status = "usr" });
}
else
{
string password;
string salt = (string)HttpContext.Current.Session["salt"];
if (string.IsNullOrEmpty(salt))
{
//the login page has expired
result = JsonConvert.SerializeObject(new { status = "expire" });
}
else
{
password = student.Password + salt;
password = MD5Helper.GetMd5(password);
if (password == loginInfo.Password)
{
//login success!
HttpContext.Current.Session.Remove("salt");
FormsAuthentication.SetAuthCookie(loginInfo.UserName, false);
HttpContext.Current.Session.Add("user", student.StuNo);
HttpContext.Current.Session.Add("role", student.Role);
result = JsonConvert.SerializeObject(new { status = student.Role });
}
else
{
result = JsonConvert.SerializeObject(new { status = "deny" });
}
}
}
return new HttpResponseMessage { Content = new StringContent(result, Encoding.UTF8, "application/json") };
}
Now I deployed my app to another server and the TTFB became shorter. And the problem about that session was deleted frequently is also fixed.

Resources