API - blacklist jwt on user logout
This commit is contained in:
@ -33,7 +33,7 @@ from fittrackee.responses import (
|
||||
from fittrackee.utils import get_readable_duration
|
||||
from fittrackee.workouts.models import Sport
|
||||
|
||||
from .models import User, UserSportPreference
|
||||
from .models import BlacklistedToken, User, UserSportPreference
|
||||
from .utils.controls import check_password, is_valid_email, register_controls
|
||||
from .utils.token import decode_user_token
|
||||
|
||||
@ -1536,3 +1536,70 @@ def resend_account_confirmation_email() -> Union[Dict, HttpResponse]:
|
||||
return response
|
||||
except (exc.OperationalError, ValueError) as e:
|
||||
return handle_error_and_return_response(e, db=db)
|
||||
|
||||
|
||||
@auth_blueprint.route('/auth/logout', methods=['POST'])
|
||||
@require_auth()
|
||||
def logout_user(auth_user: User) -> Union[Tuple[Dict, int], HttpResponse]:
|
||||
"""
|
||||
User logout.
|
||||
If a valid token is provided, it will be blacklisted.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /api/auth/logout HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
**Example responses**:
|
||||
|
||||
- successful logout
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"message": "successfully logged out",
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
- error on logout
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 401 UNAUTHORIZED
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"message": "provide a valid auth token",
|
||||
"status": "error"
|
||||
}
|
||||
|
||||
:reqheader Authorization: OAuth 2.0 Bearer Token
|
||||
|
||||
:statuscode 200: successfully logged out
|
||||
:statuscode 401:
|
||||
- provide a valid auth token
|
||||
- The access token provided is expired, revoked, malformed, or invalid
|
||||
for other reasons.
|
||||
:statuscode 500:
|
||||
- error on token blacklist
|
||||
|
||||
"""
|
||||
auth_token = request.headers.get('Authorization', '').split(' ')[1]
|
||||
try:
|
||||
db.session.add(BlacklistedToken(token=auth_token))
|
||||
db.session.commit()
|
||||
except Exception:
|
||||
return {
|
||||
'status': 'error',
|
||||
'message': 'error on token blacklist',
|
||||
}, 500
|
||||
|
||||
return {
|
||||
'status': 'success',
|
||||
'message': 'successfully logged out',
|
||||
}, 200
|
||||
|
@ -97,7 +97,11 @@ class User(BaseModel):
|
||||
:return: integer|string
|
||||
"""
|
||||
try:
|
||||
return decode_user_token(auth_token)
|
||||
resp = decode_user_token(auth_token)
|
||||
is_blacklisted = BlacklistedToken.check(auth_token)
|
||||
if is_blacklisted:
|
||||
return 'blacklisted token, please log in again'
|
||||
return resp
|
||||
except jwt.ExpiredSignatureError:
|
||||
return 'signature expired, please log in again'
|
||||
except jwt.InvalidTokenError:
|
||||
@ -233,3 +237,19 @@ class UserSportPreference(BaseModel):
|
||||
'is_active': self.is_active,
|
||||
'stopped_speed_threshold': self.stopped_speed_threshold,
|
||||
}
|
||||
|
||||
|
||||
class BlacklistedToken(BaseModel):
|
||||
__tablename__ = 'blacklisted_tokens'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
||||
token = db.Column(db.String(500), unique=True, nullable=False)
|
||||
blacklisted_on = db.Column(db.DateTime, nullable=False)
|
||||
|
||||
def __init__(self, token: str) -> None:
|
||||
self.token = token
|
||||
self.blacklisted_on = datetime.utcnow()
|
||||
|
||||
@classmethod
|
||||
def check(cls, auth_token: str) -> bool:
|
||||
return cls.query.filter_by(token=str(auth_token)).first() is not None
|
||||
|
Reference in New Issue
Block a user