Skip to content

Add OAuth2 client_credentials support to step.http_call#183

Merged
intel352 merged 4 commits intomainfrom
copilot/add-oauth-client-credentials-support
Feb 27, 2026
Merged

Add OAuth2 client_credentials support to step.http_call#183
intel352 merged 4 commits intomainfrom
copilot/add-oauth-client-credentials-support

Conversation

Copy link
Contributor

Copilot AI commented Feb 27, 2026

Pipelines calling OAuth2-protected APIs (e.g. Salesforce) currently require a separate token-fetch step with no caching, or a custom Go step. This adds native oauth2_client_credentials support directly to step.http_call.

Changes

module/pipeline_step_http_call.go

  • Added oauthConfig struct (token URL, client ID/secret, scopes) with a credential fingerprint used as a cache key
  • Implemented a process-wide globalOAuthCache (oauthTokenCache) shared across all HTTPCallStep instances, keyed by credential fingerprint (token_url + client_id + client_secret + scopes) — tokens are reused across pipeline executions for the same credentials
  • Each cache entry embeds a singleflight.Group to coalesce concurrent token fetches: only one network call is made to the token endpoint even under concurrent load (double-checked locking pattern)
  • Multi-tenancy safe: each distinct set of credentials produces an isolated cache entry, so different tenants never share tokens
  • Before each request, fetches a bearer token if auth.type = "oauth2_client_credentials" is configured; reuses cached token until expiry (with 10s buffer)
  • On 401, invalidates the shared cache entry and fetches a fresh token directly (bypassing singleflight for an unconditional refresh), then retries the request once
  • Made httpClient an injectable field (was hardcoded to http.DefaultClient) to enable unit testing; request timeouts are enforced via context (set by context.WithTimeout at the top of Execute)

module/pipeline_step_http_call_test.go

  • New test file covering: basic GET, 4xx errors, token fetch, token caching (single fetch across multiple calls), 401-retry flow, scope forwarding, missing required auth fields, TTL expiry, and concurrent singleflight deduplication (5 goroutines sharing credentials → exactly 1 token request)

cmd/wfctl/type_registry.go

  • Added auth to ConfigKeys for step.http_call

Example config

steps:
  - name: call-salesforce
    type: step.http_call
    config:
      url: "https://myorg.salesforce.com/services/data/v60.0/sobjects/Lead"
      method: POST
      auth:
        type: oauth2_client_credentials
        token_url: "https://login.salesforce.com/services/oauth2/token"
        client_id: "{{ .env.SF_CLIENT_ID }}"
        client_secret: "{{ .env.SF_CLIENT_SECRET }}"
        scopes:
          - api
      body:
        LastName: "{{ .lead_last_name }}"
        Company: "{{ .lead_company }}"

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI self-assigned this Feb 27, 2026
Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Copilot AI changed the title [WIP] Add OAuth client_credentials support to step.http_call Add OAuth2 client_credentials support to step.http_call Feb 27, 2026
@intel352 intel352 marked this pull request as ready for review February 27, 2026 02:41
Copilot AI review requested due to automatic review settings February 27, 2026 02:41
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds native OAuth2 client_credentials flow support to the step.http_call pipeline step, enabling pipelines to call OAuth2-protected APIs (like Salesforce) without manual token management. The implementation includes token caching with TTL-based expiry, automatic retry on 401 responses, and comprehensive test coverage.

Changes:

  • Added OAuth2 client credentials authentication with configurable token URL, client ID/secret, and scopes
  • Implemented thread-safe token caching with TTL derived from OAuth2 expires_in responses (with 10s expiry buffer)
  • Added automatic token invalidation and retry on 401 unauthorized responses
  • Made HTTP client injectable to enable unit testing without real network calls

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
module/pipeline_step_http_call.go Core implementation: adds oauthConfig struct, thread-safe tokenCache, token fetch/refresh logic, 401-retry flow, and injectable httpClient field
module/pipeline_step_http_call_test.go Comprehensive test coverage for basic GET, error responses, OAuth2 token fetch/caching/expiry/retry, scope forwarding, and missing auth fields
cmd/wfctl/type_registry.go Updated ConfigKeys for step.http_call to include the new auth configuration field

@intel352
Copy link
Contributor

@copilot apply changes based on the comments in this thread

Also, make sure that this functionality is not incompatible with multi-tenancy.

…Auth2 http_call step

Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 27, 2026

@copilot apply changes based on the comments in this thread

Also, make sure that this functionalit...

Applied all three review comments in 06274fe:

  1. Global cache shared across instancesglobalOAuthCache is a process-wide registry keyed by credential fingerprint (token_url + client_id + client_secret + scopes). Token caching now survives step re-creation between pipeline executions.

  2. Race condition / thundering herdoauthCacheEntry embeds a singleflight.Group; concurrent first-time fetches for the same credentials are coalesced to a single token endpoint call with double-checked locking inside.

  3. Multi-tenancy — each distinct set of credentials produces a unique cache key, so tenants naturally get isolated token entries without any additional code.

  4. httpClient timeout — context deadline already propagates to all HTTP calls (including token fetches); this is now documented on the field.

@intel352 intel352 merged commit 24e4b39 into main Feb 27, 2026
14 checks passed
@intel352 intel352 deleted the copilot/add-oauth-client-credentials-support branch February 27, 2026 03:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants