Skip to content

Feat/link identity #2002

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
FROM golang:1.23.7-alpine3.20

ENV GO111MODULE=on
ENV CGO_ENABLED=0
ENV GOOS=linux

RUN apk add --no-cache make git bash
# Install make, git, bash, zsh, curl, and postgresql-client (for psql)
RUN apk add --no-cache make git bash zsh curl postgresql-client && \
# Install oh-my-zsh non-interactively
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended

WORKDIR /go/src/github.com/supabase/auth

Expand All @@ -13,6 +17,18 @@ COPY ./Makefile ./go.* ./
# Production dependencies
RUN make deps

# Development dependences
RUN go get github.com/githubnemo/CompileDaemon
RUN go install github.com/githubnemo/CompileDaemon
# Development dependencies
RUN go get github.com/githubnemo/CompileDaemon && \
go install github.com/githubnemo/CompileDaemon

# Create a default .env.docker file if needed
RUN touch .env.docker

# Copy the full source (including .git for version info if available)
COPY . /go/src/github.com/supabase/auth

# Build with a fallback version if git describe fails
RUN CGO_ENABLED=0 go build -ldflags "-X github.com/supabase/auth/internal/utilities.Version=$(git describe --tags 2>/dev/null || echo unspecified)" -buildvcs=false

# Command to start CompileDaemon with zsh as the shell
CMD ["zsh", "-c", "CompileDaemon --build=\"make build\" --directory=/go/src/github.com/supabase/auth --recursive=true -pattern=\"(.+\\.go|.+\\.env)\" -exclude=auth -exclude=auth-arm64 -exclude=.env --command=\"/go/src/github.com/supabase/auth/auth\""]
126 changes: 105 additions & 21 deletions cmd/migrate_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"net/url"
"os"
"strings"

"github.com/gobuffalo/pop/v6"
"github.com/gobuffalo/pop/v6/logging"
Expand All @@ -17,7 +18,7 @@ var EmbeddedMigrations embed.FS

var migrateCmd = cobra.Command{
Use: "migrate",
Long: "Migrate database strucutures. This will create new tables and add missing columns and indexes.",
Long: "Migrate database structures. This will create new tables and add missing columns and indexes.",
Run: migrate,
}

Expand All @@ -33,42 +34,60 @@ func migrate(cmd *cobra.Command, args []string) {
}

log := logrus.StandardLogger()
log.Infof("Starting migration with driver: %s", globalConfig.DB.Driver)

pop.Debug = false
pop.Debug = true
if globalConfig.Logging.Level != "" {
level, err := logrus.ParseLevel(globalConfig.Logging.Level)
if err != nil {
log.Fatalf("Failed to parse log level: %+v", err)
}
log.SetLevel(level)
if level == logrus.DebugLevel {
// Set to true to display query info
pop.Debug = true
}
if level != logrus.DebugLevel {
var noopLogger = func(lvl logging.Level, s string, args ...interface{}) {
}
// Hide pop migration logging
var noopLogger = func(lvl logging.Level, s string, args ...interface{}) {}
pop.SetLogger(noopLogger)
}
}

maskedURL := maskPassword(globalConfig.DB.URL)
log.Infof("Database URL: %s", maskedURL)
log.Infof("Using DB namespace: %s", globalConfig.DB.Namespace)

u, _ := url.Parse(globalConfig.DB.URL)
processedUrl := globalConfig.DB.URL
if len(u.Query()) != 0 {
processedUrl = fmt.Sprintf("%s&application_name=gotrue_migrations", processedUrl)
} else {
processedUrl = fmt.Sprintf("%s?application_name=gotrue_migrations", processedUrl)
}

if globalConfig.DB.Namespace != "" {
if !strings.Contains(processedUrl, "search_path") {
if strings.Contains(processedUrl, "?") {
processedUrl = fmt.Sprintf("%s&search_path=%s", processedUrl, globalConfig.DB.Namespace)
} else {
processedUrl = fmt.Sprintf("%s?search_path=%s", processedUrl, globalConfig.DB.Namespace)
}
}
}

log.Infof("Processed DB URL: %s", maskPassword(processedUrl))

deets := &pop.ConnectionDetails{
Dialect: globalConfig.DB.Driver,
URL: processedUrl,
}
deets.Options = map[string]string{
"migration_table_name": "schema_migrations",
"Namespace": globalConfig.DB.Namespace,
Options: map[string]string{
"migration_table_name": "schema_migrations",
"schema": globalConfig.DB.Namespace,
"Namespace": globalConfig.DB.Namespace,
},
}

log.Infof("Connection options: %v", deets.Options)

