Firebase Authentication link not working - missing trailing slash? - ios

I want to use Firebase email link authentication in my iOS (Flutter) app with a custom domain:
The app requests Firebase Auth backend to send an email to the user. -- works
The user clicks a link in the email and is taken back to the app. -- works
The app processes the payload of that link to perform the sign in. -- does not work
Firebase Dynamic Links do work in general but the link from that particular Firebase auto-generated email is not. Technically the app is brought to foreground but the Firebase Dynamic Link onLink callback is not fired. I tracked it down to one tiny trailing slash that is missing in the email's link URL:
https://link.mydomain.com?link=https://app.mydomain.com/__/auth/action?apiKey=... (not working)
https://link.mydomain.com/?link=https://app.mydomain.com/__/auth/action?apiKey=... (works)
-------------------------^
To work around this I tried to add a path suffix to my domain but this gave the same results:
https://link.mydomain.com/app?link=https://app.mydomain.com/__/auth/action?apiKey=... (not working)
https://link.mydomain.com/app/?link=https://app.mydomain.com/__/auth/action?apiKey=... (works)
-----------------------------^
The worst part of this difference that made it so hard to find is that if you copy the link on iOS or long press it to see a preview, the slash is just silently added in the right place! You can paste the link to the notes app, click it, and it works!
Only when viewing the email's source code you can see that it is actually not there:
When requesting the link, I call FirebaseAuth.instance.sendSignInWithEmailLink with these params set:
url: 'https://app.mydomain.com',
dynamicLinkDomain: 'link.mydomain.com',
The dynamicLinkDomain param does not accept a protocol or path.
Does anybody know how to...
make Firebase add this slash to the link when sending the mail?
or
make Firebase Dynamic Link's onLink callback react to clicked links that do not have a trailing slash?
For reference
My Info.plist contains this key:
<key>FirebaseDynamicLinksCustomDomains</key>
<array>
<string>https://link.mydomain.com</string>
</array>
My entitlements file contains this key:
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:link.mydomain.com</string>
</array>
apple-app-site-association (https://link.mydomain.com/apple-app-site-association):
{
"applinks": {
"apps": [],
"details": [{
"appID": "XYZ.com.mydomain.app",
"paths": [
"NOT /_/*",
"/*"
]
}]
}
}

The links with a custom domain worked fine in Apple Mail but no other email client.
As a temporary solution, you can use a Google-provided domain (e.g. projectname.page.link) to generate sign-in links.
You can track it here.

After debugging the iOS implementation of Firebase Dynamic Links it turns out that the missing slash is explicitly required in code (though I am not sure why). Its absence in the email link seems to be a bug and should probably be fixed in the Firebase backend; I filed a bug report on that. The Firebase console has no option to configure a dynamic link domain to be used for automatically generated sign-in links (instead takes any first from the list of configured domains but somehow without path) so I hope this will be made configurable in the future.
Workaround
I simply changed that code (in my Flutter project it's in Pods/FirebaseDynamicLinks/FIRDynamicLinks.m) and removed the check for the trailing slash. But I have to repeat it when the dependency is updated and I don't know why the check is explicitly there so it might cause other problems.
Two more hints on debugging your dynamic links
Check the link itself
Copy the link into the address line of your browser and add &d=1 to get the debug view. It shows errors in a big red box above of the diagram - if there are any.
Check your iOS configuration
Add these two lines temporarily to your didFinishLaunchingWithOptions in AppDelegate.m:
[FIRApp configure];
[FIRDynamicLinks performDiagnosticsWithCompletion:nil];
(you may have to #import Firebase;)
Run the project in Xcode to see a report like this in the debug console:
---- Firebase Dynamic Links diagnostic output start ----
Firebase Dynamic Links framework version 4.0.8
System information: OS iOS, OS version 14.0.1, model iPhone
Current date 2020-10-02 05:02:39 +0000
Device locale en-US (raw en_US), timezone Europe/Berlin
Specified custom URL scheme is com.domain and Info.plist contains such scheme in CFBundleURLTypes key.
AppID Prefix: XXXXXXXX, Team ID: XXXXXXXX, AppId Prefix equal to Team ID: YES
performDiagnostic completed successfully! No errors found.
---- Firebase Dynamic Links diagnostic output end ----

This seems to have been fix in later firebase sdks, if you look at the PR linked in this github issue (I haven't tested, but I have an older sdk version with the issue)
If you don't want to update your SDK right away, an alternative solution is to not use firebase auto-generated email, and instead provide your own email where you insert the firebase link.
In that case, you can tweak the link before inserting it in the email, and make sure it has a trailing slash.

dont forget to add dynamicLinkDomain: 'link.mydomain.com',
url: 'https://app.mydomain.com',
dynamicLinkDomain: 'link.mydomain.com'
I got it working after adding dynamicLinkDomain: 'link.mydomain.com'
P.S Flutter project

Related

Universal Links query string removed/lost while generating share link, IOS, ReactNative Share

Project workspace
Mobile Project: ReactNative 0.64.3 / Expo with Managed Workflow / IOS 14.6 / NPM
Sharing Library: ReactNative's default Share library.
Website/Domain: Statically hosted via S3, single page app built with TS/Gatsby. Caching handled with CloudFront.
Synopsis: I'm using universal links and query params to allow my users to share tickets. The Universal links are being stripped of all route params and query strings when the link is shared by the native sharing dialog. Using the COPY/PASTE function in the native sharing dialog always copies a complete link, which can be pasted anywhere and used successfully to open the app. The EMAIL option also works, opening Mail with the correct url string and autofilled as expected. The problem arrises when I try to share the link via iMessage. I'm running the project on an iPhone as a fully deployed Apple Testflight standalone app.
The app does download the AASA file on app install, which is statically hosted via an S3 bucket. I have verified its availability via postman and the online AASA validator here https://branch.io/resources/aasa-validator/
The app always opens when opening a valid universal link, such as when I manually type it into iMessage or other sharing medium -- email, etc. Because of this, I'm inclined to believe I have a problem with where the link string is being generated rather than the AASA system.
const ticketShare = (ticket: Ticket) => {
const link = Linking.createURL("www.sample.com/share_ticket", { //Expo Linking library
scheme: "https",
queryParams: {
...ticket
}
}).replaceAll("%25", "%"); //double encoding problem I have yet to properly fix.
console.log("link: ", link); //always the correct url here.
Share.share({ url: link }); // request to open the native sharing dialog activity. ('url' for ios)
};
Correct url is something like:
https://www.sample.com/share_ticket?my=query&params=here
Stripped url is something like:
https://www.sample.com/
sharing dialog image.
https://www.sketchappsources.com/resources/source-image/simple-share-actionsheet-ios-13-hugo.png
"COPY" actions always copy the correct url, "MAIL" works as well.
Using "MESSAGE" or any of the "recent" actions in the top row fails, sharing the base url only.
The title and website abbreviation at the top of the action sheet do render properly, pulling metadata/images from the website.
To make it more complicated, the share function does yield the correct url directly after a fresh install, but then never again. Console.logs always log the correct url.
I have tried react-native-share's library with the same results. (Wraps the same native bridge code?)
I have checked the react-native github page for issues related to this with no result. I have combed StackOverflow for a few days without results. Usually that means its something simple.. here's to hoping.
Anyone out there run into this issue? Thanks!

Distributing iOS app over-the-air using Firebase

Our organization uses an enterprise account and the itms-services protocol to internally deploy apps over-the-air.
Our plists and ipas are hosted on Amazon S3 and everything works as expected - you click a link and an alert pops up asking if you'd like to install the app.
We are now trying to store our files on Firebase storage and send out links in the following format:
itms-services://?action=download-manifest&url=https://firebasestorage.googleapis.com/v0/b/my-app.appspot.com/o/MyApp.plist?alt=media&token=my-token
Pasting this link in Safari opens a dialog asking: 'Open this page in "iTunes"?' Clicking on open does nothing.
It's worth mentioning that if I upload the same plist/ipa combo to S3 it works fine. Also, if I paste the url itself (without the itms-services part) I am able to download both plist and ipa.
Why don't I get the dialog asking if I'd like to install the app? I have a feeling that it's somehow related to an issue with permissions to Firebase but as I am able to download the file directly I'm not really sure.
Your url should be encoded.
For example:
Normal url: https://firebasestorage.googleapis.com/v0/b/my-app.appspot.com/o/MyApp.plist?alt=media&token=my-token
Encoded url: https%3A%2F%2Ffirebasestorage.googleapis.com%2Fv0%2Fb%2Fmy-app.appspot.com%2Fo%2FMyApp.plist%3Falt%3Dmedia%26token%3Dmy-token
Finally: itms-services://?action=download-manifest&url=https%3A%2F%2Ffirebasestorage.googleapis.com%2Fv0%2Fb%2Fmy-app.appspot.com%2Fo%2FMyApp.plist%3Falt%3Dmedia%26token%3Dmy-token
gfvilela's solution worked in 2017 and if you have spaces you still need to do that but there is a new issue where firebase removes the // after itms-services: in redirects.
I used https://www.redirect-checker.org/index.php to check what my redirect actually redirects to and it shows that it's leaving out the //:
itms-services:?action=download-manifest&url=...
Somehow this still opens the popup "Open this page in Itunes" but it will not open the next popup "<domain> would like to install <app>" probably because the Itunes process that is opened parses the URL different from how iOS handles deeplinks.
I can only speculate but I think the problem with firebase removing the // lies with how they handle : since they use it with their "glob" pattern matching: https://firebase.google.com/docs/hosting/full-config.
Attempts
I tried encoding the entire URL, just the // part and even only the : but all of these redirect to mydomain.com/itms-services....
I tried using the Dynamic Links module but that doesn't support this either:
Please enter a valid URL starting with http:// or https://
Solution
I think the only way to do this using firebase is to redirect using functions similar to this question: Cloud function for firebase to redirect to a URL
This is definitely more work and is not included in the free firebase package but here is how I solved it:
run firebase init functions
Write the redirect in functions/index.js
exports.downloadApp = functions.https.onRequest((req, res) => {
res.redirect(302, 'itms-services://?action=download-manifest&url=<YOURMANIFEST>');
});
Run firebase deploy --only functions
In your firebase.json add a rewrite (not redirect) to the function:
"rewrites": [
{
"source": "/app",
"function": "downloadApp"
}
]
Run firebase deploy --only hosting
Note
This is an awful lot of workaround just because firebase doesn't have an escape character for : for when you don't want to use it for "glob" pattern matching.

Custom URL scheme without confirmation prompt (Swift)

I've found two options to open my app from a Safari web page: a custom URL scheme created in my app project's Info.plist or Apple's Universal Linking. Obviously the custom URL scheme is the easiest one to set up, but the problem I'm having with this is that Safari shows a confirmation window asking "Open myapp?" first and the user has to tap OK before the app actually opens. I want my app to open automatically as the scheme is opened, and I'm being told the only way to do this is through Universal Linking (please correct me if this is not true). If this is true, however, I would like to know if it's possible in any way to put the required apple-app-site-association file on a http:// domain instead of https://? According the official Apple documentation the format of a correct Universal Link starts explicitly with https:// but my domain name can't be loaded on https:// without redirecting a few times and that messes up the web services I've written to execute other tasks in my app. The two main questions I'm left with after this issue:
1) Is it really impossible to work around the confirmation prompt using a custom URL scheme (myscheme://)? If it's not impossible, how can I do this?
2) If I have to use Apple Universal Linking, can I use a http:// domain? If so, how do I do it? Right now if I load up the universal link, it just shows the dictionary inside the apple-app-site-association file, which I'm pretty sure is not supposed to happen. I'm told it's supposed to send a NSUserActivity object to my app delegate. How can I accomplish this with a http:// link?
It is not possible to trigger a custom URI scheme without showing an alert to the user. This used to be possible in iOS 8, but iOS 9 started showing the alert for all apps. And iOS 10.3 has extended that even to the App Store itself. You cannot bypass this. Universal Links were created to replace URI schemes for this behavior, so you do need to use them instead.
From your description, I believe you may be misunderstanding how Universal Links work. To answer the literal questions you asked first, no the Universal Link URL itself does not need to be on the https:// protocol, and yes, the apple-app-site-association must be served over https:// without redirects.
However, it sounds like you're trying to serve the content of the apple-app-site-association file for every Universal Link. That is not the correct implementation — the AASA file is hosted only at https://example.com/apple-app-site-association, and iOS automatically retrieves it when the app is installed. After that, any URL on example.com that matches the criteria in the AASA file will be eligible for Universal Links.
All of that said, you really don't want to built out this system on your own. I suggest looking into Firebase Dynamic Links or Branch.io (full disclosure: I'm on the Branch team).
Is it really impossible to work around the confirmation prompt using a custom URL scheme (myscheme://)? If it's not impossible, how can I do this?
That is possible with some hacky tricks and BAD user experience. It requires user to press "add to home screen" button, so I don't recommend this solution in most cases.
set your app scheme like myapp
create the following html file and put it into the web
window.onload = function() {
if (("standalone" in window.navigator) && window.navigator.standalone) {
window.location.href = 'myapp://open'
}
}
open the html file with safari and "add to home screen"
open the home screen icon and your native app will launch
The point is the meta tag.
<meta name="apple-mobile-web-app-capable" content="yes" />
Without this, safari will launch and confirmation prompt will appear.

Lowercase query parameters in URL using iOS universal links

I was just about to link an app to a domain using universal links (or also refered to as associated domains; available since iOS 9). So far everything worked out and while testing with Safari I was able to open my app correctly, also parse path and query parameters of any URL.
But when I opened an URL that worked before with Safari from inside iMessage or Mail it failed.
I found out that when opening an app that way each query parameter gets reformatted to lowercase characters, which is invalid towards my data handling.
For example
https://www.example.com/path?paramNameOne=ValueOne&paramNameTwo=ValueTwo
is altered to
https://www.example.com/path?paramnameone=valueone&paramnametwo=valuetwo
So question is if there is a detail with this technology that I'm not aware of or - in case of a known bug - a propper strategy to cope with this issue.
Universal linking does not accept font formatting. Remove styling from your link and check

Firebase.google.com Firebase Dynamic Links in iOS ( Swift )

How to Open an iOS App using Firebase Dynamic Links and Pass or get Parameters To an App Via Custom URL Scheme in iOS(swift)?

for eg :- https://q3tyj.app.goo.gl/abcd
My URL Scheme is ‘q3tyj.app.goo.gl’ in iOS app in Url Types.
If I type q3tyj.app.goo.gl in safari, I am able to open the application. But if I type q3tyj.app.goo.gl with some extra parameter like https://q3tyj.app.goo.gl/abcd in safari, , I am not able to open the application.

please also explain me how to get “link” parameter (which associated with dynamic link) from dynamic link in iOS app ( Swift ) .


I followed steps which were mentioned in Firebase.google.com for iOS ( Swift ).but its not working.
Thanks,
Nirav Virpara
It's hard to know exactly where your error is, but a few things to help you debug:
If you go to https://q3tyj.app.goo.gl/apple-app-site-association, you should see some JSON that points to your app. If you don't see this, make sure you've entered your team ID and your App Store ID in the project settings in the Firebase console
Make sure you're using your Bundle ID, not the shortlink domain, as your custom URL scheme in Xcode
Make sure you've enabled Associated Domains in the Capabilities tab of your Xcode project, and your domain looks like applinks:q3tyj.app.goo.gl
Universal Links (and, therefore, Dynamic Links) generally don't work if you type them directly into the Safari address bar. Instead, try typing the URL into an app like Notes and then clicking on them from there.
Good luck!
If you type https://q3tyj.app.goo.gl/apple-app-site-association this link in your browser and check, you will end up seeing a json, meaning your apple-app-site-association is correctly configured on your google website. However, you need to make sure that teamid in your google console or appid is correct.

Resources