[GH-ISSUE #300] Support switching to native application for auth #181

Closed
opened 2026-03-03 16:46:26 +03:00 by kerem · 6 comments
Owner

Originally created by @mikegray on GitHub (Oct 20, 2016).
Original GitHub issue: https://github.com/OAuthSwift/OAuthSwift/issues/300

Description:

Some providers support authentication by launching into their iOS app directly rather than using Safari or other web views.

OAuth Provider (Twitter, Github, ..):

Facebook and Dropbox

OAuth Version:

  • Version 1
  • Version 2

OS (Please fill the version) :

  • iOS :
  • OSX :
  • TVOS :
  • WatchOS :

Installation method:

  • Carthage
  • CocoaPods
  • Manually

Library version:

  • head
  • v1.0.0
  • v0.6
  • other: (Please fill in the version you are using.)

Xcode version:

  • 8.0 (Swift 3.0)
  • 8.0 (Swift 2.3)
  • 7.3.1
  • other: (Please fill in the version you are using.)
  • objective c

I have Facebook somewhat working and Dropbox too. I will provide the details of each in a follow-up comment. My version is very much a hack and I am wondering if you have a nicer/cleaner way to accomplish this - or maybe you don't want to support it because it (likely) is not strictly following the OAuth spec(s).

Originally created by @mikegray on GitHub (Oct 20, 2016). Original GitHub issue: https://github.com/OAuthSwift/OAuthSwift/issues/300 ### Description: Some providers support authentication by launching into their iOS app directly rather than using Safari or other web views. ### OAuth Provider (Twitter, Github, ..): Facebook and Dropbox ### OAuth Version: - [x] Version 1 - [x] Version 2 ### OS (Please fill the version) : - [x] iOS : - [ ] OSX : - [ ] TVOS : - [ ] WatchOS : ### Installation method: - [x] Carthage - [ ] CocoaPods - [ ] Manually ### Library version: - [x] head - [ ] v1.0.0 - [ ] v0.6 - [ ] other: (Please fill in the version you are using.) ### Xcode version: - [x] 8.0 (Swift 3.0) - [ ] 8.0 (Swift 2.3) - [ ] 7.3.1 - [ ] other: (Please fill in the version you are using.) - [ ] objective c I have Facebook somewhat working and Dropbox too. I will provide the details of each in a follow-up comment. My version is very much a hack and I am wondering if you have a nicer/cleaner way to accomplish this - or maybe you don't want to support it because it (likely) is not strictly following the OAuth spec(s).
kerem 2026-03-03 16:46:26 +03:00
Author
Owner

@mikegray commented on GitHub (Oct 20, 2016):

For Facebook, you need to add fbauth2 to your LSApplicationQueriesSchemes in Info.plist. You will need to add fb<APP_KEY> to your app url types.

Then you can check to see if the Facebook app is installed:

var isFacebookAppInstalled: Bool = {
    let appUrl = URL(string: "fbauth2://app")
    return UIApplication.shared.canOpenURL(appUrl!)
}()

If the app is installed, then you can use an authorizeUrl of "fbauth2://authorize?response_type=token&client_id=\(facebookAppId)&redirect_uri=fb\(facebookAppId)&scope=public_profile" with OAuth2Swift along with a custom OAuthSwiftURLHandlerType:

class NativeAppHandler: OAuthSwiftURLHandlerType {
    public static var sharedInstance : NativeAppHandler = NativeAppHandler()

    @objc open func handle(_ url: URL) {
        UIApplication.shared.open(url, options: [:], completionHandler: nil)
    }
}

That will launch Facebook via the fbauth2 URL scheme and it will call back into your app using the fb<APP_KEY> url scheme with the appropriate token:

func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
    if (url.host == "oauth-callback") || (url.scheme == "fb<APP_KEY>) { // probably check source too
        OAuthSwift.handle(url: url)
    }
    return true
}

That seems to work pretty well for me.

<!-- gh-comment-id:255249258 --> @mikegray commented on GitHub (Oct 20, 2016): For Facebook, you need to add `fbauth2` to your `LSApplicationQueriesSchemes` in Info.plist. You will need to add `fb<APP_KEY>` to your app url types. Then you can check to see if the Facebook app is installed: ``` var isFacebookAppInstalled: Bool = { let appUrl = URL(string: "fbauth2://app") return UIApplication.shared.canOpenURL(appUrl!) }() ``` If the app is installed, then you can use an `authorizeUrl` of `"fbauth2://authorize?response_type=token&client_id=\(facebookAppId)&redirect_uri=fb\(facebookAppId)&scope=public_profile"` with OAuth2Swift along with a custom OAuthSwiftURLHandlerType: ``` class NativeAppHandler: OAuthSwiftURLHandlerType { public static var sharedInstance : NativeAppHandler = NativeAppHandler() @objc open func handle(_ url: URL) { UIApplication.shared.open(url, options: [:], completionHandler: nil) } } ``` That will launch Facebook via the `fbauth2` URL scheme and it will call back into your app using the `fb<APP_KEY>` url scheme with the appropriate token: ``` func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool { if (url.host == "oauth-callback") || (url.scheme == "fb<APP_KEY>) { // probably check source too OAuthSwift.handle(url: url) } return true } ``` That seems to work pretty well for me.
Author
Owner

