stillbox/pkg/auth/apikey.go

50 lines
1.3 KiB
Go

package auth
import (
"context"
"crypto/sha256"
"encoding/base64"
"time"
"dynatron.me/x/stillbox/pkg/database"
"github.com/google/uuid"
"github.com/rs/zerolog/log"
)
type apiKeyAuth interface {
// CheckAPIKey validates the provided key and returns the API owner's UserID.
// An error is returned if validation fails for any reason.
CheckAPIKey(ctx context.Context, key string) (*UserID, error)
}
func (a *Auth) CheckAPIKey(ctx context.Context, key string) (*UserID, error) {
keyUuid, err := uuid.Parse(key)
if err != nil {
log.Error().Str("apikey", key).Msg("cannot parse key")
return nil, ErrBadRequest
}
db := database.FromCtx(ctx)
hash := sha256.Sum256([]byte(keyUuid.String()))
b64hash := base64.StdEncoding.EncodeToString(hash[:])
apik, err := db.GetAPIKey(ctx, b64hash)
if err != nil {
if database.IsNoRows(err) {
log.Error().Str("apikey", keyUuid.String()).Msg("no such key")
return nil, ErrUnauthorized
}
log.Error().Str("apikey", keyUuid.String()).Err(err).Msg("error looking up key")
return nil, ErrInternal
}
if (apik.Disabled != nil && *apik.Disabled) || (apik.Expires.Valid && time.Now().After(apik.Expires.Time)) {
log.Error().Str("key", apik.ApiKey).Msg("key disabled")
return nil, ErrUnauthorized
}
owner := UserID(apik.Owner)
return &owner, nil
}