Context-aware OAuth2 token sources for Go.
The standard golang.org/x/oauth2 package does not propagate context through
token refresh, making it impossible to respect deadlines and cancellations
during OAuth2 operations
(golang/oauth2#262).
oauthctx solves this with a minimal, context-aware TokenSource interface
that wraps the standard library rather than replacing it.
go get github.com/TelpeNight/oauthctximport (
"golang.org/x/oauth2"
"github.com/TelpeNight/oauthctx"
)
conf := oauthctx.NewConfig(&oauth2.Config{
ClientID: "client-id",
ClientSecret: "client-secret",
Endpoint: oauth2.Endpoint{TokenURL: "https://auth.example.com/token"},
Scopes: []string{"scope1"},
})
// TokenSource caches the token and refreshes it automatically.
ts := conf.TokenSource(&oauth2.Token{RefreshToken: refreshToken})
// Or get an HTTP client directly:
client := conf.Client(&oauth2.Token{RefreshToken: refreshToken})import (
"golang.org/x/oauth2/clientcredentials"
"github.com/TelpeNight/oauthctx"
)
creds := oauthctx.NewClientCredentials(&clientcredentials.Config{
ClientID: "client-id",
ClientSecret: "client-secret",
TokenURL: "https://auth.example.com/token",
Scopes: []string{"scope1"},
})
client := creds.Client()If you already have an oauth2-package type and just need context support:
import (
"golang.org/x/oauth2/clientcredentials"
"github.com/TelpeNight/oauthctx"
)
existing := &clientcredentials.Config{ /* ... */ }
// AdoptTokenSourceWithContext wraps types with a Token(ctx) method.
ts := oauthctx.AdoptTokenSourceWithContext(existing)
ts = oauthctx.ReuseTokenSource(nil, ts) // add caching
client := oauthctx.NewClient(ts)To force a ReuseTokenSource to bypass its cache and fetch a new token:
ctx := oauthctx.WithExpiredToken(context.Background())
token, err := ts.TokenContext(ctx)import (
"golang.org/x/oauth2"
"google.golang.org/grpc/credentials"
gcred "google.golang.org/grpc/credentials/google"
"github.com/TelpeNight/oauthctx"
grpcctx "github.com/TelpeNight/oauthctx/grpc"
)
conf := oauthctx.NewConfig(&oauth2.Config{ /* ... */ })
ts := conf.TokenSource(&oauth2.Token{RefreshToken: refreshToken})
// grpcctx.TokenSource forwards each RPC's context to token refresh,
// so deadlines and cancellations propagate correctly.
var bundle credentials.Bundle = gcred.NewDefaultCredentialsWithOptions(
gcred.DefaultCredentialsOptions{
PerRPCCreds: &grpcctx.TokenSource{TokenSource: ts},
},
)
// use bundle to create a gRPC connectionThe aim of this library is to implement only the smallest possible subset of
functionality for context support, reusing golang.org/x/oauth2 as much as
possible. This is achieved through three strategies:
- Adopt existing types — types that implement
Oauth2TokenConfigorOauth2TokenSourceWithContextcan be wrapped without changing their token logic. SeeAdoptTokenConfigandAdoptTokenSourceWithContextinconvert.go. - Reuse high-level transport — tokens are fetched through the
context-aware
TokenSourceand injected into the existing oauth2 transport implementation viaoauth2.StaticTokenSource. Seetransport.goandgrpc/credentials.go. - Reimplement a focused subset — only
ReuseTokenSourceandtokenRefresherare reimplemented, as they are the exact point where context support cannot be delegated to the upstream library.
All configuration is provided via option functions (RequestFlowOp,
TokenSourceOp, ClientOp) — no context.WithValue mess. All functions
mirror the golang.org/x/oauth2 API and should feel familiar. The library is
used in production.
If something is missing, feel free to open an issue or make a PR. Everything should be adoptable with this approach.