[OAUTH-WG] user-agent flow needs a rewrite

Brian Eaton <beaton@google.com> Sun, 11 July 2010 03:55 UTC

Return-Path: <beaton@google.com>
X-Original-To: oauth@core3.amsl.com
Delivered-To: oauth@core3.amsl.com
Received: from localhost (localhost [127.0.0.1]) by core3.amsl.com (Postfix) with ESMTP id 077813A68D9 for <oauth@core3.amsl.com>; Sat, 10 Jul 2010 20:55:05 -0700 (PDT)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -100.47
X-Spam-Level:
X-Spam-Status: No, score=-100.47 tagged_above=-999 required=5 tests=[AWL=-1.507, BAYES_40=-0.185, FM_FORGED_GMAIL=0.622, J_CHICKENPOX_42=0.6, USER_IN_WHITELIST=-100]
Received: from mail.ietf.org ([64.170.98.32]) by localhost (core3.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id V6TmNaMtr3uq for <oauth@core3.amsl.com>; Sat, 10 Jul 2010 20:55:03 -0700 (PDT)
Received: from smtp-out.google.com (smtp-out.google.com [74.125.121.35]) by core3.amsl.com (Postfix) with ESMTP id 65EEF3A6827 for <oauth@ietf.org>; Sat, 10 Jul 2010 20:55:03 -0700 (PDT)
Received: from hpaq5.eem.corp.google.com (hpaq5.eem.corp.google.com [172.25.149.5]) by smtp-out.google.com with ESMTP id o6B3t9D5012247 for <oauth@ietf.org>; Sat, 10 Jul 2010 20:55:09 -0700
DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=google.com; s=beta; t=1278820509; bh=x3nYA29hboWLmHq5VD7ilvgNRD8=; h=MIME-Version:Date:Message-ID:Subject:From:To:Content-Type; b=QhGZnTt6BjlaIJPd9nKIuccUDazzMPv+qVKrbI0g4wHi5qpao+16JKgnZY21XWh1h /Qi0yfFEKPi6GlNRg/1zw==
DomainKey-Signature: a=rsa-sha1; s=beta; d=google.com; c=nofws; q=dns; h=mime-version:date:message-id:subject:from:to:content-type:x-system-of-record; b=Iy5TlFZznGwUUTig/tlvXDcIOSRoboKmNeHiUTbQjuw3QnEtG7k6KEDYASRMcKOgw 8ckUoXmMcIeVcKAGXvwgQ==
Received: from pvc7 (pvc7.prod.google.com [10.241.209.135]) by hpaq5.eem.corp.google.com with ESMTP id o6B3t7CM006496 for <oauth@ietf.org>; Sat, 10 Jul 2010 20:55:08 -0700
Received: by pvc7 with SMTP id 7so1313087pvc.5 for <oauth@ietf.org>; Sat, 10 Jul 2010 20:55:07 -0700 (PDT)
MIME-Version: 1.0
Received: by 10.142.133.12 with SMTP id g12mr2837488wfd.118.1278820506843; Sat, 10 Jul 2010 20:55:06 -0700 (PDT)
Received: by 10.142.193.19 with HTTP; Sat, 10 Jul 2010 20:55:06 -0700 (PDT)
Date: Sat, 10 Jul 2010 20:55:06 -0700
Message-ID: <AANLkTimYZg_PrDn9JvjHwuVHOwZuGIPZk6affFCTPQor@mail.gmail.com>
From: Brian Eaton <beaton@google.com>
To: oauth@ietf.org
Content-Type: text/plain; charset="ISO-8859-1"
X-System-Of-Record: true
Subject: [OAUTH-WG] user-agent flow needs a rewrite
X-BeenThere: oauth@ietf.org
X-Mailman-Version: 2.1.9
Precedence: list
List-Id: OAUTH WG <oauth.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/listinfo/oauth>, <mailto:oauth-request@ietf.org?subject=unsubscribe>
List-Archive: <http://www.ietf.org/mail-archive/web/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: Sun, 11 Jul 2010 03:55:05 -0000

The draft 9 spec has no efficient way for a javascript client to
request a verification code.  The spec creates extra client-to-server
round trips.  There is also some inaccurate description of the
properties of the profile.  The problems are located in section 1.4.2:
http://tools.ietf.org/html/draft-ietf-oauth-v2-09#page-11.

The spec requires that the verification code is always returned on the
query string.  This ends up busting the browser cache and creating an
extra client-to-server round trip.  It basically defeats the purpose
of the user-agent profile entirely.  (If you could afford an extra
client-to-server round trip, you would just use the web server flow.)
To clarify, here's how the flow works in practice:

client web site loads
relay page is loaded from the client web site.  This page is typically
heavily cached.  It is almost always loaded from browser cache; it is
very rarely actually fetched from the server, for performance reasons.
client web site redirects browser to authorization server
authorization server redirects back to relay page, with new data on the fragment
there is no client-to-server round trip*.  the client javascript is
loaded from cache, and parses the fragment data
if necessary, the client javascript uses XMLHttpRequest (XHR) to pass
data up to the web server.  This typically happens in the background,
however, while the user is otherwise interacting with the web
application.

Here's what would happen if someone implemented the spec as written, though:

client web site loads
relay page is loaded from the client web site.  This page is typically
heavily cached.  It is almost always loaded from browser cache; it is
very rarely actually fetched from the server, for performance reasons.
client web site redirects browser to authorization server
authorization server redirects back to relay page, with new data on
the fragment, *plus a verification code on the query string*
*browser fetches new copy of relay page from web server*
*user sits and waits*
*kittens die when users have to wait*


There are also several problems with the description of the flow.
Most of the description seems to be left over from the time when the
flow was spec'ed to only return an access token.  Now that it can
return a verification code, sentences like "does not utilize the
client secret" are no longer true.

The phrase "because the access token is encoded into the redirection
URI, it may be exposed to the end-user and other applications residing
on the computer" is just silly.  That's equally true of the web server
flow.

So basically I think section 1.4.2 needs a rewrite, starting with the
entire justification of the flow.

Here's a brief description of how the flow should work:

client web site loads
client redirects to authorization server with:
   type="user_agent"
   client_id=client identifier
   redirect_uri=absolute URL on client web site
   response_type="code-and-token" or "token"  (The client chooses the
response type based on whether they want long-lived access or
short-lived access to the user's data.  Short-lived: just use
"token".)

authorization server confirms user consent (which might or might not
require user interaction...)
authorization server confirms redirect_uri is associated with the client_id.
authorization server redirects to redirect_uri, with all of the return
parameters on the URL fragment.

Here's my proposed text for section 1.4.2.

The user-agent profile is suitable for client applications residing in
a user-agent, typically implemented in a browser using a scripting
language such as JavaScript.  These clients typically have strict
latency requirements, and must be implemented using a minimum number
of client-to-server calls.  The client may have supporting code
running on a web server.  Authentication of the client-side components
is based on the redirect URI and the user-agent's same-origin policy.
Authentication of the server-side components is based on the client
secret.

Unlike other profiles, the user-agent profile returns data on the
client on the URI fragment.  In addition, the user-agent profile
returns an access token directly on the redirect URI, to reduce the
number of round trips required before the client can use the token.
The client is able to immediately use the token to communicate to
resource servers, using client-side cross-domain communication
techniques such as window.postMessage, jsonp, and iframes.  [Maybe
just mention iframes here, better not to clutter the spec with
references to all of the ways around same-origin...)

Figure 5: this probably needs to change so that it looks more like
Figure 4.  Changes from Figure 4 would be:

Step (C) returns access token and optional verification code

New step (D): client using the access token directly with a resource server.

Old step D becomes step E, but otherwise looks the same.  Except it is optional.

Step E: if this step occurs, refresh token is no longer optional


More normative language (largely based on merging the user-agent
language with the web-server language.)

The user-agent flow illustrated in Figure 5 includes the following steps:

(A) [no change] The clients sends the user-agent to the end-user
authorization endpoint as described in section 3.  The client includes
its client identifier, requested scope, local state, and a redirect
URI to which the authorization server will send the end-user back once
authorization is granted.

(B) [no change]

(C) If the end-user granted access, the authorization server redirects
the user-agent to the redirection URI provided earlier.  The
redirection URI includes the access token, and optional verification
code, in the URI fragment.

(D) The user-agent follows the redirection instructions by loading the
redirection URI.  The redirection URI is typically a heavily cached
web page, so this is unlikely to result in an additional client to
server round trip.

(E) The client code parses the URL fragment to retrieve the access
token and optional authorization code.

(F) The client uses the access token to contact resource servers.

(G) In parallel, the client passes the authorization code to the
client's server side components.

(H) The client's server-side components request an access token and
refresh token from the authorization server by authenticating and
including the authorization code received in the previous step.

(I) The authorization server validates the client credentials and the
authorization code and responds back with the refresh token and access
token.


Rest of the spec doesn't look like it needs to change much.

Section 4.1.1.  "Authorization Code" needs some modifications to make
it clear that the redirect_uri sent to the authorization server MUST
include any fragment included on the original authorization request.

redirect_uri
         REQUIRED.  The redirection URI used in the initial request.
If the client included a URI fragment on the redirect_uri sent to the
authorization server on the authorization request, the client MUST
include the same URI fragment here.


Cheers,
Brian