-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
MBL-1159: Exchange temporary token for OAuth token and login #1950
MBL-1159: Exchange temporary token for OAuth token and login #1950
Conversation
c60f91a
to
a66eb25
Compare
return OAuth.createAuthorizationSession { [weak self] result in | ||
switch result { | ||
case .loggedIn: | ||
self?.viewModel.inputs.environmentLoggedIn() |
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 went back-and-forth on where to put which pieces of logic. I settled on having the actual login call live in OAuth.swift
, but the view controller is responsible for updating the UI when the login is complete.
@@ -284,6 +284,9 @@ public protocol ServiceType { | |||
/// Fetch the logged-in user's data. | |||
func fetchUserSelf() -> SignalProducer<User, ErrorEnvelope> | |||
|
|||
/// Fetch the logged-in user's data. | |||
func fetchUserSelf_combine(withToken: String) -> AnyPublisher<User, ErrorEnvelope> |
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 will make more sense in context - AppEnvironment.login
takes a token and a user. Our new endpoint returns the new oauth token but not the user; so I needed a way to fetch the current user manually, without actually being logged in. That resulted in this "fetch me the user, given this token" endpoint.
I've discussed this with @santiDotIO and @Arkariang , hopefully we'll be able to get the User
in the original exchange call, and then I'll clean this up.
@@ -243,6 +248,9 @@ internal enum Route { | |||
case .userSelf: | |||
return (.GET, "/v1/users/self", [:], nil) | |||
|
|||
case let .userSelfWithToken(token): | |||
return (.GET, "/v1/users/self", ["oauth_token": token], nil) |
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.
Parameters passed in here will override the defaults, which in this case will be missing the oauth_token
.
Library/OAuth.swift
Outdated
authenticationError.code == .canceledLogin { | ||
onComplete(.cancelled) | ||
} else { | ||
onComplete(.failure(errorMessage: Strings.Something_went_wrong_please_try_again())) |
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.
These are all errors that aren't user-solvable, so giving them a generic error message.
KsApi/MockService.swift
Outdated
func exchangeTokenForOAuthToken(params _: OAuthTokenExchangeParams) | ||
-> AnyPublisher<OAuthTokenExchangeResponse, ErrorEnvelope> { | ||
// TODO: Implement for testing. | ||
Empty(completeImmediately: false).eraseToAnyPublisher() |
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 need to come back and evaluate what of this flow I can test. It's all fairly tightly integrated with Apple's authentication controller. I could add tests just for these new endpoints, but since network tests go against these mocks anyways, it doesn't seem super valuable to me...
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.
@ifosli @scottkicks I'm open to suggestions for what seems worthwhile to test here.
Converting to draft while I write some tests. |
@@ -55,4 +65,67 @@ public struct OAuth { | |||
return nil | |||
} | |||
} | |||
|
|||
internal static func handleRedirect( |
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.
Pulled this out into its own method to make it testable, without needing to deal with ASWebAuthenticationSession
in the tests. It's not the prettiest interface, but gets the job done.
guard error == nil else { | ||
if let authenticationError = error as? ASWebAuthenticationSessionError, | ||
authenticationError.code == .canceledLogin { | ||
DispatchQueue.main.async { |
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.
These dispatches are here to prevent us from releasing Zalgo - they ensure that the completion block is only ever called asynchronously, instead of a mix of synchronously and asynchronously.
message: errorMessage, | ||
preferredStyle: .alert | ||
) | ||
alert.addAction(UIAlertAction(title: Strings.login_errors_button_ok(), style: .cancel)) |
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.
Interestingly these strings existed, but were unused 🤷♀️
c1bf6fa
to
6baad91
Compare
@testable import Library | ||
import XCTest | ||
|
||
final class OAuthTests: XCTestCase { |
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.
These tests are all a little tautological (they really test the specific internal implementation), but I feel like I'd rather have them than not.
} | ||
.sink { result in | ||
if case let .failure(error) = result { | ||
let message = error.errorMessages.first ?? Strings.login_errors_unable_to_log_in() |
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.
nit: Will the error message here be localized? If not, we may want to just keep the "unable to log in" error, even though it may be less helpful/more generic
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.
Good question. I assumed that server error messages are localized, but I've never actually checked - is that assumption incorrect?
9aa43d3
to
0917b15
Compare
📲 What
This implements the MVP of login with OAuth.
It works like this:
🤔 Why
We are porting the app to use OAuth via a web authentication session to improve security.
👀 See
This snazzy demo!
better.mp4