Skip to content

Commit

Permalink
fix(auth): use project ref as namespace for storing token (#430)
Browse files Browse the repository at this point in the history
  • Loading branch information
grdsdev authored Jun 28, 2024
1 parent af3ad61 commit 82fa93d
Show file tree
Hide file tree
Showing 9 changed files with 59 additions and 8 deletions.
9 changes: 9 additions & 0 deletions Sources/Auth/AuthClientConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ extension AuthClient {
public var headers: [String: String]
public let flowType: AuthFlowType
public let redirectToURL: URL?

/// Optional key name used for storing tokens in local storage.
public var storageKey: String?
public let localStorage: any AuthLocalStorage
public let logger: (any SupabaseLogger)?
public let encoder: JSONEncoder
Expand All @@ -40,6 +43,7 @@ extension AuthClient {
/// - headers: Custom headers to be included in requests.
/// - flowType: The authentication flow type.
/// - redirectToURL: Default URL to be used for redirect on the flows that requires it.
/// - storageKey: Optional key name used for storing tokens in local storage.
/// - localStorage: The storage mechanism for local data.
/// - logger: The logger to use.
/// - encoder: The JSON encoder to use for encoding requests.
Expand All @@ -51,6 +55,7 @@ extension AuthClient {
headers: [String: String] = [:],
flowType: AuthFlowType = Configuration.defaultFlowType,
redirectToURL: URL? = nil,
storageKey: String? = nil,
localStorage: any AuthLocalStorage,
logger: (any SupabaseLogger)? = nil,
encoder: JSONEncoder = AuthClient.Configuration.jsonEncoder,
Expand All @@ -64,6 +69,7 @@ extension AuthClient {
self.headers = headers
self.flowType = flowType
self.redirectToURL = redirectToURL
self.storageKey = storageKey
self.localStorage = localStorage
self.logger = logger
self.encoder = encoder
Expand All @@ -80,6 +86,7 @@ extension AuthClient {
/// - headers: Custom headers to be included in requests.
/// - flowType: The authentication flow type..
/// - redirectToURL: Default URL to be used for redirect on the flows that requires it.
/// - storageKey: Optional key name used for storing tokens in local storage.
/// - localStorage: The storage mechanism for local data..
/// - logger: The logger to use.
/// - encoder: The JSON encoder to use for encoding requests.
Expand All @@ -91,6 +98,7 @@ extension AuthClient {
headers: [String: String] = [:],
flowType: AuthFlowType = AuthClient.Configuration.defaultFlowType,
redirectToURL: URL? = nil,
storageKey: String? = nil,
localStorage: any AuthLocalStorage,
logger: (any SupabaseLogger)? = nil,
encoder: JSONEncoder = AuthClient.Configuration.jsonEncoder,
Expand All @@ -104,6 +112,7 @@ extension AuthClient {
headers: headers,
flowType: flowType,
redirectToURL: redirectToURL,
storageKey: storageKey,
localStorage: localStorage,
logger: logger,
encoder: encoder,
Expand Down
2 changes: 2 additions & 0 deletions Sources/Auth/Defaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,6 @@ extension AuthClient.Configuration {

/// The default value when initializing a ``AuthClient`` instance.
public static let defaultAutoRefreshToken: Bool = true

static let defaultStorageKey = "supabase.auth.token"
}
23 changes: 20 additions & 3 deletions Sources/Auth/Internal/SessionStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,37 @@ struct StoredSession: Codable {
}

extension AuthLocalStorage {
var key: String {
Current.configuration.storageKey ?? AuthClient.Configuration.defaultStorageKey
}

var oldKey: String { "supabase.session" }

func getSession() throws -> Session? {
try retrieve(key: "supabase.session").flatMap {
var storedData = try? retrieve(key: oldKey)

if let storedData {
// migrate to new key.
try store(key: key, value: storedData)
try? remove(key: oldKey)
} else {
storedData = try retrieve(key: key)
}

return try storedData.flatMap {
try AuthClient.Configuration.jsonDecoder.decode(StoredSession.self, from: $0).session
}
}

func storeSession(_ session: Session) throws {
try store(
key: "supabase.session",
key: key,
value: AuthClient.Configuration.jsonEncoder.encode(StoredSession(session: session))
)
}

func deleteSession() throws {
try remove(key: "supabase.session")
try remove(key: key)
try? remove(key: oldKey)
}
}
4 changes: 4 additions & 0 deletions Sources/Supabase/SupabaseClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,15 @@ public final class SupabaseClient: Sendable {
])
.merged(with: HTTPHeaders(options.global.headers))

// default storage key uses the supabase project ref as a namespace
let defaultStorageKey = "sb-\(supabaseURL.host!.split(separator: ".")[0])-auth-token"

auth = AuthClient(
url: supabaseURL.appendingPathComponent("/auth/v1"),
headers: defaultHeaders.dictionary,
flowType: options.auth.flowType,
redirectToURL: options.auth.redirectToURL,
storageKey: options.auth.storageKey ?? defaultStorageKey,
localStorage: options.auth.storage,
logger: options.global.logger,
encoder: options.auth.encoder,
Expand Down
7 changes: 7 additions & 0 deletions Sources/Supabase/Types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ public struct SupabaseClientOptions: Sendable {
/// Default URL to be used for redirect on the flows that requires it.
public let redirectToURL: URL?

/// Optional key name used for storing tokens in local storage.
public let storageKey: String?

/// OAuth flow to use - defaults to PKCE flow. PKCE is recommended for mobile and server-side
/// applications.
public let flowType: AuthFlowType
Expand All @@ -60,13 +63,15 @@ public struct SupabaseClientOptions: Sendable {
public init(
storage: any AuthLocalStorage,
redirectToURL: URL? = nil,
storageKey: String? = nil,
flowType: AuthFlowType = AuthClient.Configuration.defaultFlowType,
encoder: JSONEncoder = AuthClient.Configuration.jsonEncoder,
decoder: JSONDecoder = AuthClient.Configuration.jsonDecoder,
autoRefreshToken: Bool = AuthClient.Configuration.defaultAutoRefreshToken
) {
self.storage = storage
self.redirectToURL = redirectToURL
self.storageKey = storageKey
self.flowType = flowType
self.encoder = encoder
self.decoder = decoder
Expand Down Expand Up @@ -145,6 +150,7 @@ extension SupabaseClientOptions.AuthOptions {
#if !os(Linux)
public init(
redirectToURL: URL? = nil,
storageKey: String? = nil,
flowType: AuthFlowType = AuthClient.Configuration.defaultFlowType,
encoder: JSONEncoder = AuthClient.Configuration.jsonEncoder,
decoder: JSONDecoder = AuthClient.Configuration.jsonDecoder,
Expand All @@ -153,6 +159,7 @@ extension SupabaseClientOptions.AuthOptions {
self.init(
storage: AuthClient.Configuration.defaultLocalStorage,
redirectToURL: redirectToURL,
storageKey: storageKey,
flowType: flowType,
encoder: encoder,
decoder: decoder,
Expand Down
12 changes: 12 additions & 0 deletions Tests/AuthTests/MockHelpers.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Foundation
import TestHelpers

@testable import Auth

Expand All @@ -12,3 +13,14 @@ extension Decodable {
self = try! AuthClient.Configuration.jsonDecoder.decode(Self.self, from: json(named: name))
}
}

extension Dependencies {
static var mock = Dependencies(
configuration: AuthClient.Configuration(
url: URL(string: "https://project-id.supabase.com")!,
localStorage: InMemoryLocalStorage(),
logger: nil
),
http: HTTPClientMock()
)
}
2 changes: 1 addition & 1 deletion Tests/AuthTests/Resources/local-storage.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"supabase.session" : {
"supabase.auth.token" : {
"expiration_date" : "2024-04-01T13:25:07.000Z",
"session" : {
"access_token" : "accesstoken",
Expand Down
7 changes: 3 additions & 4 deletions Tests/AuthTests/StoredSessionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ import SnapshotTesting
import XCTest

final class StoredSessionTests: XCTestCase {
override func setUpWithError() throws {
try super.setUpWithError()
}

func testStoredSession() throws {
let sut = try! DiskTestStorage()

Current = .mock
Current.configuration.storageKey = "supabase.auth.token"

let _ = try sut.getSession()

let session = Session(
Expand Down
1 change: 1 addition & 0 deletions Tests/SupabaseTests/SupabaseClientTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ final class SupabaseClientTests: XCTestCase {
XCTAssertIdentical(realtimeOptions.logger as? Logger, logger)

XCTAssertFalse(client.auth.configuration.autoRefreshToken)
XCTAssertEqual(client.auth.configuration.storageKey, "sb-project-ref-auth-token")
}

#if !os(Linux)
Expand Down

0 comments on commit 82fa93d

Please sign in to comment.