@superfan-app/spotify-auth 0.1.29 → 0.1.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -233,33 +233,33 @@ final class SpotifyAuthAuth: NSObject, SPTSessionManagerDelegate {
|
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
private func securelyStoreToken(_ session: SPTSession) {
|
|
236
|
-
//
|
|
236
|
+
// Pass token to JS and securely store refresh token
|
|
237
237
|
module?.onAccessTokenObtained(session.accessToken)
|
|
238
238
|
|
|
239
|
-
//
|
|
240
|
-
|
|
239
|
+
// Since refreshToken is now a non-optional String, we simply check for an empty value.
|
|
240
|
+
let refreshToken = session.refreshToken
|
|
241
|
+
if !refreshToken.isEmpty {
|
|
241
242
|
do {
|
|
242
243
|
let keychainKey = try getKeychainKey()
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
)
|
|
244
|
+
// Create a Keychain instance (using bundle identifier as the service)
|
|
245
|
+
let keychain = Keychain(service: Bundle.main.bundleIdentifier ?? "com.superfan.app")
|
|
246
|
+
.accessibility(.afterFirstUnlock)
|
|
247
|
+
try keychain.set(refreshToken, key: keychainKey)
|
|
248
248
|
} catch {
|
|
249
|
-
print("Failed to store refresh token securely")
|
|
249
|
+
print("Failed to store refresh token securely: \(error)")
|
|
250
250
|
}
|
|
251
251
|
}
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
private func cleanupPreviousSession() {
|
|
255
|
-
// Clear any sensitive data from previous session
|
|
256
255
|
refreshTimer?.invalidate()
|
|
257
256
|
|
|
258
257
|
do {
|
|
259
258
|
let keychainKey = try getKeychainKey()
|
|
260
|
-
|
|
259
|
+
let keychain = Keychain(service: Bundle.main.bundleIdentifier ?? "com.superfan.app")
|
|
260
|
+
try keychain.remove(keychainKey)
|
|
261
261
|
} catch {
|
|
262
|
-
print("Failed to clear previous refresh token")
|
|
262
|
+
print("Failed to clear previous refresh token: \(error)")
|
|
263
263
|
}
|
|
264
264
|
|
|
265
265
|
// Clear in-memory session data
|
|
@@ -287,8 +287,9 @@ final class SpotifyAuthAuth: NSObject, SPTSessionManagerDelegate {
|
|
|
287
287
|
let scopes = try self.requestedScopes
|
|
288
288
|
isAuthenticating = true
|
|
289
289
|
|
|
290
|
-
|
|
291
|
-
|
|
290
|
+
// Updated: Use SPTSessionManagerOptions (an OptionSet) and pass nil for campaign.
|
|
291
|
+
let options: SPTSessionManagerOptions = showDialog ? .clientOnly : []
|
|
292
|
+
sessionManager.initiateSession(with: scopes, options: options, campaign: nil)
|
|
292
293
|
} catch {
|
|
293
294
|
isAuthenticating = false
|
|
294
295
|
handleError(error, context: "authentication")
|
|
@@ -304,7 +305,8 @@ final class SpotifyAuthAuth: NSObject, SPTSessionManagerDelegate {
|
|
|
304
305
|
}
|
|
305
306
|
let scopes = try self.requestedScopes
|
|
306
307
|
isAuthenticating = true
|
|
307
|
-
|
|
308
|
+
// Updated: Use SPTSessionManagerOptions and pass nil for campaign.
|
|
309
|
+
sessionManager.initiateSession(with: scopes, options: [], campaign: nil)
|
|
308
310
|
} catch {
|
|
309
311
|
isAuthenticating = false
|
|
310
312
|
handleError(error, context: "authentication_retry")
|
|
@@ -382,19 +384,10 @@ final class SpotifyAuthAuth: NSObject, SPTSessionManagerDelegate {
|
|
|
382
384
|
private func handleError(_ error: Error, context: String) {
|
|
383
385
|
let spotifyError: SpotifyAuthError
|
|
384
386
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
case .configurationError:
|
|
390
|
-
spotifyError = .invalidConfiguration("Invalid Spotify configuration")
|
|
391
|
-
case .authenticationError:
|
|
392
|
-
spotifyError = .authenticationFailed("Please try authenticating again")
|
|
393
|
-
case .loggedOut:
|
|
394
|
-
spotifyError = .sessionError("User logged out")
|
|
395
|
-
default:
|
|
396
|
-
spotifyError = .authenticationFailed(sptError.localizedDescription)
|
|
397
|
-
}
|
|
387
|
+
// Instead of switching on SPTError cases (which are no longer available),
|
|
388
|
+
// we simply wrap the error’s description.
|
|
389
|
+
if error is SPTError {
|
|
390
|
+
spotifyError = .authenticationFailed(error.localizedDescription)
|
|
398
391
|
} else {
|
|
399
392
|
spotifyError = .authenticationFailed(error.localizedDescription)
|
|
400
393
|
}
|
|
@@ -459,4 +452,4 @@ final class SpotifyAuthAuth: NSObject, SPTSessionManagerDelegate {
|
|
|
459
452
|
deinit {
|
|
460
453
|
cleanupPreviousSession()
|
|
461
454
|
}
|
|
462
|
-
}
|
|
455
|
+
}
|
|
@@ -17,7 +17,8 @@ public class SpotifyAuthDelegate: ExpoAppDelegateSubscriber, SPTSessionManagerDe
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
public func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
|
|
20
|
-
|
|
20
|
+
// Use optional chaining to avoid calling on a nil session manager.
|
|
21
|
+
spotifyAuth.sessionManager?.application(app, open: url, options: options)
|
|
21
22
|
return true
|
|
22
23
|
}
|
|
23
|
-
}
|
|
24
|
+
}
|
|
@@ -33,9 +33,6 @@ struct AuthorizeConfig: Record {
|
|
|
33
33
|
public class SpotifyAuthModule: Module {
|
|
34
34
|
let spotifyAuth = SpotifyAuthAuth.shared
|
|
35
35
|
|
|
36
|
-
// Each module class must implement the definition function. The definition consists of components
|
|
37
|
-
// that describes the module's functionality and behavior.
|
|
38
|
-
// See https://docs.expo.dev/modules/module-api for more details about available components.
|
|
39
36
|
public func definition() -> ModuleDefinition {
|
|
40
37
|
Name("SpotifyAuth")
|
|
41
38
|
|
|
@@ -51,12 +48,12 @@ public class SpotifyAuthModule: Module {
|
|
|
51
48
|
// Defines event names that the module can send to JavaScript.
|
|
52
49
|
Events(SPOTIFY_AUTHORIZATION_EVENT_NAME)
|
|
53
50
|
|
|
54
|
-
//
|
|
51
|
+
// Called when JS starts observing the event.
|
|
55
52
|
OnStartObserving {
|
|
56
53
|
secureLog("Started observing events")
|
|
57
54
|
}
|
|
58
55
|
|
|
59
|
-
//
|
|
56
|
+
// Called when JS stops observing the event.
|
|
60
57
|
OnStopObserving {
|
|
61
58
|
secureLog("Stopped observing events")
|
|
62
59
|
}
|
|
@@ -64,7 +61,7 @@ public class SpotifyAuthModule: Module {
|
|
|
64
61
|
AsyncFunction("authorize") { (config: AuthorizeConfig, promise: Promise) in
|
|
65
62
|
secureLog("Authorization requested")
|
|
66
63
|
|
|
67
|
-
// Sanitize and validate redirect URL
|
|
64
|
+
// Sanitize and validate redirect URL.
|
|
68
65
|
guard let url = URL(string: config.redirectUrl),
|
|
69
66
|
url.scheme != nil,
|
|
70
67
|
url.host != nil else {
|
|
@@ -72,22 +69,21 @@ public class SpotifyAuthModule: Module {
|
|
|
72
69
|
return
|
|
73
70
|
}
|
|
74
71
|
|
|
75
|
-
|
|
72
|
+
// Create a configuration (this example does not use the variable afterwards).
|
|
73
|
+
let _ = SPTConfiguration(clientID: config.clientId, redirectURL: url)
|
|
76
74
|
|
|
77
75
|
do {
|
|
78
76
|
try spotifyAuth.initAuth(showDialog: config.showDialog)
|
|
79
77
|
promise.resolve()
|
|
80
78
|
} catch {
|
|
81
|
-
// Sanitize error message
|
|
79
|
+
// Sanitize error message.
|
|
82
80
|
let sanitizedError = sanitizeErrorMessage(error.localizedDescription)
|
|
83
81
|
promise.reject(SpotifyAuthError.authenticationFailed(sanitizedError))
|
|
84
82
|
}
|
|
85
83
|
}
|
|
86
84
|
|
|
87
|
-
// Enables the module to be used as a native view.
|
|
88
|
-
// view definition: Prop, Events.
|
|
85
|
+
// Enables the module to be used as a native view.
|
|
89
86
|
View(SpotifyAuthView.self) {
|
|
90
|
-
// Defines a setter for the `name` prop.
|
|
91
87
|
Prop("name") { (_: SpotifyAuthView, prop: String) in
|
|
92
88
|
secureLog("View prop updated: \(prop)")
|
|
93
89
|
}
|
|
@@ -95,7 +91,7 @@ public class SpotifyAuthModule: Module {
|
|
|
95
91
|
}
|
|
96
92
|
|
|
97
93
|
private func sanitizeErrorMessage(_ message: String) -> String {
|
|
98
|
-
// Remove
|
|
94
|
+
// Remove potential sensitive data from error messages.
|
|
99
95
|
let sensitivePatterns = [
|
|
100
96
|
"(?i)client[_-]?id",
|
|
101
97
|
"(?i)token",
|
|
@@ -124,7 +120,7 @@ public class SpotifyAuthModule: Module {
|
|
|
124
120
|
let eventData: [String: Any] = [
|
|
125
121
|
"success": true,
|
|
126
122
|
"token": token,
|
|
127
|
-
"error": nil
|
|
123
|
+
"error": NSNull() // Use NSNull() instead of nil.
|
|
128
124
|
]
|
|
129
125
|
sendEvent(SPOTIFY_AUTHORIZATION_EVENT_NAME, eventData)
|
|
130
126
|
}
|
|
@@ -134,8 +130,8 @@ public class SpotifyAuthModule: Module {
|
|
|
134
130
|
secureLog("User signed out")
|
|
135
131
|
let eventData: [String: Any] = [
|
|
136
132
|
"success": true,
|
|
137
|
-
"token": nil
|
|
138
|
-
"error": nil
|
|
133
|
+
"token": NSNull(), // Use NSNull() instead of nil.
|
|
134
|
+
"error": NSNull() // Use NSNull() instead of nil.
|
|
139
135
|
]
|
|
140
136
|
sendEvent(SPOTIFY_AUTHORIZATION_EVENT_NAME, eventData)
|
|
141
137
|
}
|
|
@@ -146,9 +142,9 @@ public class SpotifyAuthModule: Module {
|
|
|
146
142
|
secureLog("Authorization error: \(sanitizedError)")
|
|
147
143
|
let eventData: [String: Any] = [
|
|
148
144
|
"success": false,
|
|
149
|
-
"token": nil
|
|
145
|
+
"token": NSNull(), // Use NSNull() instead of nil.
|
|
150
146
|
"error": sanitizedError
|
|
151
147
|
]
|
|
152
148
|
sendEvent(SPOTIFY_AUTHORIZATION_EVENT_NAME, eventData)
|
|
153
149
|
}
|
|
154
|
-
}
|
|
150
|
+
}
|