package authtransport
import (
"errors"
"net/http"
)
type TokenSource interface {
Token() (string, error)
}
type Transport struct {
Source TokenSource
Base http.RoundTripper
}
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
if t.Source == nil {
return nil, errors.New("authtransport: nil TokenSource")
}
token, err := t.Source.Token()
if err != nil {
return nil, err
}
// Never mutate the caller's request; clone before setting headers.
cloned := req.Clone(req.Context())
cloned.Header.Set("Authorization", "Bearer "+token)
return t.base().RoundTrip(cloned)
}
func (t *Transport) base() http.RoundTripper {
if t.Base != nil {
return t.Base
}
return http.DefaultTransport
}
package authtransport
import (
"sync"
"time"
)
type ExpiringSource interface {
Fetch() (token string, expiry time.Time, err error)
}
type CachingSource struct {
Inner ExpiringSource
expiryDelta time.Duration
mu sync.Mutex
token string
expiry time.Time
}
func NewCachingSource(inner ExpiringSource) *CachingSource {
return &CachingSource{Inner: inner, expiryDelta: 30 * time.Second}
}
func (c *CachingSource) Token() (string, error) {
c.mu.Lock()
defer c.mu.Unlock()
if c.token != "" && time.Now().Before(c.expiry.Add(-c.expiryDelta)) {
return c.token, nil
}
token, expiry, err := c.Inner.Fetch()
if err != nil {
return "", err
}
c.token = token
c.expiry = expiry
return token, nil
}
package authtransport
import (
"net/http"
"time"
)
func NewClient(source TokenSource, base http.RoundTripper) *http.Client {
return &http.Client{
Timeout: 15 * time.Second,
Transport: &Transport{
Source: source,
Base: base,
},
}
}
type StaticSource string
func (s StaticSource) Token() (string, error) {
return string(s), nil
}
func Example() (*http.Response, error) {
client := NewClient(StaticSource("abc123"), nil)
return client.Get("https://api.example.com/v1/me")
}
This snippet shows how to wrap Go's http.RoundTripper to transparently attach authentication to every outgoing request, without touching call sites. The RoundTripper interface is the lowest-level extension point in net/http: it takes a *http.Request and returns a *http.Response. By implementing it, one can compose behavior like logging, retries, or auth as layers around the real transport — the classic decorator pattern applied to HTTP.
In authtransport.go, the Transport struct holds a Source that yields tokens and a Base http.RoundTripper that does the actual network work. The RoundTrip method is the heart of the decorator. A crucial detail is that RoundTrip must not mutate the request it is given — the contract says the incoming *http.Request may be reused or inspected by the caller. The code respects this by using req.Clone(req.Context()) to produce a private copy before calling Header.Set. It then delegates to t.base(), which falls back to http.DefaultTransport when Base is nil, mirroring how the standard library behaves.
The token itself comes from a TokenSource abstraction so the transport does not care whether the credential is static, loaded from disk, or refreshed against an OAuth endpoint. In tokensource.go, CachingSource wraps another source and caches the result until shortly before expiry. A sync.Mutex guards the cached token so concurrent requests do not trigger a stampede of refreshes, and an expiryDelta buffer refreshes early to avoid handing out a token that expires mid-flight. This early-refresh trade-off costs a few unnecessary refreshes but prevents 401s from clock skew and in-flight latency.
In client.go, NewClient assembles an *http.Client whose Transport is the decorator, so ordinary calls like client.Get(url) are authenticated automatically. Because the decorator is just an http.RoundTripper, it stacks cleanly with other transports. Note that returning an error from RoundTrip before the request is sent means the body is never consumed, which is the correct behavior on token-fetch failure. Reach for this pattern whenever auth logic would otherwise be duplicated across many call sites.
Related snips
package api
import (
"net/http"
"runtime/debug"
)
Expose build metadata for debugging deploys
payload = {
sub: user.id,
iss: 'https://auth.example.com',
aud: 'codesnips-api',
exp: 15.minutes.from_now.to_i,
iat: Time.now.to_i,
JWT issuance and verification without common footguns
export interface RetryOptions {
retries: number;
baseMs: number;
maxMs: number;
signal?: AbortSignal;
onRetry?: (attempt: number, delay: number, err: unknown) => void;
Exponential backoff with jitter for retries
package dbutil
import (
"context"
"github.com/jackc/pgconn"
Retry Postgres serialization failures with bounded attempts
use crossbeam::channel::unbounded;
use std::thread;
fn main() {
let (tx, rx) = unbounded();
Crossbeam for advanced concurrent data structures
// Creating a Promise
const myPromise = new Promise((resolve, reject) => {
const success = true;
setTimeout(() => {
if (success) {
Promises and async/await patterns for asynchronous JavaScript
Share this code
Here's the card — post it anywhere.