Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions exercise.tests/IntegrationTests/BaseIntegrationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ FROM users u
protected const int StudentCommentID2 = 3;


protected async Task<string> LoginAndGetToken(string email, string password, bool success = true)
protected async Task<string> LoginAndGetToken(string email, string password, bool success = true, bool longlife = false)
{
var loginBody = new LoginRequestDTO { email = email, password = password };
var loginBody = new LoginRequestDTO { email = email, password = password, longlifetoken = longlife };
var loginRequestBody = new StringContent(
JsonSerializer.Serialize(loginBody),
Encoding.UTF8,
Expand Down
81 changes: 81 additions & 0 deletions exercise.tests/IntegrationTests/TokenTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace exercise.tests.IntegrationTests
{
public class TokenTests : BaseIntegrationTest
{
[Test]
public async Task CreateToken_ShouldGenerateValidJwt()
{
string token = await LoginAndGetToken(TeacherEmail, TeacherPassword);
var handler = new JwtSecurityTokenHandler();
var jwt = handler.ReadJwtToken(token);


//var realid = jwt.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Sid)?.Value;
string? email = jwt.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value;
string? role = jwt.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Role)?.Value;
var expClaim = jwt.Claims.FirstOrDefault(c => c.Type == "exp")?.Value;


using (Assert.EnterMultipleScope())
{
Assert.That(email, Is.EqualTo(TeacherEmail));
Assert.That(role, Is.EqualTo("1"));
Assert.That(jwt.ValidTo, Is.GreaterThan(DateTime.UtcNow));
}
}

[Test]
public async Task CreateToken_LongLife_ShouldExpireLater() {
string token = await LoginAndGetToken(TeacherEmail, TeacherPassword, true, true);
var handler = new JwtSecurityTokenHandler();
var jwt = handler.ReadJwtToken(token);

//var realid = jwt.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Sid)?.Value;
string? email = jwt.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value;
string? role = jwt.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Role)?.Value;
var expClaim = jwt.Claims.FirstOrDefault(c => c.Type == "exp")?.Value;

//Console.WriteLine(expClaim);
Assert.Multiple(() =>
{
Assert.That(email, Is.EqualTo(TeacherEmail));
Assert.That(role, Is.EqualTo("1"));
Assert.That(jwt.ValidTo, Is.GreaterThan(DateTime.UtcNow.AddDays(6.5)));
Assert.That(jwt.ValidTo, Is.LessThan(DateTime.UtcNow.AddDays(7.5)));
});
}

[Test]
public async Task CreateToken_NormalLife_ShouldExpireLater()
{
string token = await LoginAndGetToken(TeacherEmail, TeacherPassword);
var handler = new JwtSecurityTokenHandler();
var jwt = handler.ReadJwtToken(token);

//var realid = jwt.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Sid)?.Value;
string? email = jwt.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value;
string? role = jwt.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Role)?.Value;
var expClaim = jwt.Claims.FirstOrDefault(c => c.Type == "exp")?.Value;

//Console.WriteLine(expClaim);

using (Assert.EnterMultipleScope())
{
Assert.That(email, Is.EqualTo(TeacherEmail));
Assert.That(role, Is.EqualTo("1"));
Assert.That(jwt.ValidTo, Is.GreaterThan(DateTime.UtcNow.AddMinutes(50)));
Assert.That(jwt.ValidTo, Is.LessThan(DateTime.UtcNow.AddHours(2)));
}
}

}
}
4 changes: 2 additions & 2 deletions exercise.wwwapi/Authorization/Handlers/UserExistsHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ protected override async Task HandleRequirementAsync(
//_logger.LogWarning("Available claims in token: {Claims}", string.Join(", ", claims));

// Get user ID from claims
var userIdClaim = context.User.FindFirst(ClaimTypes.NameIdentifier)
?? context.User.FindFirst(ClaimTypes.Sid);
var userIdClaim = context.User.FindFirst(ClaimTypes.Sid)
?? context.User.FindFirst(ClaimTypes.NameIdentifier);


if (userIdClaim == null || !int.TryParse(userIdClaim.Value, out int userId))
Expand Down
1 change: 1 addition & 0 deletions exercise.wwwapi/DTOs/Login/LoginRequestDTO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ public class LoginRequestDTO
{
public string? email { get; set; }
public string? password { get; set; }
public bool? longlifetoken { get; set; }
}
}
22 changes: 11 additions & 11 deletions exercise.wwwapi/Endpoints/UserEndpoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace exercise.wwwapi.EndPoints
{
Expand All @@ -27,8 +28,9 @@ public static void ConfigureAuthApi(this WebApplication app)
users.MapGet("/", GetUsers).WithSummary("Get all users by first name if provided");
users.MapGet("/{id:int}", GetUserById).WithSummary("Get user by user id");
users.MapPatch("/{id:int}", UpdateUser).WithSummary("Update a user");

}

/// <summary>
/// Retrieves users, optionally filtered by a case-insensitive search on first name, last name, or full name.
/// </summary>
Expand All @@ -51,11 +53,6 @@ public static void ConfigureAuthApi(this WebApplication app)
private static async Task<IResult> GetUsers(IRepository<User> repository, ClaimsPrincipal claims, string? name)
{
int? id = claims.UserRealId();
if (id == null)
{
return TypedResults.Ok(new ResponseDTO<object>()
{ Message = "Invalid token" });
}

IEnumerable<User> results = await repository.Get();
string? search = name?.Trim().ToLower();
Expand Down Expand Up @@ -145,9 +142,12 @@ private static IResult Login(IRepository<User> repository, IMapper mapper, Login
return Results.BadRequest(new ResponseDTO<Object>() { Message = "Invalid email and/or password provided" });
}

string token = CreateToken(user, config);
string token;
if (request.longlifetoken.GetValueOrDefault()) token = CreateToken(user, config, 7);
else token = CreateToken(user, config, 1.0 / 24);


ResponseDTO<LoginSuccessDTO> response = new ResponseDTO<LoginSuccessDTO>
ResponseDTO <LoginSuccessDTO> response = new ResponseDTO<LoginSuccessDTO>
{
Message = "success",
Data = new LoginSuccessDTO()
Expand All @@ -161,7 +161,7 @@ private static IResult Login(IRepository<User> repository, IMapper mapper, Login
return Results.Ok(response);

}

[Authorize]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
Expand Down Expand Up @@ -269,7 +269,7 @@ private static async Task<IResult> UpdateUser(IRepository<User> repository, Clai
}

// Helper, creates jwt tokens
private static string CreateToken(User user, IConfigurationSettings config)
private static string CreateToken(User user, IConfigurationSettings config, double days)
{
List<Claim> claims =
[
Expand All @@ -284,7 +284,7 @@ private static string CreateToken(User user, IConfigurationSettings config)
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha512Signature);
var token = new JwtSecurityToken(
claims: claims,
expires: DateTime.Now.AddDays(1),
expires: DateTime.UtcNow.AddDays(days),
signingCredentials: credentials
);
var jwt = new JwtSecurityTokenHandler().WriteToken(token);
Expand Down
Loading