-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Gravatar OAuth in SwiftUI #359
Conversation
enum OAuthError: Error { | ||
case notConfigured | ||
case couldNotCreateOAuthURLWithGivenSecrets | ||
case couldNotGetCodeFromURL(String) | ||
case oauthResponseError(String) | ||
case unknown(Error) | ||
case couldNotStoreToken(Error) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This became a bit messy and I don't like it:
notConfigured
: Secrets are not set up. This maybe could be aassertionFailure
instead of throwing? The idea is to warn the developer.couldNotCreateOAuthURLWithGivenSecrets
this comes from usingEncodable
to add the query items to the URL. Probably if we add them manually we wouldn't need this error. And if this error happens, it's probably a developer mistake which should be catch on dev time (maybe assertion also?)case couldNotGetCodeFromURL(String)
error on parsing the callback URL to get the code. This could be a runtime error but not sure how could this be handled 🤔 .oauthResponseError(String)
Something went wrong at the backend. Needs to be handled in the UI.unknown
¯_(ツ)_/¯ ... Probably on the UI also.couldNotStoreToken(Error)
Keychain error. I got errors here because the keychan was wrongly set up. But it was a dev issue. Probably there are instances that this could be a user issue, but again, not sure how this should be handled. Technically we have the token and the user should be able to edit the profile, so I don't think that throwing is a good option. The problem is that they will need to be authenticated again next time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add the assertionFailures for notConfigured
and couldNotCreateOAuthURLWithGivenSecrets
but not necessarily replace the "throw" statements. We still need the throws for prod. Throws also allow us return non-optional values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
couldNotStoreToken(Error) Keychain error. I got errors here because the keychan was wrongly set up. But it was a dev issue. Probably there are instances that this could be a user issue, but again, not sure how this should be handled. Technically we have the token and the user should be able to edit the profile, so I don't think that throwing is a good option. The problem is that they will need to be authenticated again next time
As you said, since the user is able to continue I think we don't need to re-throw this one. This can be something we swallow inside a separate do { ... } catch {...}
and handle just with an assertionFailure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks pretty good overall. Just dropping some non-critical comments and a question.
We need UI for this 2 case but it doesn't have to be in this PR.
- we don’t have the token yet but we’ll try (we need an activity indicating UI)
- we tried to get the token but we failed (we can reuse the ContentLoadingErrorView here as well)
enum OAuthError: Error { | ||
case notConfigured | ||
case couldNotCreateOAuthURLWithGivenSecrets | ||
case couldNotGetCodeFromURL(String) | ||
case oauthResponseError(String) | ||
case unknown(Error) | ||
case couldNotStoreToken(Error) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add the assertionFailures for notConfigured
and couldNotCreateOAuthURLWithGivenSecrets
but not necessarily replace the "throw" statements. We still need the throws for prod. Throws also allow us return non-optional values.
case let error as Keychain.KeychainError: | ||
return .couldNotStoreToken(error) | ||
default: | ||
print("🛑 Error: \(type(of: error)) - \(error.localizedDescription)") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's remove the "print" call. We can probably use the Apple’s os.log framework for logging, doesn't have to be in this PR though. 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, this was for debugging and see the types of the error thrown to be catch 👍
os.log
is a good idea. It might help developers debug issues on their users.
|
||
extension OAuthError { | ||
static func from(error: Error) -> OAuthError { | ||
switch error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can catch "DecodingError"s and re-throw them after adding an assertionFailure maybe? This means our decoding keys or types are not matching the response, so the developer should fix it.
case let error as DecodingError:
assertionFailure("Unable to decode the response. Error: \(error.description)")
throw .decodingError(error)
case .avatarPicker: | ||
AvatarPickerView(model: .init(email: email, authToken: token)) | ||
} | ||
Button("Log out (for testing only)") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is meant to be a temp Button I guess but I feel like it can be useful during development in the long run as well. So how about we wrap it with some kind of compiler flag and keep it?
#if DEMO
#endif
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pinarol - I tried this and the Build Conditions from the demo app won't be passed to the package, so there's no access to it from within the SDK.
I decided to move this button to the demo screen, previous to present the picker.
Now we will have a Log out
button when we detect a session:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. That works too. 👍
📲 You can test the changes from this Pull Request in Gravatar UIKit Prototype Build by scanning the QR code below to install the corresponding build.
|
📲 You can test the changes from this Pull Request in Gravatar SwiftUI Prototype Build by scanning the QR code below to install the corresponding build.
|
Task { | ||
isAuthenticating = true | ||
if !oauthSession.hasSession(with: email) { | ||
_ = try? await oauthSession.retrieveAccessToken(with: email) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The possible errors are still not yet properly handled here, but we can do this alongside building the error screens to present these errors.
@pinarol - I believe this is ready for another look 👀 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, just leaving some nits. I don't need to check again.
Demo/Demo/Gravatar-SwiftUI-Demo/DemoProfileEditorViewView.swift
Outdated
Show resolved
Hide resolved
Oh I see. Cool. |
Description
This PR implements the Gravatar OAuth flow.
Testing Steps