QuadrastackQuadrastack
Documentation

Docs

Authentication Workflow Tutorial

Learn how to test authentication flows with login, token capture, and authenticated requests using Quadrastack workflows.

Authentication Workflow Tutorial

Learn how to test authentication flows by chaining requests together. This tutorial covers login flows, token capture, and using session data in subsequent requests.

Time: 15 minutes | Level: Intermediate

What You'll Build

A complete authentication workflow that:

  1. Logs in and captures a token
  2. Uses the token to fetch user profile
  3. Updates user data with authenticated requests
  4. Validates each step with assertions

Prerequisites

  • Completed First API Test tutorial
  • Understanding of REST API authentication
  • 15 minutes

Understanding Workflows

Workflows in Quadrastack allow you to:

  • Execute requests in sequence
  • Capture data from responses using setVars
  • Pass data between steps with {{.session.stepName.body.field}}
  • Build complex multi-step test scenarios

Step 1: Create Project Structure

Create a new directory:

mkdir auth-workflow-tutorial
cd auth-workflow-tutorial

Step 2: Set Up a Mock Authentication Server

First, let's create a mock API that simulates authentication. Create mocks.yaml:

mockServer:
  auth-api:
    description: Mock authentication API
    listen: ":8080"
    defaults:
      status: 200
      headers:
        Content-Type: application/json

mocks:
  login-route:
    server: auth-api
    description: Login endpoint that returns a token
    match:
      method: POST
      path: /api/auth/login
    respond:
      - body: |
          {
            "success": true,
            "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9",
            "userId": "user-12345",
            "expiresIn": 3600
          }

  get-profile-route:
    server: auth-api
    description: Get user profile (requires auth token)
    match:
      method: GET
      path: /api/user/profile
      headers:
        Authorization: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
    respond:
      - body: |
          {
            "userId": "user-12345",
            "username": "johndoe",
            "email": "john@example.com",
            "role": "admin"
          }

  update-profile-route:
    server: auth-api
    description: Update user profile (requires auth token)
    match:
      method: PUT
      path: /api/user/profile
      headers:
        Authorization: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
    respond:
      - body: |
          {
            "success": true,
            "message": "Profile updated successfully",
            "userId": "user-12345"
          }

Step 3: Create Individual Requests

Create requests.yaml with individual request definitions:

requests:
  login:
    description: Authenticate user and get token
    method: POST
    url: http://localhost:8080/api/auth/login
    headers:
      Content-Type: application/json
    body:
      username: "johndoe"
      password: "secret123"
    expect:
      status: 200
      body:
        $.success: true
        $.token: exists
        $.userId: exists
        $.expiresIn: "> 0"

  get-profile:
    description: Fetch user profile with auth token
    method: GET
    url: http://localhost:8080/api/user/profile
    headers:
      Authorization: "Bearer {{.session.login.body.token}}"
    expect:
      status: 200
      body:
        $.userId: exists
        $.username: !empty
        $.email: matches "^[\\w.]+@[\\w.]+"

  update-profile:
    description: Update user profile with auth token
    method: PUT
    url: http://localhost:8080/api/user/profile
    headers:
      Authorization: "Bearer {{.session.login.body.token}}"
      Content-Type: application/json
    body:
      username: "johndoe_updated"
      email: "john.updated@example.com"
    expect:
      status: 200
      body:
        $.success: true
        $.message: contains "successfully"

Step 4: Create the Workflow

Now create workflows.yaml to chain these requests together:

workflows:
  auth-flow:
    description: Complete authentication workflow
    labels: ["auth", "integration"]
    steps:
      - request: login
        out: login

      - request: get-profile
        # Uses token from {{.session.login.body.token}}

      - request: update-profile
        # Uses token from {{.session.login.body.token}}

**Key workflow concepts:**

- `out` - Captures the entire response into a session variable (e.g., `login`)
- `{{.session.login.body.token}}` - Access captured data from previous steps
- Requests reference session data directly in their definitions

## Step 5: Start Mock Server and Run Workflow

First, start the mock server in one terminal:

