def assert_tokens(response_data: dict, user: User, session_id: str = None): """ Allows to check access- and refresh-tokens in the response body """ access_token = response_data.get("access_token") refresh_token = response_data.get("refresh_token") assert access_token, f"No access_token in response: {response_data}" assert refresh_token, f"No refresh_token in response: {response_data}" decoded_access_token = decode_jwt(access_token) access_exp_dt = datetime.fromisoformat(decoded_access_token.pop("exp_iso")) assert access_exp_dt > datetime.utcnow() assert decoded_access_token.get("user_id") == user.id, decoded_access_token assert decoded_access_token.get( "token_type") == "access", decoded_access_token decoded_refresh_token = decode_jwt(refresh_token) refresh_exp_dt = datetime.fromisoformat( decoded_refresh_token.pop("exp_iso")) assert refresh_exp_dt > datetime.utcnow() assert decoded_refresh_token.get( "user_id") == user.id, decoded_refresh_token assert decoded_refresh_token.get( "token_type") == "refresh", decoded_refresh_token assert refresh_exp_dt > access_exp_dt if session_id: assert decoded_refresh_token.get("session_id") == session_id
def test_reset_password__ok(self, client, user, mocked_auth_send, dbs): request_user = user await_(request_user.update(dbs, is_superuser=True)) target_user = await_( User.async_create(dbs, db_commit=True, email=self.email, password="******")) client.login(user) response = client.post(self.url, json={"email": target_user.email}) response_data = self.assert_ok_response(response) token = response_data.get("token") assert response_data["user_id"] == target_user.id assert token is not None, response_data assert decode_jwt(response_data["token"])["user_id"] == target_user.id link = f"{settings.SITE_URL}/change-password/?t={token}" expected_body = ( f"<p>You can reset your password for {settings.SITE_URL}</p>" f"<p>Please follow the link </p>" f"<p><a href={link}>{link}</a></p>") mocked_auth_send.assert_awaited_once_with( recipient_email=target_user.email, subject=f"Welcome back to {settings.SITE_URL}", html_content=expected_body, )
async def test_reset_by_email__ok(self, client, user, db_objects, mocked_auth_send, reset_by): if reset_by == "id": data = {"user_id": user.id} else: data = {"email": user.email} user.is_superuser = True await user.async_update(db_objects) response = await client.post(self.path, json=data) response_data = await response.json() assert response.status == 200 token = response_data["token"] assert response_data["user_id"] == user.id assert token is not None assert decode_jwt(response_data["token"])["user_id"] == user.id link = f"{settings.SITE_URL}/change-password/?t={token}" expected_body = ( f"<p>You can reset your password for {settings.SITE_URL}</p>" f"<p>Please follow the link </p>" f"<p><a href={link}>{link}</a></p>") mocked_auth_send.assert_called_once_with( recipient_email=user.email, subject=f"Welcome back to {settings.SITE_URL}", html_content=expected_body, )
async def authenticate_user( self, jwt_token: str, token_type: str = TOKEN_TYPE_ACCESS, ) -> Tuple[User, dict, str]: """ Allows to find active user by jwt_token """ logger.debug("Logging via JWT auth. Got token: %s", jwt_token) try: jwt_payload = decode_jwt(jwt_token) except ExpiredSignatureError: logger.debug("JWT signature has been expired for token %s", jwt_token) raise SignatureExpiredError("JWT signature has been expired for token") except InvalidTokenError as error: msg = "Token could not be decoded: %s" logger.exception(msg, error) raise AuthenticationFailedError(msg % (error,)) if jwt_payload["token_type"] != token_type: raise AuthenticationFailedError( f"Token type '{token_type}' expected, got '{jwt_payload['token_type']}' instead." ) user_id = jwt_payload.get("user_id") user = await User.get_active(self.db_session, user_id) if not user: msg = "Couldn't found active user with id=%s." logger.warning(msg, user_id) raise AuthenticationFailedError(details=(msg % (user_id,))) session_id = jwt_payload.get("session_id") if not session_id: raise AuthenticationFailedError("Incorrect data in JWT: session_id is missed") user_session = await UserSession.async_get( self.db_session, public_id=session_id, is_active=True ) if not user_session: raise AuthenticationFailedError( f"Couldn't found active session: {user_id=} | {session_id=}." ) return user, jwt_payload, session_id
async def authenticate_user(db_objects, jwt_token) -> User: logger.info("Logging via JWT auth. Got token: %s", jwt_token) try: jwt_payload = decode_jwt(jwt_token) except PyJWTError as err: msg = _("Invalid token header. Token could not be decoded as JWT.") logger.exception("Bad jwt token: %s", err) raise AuthenticationFailedError(details=msg) user_id = str(jwt_payload.get("user_id", 0)) try: assert user_id.isdigit() user = await User.async_get(db_objects, id=jwt_payload["user_id"], is_active=True) except (AssertionError, User.DoesNotExist): raise AuthenticationFailedError( details=f"Active user #{user_id} not found or token is invalid." ) return user
def test_sign_in__check_user_session__ok(self, client, dbs): self._create_user(dbs) response = client.post(self.url, json={ "email": self.email, "password": self.raw_password }) response_data = self.assert_ok_response(response) refresh_token = response_data.get("refresh_token") decoded_refresh_token = decode_jwt(refresh_token) refresh_exp_dt = datetime.fromisoformat( decoded_refresh_token.pop("exp_iso")) user_session: UserSession = await_( UserSession.async_get(dbs, user_id=self.user.id)) assert user_session.refresh_token == refresh_token assert user_session.is_active is True assert user_session.expired_at == refresh_exp_dt assert user_session.refreshed_at is not None assert decoded_refresh_token.get( "session_id") == user_session.public_id