From ba390574bfbf89463e9d15c00a089ccd4afe3c98 Mon Sep 17 00:00:00 2001 From: Guilherme Souza Date: Fri, 8 Aug 2025 08:38:40 -0300 Subject: [PATCH 1/2] feat(gotrue): implement linkIdentityWithIdToken method - Add linkIdentityWithIdToken method for linking identities with ID tokens - Refactor signInWithIdToken and linkIdentityWithIdToken to share common code - Create _signInWithIdToken helper method to eliminate code duplication - Maintain backward compatibility with existing signInWithIdToken API - Add proper documentation and experimental annotation --- packages/gotrue/lib/src/gotrue_client.dart | 83 ++++++++++++++++++---- 1 file changed, 70 insertions(+), 13 deletions(-) diff --git a/packages/gotrue/lib/src/gotrue_client.dart b/packages/gotrue/lib/src/gotrue_client.dart index 527287be..3802cebb 100644 --- a/packages/gotrue/lib/src/gotrue_client.dart +++ b/packages/gotrue/lib/src/gotrue_client.dart @@ -382,17 +382,14 @@ class GoTrueClient { /// If the ID token contains a `nonce` claim, then [nonce] must be /// provided to compare its hash with the value in the ID token. /// - /// [captchaToken] is the verification token received when the user - /// completes the captcha on the app. - /// - /// This method is experimental. - @experimental - Future signInWithIdToken({ + /// Sign in with ID token (internal helper method). + Future _signInWithIdToken({ required OAuthProvider provider, required String idToken, String? accessToken, String? nonce, String? captchaToken, + required bool linkIdentity, }) async { if (provider != OAuthProvider.google && provider != OAuthProvider.apple && @@ -402,18 +399,24 @@ class GoTrueClient { '${OAuthProvider.google.name}, ${OAuthProvider.apple.name}, ${OAuthProvider.kakao.name} or ${OAuthProvider.keycloak.name}.'); } + final body = { + 'provider': provider.snakeCase, + 'id_token': idToken, + 'nonce': nonce, + 'gotrue_meta_security': {'captcha_token': captchaToken}, + 'access_token': accessToken, + }; + + if (linkIdentity) { + body['link_identity'] = true; + } + final response = await _fetch.request( '$_url/token', RequestMethodType.post, options: GotrueRequestOptions( headers: _headers, - body: { - 'provider': provider.snakeCase, - 'id_token': idToken, - 'nonce': nonce, - 'gotrue_meta_security': {'captcha_token': captchaToken}, - 'access_token': accessToken, - }, + body: body, query: {'grant_type': 'id_token'}, ), ); @@ -432,6 +435,28 @@ class GoTrueClient { return authResponse; } + /// [captchaToken] is the verification token received when the user + /// completes the captcha on the app. + /// + /// This method is experimental. + @experimental + Future signInWithIdToken({ + required OAuthProvider provider, + required String idToken, + String? accessToken, + String? nonce, + String? captchaToken, + }) async { + return _signInWithIdToken( + provider: provider, + idToken: idToken, + accessToken: accessToken, + nonce: nonce, + captchaToken: captchaToken, + linkIdentity: false, + ); + } + /// Log in a user using magiclink or a one-time password (OTP). /// /// If the `{{ .ConfirmationURL }}` variable is specified in the email template, a magiclink will be sent. @@ -914,6 +939,38 @@ class GoTrueClient { return res.user?.identities ?? []; } + /// Link an identity to the current user using an ID token. + /// + /// [provider] is the OAuth provider (google, apple, kakao, or keycloak) + /// + /// [idToken] is the ID token from the OAuth provider + /// + /// [accessToken] is the access token from the OAuth provider + /// + /// [nonce] is the nonce used for the OAuth flow + /// + /// [captchaToken] is the verification token received when the user + /// completes the captcha on the app. + /// + /// This method is experimental. + @experimental + Future linkIdentityWithIdToken({ + required OAuthProvider provider, + required String idToken, + String? accessToken, + String? nonce, + String? captchaToken, + }) async { + return _signInWithIdToken( + provider: provider, + idToken: idToken, + accessToken: accessToken, + nonce: nonce, + captchaToken: captchaToken, + linkIdentity: true, + ); + } + /// Returns the URL to link the user's identity with an OAuth provider. Future getLinkIdentityUrl( OAuthProvider provider, { From 0fd1c349fa1cd5de4cd9dd0dcab54adf7a5cc84f Mon Sep 17 00:00:00 2001 From: Guilherme Souza Date: Fri, 8 Aug 2025 08:43:10 -0300 Subject: [PATCH 2/2] fix(gotrue): drop experimental from signInWithIdToken --- packages/gotrue/lib/src/gotrue_client.dart | 26 +++++++++------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/packages/gotrue/lib/src/gotrue_client.dart b/packages/gotrue/lib/src/gotrue_client.dart index 3802cebb..e956facd 100644 --- a/packages/gotrue/lib/src/gotrue_client.dart +++ b/packages/gotrue/lib/src/gotrue_client.dart @@ -372,16 +372,6 @@ class GoTrueClient { return authSessionUrlResponse; } - /// Allows signing in with an ID token issued by certain supported providers. - /// The [idToken] is verified for validity and a new session is established. - /// This method of signing in only supports [OAuthProvider.google], [OAuthProvider.apple], [OAuthProvider.kakao] or [OAuthProvider.keycloak]. - /// - /// If the ID token contains an `at_hash` claim, then [accessToken] must be - /// provided to compare its hash with the value in the ID token. - /// - /// If the ID token contains a `nonce` claim, then [nonce] must be - /// provided to compare its hash with the value in the ID token. - /// /// Sign in with ID token (internal helper method). Future _signInWithIdToken({ required OAuthProvider provider, @@ -435,11 +425,18 @@ class GoTrueClient { return authResponse; } + /// Allows signing in with an ID token issued by certain supported providers. + /// The [idToken] is verified for validity and a new session is established. + /// This method of signing in only supports [OAuthProvider.google], [OAuthProvider.apple], [OAuthProvider.kakao] or [OAuthProvider.keycloak]. + /// + /// If the ID token contains an `at_hash` claim, then [accessToken] must be + /// provided to compare its hash with the value in the ID token. + /// + /// If the ID token contains a `nonce` claim, then [nonce] must be + /// provided to compare its hash with the value in the ID token. + /// /// [captchaToken] is the verification token received when the user /// completes the captcha on the app. - /// - /// This method is experimental. - @experimental Future signInWithIdToken({ required OAuthProvider provider, required String idToken, @@ -951,9 +948,6 @@ class GoTrueClient { /// /// [captchaToken] is the verification token received when the user /// completes the captcha on the app. - /// - /// This method is experimental. - @experimental Future linkIdentityWithIdToken({ required OAuthProvider provider, required String idToken,