Closed dongfengweixiao closed 1 year ago
Hello, @dongfengweixiao yes this is a really important feature. I am going to add it.
Hello, @jonra1993 The above code may help you.
Hi @dongfengweixiao this feature has been implemented in this commit https://github.com/jonra1993/fastapi-alembic-sqlmodel-async/commit/c7c38d5d452a6dedeeec246544b3a30231d5136a Please check it.
Hi @dongfengweixiao this feature has been implemented in this commit c7c38d5 Please check it.
The password change function is available, but there is a problem: the old refresh token is still valid.
diff --git a/fastapi-alembic-sqlmodel-async/app/api/v1/endpoints/login.py b/fastapi-alembic-sqlmodel-async/app/api/v1/endpoints/login.py
index 0d929c1..50491aa 100644
--- a/fastapi-alembic-sqlmodel-async/app/api/v1/endpoints/login.py
+++ b/fastapi-alembic-sqlmodel-async/app/api/v1/endpoints/login.py
@@ -1,5 +1,5 @@
from datetime import timedelta
-from typing import Any
+from typing import Any, Optional
from fastapi import APIRouter, Body, Depends, HTTPException
from app.core.security import get_password_hash
from app.core.security import verify_password
@@ -27,12 +27,13 @@ class TokenType(str, Enum):
async def add_token_to_redis(
- redis_client: Redis, user: User, token: str, token_type: TokenType, expire_time: int
+ redis_client: Redis, user: User, token: str, token_type: TokenType, expire_time: Optional[int] = None
):
token_key = f"user:{user.id}:{token_type}"
print("token_key", token_key)
await redis_client.sadd(token_key, token)
- await redis_client.expire(token_key, expire_time)
+ if expire_time:
+ await redis_client.expire(token_key, expire_time)
print("done")
@@ -70,20 +71,24 @@ async def login(
refresh_token=refresh_token,
user=user,
)
- await add_token_to_redis(
- redis_client,
- user,
- access_token,
- TokenType.ACCESS,
- settings.ACCESS_TOKEN_EXPIRE_MINUTES,
- )
- await add_token_to_redis(
- redis_client,
- user,
- refresh_token,
- TokenType.REFRESH,
- settings.REFRESH_TOKEN_EXPIRE_MINUTES,
- )
+ access_token_key = f"user:{user.id}:{TokenType.ACCESS}"
+ valid_access_tokens = await redis_client.smembers(access_token_key)
+ if valid_access_tokens:
+ await add_token_to_redis(
+ redis_client,
+ user,
+ access_token,
+ TokenType.ACCESS,
+ )
+ refresh_token_key = f"user:{user.id}:{TokenType.REFRESH}"
+ valid_refresh_tokens = await redis_client.smembers(refresh_token_key)
+ if valid_refresh_tokens:
+ await add_token_to_redis(
+ redis_client,
+ user,
+ refresh_token,
+ TokenType.REFRESH,
+ )
return create_response(meta=meta_data, data=data, message="Login correctly")
@@ -159,6 +164,12 @@ async def get_refresh_token(
except (jwt.JWTError, ValidationError):
raise HTTPException(status_code=403, detail="Refresh token invalid")
+ user_id = payload["sub"]
+ refresh_token_key = f"user:{user_id}:{TokenType.REFRESH}"
+ valid_refresh_tokens = await redis_client.smembers(refresh_token_key)
+ if valid_refresh_tokens and body.refresh_token not in valid_refresh_tokens:
+ raise HTTPException(status_code=403, detail="Refresh token invalid")
+
if payload["type"] == "refresh":
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
user = await crud.user.get(id=payload["sub"])
Sure you are right @dongfengweixiao this commit validates refresh token https://github.com/jonra1993/fastapi-alembic-sqlmodel-async/commit/33862a10a87fb5901c3d13f9edd6cbb1a89a33da https://github.com/jonra1993/fastapi-alembic-sqlmodel-async/commit/2c9ac336cd2cd4e8b784ba600440f1294019de48
Sure you are right @dongfengweixiao this commit validates refresh token https://github.com/jonra1993/fastapi-alembic-sqlmodel-async/commit/33862a10a87fb5901c3d13f9edd6cbb1a89a33da https://github.com/jonra1993/fastapi-alembic-sqlmodel-async/commit/2c9ac336cd2cd4e8b784ba600440f1294019de48
Fantastic job.
It is unnecessary to record the token in Redis for normal login action. When the password is changed, the new token will be record to Redis. There are two cases when other clients log in.
Case 1: There are records in Redis. At this time, when other clients log in, they need to check whether there is a token in Redis. If yes, they need to put token to Redis together. Case 2: There are no records in Redis. At this time, all the existing tokens must have expired. When you log in again, you do not need to save new tokens to Redis.
Hello @dongfengweixiao I caught your logic now. Please check this commit https://github.com/jonra1993/fastapi-alembic-sqlmodel-async/commit/b9ff90cbf06d4f75849ee454b28de28273957351 and let me know what do you think
Hello @dongfengweixiao I caught your logic now. Please check this commit b9ff90c and let me know what do you think
logic is right, but in login function, you call redis three times: read token from redis(get_valid_tokens) -> read token from redis again(add_token_to_redis) -> write token to redis.