Skip to content

Commit 7c4e62b

Browse files
authored
fix(auth): sign out should ignore 403s (#359)
* fix(auth): sign out should ignore 403s * add integration test * fix linux build
1 parent e2431b9 commit 7c4e62b

File tree

3 files changed

+54
-2
lines changed

3 files changed

+54
-2
lines changed

Sources/Auth/AuthClient.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -712,8 +712,8 @@ public final class AuthClient: Sendable {
712712
)
713713
} catch {
714714
// ignore 404s since user might not exist anymore
715-
// ignore 401s since an invalid or expired JWT should sign out the current session
716-
let ignoredCodes = Set([404, 401])
715+
// ignore 401s, and 403s since an invalid or expired JWT should sign out the current session.
716+
let ignoredCodes = Set([404, 403, 401])
717717

718718
if case let AuthError.api(apiError) = error, let code = apiError.code,
719719
!ignoredCodes.contains(code)

Tests/AuthTests/AuthClientTests.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,37 @@ final class AuthClientTests: XCTestCase {
179179
XCTAssertTrue(sessionRemoved)
180180
}
181181

182+
func testSignOutShouldRemoveSessionIf403Returned() async throws {
183+
sut = makeSUT { _ in
184+
throw AuthError.api(AuthError.APIError(code: 403))
185+
}
186+
187+
let validSession = Session.validSession
188+
try storage.storeSession(.init(session: validSession))
189+
190+
let eventsTask = Task {
191+
await sut.authStateChanges.prefix(2).collect()
192+
}
193+
194+
await Task.megaYield()
195+
196+
do {
197+
try await sut.signOut()
198+
} catch AuthError.api {
199+
} catch {
200+
XCTFail("Unexpected error: \(error)")
201+
}
202+
203+
let events = await eventsTask.value.map(\.event)
204+
let sessions = await eventsTask.value.map(\.session)
205+
206+
XCTAssertNoDifference(events, [.initialSession, .signedOut])
207+
XCTAssertNoDifference(sessions, [validSession, nil])
208+
209+
let sessionRemoved = try storage.getSession() == nil
210+
XCTAssertTrue(sessionRemoved)
211+
}
212+
182213
func testSignInAnonymously() async throws {
183214
let session = Session(fromMockNamed: "anonymous-sign-in-response")
184215

Tests/IntegrationTests/AuthClientIntegrationTests.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ import CustomDump
1111
import TestHelpers
1212
import XCTest
1313

14+
#if canImport(FoundationNetworking)
15+
import FoundationNetworking
16+
#endif
17+
1418
final class AuthClientIntegrationTests: XCTestCase {
1519
let authClient = AuthClient(
1620
configuration: AuthClient.Configuration(
@@ -189,6 +193,23 @@ final class AuthClientIntegrationTests: XCTestCase {
189193
}
190194
}
191195

196+
func testDeleteAccountAndSignOut() async throws {
197+
let response = try await signUpIfNeededOrSignIn(email: mockEmail(), password: mockPassword())
198+
199+
let session = try XCTUnwrap(response.session)
200+
201+
var request = URLRequest(url: URL(string: "\(DotEnv.SUPABASE_URL)/rest/v1/rpc/delete_user")!)
202+
request.httpMethod = "POST"
203+
request.setValue(DotEnv.SUPABASE_ANON_KEY, forHTTPHeaderField: "apikey")
204+
request.setValue("Bearer \(session.accessToken)", forHTTPHeaderField: "Authorization")
205+
206+
_ = try await URLSession.shared.data(for: request)
207+
208+
try await XCTAssertAuthChangeEvents([.initialSession, .signedOut]) {
209+
try await authClient.signOut()
210+
}
211+
}
212+
192213
@discardableResult
193214
private func signUpIfNeededOrSignIn(
194215
email: String,

0 commit comments

Comments
 (0)