Skip to content
Open
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"type":"signup","email":"user@supabase.com","gotrue_meta_security":{"captcha_token":"captcha-token"}}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"type":"email_change","email":"user@supabase.com"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"type":"phone_change","phone":"+5544998989898"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"type":"signup","email":"user@supabase.com"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"type":"sms","phone":"+5544998989898"}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System.Threading.Tasks;
using Gotrue.Tests.Support;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Supabase.Gotrue.Resend;

namespace Gotrue.Tests.Authentication;

/// <summary>
/// Pins the exact bytes the resend requests put on the wire, including the <c>type</c> enum mappings
/// (<c>signup</c>, <c>sms</c>, <c>phone_change</c>, <c>email_change</c>) and whether the target is sent
/// as <c>email</c> or <c>phone</c>.
/// </summary>
[TestClass]
[TestCategory("Contract")]
public class ResendParametersContractTests : RequestApprovalFixture
{
[TestMethod]
public async Task ResendRequest_ShouldSerializeToExpectedPayload_GivenSignUpType()
{
const string email = "user@supabase.com";
var resend = new ResendParameters(
type: ResendType.SignUp,
email: email,
phone: null,
options: null
);
await this.Api.Resend(resend);

await this.Verify(this.EmittedRequestBody).UseDirectory("Data");
}

[TestMethod]
public async Task ResendRequest_ShouldSerializeToExpectedPayload_GivenEmailChangeType()
{
const string email = "user@supabase.com";
var resend = new ResendParameters(
type: ResendType.EmailChange,
email: email,
phone: null,
options: null
);
await this.Api.Resend(resend);

await this.Verify(this.EmittedRequestBody).UseDirectory("Data");
}

[TestMethod]
public async Task ResendRequest_ShouldSerializeToExpectedPayload_GivenPhoneChangeType()
{
var resend = new ResendParameters(
type: ResendType.PhoneChange,
email: null,
phone: "+5544998989898",
options: null
);
await this.Api.Resend(resend);

await this.Verify(this.EmittedRequestBody).UseDirectory("Data");
}

[TestMethod]
public async Task ResendRequest_ShouldSerializeToExpectedPayload_GivenSmsType()
{
var resend = new ResendParameters(
type: ResendType.Sms,
email: null,
phone: "+5544998989898",
options: null
);
await this.Api.Resend(resend);

await this.Verify(this.EmittedRequestBody).UseDirectory("Data");
}

[TestMethod]
public async Task ResendRequest_ShouldSerializeCaptchaToExpectedPayload_GivenOptions()
{
var resend = new ResendParameters(
type: ResendType.SignUp,
email: "user@supabase.com",
phone: null,
options: new ResendOptions(captchaToken: "captcha-token", emailRedirectTo: null)
);

await this.Api.Resend(resend);

await this.Verify(this.EmittedRequestBody).UseDirectory("Data");
}
}
143 changes: 143 additions & 0 deletions packages/Gotrue/Gotrue.Tests/Authentication/ResendParametersTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
using System.Net;
using System.Threading.Tasks;
using Gotrue.Tests.Support;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Supabase.Gotrue.Exceptions;
using Supabase.Gotrue.Resend;
using static Gotrue.Tests.TestUtils;

namespace Gotrue.Tests.Authentication;

