From 7c4e62b3d0dcc6f307639abb3ef8ad792589fab1 Mon Sep 17 00:00:00 2001 From: Guilherme Souza Date: Mon, 6 May 2024 08:15:27 -0300 Subject: [PATCH] fix(auth): sign out should ignore 403s (#359) * fix(auth): sign out should ignore 403s * add integration test * fix linux build --- Sources/Auth/AuthClient.swift | 4 +-- Tests/AuthTests/AuthClientTests.swift | 31 +++++++++++++++++++ .../AuthClientIntegrationTests.swift | 21 +++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index 049c7e11..2c2f0105 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -712,8 +712,8 @@ public final class AuthClient: Sendable { ) } catch { // ignore 404s since user might not exist anymore - // ignore 401s since an invalid or expired JWT should sign out the current session - let ignoredCodes = Set([404, 401]) + // ignore 401s, and 403s since an invalid or expired JWT should sign out the current session. + let ignoredCodes = Set([404, 403, 401]) if case let AuthError.api(apiError) = error, let code = apiError.code, !ignoredCodes.contains(code) diff --git a/Tests/AuthTests/AuthClientTests.swift b/Tests/AuthTests/AuthClientTests.swift index 44d88069..a063aec1 100644 --- a/Tests/AuthTests/AuthClientTests.swift +++ b/Tests/AuthTests/AuthClientTests.swift @@ -179,6 +179,37 @@ final class AuthClientTests: XCTestCase { XCTAssertTrue(sessionRemoved) } + func testSignOutShouldRemoveSessionIf403Returned() async throws { + sut = makeSUT { _ in + throw AuthError.api(AuthError.APIError(code: 403)) + } + + let validSession = Session.validSession + try storage.storeSession(.init(session: validSession)) + + let eventsTask = Task { + await sut.authStateChanges.prefix(2).collect() + } + + await Task.megaYield() + + do { + try await sut.signOut() + } catch AuthError.api { + } catch { + XCTFail("Unexpected error: \(error)") + } + + let events = await eventsTask.value.map(\.event) + let sessions = await eventsTask.value.map(\.session) + + XCTAssertNoDifference(events, [.initialSession, .signedOut]) + XCTAssertNoDifference(sessions, [validSession, nil]) + + let sessionRemoved = try storage.getSession() == nil + XCTAssertTrue(sessionRemoved) + } + func testSignInAnonymously() async throws { let session = Session(fromMockNamed: "anonymous-sign-in-response") diff --git a/Tests/IntegrationTests/AuthClientIntegrationTests.swift b/Tests/IntegrationTests/AuthClientIntegrationTests.swift index b9382f2f..4275789a 100644 --- a/Tests/IntegrationTests/AuthClientIntegrationTests.swift +++ b/Tests/IntegrationTests/AuthClientIntegrationTests.swift @@ -11,6 +11,10 @@ import CustomDump import TestHelpers import XCTest +#if canImport(FoundationNetworking) + import FoundationNetworking +#endif + final class AuthClientIntegrationTests: XCTestCase { let authClient = AuthClient( configuration: AuthClient.Configuration( @@ -189,6 +193,23 @@ final class AuthClientIntegrationTests: XCTestCase { } } + func testDeleteAccountAndSignOut() async throws { + let response = try await signUpIfNeededOrSignIn(email: mockEmail(), password: mockPassword()) + + let session = try XCTUnwrap(response.session) + + var request = URLRequest(url: URL(string: "\(DotEnv.SUPABASE_URL)/rest/v1/rpc/delete_user")!) + request.httpMethod = "POST" + request.setValue(DotEnv.SUPABASE_ANON_KEY, forHTTPHeaderField: "apikey") + request.setValue("Bearer \(session.accessToken)", forHTTPHeaderField: "Authorization") + + _ = try await URLSession.shared.data(for: request) + + try await XCTAssertAuthChangeEvents([.initialSession, .signedOut]) { + try await authClient.signOut() + } + } + @discardableResult private func signUpIfNeededOrSignIn( email: String,