[GH-ISSUE #634] OAuth 2 flow does not work in iOS app running on MacOS #417

Open
opened 2026-03-03 16:48:30 +03:00 by kerem · 5 comments
Owner

Originally created by @mjromka on GitHub (Nov 18, 2020).
Original GitHub issue: https://github.com/OAuthSwift/OAuthSwift/issues/634

Description:

I've enabled macOS compatibility for my iOS app:
image
But the OAuth 2 flow does not work properly on macOS - after successful login it goes to:

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
// handle authentication mechanism
if url.host == "oauth-callback" {
OAuthSwift.handle(url: url)
}
return true
}
It calls OAuthSwift.handle(url: url) but never gets to the completion handler (on iOS it all works fine).

Do I need to consider something to get it working on macOS or it is a bug?

OAuth Provider? (Twitter, Github, ..):

Custom

OAuth Version:

  • Version 1
  • Version 2

OS (Please fill the version) :

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

Installation method:

  • Carthage
  • CocoaPods
  • Swift Package Manager
  • Manually

Library version:

  • head
  • v2.1.0
  • v2.0.0
  • v1.4.1
  • other: (Please fill in the version you are using.)

Xcode version:

  • 12.2

  • 11.4 (Swift 5.2)

  • 11.x (Swift 5.1)

  • 10.x (Swift 5.0)

  • other: (Please fill in the version you are using.)

  • objective c

Originally created by @mjromka on GitHub (Nov 18, 2020). Original GitHub issue: https://github.com/OAuthSwift/OAuthSwift/issues/634 ### Description: I've enabled macOS compatibility for my iOS app: ![image](https://user-images.githubusercontent.com/3237392/99570954-c0916080-29e3-11eb-95ab-5ca77c6f17e8.png) But the OAuth 2 flow does not work properly on macOS - after successful login it goes to: func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { // handle authentication mechanism if url.host == "oauth-callback" { OAuthSwift.handle(url: url) } return true } It calls OAuthSwift.handle(url: url) but never gets to the completion handler (on iOS it all works fine). Do I need to consider something to get it working on macOS or it is a bug? ### OAuth Provider? (Twitter, Github, ..): Custom ### OAuth Version: - [ ] Version 1 - [x] Version 2 ### OS (Please fill the version) : - [x] iOS : - [ ] OSX : - [ ] TVOS : - [ ] WatchOS : ### Installation method: - [ ] Carthage - [x] CocoaPods - [ ] Swift Package Manager - [ ] Manually ### Library version: - [x] head - [ ] v2.1.0 - [ ] v2.0.0 - [ ] v1.4.1 - [ ] other: (Please fill in the version you are using.) ### Xcode version: - [x] 12.2 - [ ] 11.4 (Swift 5.2) - [ ] 11.x (Swift 5.1) - [ ] 10.x (Swift 5.0) - [ ] other: (Please fill in the version you are using.) - [ ] objective c
Author
Owner

@mjromka commented on GitHub (Nov 22, 2020):

Temporary fixed by replacing safariViewControllerDidFinish with:

public func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
      // "Done" pressed
      #if !targetEnvironment(macCatalyst)
            self.clearObservers()
      #endif
      self.delegate?.safariViewControllerDidFinish?(controller)
}
<!-- gh-comment-id:731855709 --> @mjromka commented on GitHub (Nov 22, 2020): Temporary fixed by replacing safariViewControllerDidFinish with: ``` public func safariViewControllerDidFinish(_ controller: SFSafariViewController) { // "Done" pressed #if !targetEnvironment(macCatalyst) self.clearObservers() #endif self.delegate?.safariViewControllerDidFinish?(controller) } ```
Author
Owner

@sholt147 commented on GitHub (Dec 11, 2020):

I have the same issue. Adding the above code suggested by MJRomka does allow the handler code to complete such that it returns to the completionHandler of my oauthSwift.authorize. However, in that completion I now get a retain error (success failure, error code -10). Please note that I do retain the instance OAuthSwift, and this same exact code works perfectly on iOS. This is a mac catalyst specific issue. xCode 12.2, Big Sur, Mac Mini/M1. The error occurs both when running native on apple silicon as well as Rosetta. It is also occurring with xCode 12.2, Catalyst, MacBook Pro (Intel).

<!-- gh-comment-id:742883785 --> @sholt147 commented on GitHub (Dec 11, 2020): I have the same issue. Adding the above code suggested by MJRomka does allow the handler code to complete such that it returns to the completionHandler of my oauthSwift.authorize. However, in that completion I now get a retain error (success failure, error code -10). Please note that I do retain the instance OAuthSwift, and this same exact code works perfectly on iOS. This is a mac catalyst specific issue. xCode 12.2, Big Sur, Mac Mini/M1. The error occurs both when running native on apple silicon as well as Rosetta. It is also occurring with xCode 12.2, Catalyst, MacBook Pro (Intel).
Author
Owner

@CollinHemeltjen commented on GitHub (Jan 28, 2021):

Temporary fixed by replacing safariViewControllerDidFinish with:

public func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
      // "Done" pressed
      #if !targetEnvironment(macCatalyst)
            self.clearObservers()
      #endif
      self.delegate?.safariViewControllerDidFinish?(controller)
}

