Files
attune/docs/testing/testing-authentication.md
2026-02-04 17:46:30 -06:00

9.4 KiB

Testing Authentication Endpoints

This guide provides step-by-step instructions for testing the Attune authentication system.

Prerequisites

  1. Database Running

    # Start PostgreSQL (if using Docker)
    docker run -d \
      --name postgres \
      -e POSTGRES_PASSWORD=postgres \
      -p 5432:5432 \
      postgres:15
    
  2. Database Setup

    # Create database and user
    psql -U postgres -c "CREATE DATABASE attune;"
    psql -U postgres -c "CREATE USER svc_attune WITH PASSWORD 'attune_password';"
    psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE attune TO svc_attune;"
    
  3. Run Migrations

    export DATABASE_URL="postgresql://svc_attune:attune_password@localhost:5432/attune"
    sqlx migrate run
    
  4. Set Environment Variables

    export DATABASE_URL="postgresql://svc_attune:attune_password@localhost:5432/attune"
    export JWT_SECRET="my-super-secret-jwt-key-min-256-bits-please"
    export JWT_ACCESS_EXPIRATION=3600
    export JWT_REFRESH_EXPIRATION=604800
    export RUST_LOG=info
    

Starting the API Server

cd attune
cargo run --bin attune-api

Expected output:

 INFO Starting Attune API Service
 INFO Configuration loaded successfully
 INFO Environment: development
 INFO Connecting to database...
 INFO Database connection established
 INFO JWT configuration initialized
 INFO Starting server on 127.0.0.1:8080
 INFO Server listening on 127.0.0.1:8080
 INFO Attune API Service is ready

Testing with cURL

1. Health Check (Verify Server is Running)

curl http://localhost:8080/api/v1/health

Expected response:

{
  "status": "healthy"
}

2. Register a New User

curl -X POST http://localhost:8080/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "login": "alice",
    "password": "securepass123",
    "display_name": "Alice Smith"
  }'

Expected response:

{
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "token_type": "Bearer",
    "expires_in": 3600
  }
}

Save the access_token for the next steps!

3. Login with Existing User

curl -X POST http://localhost:8080/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "login": "alice",
    "password": "securepass123"
  }'

Expected response:

{
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "token_type": "Bearer",
    "expires_in": 3600
  }
}

4. Get Current User (Protected Endpoint)

Replace YOUR_ACCESS_TOKEN with the actual token from step 2 or 3:

curl http://localhost:8080/auth/me \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Expected response:

{
  "data": {
    "id": 1,
    "login": "alice",
    "display_name": "Alice Smith"
  }
}

5. Change Password (Protected Endpoint)

curl -X POST http://localhost:8080/auth/change-password \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "current_password": "securepass123",
    "new_password": "newsecurepass456"
  }'

Expected response:

{
  "data": {
    "success": true,
    "message": "Password changed successfully"
  }
}

6. Login with New Password

curl -X POST http://localhost:8080/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "login": "alice",
    "password": "newsecurepass456"
  }'

Should return new tokens.

7. Refresh Access Token

Save the refresh_token from a previous login, then:

curl -X POST http://localhost:8080/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{
    "refresh_token": "YOUR_REFRESH_TOKEN"
  }'

Expected response:

{
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "token_type": "Bearer",
    "expires_in": 3600
  }
}

Error Cases to Test

Invalid Credentials

curl -X POST http://localhost:8080/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "login": "alice",
    "password": "wrongpassword"
  }'

Expected response (401):

{
  "error": "Invalid login or password",
  "code": "UNAUTHORIZED"
}

Missing Authentication Token

curl http://localhost:8080/auth/me

Expected response (401):

{
  "error": {
    "code": 401,
    "message": "Missing authentication token"
  }
}

Invalid Token

curl http://localhost:8080/auth/me \
  -H "Authorization: Bearer invalid.token.here"

Expected response (401):

{
  "error": {
    "code": 401,
    "message": "Invalid authentication token"
  }
}

Duplicate Registration

Register the same user twice:

curl -X POST http://localhost:8080/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "login": "alice",
    "password": "securepass123"
  }'

