diff --git a/sachet/server/models.py b/sachet/server/models.py index e42e1ad..532739b 100644 --- a/sachet/server/models.py +++ b/sachet/server/models.py @@ -20,12 +20,13 @@ class User(db.Model): self.register_date = datetime.datetime.now() self.admin = admin - def encode_token(self): + def encode_token(self, jti=None): """Generates an authentication token""" payload = { "exp": datetime.datetime.utcnow() + datetime.timedelta(days=7), "iat": datetime.datetime.utcnow(), - "sub": self.username + "sub": self.username, + "jti": jti } return jwt.encode( payload, diff --git a/sachet/server/users/views.py b/sachet/server/users/views.py index db6936d..28e676d 100644 --- a/sachet/server/users/views.py +++ b/sachet/server/users/views.py @@ -80,6 +80,28 @@ users_blueprint.add_url_rule( ) +class ExtendAPI(MethodView): + """Endpoint to take a token and get a new one with a later expiry date.""" + + @auth_required + def post(user, self): + token = user.encode_token(jti="renew") + resp = { + "status": "success", + "message": "Renewed token.", + "username": user.username, + "auth_token": token + } + return jsonify(resp), 200 + + +users_blueprint.add_url_rule( + "/users/extend", + view_func=ExtendAPI.as_view("extend_api"), + methods=['POST'] +) + + class UserAPI(MethodView): """User information API""" @auth_required diff --git a/tests/test_auth.py b/tests/test_auth.py index 6bc7f89..914183d 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -86,6 +86,42 @@ def test_login(client, users): assert token is not None and token != "" +def test_extend(client, tokens, validate_info): + """Test extending the token lifespan (get a new one with later expiry).""" + + # obtain new token + resp = client.post("/users/extend", + headers={ + "Authorization": f"Bearer {tokens['jeff']}" + } + ) + assert resp.status_code == 200 + resp_json = resp.get_json() + new_token = resp_json.get("auth_token") + assert new_token is not None + assert new_token is not tokens["jeff"] + + # revoke old token + + resp = client.post("/users/logout", json={ + "token": tokens["jeff"] + }, + headers={ + "Authorization": f"bearer {tokens['jeff']}" + } + ) + + # log in with the new token + resp = client.get("/users/jeff", + headers={ + "Authorization": f"Bearer {new_token}" + } + ) + assert resp.status_code == 200 + resp_json = resp.get_json() + validate_info("jeff", resp_json) + + def test_logout(client, tokens, validate_info): """Test logging out."""