[Python] FASTAPI userrs verify throws 400 Bad Request detail: VERIFY_USER_BAD_TOKEN

Discussão em 'Python' iniciado por Stack, Maio 12, 2025.

  1. Stack

    Stack Membro Participativo

    I'm trying to verify a mail with fastapi_users (V13.0.0) however I can't solve this issue were the API always returns VERIFY_USER_BAD_TOKEN, sent from a POST request at /docs you can include it as follows:

    router = APIRouter()

    router.include_router(fastapi_users.get_verify_router(UserRead), prefix=RoutesConfig.auth_prefix, tags=RoutesConfig.auth_tags,)


    Tried already several ways of generating the token (all seem good testing it here), my User model is stored in MongoDB and is working fine for both user creation & login.

    used config variables to ensure everything is good

    SECRET = "SECRET"
    # Generous time to ensure the token has not expired.
    VERIFY_TOKEN_LIFETIME = 86400
    # Seems to be the default anyway
    TOKEN_AUDIENCE_VERIFY = "fastapi-users:verify"


    gave values to User Manager variables

    verification_token_secret = Config.SECRET
    verification_token_lifetime_seconds = Config.VERIFY_TOKEN_LIFETIME
    verification_token_audience = Config.TOKEN_AUDIENCE_VERIFY


    Attempts for token generation include using generate jwt:

    from fastapi_users.jwt import generate_jwt

    token = generate_jwt(
    data={
    "sub": str(user.id),
    "email": user.email,
    "aud": Config.TOKEN_AUDIENCE_VERIFY,
    },
    secret=Config.SECRET,
    lifetime_seconds=Config.VERIFY_TOKEN_LIFETIME,
    )


    both with JWTStrategy:

    from fastapi_users.authentication.strategy.jwt import JWTStrategy

    def get_verify_user_token_strategy() -> JWTStrategy:
    return JWTStrategy(
    secret=Config.SECRET,
    lifetime_seconds=Config.VERIFY_TOKEN_LIFETIME,
    token_audience=[Config.TOKEN_AUDIENCE_VERIFY],
    )

    token = await get_verify_user_token_strategy().write_token(user)


    and CustomJWTSrategy:

    from authentication.custom_jwt_strategy import CustomJWTStrategy


    def get_jwt_strategy_verify() -> CustomJWTStrategy:
    return CustomJWTStrategy(
    secret=Config.SECRET,
    lifetime_seconds=Config.VERIFY_TOKEN_LIFETIME,
    token_audience=Config.TOKEN_AUDIENCE_VERIFY,
    )

    token = await get_jwt_strategy_verify().write_token(user)


    This is the actual UserManager, token is currently being generated by get_verify_user_token_strategy but it can be replaced with any of the previous options with the same effect VERIFY_USER_BAD_TOKEN D:

    from fastapi_users.db import ObjectIDIDMixin
    from fastapi_users import BaseUserManager
    from beanie import PydanticObjectId
    from models.model_user import User

    # The class manager does get used already successfully (login logic not included since it's not relevant)
    class UserManager(ObjectIDIDMixin, BaseUserManager[User, PydanticObjectId]):
    user_db = User

    verification_token_secret = Config.SECRET
    verification_token_lifetime_seconds = Config.VERIFY_TOKEN_LIFETIME
    verification_token_audience = Config.TOKEN_AUDIENCE_VERIFY

    # Call on_after_request_verify to preserve the structure
    async def on_after_register(self, user: User, request: Optional[Request] = None):
    token = await get_verify_user_token_strategy().write_token(user)
    await self.on_after_request_verify(user, token)

    # Token gets created and mail gets sent correctly.
    async def on_after_request_verify(self, user: User, token: str, request:Optional[Request] = None):
    print(f"Verification requested for user {user.id}. Verification token: {token}")
    # Generate link
    verification_link = f"{RoutesConfig.verification_verify}{token}"
    # Send link.
    await send_verification_email(user.email, verification_link)

    # Not trying to do anything fancy yet just the example provided by the docs
    # https://fastapi-users.github.io/fastapi-users/13.0/configuration/user-manager/#on_after_verify
    async def on_after_verify(
    self, user: User, request: Optional[Request] = None
    ):
    # This code isn't reached, there's a validation (the one my token fails) happening before.
    print(f"User {user.id} has been verified")



    Seems I'm doing everything ok still can't figure out why is this happening, I'm not too stray away from what docs example shows (it actually doesn't show token generation), the usage isn't that deep about what's bad (but it's very clear about the token being bad but not much else), GPT insists there can be differences on what's being written vs what's expected to be read, but keeps pointing to attributes & methods out of date...

    Went into BaseUserManager to look it's insides and seem like it should work yet it doesn't. Think I've included everything relevant but if anything is missing please do comment I'll do my best to answer.

    Thank you all for your time! :)

    This is part of the User model, it seems like an overkill to include... but I'm desperate for help n.n

    from fastapi_users.db import BeanieBaseUser
    from beanie import Document
    from pydantic import Field
    from typing import List

    class User(BeanieBaseUser, Document):
    oauth_accounts: List[OAuthAccount] = Field(default_factory=list)
    is_superuser: bool = Field(default=False)
    name: str = Field(default="Name")
    lastname: str = Field(default="Lastname")


    EDIT 1: I've tested every step of verify() of BaseUserManager and it works ok, if I paste its contents into on_after_request_verify right after generating the token, it does get correctly executed, the jwt is successfully decoded by decode_jwt, its contents verified and is_verified does get updated in the db:

    async def on_after_request_verify(
    self, user: User, token: str, request: Optional[Request] = None
    ):
    print(f"Verification requested for user {user.id}. Verification token: {token}")
    # Here starts the code at verify()
    try:
    my_test = decode_jwt(token, Config.SECRET, Config.TOKEN_AUDIENCE_VERIFY)
    except jwt.PyJWTError:
    raise exceptions.InvalidVerifyToken()

    try:
    user_id = my_test["sub"]
    email = my_test["email"]
    except KeyError:
    raise exceptions.InvalidVerifyToken()
    try:
    user = await self.get_by_email(email)
    except exceptions.UserNotExists:
    raise exceptions.InvalidVerifyToken()
    try:
    parsed_id = self.parse_id(user_id)
    except exceptions.InvalidID:
    raise exceptions.InvalidVerifyToken()
    if parsed_id != user.id:
    raise exceptions.InvalidVerifyToken()
    if user.is_verified:
    raise exceptions.UserAlreadyVerified()

    verified_user = await self._update(user, {"is_verified": True})
    # Here ends the code at verify()
    # Generate link
    verification_link = f"{RoutesConfig.verification_verify}{token}"
    print('verification_link', verification_link)


    Which is weird because the only thing I removed was the call to self.on_after_verify, and the function has nothing in it nor arises any error code as you can see bellow.

    async def on_after_verify(
    self, user: models.UP, request: Optional[Request] = None
    ) -> None:
    """
    Perform logic after successful user verification.

    *You should overload this method to add your own logic.*

    :param user: The verified user.
    :param request: Optional FastAPI request that
    triggered the operation, defaults to None.
    """
    return # pragma: no cover


    Still wondering were this error is coming from?? I'd prefer to use the built in route rather than having a workaround one :p

    Continue reading...

Compartilhe esta Página