I am currently trying to authorize my users with OAuth2. I am currently using the following library: https://github.com/p2/OAuth2
let oauth2 = OAuth2CodeGrant(settings: [
"client_id": "my-id",
"authorize_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://www.googleapis.com/oauth2/v3/token",
"scope": "profile", // depends on the API you use
"redirect_uris": ["com.TestAuthorizeApp:/oauth2Callback"],
])
//let oauth2 = OAuth2CodeGrant(settings: settings)
oauth2.onAuthorize = { parameters in
print("Did authorize with parameters: \(parameters)")
}
oauth2.onFailure = { error in // `error` is nil on cancel
if let error = error {
print("Authorization went wrong: \(error)")
}
}
oauth2.authConfig.authorizeEmbedded = false
oauth2.authorize()
When I run this it loads up google in the browser and I am able to sign in. It then asks me about the permissions I have declared in the scope and that works fine. I click ok open and it redirects me back to my app.
However when I run this code again I am expecting that the access token has been stored in the key chain. However this doesn't seem to be working.
I have looked inside the source code and found the following check: tryToObtainAccessTokenIfNeeded
which always returns false. This means I get the page again where I need to click 'Allow'.
I was wondering if someone could help me figure out why it's not saving anything in the keychain. Also does this mean the user is not really being authenticated?
Thanks.
===
Edit
Have added oauth2.verbose = true
as per Pascal's comment. I get the following output.
OAuth2: Looking for items in keychain
OAuth2: No access token, maybe I can refresh
OAuth2: I don't have a refresh token, not trying to refresh
Which is what I thought was happening. However I am still unsure as to why it's not saving / finding anything in the keychain.
=====
Edit 2
It turns out that I wasn't actually getting an access token back at all. Please see this conversation: https://github.com/p2/OAuth2/issues/109 and my answer below.
OAuth2 lets users grant third-party apps access to their web resources, without sharing their passwords, through a security object known as an access token. It's impossible to obtain the password from the access token, since your password is kept safe inside the main service.
Overview. Starting with iOS 12 and macOS 10.14, Apple has added OAuth 2.0 support for Exchange ActiveSync accounts that can be deployed through an Enterprise Mobility Management.
The OAuth 2.0 specification defines a delegation protocol that is useful for conveying authorization decisions across a network of web-enabled applications and APIs. OAuth is used in a wide variety of applications, including providing mechanisms for user authentication.
With the help from Pascal here: https://github.com/p2/OAuth2/issues/109 I have managed to get it working. Turns out that I wasn't implementing step: '3 Authorize the User' as I should have been.
So a complete solution is:
Inside my view controller I have the following:
let OAuth2AppDidReceiveCallbackNotification = "OAuth2AppDidReceiveCallback"
override func viewDidLoad() {
super.viewDidLoad()
// This notification is for handling step 3 in guide.
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.handleRedirect(_:)), name: OAuth2AppDidReceiveCallbackNotification, object: nil)
}
func authoriseUser {
let oauth2 = OAuth2CodeGrant(settings: [
"client_id": "my-id", // Use own client_id here
"authorize_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://www.googleapis.com/oauth2/v3/token",
"scope": "profile", // depends on the API you use
"redirect_uris": ["com.TestAuthorizeApp:/oauth2Callback"],
])
//let oauth2 = OAuth2CodeGrant(settings: settings)
oauth2.onAuthorize = { parameters in
print("Did authorize with parameters: \(parameters)")
}
oauth2.onFailure = { error in // `error` is nil on cancel
if let error = error {
print("Authorization went wrong: \(error)")
}
}
oauth2.authConfig.authorizeEmbedded = false
oauth2.authorize()
}
// This method gets called by notification and is the last thing we need to do to get our access token.
func handleRedirect(notification: NSNotification) {
oauth2.handleRedirectURL(notification.object as! NSURL)
}
The above code should handle sending you to the google web page where you can log in and then click allow.
Now you need to handle returning to the app in the app delegate:
let OAuth2AppDidReceiveCallbackNotification = "OAuth2AppDidReceiveCallback"
func application(application: UIApplication,
openURL url: NSURL,
sourceApplication: String?,
annotation: AnyObject) -> Bool {
// you should probably first check if this is your URL being opened
NSNotificationCenter.defaultCenter().postNotificationName(OAuth2AppDidReceiveCallbackNotification, object: url)
return true
}
Hopefully this will help anyone else who might be having issues trying to get an access token.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With