Skip to content

Commit ef81592

Browse files
Revoke refresh token on password change (#708)
### Description Currently, is someone account is compromised, the user can not log out all active sessions. Due to the use of a JWT we can not revoke active access token, but as they have a limited duration they will automatically expire after some time. We should revoke refresh token to ensure that clients won't be able to get a new access token. As a consequence the user changing its password in settings will be logged out of the app, but not immediately, only after the expiration of its refresh token
1 parent 72e0b83 commit ef81592

File tree

2 files changed

+35
-2
lines changed

2 files changed

+35
-2
lines changed

app/core/auth/cruds_auth.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ async def revoke_refresh_token_by_client_and_user_id(
102102
db: AsyncSession,
103103
client_id: str,
104104
user_id: str,
105-
) -> models_auth.RefreshToken | None:
105+
) -> None:
106106
"""Revoke a refresh token from database"""
107107

108108
await db.execute(
@@ -115,4 +115,20 @@ async def revoke_refresh_token_by_client_and_user_id(
115115
.values(revoked_on=datetime.now(UTC)),
116116
)
117117
await db.commit()
118-
return None
118+
119+
120+
async def revoke_refresh_token_by_user_id(
121+
db: AsyncSession,
122+
user_id: str,
123+
) -> None:
124+
"""Revoke a refresh token from database"""
125+
126+
await db.execute(
127+
update(models_auth.RefreshToken)
128+
.where(
129+
models_auth.RefreshToken.user_id == user_id,
130+
models_auth.RefreshToken.revoked_on.is_(None),
131+
)
132+
.values(revoked_on=datetime.now(UTC)),
133+
)
134+
await db.commit()

app/core/users/endpoints_users.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from sqlalchemy.exc import IntegrityError
2121
from sqlalchemy.ext.asyncio import AsyncSession
2222

23+
from app.core.auth import cruds_auth
2324
from app.core.groups import cruds_groups, models_groups
2425
from app.core.groups.groups_type import AccountType, GroupType
2526
from app.core.schools.schools_type import SchoolType
@@ -576,6 +577,14 @@ async def reset_password(
576577
email=recover_request.email,
577578
)
578579

580+
# Revoke existing auth refresh tokens
581+
# to force the user to reauthenticate on all services and devices
582+
# when their token expire
583+
await cruds_auth.revoke_refresh_token_by_user_id(
584+
db=db,
585+
user_id=recover_request.user_id,
586+
)
587+
579588
return standard_responses.Result()
580589

581590

@@ -753,6 +762,14 @@ async def change_password(
753762
new_password_hash=new_password_hash,
754763
)
755764

765+
# Revoke existing auth refresh tokens
766+
# to force the user to reauthenticate on all services and devices
767+
# when their token expire
768+
await cruds_auth.revoke_refresh_token_by_user_id(
769+
db=db,
770+
user_id=user.id,
771+
)
772+
756773
return standard_responses.Result()
757774

758775

0 commit comments

Comments
 (0)