Skip to content
This repository was archived by the owner on Sep 8, 2025. It is now read-only.

Commit 3570ace

Browse files
committed
fix all link identity methods
1 parent 6563daf commit 3570ace

File tree

4 files changed

+71
-19
lines changed

4 files changed

+71
-19
lines changed

supabase_auth/_async/gotrue_client.py

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
model_validate,
3333
parse_auth_otp_response,
3434
parse_auth_response,
35+
parse_link_identity_response,
3536
parse_sso_response,
3637
parse_user_response,
3738
)
@@ -70,6 +71,7 @@
7071
SignUpWithPasswordCredentials,
7172
Subscription,
7273
UserAttributes,
74+
UserIdentity,
7375
UserResponse,
7476
VerifyOtpParams,
7577
)
@@ -366,10 +368,14 @@ async def sign_in_with_oauth(
366368
params["redirect_to"] = redirect_to
367369
if scopes:
368370
params["scopes"] = scopes
369-
url = await self._get_url_for_provider(provider, params)
371+
url = await self._get_url_for_provider(
372+
f"{self._url}/authorize", provider, params
373+
)
370374
return OAuthResponse(provider=provider, url=url)
371375

372-
async def link_identity(self, credentials):
376+
async def link_identity(
377+
self, credentials: SignInWithOAuthCredentials
378+
) -> OAuthResponse:
373379
provider = credentials.get("provider")
374380
options = credentials.get("options", {})
375381
redirect_to = options.get("redirect_to")
@@ -379,23 +385,40 @@ async def link_identity(self, credentials):
379385
params["redirect_to"] = redirect_to
380386
if scopes:
381387
params["scopes"] = scopes
382-
params["skip_browser_redirect"] = True
388+
params["skip_http_redirect"] = "true"
389+
url = await self._get_url_for_provider(
390+
"user/identities/authorize", provider, params
391+
)
383392

384-
url = await self._get_url_for_provider(provider, params)
385-
return OAuthResponse(provider=provider, url=url)
393+
session = await self.get_session()
394+
if not session:
395+
raise AuthSessionMissingError()
396+
397+
response = await self._request(
398+
method="GET",
399+
path=url,
400+
jwt=session.access_token,
401+
xform=parse_link_identity_response,
402+
)
403+
return OAuthResponse(provider=provider, url=response.url)
386404

387405
async def get_user_identities(self):
388-
response = self.get_user()
406+
response = await self.get_user()
389407
return (
390408
IdentitiesResponse(identities=response.user.identities)
391409
if response.user
392410
else AuthSessionMissingError()
393411
)
394412

395-
async def unlink_identity(self, identity):
413+
async def unlink_identity(self, identity: UserIdentity):
414+
session = await self.get_session()
415+
if not session:
416+
raise AuthSessionMissingError()
417+
396418
return await self._request(
397-
"POST",
398-
f"/user/identities/{identity.id}",
419+
"DELETE",
420+
f"user/identities/{identity.identity_id}",
421+
jwt=session.access_token,
399422
)
400423

401424
async def sign_in_with_otp(
@@ -975,6 +998,7 @@ def _is_implicit_grant_flow(self, url: str) -> bool:
975998

976999
async def _get_url_for_provider(
9771000
self,
1001+
url: str,
9781002
provider: Provider,
9791003
params: Dict[str, str],
9801004
) -> str:
@@ -992,7 +1016,7 @@ async def _get_url_for_provider(
9921016

9931017
params["provider"] = provider
9941018
query = urlencode(params)
995-
return f"{self._url}/authorize?{query}"
1019+
return f"{url}?{query}"
9961020

9971021
def _decode_jwt(self, jwt: str) -> DecodedJWTDict:
9981022
"""

supabase_auth/_sync/gotrue_client.py

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
model_validate,
3333
parse_auth_otp_response,
3434
parse_auth_response,
35+
parse_link_identity_response,
3536
parse_sso_response,
3637
parse_user_response,
3738
)
@@ -70,6 +71,7 @@
7071
SignUpWithPasswordCredentials,
7172
Subscription,
7273
UserAttributes,
74+
UserIdentity,
7375
UserResponse,
7476
VerifyOtpParams,
7577
)
@@ -366,10 +368,10 @@ def sign_in_with_oauth(
366368
params["redirect_to"] = redirect_to
367369
if scopes:
368370
params["scopes"] = scopes
369-
url = self._get_url_for_provider(provider, params)
371+
url = self._get_url_for_provider(f"{self._url}/authorize", provider, params)
370372
return OAuthResponse(provider=provider, url=url)
371373

372-
def link_identity(self, credentials):
374+
def link_identity(self, credentials: SignInWithOAuthCredentials) -> OAuthResponse:
373375
provider = credentials.get("provider")
374376
options = credentials.get("options", {})
375377
redirect_to = options.get("redirect_to")
@@ -379,10 +381,20 @@ def link_identity(self, credentials):
379381
params["redirect_to"] = redirect_to
380382
if scopes:
381383
params["scopes"] = scopes
382-
params["skip_browser_redirect"] = True
384+
params["skip_http_redirect"] = "true"
385+
url = self._get_url_for_provider("user/identities/authorize", provider, params)
383386

384-
url = self._get_url_for_provider(provider, params)
385-
return OAuthResponse(provider=provider, url=url)
387+
session = self.get_session()
388+
if not session:
389+
raise AuthSessionMissingError()
390+
391+
response = self._request(
392+
method="GET",
393+
path=url,
394+
jwt=session.access_token,
395+
xform=parse_link_identity_response,
396+
)
397+
return OAuthResponse(provider=provider, url=response.url)
386398

387399
def get_user_identities(self):
388400
response = self.get_user()
@@ -392,10 +404,15 @@ def get_user_identities(self):
392404
else AuthSessionMissingError()
393405
)
394406

395-
def unlink_identity(self, identity):
407+
def unlink_identity(self, identity: UserIdentity):
408+
session = self.get_session()
409+
if not session:
410+
raise AuthSessionMissingError()
411+
396412
return self._request(
397-
"POST",
398-
f"/user/identities/{identity.id}",
413+
"DELETE",
414+
f"user/identities/{identity.identity_id}",
415+
jwt=session.access_token,
399416
)
400417

401418
def sign_in_with_otp(
@@ -973,6 +990,7 @@ def _is_implicit_grant_flow(self, url: str) -> bool:
973990

974991
def _get_url_for_provider(
975992
self,
993+
url: str,
976994
provider: Provider,
977995
params: Dict[str, str],
978996
) -> str:
@@ -988,7 +1006,7 @@ def _get_url_for_provider(
9881006

9891007
params["provider"] = provider
9901008
query = urlencode(params)
991-
return f"{self._url}/authorize?{query}"
1009+
return f"{url}?{query}"
9921010

9931011
def _decode_jwt(self, jwt: str) -> DecodedJWTDict:
9941012
"""

supabase_auth/helpers.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
AuthResponse,
1818
GenerateLinkProperties,
1919
GenerateLinkResponse,
20+
LinkIdentityResponse,
2021
Session,
2122
SSOResponse,
2223
User,
@@ -77,6 +78,10 @@ def parse_auth_otp_response(data: Any) -> AuthOtpResponse:
7778
return model_validate(AuthOtpResponse, data)
7879

7980

81+
def parse_link_identity_response(data: Any) -> LinkIdentityResponse:
82+
return model_validate(LinkIdentityResponse, data)
83+
84+
8085
def parse_link_response(data: Any) -> GenerateLinkResponse:
8186
properties = GenerateLinkProperties(
8287
action_link=data.get("action_link"),

supabase_auth/types.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ class SSOResponse(BaseModel):
104104
url: str
105105

106106

107+
class LinkIdentityResponse(BaseModel):
108+
url: str
109+
110+
107111
class IdentitiesResponse(BaseModel):
108112
identities: List[UserIdentity]
109113

@@ -151,6 +155,7 @@ def validator(cls, values: dict) -> dict:
151155

152156
class UserIdentity(BaseModel):
153157
id: str
158+
identity_id: str
154159
user_id: str
155160
identity_data: Dict[str, Any]
156161
provider: str

0 commit comments

Comments
 (0)