emissary/internal/auth/agent/authenticator.go

106 lines
2.4 KiB
Go
Raw Normal View History

package agent
import (
"context"
"net/http"
"strings"
"time"
"forge.cadoles.com/Cadoles/emissary/internal/auth"
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
"github.com/lestrrat-go/jwx/v2/jws"
"github.com/lestrrat-go/jwx/v2/jwt"
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/logger"
)
const DefaultAcceptableSkew = 5 * time.Minute
type Authenticator struct {
repo datastore.AgentRepository
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)
}
rawToken := strings.TrimPrefix(authorization, "Bearer ")
if rawToken == "" {
return nil, errors.WithStack(auth.ErrUnauthenticated)
}
token, err := jwt.Parse([]byte(rawToken), jwt.WithVerify(false))
if err != nil {
return nil, errors.WithStack(err)
}
rawThumbprint, exists := token.Get(keyThumbprint)
if !exists {
return nil, errors.Errorf("could not find '%s' claim", keyThumbprint)
}
thumbrint, ok := rawThumbprint.(string)
if !ok {
return nil, errors.Errorf("unexpected '%s' claim value: '%v'", keyThumbprint, rawThumbprint)
}
agents, _, err := a.repo.Query(
ctx,
datastore.WithAgentQueryThumbprints(thumbrint),
datastore.WithAgentQueryLimit(1),
)
if err != nil {
return nil, errors.WithStack(err)
}
if len(agents) != 1 {
return nil, errors.Errorf("unexpected number of found agents: '%d'", len(agents))
}
agent, err := a.repo.Get(
ctx,
agents[0].ID,
)
if err != nil {
return nil, errors.WithStack(err)
}
_, err = jwt.Parse(
[]byte(rawToken),
jwt.WithKeySet(agent.KeySet.Set, jws.WithRequireKid(false)),
jwt.WithValidate(true),
jwt.WithAcceptableSkew(a.acceptableSkew),
)
if err != nil {
return nil, errors.WithStack(err)
}
contactedAt := time.Now()
agent, err = a.repo.Update(ctx, agent.ID, datastore.WithAgentUpdateContactedAt(contactedAt))
if err != nil {
return nil, errors.WithStack(err)
}
user := &User{
agent: agent,
}
return user, nil
}
func NewAuthenticator(repo datastore.AgentRepository, acceptableSkew time.Duration) *Authenticator {
return &Authenticator{
repo: repo,
acceptableSkew: acceptableSkew,
}
}
var _ auth.Authenticator = &Authenticator{}