Skip to content
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

feat(auth): add resend method #190

Merged
merged 1 commit into from
Dec 14, 2023
Merged
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
64 changes: 64 additions & 0 deletions Sources/Auth/AuthClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,70 @@ public actor AuthClient {
return response
}

/// Resends an existing signup confirmation email or email change email.
///
/// To obfuscate whether such the email already exists in the system this method succeeds in both
/// cases.
public func resend(
email: String,
type: ResendEmailType,
emailRedirectTo: URL? = nil,
captchaToken: String? = nil
) async throws {
if type != .emailChange {
await sessionManager.remove()
}

_ = try await api.execute(
Request(
path: "/resend",
method: .post,
query: [
emailRedirectTo.map { URLQueryItem(name: "redirect_to", value: $0.absoluteString) },
].compactMap { $0 },
body: configuration.encoder.encode(
ResendEmailParams(
type: type,
email: email,
gotrueMetaSecurity: captchaToken.map(AuthMetaSecurity.init(captchaToken:))
)
)
)
)
}

/// Resends an existing SMS OTP or phone change OTP.
/// - Returns: An object containing the unique ID of the message as reported by the SMS sending
/// provider. Useful for tracking deliverability problems.
///
/// To obfuscate whether such the phone number already exists in the system this method succeeds
/// in both cases.
@discardableResult
public func resend(
phone: String,
type: ResendMobileType,
captchaToken: String? = nil
) async throws -> ResendMobileResponse {
if type != .phoneChange {
await sessionManager.remove()
}

return try await api.execute(
Request(
path: "/resend",
method: .post,
body: configuration.encoder.encode(
ResendMobileParams(
type: type,
phone: phone,
gotrueMetaSecurity: captchaToken.map(AuthMetaSecurity.init(captchaToken:))
)
)
)
)
.decoded(decoder: configuration.decoder)
}

/// Gets the current user details if there is an existing session.
/// - Parameter jwt: Takes in an optional access token jwt. If no jwt is provided, user() will
/// attempt to get the jwt from the current session.
Expand Down
32 changes: 32 additions & 0 deletions Sources/Auth/Types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -586,3 +586,35 @@ public enum SignOutScope: String, Sendable {
/// session.
case others
}

public enum ResendEmailType: String, Hashable, Sendable, Encodable {
case signup
case emailChange = "email_change"
}

struct ResendEmailParams: Encodable {
let type: ResendEmailType
let email: String
let gotrueMetaSecurity: AuthMetaSecurity?
}

public enum ResendMobileType: String, Hashable, Sendable, Encodable {
case sms
case phoneChange = "phone_change"
}

struct ResendMobileParams: Encodable {
let type: ResendMobileType
let phone: String
let gotrueMetaSecurity: AuthMetaSecurity?
}

public struct ResendMobileResponse: Decodable, Hashable, Sendable {
/// Unique ID of the message as reported by the SMS sending provider. Useful for tracking
/// deliverability problems.
public let messageId: String?

public init(messageId: String?) {
self.messageId = messageId
}
}
25 changes: 25 additions & 0 deletions Tests/AuthTests/RequestsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,31 @@ final class RequestsTests: XCTestCase {
}
}

func testResendEmail() async {
let sut = makeSUT()

await assert {
try await sut.resend(
email: "[email protected]",
type: .emailChange,
emailRedirectTo: URL(string: "https://supabase.com"),
captchaToken: "captcha-token"
)
}
}

func testResendPhone() async {
let sut = makeSUT()

await assert {
try await sut.resend(
phone: "+1 202-918-2132",
type: .phoneChange,
captchaToken: "captcha-token"
)
}
}

private func assert(_ block: () async throws -> Void) async {
do {
try await block()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
curl \
--request POST \
--header "Content-Type: application/json" \
--header "X-Client-Info: gotrue-swift/x.y.z" \
--header "apikey: dummy.api.key" \
--data "{\"email\":\"[email protected]\",\"gotrue_meta_security\":{\"captcha_token\":\"captcha-token\"},\"type\":\"email_change\"}" \
"http://localhost:54321/auth/v1/resend?redirect_to=https://supabase.com"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
curl \
--request POST \
--header "Content-Type: application/json" \
--header "X-Client-Info: gotrue-swift/x.y.z" \
--header "apikey: dummy.api.key" \
--data "{\"gotrue_meta_security\":{\"captcha_token\":\"captcha-token\"},\"phone\":\"+1 202-918-2132\",\"type\":\"phone_change\"}" \
"http://localhost:54321/auth/v1/resend"