dramaling-vocab-learning/backend/DramaLing.Api.Tests/Integration/Fixtures/JwtTestHelper.cs

167 lines
5.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace DramaLing.Api.Tests.Integration.Fixtures;
/// <summary>
/// JWT 測試助手類別
/// 提供測試用的 JWT Token 生成功能
/// </summary>
public static class JwtTestHelper
{
private const string TestSecretKey = "test-secret-minimum-32-characters-long-for-jwt-signing-in-test-mode-only";
private const string TestIssuer = "https://test.supabase.co";
private const string TestAudience = "authenticated";
/// <summary>
/// 為指定使用者生成測試用 JWT Token
/// </summary>
public static string GenerateJwtToken(Guid userId, string? email = null, string? username = null)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.UTF8.GetBytes(TestSecretKey);
var claims = new List<Claim>
{
new("sub", userId.ToString()),
new("aud", TestAudience),
new("iss", TestIssuer),
new("iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64),
new("exp", DateTimeOffset.UtcNow.AddHours(1).ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)
};
// 添加可選的 claims
if (!string.IsNullOrEmpty(email))
claims.Add(new Claim("email", email));
if (!string.IsNullOrEmpty(username))
claims.Add(new Claim("preferred_username", username));
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = DateTime.UtcNow.AddHours(1),
Issuer = TestIssuer,
Audience = TestAudience,
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(key),
SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
/// <summary>
/// 為 TestUser1 生成 JWT Token
/// </summary>
public static string GenerateTestUser1Token()
{
return GenerateJwtToken(
TestDataSeeder.TestUser1Id,
"test1@example.com",
"testuser1"
);
}
/// <summary>
/// 為 TestUser2 生成 JWT Token
/// </summary>
public static string GenerateTestUser2Token()
{
return GenerateJwtToken(
TestDataSeeder.TestUser2Id,
"test2@example.com",
"testuser2"
);
}
/// <summary>
/// 生成已過期的 JWT Token (用於測試無效 token)
/// </summary>
public static string GenerateExpiredJwtToken(Guid userId)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.UTF8.GetBytes(TestSecretKey);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim("sub", userId.ToString()),
new Claim("aud", TestAudience)
}),
Expires = DateTime.UtcNow.AddHours(-1), // 1 小時前過期
IssuedAt = DateTime.UtcNow.AddHours(-2), // 2 小時前簽發
// 不設置 NotBefore讓它使用預設值
Issuer = TestIssuer,
Audience = TestAudience,
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(key),
SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
/// <summary>
/// 生成無效簽章的 JWT Token (用於測試無效 token)
/// </summary>
public static string GenerateInvalidSignatureToken(Guid userId)
{
var tokenHandler = new JwtSecurityTokenHandler();
var wrongKey = Encoding.UTF8.GetBytes("wrong-secret-key-for-invalid-signature-test-purposes-only");
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim("sub", userId.ToString()),
new Claim("aud", TestAudience)
}),
Expires = DateTime.UtcNow.AddHours(1),
Issuer = TestIssuer,
Audience = TestAudience,
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(wrongKey), // 使用錯誤的 key
SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
/// <summary>
/// 驗證 JWT Token 是否有效 (用於測試驗證)
/// </summary>
public static ClaimsPrincipal? ValidateToken(string token)
{
try
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.UTF8.GetBytes(TestSecretKey);
var validationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = true,
ValidIssuer = TestIssuer,
ValidateAudience = true,
ValidAudience = TestAudience,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
var principal = tokenHandler.ValidateToken(token, validationParameters, out _);
return principal;
}
catch
{
return null;
}
}
}