Firebase OpenID Authentication redirects to about:blank - swift

My App uses the Firebase Auth SDK (10.4.0) to authenticate users with OAuth. In my project settings I enabled OpenID Connect as a Sign-In-Method. I use an education institute named "IServ" as my openid auth provider. They have provided me with the ClientID, Issuer-URL and the Client Secret which I entered in the firebase console. I am able to launch the login form of my provider in a WebView with the following code:
let oAuthProvider = OAuthProvider(providerID: "oidc.iserv")
oAuthProvider.scopes = ["roles"]
oAuthProvider.getCredentialWith(nil) { credential, error in
print(credential, error)
}
After entering the login information in the login form of my provider and pressing continue the browser strangely redirects to a white webpage and after a few seconds it redirects to "about:blank".
I have two iOS Apps that are both connected to the same Firebase project. In one of the two apps the oauth login is working and the user gets redirected to the app (i.e. the WebView closes automatically). But in my other app the redirect is not working, although its the exact same code.
I can't retrieve any error in my Xcode console, nor in the WebView. Is this a common issue and has this something to do with the Xcode project setup?
I would really appreciate some help regarding this.

I had the same issues in a SwiftUI app, which I solved with the following approach.
Just before the about:blank is loaded, the app's callback onOpenURL is called (it's just the SwiftUI version of UIApplicationDelegate application:openURL:options).
In the callback, add the following code that triggers the authentication flow continuation:
.onOpenURL { url in
let authCanHandle = Auth.auth().canHandle(url)
if authCanHandle {
return
}
/* Here other dynamic link stuff */
}

This did the trick, for AppDelegate add the following code
func application(_ app: UIApplication, open url: URL, options: func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
let twitterhandeled = Auth.auth().canHandle(url)
return twitterhandeled
}

Related

CloudKit CKShare URL Goes Nowhere

I have successfully saved a CKShare URL to CloudKit, and I can see that the user is INVITED in the CloudKit Dashboard. My Mac app emailed the URL to that person, but when they click it, all they see it this screen on icloud.com:
Clicking OK makes everything disappear so all you see is the background on the web page.
My understanding is that the URL is supposed to open my Mac app where it will fire userDidAcceptCloudKitShareWith in my app delegate. But it does nothing.
Could this be because my app is in development and not in the Mac App Store yet? Do I need a custom URL scheme to get it to open my app?
Documentation on this stuff is pretty sparse. I'd love any help someone can provide.
I have since learned that you must specify a fallback URL for your CloudKit container. In cases where the app isn't installed (or isn't recognized, which seems to be the case when doing dev builds in Xcode like I am), CloudKit will forward share URL to somewhere you specify. They append the unique share ID to the URL so that you can process it on your own web page.
In the CloudKit dashboard, go to Environment Settings... and you'll see this popup:
I have it redirect to https://myapp.com/share/?id= and on my web page where it redirects to, I do a $_GET['id'] to grab the id. I then do another redirect to my application using a custom URL scheme and pass the share ID (e.g. myapp://abc123 where abc123 is the share ID).
In my app delegate, I receive the URL like this:
func application(_ application: NSApplication, open urls: [URL]) {
if let url = urls.first, let shareId = url.host{
fetchShare(shareId) //<-- sharedId = abc123
}
}
I then use CKFetchShareMetadataOperation to look up the URL of the share and CKAcceptSharesOperation to accept it like this:
func fetchShare(shareId: String){
if let url = URL(string: "https://www.icloud.com/share/\(shareId)"){
let operation = CKFetchShareMetadataOperation(shareURLs: [url])
operation.perShareMetadataBlock = { url, metadata, error in
if let metadata = metadata{
//:::
acceptShare(metadata: metadata)
}
}
operation.fetchShareMetadataCompletionBlock = { error in
if let error = error{
print("fetch Share error: \(error)")
}
}
CKContainer.default().add(operation)
}
}
func acceptShare(metadata: CKShareMetadata){
let operation = CKAcceptSharesOperation(shareMetadatas: [metadata])
operation.acceptSharesCompletionBlock = { error in
if let error = error{
print("accept share error: \(error)")
}else{
//Share accepted!
}
}
CKContainer.default().add(operation)
}
I think there are easier ways to work through this using NSItemProvider and NSSharingService, but I'm doing a lot of custom UI and wanted to have full control of the share workflow.
I hope this helps someone. :)

