63 lines
1.7 KiB
Go
63 lines
1.7 KiB
Go
package auth
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
)
|
|
|
|
type ctxKey struct{}
|
|
|
|
// WithUser stores the verified identity on the request context.
|
|
func WithUser(ctx context.Context, u User) context.Context {
|
|
return context.WithValue(ctx, ctxKey{}, u)
|
|
}
|
|
|
|
// FromContext returns the identity attached by the middleware.
|
|
func FromContext(ctx context.Context) (User, bool) {
|
|
u, ok := ctx.Value(ctxKey{}).(User)
|
|
return u, ok
|
|
}
|
|
|
|
// Middleware verifies the Authorization header and rejects requests that carry
|
|
// no usable credential.
|
|
type Middleware struct {
|
|
verifier Verifier
|
|
}
|
|
|
|
// NewMiddleware returns middleware backed by the verifier.
|
|
func NewMiddleware(v Verifier) *Middleware {
|
|
return &Middleware{verifier: v}
|
|
}
|
|
|
|
// Wrap protects h, making the verified identity available to it.
|
|
func (m *Middleware) Wrap(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
token, err := BearerToken(r.Header.Get("Authorization"))
|
|
if err != nil {
|
|
unauthorized(w, err)
|
|
return
|
|
}
|
|
user, err := m.verifier.Verify(r.Context(), token)
|
|
if err != nil {
|
|
unauthorized(w, err)
|
|
return
|
|
}
|
|
next.ServeHTTP(w, r.WithContext(WithUser(r.Context(), user)))
|
|
})
|
|
}
|
|
|
|
// WrapFunc protects a handler function.
|
|
func (m *Middleware) WrapFunc(next http.HandlerFunc) http.HandlerFunc {
|
|
return m.Wrap(next).ServeHTTP
|
|
}
|
|
|
|
func unauthorized(w http.ResponseWriter, err error) {
|
|
w.Header().Set("WWW-Authenticate", `Bearer realm="go-places"`)
|
|
// The response deliberately does not echo the reason.
|
|
if errors.Is(err, ErrMissingToken) {
|
|
http.Error(w, "unauthorized: a bearer token is required", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
http.Error(w, "unauthorized: the token was rejected", http.StatusUnauthorized)
|
|
}
|