```bash
quadrastack --mock-server auth-api

Expected Output:

Mock server 'auth-api' started on :8080
Press Ctrl+C to stop

In another terminal, run the workflow:

quadrastack --workflow auth-flow

Expected Output:

Running workflow: auth-flow
  ✓ Step 1: login (200 OK, 3ms)
    ✓ Captured output 'login'
  ✓ Step 2: get-profile (200 OK, 2ms)
  ✓ Step 3: update-profile (200 OK, 2ms)

Workflow completed: 3/3 steps passed

Step 8: Handling Authentication Errors

Add error handling to your workflow. Update requests.yaml:

requests:
  login-invalid:
    description: Test invalid credentials
    method: POST
    url: http://localhost:8080/api/auth/login
    headers:
      Content-Type: application/json
    body:
      username: "invalid"
      password: "wrong"
    expect:
      status: 401
      body:
        $.success: false
        $.error: exists

  get-profile-unauthorized:
    description: Test unauthorized access
    method: GET
    url: http://localhost:8080/api/user/profile
    headers:
      Authorization: "Bearer invalid-token"
    expect:
      status: 401

Add mock responses for errors in mocks.yaml:

mocks:
  login-invalid-route:
    server: auth-api
    description: Handle invalid credentials
    match:
      method: POST
      path: /api/auth/login
      body:
        $.username: "invalid"
    respond:
      - status: 401
        body: |
          {
            "success": false,
            "error": "Invalid credentials"
          }

  unauthorized-route:
    server: auth-api
    description: Handle invalid token
    match:
      method: GET
      path: /api/user/profile
      headers:
        Authorization: contains "invalid-token"
    respond:
      - status: 401
        body: |
          {
            "error": "Unauthorized",
            "message": "Invalid or expired token"
          }

Step 9: OAuth 2.0 Flow Example

Here's a more complex OAuth-style flow:

workflows:
  oauth-flow:
    description: OAuth 2.0 authentication flow
    steps:
      # Step 1: Get authorization code
      - request: get-auth-code
        out: authCodeStep

      # Step 2: Exchange code for token
      # Request 'exchange-token' must use {{.session.authCodeStep.body.code}}
      - request: exchange-token
        out: tokenStep

      # Step 3: Use access token
      # Request 'get-protected-resource' must use {{.session.tokenStep.body.access_token}}
      - request: get-protected-resource

      # Step 4: Refresh token if needed
      # Request 'refresh-token' must use {{.session.tokenStep.body.refresh_token}}
      - request: refresh-token

Final Project Structure

auth-workflow-tutorial/
├── mocks.yaml
├── requests.yaml
├── workflows.yaml
└── vars.yaml

What You Learned

  • Creating workflows with sequential steps
  • Capturing response data with setVars
  • Accessing session data with {{.session.stepName.body.field}}
  • Passing data between workflow steps
  • Testing authentication flows (login, token, authenticated requests)
  • Handling authentication errors
  • Building complex multi-step scenarios

Advanced Patterns

Pattern 1: Conditional Workflows

Pattern 1: Conditional Logic (Future)

Currently, you can use labels and separate scenarios to handle conditional flows.

Pattern 2: Token Refresh Flow

workflows:
  auto-refresh:
    steps:
      - request: login
        out: login

      - request: api-call-1
        # Uses {{.session.login.body.token}}

      # If token expires, refresh it
      # Request 'refresh-token' uses {{.session.login.body.refreshToken}}
      - request: refresh-token
        out: refresh

      - request: api-call-2
        # Uses {{.session.refresh.body.token}}

Next Steps

Common Issues

Token Not Captured

Token Not Captured

If {{.session.login.body.token}} is empty, check the response structure:

# Use verbose mode to see full response
quadrastack --workflow auth-flow --verbose

Session Variable Not Found

Ensure the step name matches:

# Step name is "login" (from out property or default if supported)
- request: login
  out: login

# Reference must use "login"
- request: get-profile
  # Definition uses {{.session.login.body.token}}

Mock Server Not Responding

Make sure the mock server is running and ports match:

# Start mock server
quadrastack --mock-server auth-api

# Check if it's listening
curl http://localhost:8080/api/auth/login

Tips

  1. Always validate tokens exist - Add $.token: exists assertions
  2. Use descriptive step names - Makes session variables easier to reference
  3. Test error cases - Invalid credentials, expired tokens, unauthorized access
  4. Start with mocks - Test your workflow logic before using real APIs
  5. Use verbose mode - Helps debug session variable issues

You now know how to build complex authentication workflows with Quadrastack!

Authentication Workflow Tutorial - Quadrastack Docs