github.com/supabase/cli@v1.168.1/internal/gen/keys/keys.go (about)

     1  package keys
     2  
     3  import (
     4  	"context"
     5  	"crypto/sha256"
     6  	"encoding/hex"
     7  	"fmt"
     8  	"os"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/go-errors/errors"
    13  	"github.com/go-git/go-git/v5"
    14  	"github.com/golang-jwt/jwt/v5"
    15  	"github.com/spf13/afero"
    16  	"github.com/supabase/cli/internal/utils"
    17  )
    18  
    19  type CustomClaims struct {
    20  	Ref  string `json:"ref"`
    21  	Role string `json:"role"`
    22  	jwt.RegisteredClaims
    23  }
    24  
    25  func NewJWTToken(ref, role string, expiry time.Time) *jwt.Token {
    26  	claims := CustomClaims{
    27  		ref,
    28  		role,
    29  		jwt.RegisteredClaims{
    30  			ExpiresAt: jwt.NewNumericDate(expiry),
    31  			Issuer:    "supabase",
    32  		},
    33  	}
    34  	return jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    35  }
    36  
    37  type CustomName struct {
    38  	DbHost         string `env:"db.host,default=NEXT_PUBLIC_SUPABASE_URL"`
    39  	DbPassword     string `env:"db.password,default=SUPABASE_DB_PASSWORD"`
    40  	JWTSecret      string `env:"db.password,default=SUPABASE_AUTH_JWT_SECRET"`
    41  	AnonKey        string `env:"auth.anon_key,default=SUPABASE_AUTH_ANON_KEY"`
    42  	ServiceRoleKey string `env:"auth.service_role_key,default=SUPABASE_AUTH_SERVICE_ROLE_KEY"`
    43  }
    44  
    45  func Run(ctx context.Context, projectRef, format string, names CustomName, fsys afero.Fs) error {
    46  	branch := GetGitBranch(fsys)
    47  	if err := GenerateSecrets(ctx, projectRef, branch, fsys); err != nil {
    48  		return err
    49  	}
    50  	return utils.EncodeOutput(format, os.Stdout, map[string]string{
    51  		names.DbHost:         fmt.Sprintf("%s-%s.fly.dev", projectRef, branch),
    52  		names.DbPassword:     utils.Config.Db.Password,
    53  		names.JWTSecret:      utils.Config.Auth.JwtSecret,
    54  		names.AnonKey:        utils.Config.Auth.AnonKey,
    55  		names.ServiceRoleKey: utils.Config.Auth.ServiceRoleKey,
    56  	})
    57  }
    58  
    59  func GenerateSecrets(ctx context.Context, projectRef, branch string, fsys afero.Fs) error {
    60  	// Load JWT secret from api
    61  	resp, err := utils.GetSupabase().GetPostgRESTConfigWithResponse(ctx, projectRef)
    62  	if err != nil {
    63  		return errors.Errorf("failed to get postgrest config: %w", err)
    64  	}
    65  	if resp.JSON200 == nil {
    66  		return errors.New("Unexpected error retrieving JWT secret: " + string(resp.Body))
    67  	}
    68  	utils.Config.Auth.JwtSecret = *resp.JSON200.JwtSecret
    69  	// Generate database password
    70  	key := strings.Join([]string{
    71  		projectRef,
    72  		utils.Config.Auth.JwtSecret,
    73  		branch,
    74  	}, ":")
    75  	hash := sha256.Sum256([]byte(key))
    76  	utils.Config.Db.Password = hex.EncodeToString(hash[:])
    77  	// Generate JWT tokens
    78  	expiry := time.Now().AddDate(10, 0, 0)
    79  	anonToken := NewJWTToken(projectRef, "anon", expiry)
    80  	utils.Config.Auth.AnonKey, err = anonToken.SignedString([]byte(utils.Config.Auth.JwtSecret))
    81  	if err != nil {
    82  		return errors.Errorf("failed to sign anon key: %w", err)
    83  	}
    84  	serviceToken := NewJWTToken(projectRef, "service_role", expiry)
    85  	utils.Config.Auth.ServiceRoleKey, err = serviceToken.SignedString([]byte(utils.Config.Auth.JwtSecret))
    86  	if err != nil {
    87  		return errors.Errorf("failed to sign service_role key: %w", err)
    88  	}
    89  	return nil
    90  }
    91  
    92  func GetGitBranch(fsys afero.Fs) string {
    93  	return GetGitBranchOrDefault("main", fsys)
    94  }
    95  
    96  func GetGitBranchOrDefault(def string, fsys afero.Fs) string {
    97  	head := os.Getenv("GITHUB_HEAD_REF")
    98  	if len(head) > 0 {
    99  		return head
   100  	}
   101  	opts := &git.PlainOpenOptions{DetectDotGit: true}
   102  	if repo, err := git.PlainOpenWithOptions(".", opts); err == nil {
   103  		if ref, err := repo.Head(); err == nil {
   104  			return ref.Name().Short()
   105  		}
   106  	}
   107  	return def
   108  }