feat(auth): add email verification and resend verification flow#40
feat(auth): add email verification and resend verification flow#40aniebietafia wants to merge 2 commits intomainfrom
Conversation
add VerificationToken model and migration for persistent token storage implement class-based verification logic via VerificationTokenRepository and AuthVerificationService add GET /api/v1/auth/verify-email with token validation, expiry handling, and idempotent verified-user behavior add POST /api/v1/auth/resend-verification with enumeration-safe response and 3/minute rate limit integrate signup to generate verification tokens and enqueue email dispatch via Kafka email producer add standardized 429 error handling through custom rate-limit exception handler document endpoints in docs/auth_verification_api.md add tests for verification CRUD and API flows (success, missing token, invalid token, expired token, resend cases) keep code quality gates compliant (ruff, isort, mypy) and auth tests green Signed-off-by: aniebietafia <aniebietafia87@gmail.com>
add VerificationToken model and migration for persistent token storage implement class-based verification logic via VerificationTokenRepository and AuthVerificationService add GET /api/v1/auth/verify-email with token validation, expiry handling, and idempotent verified-user behavior add POST /api/v1/auth/resend-verification with enumeration-safe response and 3/minute rate limit integrate signup to generate verification tokens and enqueue email dispatch via Kafka email producer add standardized 429 error handling through custom rate-limit exception handler document endpoints in docs/auth_verification_api.md add tests for verification CRUD and API flows (success, missing token, invalid token, expired token, resend cases) keep code quality gates compliant (ruff, isort, mypy) and auth tests green Signed-off-by: aniebietafia <aniebietafia87@gmail.com>
📝 WalkthroughWalkthroughThis pull request implements a complete email verification system for user registration, including a new VerificationToken model, database migration, CRUD operations, verification service, API endpoints for email verification and resend, rate limiting infrastructure, and comprehensive test coverage. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Client
participant Server
participant DB
participant EmailProducer
User->>Client: POST /signup with email
Client->>Server: signup request
Server->>DB: create user
Server->>DB: create verification token
DB-->>Server: token created
Server->>EmailProducer: enqueue verification email
EmailProducer-->>Server: acknowledged
Server-->>Client: signup success
Client-->>User: show verification message
User->>User: click verification link
User->>Client: GET /verify-email?token=xxx
Client->>Server: verify-email request with token
Server->>DB: fetch token
DB-->>Server: token found
Server->>Server: validate token format & expiration
Server->>DB: update user.verified = true
Server->>DB: delete token
DB-->>Server: operations complete
Server-->>Client: verification success
Client-->>User: email verified
sequenceDiagram
participant User
participant Client
participant Server
participant DB
participant EmailProducer
participant RateLimiter
User->>Client: POST /resend-verification with email
Client->>Server: resend request
Server->>RateLimiter: check rate limit (3/min)
alt Rate limit exceeded
RateLimiter-->>Server: RateLimitExceeded
Server-->>Client: 429 error
else Rate limit OK
RateLimiter-->>Server: allowed
Server->>DB: find user by email
DB-->>Server: user found
Server->>DB: delete unexpired tokens for user
Server->>DB: create new verification token
DB-->>Server: token created
Server->>EmailProducer: enqueue verification email
EmailProducer-->>Server: acknowledged
Server-->>Client: success (generic message)
Client-->>User: resend email sent
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| ) | ||
| except Exception as exc: | ||
| logger.warning( | ||
| "Failed to enqueue verification resend for %s: %s", payload.email, exc |
Check failure
Code scanning / CodeQL
Log Injection High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 18 hours ago
In general, to fix log injection, you should sanitize any user-controlled values before including them in log messages, especially by removing or neutralizing newline and carriage-return characters so a user cannot split or visually manipulate log entries. This is typically done by replacing \r and \n with empty strings or safe placeholders on a local variable that you only use for logging, so you do not alter the original data used for application logic.
For this specific code, the best minimal fix is to sanitize payload.email right before logging it in the except block of resend_verification. We will introduce a local variable, e.g. safe_email, that converts payload.email to str and strips \r and \n characters using .replace('\r', '').replace('\n', ''). We then pass safe_email to logger.warning instead of the raw payload.email. This keeps the functional behavior (resend logic, response) unchanged and only affects what is recorded in logs. No new imports or helpers are strictly necessary; the sanitization can be an inline transformation on that one line.
Concretely:
- Edit
app/api/v1/endpoints/auth.pywithin theresend_verificationfunction. - In the
except Exception as exc:block, beforelogger.warning(...), definesafe_email = str(payload.email).replace('\r', '').replace('\n', ''). - Change the
logger.warningcall to usesafe_emailas the first%sargument. - Leave all other behavior intact.
| @@ -197,8 +197,9 @@ | ||
| email_producer=email_producer, | ||
| ) | ||
| except Exception as exc: | ||
| safe_email = str(payload.email).replace("\r", "").replace("\n", "") | ||
| logger.warning( | ||
| "Failed to enqueue verification resend for %s: %s", payload.email, exc | ||
| "Failed to enqueue verification resend for %s: %s", safe_email, exc | ||
| ) | ||
|
|
||
| return ActionAcknowledgement( |
| from alembic import op | ||
|
|
||
| # revision identifiers, used by Alembic. | ||
| revision: str = "4b4b6b5d1c2a" |
Check notice
Code scanning / CodeQL
Unused global variable Note
Copilot Autofix
AI about 18 hours ago
Copilot could not generate an autofix suggestion
Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.
|
|
||
| # revision identifiers, used by Alembic. | ||
| revision: str = "4b4b6b5d1c2a" | ||
| down_revision: str | Sequence[str] | None = "11781e907181" |
Check notice
Code scanning / CodeQL
Unused global variable Note
Copilot Autofix
AI about 18 hours ago
Copilot could not generate an autofix suggestion
Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.
| # revision identifiers, used by Alembic. | ||
| revision: str = "4b4b6b5d1c2a" | ||
| down_revision: str | Sequence[str] | None = "11781e907181" | ||
| branch_labels: str | Sequence[str] | None = None |
Check notice
Code scanning / CodeQL
Unused global variable Note
Copilot Autofix
AI about 18 hours ago
Copilot could not generate an autofix suggestion
Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.
| revision: str = "4b4b6b5d1c2a" | ||
| down_revision: str | Sequence[str] | None = "11781e907181" | ||
| branch_labels: str | Sequence[str] | None = None | ||
| depends_on: str | Sequence[str] | None = None |
Check notice
Code scanning / CodeQL
Unused global variable Note
Copilot Autofix
AI about 18 hours ago
Copilot could not generate an autofix suggestion
Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (3)
app/models/verification_token.py (1)
10-11: Avoid a second source of truth for token expiry.
app/crud/verification_token.pyalready computesexpires_atfromsettings.VERIFICATION_TOKEN_EXPIRE_HOURS, so this hard-coded 24-hour fallback is dead today. If any future insert path relies on the model default, it will silently ignore the configured TTL.Also applies to: 26-28
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/models/verification_token.py` around lines 10 - 11, The model-level default_expiry() currently hardcodes a 24-hour TTL which duplicates and can contradict the TTL computed in app/crud/verification_token.py; update default_expiry() to derive its timedelta from settings.VERIFICATION_TOKEN_EXPIRE_HOURS (import settings) so the model default and CRUD insertion use the same single source of truth for expires_at, or remove the model default entirely and rely solely on the CRUD layer—adjust the expires_at field and any references to default_expiry accordingly (see default_expiry, expires_at and app/crud/verification_token.py).app/crud/auth_verification_api.md (1)
82-83: Clarify the idempotency note.Successful verification deletes the token, so the same link is not 200-safe after first use; it falls through to
INVALID_TOKEN. Please narrow this note to the case where the user is already verified and the token row still exists.📝 Suggested wording
-- Already verified users are handled idempotently by `GET /verify-email` and receive `200`. +- If the user is already verified and the token row still exists, `GET /verify-email` returns `200`. +- After a successful verification consumes the token, the same link returns `INVALID_TOKEN`.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/crud/auth_verification_api.md` around lines 82 - 83, Update the idempotency note for GET /verify-email to clarify that tokens are single-use and are deleted upon successful verification so reusing the same link will typically yield INVALID_TOKEN; only treat the request as idempotent (returning 200) in the specific case where the user is already verified and the token row still exists. Replace the existing second bullet with wording that explicitly states: "Already verified users are handled idempotently by GET /verify-email only when the verification token row still exists; otherwise a reused link will result in INVALID_TOKEN." Ensure references to "successful verification deletes the token", "GET /verify-email", and "INVALID_TOKEN" remain in the doc so the behavior is clear.tests/test_auth/test_email_verification.py (1)
167-193: Add the resend failure-path regression test.This suite covers the happy path, but not the case where
send_email()raises after a resend. That is the path currently able to invalidate the old token while the API still returns 200, so it is worth pinning once the service logic is adjusted.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_auth/test_email_verification.py` around lines 167 - 193, Add a regression test that simulates send_email raising and asserts the old verification token is NOT invalidated: create a test (e.g., test_resend_verification_failure_does_not_invalidate_token) that uses the same helpers (_create_unverified_user and _get_verification_token), capture old_value, set email_producer_mock.send_email.side_effect = Exception("send failure"), call POST /api/v1/auth/resend-verification, assert email_producer_mock.send_email was awaited once, and then fetch the token via _get_verification_token and assert its token equals the previously captured old_value to ensure a failed send does not rotate the token.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@alembic/versions/4b4b6b5d1c2a_add_verification_tokens_table.py`:
- Around line 32-43: Replace the redundant single-column index
ix_verification_tokens_id with a composite index on (user_id, expires_at) so
queries like delete_unexpired_tokens_for_user (in
app/crud/verification_token.py) can use an index; specifically, remove the
op.create_index call that creates ix_verification_tokens_id and instead add
op.create_index(op.f("ix_verification_tokens_user_id_expires_at"),
"verification_tokens", ["user_id", "expires_at"], unique=False). Make the same
replacement for the corresponding index in the later block (the second
occurrence around lines 47-50) so both upgrade and downgrade/other index
definitions reflect the composite index. Ensure ix_verification_tokens_token
(unique on token) remains unchanged.
In `@app/api/v1/endpoints/auth.py`:
- Around line 193-202: The current broad except around
auth_verification_service.resend_verification_email masks DB/token failures;
narrow the error handling so persistence errors propagate and only
email-producer failures are caught. Move the try/except so it surrounds just the
call to the email_producer (or have resend_verification_email raise a specific
EmailProducerError), catch that specific exception (e.g., EmailProducerError or
the producer's raised exception) and log via logger.warning("Failed to enqueue
verification resend for %s: %s", payload.email, exc); do not catch Exception
from within the overall resend_verification_email call so lookup/token mutation
errors surface as failures.
In `@app/crud/verification_token.py`:
- Around line 15-23: The create_token method (and the similar
revoke/current-token helper at lines 32-42) commits independently which can
cause a gap when called back-to-back from app/services/auth_verification.py;
change these helpers to support a no-commit mode (e.g. add an optional commit:
bool = True parameter or provide internal variants like _create_token_no_commit
and _revoke_current_tokens_no_commit) so they perform db.add/db.flush/db.refresh
without db.commit when commit is False, and then update the service layer to
call both helpers and perform a single db.commit() after both have succeeded;
reference the create_token function and the revoke_current_tokens helper when
implementing this change.
In `@app/services/auth_verification.py`:
- Around line 87-99: Don't delete unexpired tokens before emailing — instead
reuse an existing token if present or create a new one, then enqueue the email,
and only remove other tokens after send_email succeeds. Concretely: use the
repository method that returns existing tokens (e.g.,
get_unexpired_tokens_for_user) to pick/reuse a token; if none exists call
create_token to make one; build verification_link from that token; await
email_producer.send_email(...); only after send_email resolves successfully call
delete_unexpired_tokens_for_user (or a repository method that deletes other
tokens while preserving the just-sent token) to remove stale tokens.
In `@tests/test_auth/test_email_verification.py`:
- Around line 113-116: Replace the hard-coded UUID string in
test_verify_email_invalid_token_returns_custom_error with a programmatically
generated UUID (using uuid.uuid4()) so the request to
"/api/v1/auth/verify-email?token=..." still exercises the INVALID_TOKEN branch
without committing a literal that trips Gitleaks; update the test's client.get
call to interpolate uuid.uuid4().hex or str(uuid.uuid4()) for the token and
import uuid at the top of the test file if not already present.
---
Nitpick comments:
In `@app/crud/auth_verification_api.md`:
- Around line 82-83: Update the idempotency note for GET /verify-email to
clarify that tokens are single-use and are deleted upon successful verification
so reusing the same link will typically yield INVALID_TOKEN; only treat the
request as idempotent (returning 200) in the specific case where the user is
already verified and the token row still exists. Replace the existing second
bullet with wording that explicitly states: "Already verified users are handled
idempotently by GET /verify-email only when the verification token row still
exists; otherwise a reused link will result in INVALID_TOKEN." Ensure references
to "successful verification deletes the token", "GET /verify-email", and
"INVALID_TOKEN" remain in the doc so the behavior is clear.
In `@app/models/verification_token.py`:
- Around line 10-11: The model-level default_expiry() currently hardcodes a
24-hour TTL which duplicates and can contradict the TTL computed in
app/crud/verification_token.py; update default_expiry() to derive its timedelta
from settings.VERIFICATION_TOKEN_EXPIRE_HOURS (import settings) so the model
default and CRUD insertion use the same single source of truth for expires_at,
or remove the model default entirely and rely solely on the CRUD layer—adjust
the expires_at field and any references to default_expiry accordingly (see
default_expiry, expires_at and app/crud/verification_token.py).
In `@tests/test_auth/test_email_verification.py`:
- Around line 167-193: Add a regression test that simulates send_email raising
and asserts the old verification token is NOT invalidated: create a test (e.g.,
test_resend_verification_failure_does_not_invalidate_token) that uses the same
helpers (_create_unverified_user and _get_verification_token), capture
old_value, set email_producer_mock.send_email.side_effect = Exception("send
failure"), call POST /api/v1/auth/resend-verification, assert
email_producer_mock.send_email was awaited once, and then fetch the token via
_get_verification_token and assert its token equals the previously captured
old_value to ensure a failed send does not rotate the token.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ca2c2dd1-6f7e-4303-a531-88580cbf3fed
📒 Files selected for processing (14)
.env.examplealembic/versions/4b4b6b5d1c2a_add_verification_tokens_table.pyapp/api/v1/endpoints/auth.pyapp/core/config.pyapp/core/rate_limiter.pyapp/crud/auth_verification_api.mdapp/crud/verification_token.pyapp/main.pyapp/models/__init__.pyapp/models/verification_token.pyapp/schemas/auth.pyapp/services/auth_verification.pytests/test_auth/test_email_verification.pytests/test_auth/test_verification_token_crud.py
| op.create_index( | ||
| op.f("ix_verification_tokens_id"), | ||
| "verification_tokens", | ||
| ["id"], | ||
| unique=False, | ||
| ) | ||
| op.create_index( | ||
| op.f("ix_verification_tokens_token"), | ||
| "verification_tokens", | ||
| ["token"], | ||
| unique=True, | ||
| ) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify repository query patterns that filter by user_id/expires_at for verification tokens.
fd 'verification_token\.py$' app tests | xargs -r rg -n -C2 'user_id|expires_at|delete_unexpired_tokens_for_user|get_token\('Repository: Brints/FluentMeet
Length of output: 3144
🏁 Script executed:
cat -n alembic/versions/4b4b6b5d1c2a_add_verification_tokens_table.pyRepository: Brints/FluentMeet
Length of output: 1916
Replace redundant id index with composite index supporting user-based token cleanup.
The index on id at line 32 is redundant—primary keys automatically have index support. More critically, the delete_unexpired_tokens_for_user() method in app/crud/verification_token.py:32-37 filters by both user_id and expires_at, but no index covers this pattern, causing full table scans as data grows.
Replace the id index with a composite index on (user_id, expires_at):
Suggested migration adjustment
def upgrade() -> None:
op.create_index(
- op.f("ix_verification_tokens_id"),
+ op.f("ix_verification_tokens_user_id_expires_at"),
"verification_tokens",
- ["id"],
+ ["user_id", "expires_at"],
unique=False,
)
def downgrade() -> None:
op.drop_index(
op.f("ix_verification_tokens_token"), table_name="verification_tokens"
)
op.drop_index(
- op.f("ix_verification_tokens_id"), table_name="verification_tokens"
+ op.f("ix_verification_tokens_user_id_expires_at"), table_name="verification_tokens"
)
op.drop_table("verification_tokens")Also applies to: 47-50
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@alembic/versions/4b4b6b5d1c2a_add_verification_tokens_table.py` around lines
32 - 43, Replace the redundant single-column index ix_verification_tokens_id
with a composite index on (user_id, expires_at) so queries like
delete_unexpired_tokens_for_user (in app/crud/verification_token.py) can use an
index; specifically, remove the op.create_index call that creates
ix_verification_tokens_id and instead add
op.create_index(op.f("ix_verification_tokens_user_id_expires_at"),
"verification_tokens", ["user_id", "expires_at"], unique=False). Make the same
replacement for the corresponding index in the later block (the second
occurrence around lines 47-50) so both upgrade and downgrade/other index
definitions reflect the composite index. Ensure ix_verification_tokens_token
(unique on token) remains unchanged.
| try: | ||
| await auth_verification_service.resend_verification_email( | ||
| db=db, | ||
| email=str(payload.email), | ||
| email_producer=email_producer, | ||
| ) | ||
| except Exception as exc: | ||
| logger.warning( | ||
| "Failed to enqueue verification resend for %s: %s", payload.email, exc | ||
| ) |
There was a problem hiding this comment.
Don't mask token/database failures as a successful resend.
resend_verification_email() does user lookup and token mutation before it touches the email producer. This except Exception converts failures in those DB steps into a 200 "If an account..." response, which hides outages and can leave state partially changed. Catch only the producer failure path, or let the persistence errors propagate.
🧰 Tools
🪛 GitHub Check: CodeQL
[failure] 201-201: Log Injection
This log entry depends on a user-provided value.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/api/v1/endpoints/auth.py` around lines 193 - 202, The current broad
except around auth_verification_service.resend_verification_email masks DB/token
failures; narrow the error handling so persistence errors propagate and only
email-producer failures are caught. Move the try/except so it surrounds just the
call to the email_producer (or have resend_verification_email raise a specific
EmailProducerError), catch that specific exception (e.g., EmailProducerError or
the producer's raised exception) and log via logger.warning("Failed to enqueue
verification resend for %s: %s", payload.email, exc); do not catch Exception
from within the overall resend_verification_email call so lookup/token mutation
errors surface as failures.
| def create_token(self, db: Session, user_id: int) -> VerificationToken: | ||
| expires_at = datetime.now(UTC) + timedelta( | ||
| hours=settings.VERIFICATION_TOKEN_EXPIRE_HOURS | ||
| ) | ||
| verification_token = VerificationToken(user_id=user_id, expires_at=expires_at) | ||
| db.add(verification_token) | ||
| db.commit() | ||
| db.refresh(verification_token) | ||
| return verification_token |
There was a problem hiding this comment.
Let resend own the transaction boundary.
In app/services/auth_verification.py, Lines 87-88 call these helpers back-to-back during resend. Because both methods commit independently, a failure between them can revoke the user's current valid token before the replacement exists. Expose no-commit variants or move the single commit() up to the service layer.
Also applies to: 32-42
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/crud/verification_token.py` around lines 15 - 23, The create_token method
(and the similar revoke/current-token helper at lines 32-42) commits
independently which can cause a gap when called back-to-back from
app/services/auth_verification.py; change these helpers to support a no-commit
mode (e.g. add an optional commit: bool = True parameter or provide internal
variants like _create_token_no_commit and _revoke_current_tokens_no_commit) so
they perform db.add/db.flush/db.refresh without db.commit when commit is False,
and then update the service layer to call both helpers and perform a single
db.commit() after both have succeeded; reference the create_token function and
the revoke_current_tokens helper when implementing this change.
| self._token_repository.delete_unexpired_tokens_for_user(db=db, user_id=user.id) | ||
| token = self._token_repository.create_token(db=db, user_id=user.id) | ||
|
|
||
| verification_link = ( | ||
| f"{settings.FRONTEND_BASE_URL}/verify-email?token={token.token}" | ||
| ) | ||
| await email_producer.send_email( | ||
| to=user.email, | ||
| subject="Verify your FluentMeet account", | ||
| html_body=None, | ||
| template_data={"verification_link": verification_link}, | ||
| template="verification", | ||
| ) |
There was a problem hiding this comment.
Keep the current link valid until the replacement is queued.
Line 87 deletes the current unexpired token before Line 93 tries to enqueue the replacement email. If send_email() fails, the endpoint still returns 200 and the user loses the only verification link they already had. Reuse the existing token or delete the old one only after queueing succeeds.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/services/auth_verification.py` around lines 87 - 99, Don't delete
unexpired tokens before emailing — instead reuse an existing token if present or
create a new one, then enqueue the email, and only remove other tokens after
send_email succeeds. Concretely: use the repository method that returns existing
tokens (e.g., get_unexpired_tokens_for_user) to pick/reuse a token; if none
exists call create_token to make one; build verification_link from that token;
await email_producer.send_email(...); only after send_email resolves
successfully call delete_unexpired_tokens_for_user (or a repository method that
deletes other tokens while preserving the just-sent token) to remove stale
tokens.
| def test_verify_email_invalid_token_returns_custom_error(client: TestClient) -> None: | ||
| response = client.get( | ||
| "/api/v1/auth/verify-email?token=8f14e45f-ceea-4f6a-9fef-3d4d3e0d1be1" | ||
| ) |
There was a problem hiding this comment.
Replace the UUID literal that trips Gitleaks.
The scanner is flagging this hard-coded value as a generic secret. uuid4() exercises the same INVALID_TOKEN branch without adding noisy security findings.
🧪 Suggested change
+from uuid import uuid4
...
def test_verify_email_invalid_token_returns_custom_error(client: TestClient) -> None:
- response = client.get(
- "/api/v1/auth/verify-email?token=8f14e45f-ceea-4f6a-9fef-3d4d3e0d1be1"
- )
+ response = client.get(f"/api/v1/auth/verify-email?token={uuid4()}")📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def test_verify_email_invalid_token_returns_custom_error(client: TestClient) -> None: | |
| response = client.get( | |
| "/api/v1/auth/verify-email?token=8f14e45f-ceea-4f6a-9fef-3d4d3e0d1be1" | |
| ) | |
| from uuid import uuid4 | |
| def test_verify_email_invalid_token_returns_custom_error(client: TestClient) -> None: | |
| response = client.get(f"/api/v1/auth/verify-email?token={uuid4()}") |
🧰 Tools
🪛 Gitleaks (8.30.0)
[high] 115-115: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/test_auth/test_email_verification.py` around lines 113 - 116, Replace
the hard-coded UUID string in
test_verify_email_invalid_token_returns_custom_error with a programmatically
generated UUID (using uuid.uuid4()) so the request to
"/api/v1/auth/verify-email?token=..." still exercises the INVALID_TOKEN branch
without committing a literal that trips Gitleaks; update the test's client.get
call to interpolate uuid.uuid4().hex or str(uuid.uuid4()) for the token and
import uuid at the top of the test file if not already present.
Summary by CodeRabbit
Release Notes
New Features
Documentation