HowTo: Use OAuth2PasswordGrant + alamofire + own authorize loginscreen
Sorry for this question but I tryhard since 2 days 🙄
I have 4 components:
- ViewController
- LoginViewController
- OAuthHandler
- OAuth2RetryHandler (from alamofire)
ViewController:
class ViewController: UIViewController {
var loginHandler: LoginHandler?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
loginHandler = LoginHandler(clientId: CLIENT_ID, clientSecret: CLIENT_SECRET)
loginHandler?.authorize(view: self, completion: { (response) in
print(response)
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
LoginViewController: currently just a blank UIViewController
OAuth2RetryHandler: is just copied from the sample
OAuthHandler:
class OAuthHandler: OAuth2PasswordGrantDelegate {
var oauth2: OAuth2PasswordGrant
fileprivate var alamofireManager: SessionManager
public init(clientId: String, clientSecret: String) {
oauth2 = OAuth2PasswordGrant(settings: [
"client_id": clientId,
"client_secret": clientSecret,
"token_uri": TOKEN_URI,
"keychain": true,
"verbose": true
] as OAuth2JSON)
let sessionManager = SessionManager()
let retrier = OAuth2RetryHandler(oauth2: oauth2)
sessionManager.retrier = retrier
sessionManager.adapter = retrier
alamofireManager = sessionManager
oauth2.delegate = self
}
public func authorize(view: UIViewController, completion: @escaping ( OAuth2JSON? ) -> Void) {
oauth2.authConfig.authorizeEmbedded = true
oauth2.authConfig.authorizeContext = view
alamofireManager.request("https://api.github.com/user").validate().responseJSON { (response) in
debugPrint(response)
if let dict = response.result.value as? [String: Any] {
print(dict)
}
else {
print(OAuth2Error.generic("\(response)"))
}
}
}
public func loginController(oauth2: OAuth2PasswordGrant) -> AnyObject {
if let vc = oauth2.authConfig.authorizeContext as? UIViewController {
vc.present(LoginViewController(), animated: true, completion: nil)
return vc
}
return LoginViewController()
}
}
For this build I get the following error:
[Debug] OAuth2: Initialization finished
[Debug] OAuth2: Looking for items in keychain
[Debug] OAuth2: Initialization finished
[Debug] OAuth2: Starting authorization
[Debug] OAuth2: No access token, checking if a refresh token is available
[Debug] OAuth2: Error refreshing token: I don't have a refresh token, not trying to refresh
[Debug] OAuth2: Presenting the login controller
2018-03-01 13:56:54.497130+0100 LibAccount_Example[424:81789] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Application tried to present modal view controller on itself. Presenting controller is <LibAccount_Example.ViewController: 0x102216aa0>.'
My current solution looks like the following:
My 4 components:
- BasicViewController
- CustomLoginViewController
- OAuth2RetryHandler
- LoginHandler
BasicViewController:
class BasicViewController: UIViewController {
var loginHandler: LoginHandler?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
loginHandler = LoginHandler(clientId: OAUTH_CLIENT_ID, clientSecret: OAUTH_CLIENT_SECRET)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let userLoggedIn = UserDefaults.standard.bool(forKey: "LOGGED_IN")
let token = loginHandler?.oauth2.accessToken
if (!userLoggedIn) || (userLoggedIn && token == nil) {
loginHandler?.authorize(presenting: self)
}
}
public func logout() {
loginHandler?.logout()
self.viewDidAppear(true)
}
@objc internal func handleLogoutButton(sender: UIButton) {
logout()
}
}
extension BasicViewController: OAuth2PasswordGrantDelegate {
public func loginController(oauth2: OAuth2PasswordGrant) -> AnyObject {
return CustomLoginViewController(oauth: oauth2)
}
}
CustomLoginViewController:
open class CustomLoginViewController: UIViewController {
// MARK: - object-properties
fileprivate var oauth: OAuth2PasswordGrant?
// MARK: - system-methods
public init(oauth: OAuth2PasswordGrant) {
self.oauth = oauth
super.init(nibName: nil, bundle: nil)
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override open func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
OAuth2RetryHandler: is just copied from the sample
LoginHandler:
open class LoginHandler {
public var oauth2: OAuth2PasswordGrant
open var alamofireManager: SessionManager
public init(clientId: String, clientSecret: String) {
oauth2 = OAuth2PasswordGrant(settings: [
"client_id": clientId,
"client_secret": clientSecret,
"token_uri": TOKEN_URI,
"keychain": true,
"grant_type": "password",
"scope": "",
"verbose": true
] as OAuth2JSON)
let sessionManager = SessionManager()
let retrier = OAuth2RetryHandler(oauth2: oauth2)
sessionManager.retrier = retrier
sessionManager.adapter = retrier
alamofireManager = sessionManager
}
public func authorize(presenting view: UIViewController) {
// as far as I know now, the following if-request is not necessary
// because "authorizeEmbedded" is checking it already
if oauth2.isAuthorizing {
oauth2.abortAuthorization()
return
}
if let view = view as? OAuth2PasswordGrantDelegate {
oauth2.delegate = view
}
oauth2.authorizeEmbedded(from: view) { (authParameters, error) in
if let _ = authParameters {
print(authParameters)
UserDefaults.standard.set(true, forKey: "LOGGED_IN")
}
else {
UserDefaults.standard.set(false, forKey: "LOGGED_IN")
print("Authorization was canceled or went wrong: \(String(describing: error?.description))")
}
}
}
public func logout() {
oauth2.forgetTokens()
oauth2.username = nil
oauth2.password = nil
UserDefaults.standard.set(false, forKey: "LOGGED_IN")
}
}
@PetresS - Did you manage to solve this issue?
@TheSwiftyCoder yes I did. The 2nd comment is my current solution, but I'm not sure if my solution is a "best practice"-solution.
I would appreciate if someone could post a best-practice-sample.
@PetresS
Your solution gave me north, but I think I'm doing something wrong because my login screen did not render :(
I don't know what can be.
@AndersonAltissimo
did you solve your problem? sorry for late response :(
@PetresS
No problem man I solved the problem 👍
Struggling with the same, the documentation could need a fair share of updating here. There's no description of the flow of how you are supposed to work with the delagate and perform the actual login.