db, err := pop.NewConnection(deets)
if err != nil {
log.Fatalf("%+v", errors.Wrap(err, "opening db connection"))
Expand All @@ -79,39 +98,104 @@ func migrate(cmd *cobra.Command, args []string) {
log.Fatalf("%+v", errors.Wrap(err, "checking database connection"))
}

log.Debugf("Reading migrations from executable")
if globalConfig.DB.Namespace != "" {
log.Infof("Ensuring schema %s exists", globalConfig.DB.Namespace)
_, err = db.Store.Exec(fmt.Sprintf("CREATE SCHEMA IF NOT EXISTS %s", globalConfig.DB.Namespace))
if err != nil {
log.Errorf("❌ Error creating schema: %v", err)
}
}

log.Infof("Reading migrations from executable")
box, err := pop.NewMigrationBox(EmbeddedMigrations, db)
if err != nil {
log.Fatalf("%+v", errors.Wrap(err, "creating db migrator"))
}

mig := box.Migrator

log.Debugf("before status")
if globalConfig.DB.Namespace != "" {
_, err = db.Store.Exec(fmt.Sprintf("SET search_path TO %s", globalConfig.DB.Namespace))
if err != nil {
log.Errorf("❌ Error setting search_path: %v", err)
}
}

log.Infof("Before migration status")
if log.Level == logrus.DebugLevel {
err = mig.Status(os.Stdout)
if err != nil {
log.Fatalf("%+v", errors.Wrap(err, "migration status"))
log.Warnf("%+v", errors.Wrap(err, "migration status"))
}
}

// turn off schema dump
mig.SchemaPath = ""

count, err := mig.UpTo(0)
if err != nil {
log.Fatalf("%v", errors.Wrap(err, "running db migrations"))
} else {
log.WithField("count", count).Infof("GoTrue migrations applied successfully")
// Custom migration execution to continue on errors with success and failure counts
totalCount := 0 // Count of successfully applied migrations
failedCount := 0 // Count of migrations that failed
mtn := db.MigrationTableName()
dialectName := db.Dialect.Name() // Get the dialect name (e.g., "postgres", "mysql", "sqlite3")
for _, mi := range mig.UpMigrations.Migrations {
// Filter migrations by dialect compatibility
if mi.DBType != "all" && mi.DBType != dialectName {
log.Infof("Skipping migration %s (incompatible dialect: %s, expected: %s)", mi.Version, mi.DBType, dialectName)
continue
}

exists, err := db.Where("version = ?", mi.Version).Exists(mtn)
if err != nil {
log.Warnf("Error checking migration %s: %v", mi.Version, err)
failedCount++ // Increment failed count for check errors
continue
}
if exists {
log.Infof("Migration %s already applied", mi.Version)
continue
}

log.Infof("Applying migration: %s", mi.Version)
err = db.Transaction(func(tx *pop.Connection) error {
err := mi.Run(tx)
if err != nil {
return err
}
_, err = tx.Store.Exec(fmt.Sprintf("INSERT INTO %s (version) VALUES ('%s')", mtn, mi.Version))
if err != nil {
return fmt.Errorf("problem inserting migration version %s: %w", mi.Version, err)
}
return nil
})
if err != nil {
log.Errorf("❌ Error applying migration %s: %v", mi.Version, err)
failedCount++ // Increment failed count for application errors
continue // Continue to the next migration despite the error
}
totalCount++
log.Infof("✅ Successfully applied migration: %s", mi.Version)
}

log.Debugf("after status")
log.WithFields(logrus.Fields{
"success_count": totalCount,
"failed_count": failedCount,
}).Infof("GoTrue migrations completed (some may have been skipped due to errors)")

log.Infof("After migration status")
if log.Level == logrus.DebugLevel {
err = mig.Status(os.Stdout)
if err != nil {
log.Fatalf("%+v", errors.Wrap(err, "migration status"))
log.Warnf("%+v", errors.Wrap(err, "migration status"))
}
}
}

func maskPassword(dbURL string) string {
u, err := url.Parse(dbURL)
if err != nil {
return "[unparseable-url]"
}
if u.User != nil {
u.User = url.UserPassword(u.User.Username(), "********")
}
return u.String()
}
7 changes: 7 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ require (
)

require (
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/bits-and-blooms/bitset v1.10.0 // indirect
github.com/charmbracelet/x/ansi v0.4.2 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
Expand All @@ -54,9 +56,13 @@ require (
github.com/lestrrat-go/httprc v1.0.5 // indirect
github.com/lestrrat-go/iter v1.0.2 // indirect
github.com/lestrrat-go/option v1.0.1 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/speakeasy-api/openapi-overlay v0.9.0 // indirect
github.com/vmware-labs/yaml-jsonpath v0.3.2 // indirect
Expand Down Expand Up @@ -112,6 +118,7 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/charmbracelet/lipgloss v1.0.0
github.com/crewjam/httperr v0.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.13.0 // indirect
Expand Down
15 changes: 15 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyR
github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM=
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/badoux/checkmail v0.0.0-20170203135005-d0a759655d62 h1:vMqcPzLT1/mbYew0gM6EJy4/sCNy9lY9rmlFO+pPwhY=
Expand All @@ -33,6 +35,10 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg=
github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo=
github.com/charmbracelet/x/ansi v0.4.2 h1:0JM6Aj/g/KC154/gOP4vfxun0ff6itogDYk41kof+qk=
github.com/charmbracelet/x/ansi v0.4.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
Expand Down Expand Up @@ -273,6 +279,8 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/luna-duclos/instrumentedsql v1.1.3 h1:t7mvC0z1jUt5A0UQ6I/0H31ryymuQRnJcWCiqV3lSAA=
github.com/luna-duclos/instrumentedsql v1.1.3/go.mod h1:9J1njvFds+zN7y85EDhN9XNQLANWwZt2ULeIC8yMNYs=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
Expand All @@ -291,6 +299,8 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
Expand All @@ -305,6 +315,8 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 h1:j2kD3MT1z4PXCiUllUJF9mWUESr9TWKS7iEKsQ/IipM=
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
Expand Down Expand Up @@ -348,6 +360,9 @@ github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSz
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
Expand Down
1 change: 1 addition & 0 deletions internal/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ func NewAPIWithVersion(globalConfig *conf.GlobalConfiguration, db *storage.Conne
r.Get("/authorize", api.LinkIdentity)
r.Delete("/{identity_id}", api.DeleteIdentity)
})
r.Post("/identities/link_token", api.LinkIdentityWithIDToken)
})

r.With(api.requireAuthentication).Route("/factors", func(r *router) {
Expand Down
Loading