Skip to content

Commit 6131137

Browse files
authored
Merge pull request #120 from boolean-uk/119-auto-login-endpoint-variable-lifetime-of-token
Auto-login endpoint and variable token lifespan
2 parents 05244fd + 0226996 commit 6131137

File tree

5 files changed

+97
-15
lines changed

5 files changed

+97
-15
lines changed

exercise.tests/IntegrationTests/BaseIntegrationTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,9 @@ FROM users u
7272
protected const int StudentCommentID2 = 3;
7373

7474

75-
protected async Task<string> LoginAndGetToken(string email, string password, bool success = true)
75+
protected async Task<string> LoginAndGetToken(string email, string password, bool success = true, bool longlife = false)
7676
{
77-
var loginBody = new LoginRequestDTO { email = email, password = password };
77+
var loginBody = new LoginRequestDTO { email = email, password = password, longlifetoken = longlife };
7878
var loginRequestBody = new StringContent(
7979
JsonSerializer.Serialize(loginBody),
8080
Encoding.UTF8,
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
using Microsoft.Extensions.Configuration;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.IdentityModel.Tokens.Jwt;
5+
using System.Linq;
6+
using System.Security.Claims;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
10+
namespace exercise.tests.IntegrationTests
11+
{
12+
public class TokenTests : BaseIntegrationTest
13+
{
14+
[Test]
15+
public async Task CreateToken_ShouldGenerateValidJwt()
16+
{
17+
string token = await LoginAndGetToken(TeacherEmail, TeacherPassword);
18+
var handler = new JwtSecurityTokenHandler();
19+
var jwt = handler.ReadJwtToken(token);
20+
21+
22+
//var realid = jwt.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Sid)?.Value;
23+
string? email = jwt.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value;
24+
string? role = jwt.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Role)?.Value;
25+
var expClaim = jwt.Claims.FirstOrDefault(c => c.Type == "exp")?.Value;
26+
27+
28+
using (Assert.EnterMultipleScope())
29+
{
30+
Assert.That(email, Is.EqualTo(TeacherEmail));
31+
Assert.That(role, Is.EqualTo("1"));
32+
Assert.That(jwt.ValidTo, Is.GreaterThan(DateTime.UtcNow));
33+
}
34+
}
35+
36+
[Test]
37+
public async Task CreateToken_LongLife_ShouldExpireLater() {
38+
string token = await LoginAndGetToken(TeacherEmail, TeacherPassword, true, true);
39+
var handler = new JwtSecurityTokenHandler();
40+
var jwt = handler.ReadJwtToken(token);
41+
42+
//var realid = jwt.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Sid)?.Value;
43+
string? email = jwt.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value;
44+
string? role = jwt.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Role)?.Value;
45+
var expClaim = jwt.Claims.FirstOrDefault(c => c.Type == "exp")?.Value;
46+
47+
//Console.WriteLine(expClaim);
48+
Assert.Multiple(() =>
49+
{
50+
Assert.That(email, Is.EqualTo(TeacherEmail));
51+
Assert.That(role, Is.EqualTo("1"));
52+
Assert.That(jwt.ValidTo, Is.GreaterThan(DateTime.UtcNow.AddDays(6.5)));
53+
Assert.That(jwt.ValidTo, Is.LessThan(DateTime.UtcNow.AddDays(7.5)));
54+
});
55+
}
56+
57+
[Test]
58+
public async Task CreateToken_NormalLife_ShouldExpireLater()
59+
{
60+
string token = await LoginAndGetToken(TeacherEmail, TeacherPassword);
61+
var handler = new JwtSecurityTokenHandler();
62+
var jwt = handler.ReadJwtToken(token);
63+
64+
//var realid = jwt.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Sid)?.Value;
65+
string? email = jwt.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value;
66+
string? role = jwt.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Role)?.Value;
67+
var expClaim = jwt.Claims.FirstOrDefault(c => c.Type == "exp")?.Value;
68+
69+
//Console.WriteLine(expClaim);
70+
71+
using (Assert.EnterMultipleScope())
72+
{
73+
Assert.That(email, Is.EqualTo(TeacherEmail));
74+
Assert.That(role, Is.EqualTo("1"));
75+
Assert.That(jwt.ValidTo, Is.GreaterThan(DateTime.UtcNow.AddMinutes(50)));
76+
Assert.That(jwt.ValidTo, Is.LessThan(DateTime.UtcNow.AddHours(2)));
77+
}
78+
}
79+
80+
}
81+
}

