Skip to content

Commit 4119c46

Browse files
committed
fix: #431 use a dedicated secret for jwt
1 parent d16e1f5 commit 4119c46

5 files changed

Lines changed: 46 additions & 13 deletions

File tree

django_email_learning/apps.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,42 @@ def check_site_base_url_config(app_configs, **kwargs): # type: ignore[no-untype
99
errors = []
1010
from django.conf import settings
1111

12-
if (
13-
not hasattr(settings, "DJANGO_EMAIL_LEARNING")
14-
or "SITE_BASE_URL" not in settings.DJANGO_EMAIL_LEARNING
12+
if not hasattr(settings, "DJANGO_EMAIL_LEARNING") or not isinstance(
13+
settings.DJANGO_EMAIL_LEARNING, dict
1514
):
15+
errors.append(
16+
checks.Error(
17+
"DJANGO_EMAIL_LEARNING is not set in settings or is not a dictionary.",
18+
hint="Please set DJANGO_EMAIL_LEARNING to a dictionary containing the required configurations.",
19+
id="django_email_learning.E000",
20+
)
21+
)
22+
return errors
23+
24+
if "SITE_BASE_URL" not in settings.DJANGO_EMAIL_LEARNING:
1625
errors.append(
1726
checks.Error(
1827
"DJANGO_EMAIL_LEARNING['SITE_BASE_URL'] is not set in settings.",
1928
hint="Please set DJANGO_EMAIL_LEARNING['SITE_BASE_URL'] to the base URL of your site.",
2029
id="django_email_learning.E001",
2130
)
2231
)
23-
if (
24-
not hasattr(settings, "DJANGO_EMAIL_LEARNING")
25-
or "ENCRYPTION_SECRET_KEY" not in settings.DJANGO_EMAIL_LEARNING
26-
):
32+
if "ENCRYPTION_SECRET_KEY" not in settings.DJANGO_EMAIL_LEARNING:
2733
errors.append(
2834
checks.Error(
2935
"DJANGO_EMAIL_LEARNING['ENCRYPTION_SECRET_KEY'] is not set in settings.",
3036
hint="Please set DJANGO_EMAIL_LEARNING['ENCRYPTION_SECRET_KEY'] to a long, random string.",
3137
id="django_email_learning.E002",
3238
)
3339
)
40+
if "JWT_SECRET_KEY" not in settings.DJANGO_EMAIL_LEARNING:
41+
errors.append(
42+
checks.Error(
43+
"DJANGO_EMAIL_LEARNING['JWT_SECRET_KEY'] is not set in settings.",
44+
hint="Please set DJANGO_EMAIL_LEARNING['JWT_SECRET_KEY'] to a long, random string.",
45+
id="django_email_learning.E003",
46+
)
47+
)
3448
return errors
3549

3650

django_email_learning/services/jwt_service.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
import datetime
44
import jwt
55

6-
SECRET = settings.SECRET_KEY
6+
7+
SECRET = settings.DJANGO_EMAIL_LEARNING["JWT_SECRET_KEY"]
78
ALGORITHM = "HS256"
89

910

@@ -24,13 +25,13 @@ def generate_jwt(
2425
if not exp:
2526
exp = timezone.now() + datetime.timedelta(seconds=expiration_seconds)
2627
payload_copy["exp"] = exp
27-
token = jwt.encode(payload_copy, SECRET, algorithm=ALGORITHM)
28+
token = jwt.encode(payload_copy, SECRET, algorithm=ALGORITHM) # type: ignore[arg-type]
2829
return token
2930

3031

3132
def decode_jwt(token: str) -> dict:
3233
try:
33-
decoded = jwt.decode(token, SECRET, algorithms=[ALGORITHM])
34+
decoded = jwt.decode(token, SECRET, algorithms=[ALGORITHM]) # type: ignore[arg-type]
3435
return decoded
3536
except (jwt.InvalidSignatureError, jwt.DecodeError, jwt.InvalidAlgorithmError):
3637
raise InvalidTokenException("The signature is invalid")

django_service/settings.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@
105105

106106
DJANGO_EMAIL_LEARNING = {
107107
"SITE_BASE_URL": "http://localhost:8000",
108-
"ENCRYPTION_SECRET_KEY": "your-very-secure-and-random-key",
108+
"JWT_SECRET_KEY": "your-very-asdasdasdasdaasdawqA2eSDFaasecret-keym0824+s26vkron%(@whp&wyaw=26",
109+
"ENCRYPTION_SECRET_KEY": "your-very-asdasdasdasecure-and-random-jwt-secret-keym0824+s26vkron%(@whp&wyaw=26",
109110
"FROM_EMAIL": os.environ.get("FROM_EMAIL", "webmaster@localhost"),
110111
"TERMS_OF_SERVICE_URL": "https://www.example.com/terms",
111112
"AI": {

docs/source/installation.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,21 @@ Same as all other sensitive configurations, it's a good practice to load this fr
115115
Changing this key after data has been created will prevent access to previously encrypted data. Chaning requires re-encrypting all existing data with the new key.
116116

117117

118+
**JWT_SECRET_KEY**
119+
120+
A dedicated secret key used for signing and verifying JSON Web Tokens (JWTs). It should be a long, random string, independent of Django's ``SECRET_KEY`` and ``ENCRYPTION_SECRET_KEY``.
121+
122+
Using a separate key ensures that a JWT secret compromise does not affect other parts of your application.
123+
124+
Same as all other sensitive configurations, it's a good practice to load this from an environment variable or a secure vault.
125+
126+
118127
.. code-block:: python
119128
120129
DJANGO_EMAIL_LEARNING = {
121130
'SITE_BASE_URL': 'https://yourdomain.com',
122131
'ENCRYPTION_SECRET_KEY': 'your-very-long-random-string',
132+
'JWT_SECRET_KEY': 'another-very-long-random-string',
123133
}
124134
125135
@@ -135,6 +145,7 @@ The default email address for outgoing course emails. If not specified, falls ba
135145
DJANGO_EMAIL_LEARNING = {
136146
'SITE_BASE_URL': 'https://yourdomain.com',
137147
'ENCRYPTION_SECRET_KEY': 'your-very-long-random-string',
148+
'JWT_SECRET_KEY': 'another-very-long-random-string',
138149
'FROM_EMAIL': 'courses@yourdomain.com',
139150
}
140151
@@ -147,6 +158,7 @@ Optional link to your terms of service. When provided, this link is displayed in
147158
DJANGO_EMAIL_LEARNING = {
148159
'SITE_BASE_URL': 'https://yourdomain.com',
149160
'ENCRYPTION_SECRET_KEY': 'your-very-long-random-string',
161+
'JWT_SECRET_KEY': 'another-very-long-random-string',
150162
'TERMS_OF_SERVICE_URL': 'https://yourdomain.com/terms',
151163
}
152164
@@ -165,6 +177,7 @@ All values are boolean.
165177
DJANGO_EMAIL_LEARNING = {
166178
'SITE_BASE_URL': 'https://yourdomain.com',
167179
'ENCRYPTION_SECRET_KEY': 'your-very-long-random-string',
180+
'JWT_SECRET_KEY': 'another-very-long-random-string',
168181
'QUIZ_DEFAULTS': {
169182
'LIMITED_ATTEMPTS': True,
170183
'IS_BLOCKING': True,
@@ -185,6 +198,7 @@ Optional configuration for injecting a custom component in the platform sidebar.
185198
DJANGO_EMAIL_LEARNING = {
186199
'SITE_BASE_URL': 'https://yourdomain.com',
187200
'ENCRYPTION_SECRET_KEY': 'your-very-long-random-string',
201+
'JWT_SECRET_KEY': 'another-very-long-random-string',
188202
'SIDEBAR': {
189203
'CUSTOM_COMPONENT': {
190204
'SCRIPT_URL': 'url/path-to-your-component.js',
@@ -209,6 +223,7 @@ Optional configuration for branding assets in the platform header.
209223
DJANGO_EMAIL_LEARNING = {
210224
'SITE_BASE_URL': 'https://yourdomain.com',
211225
'ENCRYPTION_SECRET_KEY': 'your-very-long-random-string',
226+
'JWT_SECRET_KEY': 'another-very-long-random-string',
212227
'LOGO': {
213228
'HORIZONTAL_LOCKUP': {
214229
'LIGHT_BACKGROUND': 'url/path-to-horizontal-logo-for-light-background.png',
@@ -232,6 +247,7 @@ If not specified, a default location will be used.
232247
DJANGO_EMAIL_LEARNING = {
233248
'SITE_BASE_URL': 'https://yourdomain.com',
234249
'ENCRYPTION_SECRET_KEY': 'your-very-long-random-string',
250+
'JWT_SECRET_KEY': 'another-very-long-random-string',
235251
'PRIVATE_FILE_STORAGE_LOCATION': '/path/to/private/storage/',
236252
}
237253
@@ -275,6 +291,7 @@ Currently supported built-in models are:
275291
DJANGO_EMAIL_LEARNING = {
276292
'SITE_BASE_URL': 'https://yourdomain.com',
277293
'ENCRYPTION_SECRET_KEY': 'your-very-long-random-string',
294+
'JWT_SECRET_KEY': 'another-very-long-random-string',
278295
'AI': {
279296
'OPENAI_API_KEY': os.environ.get('OPENAI_API_KEY'),
280297
'TEXT_EDITING_MODEL': LanguageModel.GPT_4O_MINI.model_name,

tests/test_models/test_delivery_schedule.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def test_generate_link_assignment_with_zero_deadline_uses_datetime_max_exp(
5858
token = parse_qs(urlparse(link).query)["token"][0]
5959
decoded_token = jwt.decode(
6060
token,
61-
settings.SECRET_KEY,
61+
settings.DJANGO_EMAIL_LEARNING["JWT_SECRET_KEY"],
6262
algorithms=["HS256"],
6363
options={"verify_exp": False},
6464
)
@@ -69,7 +69,7 @@ def test_generate_link_assignment_with_zero_deadline_uses_datetime_max_exp(
6969
)
7070
expected_decoded = jwt.decode(
7171
expected_token,
72-
settings.SECRET_KEY,
72+
settings.DJANGO_EMAIL_LEARNING["JWT_SECRET_KEY"],
7373
algorithms=["HS256"],
7474
options={"verify_exp": False},
7575
)

0 commit comments

Comments
 (0)