Skip to content
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
20 changes: 6 additions & 14 deletions internal/api/external.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,26 +334,18 @@ func (a *API) createAccountFromExternalIdentity(tx *storage.Connection, r *http.
Data: identityData,
}

// This is a little bit of a hack. Let me explain: When
// is_sso_user == true, it allows there to be different user
// rows with the same email address. Initially it was added to
// support SSO accounts, but at this point renaming the column
// or adding a new one requires re-indexing the table which is
// expensive and introduces a potentially unnecessary API
// surface change. It therefore set to true for other linking
// domains, not just SSO ones. This enables different linking
// domains to co-exist, such as when using
// GOTRUE_EXPERIMENTAL_PROVIDER_LINKING_DOMAINS="provider_a=social,provider_b=social".
isSSOUser := decision.LinkingDomain != "default"

// because params above sets no password, this method is not
// computationally hard so it can be used within a database
// transaction
user, terr = params.ToUserModel(isSSOUser)
if terr != nil {
return 0, nil, terr
}

// Re-use the user ID populated during triggerBeforeUserCreatedExternal
if userData.UserID != uuid.Nil {
user.ID = userData.UserID
}

if user, terr = a.signupNewUser(tx, user); terr != nil {
return 0, nil, terr
}
Expand All @@ -362,7 +354,7 @@ func (a *API) createAccountFromExternalIdentity(tx *storage.Connection, r *http.
return 0, nil, terr
}
user.Identities = append(user.Identities, *identity)

case models.AccountExists:
user = decision.User
identity = decision.Identities[0]
Expand Down
10 changes: 7 additions & 3 deletions internal/api/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ func (a *API) triggerAfterUserCreated(
return nil
}

// We still check tx because we want to make sure we aren't calling this
// trigger in code paths that haven't actually created the user yet.
if err := checkTX(conn); err != nil {
return err
}
Expand Down Expand Up @@ -113,6 +111,12 @@ func (a *API) triggerBeforeUserCreatedExternal(
if err != nil {
return err
}

// Attach pre-generated user ID to userData so downstream callbacks re-use it
if userData != nil && user != nil {
userData.UserID = user.ID
}

return a.triggerBeforeUserCreated(r, db, user)
}

Expand All @@ -122,4 +126,4 @@ func checkTX(conn *storage.Connection) error {
"unable to trigger hooks during transaction")
}
return nil
}
}
4 changes: 3 additions & 1 deletion internal/api/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"os"
"time"

"github.com/gofrs/uuid"
"github.com/supabase/auth/internal/utilities"
"golang.org/x/oauth2"
)
Expand Down Expand Up @@ -131,6 +132,7 @@ type Email struct {

// UserProvidedData is a struct that contains the user's data returned from the oauth provider
type UserProvidedData struct {
UserID uuid.UUID // Persists the pre-generated User ID across hook triggers and account creation (Issue #41309)
Emails []Email
Metadata *Claims
}
Expand Down Expand Up @@ -182,4 +184,4 @@ func makeRequest(ctx context.Context, tok *oauth2.Token, g *oauth2.Config, url s
}

return nil
}
}