exercise.wwwapi/Authorization/Handlers/UserExistsHandler.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ protected override async Task HandleRequirementAsync(
2727
//_logger.LogWarning("Available claims in token: {Claims}", string.Join(", ", claims));
2828

2929
// Get user ID from claims
30-
var userIdClaim = context.User.FindFirst(ClaimTypes.NameIdentifier)
31-
?? context.User.FindFirst(ClaimTypes.Sid);
30+
var userIdClaim = context.User.FindFirst(ClaimTypes.Sid)
31+
?? context.User.FindFirst(ClaimTypes.NameIdentifier);
3232

3333

3434
if (userIdClaim == null || !int.TryParse(userIdClaim.Value, out int userId))

exercise.wwwapi/DTOs/Login/LoginRequestDTO.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ public class LoginRequestDTO
77
{
88
public string? email { get; set; }
99
public string? password { get; set; }
10+
public bool? longlifetoken { get; set; }
1011
}
1112
}

exercise.wwwapi/Endpoints/UserEndpoints.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using System.IdentityModel.Tokens.Jwt;
1414
using System.Security.Claims;
1515
using System.Text;
16+
using System.Threading.Tasks;
1617

1718
namespace exercise.wwwapi.EndPoints
1819
{
@@ -27,8 +28,9 @@ public static void ConfigureAuthApi(this WebApplication app)
2728
users.MapGet("/", GetUsers).WithSummary("Get all users by first name if provided");
2829
users.MapGet("/{id:int}", GetUserById).WithSummary("Get user by user id");
2930
users.MapPatch("/{id:int}", UpdateUser).WithSummary("Update a user");
31+
3032
}
31-
33+
3234
/// <summary>
3335
/// Retrieves users, optionally filtered by a case-insensitive search on first name, last name, or full name.
3436
/// </summary>
@@ -51,11 +53,6 @@ public static void ConfigureAuthApi(this WebApplication app)
5153
private static async Task<IResult> GetUsers(IRepository<User> repository, ClaimsPrincipal claims, string? name)
5254
{
5355
int? id = claims.UserRealId();
54-
if (id == null)
55-
{
56-
return TypedResults.Ok(new ResponseDTO<object>()
57-
{ Message = "Invalid token" });
58-
}
5956

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

148-
string token = CreateToken(user, config);
145+
string token;
146+
if (request.longlifetoken.GetValueOrDefault()) token = CreateToken(user, config, 7);
147+
else token = CreateToken(user, config, 1.0 / 24);
148+
149149

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

163163
}
164-
164+
165165
[Authorize]
166166
[ProducesResponseType(StatusCodes.Status200OK)]
167167
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
@@ -269,7 +269,7 @@ private static async Task<IResult> UpdateUser(IRepository<User> repository, Clai
269269
}
270270

271271
// Helper, creates jwt tokens
272-
private static string CreateToken(User user, IConfigurationSettings config)
272+
private static string CreateToken(User user, IConfigurationSettings config, double days)
273273
{
274274
List<Claim> claims =
275275
[
@@ -284,7 +284,7 @@ private static string CreateToken(User user, IConfigurationSettings config)
284284
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha512Signature);
285285
var token = new JwtSecurityToken(
286286
claims: claims,
287-
expires: DateTime.Now.AddDays(1),
287+
expires: DateTime.UtcNow.AddDays(days),
288288
signingCredentials: credentials
289289
);
290290
var jwt = new JwtSecurityTokenHandler().WriteToken(token);

0 commit comments

Comments
 (0)