63 lines
1.9 KiB
Go
63 lines
1.9 KiB
Go
|
|
// Package auth carries the authenticated principal across the HTTP stack
|
||
|
|
// so handlers do not re-decode the Pocket-ID session cookie on each call.
|
||
|
|
// The Pocket-ID verification lives in services/admin-api/internal/auth; the
|
||
|
|
// approval-service accepts a Principal already stamped by the gateway or
|
||
|
|
// its own future middleware wrap (Phase 2).
|
||
|
|
package auth
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"errors"
|
||
|
|
)
|
||
|
|
|
||
|
|
type ctxKey int
|
||
|
|
|
||
|
|
const (
|
||
|
|
principalKey ctxKey = iota
|
||
|
|
sessionKey
|
||
|
|
)
|
||
|
|
|
||
|
|
// Principal is the subject identifier (Pocket-ID `sub`) of the browser user
|
||
|
|
// driving the approval. Attestations will carry it as `performed_by.sub`.
|
||
|
|
type Principal struct {
|
||
|
|
Sub string
|
||
|
|
Groups []string
|
||
|
|
}
|
||
|
|
|
||
|
|
// ErrNoPrincipal is returned by MustPrincipal when the request context did
|
||
|
|
// not carry an authenticated principal.
|
||
|
|
var ErrNoPrincipal = errors.New("auth: request is not authenticated")
|
||
|
|
|
||
|
|
// WithPrincipal returns a copy of ctx carrying p.
|
||
|
|
func WithPrincipal(ctx context.Context, p Principal) context.Context {
|
||
|
|
return context.WithValue(ctx, principalKey, p)
|
||
|
|
}
|
||
|
|
|
||
|
|
// FromContext returns the Principal stamped by WithPrincipal.
|
||
|
|
func FromContext(ctx context.Context) (Principal, bool) {
|
||
|
|
p, ok := ctx.Value(principalKey).(Principal)
|
||
|
|
return p, ok
|
||
|
|
}
|
||
|
|
|
||
|
|
// MustFromContext is a convenience that returns ErrNoPrincipal when no
|
||
|
|
// principal is present.
|
||
|
|
func MustFromContext(ctx context.Context) (Principal, error) {
|
||
|
|
p, ok := FromContext(ctx)
|
||
|
|
if !ok || p.Sub == "" {
|
||
|
|
return Principal{}, ErrNoPrincipal
|
||
|
|
}
|
||
|
|
return p, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// WithSession binds the Pocket-ID `sid` claim to ctx. Approval-sessions
|
||
|
|
// scope themselves to the browser login via this value.
|
||
|
|
func WithSession(ctx context.Context, sid string) context.Context {
|
||
|
|
return context.WithValue(ctx, sessionKey, sid)
|
||
|
|
}
|
||
|
|
|
||
|
|
// SessionFromContext returns the browser sid carried in ctx.
|
||
|
|
func SessionFromContext(ctx context.Context) (string, bool) {
|
||
|
|
s, ok := ctx.Value(sessionKey).(string)
|
||
|
|
return s, ok && s != ""
|
||
|
|
}
|