Skip to content

Commit 7a3730a

Browse files
authored
Merge pull request #282 from nbhansen/fix/ci-jwt-secret
fix: Provide default JWT secret for CI tests on fork PRs may the gods be with you all
2 parents 9195a61 + a816321 commit 7a3730a

File tree

3 files changed

+34
-34
lines changed

3 files changed

+34
-34
lines changed

Backend/VTA.API/Controllers/UsersController.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -422,9 +422,13 @@ private bool UsernameExists(string username)
422422
/// <exception cref="InvalidOperationException"></exception>
423423
private string GenerateJwt(User user)
424424
{
425-
var secretKey = config.GetValue<string>("Secret:SecretKey")
426-
?? Environment.GetEnvironmentVariable("JWT_SECRET") //Someone added this, why, i do not know, cause the key is stored in the appsettings.json not env variables
427-
?? throw new InvalidOperationException("A JWT secret is required for token generation."); //Throw if no secret is found
425+
// Get secret from configuration first, fall back to environment variable
426+
// Use helper variables to properly handle empty strings (not just null)
427+
var configSecret = config.GetValue<string>("Secret:SecretKey");
428+
var envSecret = Environment.GetEnvironmentVariable("JWT_SECRET");
429+
var secretKey = !string.IsNullOrWhiteSpace(configSecret) ? configSecret
430+
: !string.IsNullOrWhiteSpace(envSecret) ? envSecret
431+
: throw new InvalidOperationException("A JWT secret is required for token generation.");
428432
var validIssuer = "api.vta.com";
429433
var validAudience = "user.vta.com";
430434

Backend/VTA.API/Program.cs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,16 @@
4242
}
4343
);
4444

45-
var config = new ConfigurationBuilder()
46-
.SetBasePath(Directory.GetCurrentDirectory())
47-
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
48-
.AddEnvironmentVariables()
49-
.Build();
50-
51-
var jwtSecretKey = Environment.GetEnvironmentVariable("JWT_SECRET") //I still do not know why this was added, we are reading the Secretkey from appsettings, not the OS env variables
52-
?? config["Secret:SecretKey"];//load our secret
53-
54-
if (string.IsNullOrEmpty(jwtSecretKey))
45+
// Get JWT secret from environment variable (for production/CI) or configuration (for development)
46+
// Use a helper to treat empty/whitespace strings the same as null
47+
var envSecret = Environment.GetEnvironmentVariable("JWT_SECRET");
48+
var jwtSecretKey = !string.IsNullOrWhiteSpace(envSecret)
49+
? envSecret
50+
: builder.Configuration["Secret:SecretKey"];
51+
52+
if (string.IsNullOrWhiteSpace(jwtSecretKey))
5553
{
56-
throw new ArgumentNullException("JWT_SECRET_KEY environment variable or SecretKey in appsettings.json is required.");
54+
throw new ArgumentNullException("JWT_SECRET environment variable or Secret:SecretKey in configuration is required.");
5755
}
5856
/*Configure Json Web Tokens*/
5957
var jwtIssuer = "api.vta.com";

Backend/VTA.Tests/TestHelpers/CustomApplicationFactory.cs

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,18 @@ public class CustomApplicationFactory : WebApplicationFactory<Program>, IAsyncLi
1414
{
1515
private readonly MySqlContainer _mySqlContainer;
1616

17+
// Static constructor runs once per AppDomain, before any instance is created.
18+
// This ensures JWT_SECRET is set before WebApplicationFactory starts any host.
19+
static CustomApplicationFactory()
20+
{
21+
// GitHub Actions doesn't expose secrets to fork PRs for security reasons.
22+
// The workflow sets JWT_SECRET from secrets, but it resolves to empty string.
23+
if (string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("JWT_SECRET")))
24+
{
25+
Environment.SetEnvironmentVariable("JWT_SECRET", "test-jwt-secret-for-ci-must-be-at-least-32-chars");
26+
}
27+
}
28+
1729
public CustomApplicationFactory()
1830
{
1931
var config = new ConfigurationBuilder()
@@ -27,19 +39,6 @@ public CustomApplicationFactory()
2739
?? Environment.GetEnvironmentVariable("TEST_CONNECTION_STRING")
2840
?? "server=localhost;port=3306;user=vta_user;password=vta_password;database=vta_test";
2941

30-
var jwtSecretConfig = new JwtSecretConfig
31-
{
32-
SecretKey = config.GetValue<string>("Secret:SecretKey")
33-
?? Environment.GetEnvironmentVariable("JWT_SECRET_KEY"),
34-
ValidIssuer = config.GetValue<string>("Secret:ValidIssuer") ?? "api.vta.com",
35-
ValidAudience = config.GetValue<string>("Secret:ValidAudience") ?? "user.vta.com"
36-
};
37-
38-
if (string.IsNullOrEmpty(jwtSecretConfig.SecretKey))
39-
{
40-
throw new ArgumentNullException("A JWT secret is required for token generation.");
41-
}
42-
4342
var builder = new MySqlConnectionStringBuilder(connectionString);
4443
var database = string.IsNullOrEmpty(builder.Database) ? "vta_test" : builder.Database;
4544
var username = string.IsNullOrEmpty(builder.UserID) ? "root" : builder.UserID;
@@ -97,7 +96,12 @@ protected override void ConfigureWebHost(IWebHostBuilder builder)
9796
config
9897
.AddJsonFile("/var/www/VTA.API/appsettings.json", optional: true)
9998
.AddJsonFile("appsettings.json", optional: true)
100-
.AddEnvironmentVariables();
99+
.AddEnvironmentVariables()
100+
.AddInMemoryCollection(new Dictionary<string, string?>
101+
{
102+
["Secret:SecretKey"] = Environment.GetEnvironmentVariable("JWT_SECRET_KEY")
103+
?? "test-jwt-secret-for-ci-must-be-at-least-32-chars"
104+
});
101105
});
102106

103107
builder.ConfigureLogging(logging =>
@@ -108,11 +112,5 @@ protected override void ConfigureWebHost(IWebHostBuilder builder)
108112
});
109113
}
110114

111-
private class JwtSecretConfig
112-
{
113-
public string? SecretKey { get; set; }
114-
public string? ValidIssuer { get; set; }
115-
public string? ValidAudience { get; set; }
116-
}
117115
}
118116
}

0 commit comments

Comments
 (0)