Originally when we built the mobile app Apple offered seamless embedding of Safari in mobile apps.
All was good, you could log in to Safari, and magically with one click have the application authenticated.
However, fast forward a few years and Apple released iOS 11. Sadly, this broke “shared cookies”, this meant that you had to log in to the application web sites AGAIN after already being logged in, in Safari.
To add additional layers of pain , cookie management in Safari View Controller is somewhat sketchy. In iOS 10 we used to see regular random logoffs in the app, this got a bit better in iOS 11, now iOS 12 is out we are seeing all sorts of edge cases and weirdness.
At Discourse we are committed to the web and the future of the web. The long game is that no app will be needed and the operating systems on the phone will provide all the features we want (notifications, dedicated PWA and so on). Luckily Android are pushing forward here, and the need for an App is lessened every year. Apple are making some slow starts here, but I estimate it will be at least 2-3 years prior to them catching up to where Android is today.
Delegated authentication
To solve our immediate pain of
- Double login required
- Safari View Controller random log offs
- Safari View Controller trouble handling authentication redirects with SSO / OAuth
- No way to login via email
- No way to identify traffic as application traffic vs mobile safari traffic
We are going to introduce a system of “delegated authentication” using user API.
When asking for basic info about the site, the site will signal it supports the new interfaces to allow for a transition time.
Basic flow (not logged on to app):
-
App opens to Safari on the Mobile phone asking for a token with
notifications,session_info
scope AND an extra param to ask for aone time password
. https://github.com/discourse/DiscourseMobile/blob/master/js/site_manager.js#L495 . -
The UX on Discourse communicates it will allow those scopes and allow the mobile to login.
-
Discourse generates a time limited, single use login token which is a
SecureRandom.hex
, this can be stored in redis. It is returned encrypted to the application with rest of the API key info. -
The app then open Discourse in Safari View Controller at a special new URL.
https://discourse.site/session/otp?token=ABCDEFGE
-
That redirects back to
/
and user is logged on to SVC.
Advanced flow (user already has an api key but is not logged on to site)
-
When opening SVC from the app we will always add
?user_api_public_key=USER_API_PUBLIC_KEY
-
This allows us to flag the session as a mobile app session and ensure user is logged on.
-
If the user is logged in and all is good an ember initializer will strip the
?user_api_public_key=USER_API_PUBLIC_KEY
with replace state. -
If user is not logged on AND we know the public key is legit, we will redirect back to the app, asking it to get another OTP.
discourse://otp=true&site=meta.discourse.org
-
If app knows it needs another otp for the site it opens Safari and ask for an otp:
${site.url}/user-api-key/otp?public_key=[...]
-
Discourse in Safari will display a simple page “Would you like to allow Discourse Mobile to access the site” or something like that
-
When user clicks button it will do the same redirects the basic flow does.
Delegated auth is generally useful for all user api keys so this work is not throwaway. For example an Electron wrapper for Discourse could use the same mechanism to log in when you are already logged in to Chrome.
That said, our immediate pain is iOS mobile app.
Additionally part of this work is a review of our user api key system, and long needed documentation of the system, cause people occasionally ask about this and documentation is very weak.
This work is also strategic, cause it means that people will always be logged on to Discourse in Safari (every single app user) which will make transitioning to other systems easier. Maybe in a few years all the app will do is automate creation of PWA links for Apple. I don’t know, but this moves us in the right direction.