Files
retail-nest/TESTING_REFRESH_TOKEN.md
2025-10-21 16:30:18 +07:00

531 lines
10 KiB
Markdown

# Testing Refresh Token System
This guide provides step-by-step instructions to test the refresh token implementation.
## Prerequisites
1. Database is running and migrations are applied:
```bash
npm run migration:run
```
2. Application is running:
```bash
npm run start:dev
```
3. Seed data is loaded (includes admin user):
```bash
npm run seed
```
## Test Scenarios
### 1. Test User Login (Get Tokens)
**Request**:
```bash
curl -X POST http://localhost:3000/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "admin@retailpos.com",
"password": "Admin123!"
}'
```
**Expected Response**:
```json
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6...",
"user": {
"id": "uuid",
"email": "admin@retailpos.com",
"name": "Admin User",
"roles": ["admin"],
"isActive": true,
"createdAt": "2025-01-15T10:00:00.000Z"
}
}
```
**Verification**:
- ✅ Response includes `access_token`
- ✅ Response includes `refresh_token`
- ✅ Response includes user details
- ✅ HTTP status is 200
**Save the tokens**:
```bash
# Save for next tests
export ACCESS_TOKEN="<your-access-token>"
export REFRESH_TOKEN="<your-refresh-token>"
```
---
### 2. Test Access Token Works
Use the access token to access a protected endpoint:
**Request**:
```bash
curl -X GET http://localhost:3000/api/auth/profile \
-H "Authorization: Bearer $ACCESS_TOKEN"
```
**Expected Response**:
```json
{
"success": true,
"data": {
"id": "uuid",
"email": "admin@retailpos.com",
"roles": ["admin"]
}
}
```
**Verification**:
- ✅ Protected endpoint returns user data
- ✅ HTTP status is 200
- ❌ Without token returns 401
---
### 3. Test Token Refresh
Exchange refresh token for new tokens:
**Request**:
```bash
curl -X POST http://localhost:3000/api/auth/refresh \
-H "Content-Type: application/json" \
-d "{
\"refreshToken\": \"$REFRESH_TOKEN\"
}"
```
**Expected Response**:
```json
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "x1y2z3a4b5c6d7e8f9g0h1i2j3k4l5m6n7o8p9q0r1s2t3u4v5w6...",
"user": {
"id": "uuid",
"email": "admin@retailpos.com",
"name": "Admin User",
"roles": ["admin"],
"isActive": true,
"createdAt": "2025-01-15T10:00:00.000Z"
}
}
```
**Verification**:
- ✅ New `access_token` is different from old one
- ✅ New `refresh_token` is different from old one
- ✅ HTTP status is 200
- ✅ User details are returned
**Save new tokens**:
```bash
export NEW_ACCESS_TOKEN="<new-access-token>"
export NEW_REFRESH_TOKEN="<new-refresh-token>"
```
---
### 4. Test Token Rotation (Old Token Should Be Revoked)
Try to use the OLD refresh token again:
**Request**:
```bash
curl -X POST http://localhost:3000/api/auth/refresh \
-H "Content-Type: application/json" \
-d "{
\"refreshToken\": \"$REFRESH_TOKEN\"
}"
```
**Expected Response**:
```json
{
"success": false,
"error": {
"statusCode": 401,
"message": "Invalid refresh token"
}
}
```
**Verification**:
- ✅ HTTP status is 401
- ✅ Error message indicates invalid token
- ✅ Old token cannot be reused (token rotation works!)
---
### 5. Test Logout
Logout and revoke the refresh token:
**Request**:
```bash
curl -X POST http://localhost:3000/api/auth/logout \
-H "Content-Type: application/json" \
-d "{
\"refreshToken\": \"$NEW_REFRESH_TOKEN\"
}"
```
**Expected Response**:
```json
{
"success": true,
"message": "Logged out successfully"
}
```
**Verification**:
- ✅ HTTP status is 200
- ✅ Success message returned
---
### 6. Test Revoked Token Cannot Be Used
Try to use the revoked token:
**Request**:
```bash
curl -X POST http://localhost:3000/api/auth/refresh \
-H "Content-Type: application/json" \
-d "{
\"refreshToken\": \"$NEW_REFRESH_TOKEN\"
}"
```
**Expected Response**:
```json
{
"success": false,
"error": {
"statusCode": 401,
"message": "Invalid refresh token"
}
}
```
**Verification**:
- ✅ HTTP status is 401
- ✅ Revoked token cannot be used
---
### 7. Test Revoke All Tokens
Login again and test revoking all tokens:
**Step 1 - Login**:
```bash
curl -X POST http://localhost:3000/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "admin@retailpos.com",
"password": "Admin123!"
}'
```
Save the tokens:
```bash
export ACCESS_TOKEN="<access-token>"
export REFRESH_TOKEN="<refresh-token>"
```
**Step 2 - Revoke All Tokens**:
```bash
curl -X POST http://localhost:3000/api/auth/revoke-all \
-H "Authorization: Bearer $ACCESS_TOKEN"
```
**Expected Response**:
```json
{
"success": true,
"message": "All refresh tokens revoked successfully"
}
```
**Step 3 - Verify Token is Revoked**:
```bash
curl -X POST http://localhost:3000/api/auth/refresh \
-H "Content-Type: application/json" \
-d "{
\"refreshToken\": \"$REFRESH_TOKEN\"
}"
```
**Verification**:
- ✅ Revoke all returns success
- ✅ All tokens are revoked
- ✅ Cannot use any refresh token after revoke-all
---
### 8. Test Invalid Scenarios
#### 8.1 Invalid Refresh Token
```bash
curl -X POST http://localhost:3000/api/auth/refresh \
-H "Content-Type: application/json" \
-d '{
"refreshToken": "invalid-token-12345"
}'
```
**Expected**: 401 Unauthorized
#### 8.2 Missing Refresh Token
```bash
curl -X POST http://localhost:3000/api/auth/refresh \
-H "Content-Type: application/json" \
-d '{}'
```
**Expected**: 400 Bad Request (validation error)
#### 8.3 Expired Access Token
Wait for access token to expire (24 hours by default) or manually set a short expiry:
**Expected**: 401 Unauthorized when using expired access token
---
### 9. Database Verification
Connect to the database and verify tokens are stored correctly:
```bash
# Connect to PostgreSQL
psql -h localhost -U postgres -d retail_pos
# Or for remote database
psql -h pg-30ed1d6a-renolation.b.aivencloud.com -p 20912 -U avnadmin -d defaultdb
```
**Query 1 - View Refresh Tokens**:
```sql
SELECT
id,
LEFT(token, 20) || '...' as token_preview,
"userId",
"expiresAt",
"isRevoked",
"createdAt"
FROM refresh_tokens
ORDER BY "createdAt" DESC
LIMIT 10;
```
**Query 2 - Count Active Tokens**:
```sql
SELECT COUNT(*) as active_tokens
FROM refresh_tokens
WHERE "isRevoked" = false
AND "expiresAt" > NOW();
```
**Query 3 - Tokens Per User**:
```sql
SELECT
u.email,
COUNT(*) as token_count,
SUM(CASE WHEN rt."isRevoked" = false THEN 1 ELSE 0 END) as active_count
FROM refresh_tokens rt
JOIN users u ON u.id = rt."userId"
GROUP BY u.email;
```
**Verification**:
- ✅ Tokens are stored as hashed values
- ✅ Revoked tokens have `isRevoked = true`
- ✅ Tokens have proper expiration dates
- ✅ Foreign key relationship to users exists
---
### 10. Load Testing (Optional)
Test multiple concurrent refresh operations:
```bash
# Create a simple load test script
cat > test_refresh.sh << 'EOF'
#!/bin/bash
# Login first
RESPONSE=$(curl -s -X POST http://localhost:3000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"admin@retailpos.com","password":"Admin123!"}')
REFRESH_TOKEN=$(echo $RESPONSE | jq -r '.refresh_token')
# Perform 10 sequential refreshes
for i in {1..10}; do
echo "Refresh attempt $i"
RESPONSE=$(curl -s -X POST http://localhost:3000/api/auth/refresh \
-H "Content-Type: application/json" \
-d "{\"refreshToken\":\"$REFRESH_TOKEN\"}")
REFRESH_TOKEN=$(echo $RESPONSE | jq -r '.refresh_token')
if [ "$REFRESH_TOKEN" == "null" ]; then
echo "Failed at attempt $i"
break
fi
done
EOF
chmod +x test_refresh.sh
./test_refresh.sh
```
**Verification**:
- ✅ All refreshes succeed
- ✅ Each refresh invalidates previous token
- ✅ No database errors
---
## Postman/Insomnia Collection
For easier testing, import this collection:
### Login
```
POST http://localhost:3000/api/auth/login
Content-Type: application/json
{
"email": "admin@retailpos.com",
"password": "Admin123!"
}
```
### Refresh Token
```
POST http://localhost:3000/api/auth/refresh
Content-Type: application/json
{
"refreshToken": "{{refresh_token}}"
}
```
### Get Profile
```
GET http://localhost:3000/api/auth/profile
Authorization: Bearer {{access_token}}
```
### Logout
```
POST http://localhost:3000/api/auth/logout
Content-Type: application/json
{
"refreshToken": "{{refresh_token}}"
}
```
### Revoke All Tokens
```
POST http://localhost:3000/api/auth/revoke-all
Authorization: Bearer {{access_token}}
```
---
## Expected Test Results Summary
| Test Case | Expected Result | Status |
|-----------|----------------|--------|
| Login | Returns access_token + refresh_token | ✅ |
| Access protected endpoint | Returns user data | ✅ |
| Refresh token | Returns new tokens | ✅ |
| Token rotation | Old token becomes invalid | ✅ |
| Logout | Token is revoked | ✅ |
| Use revoked token | 401 Unauthorized | ✅ |
| Revoke all tokens | All tokens revoked | ✅ |
| Invalid token | 401 Unauthorized | ✅ |
| Missing token | 400 Bad Request | ✅ |
---
## Troubleshooting
### Issue: "Cannot find module '@nestjs/schedule'"
**Solution**: The TokenCleanupService is optional. The system works without it. See OPTIONAL_SETUP.md if you want to enable automatic cleanup.
### Issue: "Invalid refresh token" immediately after login
**Possible Causes**:
1. Token not being stored in database
2. Hash mismatch
3. Database connection issue
**Debug**:
```sql
-- Check if token was created
SELECT * FROM refresh_tokens ORDER BY "createdAt" DESC LIMIT 1;
```
### Issue: Tokens not being revoked
**Debug**:
```sql
-- Check if isRevoked is being set
SELECT "isRevoked", COUNT(*) FROM refresh_tokens GROUP BY "isRevoked";
```
### Issue: Database bloat
**Solution**: Run manual cleanup:
```sql
DELETE FROM refresh_tokens WHERE "expiresAt" < NOW();
DELETE FROM refresh_tokens WHERE "isRevoked" = true;
```
Or enable automatic cleanup (see OPTIONAL_SETUP.md).
---
## Production Testing Checklist
Before deploying to production:
- [ ] All test scenarios pass
- [ ] Token rotation works correctly
- [ ] Tokens are stored as hashes
- [ ] Foreign key constraints work
- [ ] Expired tokens are rejected
- [ ] Revoked tokens are rejected
- [ ] Rate limiting is configured (optional)
- [ ] HTTPS is enforced
- [ ] Logging is in place
- [ ] Database backup is configured
- [ ] Cleanup strategy is decided (automatic or manual)
---
## Performance Benchmarks
Expected performance (adjust based on your infrastructure):
- Login: < 200ms
- Refresh: < 150ms
- Logout: < 100ms
- Token validation: < 50ms
- Database query: < 20ms
Monitor these metrics in production and optimize as needed.