Authentication working

* Updates to use new API style per discussion with Maurice
* Credential validation functioning
This commit is contained in:
Jeremy Beker 2019-05-29 10:54:52 -04:00
parent de82f718d5
commit 0df86e5761
No known key found for this signature in database
GPG Key ID: CD5EE767A4A34FD0
10 changed files with 99 additions and 101 deletions

View File

@ -252,6 +252,9 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
// MARK: - API // MARK: - API
public func storeCredentials(_ credentials: Credentials) throws { public func storeCredentials(_ credentials: Credentials) throws {
// The delegate may need the credentials to determine the server
delegate.credentials = credentials
guard let server = delegate.server else { guard let server = delegate.server else {
throw CredentialsError.incompleteCredentials throw CredentialsError.incompleteCredentials
} }
@ -259,7 +262,9 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
switch credentials { switch credentials {
case .basic(let username, _): case .basic(let username, _):
self.username = username self.username = username
case .googleLogin(let username, _, _, _): case .googleBasicLogin(let username, _, _):
self.username = username
case .googleAuthLogin(let username, _, _):
self.username = username self.username = username
} }
@ -284,7 +289,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
self.username = nil self.username = nil
} }
public static func validateCredentials(transport: Transport = URLSession.webserviceTransport(), type: AccountType, credentials: Credentials, completion: @escaping (Result<Bool, Error>) -> Void) { public static func validateCredentials(transport: Transport = URLSession.webserviceTransport(), type: AccountType, credentials: Credentials, completion: @escaping (Result<Credentials?, Error>) -> Void) {
switch type { switch type {
case .onMyMac: case .onMyMac:
LocalAccountDelegate.validateCredentials(transport: transport, credentials: credentials, completion: completion) LocalAccountDelegate.validateCredentials(transport: transport, credentials: credentials, completion: completion)

View File

@ -46,6 +46,6 @@ protocol AccountDelegate {
// Called at the end of accounts init method. // Called at the end of accounts init method.
func accountDidInitialize(_ account: Account) func accountDidInitialize(_ account: Account)
static func validateCredentials(transport: Transport, credentials: Credentials, completion: @escaping (Result<Bool, Error>) -> Void) static func validateCredentials(transport: Transport, credentials: Credentials, completion: @escaping (Result<Credentials?, Error>) -> Void)
} }

View File

@ -42,7 +42,7 @@ final class FeedbinAPICaller: NSObject {
self.transport = transport self.transport = transport
} }
func validateCredentials(completion: @escaping (Result<Bool, Error>) -> Void) { func validateCredentials(completion: @escaping (Result<Credentials?, Error>) -> Void) {
let callURL = feedbinBaseURL.appendingPathComponent("authentication.json") let callURL = feedbinBaseURL.appendingPathComponent("authentication.json")
let request = URLRequest(url: callURL, credentials: credentials) let request = URLRequest(url: callURL, credentials: credentials)
@ -50,12 +50,12 @@ final class FeedbinAPICaller: NSObject {
transport.send(request: request) { result in transport.send(request: request) { result in
switch result { switch result {
case .success: case .success:
completion(.success(true)) completion(.success(self.credentials))
case .failure(let error): case .failure(let error):
switch error { switch error {
case TransportError.httpError(let status): case TransportError.httpError(let status):
if status == 401 { if status == 401 {
completion(.success(false)) completion(.success(self.credentials))
} else { } else {
completion(.failure(error)) completion(.failure(error))
} }

View File

@ -485,7 +485,7 @@ final class FeedbinAccountDelegate: AccountDelegate {
accountMetadata = account.metadata accountMetadata = account.metadata
} }
static func validateCredentials(transport: Transport, credentials: Credentials, completion: @escaping (Result<Bool, Error>) -> Void) { static func validateCredentials(transport: Transport, credentials: Credentials, completion: @escaping (Result<Credentials?, Error>) -> Void) {
let caller = FeedbinAPICaller(transport: transport) let caller = FeedbinAPICaller(transport: transport)
caller.credentials = credentials caller.credentials = credentials

View File

@ -35,41 +35,43 @@ final class GoogleReaderCompatibleAPICaller: NSObject {
private var transport: Transport! private var transport: Transport!
var credentials: Credentials? var credentials: Credentials?
var apiAuthToken: String?
weak var accountMetadata: AccountMetadata? weak var accountMetadata: AccountMetadata?
var server: String? {
get {
guard let localCredentials = credentials else {
return nil
}
switch localCredentials {
case .googleBasicLogin(_, _, let apiUrl):
return apiUrl.host
case .googleAuthLogin(_, _, let apiUrl):
return apiUrl.host
default:
return nil
}
}
}
init(transport: Transport) { init(transport: Transport) {
super.init() super.init()
self.transport = transport self.transport = transport
} }
func validateCredentials(completion: @escaping (Result<Bool, Error>) -> Void) { func validateCredentials(completion: @escaping (Result<Credentials?, Error>) -> Void) {
guard let credentials = credentials else { guard let credentials = credentials else {
completion(.failure(CredentialsError.incompleteCredentials)) completion(.failure(CredentialsError.incompleteCredentials))
return return
} }
guard case .googleLogin(let username, let password, let apiUrl, _) = credentials else { guard case .googleBasicLogin(let username, _, let apiUrl) = credentials else {
completion(.failure(CredentialsError.incompleteCredentials)) completion(.failure(CredentialsError.incompleteCredentials))
return return
} }
guard var loginURL = URLComponents(url: apiUrl.appendingPathComponent("/accounts/ClientLogin"), resolvingAgainstBaseURL: false) else { let request = URLRequest(url: apiUrl.appendingPathComponent("/accounts/ClientLogin"), credentials: credentials)
completion(.failure(CredentialsError.incompleteCredentials))
return
}
loginURL.queryItems = [
URLQueryItem(name: "Email", value: username),
URLQueryItem(name: "Passwd", value: password)
]
guard let callURL = loginURL.url else {
completion(.failure(CredentialsError.incompleteCredentials))
return
}
let request = URLRequest(url: callURL, credentials: credentials)
transport.send(request: request) { result in transport.send(request: request) { result in
switch result { switch result {
@ -97,20 +99,11 @@ final class GoogleReaderCompatibleAPICaller: NSObject {
} }
// Save Auth Token for later use // Save Auth Token for later use
self.apiAuthToken = authString self.credentials = .googleAuthLogin(username: username, apiKey: authString, url: apiUrl)
completion(.success(true)) completion(.success(self.credentials))
case .failure(let error): case .failure(let error):
switch error { completion(.failure(error))
case TransportError.httpError(let status):
if status == 401 {
completion(.success(false))
} else {
completion(.failure(error))
}
default:
completion(.failure(error))
}
} }
} }

View File

@ -31,7 +31,12 @@ final class GoogleReaderCompatibleAccountDelegate: AccountDelegate {
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "GoogleReaderCompatible") private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "GoogleReaderCompatible")
let supportsSubFolders = false let supportsSubFolders = false
let server: String? = "api.GoogleReaderCompatible.com" var server: String? {
get {
return caller.server
}
}
var opmlImportInProgress = false var opmlImportInProgress = false
var credentials: Credentials? { var credentials: Credentials? {
@ -485,7 +490,7 @@ final class GoogleReaderCompatibleAccountDelegate: AccountDelegate {
accountMetadata = account.metadata accountMetadata = account.metadata
} }
static func validateCredentials(transport: Transport, credentials: Credentials, completion: @escaping (Result<Bool, Error>) -> Void) { static func validateCredentials(transport: Transport, credentials: Credentials, completion: @escaping (Result<Credentials?, Error>) -> Void) {
let caller = GoogleReaderCompatibleAPICaller(transport: transport) let caller = GoogleReaderCompatibleAPICaller(transport: transport)
caller.credentials = credentials caller.credentials = credentials

View File

@ -182,8 +182,8 @@ final class LocalAccountDelegate: AccountDelegate {
func accountDidInitialize(_ account: Account) { func accountDidInitialize(_ account: Account) {
} }
static func validateCredentials(transport: Transport, credentials: Credentials, completion: (Result<Bool, Error>) -> Void) { static func validateCredentials(transport: Transport, credentials: Credentials, completion: (Result<Credentials?, Error>) -> Void) {
return completion(.success(false)) return completion(.success(nil))
} }
} }

View File

@ -74,38 +74,36 @@ class AccountsFeedbinWindowController: NSWindowController {
self.progressIndicator.stopAnimation(self) self.progressIndicator.stopAnimation(self)
switch result { switch result {
case .success(let authenticated): case .success(let validatedCredentials):
if authenticated { guard let validatedCredentials = validatedCredentials else {
self.errorMessageLabel.stringValue = NSLocalizedString("Invalid email/password combination.", comment: "Credentials Error")
var newAccount = false return
if self.account == nil { }
self.account = AccountManager.shared.createAccount(type: .feedbin) var newAccount = false
newAccount = true if self.account == nil {
} self.account = AccountManager.shared.createAccount(type: .feedbin)
newAccount = true
do { }
try self.account?.removeBasicCredentials()
try self.account?.storeCredentials(credentials) do {
if newAccount { try self.account?.removeBasicCredentials()
self.account?.refreshAll() { result in try self.account?.storeCredentials(validatedCredentials)
switch result { if newAccount {
case .success: self.account?.refreshAll() { result in
break switch result {
case .failure(let error): case .success:
NSApplication.shared.presentError(error) break
} case .failure(let error):
NSApplication.shared.presentError(error)
} }
} }
self.hostWindow?.endSheet(self.window!, returnCode: NSApplication.ModalResponse.OK)
} catch {
self.errorMessageLabel.stringValue = NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error")
} }
self.hostWindow?.endSheet(self.window!, returnCode: NSApplication.ModalResponse.OK)
} else { } catch {
self.errorMessageLabel.stringValue = NSLocalizedString("Invalid email/password combination.", comment: "Credentials Error") self.errorMessageLabel.stringValue = NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error")
} }
case .failure: case .failure:
self.errorMessageLabel.stringValue = NSLocalizedString("Network error. Try again later.", comment: "Credentials Error") self.errorMessageLabel.stringValue = NSLocalizedString("Network error. Try again later.", comment: "Credentials Error")

View File

@ -70,7 +70,7 @@ class AccountsGoogleReaderCompatibleWindowController: NSWindowController {
return return
} }
let credentials = Credentials.googleLogin(username: usernameTextField.stringValue, password: passwordTextField.stringValue, apiUrl: apiURL, apiKey: nil) let credentials = Credentials.googleBasicLogin(username: usernameTextField.stringValue, password: passwordTextField.stringValue, url: apiURL)
Account.validateCredentials(type: .googleReaderCompatible, credentials: credentials) { [weak self] result in Account.validateCredentials(type: .googleReaderCompatible, credentials: credentials) { [weak self] result in
guard let self = self else { return } guard let self = self else { return }
@ -80,42 +80,39 @@ class AccountsGoogleReaderCompatibleWindowController: NSWindowController {
self.progressIndicator.stopAnimation(self) self.progressIndicator.stopAnimation(self)
switch result { switch result {
case .success(let authenticated): case .success(let validatedCredentials):
guard let validatedCredentials = validatedCredentials else {
self.errorMessageLabel.stringValue = NSLocalizedString("Invalid email/password combination.", comment: "Credentials Error")
return
}
if authenticated {
var newAccount = false
var newAccount = false if self.account == nil {
if self.account == nil { self.account = AccountManager.shared.createAccount(type: .googleReaderCompatible)
self.account = AccountManager.shared.createAccount(type: .googleReaderCompatible) newAccount = true
newAccount = true }
}
do {
do { try self.account?.removeBasicCredentials()
try self.account?.removeBasicCredentials() try self.account?.storeCredentials(validatedCredentials)
try self.account?.storeCredentials(credentials) if newAccount {
if newAccount { self.account?.refreshAll() { result in
self.account?.refreshAll() { result in switch result {
switch result { case .success:
case .success: break
break case .failure(let error):
case .failure(let error): NSApplication.shared.presentError(error)
NSApplication.shared.presentError(error)
}
} }
} }
self.hostWindow?.endSheet(self.window!, returnCode: NSApplication.ModalResponse.OK)
} catch {
self.errorMessageLabel.stringValue = NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error")
} }
self.hostWindow?.endSheet(self.window!, returnCode: NSApplication.ModalResponse.OK)
} else { } catch {
self.errorMessageLabel.stringValue = NSLocalizedString("Invalid email/password combination.", comment: "Credentials Error") self.errorMessageLabel.stringValue = NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error")
} }
case .failure: case .failure:
self.errorMessageLabel.stringValue = NSLocalizedString("Network error. Try again later.", comment: "Credentials Error") self.errorMessageLabel.stringValue = NSLocalizedString("Network error. Try again later.", comment: "Credentials Error")
} }
} }

@ -1 +1 @@
Subproject commit 261feb7537eb7c6dc425fd1c9e24b22dea8fc982 Subproject commit 07ec7f9179dfdf7e89fc97a4a90c4690fa71581a