question

HeinrichLudike-5598 avatar image
0 Votes"
HeinrichLudike-5598 asked HeinrichLudike-5598 commented

Identity & DotNetCore MVC and others

I am developing an application with various modules, all of this is hosted on azure.

We have our base application and then all our modules. Our modules are supposed to be logged into, the modules are written in DotNetCore MVC, WebForms, Python pretty much anything.

What would the best practice be to use the credentials that we have on our base application even though the modules do not necessarily have access to the database?
I am currently using API calls, but just like to know if there is a more suitable solution for the problem.

Regards

dotnet-aspnet-core-mvc
· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hi @HeinrichLudike-5598,

In my opinion, I also prefer to use API to validate the user, after user login success, uses a JWT token or Access token to store the user's identity. After that, configure the application to use Policy or Claim based authentication.

1 Vote 1 ·
HeinrichLudike-5598 avatar image
0 Votes"
HeinrichLudike-5598 answered ZhiLv-MSFT commented

I have implemented JWT bearer tokens and it is working for my APIs. However, with the code implemented I have a slight issue. When I implement the code below it does not seem that my user is authenticating.

             services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                 .AddJwtBearer(options =>
                 {
                     options.TokenValidationParameters = new TokenValidationParameters
                     {
                         ValidateIssuer = true,
                         ValidateAudience = true,
                         ValidateLifetime = true,
                         ValidateIssuerSigningKey = true,
                         ValidIssuer = Configuration["Jwt:Issuer"],
                         ValidAudience = Configuration["Jwt:Issuer"],
                         IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
                     };
                 });

99452-image.png
This is what happens with the code in my startup.cs

99461-image.png
This is what happens if I uncomment that section.

Any idea where the issue might be?



image.png (16.9 KiB)
image.png (18.5 KiB)
· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hi @HeinrichLudike-5598,

Can you share the relates code about how to generate the JWT Token? When generating the JWT token, have you ever added the claims in the JWT token? From the screenshot, it seems that you didn't add claims in the JWT token.

0 Votes 0 ·
HeinrichLudike-5598 avatar image
0 Votes"
HeinrichLudike-5598 answered

This is still the default code I have in my Areas/Identity/Pages/Account/Manage/Login.cshtml.cs I assume this is where the call for JWT should be made as well?

         public async Task<IActionResult> OnPostAsync(string returnUrl = null)
         {
             returnUrl = returnUrl ?? Url.Content("~/");
    
             if (ModelState.IsValid)
             {
                 // This doesn't count login failures towards account lockout
                 // To enable password failures to trigger account lockout, set lockoutOnFailure: true
                 var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);
                 if (result.Succeeded)
                 {
                     _logger.LogInformation("User logged in.");
                     return LocalRedirect(returnUrl);
                 }
                 if (result.RequiresTwoFactor)
                 {
                     return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe });
                 }
                 if (result.IsLockedOut)
                 {
                     _logger.LogWarning("User account locked out.");
                     return RedirectToPage("./Lockout");
                 }
                 else
                 {
                     ModelState.AddModelError(string.Empty, "Invalid login attempt.");
                     return Page();
                 }
             }
    
             // If we got this far, something failed, redisplay form
             return Page();
         }
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

HeinrichLudike-5598 avatar image
0 Votes"
HeinrichLudike-5598 answered HeinrichLudike-5598 published

This is what I have. I make a call to the AccessController, which executes this code.

public async Task<JsonResult> UserAuth(string email, string password)
{
var result = await _signInManager.PasswordSignInAsync(email, password, false, lockoutOnFailure: false);

 if (result.Succeeded)
 {
     _logger.LogInformation("User logged in.");

     var tokenString = GenerateJSONWebToken();
     return new JsonResult(new { token = tokenString });
 }

This is what GenerateJSONWebToken looks like.

private string GenerateJSONWebToken()
{
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

 var token = new JwtSecurityToken(_config["Jwt:Issuer"],
   _config["Jwt:Issuer"],
   null,
   expires: DateTime.Now.AddMinutes(120),
   signingCredentials: credentials);
    
  return new JwtSecurityTokenHandler().WriteToken(token);

}

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

ZhiLv-MSFT avatar image
0 Votes"
ZhiLv-MSFT answered HeinrichLudike-5598 commented

Hi @HeinrichLudike-5598,

From the GenerateJSONWebToken method, when you create the JwtSecurityToken instance, the third parameter (Claims) is null, you didn't add claims in JWT token.

You can refer the following sample code to handle Claims with JWT:

     [HttpPost]  
     [Route("login")]  
     public async Task<IActionResult> Login([FromBody] LoginModel model)  
     {  
         var user = await userManager.FindByNameAsync(model.Username);  
         if (user != null && await userManager.CheckPasswordAsync(user, model.Password))  
         {  
             var userRoles = await userManager.GetRolesAsync(user);  
  
             var authClaims = new List<Claim>  
             {  
                 new Claim(ClaimTypes.Name, user.UserName),  
                 new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),  
             };  
  
             foreach (var userRole in userRoles)  
             {  
                 authClaims.Add(new Claim(ClaimTypes.Role, userRole));  
             }  
  
             var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT:Secret"]));  
  
             var token = new JwtSecurityToken(  
                 issuer: _configuration["JWT:ValidIssuer"],  
                 audience: _configuration["JWT:ValidAudience"],  
                 expires: DateTime.Now.AddHours(3),  
                 claims: authClaims,  
                 signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)  
                 );  
  
             return Ok(new  
             {  
                 token = new JwtSecurityTokenHandler().WriteToken(token),  
                 expiration = token.ValidTo  
             });  
         }  
         return Unauthorized();  
     }  

More detail information, check the following articles:

Authentication And Authorization In ASP.NET 5 With JWT And Swagger

JWT Authentication In ASP.NET Core


If the answer is helpful, please click "Accept Answer" and upvote it.
Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

Best regards,
Dillion

· 5
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Thank you, so will this have to be called the moment a user logs in as well instead of this? Is it wise to have my main application generate the tokens as well? Would it be better for a separate API to handle this?

 public async Task<IActionResult> OnPostAsync(string returnUrl = null)
 {
     returnUrl = returnUrl ?? Url.Content("~/");
    
     if (ModelState.IsValid)
     {
         // This doesn't count login failures towards account lockout
         // To enable password failures to trigger account lockout, set lockoutOnFailure: true
         var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);
         if (result.Succeeded)
         {
             _logger.LogInformation("User logged in.");
             return LocalRedirect(returnUrl);
         }
         // Code below removed, just for lockout & 2FA etc 
    }
 }
0 Votes 0 ·
ZhiLv-MSFT avatar image ZhiLv-MSFT HeinrichLudike-5598 ·

Hi @HeinrichLudike-5598,

Yes, if you already configure the application to use JWT authentication, after user login, you should generate the JWT token.

It depends on your design pattern, both using your main application and a separate API to handle the JWT are ok. But if use a separate API to handle the JWT, this API act as an Authentication Server, when user login, it should call this API and get the JWT token. And you could use it to manager another application.

1 Vote 1 ·

Okay thank you, the claims are working when I access it via postman. The problem I have is when I log in to my application and I have my JWT enabled in code is where things go strange.

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
};
})

Whenever I log in, it just returns me to my application with no user logged in, I'm a little stumped here.
99858-image.png


0 Votes 0 ·
image.png (31.1 KiB)
Show more comments