From 7854aa8881de883f82c3628b8c916366f77fec54 Mon Sep 17 00:00:00 2001 From: Alexander Bakker Date: Fri, 28 Jun 2024 13:43:48 +0200 Subject: [PATCH] Introduce VerifyTokenHash to support the PKCE flow for email signup (#98) Relevant documentation: https://supabase.com/docs/guides/auth/passwords?queryGroups=flow&flow=pkce#signing-up-with-an-email-and-password --- Gotrue/Api.cs | 16 ++++++++++++++++ Gotrue/Client.cs | 20 ++++++++++++++++++++ Gotrue/Constants.cs | 4 +++- Gotrue/Interfaces/IGotrueApi.cs | 1 + Gotrue/Interfaces/IGotrueClient.cs | 8 ++++++++ Gotrue/Interfaces/IGotrueStatelessClient.cs | 9 +++++++++ Gotrue/StatelessClient.cs | 13 +++++++++++++ 7 files changed, 70 insertions(+), 1 deletion(-) diff --git a/Gotrue/Api.cs b/Gotrue/Api.cs index 5e54727f..597d10b0 100644 --- a/Gotrue/Api.cs +++ b/Gotrue/Api.cs @@ -441,6 +441,22 @@ public Task SendMobileOTP(string phone) return Helpers.MakeRequest(HttpMethod.Post, $"{Url}/verify", data, Headers); } + /// + /// Verify token hash used in an email confirmation link. + /// + /// The token hash used in an email confirmation link + /// Type of verification, e.g. email. + /// + public Task VerifyTokenHash(string tokenHash, EmailOtpType type) + { + var data = new Dictionary + { + { "token_hash", tokenHash }, + { "type", Core.Helpers.GetMappedToAttr(type).Mapping } + }; + return Helpers.MakeRequest(HttpMethod.Post, $"{Url}/verify", data, Headers); + } + /// /// Sends a reset request to an email address. /// diff --git a/Gotrue/Client.cs b/Gotrue/Client.cs index a7c86b48..2a8946f8 100644 --- a/Gotrue/Client.cs +++ b/Gotrue/Client.cs @@ -373,6 +373,26 @@ public Task SignIn(Provider provider, SignInOptions? options return null; } + /// + public async Task VerifyTokenHash(string tokenHash, EmailOtpType type = EmailOtpType.Email) + { + if (!Online) + throw new GotrueException("Only supported when online", Offline); + + DestroySession(); + + var session = await _api.VerifyTokenHash(tokenHash, type); + + if (session?.AccessToken != null) + { + UpdateSession(session); + NotifyAuthStateChange(SignedIn); + return session; + } + + return null; + } + /// public Task LinkIdentity(Provider provider, SignInOptions options) { diff --git a/Gotrue/Constants.cs b/Gotrue/Constants.cs index b10226ba..d747b64d 100644 --- a/Gotrue/Constants.cs +++ b/Gotrue/Constants.cs @@ -53,7 +53,9 @@ public enum EmailOtpType [MapTo("recovery")] Recovery, [MapTo("email_change")] - EmailChange + EmailChange, + [MapTo("email")] + Email } /// diff --git a/Gotrue/Interfaces/IGotrueApi.cs b/Gotrue/Interfaces/IGotrueApi.cs index e6aa5579..10215b4d 100644 --- a/Gotrue/Interfaces/IGotrueApi.cs +++ b/Gotrue/Interfaces/IGotrueApi.cs @@ -38,6 +38,7 @@ public interface IGotrueApi : IGettableHeaders Task UpdateUserById(string jwt, string userId, UserAttributes userData); Task VerifyMobileOTP(string phone, string token, MobileOtpType type); Task VerifyEmailOTP(string email, string token, EmailOtpType type); + Task VerifyTokenHash(string tokenHash, EmailOtpType type); Task Reauthenticate(string userJwt); ProviderAuthState GetUriForProvider(Provider provider, SignInOptions? options = null); Task ExchangeCodeForSession(string codeVerifier, string authCode); diff --git a/Gotrue/Interfaces/IGotrueClient.cs b/Gotrue/Interfaces/IGotrueClient.cs index a6114030..c1f2b6c3 100644 --- a/Gotrue/Interfaces/IGotrueClient.cs +++ b/Gotrue/Interfaces/IGotrueClient.cs @@ -387,6 +387,14 @@ public interface IGotrueClient : IGettableHeaders /// Task VerifyOTP(string email, string token, EmailOtpType type = EmailOtpType.MagicLink); + /// + /// Log in a user given the token hash used in an email confirmation link. + /// + /// + /// + /// + Task VerifyTokenHash(string tokenHash, EmailOtpType type = EmailOtpType.Email); + /// /// Links an oauth identity to an existing user. /// diff --git a/Gotrue/Interfaces/IGotrueStatelessClient.cs b/Gotrue/Interfaces/IGotrueStatelessClient.cs index 09df42c0..fa211200 100644 --- a/Gotrue/Interfaces/IGotrueStatelessClient.cs +++ b/Gotrue/Interfaces/IGotrueStatelessClient.cs @@ -248,6 +248,15 @@ public interface IGotrueStatelessClient /// Task VerifyOTP(string email, string otpToken, StatelessClientOptions options, EmailOtpType type = EmailOtpType.MagicLink); + /// + /// Log in a user given the token hash used in an email confirmation link. + /// + /// + /// + /// + /// + Task VerifyTokenHash(string tokenHash, StatelessClientOptions options, EmailOtpType type = EmailOtpType.Email); + /// /// Retrieve the current settings for the Gotrue instance. /// diff --git a/Gotrue/StatelessClient.cs b/Gotrue/StatelessClient.cs index 52cbda31..f5632409 100644 --- a/Gotrue/StatelessClient.cs +++ b/Gotrue/StatelessClient.cs @@ -122,6 +122,19 @@ public async Task SignOut(string accessToken, StatelessClientOptions optio return null; } + /// + public async Task VerifyTokenHash(string tokenHash, StatelessClientOptions options, EmailOtpType type = EmailOtpType.Email) + { + var session = await GetApi(options).VerifyTokenHash(tokenHash, type); + + if (session?.AccessToken != null) + { + return session; + } + + return null; + } + /// public async Task Update(string accessToken, UserAttributes attributes, StatelessClientOptions options) {