Skip to content

Commit

Permalink
feat: update primary key for identities table (#1311)
Browse files Browse the repository at this point in the history
## What kind of change does this PR introduce?
* We need to be able to have a consistent identifier to fetch an
identity from the database. The current approach for using a composite
primary key isn't sufficient and not ideal for exposing through the API.
* Remove the composite primary key on (`auth.identities.id`,
`auth.identities.provider`)
* Rename `auth.identities.id` to `auth.identities.provider_id`
* Add a new primary key called `auth.identities.id` 
* Add a unique constraint on (`auth.identities.provider_id`,
`auth.identities.provider`)

---------

Co-authored-by: Stojan Dimitrovski <sdimitrovski@gmail.com>
  • Loading branch information
kangmingtay and hf authored Nov 20, 2023
1 parent 9b93ac1 commit d8ec801
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 14 deletions.
2 changes: 1 addition & 1 deletion internal/api/admin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ func (ts *AdminTestSuite) TestAdminUserUpdate() {

for _, identity := range u.Identities {
// for email & phone identities, the providerId is the same as the userId
require.Equal(ts.T(), u.ID.String(), identity.ID)
require.Equal(ts.T(), u.ID.String(), identity.ProviderID)
require.Equal(ts.T(), u.ID, identity.UserID)
if identity.Provider == "email" {
require.Equal(ts.T(), newEmail, identity.IdentityData["email"])
Expand Down
14 changes: 9 additions & 5 deletions internal/models/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ import (
)

type Identity struct {
ID string `json:"id" db:"id"`
// returned as identity_id in JSON for backward compatibility with the interface exposed by the client library
// see https://github.com/supabase/gotrue-js/blob/c9296bbc27a2f036af55c1f33fca5930704bd021/src/lib/types.ts#L230-L240
ID uuid.UUID `json:"identity_id" db:"id"`
// returned as id in JSON for backward compatibility with the interface exposed by the client library
// see https://github.com/supabase/gotrue-js/blob/c9296bbc27a2f036af55c1f33fca5930704bd021/src/lib/types.ts#L230-L240
ProviderID string `json:"id" db:"provider_id"`
UserID uuid.UUID `json:"user_id" db:"user_id"`
IdentityData JSONMap `json:"identity_data,omitempty" db:"identity_data"`
Provider string `json:"provider" db:"provider"`
Expand All @@ -35,7 +40,7 @@ func NewIdentity(user *User, provider string, identityData map[string]interface{
now := time.Now()

identity := &Identity{
ID: providerId.(string),
ProviderID: providerId.(string),
UserID: user.ID,
IdentityData: identityData,
Provider: provider,
Expand Down Expand Up @@ -63,7 +68,7 @@ func (i *Identity) IsForSSOProvider() bool {
// FindIdentityById searches for an identity with the matching id and provider given.
func FindIdentityByIdAndProvider(tx *storage.Connection, providerId, provider string) (*Identity, error) {
identity := &Identity{}
if err := tx.Q().Where("id = ? AND provider = ?", providerId, provider).First(identity); err != nil {
if err := tx.Q().Where("provider_id = ? AND provider = ?", providerId, provider).First(identity); err != nil {
if errors.Cause(err) == sql.ErrNoRows {
return nil, IdentityNotFoundError{}
}
Expand Down Expand Up @@ -117,9 +122,8 @@ func (i *Identity) UpdateIdentityData(tx *storage.Connection, updates map[string
}
// pop doesn't support updates on tables with composite primary keys so we use a raw query here.
return tx.RawQuery(
"update "+(&pop.Model{Value: Identity{}}).TableName()+" set identity_data = ? where provider = ? and id = ?",
"update "+(&pop.Model{Value: Identity{}}).TableName()+" set identity_data = ? where id = ?",
i.IdentityData,
i.Provider,
i.ID,
).Exec()
}
13 changes: 5 additions & 8 deletions internal/models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -673,9 +673,7 @@ func (u *User) RemoveUnconfirmedIdentities(tx *storage.Connection, identity *Ide

// finally, remove all identities except the current identity being authenticated
for i := range u.Identities {
identityId := u.Identities[i].Provider + u.Identities[i].ID
identityIdToKeep := identity.Provider + identity.ID
if identityId != identityIdToKeep {
if u.Identities[i].ID != identity.ID {
if terr := tx.Destroy(&u.Identities[i]); terr != nil {
return terr
}
Expand Down Expand Up @@ -762,10 +760,9 @@ func (u *User) SoftDeleteUserIdentities(tx *storage.Connection) error {
if err := tx.RawQuery(
"update "+
(&pop.Model{Value: Identity{}}).TableName()+
" set id = ? where id = ? and provider = ?",
obfuscateIdentityId(identity),
" set provider_id = ? where id = ?",
obfuscateIdentityProviderId(identity),
identity.ID,
identity.Provider,
).Exec(); err != nil {
return err
}
Expand All @@ -787,6 +784,6 @@ func obfuscatePhone(u *User, phone string) string {
return obfuscateValue(u.ID, phone)[:15]
}

func obfuscateIdentityId(identity *Identity) string {
return obfuscateValue(identity.UserID, identity.Provider+":"+identity.ID)
func obfuscateIdentityProviderId(identity *Identity) string {
return obfuscateValue(identity.UserID, identity.Provider+":"+identity.ProviderID)
}
29 changes: 29 additions & 0 deletions migrations/20231117164230_add_id_pkey_identities.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
do $$
begin
if not exists(select *
from information_schema.columns
where table_schema = '{{ index .Options "Namespace" }}' and table_name='identities' and column_name='provider_id')
then
alter table if exists {{ index .Options "Namespace" }}.identities
rename column id to provider_id;
end if;
end$$;

alter table if exists {{ index .Options "Namespace" }}.identities
drop constraint if exists identities_pkey,
add column if not exists id uuid default gen_random_uuid() primary key;

do $$
begin
if not exists
(select constraint_name
from information_schema.table_constraints
where table_schema = '{{ index .Options "Namespace" }}'
and table_name = 'identities'
and constraint_name = 'identities_provider_id_provider_unique')
then
alter table if exists {{ index .Options "Namespace" }}.identities
add constraint identities_provider_id_provider_unique
unique(provider_id, provider);
end if;
end $$;

0 comments on commit d8ec801

Please sign in to comment.