Complete Authentication Flow - End-to-End Smoke Test¶
End-to-end smoke test covering the complete authentication lifecycle from registration through logout.
Purpose¶
Run a full authentication lifecycle smoke test from registration through logout. Tests all major auth components in a single end-to-end flow for manual verification.
This is a smoke test - it validates the entire auth system is operational by exercising all major endpoints in sequence.
Prerequisites¶
- Development environment running:
make dev-up - Fresh database state (or unique test email)
- Environment variables configured:
export BASE_URL="https://localhost:8000"
export TEST_EMAIL="smoke-test-$(date +%s)@example.com" # Unique email
export TEST_PASSWORD="SecurePass123!"
export FRONTEND_URL="https://localhost:3000"
Steps¶
Step 1: User Registration¶
Register new user account:
curl -k -X POST "$BASE_URL/api/v1/auth/register" \
-H "Content-Type: application/json" \
-d "{
\"email\": \"$TEST_EMAIL\",
\"password\": \"$TEST_PASSWORD\",
\"name\": \"Smoke Test User\"
}"
Expected: HTTP 201 Created
{
"id": "uuid-here",
"email": "smoke-test-123@example.com",
"name": "Smoke Test User",
"email_verified": false,
"is_active": true,
"created_at": "2025-10-05T..."
}
Extract verification token from logs:
# In separate terminal
docker logs dashtam-dev-app --tail 50 2>&1 | grep -A 20 '📧 EMAIL' | grep 'verify-email'
# Look for URL:
# https://localhost:3000/verify-email?token=YOUR_TOKEN_HERE
export VERIFICATION_TOKEN="<token-from-logs>"
echo "Verification Token: $VERIFICATION_TOKEN"
Step 2: Email Verification¶
Verify email with extracted token:
curl -k -X POST "$BASE_URL/api/v1/auth/verify-email" \
-H "Content-Type: application/json" \
-d "{
\"token\": \"$VERIFICATION_TOKEN\"
}"
Expected: HTTP 200 OK
Step 3: Login¶
Login with verified account:
LOGIN_RESPONSE=$(curl -k -s -X POST "$BASE_URL/api/v1/auth/login" \
-H "Content-Type: application/json" \
-d "{
\"email\": \"$TEST_EMAIL\",
\"password\": \"$TEST_PASSWORD\"
}")
echo "$LOGIN_RESPONSE"
# Extract tokens
export ACCESS_TOKEN=$(echo "$LOGIN_RESPONSE" | grep -o '"access_token":"[^"]*"' | cut -d'"' -f4)
export REFRESH_TOKEN=$(echo "$LOGIN_RESPONSE" | grep -o '"refresh_token":"[^"]*"' | cut -d'"' -f4)
echo "Access Token: ${ACCESS_TOKEN:0:50}..."
echo "Refresh Token: ${REFRESH_TOKEN:0:50}..."
Expected: HTTP 200 OK with JWT tokens
Step 4: Access Protected Resource¶
Get user profile with access token:
Expected: HTTP 200 OK
{
"id": "uuid-here",
"email": "smoke-test-123@example.com",
"name": "Smoke Test User",
"email_verified": true,
"is_active": true
}
Step 5: Update Profile¶
Update user full name:
curl -k -X PATCH "$BASE_URL/api/v1/auth/me" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"name\": \"Updated Smoke Test\"
}"
Expected: HTTP 200 OK with updated profile
Step 6: Token Refresh¶
Refresh access token using refresh token:
REFRESH_RESPONSE=$(curl -k -s -X POST "$BASE_URL/api/v1/auth/refresh" \
-H "Content-Type: application/json" \
-d "{
\"refresh_token\": \"$REFRESH_TOKEN\"
}")
echo "$REFRESH_RESPONSE"
# Extract new access token (refresh token stays same - no rotation)
export NEW_ACCESS_TOKEN=$(echo "$REFRESH_RESPONSE" | grep -o '"access_token":"[^"]*"' | cut -d'"' -f4)
echo "New Access Token: ${NEW_ACCESS_TOKEN:0:50}..."
echo "Refresh Token (unchanged): ${REFRESH_TOKEN:0:50}..."
Expected: HTTP 200 OK with new access token
Verify new token works:
Step 7: Password Reset Request¶
Request password reset:
curl -k -X POST "$BASE_URL/api/v1/password-resets" \
-H "Content-Type: application/json" \
-d "{
\"email\": \"$TEST_EMAIL\"
}"
Expected: HTTP 202 Accepted
Extract reset token from logs:
docker logs dashtam-dev-app --tail 50 2>&1 | grep -A 20 '📧 EMAIL' | grep 'reset-password'
# Look for URL:
# https://localhost:3000/reset-password?token=YOUR_TOKEN_HERE
export RESET_TOKEN="<token-from-logs>"
echo "Reset Token: $RESET_TOKEN"
Verify token is retrievable (optional):
Expected: HTTP 200 OK
Step 8: Password Reset Confirmation¶
Complete password reset with new password:
export NEW_PASSWORD="NewSecurePass456!"
curl -k -X PATCH "$BASE_URL/api/v1/password-resets/$RESET_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"new_password\": \"$NEW_PASSWORD\"
}"
Expected: HTTP 200 OK
Verify new password works:
curl -k -X POST "$BASE_URL/api/v1/auth/login" \
-H "Content-Type: application/json" \
-d "{
\"email\": \"$TEST_EMAIL\",
\"password\": \"$NEW_PASSWORD\"
}"
Expected: HTTP 200 OK with new access/refresh tokens
Extract new tokens for logout test:
LOGIN_RESPONSE2=$(curl -k -s -X POST "$BASE_URL/api/v1/auth/login" \
-H "Content-Type: application/json" \
-d "{
\"email\": \"$TEST_EMAIL\",
\"password\": \"$NEW_PASSWORD\"
}")
export ACCESS_TOKEN2=$(echo "$LOGIN_RESPONSE2" | grep -o '"access_token":"[^"]*"' | cut -d'"' -f4)
export REFRESH_TOKEN2=$(echo "$LOGIN_RESPONSE2" | grep -o '"refresh_token":"[^"]*"' | cut -d'"' -f4)
Step 9: Logout (Refresh Token Revocation)¶
Logout and revoke refresh token:
curl -k -X POST "$BASE_URL/api/v1/auth/logout" \
-H "Authorization: Bearer $ACCESS_TOKEN2" \
-H "Content-Type: application/json" \
-d "{
\"refresh_token\": \"$REFRESH_TOKEN2\"
}"
Expected: HTTP 200 OK
Verify refresh token is revoked:
# This should FAIL with 401 Unauthorized
curl -k -X POST "$BASE_URL/api/v1/auth/refresh" \
-H "Content-Type: application/json" \
-d "{
\"refresh_token\": \"$REFRESH_TOKEN2\"
}"
Expected: HTTP 401 Unauthorized (token revoked)
Verify access token still works (until expiration ~30 min):
# This should SUCCEED - access tokens remain valid until expiration
curl -k -X GET "$BASE_URL/api/v1/auth/me" \
-H "Authorization: Bearer $ACCESS_TOKEN2"
Expected: HTTP 200 OK (access token still valid)
Why? Logout only revokes refresh tokens immediately. JWT access tokens remain valid until natural expiration. This is correct behavior for stateless JWT systems.
See: JWT Authentication - Logout Behavior
✅ Smoke Test Complete! This flow tested 10 endpoints and verified: user registration, email verification, JWT authentication, token refresh, password reset with session revocation, profile management, and logout behavior. All major auth components are operational.
Troubleshooting¶
Registration fails with 409 Conflict¶
Cause: Email already registered
Solution: Use unique email with timestamp:
Cannot extract token from logs¶
Cause: Email not logged or wrong container
Solution:
# Verify development mode
docker exec dashtam-dev-app env | grep DEBUG
# Should show: DEBUG=True
# Check recent logs with more context
docker logs dashtam-dev-app --tail 200 2>&1 | grep -A 30 '📧'
Login fails with 400 "Email not verified"¶
Cause: Email verification step skipped or failed
Solution: Complete email verification (Step 2) before login
Refresh token still works after logout¶
Cause: Looking at wrong token or caching issue
Solution:
# Use exact refresh token from Step 9 login
echo "Using refresh token: ${REFRESH_TOKEN2:0:50}..."
# Clear any HTTP caching
curl -k -X POST "$BASE_URL/api/v1/auth/refresh" \
-H "Content-Type: application/json" \
-H "Cache-Control: no-cache" \
-d "{
\"refresh_token\": \"$REFRESH_TOKEN2\"
}"
Access token rejected after logout¶
Cause: Misunderstanding JWT behavior - this is a bug if it happens
Solution: Access tokens should remain valid after logout (up to expiration). If rejected immediately, check:
# Verify token not expired
echo "$ACCESS_TOKEN2" | cut -d'.' -f2 | base64 -d 2>/dev/null | grep exp
# Check for auth middleware issues in logs
docker logs dashtam-dev-app --tail 50 | grep -i "auth"
SSL certificate errors¶
Cause: Self-signed certificates in development
Solution: Always use -k flag with curl:
Related Flows¶
- Registration - Detailed registration flow
- Email Verification - Email verification details
- Login - Login and token usage details
- Password Reset - Password reset details
- JWT Authentication Architecture - Complete auth design
Notes: Emails are logged to console in development mode (no AWS SES needed). All email tokens are available in Docker logs. Use unique email per test run to avoid conflicts. Test users remain in database (no delete endpoint yet).
Document Information¶
Template: api-flow-template.md Created: 2025-10-15 Last Updated: 2025-10-15