Skip to content

Auth

login(request) async

Authenticate a user and issue JWT tokens.

Validates the provided credentials, ensures the account is active, and returns an access and refresh token.

Parameters:

Name Type Description Default
request LoginRequest

Login credentials payload.

required

Returns:

Type Description
LoginResponse

Access and refresh tokens.

Raises:

Type Description
HTTPException

401 if invalid credentials or user not found; 403 if account not activated.

Source code in routers/auth.py
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
@router.post("/login", response_model=LoginResponse)
async def login(request: LoginRequest) -> LoginResponse:
    """
    Authenticate a user and issue JWT tokens.

    Validates the provided credentials, ensures the account is active, and returns an access and refresh token.

    Args:
        request (LoginRequest): Login credentials payload.

    Returns:
        Access and refresh tokens.

    Raises:
        HTTPException: 401 if invalid credentials or user not found; 403 if account not activated.
    """
    if not await verify_user(request.email, request.password):
        raise HTTPException(status_code=401, detail="Invalid credentials")

    user = await User.find_one({"email": request.email})
    if not user:
        raise HTTPException(status_code=401, detail="User not found")
    if not user.is_active:
        raise HTTPException(
            status_code=403, detail="Account not activated. Please check your email."
        )

    return LoginResponse(
        access_token=create_access_token(sub=user.id),
        refresh_token=create_refresh_token(sub=user.id),
    )

refresh(request) async

Exchange a refresh token for a new access token.

Decodes and validates the provided refresh token and returns a new access token if the token and user are valid.

Parameters:

Name Type Description Default
request RefreshRequest

Refresh token payload.

required

Returns:

Type Description
RefreshResponse

Fresh access token.

Raises:

Type Description
HTTPException

401 if invalid refresh token or user not found.

Source code in routers/auth.py
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
@router.post("/refresh", response_model=RefreshResponse)
async def refresh(request: RefreshRequest) -> RefreshResponse:
    """
    Exchange a refresh token for a new access token.

    Decodes and validates the provided refresh token and returns a new access token if the token and user are valid.

    Args:
        request (RefreshRequest): Refresh token payload.

    Returns:
        Fresh access token.

    Raises:
        HTTPException: 401 if invalid refresh token or user not found.
    """
    try:
        payload = jwt.decode(
            request.refresh_token,
            JWT_SECRET_KEY,
            algorithms=[JWT_ALGORITHM],
            audience=JWT_AUDIENCE_REFRESH,
        )
        user_id = payload.get("sub")
        if not user_id:
            raise HTTPException(status_code=401, detail="Invalid refresh token")

        # Use find_by_id instead of find_one with _id
        user = await User.find_by_id(user_id)
        if not user:
            raise HTTPException(status_code=401, detail="User not found")
    except JWTError:
        raise HTTPException(status_code=401, detail="Invalid refresh token")

    return RefreshResponse(
        access_token=create_access_token(sub=user.id),
    )

resend_activation(request) async

Resend the account activation email.

Generates a new activation code (if the account is not yet active) and sends the activation email again.

Parameters:

Name Type Description Default
request ResendActivationRequest

Email address for the account.

required

Returns:

Type Description
dict

Confirmation message.

Raises:

Type Description
HTTPException

404 if user is not found.

Source code in routers/auth.py
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
@router.post("/resend-activation")
async def resend_activation(request: ResendActivationRequest) -> dict:
    """
    Resend the account activation email.

    Generates a new activation code (if the account is not yet active) and sends the activation email again.

    Args:
        request (ResendActivationRequest): Email address for the account.

    Returns:
        Confirmation message.

    Raises:
        HTTPException: 404 if user is not found.
    """
    user = await User.find_one({"email": request.email})
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    if user.is_active:
        return {"message": "Account already activated."}
    user.activation_code = generate_activation_code()
    await user.save()
    verification_url = (
        f"{FRONTEND_URL}/verify?email={user.email}&code={user.activation_code}"
    )
    email_service.send_email(
        to_email=user.email,
        subject="Activate your EVE account",
        template_name="activation.html",
        context={
            "verification_url": verification_url,
        },
    )
    return {"message": "Activation code resent."}

signup(request) async

Register a new user and send an activation email.

Creates a user account and emails an activation link containing a one-time activation code.

Parameters:

Name Type Description Default
request SignupRequest

Signup payload with user details and password.

required

Returns:

Type Description
SignupResponse

Created user summary.

Raises:

Type Description
HTTPException

400 if invalid or duplicate signup data.

Source code in routers/auth.py
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
@router.post("/signup", response_model=SignupResponse)
async def signup(request: SignupRequest) -> SignupResponse:
    """
    Register a new user and send an activation email.

    Creates a user account and emails an activation link containing a one-time activation code.

    Args:
        request (SignupRequest): Signup payload with user details and password.

    Returns:
        Created user summary.

    Raises:
        HTTPException: 400 if invalid or duplicate signup data.
    """
    try:
        user = await create_user(
            email=request.email,
            password=request.password,
            first_name=request.first_name,
            last_name=request.last_name,
        )
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    verification_url = (
        f"{FRONTEND_URL}/verify?email={user.email}&code={user.activation_code}"
    )
    email_service.send_email(
        to_email=user.email,
        subject="Activate your EVE account",
        template_name="activation.html",
        context={
            "verification_url": verification_url,
        },
    )
    return SignupResponse(
        id=user.id,
        email=user.email,
        first_name=user.first_name,
        last_name=user.last_name,
    )

verify(request) async

Verify account activation using the activation code.

Marks the user as active if the provided code matches and clears the code.

Parameters:

Name Type Description Default
request VerifyRequest

Verification payload with email and activation code.

required

Returns:

Type Description
dict

Confirmation message.

Raises:

Type Description
HTTPException

404 if user not found; 400 if activation code is invalid.

Source code in routers/auth.py
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
@router.post("/verify")
async def verify(request: VerifyRequest) -> dict:
    """
    Verify account activation using the activation code.

    Marks the user as active if the provided code matches and clears the code.

    Args:
        request (VerifyRequest): Verification payload with email and activation code.

    Returns:
        Confirmation message.

    Raises:
        HTTPException: 404 if user not found; 400 if activation code is invalid.
    """
    user = await User.find_one({"email": request.email})
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    if user.is_active:
        return {"message": "Account already activated."}
    if user.activation_code != request.activation_code:
        raise HTTPException(status_code=400, detail="Invalid activation code")
    user.is_active = True
    user.activation_code = None
    await user.save()
    return {"message": "Account activated successfully."}