emissary/internal/auth/thirdparty/authenticator.go

76 lines
1.7 KiB
Go

package thirdparty
import (
"context"
"net/http"
"strings"
"time"
"forge.cadoles.com/Cadoles/emissary/internal/auth"
"forge.cadoles.com/Cadoles/emissary/internal/jwk"
"github.com/lestrrat-go/jwx/v2/jwt"
"github.com/pkg/errors"
)
const DefaultAcceptableSkew = 5 * time.Minute
type (
GetKeySet func(context.Context) (jwk.Set, error)
GetTokenRole func(context.Context, jwt.Token) (string, error)
)
type Authenticator struct {
getKeySet GetKeySet
getTokenRole GetTokenRole
acceptableSkew time.Duration
}
// Authenticate implements auth.Authenticator.
func (a *Authenticator) Authenticate(ctx context.Context, r *http.Request) (auth.User, error) {
authorization := r.Header.Get("Authorization")
if authorization == "" {
return nil, errors.WithStack(auth.ErrUnauthenticated)
}
rawToken := strings.TrimPrefix(authorization, "Bearer ")
if rawToken == "" {
return nil, errors.WithStack(auth.ErrUnauthenticated)
}
keys, err := a.getKeySet(ctx)
if err != nil {
return nil, errors.WithStack(err)
}
token, err := parseToken(ctx, keys, rawToken, a.acceptableSkew)
if err != nil {
return nil, errors.WithStack(err)
}
rawRole, err := a.getTokenRole(ctx, token)
if err != nil {
return nil, errors.WithStack(err)
}
if !isValidRole(rawRole) {
return nil, errors.Errorf("invalid role '%s'", rawRole)
}
user := &User{
subject: token.Subject(),
role: Role(rawRole),
}
return user, nil
}
func NewAuthenticator(getKeySet GetKeySet, getTokenRole GetTokenRole, acceptableSkew time.Duration) *Authenticator {
return &Authenticator{
getTokenRole: getTokenRole,
getKeySet: getKeySet,
acceptableSkew: acceptableSkew,
}
}
var _ auth.Authenticator = &Authenticator{}