Skip to content

Commit fb6604c

Browse files
authored
Merge pull request #31 from roblack/enable-access-for-token-refresh
Add callback for handling refreshed oauth2 user credentials
2 parents 3f83ed4 + 4becb8d commit fb6604c

File tree

7 files changed

+44
-22
lines changed

7 files changed

+44
-22
lines changed

.swiftpm/xcode/xcshareddata/xcschemes/Twift.xcscheme

+1-9
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@
4040
buildConfiguration = "Debug"
4141
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
4242
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
43-
shouldUseLaunchSchemeArgsEnv = "NO"
44-
codeCoverageEnabled = "YES">
43+
shouldUseLaunchSchemeArgsEnv = "NO">
4544
<EnvironmentVariables>
4645
<EnvironmentVariable
4746
key = "ENVIRONMENT"
@@ -72,13 +71,6 @@
7271
debugDocumentVersioning = "YES"
7372
debugServiceExtension = "internal"
7473
allowLocationSimulation = "YES">
75-
<EnvironmentVariables>
76-
<EnvironmentVariable
77-
key = "ENVIRONMENT"
78-
value = "PRODUCTION"
79-
isEnabled = "YES">
80-
</EnvironmentVariable>
81-
</EnvironmentVariables>
8274
</LaunchAction>
8375
<ProfileAction
8476
buildConfiguration = "Release"

Demo App/Twift_SwiftUI/Twift_SwiftUIApp.swift

+4-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ extension Twift {
1313
switch authenticationType {
1414
case .appOnly(_): return false
1515
case .userAccessTokens(_, _): return true
16-
case .oauth2UserAuth(_): return true
16+
case .oauth2UserAuth(_, _): return true
1717
}
1818
}
1919
}
@@ -50,7 +50,9 @@ struct Twift_SwiftUIApp: App {
5050
scope: Set(OAuth2Scope.allCases))
5151

5252
if let user = user {
53-
container.client = Twift(.oauth2UserAuth(user))
53+
container.client = Twift(oauth2User: user) { refreshedToken in
54+
print(refreshedToken)
55+
}
5456

5557
try? await container.client?.refreshOAuth2AccessToken()
5658
}

README.md

+4-5
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ New `Twift` instances must be initiated with either OAuth 2.0 user authenticatio
1818
```swift
1919
// OAuth 2.0 User Authentication
2020
let oauthUser: OAuth2User = OAUTH2_USER
21-
let userAuthenticatedClient = Twift(.oauth2UserAuth(oauthUser: oauthUser)
21+
let userAuthenticatedClient = Twift(oauth2User: oauthUser, onTokenRefresh: saveUserCredentials)
2222

2323
// App-Only Bearer Token
24-
let appOnlyClient = Twift(.appOnly(bearerToken: BEARER_TOKEN)
24+
let appOnlyClient = Twift(appOnlyBearerToken: BEARER_TOKEN)
2525
```
2626

2727
You can authenticate users with `Twift.Authentication().authenticateUser()`:
@@ -36,12 +36,11 @@ do {
3636
scope: Set(OAuth2Scope.allCases)
3737
)
3838

39-
client = Twift(.oauth2UserAuth(oauthUser))
39+
client = Twift(oauth2User: oauthUser, onTokenRefresh: saveUserCredentials)
4040

4141
// It's recommended that you store your user auth tokens via Keychain or another secure storage method.
4242
// OAuth2User can be encoded to a data object for storage and later retrieval.
43-
let encoded = try? JSONEncoder().encode(oauthUser))
44-
saveUserAuthExample(encoded) // Saves the data to Keychain, for example
43+
saveUserCredentials(oauthUser) // Saves the data to Keychain, for example
4544
} catch {
4645
print(error.localizedDescription)
4746
}

Sources/Twift+API.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ extension Twift {
1212
body: Data? = nil,
1313
expectedReturnType: T.Type
1414
) async throws -> T {
15-
if case AuthenticationType.oauth2UserAuth(_) = self.authenticationType {
15+
if case AuthenticationType.oauth2UserAuth(_, _) = self.authenticationType {
1616
try await self.refreshOAuth2AccessToken()
1717
}
1818

@@ -84,7 +84,7 @@ extension Twift {
8484
consumerCredentials: clientCredentials,
8585
userCredentials: userCredentials
8686
)
87-
case .oauth2UserAuth(let oauthUser):
87+
case .oauth2UserAuth(let oauthUser, _):
8888
request.addValue("Bearer \(oauthUser.accessToken)", forHTTPHeaderField: "Authorization")
8989
}
9090

Sources/Twift+Authentication.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ extension Twift {
1919
/// OAuth 2.0 User Context authentication.
2020
///
2121
/// When this authentication method is used, the `oauth2User` access token may be automatically refreshed by the client if it has expired.
22-
case oauth2UserAuth(_ oauth2User: OAuth2User)
22+
case oauth2UserAuth(_ oauth2User: OAuth2User, onRefresh: ((OAuth2User) -> Void)?)
2323

2424
/// App-only authentication
2525
case appOnly(bearerToken: String)

Sources/Twift.swift

+31-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ import Combine
55
public class Twift: NSObject, ObservableObject {
66
/// The type of authentication access for this Twift instance
77
public private(set) var authenticationType: AuthenticationType
8+
public var oauthUser: OAuth2User? {
9+
switch authenticationType {
10+
case .oauth2UserAuth(let user, _):
11+
return user
12+
default:
13+
return nil
14+
}
15+
}
816

917
internal let decoder: JSONDecoder
1018
internal let encoder: JSONEncoder
@@ -17,6 +25,22 @@ public class Twift: NSObject, ObservableObject {
1725
self.encoder = Self.initializeEncoder()
1826
}
1927

28+
/// Initialises an instance with OAuth2 User authentication
29+
/// - Parameters:
30+
/// - oauth2User: The OAuth2 User object for authenticating requests on behalf of a user
31+
/// - onTokenRefresh: A callback invoked when the access token is refreshed by Twift. Useful for storing updated credentials.
32+
public convenience init(oauth2User: OAuth2User,
33+
onTokenRefresh: @escaping (OAuth2User) -> Void = { _ in }) {
34+
self.init(.oauth2UserAuth(oauth2User, onRefresh: onTokenRefresh))
35+
}
36+
37+
/// Initialises an instance with App-Only Bearer Token authentication
38+
/// - Parameters:
39+
/// - appOnlyBearerToken: The App-Only Bearer Token issued by Twitter for authenticating requests
40+
public convenience init(appOnlyBearerToken: String) {
41+
self.init(.appOnly(bearerToken: appOnlyBearerToken))
42+
}
43+
2044
/// Swift's native implementation of ISO 8601 date decoding defaults to a format that doesn't include milliseconds, causing decoding errors because of Twitter's date format.
2145
/// This function returns a decoder which can decode Twitter's date formats, as well as converting keys from snake_case to camelCase.
2246
static internal func initializeDecoder() -> JSONDecoder {
@@ -71,9 +95,10 @@ public class Twift: NSObject, ObservableObject {
7195
}
7296

7397
/// Refreshes the OAuth 2.0 token, optionally forcing a refresh even if the token is still valid
98+
/// After a successful refresh, a user-defined callback is performed. (optional)
7499
/// - Parameter onlyIfExpired: Set to false to force the token to refresh even if it hasn't yet expired.
75100
public func refreshOAuth2AccessToken(onlyIfExpired: Bool = true) async throws {
76-
guard case AuthenticationType.oauth2UserAuth(let oauthUser) = self.authenticationType else {
101+
guard case AuthenticationType.oauth2UserAuth(let oauthUser, let refreshCompletion) = self.authenticationType else {
77102
throw TwiftError.WrongAuthenticationType(needs: .oauth2UserAuth)
78103
}
79104

@@ -106,6 +131,10 @@ public class Twift: NSObject, ObservableObject {
106131
var refreshedOAuthUser = try JSONDecoder().decode(OAuth2User.self, from: data)
107132
refreshedOAuthUser.clientId = clientId
108133

109-
self.authenticationType = .oauth2UserAuth(refreshedOAuthUser)
134+
if let refreshCompletion = refreshCompletion {
135+
refreshCompletion(refreshedOAuthUser)
136+
}
137+
138+
self.authenticationType = .oauth2UserAuth(refreshedOAuthUser, onRefresh: refreshCompletion)
110139
}
111140
}

Tests/TwiftTests/TwiftTests.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import XCTest
44
@MainActor
55
final class TwiftTests {
66
static var userAuthClient: Twift {
7-
Twift(.oauth2UserAuth(OAuth2User(accessToken: "test", refreshToken: "test_refresh", scope: Set(OAuth2Scope.allCases))))
7+
Twift(.oauth2UserAuth(OAuth2User(accessToken: "test", refreshToken: "test_refresh", scope: Set(OAuth2Scope.allCases)), onRefresh: { _ in }))
88
}
99

1010
static var appOnlyClient: Twift {

0 commit comments

Comments
 (0)