Skip to content

Commit

Permalink
feat: Add remoteoauth helpers (TokenAuthTransport and `TokenAuthEdi…
Browse files Browse the repository at this point in the history
…tor`) (#1875)

These helpers can be used to inject dynamic tokens into requests.

`TokenAuthTransport` is a `http.RoundTripper`, with optional custom rewriting capability
`TokenAuthEditor(oauth2.TokenSource)` returns a `func(context.Context, *http.Request) error` to be used as a `RequestEditorFn` in openapi clients.
  • Loading branch information
disq authored Aug 27, 2024
1 parent 11aaab4 commit bb1be84
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 0 deletions.
26 changes: 26 additions & 0 deletions helpers/remoteoauth/tokenautheditor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package remoteoauth

import (
"context"
"fmt"
"net/http"

"golang.org/x/oauth2"
)

// TokenAuthEditor returns a custom RequestEditorFn to inject the OAuth2 token
func TokenAuthEditor(tokenSource oauth2.TokenSource) func(context.Context, *http.Request) error {
return func(_ context.Context, req *http.Request) error {
token, err := tokenSource.Token()
if err != nil {
return fmt.Errorf("remoteoauth: failed to get token: %w", err)
}
if token == nil {
return errNilToken
}

// Inject the token as the Authorization header
token.SetAuthHeader(req)
return nil
}
}
56 changes: 56 additions & 0 deletions helpers/remoteoauth/tokenauthtransport.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package remoteoauth

import (
"errors"
"fmt"
"net/http"

"golang.org/x/oauth2"
)

// TokenAuthTransport is a custom http.RoundTripper to inject the OAuth2 token
type TokenAuthTransport struct {
TokenSource oauth2.TokenSource // required

BaseTransport http.RoundTripper
Rewriter RewriterFunc
}

var errNilToken = errors.New("remoteoauth: nil token")

// RewriterFunc is a function that can be used to rewrite the request before it is sent
type RewriterFunc func(*http.Request, oauth2.Token)

// RoundTrip executes a single HTTP transaction and injects the token into the request header
func (t *TokenAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
reqCopy := req.Clone(req.Context())

token, err := t.TokenSource.Token()
if err != nil {
return nil, fmt.Errorf("remoteoauth: failed to get token: %w", err)
}
if token == nil {
return nil, errNilToken
}

if t.Rewriter != nil {
t.Rewriter(reqCopy, *token)
} else {
// Inject the token as the Authorization header
token.SetAuthHeader(reqCopy)
}

transport := t.BaseTransport
if transport == nil {
transport = http.DefaultTransport
}

return transport.RoundTrip(reqCopy)
}

// NewAuthTransport creates a new TokenAuthTransport with the provided TokenSource
func NewAuthTransport(ts oauth2.TokenSource) http.RoundTripper {
return &TokenAuthTransport{
TokenSource: ts,
}
}

1 comment on commit bb1be84

@github-actions
Copy link

Choose a reason for hiding this comment

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

⏱️ Benchmark results

  • Glob-8 ns/op: 93.18

Please sign in to comment.