Skip to content

Commit

Permalink
README bump & funding.yml
Browse files Browse the repository at this point in the history
  • Loading branch information
fdocr committed Dec 5, 2023
1 parent b2a7ba7 commit e2a426f
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 41 deletions.
1 change: 1 addition & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
github: [fdocr]
92 changes: 57 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,65 +1,87 @@
# Universal Deep Link (UDL) Server

[![Build Status](https://github.com/fdocr/udl-server/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/fdocr/udl-server/actions/workflows/ci.yml/badge.svg?branch=main)

This is a server that bounces traffic to better leverage Deep Linking in mobile apps.

The project's objectives are to be a simple, effective and lightweight tool that can help any website provide a seamless integration with their mobile apps.
The project's objectives are to be a simple, effective and lightweight tool that can help any website provide a seamless integration with associated mobile apps.

I used to host a server for public use (free of charge) but [due to _"reasons"_](https://github.com/fdocr/udl-server/issues/19#issuecomment-1536587313) it's not available anymore. You can [self host the project](#self-hosting) on most PaaS hosting providers quite easily. [Create an issue](https://github.com/fdocr/udl-server/issues/new) if you need help or have questions.
## How it works

## How it works, and why?
Modern mobile browsers provide developers with [Universal Links (iOS)](https://developer.apple.com/library/archive/documentation/General/Conceptual/AppSearch/UniversalLinks.html) or [Android Intents](https://developer.chrome.com/docs/multidevice/android/intents/) to support deep linking users from a website directly into a mobile app.

It's a dead simple pivot server that will allow for Universal Links to work with you app.
However, **do you share links to your website on social media or other 3rd party apps?** Your website will likely be browsed on a webview (embedded browser) inside a 3rd party app, i.e. Instagram, TikTok, Reddit, etc. This means iOS Universal Links won't open your app automatically because users are clicking links on the same domain.

Modern mobile browsers provide developers with [Universal Links (iOS)](https://developer.apple.com/library/archive/documentation/General/Conceptual/AppSearch/UniversalLinks.html) or [Android Intents](https://developer.chrome.com/docs/multidevice/android/intents/) to support deep linking users from a website directly into a mobile app. However, Operating Systems currently won't trigger these features when the user clicks a link within the same domain or when the user types the URL directly in the address bar.
> When a user is browsing your website in Safari and they tap a universal link to a URL in the same domain as the current webpage, iOS respects the user’s most likely intent and opens the link in Safari. If the user taps a universal link to a URL in a different domain, iOS opens the link in your app. ([reference docs](https://developer.apple.com/library/archive/documentation/General/Conceptual/AppSearch/UniversalLinks.html))
These and other edge cases make for a less than ideal experience, if your objective is to allow for a seamlessly transition to your mobile app. A custom banner (in your website) that links to an external site will trigger the deep linking though, and this is where the UDL Server comes in.
This is how the UDL Server helps make this a smooth experience for you:

![diagram](udl-server-diagram.png)
![diagram](udl-server-deep-link.png)

## Self-hosting
It's a dead simple pivot server that will allow for Universal Links to trigger wherever your users are browsing from.

Power users will likely need better reliability and scalability than a free service is able to offer. Self-hosting with Heroku (or similar hosting solutions) is as easy as:
## Usage

1. Fork this repository
1. Configure the app to automatically deploy to your Heroku account
- Using a [custom domain with Heroku](https://devcenter.heroku.com/articles/custom-domains) is very simple (i.e. `udl.your-domain.com`)
- Heroku's default subdomain works too (i.e. `my-app.herokuapp.com`)
1. Keep up with upstream (this repo) for future updates
- Use the **"Sync fork"** feature in your GitHub repo
- Or manually with git commands:
- `git remote add upstream [email protected]:fdocr/udl-server.git`
- `git pull upstream main`
- `git push origin main`
1. Configure `AASA_APP_ID` ENV variable to match your App Id
- Use the team ID or app ID prefix, followed by the bundle ID (joined by a dot `.`).
- This will allow your UDL Server to directly serve as a Universal Link target for your app and improve the experience
- Example: `R9SWHSQNV8.com.forem.app`
Following the **"reservation"** example app from the diagram above (assuming you support Universal Links already) all you need is to add a button (link) on your website that reads "open in app" requesting a redirect to your target location:

## Throttling, Safelist and Blocklist
```
https://udl.fdo.cr/?r=https://reservation.com/restaurants/silvestre
```

The UDL Server uses [Rack::Attack](https://github.com/rack/rack-attack) to protect itself against abuse. It will respond with a `429` instead of the expected redirect when this happens.
This will bypass the limitation of Safari (embedded webview) that doesn't allow your Universal Links to trigger. You should now have a working "open in app" UX.

[IP based throttling](https://github.com/rack/rack-attack#throttling) is enabled by default with a limit of 3 requests on a 10 second period, but only if you provide access to a Redis to work as cache (via `REDIS_URL` ENV variable). You can override these values by using `UDL_THROTTLE_LIMIT` and `UDL_THROTTLE_PERIOD`.
`https://udl.fdo.cr` is a **public (free to use) UDL Server** instance for anyone to try out and use on your own. It has usage limits (throttling), which should be more than enough for most low-medium traffic websites.

You can further restrict if the server will allow or deny a redirect based on passing in a regular expression via `UDL_SAFELIST_REGEXP` or `UDL_BLOCKLIST_REGEXP`. These regular expressions will be checked against the `r` param and will allow or deny the response (respectively). For example:
If this service adds value to you or your company please consider sponsoring me right here on GitHub. I offer different sponsor tiers too where I will host a private instance without usage limits for high traffic websites. [Read more about this on my profile](https://github.com/sponsors/fdocr) to support my OSS work.

```bash
# All redirect requests for "tiktok.com" will be safelisted
# https://github.com/rack/rack-attack#safelisting
UDL_SAFELIST_REGEXP="^https:\/\/tiktok.com"
```
## Self host

[Read more](https://github.com/rack/rack-attack#how-it-works) about how `Rack::Attack` safelist/blacklist features work.
The project makes it easy for you to self host an UDL Server. The easiest and recommended way to do this is to:

## Troubleshooting
1. Fork this repository
1. Configure a PaaS to automatically deploy from your fork repository
1. Configure your custom domain/sub-domain
1. Keep up with upstream (this repo) for future updates
- Use the **"Sync fork"** feature in your GitHub repo
- Or manually using git
1. Customize your deployment using ENV variables
- `THROTTLE_LIMIT`
- Number of requests allowed per `THROTTLE_PERIOD`
- i.e. `30` (default is `5`)
- `THROTTLE_PERIOD`
- Period to track requests to be throttled in seconds
- i.e. `60` (default is `30`)
- `SAFELIST`
- Space separated list of domains for private instance usage
- i.e. `"fdo.cr github.com"` (if set it will disable throttling)
- `DISABLE_DEFENSE`
- Disable throttle/safelist feature
- i.e. `"true"`
- `AASA_APP_IDS`
- Enable activity continuation and other [associated domain features](https://developer.apple.com/documentation/xcode/supporting-associated-domains)
- i.e. `ABCDE12345.com.example.app`

Alternatively you can run the lightweight [`fdocr/udl-server`](https://hub.docker.com/repository/docker/fdocr/udl-server/general) Docker container on your own. At the time of this writing the docker image is only about `26.3 MB` in size (`10.87 MB` compressed).

### Troubleshooting

Some common details to keep in mind in case your redirects aren't working properly:

- Make sure your redirects are all using `https`
- You will likely need to make this request on a `target="_blank"` anchor tag in order to get Apple's Universal Links to work.
- If links aren't working try to use `target="_blank"` on your anchor tag
- Make sure your iOS app has properly configured [Associated Domains](https://developer.apple.com/documentation/safariservices/supporting_associated_domains) for the websites you want to support.
- There's a chance it won't work in development mode (i.e. only signed with a Production certificate). I suggest releasing to TestFlight in order to properly test everything.

## Performance & Roadmap

The project was originally written using [Ruby](https://www.ruby-lang.org/en/) & [Sinatra](https://sinatrarb.com/). It was a joy to write and worked perfectly fine, but I always wanted this "pivot request" to be as instantaneous as possible. It was doing `~40 ms` response times for `P99` (on less than 1 rpm but consistent daily traffic).

The project is now ported to [Crystal](https://crystal-lang.org/) and [Kemal framework](https://kemalcr.com/). I'm now seeing response times drop to microseconds, which is very exciting!

![diagram](nanosecond-response-times.png)

I'm aiming to work on adding a bunch of other features to the project and [share blog posts](https://fdo.cr/blog) with walkthroughs/benchmarks/etc. Feel free to tag along and submit feature requests in the issue tracker!

## Contributing

Please check out the [Contributing Guide](https://github.com/fdocr/udl-server/blob/main/CONTRIBUTING.md).
Expand Down
Binary file added nanosecond-response-times.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 6 additions & 6 deletions src/server.cr
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,17 @@ get "/.well-known/apple-app-site-association" do |env|
aasa_app_ids = aasa_apps.split(" ")
{
applinks: {
apps: [] of String,
apps: [] of String,
details: aasa_app_ids.map do |id|
{ appID: id, paths: [ "/*" ] }
end
{appID: id, paths: ["/*"]}
end,
},
activitycontinuation: {
apps: aasa_app_ids
}
apps: aasa_app_ids,
},
}.to_json
else
{ error: "AASA_APP_ID not configured" }.to_json
{error: "AASA_APP_ID not configured"}.to_json
end
end

Expand Down
Binary file added udl-server-deep-link.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed udl-server-diagram.png
Binary file not shown.

0 comments on commit e2a426f

Please sign in to comment.