Expected response (409):

{
  "error": "Identity with login 'alice' already exists",
  "code": "CONFLICT"
}

Validation Errors

Short password:

curl -X POST http://localhost:8080/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "login": "bob",
    "password": "short"
  }'

Expected response (422):

{
  "error": "Invalid registration request: ...",
  "code": "VALIDATION_ERROR"
}

Testing with HTTPie (Alternative)

If you prefer HTTPie (more readable):

# Install HTTPie
pip install httpie

# Register
http POST localhost:8080/auth/register \
  login=alice password=securepass123 display_name="Alice Smith"

# Login
http POST localhost:8080/auth/login \
  login=alice password=securepass123

# Get current user (set TOKEN variable first)
TOKEN="your_access_token_here"
http GET localhost:8080/auth/me \
  "Authorization: Bearer $TOKEN"

# Change password
http POST localhost:8080/auth/change-password \
  "Authorization: Bearer $TOKEN" \
  current_password=securepass123 \
  new_password=newsecurepass456

Testing with Postman

  1. Import Collection

    • Create a new collection named "Attune Auth"
    • Add base URL variable: {{baseUrl}} = http://localhost:8080
  2. Setup Environment

    • Create environment "Attune Local"
    • Variables:
      • baseUrl: http://localhost:8080
      • accessToken: (will be set by tests)
      • refreshToken: (will be set by tests)
  3. Add Requests

    • POST {{baseUrl}}/auth/register
    • POST {{baseUrl}}/auth/login
    • GET {{baseUrl}}/auth/me with header: Authorization: Bearer {{accessToken}}
    • POST {{baseUrl}}/auth/change-password
    • POST {{baseUrl}}/auth/refresh
  4. Test Scripts Add to login/register requests to save tokens:

    pm.test("Status is 200", function () {
        pm.response.to.have.status(200);
    });
    
    var jsonData = pm.response.json();
    pm.environment.set("accessToken", jsonData.data.access_token);
    pm.environment.set("refreshToken", jsonData.data.refresh_token);
    

Automated Testing

Unit Tests

Run the authentication unit tests:

# Test password hashing
cargo test --package attune-api password

# Test JWT utilities
cargo test --package attune-api jwt

# Test middleware
cargo test --package attune-api middleware

# Run all API tests
cargo test --package attune-api

Integration Tests (Future)

Integration tests will be added to test the full authentication flow:

cargo test --package attune-api --test auth_integration

Troubleshooting

Server Won't Start

  1. Database Connection Error

    Error: error communicating with database
    
    • Verify PostgreSQL is running
    • Check DATABASE_URL is correct
    • Verify database exists and user has permissions
  2. Migration Error

    Error: migration version not found
    
    • Run migrations: sqlx migrate run
  3. JWT_SECRET Warning

    WARN JWT_SECRET not set, using default
    
    • Set JWT_SECRET environment variable

Authentication Fails

  1. Invalid Credentials

    • Verify password is correct
    • Check if identity exists in database:
      SELECT * FROM attune.identity WHERE login = 'alice';
      
  2. Token Expired

    • Use the refresh token to get a new access token
    • Check JWT_ACCESS_EXPIRATION setting
  3. Invalid Token Format

    • Ensure Authorization header format: Bearer <token>
    • No extra spaces or quotes

Database Issues

Check identities in database:

-- Connect to database
psql -U svc_attune -d attune

-- View all identities
SELECT id, login, display_name, created FROM attune.identity;

-- Check password hash exists
SELECT login, 
       attributes->>'password_hash' IS NOT NULL as has_password 
FROM attune.identity;

Security Notes

  • Never use default JWT_SECRET in production
  • Always use HTTPS in production
  • Store tokens securely on the client side
  • Implement rate limiting on auth endpoints (future)
  • Consider adding MFA for production (future)

Next Steps

After validating authentication works:

  1. Test Pack Management API with authentication
  2. Implement additional CRUD APIs
  3. Add RBAC permission checking (Phase 2.13)
  4. Add integration tests
  5. Implement token revocation for logout

Resources