Executing an iMessage Extension app by clicking on a button in the main application

Is it possible to launch my iMessage app extension from the main application( developed using Swift4), by clicking a button? I have already published the app in the store, but I don't know how to do it.
You should be able to do this by using the same iTunes link for your app, with an added query parameter of ?app=messages
Using Starbucks as an example, their App Store URL is
https://itunes.apple.com/us/app/starbucks/id331177714
But the URL to launch iMessage, and pull up your iMessage app would be
https://itunes.apple.com/us/app/starbucks/id331177714?app=messages
Not as far as I can tell, up as late as iOS 12.1
This is long-standing as a request - you can go only go in the one direction, from extension to App.
There's a forum discussion from 2016 where the answer was that it's not possible. Another 2018 forum question got no response.
From extension to app, see the technique described in this blog, using a URL:
guard let url: URL = URL(string: "AppName://?myParam=myValue") else { return }
self.extensionContext?.open(url, completionHandler: { (success: Bool) in
})
Calling the above code will open the phone app.
You can access the URL params in your phone app’s App Delegate:
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
return true
}

how to authorise OAuth2 in tvOS

app going to unknown state and doesn't do anything i'm having this code in didFinishLaunchingWithOptions for P2Auth2 and can't authorise
Authenticator.sharedInstance.oauth2 = OAuth2
Authenticator.sharedInstance.reauthorizeSilently(callback: { (wasFailure, error) -> Void in
if (!wasFailure) {
print("Re-Auth Successfull")
} else {
print("Re-Auth failed")
}
})
return true
As tvOS does not support web views, it seems unlikely that you will be able to directly log in from AppleTV. Most AppleTV solutions that succeed use a second device to acquire the OAuth login token. If you can authenticate directly without using OAuth2, I recommend that you do so.

redirect uri causing issues when navigating to an oauth url

I am trying to navigate to the Microsoft Live oauth authentication page so that I can authorize my user and get a token for use by the app. When I use the following NSURL string, I am able to navigate to the site and authorize my app, retrieving the token.
let stringUrl = "https://login.live.com/oauth20_authorize.srf?client_id=\(clientId)&scope=\(scope)&response_type=code"
let url = NSURL(string: stringUrl)!
However, I want to redirect back to my app (using SFSafariViewController). In order to do so, I added a URL Scheme to my app, and passed that in as the redirect_uri in the URL.
let stringUrl = "https://login.live.com/oauth20_authorize.srf?client_id=\(clientId)&scope=\(scope)&response_type=code&redirect_uri=Lifestream://onedrive-oauth-callback"
let url = NSURL(string: stringUrl)!
However the Live login site gives me an error saying something went wrong and it couldn't continue. This error occurs before the oauth login page is displayed. It happens immediately upon navigating to the URL.
Am I creating my URL scheme incorrectly, or passing the scheme in to the redirect uri improperly? I'm confused as to why it works fine without the redirect_uri, but the site can't load when I provide it.
Can someone point me in the right direction on how I am supposed to pass a redirect url for my app, into an oauth redirect?
Update
It seems that Microsoft does not allow you to register a redirect URL that is a App URL scheme. I dont know how to get this info back into my app then, other than just paying for a site I can point MSFT to, which would then do nothing but redirects into the app for me.
I had a similar OAuth problem and didn't want to mess around with a web server so instead what I did was kinda cheeky. Make a UIWebView and put the request on the web view. Then delegate it to yourself and pass the redirect URL to be http://localhost:8000 (it can be anything like that it really doesn't matter). Then inside the delegate do this:
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool
{
if let URL = request.URL?.absoluteString
{
// This should be the redirect URL that you pass it can be anything like local host mentioned above.
if URL.hasPrefix(redirectURL)
{
// Now you can simply do some string manipulation to pull out the relevant components.
// I'm not sure what sort of token or how you get it back but assuming the redirect URL is
// YourRedirectURL&code=ACCESS_TOKEN and you want access token heres how you would get it.
var code : String?
if let URLParams = request.URL?.query?.componentsSeparatedByString("&")
{
for param in URLParams
{
let keyValue = param.componentsSeparatedByString("=")
let key = keyValue.first
if key == "code"
{
code = keyValue.last
}
}
}
// Here if code != nil then it has the ACCESS_TOKEN and you are done! If its nil something went wrong.
return false // So that the webview doesnt redirect to the dummy URL you passed.
}
}
return true
}
This is sorta hacky but it works great and you don't need any server nor do you need a redirect URI on your app, its an awesome way to do it in my opinion. You could optimize this for swift 2 to reduce the indentation by using guards but I wrote this before it came out so...
Hope this helps!

