Skip to content

Commit

Permalink
refactor(auth): in memory code verifier storage (#296)
Browse files Browse the repository at this point in the history
  • Loading branch information
grdsdev committed Apr 1, 2024
1 parent b3ac087 commit bfbcddd
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 57 deletions.
55 changes: 20 additions & 35 deletions Sources/Auth/AuthClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -529,34 +529,31 @@ public actor AuthClient {

/// Log in an existing user by exchanging an Auth Code issued during the PKCE flow.
public func exchangeCodeForSession(authCode: String) async throws -> Session {
guard let codeVerifier = try codeVerifierStorage.getCodeVerifier() else {
guard let codeVerifier = codeVerifierStorage.get() else {
throw AuthError.pkce(.codeVerifierNotFound)
}
do {
let session: Session = try await api.execute(
.init(
path: "/token",
method: .post,
query: [URLQueryItem(name: "grant_type", value: "pkce")],
body: configuration.encoder.encode(
[
"auth_code": authCode,
"code_verifier": codeVerifier,
]
)

let session: Session = try await api.execute(
.init(
path: "/token",
method: .post,
query: [URLQueryItem(name: "grant_type", value: "pkce")],
body: configuration.encoder.encode(
[
"auth_code": authCode,
"code_verifier": codeVerifier,
]
)
)
.decoded(decoder: configuration.decoder)
)
.decoded(decoder: configuration.decoder)

try codeVerifierStorage.deleteCodeVerifier()
codeVerifierStorage.set(nil)

try await sessionManager.update(session)
eventEmitter.emit(.signedIn, session: session)
try await sessionManager.update(session)
eventEmitter.emit(.signedIn, session: session)

return session
} catch {
throw error
}
return session
}

/// Log in an existing user via a third-party provider.
Expand Down Expand Up @@ -1020,19 +1017,7 @@ public actor AuthClient {
}

let codeVerifier = PKCE.generateCodeVerifier()

do {
try codeVerifierStorage.storeCodeVerifier(codeVerifier)
} catch {
assertionFailure(
"""
An error occurred while storing the code verifier,
PKCE flow may not work as expected.
Error: \(error.localizedDescription)
"""
)
}
codeVerifierStorage.set(codeVerifier)

let codeChallenge = PKCE.generateCodeChallenge(from: codeVerifier)
let codeChallengeMethod = codeVerifier == codeChallenge ? "plain" : "s256"
Expand All @@ -1049,7 +1034,7 @@ public actor AuthClient {

private func isPKCEFlow(url: URL) -> Bool {
let fragments = extractParams(from: url)
let currentCodeVerifier = try? codeVerifierStorage.getCodeVerifier()
let currentCodeVerifier = codeVerifierStorage.get()
return fragments.contains(where: { $0.name == "code" }) && currentCodeVerifier != nil
}

Expand Down
23 changes: 6 additions & 17 deletions Sources/Auth/Internal/CodeVerifierStorage.swift
Original file line number Diff line number Diff line change
@@ -1,30 +1,19 @@
import ConcurrencyExtras
import Foundation
@_spi(Internal) import _Helpers

struct CodeVerifierStorage: Sendable {
var getCodeVerifier: @Sendable () throws -> String?
var storeCodeVerifier: @Sendable (_ code: String) throws -> Void
var deleteCodeVerifier: @Sendable () throws -> Void
var get: @Sendable () -> String?
var set: @Sendable (_ code: String?) -> Void
}

extension CodeVerifierStorage {
static let live: Self = {
@Dependency(\.configuration.localStorage) var localStorage

let key = "supabase.code-verifier"
let code = LockIsolated(String?.none)

return Self(
getCodeVerifier: {
try localStorage.retrieve(key: key).flatMap {
String(data: $0, encoding: .utf8)
}
},
storeCodeVerifier: { code in
try localStorage.store(key: key, value: Data(code.utf8))
},
deleteCodeVerifier: {
try localStorage.remove(key: key)
}
get: { code.value },
set: { code.setValue($0) }
)
}()
}
5 changes: 2 additions & 3 deletions Tests/AuthTests/Mocks/Mocks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@ let clientURL = URL(string: "http://localhost:54321/auth/v1")!

extension CodeVerifierStorage {
static let mock = Self(
getCodeVerifier: unimplemented("CodeVerifierStorage.getCodeVerifier"),
storeCodeVerifier: unimplemented("CodeVerifierStorage.storeCodeVerifier"),
deleteCodeVerifier: unimplemented("CodeVerifierStorage.deleteCodeVerifier")
get: unimplemented("CodeVerifierStorage.get"),
set: unimplemented("CodeVerifierStorage.set")
)
}

Expand Down
4 changes: 2 additions & 2 deletions Tests/AuthTests/RequestsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ final class RequestsTests: XCTestCase {

Current.sessionManager = .live
Current.sessionStorage.storeSession = { _ in }
Current.codeVerifierStorage.getCodeVerifier = { nil }
Current.codeVerifierStorage.get = { nil }
Current.currentDate = { currentDate }

let url = URL(
Expand All @@ -184,7 +184,7 @@ final class RequestsTests: XCTestCase {
func testSessionFromURLWithMissingComponent() async {
let sut = makeSUT()

Current.codeVerifierStorage.getCodeVerifier = { nil }
Current.codeVerifierStorage.get = { nil }

let url = URL(
string:
Expand Down

0 comments on commit bfbcddd

Please sign in to comment.