[TestClass]
[TestCategory("E2E")]
public class ResendParametersTests : AuthClientFixture
Comment thread
diegofesanto marked this conversation as resolved.
{
[TestMethod]
public async Task Resend_ShouldRequestConfirmationCode_GivenSignUpType()
{
var email = RandomEmail();
var resend = new ResendParameters(
type: ResendType.SignUp,
email: email,
phone: null,
options: null
);
var response = await this.Client.Resend(resend);

Assert.IsNotNull(response);
Assert.AreEqual(HttpStatusCode.OK, response.ResponseMessage?.StatusCode);
}

[TestMethod]
public async Task Resend_ShouldRequestConfirmationCode_GivenSmsType()
{
var resend = new ResendParameters(
type: ResendType.Sms,
email: null,
phone: "5544989899898",
options: null
);
var response = await this.Client.Resend(resend);

Assert.IsNotNull(response);
Assert.AreEqual(HttpStatusCode.OK, response.ResponseMessage?.StatusCode);
}

[TestMethod]
public async Task Resend_ShouldRequestConfirmationCode_GivenPhoneChangeType()
{
var resend = new ResendParameters(
type: ResendType.PhoneChange,
email: null,
phone: "5544989899898",
options: null
);
var response = await this.Client.Resend(resend);

Assert.IsNotNull(response);
Assert.AreEqual(HttpStatusCode.OK, response.ResponseMessage?.StatusCode);
}

[TestMethod]
public async Task Resend_ShouldRequestConfirmationCode_GivenEmailChangeType()
{
var resend = new ResendParameters(
type: ResendType.EmailChange,
email: RandomEmail(),
phone: null,
options: null
);
var response = await this.Client.Resend(resend);

Assert.IsNotNull(response);
Assert.AreEqual(HttpStatusCode.OK, response.ResponseMessage?.StatusCode);
}

[TestMethod]
public async Task Resend_ShouldThrowErrorWhenParameterAreInvalid_GivenPhoneForTypeEmail()
{
var resend = new ResendParameters(
type: ResendType.EmailChange,
email: null,
phone: "5544989899898",
options: null
);

var action = async () => await this.Client.Resend(resend);

var exception = await Assert.ThrowsAsync<GotrueException>(action);
Assert.Contains("Missing email address or phone number", exception.Message);
Assert.Contains("400", exception.Message);
}

[TestMethod]
public async Task Resend_ShouldThrowErrorWhenParameterAreInvalid_GivenEmailForTypePhone()
{
var resend = new ResendParameters(
type: ResendType.Sms,
email: RandomEmail(),
phone: null,
options: null
);

var action = async () => await this.Client.Resend(resend);

var exception = await Assert.ThrowsAsync<GotrueException>(action);
Assert.Contains("Type provided requires a phone number", exception.Message);
Assert.Contains("400", exception.Message);
}

[TestMethod]
public async Task Resend_ShouldThrowErrorWhenParameterAreInvalid_GivenNothingForTypeEmail()
{
var resend = new ResendParameters(
type: ResendType.EmailChange,
email: null,
phone: null,
options: null
);

var action = async () => await this.Client.Resend(resend);

var exception = await Assert.ThrowsAsync<GotrueException>(action);
Assert.Contains("Missing email address or phone number", exception.Message);
Assert.Contains("400", exception.Message);
}

[TestMethod]
public async Task Resend_ShouldThrowErrorWhenParameterAreInvalid_GivenNothingForTypePhone()
{
var resend = new ResendParameters(
type: ResendType.Sms,
email: null,
phone: null,
options: null
);

var action = async () => await this.Client.Resend(resend);

var exception = await Assert.ThrowsAsync<GotrueException>(action);
Assert.Contains("Type provided requires a phone number", exception.Message);
Assert.Contains("400", exception.Message);
}
}
19 changes: 19 additions & 0 deletions packages/Gotrue/Gotrue/Api.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using Supabase.Gotrue.Exceptions;
using Supabase.Gotrue.Interfaces;
using Supabase.Gotrue.Mfa;
using Supabase.Gotrue.Resend;
using Supabase.Gotrue.Responses;
using static Supabase.Gotrue.Constants;

Expand Down Expand Up @@ -889,4 +890,22 @@ public Task<BaseResponse> GenerateLink(string jwt, GenerateLinkOptions options)

return this.MakeRequestAsync<Session>(HttpMethod.Post, $"{this.Url}/token?grant_type=refresh_token", data, this.Headers.MergeLeft(headers));
}

/// <summary>
/// Resends a confirmation code to a user's email or phone.
/// </summary>
/// <param name="resendParameters"></param>
/// <returns>BaseResponse</returns>
public Task<BaseResponse> Resend(ResendParameters resendParameters)
{
var body = resendParameters.ToDictionary();
var url = resendParameters.ApplyRedirectTo(this.Url);

return this.MakeRequestAsync(
HttpMethod.Post,
url,
body,
this.Headers
);
}
}
5 changes: 5 additions & 0 deletions packages/Gotrue/Gotrue/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
using Supabase.Gotrue.Exceptions;
using Supabase.Gotrue.Interfaces;
using Supabase.Gotrue.Mfa;
using Supabase.Gotrue.Resend;
using Supabase.Gotrue.Responses;
using static Supabase.Gotrue.Constants;
using static Supabase.Gotrue.Constants.AuthState;
using static Supabase.Gotrue.Exceptions.FailureHint.Reason;
Expand Down Expand Up @@ -983,6 +985,9 @@ private void ApplyLoadedSession(Session? before, Session? loaded)
/// <inheritdoc />
public void Shutdown() => this.NotifyAuthStateChange(AuthState.Shutdown);

/// <inheritdoc />
public Task<BaseResponse> Resend(ResendParameters resendParameters) => this.api.Resend(resendParameters);

/// <inheritdoc />
public async Task<MfaEnrollResponse?> Enroll(MfaEnrollParams mfaEnrollParams)
{
Expand Down
Loading