@mikegray commented on GitHub (Oct 20, 2016):

For Dropbox, you can do something pretty similar. Register dbapi-2 in LSApplicationQueriesSchemes. Add an url type to your app for callback of db-<APP_KEY>.

Check if Dropbox is installed:

var isDropboxAppInstalled: Bool = {
    let appUrl = URL(string: "dbapi-2://app")
    return UIApplication.shared.canOpenURL(appUrl!)
}()

Your authorizeUrl will be something like: "dbapi-2://1/connect?k=\(dropboxAppId)&s=&state=oauth2:\(nonce)" where nonce is random state string. Dropbox example uses UUID().uuidString.

The callback does not match what OAuthSwift/OAuth expectations though - it is almost more closely appears like an OAuth v1 response with oauth_token_secret being the important value:
db-<APP_KEY>://1/connect?oauth_token_secret=<MY_SECRET>&state=oauth2%3A<NONCE>&uid=<DROPBOX_USER_ID>&oauth_token=oauth2%3A (Note that the %3A is just : url encoded.)

So you would need to either rewrite the url query before passing it into OAuthSwift.handle(url: url), or define the original settings as being OAuth1Swift? This one doesn't fit as neatly in to the current flows in OAuthSwift.

<!-- gh-comment-id:255252495 --> @mikegray commented on GitHub (Oct 20, 2016): For Dropbox, you can do something pretty similar. Register `dbapi-2` in `LSApplicationQueriesSchemes`. Add an url type to your app for callback of `db-<APP_KEY>`. Check if Dropbox is installed: ``` var isDropboxAppInstalled: Bool = { let appUrl = URL(string: "dbapi-2://app") return UIApplication.shared.canOpenURL(appUrl!) }() ``` Your authorizeUrl will be something like: `"dbapi-2://1/connect?k=\(dropboxAppId)&s=&state=oauth2:\(nonce)"` where nonce is random state string. Dropbox example uses `UUID().uuidString`. The callback does not match what OAuthSwift/OAuth expectations though - it is almost more closely appears like an OAuth v1 response with `oauth_token_secret` being the important value: `db-<APP_KEY>://1/connect?oauth_token_secret=<MY_SECRET>&state=oauth2%3A<NONCE>&uid=<DROPBOX_USER_ID>&oauth_token=oauth2%3A` (Note that the `%3A` is just `:` url encoded.) So you would need to either rewrite the url query before passing it into OAuthSwift.handle(url: url), or define the original settings as being OAuth1Swift? This one doesn't fit as neatly in to the current flows in OAuthSwift.
Author
Owner

@mikegray commented on GitHub (Oct 20, 2016):

I am happy to submit a PR if this is generally interesting and you want to take it on for the future. Happy for any advice on how best to integrate these into OAuthSwift - custom handler vs. native app handler vs. subclass OAuthSwift vs. other. I am assuming I could have done it differently/better. Sorry, still pretty new to OAuth in general.

<!-- gh-comment-id:255252937 --> @mikegray commented on GitHub (Oct 20, 2016): I am happy to submit a PR if this is generally interesting and you want to take it on for the future. Happy for any advice on how best to integrate these into OAuthSwift - custom handler vs. native app handler vs. subclass OAuthSwift vs. other. I am assuming I could have done it differently/better. Sorry, still pretty new to OAuth in general.
Author
Owner

@phimage commented on GitHub (Oct 21, 2016):

ok thanks for the detailled informations
I think you can use OAuthSwiftOpenURLExternally, same as your NativeAppHandler

For dropbox it's a little weird but I see frequently mixing implementation when a provider pass to oauth2.
I think they must fix it, and you must edit the url until that

I don't see anything to integrate in OAuthSwift main project
Maybe only wiki page to explain the subtility, how you achieve that etc...
A page named NativeApplication with the two examples

some link for me

<!-- gh-comment-id:255330737 --> @phimage commented on GitHub (Oct 21, 2016): ok thanks for the detailled informations I think you can use OAuthSwiftOpenURLExternally, same as your NativeAppHandler For dropbox it's a little weird but I see frequently mixing implementation when a provider pass to oauth2. I think they must fix it, and you must edit the url until that I don't see anything to integrate in OAuthSwift main project Maybe only wiki page to explain the subtility, how you achieve that etc... A page named NativeApplication with the two examples some link for me - https://blogs.dropbox.com/developers/2015/08/important-update-your-core-api-app-for-ios-9/ - https://www.dropbox.com/developers-v1/core/docs#oa2-from-oa1
Author
Owner
<!-- gh-comment-id:255333952 --> @phimage commented on GitHub (Oct 21, 2016): https://github.com/OAuthSwift/OAuthSwift/wiki/Authenticate-with-Native-Applications
Author
Owner

@mikegray commented on GitHub (Oct 21, 2016):

Sounds good, thanks.

<!-- gh-comment-id:255423432 --> @mikegray commented on GitHub (Oct 21, 2016): Sounds good, thanks.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/OAuthSwift#181
No description provided.