I am developing a mobile app using Flutter. For the authentication process, the requirement is to allow users to log in by email registration or social accounts, so I decided to use Keycloak as auth server and, more specifically, the OAuth2 Authentication Flow.
Our backend will have a Spring Cloud Gateway (BFF) that will act as a confidential client for Keycloak. Upon successful authentication, the gateway will store users' access tokens and map them to sessions, returning a cookie that represents the logged in user. On each subsequent API call, the access token is then retrieved based on session cookie and relayed to downstream services.
So far what I have understood/accomplished is the following:
The problem is, I would like to use Custom Tabs to handle browser-based authentication for better integration with our app with respect to WebView. How can I retrieve the session-id cookie that our backend sends along with the redirect to the mobile app URI? This cookie should be included in all subsequent calls that the app performs, so there has to be a way to intercept or access it and store in some cookie jar used by flutter HTTP packages (dio or http). So far I talked about Android but the same should hold for iOS and its Safari View Controller.
I know that the cookie based approach could be avoided by making the mobile app a non-confidential client that can directly start the authentication flow, handle itself redirects from Keycloak, and store the access/refresh tokens. Still, I don't like the idea of directly storing tokens at the app level, since they could be compromised more easily and control over user session could be lost.
Sorry if this is a duplicate, but I searched a lot on this topic over the past few days and didn't find a definitive answer. All I found was basically along the lines of "use WebView/Custom Tabs with custom URI scheme redirects to start the login flow, then the user is authenticated and the control returns to your app".
RETRIEVING THE COOKIE
Your app will receive the authorization response when the Custom Tab
or ASWebAuthenticationSession
window is closed, and the authorization response is returned to the app. This is usually a URL like this:
com.mycompany.myapp:/callback?code=xxx&state=yyy
The authorization code can then be extracted and posted to the backend, where it can be exchanged for tokens. The backend can then return a session cookie. The mobile client can read the response headers to extract the cookie, then send it in API requests.
MOBILE APPS AND COOKIES
The rest of my answer is an examination of cookie based sessions in mobile apps. It is a bit beyond the scope of the question, so free to ignore if you prefer.
BACKEND FOR FRONTEND PATTERN
This is designed for web clients, where the use of cookies reduces the impact of cross site scripting exploits. It is specific to the browser and explained in OAuth for Browser Based Apps. Note also that the browser prevents JavaScript code from reading HTTP-only cookie response headers.
API MESSAGE CREDENTIAL
A mobile client uses native views that do not send cookies. Instead, the mobile client would need to send the cookie HTTP header manually. Therefore using a session cookie is no more secure than using an access token directly, since both are plain string HTTP headers. Indeed, there are risks that the cookie may be a longer lived credential, that could be misused by an attacker for longer than the access token.
CONFIDENTIAL CLIENT?
By using a BFF, the token endpoint can require a client secret (which an attacker would not know) before tokens are returned. But an attacker can instead just call the weaker BFF entry points, which do not require a client secret. Using a BFF does not turn either a browser based app or mobile app into a true confidential client.
TOKEN REFRESH
Session cookies often originate from website solutions that existed before OAuth. In a web client I prefer to use separate cookies per OAuth token. The access token cookie
then has a short lifetime such as 15 minutes. If stolen by an attacker, it cannot be abused for long. To enable this, the client also needs a separate, longer lived, token refresh credential, eg another cookie.
By returning this directly to the client, the app has best control over synchronizing token refresh, if multiple views call APIs concurrently. If you try to do token refresh in the BFF, it will be more difficult to achieve this, which can lead to reliability problems for some types of app.
MOBILE BFF USE CASE
A mobile BFF might be useful in advanced cases where a mobile client must provide a credential to an authorization server. In such cases, routing a request via a utility backend component that attaches the credential can make sense. This does not mean the app has to use cookies as API credentials though.
LOGOUT
One advantage of a session cookie with backend stored tokens model, can be to deny all use of API credentials immediately upon logout, which OAuth itself does not guarantee. Some people might consider this behaviour important.
SUMMARY
In OAuth, mobile apps usually follow the RFC8252 standard, and use access and refresh tokens directly. Using a BFF for a mobile app is not necessary less secure, but also does not improve upon the security of the mobile app beyond RFC8252. It also has the potential to add unnecessary complexity.