Skip to content

Commit 82fa93d

Browse files
authored
fix(auth): use project ref as namespace for storing token (#430)
1 parent af3ad61 commit 82fa93d

File tree

9 files changed

+59
-8
lines changed

9 files changed

+59
-8
lines changed

Sources/Auth/AuthClientConfiguration.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ extension AuthClient {
2424
public var headers: [String: String]
2525
public let flowType: AuthFlowType
2626
public let redirectToURL: URL?
27+
28+
/// Optional key name used for storing tokens in local storage.
29+
public var storageKey: String?
2730
public let localStorage: any AuthLocalStorage
2831
public let logger: (any SupabaseLogger)?
2932
public let encoder: JSONEncoder
@@ -40,6 +43,7 @@ extension AuthClient {
4043
/// - headers: Custom headers to be included in requests.
4144
/// - flowType: The authentication flow type.
4245
/// - redirectToURL: Default URL to be used for redirect on the flows that requires it.
46+
/// - storageKey: Optional key name used for storing tokens in local storage.
4347
/// - localStorage: The storage mechanism for local data.
4448
/// - logger: The logger to use.
4549
/// - encoder: The JSON encoder to use for encoding requests.
@@ -51,6 +55,7 @@ extension AuthClient {
5155
headers: [String: String] = [:],
5256
flowType: AuthFlowType = Configuration.defaultFlowType,
5357
redirectToURL: URL? = nil,
58+
storageKey: String? = nil,
5459
localStorage: any AuthLocalStorage,
5560
logger: (any SupabaseLogger)? = nil,
5661
encoder: JSONEncoder = AuthClient.Configuration.jsonEncoder,
@@ -64,6 +69,7 @@ extension AuthClient {
6469
self.headers = headers
6570
self.flowType = flowType
6671
self.redirectToURL = redirectToURL
72+
self.storageKey = storageKey
6773
self.localStorage = localStorage
6874
self.logger = logger
6975
self.encoder = encoder
@@ -80,6 +86,7 @@ extension AuthClient {
8086
/// - headers: Custom headers to be included in requests.
8187
/// - flowType: The authentication flow type..
8288
/// - redirectToURL: Default URL to be used for redirect on the flows that requires it.
89+
/// - storageKey: Optional key name used for storing tokens in local storage.
8390
/// - localStorage: The storage mechanism for local data..
8491
/// - logger: The logger to use.
8592
/// - encoder: The JSON encoder to use for encoding requests.
@@ -91,6 +98,7 @@ extension AuthClient {
9198
headers: [String: String] = [:],
9299
flowType: AuthFlowType = AuthClient.Configuration.defaultFlowType,
93100
redirectToURL: URL? = nil,
101+
storageKey: String? = nil,
94102
localStorage: any AuthLocalStorage,
95103
logger: (any SupabaseLogger)? = nil,
96104
encoder: JSONEncoder = AuthClient.Configuration.jsonEncoder,
@@ -104,6 +112,7 @@ extension AuthClient {
104112
headers: headers,
105113
flowType: flowType,
106114
redirectToURL: redirectToURL,
115+
storageKey: storageKey,
107116
localStorage: localStorage,
108117
logger: logger,
109118
encoder: encoder,

Sources/Auth/Defaults.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,6 @@ extension AuthClient.Configuration {
5353

5454
/// The default value when initializing a ``AuthClient`` instance.
5555
public static let defaultAutoRefreshToken: Bool = true
56+
57+
static let defaultStorageKey = "supabase.auth.token"
5658
}

Sources/Auth/Internal/SessionStorage.swift

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,37 @@ struct StoredSession: Codable {
2020
}
2121

2222
extension AuthLocalStorage {
23+
var key: String {
24+
Current.configuration.storageKey ?? AuthClient.Configuration.defaultStorageKey
25+
}
26+
27+
var oldKey: String { "supabase.session" }
28+
2329
func getSession() throws -> Session? {
24-
try retrieve(key: "supabase.session").flatMap {
30+
var storedData = try? retrieve(key: oldKey)
31+
32+
if let storedData {
33+
// migrate to new key.
34+
try store(key: key, value: storedData)
35+
try? remove(key: oldKey)
36+
} else {
37+
storedData = try retrieve(key: key)
38+
}
39+
40+
return try storedData.flatMap {
2541
try AuthClient.Configuration.jsonDecoder.decode(StoredSession.self, from: $0).session
2642
}
2743
}
2844

2945
func storeSession(_ session: Session) throws {
3046
try store(
31-
key: "supabase.session",
47+
key: key,
3248
value: AuthClient.Configuration.jsonEncoder.encode(StoredSession(session: session))
3349
)
3450
}
3551

3652
func deleteSession() throws {
37-
try remove(key: "supabase.session")
53+
try remove(key: key)
54+
try? remove(key: oldKey)
3855
}
3956
}

Sources/Supabase/SupabaseClient.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,15 @@ public final class SupabaseClient: Sendable {
144144
])
145145
.merged(with: HTTPHeaders(options.global.headers))
146146

147+
// default storage key uses the supabase project ref as a namespace
148+
let defaultStorageKey = "sb-\(supabaseURL.host!.split(separator: ".")[0])-auth-token"
149+
147150
auth = AuthClient(
148151
url: supabaseURL.appendingPathComponent("/auth/v1"),
149152
headers: defaultHeaders.dictionary,
150153
flowType: options.auth.flowType,
151154
redirectToURL: options.auth.redirectToURL,
155+
storageKey: options.auth.storageKey ?? defaultStorageKey,
152156
localStorage: options.auth.storage,
153157
logger: options.global.logger,
154158
encoder: options.auth.encoder,

Sources/Supabase/Types.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ public struct SupabaseClientOptions: Sendable {
4444
/// Default URL to be used for redirect on the flows that requires it.
4545
public let redirectToURL: URL?
4646

47+
/// Optional key name used for storing tokens in local storage.
48+
public let storageKey: String?
49+
4750
/// OAuth flow to use - defaults to PKCE flow. PKCE is recommended for mobile and server-side
4851
/// applications.
4952
public let flowType: AuthFlowType
@@ -60,13 +63,15 @@ public struct SupabaseClientOptions: Sendable {
6063
public init(
6164
storage: any AuthLocalStorage,
6265
redirectToURL: URL? = nil,
66+
storageKey: String? = nil,
6367
flowType: AuthFlowType = AuthClient.Configuration.defaultFlowType,
6468
encoder: JSONEncoder = AuthClient.Configuration.jsonEncoder,
6569
decoder: JSONDecoder = AuthClient.Configuration.jsonDecoder,
6670
autoRefreshToken: Bool = AuthClient.Configuration.defaultAutoRefreshToken
6771
) {
6872
self.storage = storage
6973
self.redirectToURL = redirectToURL
74+
self.storageKey = storageKey
7075
self.flowType = flowType
7176
self.encoder = encoder
7277
self.decoder = decoder
@@ -145,6 +150,7 @@ extension SupabaseClientOptions.AuthOptions {
145150
#if !os(Linux)
146151
public init(
147152
redirectToURL: URL? = nil,
153+
storageKey: String? = nil,
148154
flowType: AuthFlowType = AuthClient.Configuration.defaultFlowType,
149155
encoder: JSONEncoder = AuthClient.Configuration.jsonEncoder,
150156
decoder: JSONDecoder = AuthClient.Configuration.jsonDecoder,
@@ -153,6 +159,7 @@ extension SupabaseClientOptions.AuthOptions {
153159
self.init(
154160
storage: AuthClient.Configuration.defaultLocalStorage,
155161
redirectToURL: redirectToURL,
162+
storageKey: storageKey,
156163
flowType: flowType,
157164
encoder: encoder,
158165
decoder: decoder,

Tests/AuthTests/MockHelpers.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Foundation
2+
import TestHelpers
23

34
@testable import Auth
45

@@ -12,3 +13,14 @@ extension Decodable {
1213
self = try! AuthClient.Configuration.jsonDecoder.decode(Self.self, from: json(named: name))
1314
}
1415
}
16+
17+
extension Dependencies {
18+
static var mock = Dependencies(
19+
configuration: AuthClient.Configuration(
20+
url: URL(string: "https://project-id.supabase.com")!,
21+
localStorage: InMemoryLocalStorage(),
22+
logger: nil
23+
),
24+
http: HTTPClientMock()
25+
)
26+
}

Tests/AuthTests/Resources/local-storage.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"supabase.session" : {
2+
"supabase.auth.token" : {
33
"expiration_date" : "2024-04-01T13:25:07.000Z",
44
"session" : {
55
"access_token" : "accesstoken",

Tests/AuthTests/StoredSessionTests.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@ import SnapshotTesting
44
import XCTest
55

66
final class StoredSessionTests: XCTestCase {
7-
override func setUpWithError() throws {
8-
try super.setUpWithError()
9-
}
10-
117
func testStoredSession() throws {
128
let sut = try! DiskTestStorage()
139

10+
Current = .mock
11+
Current.configuration.storageKey = "supabase.auth.token"
12+
1413
let _ = try sut.getSession()
1514

1615
let session = Session(

Tests/SupabaseTests/SupabaseClientTests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ final class SupabaseClientTests: XCTestCase {
8181
XCTAssertIdentical(realtimeOptions.logger as? Logger, logger)
8282

8383
XCTAssertFalse(client.auth.configuration.autoRefreshToken)
84+
XCTAssertEqual(client.auth.configuration.storageKey, "sb-project-ref-auth-token")
8485
}
8586

8687
#if !os(Linux)

0 commit comments

Comments
 (0)