|
10 | 10 |
|
11 | 11 |
|
12 | 12 | class JWTService: |
| 13 | + ACCESS_TOKEN_TYPE = "access" |
| 14 | + REFRESH_TOKEN_TYPE = "refresh" |
| 15 | + |
13 | 16 | @staticmethod |
14 | | - def create_access_token(data: dict) -> str: |
| 17 | + def _create_token(data: dict, token_type: str, expires_delta: timedelta) -> str: |
| 18 | + now = datetime.now(timezone.utc) |
15 | 19 | to_encode = data.copy() |
16 | | - expire = datetime.now(timezone.utc) + timedelta( |
17 | | - minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES |
| 20 | + to_encode.update( |
| 21 | + { |
| 22 | + "iss": settings.JWT_ISSUER, |
| 23 | + "sub": str(to_encode["sub"]), |
| 24 | + "aud": settings.JWT_AUDIENCE, |
| 25 | + "exp": now + expires_delta, |
| 26 | + "nbf": now, |
| 27 | + "iat": now, |
| 28 | + "jti": str(uuid4()), |
| 29 | + "token_type": token_type, |
| 30 | + } |
18 | 31 | ) |
19 | | - to_encode.update({"exp": expire, "jti": str(uuid4())}) |
20 | 32 | return jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM) |
21 | 33 |
|
| 34 | + @staticmethod |
| 35 | + def create_access_token(data: dict) -> str: |
| 36 | + return JWTService._create_token( |
| 37 | + data=data, |
| 38 | + token_type=JWTService.ACCESS_TOKEN_TYPE, |
| 39 | + expires_delta=timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES), |
| 40 | + ) |
| 41 | + |
22 | 42 | @staticmethod |
23 | 43 | def create_refresh_token(data: dict) -> str: |
24 | | - to_encode = data.copy() |
25 | | - expire = datetime.now(timezone.utc) + timedelta( |
26 | | - minutes=settings.REFRESH_TOKEN_EXPIRE_MINUTES, |
| 44 | + return JWTService._create_token( |
| 45 | + data=data, |
| 46 | + token_type=JWTService.REFRESH_TOKEN_TYPE, |
| 47 | + expires_delta=timedelta(minutes=settings.REFRESH_TOKEN_EXPIRE_MINUTES), |
27 | 48 | ) |
28 | | - to_encode.update({"exp": expire}) |
29 | | - return jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM) |
30 | 49 |
|
31 | 50 | @staticmethod |
32 | 51 | def decode_token(token: str) -> dict: |
33 | 52 | try: |
34 | 53 | return jwt.decode( |
35 | | - token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM] |
| 54 | + token, |
| 55 | + settings.SECRET_KEY, |
| 56 | + algorithms=[settings.ALGORITHM], |
| 57 | + issuer=settings.JWT_ISSUER, |
| 58 | + audience=settings.JWT_AUDIENCE, |
36 | 59 | ) |
37 | 60 | except JWTError: |
38 | 61 | raise InvalidCredentialsError("Invalid or expired token") |
| 62 | + |
| 63 | + @staticmethod |
| 64 | + def require_token_type(payload: dict, expected_token_type: str) -> None: |
| 65 | + if payload.get("token_type") != expected_token_type: |
| 66 | + raise InvalidCredentialsError(f"{expected_token_type.title()} token required") |
0 commit comments