Because Catalyst does not use a popovercontroller, but instead sends the request directly to Safari. It seems that this function indeed gets triggered once the app opens Safari on Mac Catalyst. Therefore, it removes the Observers before the user can log in. This code does get rid of the problem. It is probably something Apple needs to fix, but in the meantime it might not be the worst idea to implement it like this.

<!-- gh-comment-id:768955776 --> @CollinHemeltjen commented on GitHub (Jan 28, 2021): > Temporary fixed by replacing safariViewControllerDidFinish with: > > ``` > public func safariViewControllerDidFinish(_ controller: SFSafariViewController) { > // "Done" pressed > #if !targetEnvironment(macCatalyst) > self.clearObservers() > #endif > self.delegate?.safariViewControllerDidFinish?(controller) > } > ``` Because Catalyst does not use a popovercontroller, but instead sends the request directly to Safari. It seems that this function indeed gets triggered once the app opens Safari on Mac Catalyst. Therefore, it removes the Observers before the user can log in. This code does get rid of the problem. It is probably something Apple needs to fix, but in the meantime it might not be the worst idea to implement it like this.
Author
Owner

@sholt147 commented on GitHub (Jan 28, 2021):

Thank you Collin! I had already done this. BUT your response clued me in on the issue I was having down the road with the oauth instance not being retained. Basically on iOS, safariViewControllerDidFinish is only called when the Done button on the popover is tapped. In the case of an oauth flow it is a way to gracefully handle someone cancelling out of the auth process (by tapping the Done button) without completing authorization (In my flow, I release the now defunct oauth instance, pop up an alert letting the user know they haven't completed auth, and then show a view not dependent on the auth credentials). However, since Done is called on MacOS instantly (when I must say I agree it should NOT be called at that time), my graceful code is executed when it shouldn't be. I hope this helps the next person.

<!-- gh-comment-id:769186019 --> @sholt147 commented on GitHub (Jan 28, 2021): Thank you Collin! I had already done this. BUT your response clued me in on the issue I was having down the road with the oauth instance not being retained. Basically on iOS, safariViewControllerDidFinish is only called when the Done button on the popover is tapped. In the case of an oauth flow it is a way to gracefully handle someone cancelling out of the auth process (by tapping the Done button) without completing authorization (In my flow, I release the now defunct oauth instance, pop up an alert letting the user know they haven't completed auth, and then show a view not dependent on the auth credentials). However, since Done is called on MacOS instantly (when I must say I agree it should NOT be called at that time), my graceful code is executed when it shouldn't be. I hope this helps the next person.
Author
Owner

@Gurpartap commented on GitHub (Jul 22, 2022):

Temporary fixed by replacing safariViewControllerDidFinish with:

public func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
      // "Done" pressed
      #if !targetEnvironment(macCatalyst)
            self.clearObservers()
      #endif
      self.delegate?.safariViewControllerDidFinish?(controller)
}

it works for my Mac Catalyst app build. thank you.

edit: a better approach is to just swap out the authorizeURLHandler on Mac Catalyst:

var authorizeURLHandler: OAuthSwiftURLHandlerType?

// …

#if targetEnvironment(macCatalyst)
authorizeURLHandler = OAuthSwiftOpenURLExternally.sharedInstance
#endif

#if !targetEnvironment(macCatalyst)
if authorizeURLHandler == nil {
    authorizeURLHandler = SafariURLHandler(
        present: { [weak self] safariViewController, _ in
            self?.present(safariViewController, animated: true, completion: nil)
        },
        dismiss: { safariViewController, urlHandler in
            safariViewController.dismiss(animated: urlHandler.animated, completion: urlHandler.dismissCompletion)
        },
        oauthSwift: oauthswift
    )
}
#endif

oauthswift.authorizeURLHandler = authorizeURLHandler
<!-- gh-comment-id:1192826216 --> @Gurpartap commented on GitHub (Jul 22, 2022): > Temporary fixed by replacing safariViewControllerDidFinish with: > > ```swift > public func safariViewControllerDidFinish(_ controller: SFSafariViewController) { > // "Done" pressed > #if !targetEnvironment(macCatalyst) > self.clearObservers() > #endif > self.delegate?.safariViewControllerDidFinish?(controller) > } > ``` it works for my Mac Catalyst app build. thank you. edit: a better approach is to just swap out the authorizeURLHandler on Mac Catalyst: ```swift var authorizeURLHandler: OAuthSwiftURLHandlerType? // … #if targetEnvironment(macCatalyst) authorizeURLHandler = OAuthSwiftOpenURLExternally.sharedInstance #endif #if !targetEnvironment(macCatalyst) if authorizeURLHandler == nil { authorizeURLHandler = SafariURLHandler( present: { [weak self] safariViewController, _ in self?.present(safariViewController, animated: true, completion: nil) }, dismiss: { safariViewController, urlHandler in safariViewController.dismiss(animated: urlHandler.animated, completion: urlHandler.dismissCompletion) }, oauthSwift: oauthswift ) } #endif oauthswift.authorizeURLHandler = authorizeURLHandler ```
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#417
No description provided.