Call back url for iOS app?

I am working on a project where I share video on Vimeo. In this My app open a video where user needs to press authorize button to authorize the app at Vimeo and to get access tokens. So, for this, my app opens safari and open Vimeo's site there. The user needs to press allow button then it has to come back again to the app. But I am not able to know what should be the call back url to make the Safari/Vimeo to come back to my app.
Please suggest your views regarding this.
You need to set a custom URL scheme for your app by editing your app's Info.plist. There's plenty of documentation about this on Apple's developer website. Here's an article that goes into detail: http://iosdevelopertips.com/cocoa/launching-your-own-application-via-a-custom-url-scheme.html
Then your website just needs to open a url that uses your app's url scheme (eg: myappscheme://do/something/cool?foo=bar). If your app cares about any data passed in to it via your website then implement the "application:openURL:sourceApplication:annotation:" method and inspect the NSURL passed in. You can read more about this in Apple's documentation: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIApplicationDelegate_Protocol/Reference/Reference.html
You need to implement something called 'URL Scheme' to your app, which means register your app to a certain url, so it can be opened from.
1) You should add a row to your info.plist file.
2) You need to listen to the url in your app, and do what needed.
Google for more info...
To support a custom URL scheme:
Define the format for your app's URLs.
Register your scheme so that the system directs appropriate URLs to your app.
Handle the URLs that your app receives.
URLs must start with your custom scheme name. Add parameters for any options your app supports. For example, a photo library app might define a URL format that includes the name or index of a photo album to display.
an example is :
myphotoapp:albumname?name="foods"
myphotoapp:albumname?index=1
Register Your URL Scheme
click on project target and goto info page
in the info page expand URL Types section and hit the + button
fill fields with appropriate values.
Handle Incoming URLs
The system delivers the URL to your app by calling your app delegate's application(_:open:options:)method. you can useNSURLComponents` APIs to extract the components. Obtain additional information about the URL, such as which app opened it, from the system-provided options dictionary.
func application(_ application: UIApplication,
open url: URL,
options: [UIApplicationOpenURLOptionsKey : Any] = [:] ) -> Bool {
// Determine who sent the URL.
let sendingAppID = options[.sourceApplication]
print("source application = \(sendingAppID ?? "Unknown")")
// Process the URL.
guard let components = NSURLComponents(url: url, resolvingAgainstBaseURL: true),
let albumPath = components.path,
let params = components.queryItems else {
print("Invalid URL or album path missing")
return false
}
if let photoIndex = params.first(where: { $0.name == "index" })?.value {
print("albumPath = \(albumPath)")
print("photoIndex = \(photoIndex)")
return true
} else {
print("Photo index missing")
return false
}
}
If your app has opted into Scenes, and your app is not running, the system delivers the URL to the scene(_:willConnectTo:options:) delegate method after launch, and to scene(_:openURLContexts:) when your app opens a URL while running or suspended in memory.
func scene(_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions) {
// Determine who sent the URL.
if let urlContext = connectionOptions.urlContexts.first {
let sendingAppID = urlContext.options.sourceApplication
let url = urlContext.url
print("source application = \(sendingAppID ?? "Unknown")")
print("url = \(url)")
// Process the URL similarly to the UIApplicationDelegate example.
}
}