storj/satellite/console/consoleauth/service.go
Cameron 0633aca607 satellite/console: create new consoleauth service
We want to send email verification reminders to users from the satellite
core, but some of the functionality required to do so exists in the
satellite console service. We could simply import the console service
into the core to achieve this, but the service requires a lot of
dependencies that would go unused just to be able to send these emails.

Instead, we break out the needed functionality into a new service which
can be imported separately by the console service and the future email
chore.

The consoleauth service creates, signs, and checks the expiration of auth
tokens.

Change-Id: I2ad794b7fd256f8af24c1a8d73a203d508069078
2022-05-13 16:27:07 +00:00

91 lines
2.0 KiB
Go

// Copyright (C) 2022 Storj Labs, Inc.
// See LICENSE for copying information.
package consoleauth
import (
"context"
"encoding/base64"
"time"
"github.com/spacemonkeygo/monkit/v3"
"storj.io/common/uuid"
)
var mon = monkit.Package()
// Config contains configuration parameters for console auth.
type Config struct {
TokenExpirationTime time.Duration `help:"expiration time for auth tokens, account recovery tokens, and activation tokens" default:"24h"`
}
// Service handles creating, signing, and checking the expiration of auth tokens.
type Service struct {
config Config
Signer
}
// NewService creates a new consoleauth service.
func NewService(config Config, signer Signer) *Service {
return &Service{
config: config,
Signer: signer,
}
}
// Signer creates signature for provided data.
type Signer interface {
Sign(data []byte) ([]byte, error)
}
// CreateToken creates a new auth token.
func (s *Service) CreateToken(ctx context.Context, id uuid.UUID, email string) (_ string, err error) {
defer mon.Task()(&ctx)(&err)
claims := &Claims{
ID: id,
Expiration: time.Now().Add(s.config.TokenExpirationTime),
}
if email != "" {
claims.Email = email
}
return s.createToken(ctx, claims)
}
// createToken creates string representation.
func (s *Service) createToken(ctx context.Context, claims *Claims) (_ string, err error) {
defer mon.Task()(&ctx)(&err)
json, err := claims.JSON()
if err != nil {
return "", err
}
token := Token{Payload: json}
err = s.SignToken(&token)
if err != nil {
return "", err
}
return token.String(), nil
}
// SignToken signs token.
func (s *Service) SignToken(token *Token) error {
encoded := base64.URLEncoding.EncodeToString(token.Payload)
signature, err := s.Signer.Sign([]byte(encoded))
if err != nil {
return err
}
token.Signature = signature
return nil
}
// IsExpired returns whether token is expired.
func (s *Service) IsExpired(now, tokenCreatedAt time.Time) bool {
return now.Sub(tokenCreatedAt) > s.config.TokenExpirationTime
}