Skip to content

Create integration tests for different OAuth providers #9

@koistya

Description

@koistya

Description

Create comprehensive integration tests using mock OAuth servers to test the complete OAuth flow with different providers (GitHub, Google, Microsoft, etc.). This will ensure the library works correctly with various OAuth implementations.

What needs to be done

Create integration tests that:

  1. Set up mock OAuth servers that simulate real provider behavior
  2. Test complete authorization flows for major providers
  3. Test error scenarios (access denied, invalid scope, network errors)
  4. Test edge cases specific to each provider
  5. Verify state parameter handling
  6. Test timeout scenarios
  7. Test concurrent authorization flows

Why this matters

Different OAuth providers have subtle differences:

  • Parameter naming conventions
  • Error response formats
  • Required vs optional parameters
  • Redirect behavior
  • State parameter handling

Without integration tests:

  • Provider-specific bugs only discovered in production
  • Regressions when updating the library
  • No confidence that the library works with all providers

Implementation considerations

⚠️ Note: This feature requires critical thinking during implementation. Consider:

  1. Mock vs Real: Should we test against real OAuth providers in a separate test suite? This would catch actual breaking changes.

  2. Test data management: How do we manage test credentials securely? GitHub Secrets? Environment variables?

  3. Alternative approach: Use a tool like WireMock or Mockoon to record and replay real OAuth flows

  4. CI/CD integration: These tests will be slower. Should they run on every commit or only on releases?

  5. Provider versioning: OAuth providers update their APIs. How do we keep our mocks in sync?

Suggested test structure

// tests/integration/providers/github.test.ts
import { expect, test, describe, beforeAll, afterAll } from "bun:test";
import { getAuthCode } from "../../src";
import { createMockOAuthServer } from "../helpers/mock-server";

describe("GitHub OAuth Integration", () => {
  let mockServer: MockOAuthServer;
  
  beforeAll(async () => {
    mockServer = await createMockOAuthServer({
      provider: "github",
      port: 4000
    });
  });
  
  afterAll(async () => {
    await mockServer.stop();
  });
  
  test("successful authorization flow", async () => {
    const state = crypto.randomUUID();
    
    mockServer.expectAuthorization({
      client_id: "test-client",
      state,
      response: {
        code: "test-code-123",
        state
      }
    });
    
    const result = await getAuthCode({
      authorizationUrl: `http://localhost:4000/authorize?client_id=test-client&state=${state}`,
      openBrowser: false
    });
    
    expect(result.code).toBe("test-code-123");
    expect(result.state).toBe(state);
  });
  
  test("user denies access", async () => {
    mockServer.expectAuthorization({
      response: {
        error: "access_denied",
        error_description: "The user denied access"
      }
    });
    
    await expect(getAuthCode({
      authorizationUrl: "http://localhost:4000/authorize",
      openBrowser: false
    })).rejects.toThrow(OAuthError);
  });
  
  test("handles GitHub-specific scopes", async () => {
    // Test GitHub's specific scope handling...
  });
});

// tests/helpers/mock-server.ts
export class MockOAuthServer {
  constructor(private config: MockServerConfig) {}
  
  async start() {
    // Create HTTP server that mimics OAuth provider
  }
  
  expectAuthorization(expectation: AuthExpectation) {
    // Set up expected request/response
  }
  
  async stop() {
    // Clean up
  }
}

// Provider-specific behaviors
const providerConfigs = {
  github: {
    authorizePath: "/login/oauth/authorize",
    errorFormat: "query", // vs "fragment"
    requiredParams: ["client_id"],
    stateRequired: false
  },
  google: {
    authorizePath: "/o/oauth2/v2/auth",
    errorFormat: "query",
    requiredParams: ["client_id", "response_type", "scope"],
    stateRequired: true
  },
  microsoft: {
    authorizePath: "/oauth2/v2.0/authorize",
    errorFormat: "query",
    requiredParams: ["client_id", "response_type", "scope"],
    stateRequired: true
  }
};

Test scenarios to cover

Success cases

  • Basic authorization with code
  • Authorization with state parameter
  • Authorization with additional parameters
  • Multiple concurrent authorizations

Error cases

  • User denies access
  • Invalid client ID
  • Invalid redirect URI
  • Invalid scope
  • Server errors (500, 503)
  • Network timeouts
  • Malformed responses

Provider-specific cases

  • GitHub: Apps vs OAuth Apps
  • Google: Offline access, consent prompts
  • Microsoft: Tenant-specific endpoints
  • Auth0: Custom domains
  • Okta: Authorization servers

Skills required

  • TypeScript
  • OAuth 2.0 protocol knowledge
  • Integration testing
  • Mock server creation
  • HTTP protocol
  • Testing async flows

Difficulty

Hard - Requires deep understanding of OAuth and testing complex async flows

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions