Skip to content

Commit 9f50f1f

Browse files
authored
Merge pull request #27 from Tunous/throwing-authenticateUser
2 parents 53e512c + c5dd6c6 commit 9f50f1f

File tree

3 files changed

+45
-24
lines changed

3 files changed

+45
-24
lines changed

Demo App/Twift_SwiftUI/Twift_SwiftUIApp.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ struct Twift_SwiftUIApp: App {
4545
footer: Text("Use this authentication method for most cases. This test app enables all user scopes by default.")
4646
) {
4747
AsyncButton {
48-
let (user, _) = await Twift.Authentication().authenticateUser(clientId: CLIENT_ID,
48+
let user = try? await Twift.Authentication().authenticateUser(clientId: CLIENT_ID,
4949
redirectUri: URL(string: TWITTER_CALLBACK_URL)!,
5050
scope: Set(OAuth2Scope.allCases))
5151

README.md

+9-7
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,21 @@ You can authenticate users with `Twift.Authentication().authenticateUser()`:
2929
```swift
3030
var client: Twift?
3131

32-
let (oauthUser, error) = await Twift.Authentication().authenticateUser(
33-
clientId: TWITTER_CLIENT_ID,
34-
redirectUri: URL(string: TWITTER_CALLBACK_URL)!,
35-
scope: Set(OAuth2Scope.allCases)
36-
)
37-
38-
if let oauthUser = oauthUser {
32+
do {
33+
let oauthUser = try await Twift.Authentication().authenticateUser(
34+
clientId: TWITTER_CLIENT_ID,
35+
redirectUri: URL(string: TWITTER_CALLBACK_URL)!,
36+
scope: Set(OAuth2Scope.allCases)
37+
)
38+
3939
client = Twift(.oauth2UserAuth(oauthUser))
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.
4343
let encoded = try? JSONEncoder().encode(oauthUser))
4444
saveUserAuthExample(encoded) // Saves the data to Keychain, for example
45+
} catch {
46+
print(error.localizedDescription)
4547
}
4648
```
4749

Sources/Twift+Authentication.swift

+35-16
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,33 @@ extension Twift.Authentication {
144144
/// - scope: The user access scopes for your authentication. For automatic token refreshing, ensure that `offlineAccess` is included in the scope.
145145
/// - presentationContextProvider: Optional presentation context provider. When not provided, this function will handle the presentation context itself.
146146
/// - Returns: A tuple containing the authenticated user access tokens or any encoutered error.
147+
@_disfavoredOverload
148+
@available(*, deprecated, message: "Use throwing 'authenticateUser' function instead")
147149
public func authenticateUser(clientId: String,
148150
redirectUri: URL,
149151
scope: Set<OAuth2Scope>,
150152
presentationContextProvider: ASWebAuthenticationPresentationContextProviding? = nil
151153
) async -> (OAuth2User?, Error?) {
154+
do {
155+
let oauthUser: OAuth2User = try await authenticateUser(clientId: clientId, redirectUri: redirectUri, scope: scope, presentationContextProvider: presentationContextProvider)
156+
return (oauthUser, nil)
157+
} catch {
158+
return (nil, error)
159+
}
160+
}
161+
162+
/// Authenticates the user using Twitter's OAuth 2.0 PKCE flow.
163+
/// - Parameters:
164+
/// - clientId: The client ID for your Twitter API app
165+
/// - redirectUri: The URI to redirect users to after completing authentication.
166+
/// - scope: The user access scopes for your authentication. For automatic token refreshing, ensure that `offlineAccess` is included in the scope.
167+
/// - presentationContextProvider: Optional presentation context provider. When not provided, this function will handle the presentation context itself.
168+
/// - Returns: The authenticated user access tokens.
169+
public func authenticateUser(clientId: String,
170+
redirectUri: URL,
171+
scope: Set<OAuth2Scope>,
172+
presentationContextProvider: ASWebAuthenticationPresentationContextProviding? = nil
173+
) async throws -> OAuth2User {
152174
let state = UUID().uuidString
153175

154176
let authUrlQueryItems: [URLQueryItem] = [
@@ -167,39 +189,36 @@ extension Twift.Authentication {
167189
authUrl.path = "/i/oauth2/authorize"
168190
authUrl.queryItems = authUrlQueryItems
169191

170-
let (returnedUrl, error): (URL?, Error?) = await withCheckedContinuation { continuation in
192+
let returnedUrl: URL = try await withCheckedThrowingContinuation { continuation in
171193
guard let authUrl = authUrl.url else {
172-
return continuation.resume(returning: (nil, TwiftError.UnknownError(nil)))
194+
return continuation.resume(throwing: TwiftError.UnknownError(nil))
173195
}
174196

175197
let authSession = ASWebAuthenticationSession(url: authUrl, callbackURLScheme: redirectUri.scheme) { (url, error) in
176-
return continuation.resume(returning: (url, error))
198+
if let error = error {
199+
return continuation.resume(throwing: error)
200+
}
201+
if let url = url {
202+
return continuation.resume(returning: url)
203+
}
204+
return continuation.resume(throwing: TwiftError.UnknownError("There was a problem authenticating the user: no URL was returned from the first authentication step."))
177205
}
178206

179207
authSession.presentationContextProvider = presentationContextProvider ?? self
180208
authSession.start()
181209
}
182210

183-
if let error = error {
184-
print(error.localizedDescription)
185-
return (nil, error)
186-
}
187-
188-
guard let returnedUrl = returnedUrl else {
189-
return (nil, TwiftError.UnknownError("There was a problem authenticating the user: no URL was returned from the first authentication step."))
190-
}
191-
192211
let returnedUrlComponents = URLComponents(string: returnedUrl.absoluteString)
193212

194213
let returnedState = returnedUrlComponents?.queryItems?.first(where: { $0.name == "state" })?.value
195214
guard let returnedState = returnedState,
196215
returnedState == state else {
197-
return (nil, TwiftError.UnknownError("There was a problem authenticating the user: the state values for the first authentication step are not equal."))
216+
throw TwiftError.UnknownError("There was a problem authenticating the user: the state values for the first authentication step are not equal.")
198217
}
199218

200219
let returnedCode = returnedUrlComponents?.queryItems?.first(where: { $0.name == "code" })?.value
201220
guard let returnedCode = returnedCode else {
202-
return (nil, TwiftError.UnknownError("There was a problem authenticating the user: no request token was found in the returned URL."))
221+
throw TwiftError.UnknownError("There was a problem authenticating the user: no request token was found in the returned URL.")
203222
}
204223

205224
var codeRequest = URLRequest(url: URL(string: "https://api.twitter.com/2/oauth2/token")!)
@@ -223,12 +242,12 @@ extension Twift.Authentication {
223242
var oauth2User = try JSONDecoder().decode(OAuth2User.self, from: data)
224243
oauth2User.clientId = clientId
225244

226-
return (oauth2User, nil)
245+
return oauth2User
227246
} catch {
228247
print(error.localizedDescription)
229248
}
230249

231-
return (nil, TwiftError.UnknownError("Unable to fetch and decode the OAuth 2.0 user context."))
250+
throw TwiftError.UnknownError("Unable to fetch and decode the OAuth 2.0 user context.")
232251
}
233252
}
234253

0 commit comments

Comments
 (0)