Re: [OAUTH-WG] Recommendations for OAuth 2.0 with Browser-Based Apps

David Waite <david@alkaline-solutions.com> Mon, 06 May 2019 20:42 UTC

Return-Path: <david@alkaline-solutions.com>
X-Original-To: oauth@ietfa.amsl.com
Delivered-To: oauth@ietfa.amsl.com
Received: from localhost (localhost [127.0.0.1]) by ietfa.amsl.com (Postfix) with ESMTP id 3D2D81201D7 for <oauth@ietfa.amsl.com>; Mon, 6 May 2019 13:42:14 -0700 (PDT)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -1.901
X-Spam-Level:
X-Spam-Status: No, score=-1.901 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, SPF_PASS=-0.001] autolearn=ham autolearn_force=no
Received: from mail.ietf.org ([4.31.198.44]) by localhost (ietfa.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id dc-DEHOHpHBZ for <oauth@ietfa.amsl.com>; Mon, 6 May 2019 13:42:12 -0700 (PDT)
Received: from alkaline-solutions.com (lithium5.alkaline-solutions.com [173.255.196.46]) by ietfa.amsl.com (Postfix) with ESMTP id C5915120175 for <oauth@ietf.org>; Mon, 6 May 2019 13:42:11 -0700 (PDT)
Received: from [IPv6:2601:282:202:b210:d54:39a7:6ba1:58a5] (unknown [IPv6:2601:282:202:b210:d54:39a7:6ba1:58a5]) by alkaline-solutions.com (Postfix) with ESMTPSA id 5A814316B2; Mon, 6 May 2019 20:42:10 +0000 (UTC)
Content-Type: text/plain; charset="utf-8"
Mime-Version: 1.0 (Mac OS X Mail 12.4 \(3445.104.11\))
From: David Waite <david@alkaline-solutions.com>
In-Reply-To: <11125817.AKI43N3Yza@papegaaij>
Date: Mon, 06 May 2019 14:42:09 -0600
Cc: oauth@ietf.org
Content-Transfer-Encoding: quoted-printable
Message-Id: <C0E40840-26FE-4BC9-8D13-B06D399E4A52@alkaline-solutions.com>
References: <11125817.AKI43N3Yza@papegaaij>
To: Emond Papegaaij <emond.papegaaij@gmail.com>
X-Mailer: Apple Mail (2.3445.104.11)
Archived-At: <https://mailarchive.ietf.org/arch/msg/oauth/rgi6sp4-6rXV0a-ObxeZUEheeNM>
Subject: Re: [OAUTH-WG] Recommendations for OAuth 2.0 with Browser-Based Apps
X-BeenThere: oauth@ietf.org
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: OAUTH WG <oauth.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/options/oauth>, <mailto:oauth-request@ietf.org?subject=unsubscribe>
List-Archive: <https://mailarchive.ietf.org/arch/browse/oauth/>
List-Post: <mailto:oauth@ietf.org>
List-Help: <mailto:oauth-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/oauth>, <mailto:oauth-request@ietf.org?subject=subscribe>
X-List-Received-Date: Mon, 06 May 2019 20:42:14 -0000

On May 6, 2019, at 1:42 PM, Emond Papegaaij <emond.papegaaij@gmail.com> wrote:
> 
> Hi all,
> 
> For a browser-based app, we try to follow the recommendations set in draft-
> ietf-oauth-browser-based-apps-01. This does allow us to create a secure OAuth 
> 2.0 browser-based application, but at the moment it comes at a cost wrt. user 
> experience when the access token expires. Our current solution forces us to 
> redirect the user to the authorization server for a new authorization code. 
> This will destroy most state the browser-based app has, causing the user to 
> loose data. We are looking for a way to get a new access token in a secure way 
> without disrupting the user.
> As a refresh token is not issued to the app (as it should be), the application 
> is forced to do a front-channel re-authentication for an authorization code. 
> We are thinking of letting this front-channel communication happen in a hidden 
> iframe. Naturally, this can only be done if no user interaction is required, 
> hence we want to use the OIDC prompt=none. Is this a viable way of doing this 
> re-authentication? Can it hurt to open up our authorization server for non-
> interactive authorization requests inside an iframe? At the moment we do not 
> allow iframes at all.

Some AS implementations will block authentication in an iframe, but will allow you to use the OIDC prompt=none. This is already used quite often today by implicit apps. It is possible that AS implementations may allow iframes in the future, by detecting the frame is not covered with any buttons, and having the authentication be based on phishing-resistant authentication methods like W3C Web Authentication.

You could also trigger re-authorization with a user click, thus allowing opening the AS in a new window or tab. Once back on the site via callback, the temporary/pop-up window can do things like exchange the code for an access token, persist it, postMessage the original window, do window.close, etc.

The iframe and pop-up methods together can be used in lieu of persisting state across a redirect to the ISP. Many apps after reaching a sufficient level of complexity just wind up persisting the page state in some combination of local and remote storage, however. Javascript state is very brittle and will be broken by things as simple as a page refresh.

Native apps which opened the system browser had at least the capability of this problem as well - the application could be unloaded from memory/quit between when authentication started and ended.

On the other hand, refresh tokens IMHO are given quite a bit more fear in browser apps than warranted. It really depends on the AS - whether it can tie refresh tokens to the user’s authentication, or if they are tied to a long-term / persisted / "offline” authorization independent of an active user authentication. Currently, the latter is more common in implementations, and doesn’t make sense for browser applications. This doesn’t mean refresh tokens are automatically discounted for all environments.

Given the choice between an 8 hour access token, or a 10 minute access token and a refresh token that will expire at a maximum of 8 hours, the second provides quite a few more options to be more secure. (eg. checking backing user session and revocation, checking for updates to client blacklist, the rotation of the access token, rotating refresh tokens to prevent use by more than one client, expiring access on inactivity based on lag in refreshing, and so on).

If the refresh token is tied to the AS concept of user session, then it mostly replaces the ‘hidden iframe’ use above - you’ll only have your refresh token expire when the AS is asking for user presence on the front channel, presumably for interaction. Although, I suppose in some environments there could be a non-interactive reauthentication/factor as well (such as kerberos, MTLS, or re-verifying user location via geoip) where a hidden iframe might still provide UX benefit.

Browser based apps are significantly more vulnerable to code injection attacks than native apps (although don’t believe native apps are immune), so it may make sense for an AS to have a stricter default policy for browser-based applications than they would have for a native app. It also could make sense to allow for more scopes or longer-lived tokens for an audited, first-party browser-based application. Restrictions may be opened up even more for applications/browsers which also use PoP methods to prevent key exfiltration.

OAuth is nice in that the AS consolidates those responsibilities, however the flip side of that is that a client developer is really dependent on an AS to   provide a combination of features juggling good security and user experience.

> Maybe anybody knows a different way of achieving this? As I cannot believe we 
> are the only ones facing this issue, maybe a recommendation can be put in the 
> spec?

I think so far it has been an omission since this isn’t a “new” problem - implicit has the same issues and a subset of techniques (the difference bineg the capability to use refresh tokens now). However, it makes sense to document current practices in a BCP document :-)

WRT refresh tokens: I’m at the start of an independent blog post on considering token policies and lifetimes, but I expect it to take some time to polish and to get through reviewer feedback before publishing.

-DW