Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions api/app/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,9 @@ def get_password_hash(password: str):

def create_access_token(data: dict, expires_delta: timedelta | None = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.now(timezone.utc) + expires_delta
else:
expire = datetime.now(timezone.utc) + timedelta(minutes=15)
# A login lasts ACCESS_TOKEN_EXPIRE_MINUTES. The default was 15 minutes, which /login used, so the browser was
# logged out a quarter of an hour after every sign-in (api.ts drops the token on the first 401).
expire = datetime.now(timezone.utc) + (expires_delta or timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES))
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
Expand Down
23 changes: 23 additions & 0 deletions api/tests/test_auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""Signing in: how long the token the browser keeps lasts."""
from datetime import datetime, timezone

from fastapi.testclient import TestClient
from jose import jwt

from app import auth, main, tracker


def test_a_login_lasts_a_week_not_a_quarter_of_an_hour(db):
tracker.create_user("ada@example.com", auth.get_password_hash("correct horse"))
client = TestClient(main.app)
token = client.post("/login", data={"username": "ada@example.com", "password": "correct horse"}).json()["access_token"]
expires = jwt.decode(token, auth.SECRET_KEY, algorithms=[auth.ALGORITHM])["exp"]
days = (datetime.fromtimestamp(expires, timezone.utc) - datetime.now(timezone.utc)).total_seconds() / 86400
assert 6.99 < days <= 7 # not the 15-minute default that logged people out
assert client.get("/persona", headers={"Authorization": f"Bearer {token}"}).status_code == 200


def test_a_wrong_password_gets_no_token(db):
tracker.create_user("ada@example.com", auth.get_password_hash("correct horse"))
reply = TestClient(main.app).post("/login", data={"username": "ada@example.com", "password": "wrong"})
assert reply.status_code == 401 and "access_token" not in reply.json()
Loading