Skip to content

feat(auth): add OAuth2 grant-type packages with keychain token caching - #91

Merged
wesleymccollam merged 6 commits into
v1300from
feat/human-auth-oauth-flows
Jul 30, 2026
Merged

feat(auth): add OAuth2 grant-type packages with keychain token caching#91
wesleymccollam merged 6 commits into
v1300from
feat/human-auth-oauth-flows

Conversation

@wesleymccollam

Copy link
Copy Markdown
Contributor

Summary

Adds OAuth2 support to the PingFederate Go client so callers can obtain an oauth2.TokenSource for the PingFederate admin API using any of three grant types, with automatic token caching in the OS keychain.

  • config/ — high-level, fluent Configuration builder that derives OAuth2 endpoints from the runtime base URL and wires up each grant flow (authorization_code with PKCE, device_code per RFC 8628, and client_credentials), including the loopback-redirect handling and HTML auth-result page for the browser flow.
  • oauth2/ — grant-type enum, token-endpoint auth methods, endpoint derivation, and a keychain-backed TokenStorage implementation so tokens persist and silently refresh across runs.
  • utils/browser/ — cross-platform browser launcher for the authorization_code flow.
  • examples/ — runnable end-to-end examples for all three grant types, each demonstrating how to inject a custom HTTP client into the token exchange and call the admin API.

The HTTP client used for the token exchange is injected via oauth2.HTTPClient on the context, so callers control TLS/transport for both the token flow and the admin API client.

Scope / compatibility

  • Purely additive: only go.mod/go.sum (new dependencies) and a new README/Makefile/CONTRIBUTING are touched among existing files; no changes to the generated configurationapi package.
  • Targets the v1300 module line.

Testing

  • Unit tests across config/, oauth2/, and utils/browser/ pass; gofmt/go vet clean; go mod tidy is a no-op.
  • All three grant-type examples were run end-to-end against a live PingFederate 13.0.0.6 instance and successfully read the server version.
  • Verified against the downstream terraform-provider-pingfederate build (local module replace): devchecknotest passes clean (build, generate, golangci-lint, tfproviderlint, tflint, terrafmt, impi, vet).

…, device_code, and client_credentials grant types and keychain token caching

@henryrecker-pingidentity henryrecker-pingidentity left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Some minor comments

Comment thread config/html/auth_result.html Outdated
Comment thread config/hooks.go
Comment thread examples/authorization_code/README.md Outdated
Comment thread go.mod Outdated
Comment thread Makefile
Comment thread config/pingfederate.go
// Configuration represents the OAuth2 authentication configuration for the PingFederate Go
// Client SDK. It holds the runtime endpoint and the credentials for the configured grant type,
// and produces an oauth2.TokenSource via TokenSource. Populate it using the builder methods (With...).
type Configuration struct {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We now have two separate configuration objects in the client - see configurationapi/configuration.go

We need to consolidate and avoid having two separate ways to build a client

Comment thread config/client.go Outdated
// calls. It returns the client along with a context carrying the token source
// (configurationapi.ContextOAuth2) so callers can pass the returned context directly to generated
// API methods, without separately constructing and wiring a configurationapi.Configuration.
func (c *Configuration) NewAPIClient(ctx context.Context, adminAPIURL string, httpClient *http.Client) (*configurationapi.APIClient, context.Context, error) {

@henryrecker-pingidentity henryrecker-pingidentity Jul 29, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This pattern seems too different from what we've established with our clients.

Compare to pingone-go-client's structure - you create a Configuration, pass it into NewAPIClient, and then you can just use that client directly. Here we're expecting the user to pass around this auth context everywhere on their own.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@wesleymccollam To clarify on the latest changes, the examples for auth code and device code still have the user inserting an http client into the context, while also passing it in to the call to create the API client.

Currently you just directly set the HTTPClient field on the configuration struct - https://github.com/pingidentity/terraform-provider-pingfederate/blob/7d619e09230fde0571455bb4277ad72acd5369f4/internal/provider/provider.go#L688

Can we unify these, or update the examples to the expected pattern? It just seems a little all over the place.

@wesleymccollam
wesleymccollam merged commit 04a71ab into v1300 Jul 30, 2026
4 checks passed
@wesleymccollam
wesleymccollam deleted the feat/human-auth-oauth-flows branch July 30, 2026 18:42
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.

2 participants