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 }