-
Notifications
You must be signed in to change notification settings - Fork 0
Authentication
Authentication is the process of validating user credentials and authorization is the process of checking privileges for a user to access specific modules in an application.
A simple example of authentication is entering a username and password when you log in to any website. These credentials are verified from the database or any other alternative, if it exists then the user is a valid candidate for the next Process-Authorization.
We have used JWT(JSON Web Token) Authentication. JWT is an open standard used for securely transmitting information between parties as a JSON object. JSON Web Tokens are very useful for various scenarios like authorization purposes or Information exchange using digitally signed key-value pairs.
We need to install Microsoft.AspNetCore.Authentication.JwtBearer and System.IdentityModel.Tokens.Jwt packages from NuGet Package Manager.
appsettings.json file has specified the values for the issuer, the audience, the expiration duration and the signing key as key-value pairs.
"JwtSettings": {
"Key": "", //Key
"Issuer": "", //Issuer
"Audience": "", //Audience
"DurationInMinutes": 60 // Expiration Duration
}
Then create model classes for JwtSettings, AuthenticationRequest and AuthenticationResponse at location Core/ProjetName.Application/Models as below,
public class JwtSettings
{
public string Key { get; set; }
public string Issuer { get; set; }
public string Audience { get; set; }
public double DurationInMinutes { get; set; }
}
public class AuthenticationRequest
{
public string Email { get; set; }
public string Password { get; set; }
}
public class AuthenticationResponse
{
public string Id { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public string Token { get; set; }
}
Run below command in Package manager console,
add-migration initial -Context IdentityDbContext
Now, create a authentication service interface named IAuthenticationService and implement that interface in AuthenticationService service class as below,
public interface IAuthenticationService
{
Task<AuthenticationResponse> AuthenticateAsync(AuthenticationRequest request);
}
public class AuthenticationService : IAuthenticationService
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly JwtSettings _jwtSettings;
public AuthenticationService(UserManager<ApplicationUser> userManager,
IOptions<JwtSettings> jwtSettings,
SignInManager<ApplicationUser> signInManager)
{
_userManager = userManager;
_jwtSettings = jwtSettings.Value;
_signInManager = signInManager;
}
public async Task<AuthenticationResponse> AuthenticateAsync(AuthenticationRequest request)
{
var user = await _userManager.FindByEmailAsync(request.Email);
if (user == null)
{
throw new Exception($"User with {request.Email} not found.");
}
var result = await _signInManager.PasswordSignInAsync(user.UserName, request.Password, false, lockoutOnFailure: false);
if (!result.Succeeded)
{
throw new Exception($"Credentials for '{request.Email} aren't valid'.");
}
JwtSecurityToken jwtSecurityToken = await GenerateToken(user);
AuthenticationResponse response = new AuthenticationResponse
{
Id = user.Id,
Token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken),
Email = user.Email,
UserName = user.UserName
};
return response;
}
private async Task<JwtSecurityToken> GenerateToken(ApplicationUser user)
{
var userClaims = await _userManager.GetClaimsAsync(user);
var roles = await _userManager.GetRolesAsync(user);
var roleClaims = new List<Claim>();
for (int i = 0; i < roles.Count; i++)
{
roleClaims.Add(new Claim("roles", roles[i]));
}
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Email, user.Email),
new Claim("uid", user.Id)
}
.Union(userClaims)
.Union(roleClaims);
var symmetricSecurityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.Key));
var signingCredentials = new SigningCredentials(symmetricSecurityKey, SecurityAlgorithms.HmacSha256);
var jwtSecurityToken = new JwtSecurityToken(
issuer: _jwtSettings.Issuer,
audience: _jwtSettings.Audience,
claims: claims,
expires: DateTime.UtcNow.AddMinutes(_jwtSettings.DurationInMinutes),
signingCredentials: signingCredentials);
return jwtSecurityToken;
}
}
We need to write extension service class code for IdentityServiceExtensions inside Identity layer of Infrastructure folder as below,
public static class IdentityServiceExtensions
{
public static void AddIdentityServices(this IServiceCollection services, IConfiguration configuration)
{
services.Configure<JwtSettings>(configuration.GetSection("JwtSettings"));
services.AddTransient<IAuthenticationService, AuthenticationService>();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(o =>
{
o.RequireHttpsMetadata = false;
o.SaveToken = false;
o.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero,
ValidIssuer = configuration["JwtSettings:Issuer"],
ValidAudience = configuration["JwtSettings:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["JwtSettings:Key"]))
};
o.Events = new JwtBearerEvents()
{
OnAuthenticationFailed = c =>
{
c.NoResult();
c.Response.StatusCode = 500;
c.Response.ContentType = "text/plain";
return c.Response.WriteAsync(c.Exception.ToString());
},
OnChallenge = context =>
{
context.HandleResponse();
context.Response.StatusCode = 401;
context.Response.ContentType = "application/json";
var result = JsonConvert.SerializeObject("401 Not authorized");
return context.Response.WriteAsync(result);
},
OnForbidden = context =>
{
context.Response.StatusCode = 403;
context.Response.ContentType = "application/json";
var result = JsonConvert.SerializeObject("403 Not authorized");
return context.Response.WriteAsync(result);
},
};
});
}
}
To enable the Authentication on our project we need to add the code snippet below in our Program.cs under configure method.
app.UseAuthentication();
Demo video to get the clear result view of above implemented module.