Skip to content

Commit 5de2d8d

Browse files
authored
fix(auth): store session directly without wrapping in StoredSession type (#513)
1 parent a15efb1 commit 5de2d8d

File tree

2 files changed

+113
-65
lines changed

2 files changed

+113
-65
lines changed

Sources/Auth/Internal/SessionStorage.swift

Lines changed: 78 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,61 +8,112 @@
88
import Foundation
99
import Helpers
1010

11-
/// A locally stored ``Session``, it contains metadata such as `expirationDate`.
12-
struct StoredSession: Codable {
13-
var session: Session
14-
var expirationDate: Date
15-
16-
init(session: Session, expirationDate _: Date? = nil) {
17-
self.session = session
18-
expirationDate = Date(timeIntervalSince1970: session.expiresAt)
19-
}
20-
}
21-
2211
struct SessionStorage {
2312
var get: @Sendable () throws -> Session?
2413
var store: @Sendable (_ session: Session) throws -> Void
2514
var delete: @Sendable () throws -> Void
2615
}
2716

2817
extension SessionStorage {
18+
/// Key used to store session on ``AuthLocalStorage``.
19+
///
20+
/// It uses value from ``AuthClient/Configuration/storageKey`` or default to `supabase.auth.token` if not provided.
21+
static func key(_ clientID: AuthClientID) -> String {
22+
Dependencies[clientID].configuration.storageKey ?? STORAGE_KEY
23+
}
24+
2925
static func live(clientID: AuthClientID) -> SessionStorage {
30-
var key: String {
31-
Dependencies[clientID].configuration.storageKey ?? STORAGE_KEY
26+
var storage: any AuthLocalStorage {
27+
Dependencies[clientID].configuration.localStorage
3228
}
3329

34-
var oldKey: String { "supabase.session" }
30+
var logger: (any SupabaseLogger)? {
31+
Dependencies[clientID].configuration.logger
32+
}
3533

36-
var storage: any AuthLocalStorage {
37-
Dependencies[clientID].configuration.localStorage
34+
let migrations: [StorageMigration] = [
35+
.sessionNewKey(clientID: clientID),
36+
.storeSessionDirectly(clientID: clientID),
37+
]
38+
39+
var key: String {
40+
SessionStorage.key(clientID)
3841
}
3942

4043
return SessionStorage(
4144
get: {
42-
var storedData = try? storage.retrieve(key: oldKey)
43-
44-
if let storedData {
45-
// migrate to new key.
46-
try storage.store(key: key, value: storedData)
47-
try? storage.remove(key: oldKey)
48-
} else {
49-
storedData = try storage.retrieve(key: key)
45+
for migration in migrations {
46+
do {
47+
try migration.run()
48+
} catch {
49+
logger?.error("Storage migration failed: \(error.localizedDescription)")
50+
}
5051
}
5152

53+
let storedData = try storage.retrieve(key: key)
5254
return try storedData.flatMap {
53-
try AuthClient.Configuration.jsonDecoder.decode(StoredSession.self, from: $0).session
55+
try AuthClient.Configuration.jsonDecoder.decode(Session.self, from: $0)
5456
}
5557
},
5658
store: { session in
5759
try storage.store(
5860
key: key,
59-
value: AuthClient.Configuration.jsonEncoder.encode(StoredSession(session: session))
61+
value: AuthClient.Configuration.jsonEncoder.encode(session)
6062
)
6163
},
6264
delete: {
6365
try storage.remove(key: key)
64-
try? storage.remove(key: oldKey)
6566
}
6667
)
6768
}
6869
}
70+
71+
struct StorageMigration {
72+
var run: @Sendable () throws -> Void
73+
}
74+
75+
extension StorageMigration {
76+
/// Migrate stored session from `supabase.session` key to the custom provided storage key
77+
/// or the default `supabase.auth.token` key.
78+
static func sessionNewKey(clientID: AuthClientID) -> StorageMigration {
79+
StorageMigration {
80+
let storage = Dependencies[clientID].configuration.localStorage
81+
let newKey = SessionStorage.key(clientID)
82+
83+
if let storedData = try? storage.retrieve(key: "supabase.session") {
84+
// migrate to new key.
85+
try storage.store(key: newKey, value: storedData)
86+
try? storage.remove(key: "supabase.session")
87+
}
88+
}
89+
}
90+
91+
/// Migrate the stored session.
92+
///
93+
/// Migrate the stored session which used to be stored as:
94+
/// ```json
95+
/// {
96+
/// "session": <Session>,
97+
/// "expiration_date": <Date>
98+
/// }
99+
/// ```
100+
/// To directly store the `Session` object.
101+
static func storeSessionDirectly(clientID: AuthClientID) -> StorageMigration {
102+
struct StoredSession: Codable {
103+
var session: Session
104+
var expirationDate: Date
105+
}
106+
107+
return StorageMigration {
108+
let storage = Dependencies[clientID].configuration.localStorage
109+
let key = SessionStorage.key(clientID)
110+
111+
if let data = try? storage.retrieve(key: key),
112+
let storedSession = try? AuthClient.Configuration.jsonDecoder.decode(StoredSession.self, from: data)
113+
{
114+
let session = try AuthClient.Configuration.jsonEncoder.encode(storedSession.session)
115+
try storage.store(key: key, value: session)
116+
}
117+
}
118+
}
119+
}

Tests/AuthTests/Resources/local-storage.json

Lines changed: 35 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,42 @@
11
{
22
"supabase.auth.token" : {
3-
"expiration_date" : "2024-04-01T13:25:07.000Z",
4-
"session" : {
5-
"access_token" : "accesstoken",
6-
"expires_at" : 1711977907,
7-
"expires_in" : 120,
8-
"refresh_token" : "refreshtoken",
9-
"token_type" : "bearer",
10-
"user" : {
11-
"app_metadata" : {
3+
"access_token" : "accesstoken",
4+
"expires_at" : 1711977907,
5+
"expires_in" : 120,
6+
"refresh_token" : "refreshtoken",
7+
"token_type" : "bearer",
8+
"user" : {
9+
"app_metadata" : {
10+
"provider" : "email",
11+
"providers" : [
12+
"email"
13+
]
14+
},
15+
"aud" : "authenticated",
16+
"confirmation_sent_at" : "2022-04-09T11:57:01.000Z",
17+
"created_at" : "2022-04-09T11:57:01.000Z",
18+
"email" : "[email protected]",
19+
"id" : "859F402D-B3DE-4105-A1B9-932836D9193B",
20+
"identities" : [
21+
{
22+
"created_at" : "2022-04-09T11:57:01.000Z",
23+
"id" : "859f402d-b3de-4105-a1b9-932836d9193b",
24+
"identity_data" : {
25+
"sub" : "859f402d-b3de-4105-a1b9-932836d9193b"
26+
},
27+
"identity_id" : "859F402D-B3DE-4105-A1B9-932836D9193B",
28+
"last_sign_in_at" : "2022-04-09T11:57:01.000Z",
1229
"provider" : "email",
13-
"providers" : [
14-
"email"
15-
]
16-
},
17-
"aud" : "authenticated",
18-
"confirmation_sent_at" : "2022-04-09T11:57:01.000Z",
19-
"created_at" : "2022-04-09T11:57:01.000Z",
20-
"email" : "[email protected]",
21-
"id" : "859F402D-B3DE-4105-A1B9-932836D9193B",
22-
"identities" : [
23-
{
24-
"created_at" : "2022-04-09T11:57:01.000Z",
25-
"id" : "859f402d-b3de-4105-a1b9-932836d9193b",
26-
"identity_data" : {
27-
"sub" : "859f402d-b3de-4105-a1b9-932836d9193b"
28-
},
29-
"identity_id" : "859F402D-B3DE-4105-A1B9-932836D9193B",
30-
"last_sign_in_at" : "2022-04-09T11:57:01.000Z",
31-
"provider" : "email",
32-
"updated_at" : "2022-04-09T11:57:01.000Z",
33-
"user_id" : "859F402D-B3DE-4105-A1B9-932836D9193B"
34-
}
35-
],
36-
"is_anonymous" : false,
37-
"phone" : "",
38-
"role" : "authenticated",
39-
"updated_at" : "2022-04-09T11:57:01.000Z",
40-
"user_metadata" : {
41-
"referrer_id" : null
30+
"updated_at" : "2022-04-09T11:57:01.000Z",
31+
"user_id" : "859F402D-B3DE-4105-A1B9-932836D9193B"
4232
}
33+
],
34+
"is_anonymous" : false,
35+
"phone" : "",
36+
"role" : "authenticated",
37+
"updated_at" : "2022-04-09T11:57:01.000Z",
38+
"user_metadata" : {
39+
"referrer_id" : null
4340
}
4441
}
4542
}

0 commit comments

Comments
 (0)