feat(auth): remote and local third-party authentication
Some checks reported warnings
arcad/emissary/pipeline/head This commit is unstable

This commit is contained in:
2023-07-26 07:14:49 -06:00
parent 42d49eb090
commit 8e88b5a7f1
13 changed files with 307 additions and 63 deletions

View File

@ -8,22 +8,25 @@ import (
"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"
"gitlab.com/wpetit/goweb/logger"
)
const DefaultAcceptableSkew = 5 * time.Minute
type (
GetKeySet func(context.Context) (jwk.Set, error)
GetTokenRole func(context.Context, jwt.Token) (string, error)
)
type Authenticator struct {
keys jwk.Set
issuer string
getKeySet GetKeySet
getTokenRole GetTokenRole
acceptableSkew time.Duration
}
// Authenticate implements auth.Authenticator.
func (a *Authenticator) Authenticate(ctx context.Context, r *http.Request) (auth.User, error) {
ctx = logger.With(r.Context(), logger.F("remoteAddr", r.RemoteAddr))
authorization := r.Header.Get("Authorization")
if authorization == "" {
return nil, errors.WithStack(auth.ErrUnauthenticated)
@ -34,37 +37,37 @@ func (a *Authenticator) Authenticate(ctx context.Context, r *http.Request) (auth
return nil, errors.WithStack(auth.ErrUnauthenticated)
}
token, err := parseToken(ctx, a.keys, a.issuer, rawToken, a.acceptableSkew)
keys, err := a.getKeySet(ctx)
if err != nil {
return nil, errors.WithStack(err)
}
rawRole, exists := token.Get(keyRole)
if !exists {
return nil, errors.New("could not find 'thumbprint' claim")
token, err := parseToken(ctx, keys, rawToken, a.acceptableSkew)
if err != nil {
return nil, errors.WithStack(err)
}
role, ok := rawRole.(string)
if !ok {
return nil, errors.Errorf("unexpected '%s' claim value: '%v'", keyRole, rawRole)
rawRole, err := a.getTokenRole(ctx, token)
if err != nil {
return nil, errors.WithStack(err)
}
if !isValidRole(role) {
return nil, errors.Errorf("invalid role '%s'", role)
if !isValidRole(rawRole) {
return nil, errors.Errorf("invalid role '%s'", rawRole)
}
user := &User{
subject: token.Subject(),
role: Role(role),
role: Role(rawRole),
}
return user, nil
}
func NewAuthenticator(keys jwk.Set, issuer string, acceptableSkew time.Duration) *Authenticator {
func NewAuthenticator(getKeySet GetKeySet, getTokenRole GetTokenRole, acceptableSkew time.Duration) *Authenticator {
return &Authenticator{
keys: keys,
issuer: issuer,
getTokenRole: getTokenRole,
getKeySet: getKeySet,
acceptableSkew: acceptableSkew,
}
}

View File

@ -11,15 +11,13 @@ import (
"github.com/pkg/errors"
)
const keyRole = "role"
func parseToken(ctx context.Context, keys jwk.Set, issuer string, rawToken string, acceptableSkew time.Duration) (jwt.Token, error) {
func parseToken(ctx context.Context, keys jwk.Set, rawToken string, acceptableSkew time.Duration) (jwt.Token, error) {
token, err := jwt.Parse(
[]byte(rawToken),
jwt.WithKeySet(keys, jws.WithRequireKid(false)),
jwt.WithIssuer(issuer),
jwt.WithValidate(true),
jwt.WithAcceptableSkew(acceptableSkew),
jwt.WithContext(ctx),
)
if err != nil {
return nil, errors.WithStack(err)
@ -28,18 +26,16 @@ func parseToken(ctx context.Context, keys jwk.Set, issuer string, rawToken strin
return token, nil
}
func GenerateToken(ctx context.Context, key jwk.Key, issuer, subject string, role Role) (string, error) {
const DefaultRoleKey string = "role"
func GenerateToken(ctx context.Context, key jwk.Key, subject string, role Role) (string, error) {
token := jwt.New()
if err := token.Set(jwt.SubjectKey, subject); err != nil {
return "", errors.WithStack(err)
}
if err := token.Set(jwt.IssuerKey, issuer); err != nil {
return "", errors.WithStack(err)
}
if err := token.Set(keyRole, role); err != nil {
if err := token.Set(DefaultRoleKey, role); err != nil {
return "", errors.WithStack(err)
}