From 423b86c25e72762fd3209000c5254ddee9cde9fe Mon Sep 17 00:00:00 2001 From: Stojan Dimitrovski Date: Fri, 8 Aug 2025 12:35:05 +0200 Subject: [PATCH] feat: implement `linkIdentity` for oidc / native sign-in --- src/GoTrueClient.ts | 68 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/src/GoTrueClient.ts b/src/GoTrueClient.ts index 833a41f8..2f49f9b2 100644 --- a/src/GoTrueClient.ts +++ b/src/GoTrueClient.ts @@ -2174,11 +2174,27 @@ export default class GoTrueClient { throw error } } + /** * Links an oauth identity to an existing user. * This method supports the PKCE flow. */ - async linkIdentity(credentials: SignInWithOAuthCredentials): Promise { + async linkIdentity(credentials: SignInWithOAuthCredentials): Promise + + /** + * Links an OIDC identity to an existing user. + */ + async linkIdentity(credentials: SignInWithIdTokenCredentials): Promise + + async linkIdentity(credentials: any): Promise { + if ('token' in credentials) { + return this.linkIdentityIdToken(credentials) + } + + return this.linkIdentityOAuth(credentials) + } + + private async linkIdentityOAuth(credentials: SignInWithOAuthCredentials): Promise { try { const { data, error } = await this._useSession(async (result) => { const { data, error } = result @@ -2211,6 +2227,56 @@ export default class GoTrueClient { } } + private async linkIdentityIdToken( + credentials: SignInWithIdTokenCredentials + ): Promise { + return await this._useSession(async (result) => { + try { + const { + error: sessionError, + data: { session }, + } = result + if (sessionError) throw sessionError + + const { options, provider, token, access_token, nonce } = credentials + + const res = await _request(this.fetch, 'POST', `${this.url}/token?grant_type=id_token`, { + headers: this.headers, + jwt: session?.access_token ?? undefined, + body: { + provider, + id_token: token, + access_token, + nonce, + link_identity: true, + gotrue_meta_security: { captcha_token: options?.captchaToken }, + }, + xform: _sessionResponse, + }) + + const { data, error } = res + if (error) { + return { data: { user: null, session: null }, error } + } else if (!data || !data.session || !data.user) { + return { + data: { user: null, session: null }, + error: new AuthInvalidTokenResponseError(), + } + } + if (data.session) { + await this._saveSession(data.session) + await this._notifyAllSubscribers('USER_UPDATED', data.session) + } + return { data, error } + } catch (error) { + if (isAuthError(error)) { + return { data: { user: null, session: null }, error } + } + throw error + } + }) + } + /** * Unlinks an identity from a user by deleting it. The user will no longer be able to sign in with that identity once it's unlinked. */