Skip to content

Commit 29c8a20

Browse files
author
Max Azatian
committed
JWT_SECRET_KEY fix: added validation, + updated .env + added tests
1 parent 8617439 commit 29c8a20

File tree

3 files changed

+68
-3
lines changed

3 files changed

+68
-3
lines changed

backend/.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
PROJECT_NAME=integr8scode
2-
SECRET_KEY=your_secret_key_here
2+
JWT_SECRET_KEY=your_secret_key_here
33
ALGORITHM=HS256
44
ACCESS_TOKEN_EXPIRE_MINUTES=30
55
MONGODB_URL=mongodb://mongo:27017/integr8scode

backend/app/config.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
from app.runtime_registry import EXAMPLE_SCRIPTS as EXEC_EXAMPLE_SCRIPTS
44
from app.runtime_registry import SUPPORTED_RUNTIMES as RUNTIME_MATRIX
5-
from pydantic import Field
5+
from pydantic import Field, field_validator
66
from pydantic_settings import BaseSettings
77

88

99
class Settings(BaseSettings):
1010
PROJECT_NAME: str = "integr8scode"
1111
API_V1_STR: str = "/api/v1"
12-
SECRET_KEY: str = "default_secret_key"
12+
SECRET_KEY: str = Field(default=None, env="JWT_SECRET_KEY")
1313
ALGORITHM: str = "HS256"
1414
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
1515
MONGODB_URL: str = "mongodb://mongo:27017/integr8scode"
@@ -43,6 +43,17 @@ class Settings(BaseSettings):
4343

4444
TESTING: bool = False
4545

46+
@field_validator("SECRET_KEY")
47+
@classmethod
48+
def validate_secret_key(cls, v: Optional[str]) -> str:
49+
if not v:
50+
raise ValueError("JWT_SECRET_KEY environment variable must be set")
51+
if len(v) < 32:
52+
raise ValueError("JWT_SECRET_KEY must be at least 32 characters long")
53+
if v == "your_secret_key_here" or v == "default_secret_key":
54+
raise ValueError("JWT_SECRET_KEY must not use default placeholder values")
55+
return v
56+
4657
class Config:
4758
env_file = ".env"
4859

backend/tests/unit/test_config.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import os
2+
from unittest import mock
3+
4+
import pytest
5+
from pydantic import ValidationError
6+
7+
from app.config import Settings
8+
9+
10+
class TestSettingsValidation:
11+
def test_secret_key_missing(self):
12+
with mock.patch.dict(os.environ, {}, clear=True):
13+
with pytest.raises(ValidationError) as exc_info:
14+
Settings()
15+
16+
errors = exc_info.value.errors()
17+
assert len(errors) == 1
18+
assert errors[0]["loc"] == ("SECRET_KEY",)
19+
assert "JWT_SECRET_KEY environment variable must be set" in errors[0]["msg"]
20+
21+
def test_secret_key_too_short(self):
22+
with mock.patch.dict(os.environ, {"JWT_SECRET_KEY": "short_key"}, clear=True):
23+
with pytest.raises(ValidationError) as exc_info:
24+
Settings()
25+
26+
errors = exc_info.value.errors()
27+
assert len(errors) == 1
28+
assert errors[0]["loc"] == ("SECRET_KEY",)
29+
assert "JWT_SECRET_KEY must be at least 32 characters long" in errors[0]["msg"]
30+
31+
def test_secret_key_default_placeholder(self):
32+
test_cases = ["your_secret_key_here", "default_secret_key"]
33+
34+
for placeholder in test_cases:
35+
with mock.patch.dict(os.environ, {"JWT_SECRET_KEY": placeholder}, clear=True):
36+
with pytest.raises(ValidationError) as exc_info:
37+
Settings()
38+
39+
errors = exc_info.value.errors()
40+
assert len(errors) == 1
41+
assert errors[0]["loc"] == ("SECRET_KEY",)
42+
assert "JWT_SECRET_KEY must not use default placeholder values" in errors[0]["msg"]
43+
44+
def test_secret_key_valid(self):
45+
valid_key = "a" * 32 # 32 character key
46+
with mock.patch.dict(os.environ, {"JWT_SECRET_KEY": valid_key}, clear=True):
47+
settings = Settings()
48+
assert settings.SECRET_KEY == valid_key
49+
50+
def test_secret_key_longer_than_minimum(self):
51+
long_key = "a" * 64 # 64 character key
52+
with mock.patch.dict(os.environ, {"JWT_SECRET_KEY": long_key}, clear=True):
53+
settings = Settings()
54+
assert settings.SECRET_KEY == long_key

0 commit comments

Comments
 (0)