Skip to content

feat: implement linkIdentity for oidc / native sign-in #1096

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 1 commit into
base: master
Choose a base branch
from
Open
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
68 changes: 67 additions & 1 deletion src/GoTrueClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<OAuthResponse> {
async linkIdentity(credentials: SignInWithOAuthCredentials): Promise<OAuthResponse>

/**
* Links an OIDC identity to an existing user.
*/
async linkIdentity(credentials: SignInWithIdTokenCredentials): Promise<AuthTokenResponse>

async linkIdentity(credentials: any): Promise<any> {
if ('token' in credentials) {
return this.linkIdentityIdToken(credentials)
}

return this.linkIdentityOAuth(credentials)
}

private async linkIdentityOAuth(credentials: SignInWithOAuthCredentials): Promise<OAuthResponse> {
try {
const { data, error } = await this._useSession(async (result) => {
const { data, error } = result
Expand Down Expand Up @@ -2211,6 +2227,56 @@ export default class GoTrueClient {
}
}

private async linkIdentityIdToken(
credentials: SignInWithIdTokenCredentials
): Promise<AuthTokenResponse> {
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.
*/
Expand Down