Podcast interview: Is OAuth 2 the Devil?

Alex Bilbie and Zackary Blank come on the show to talk about OAuth 2, which has been getting a lot of flamey bad press over the last year or two after the original author quit the project.

Why these guys? Well, Alex until recently was working at the University of Lincoln where they did a whole bunch of OAuth 2 work as auth for various API projects at the university. During this time he created the fully spec compliant CodeIgniter OAuth 2.0 Server, which was later replaced with a new generic PHP version for “The League of Extraordinary Packages”. It’s safe to say that Alex knows his shit when it comes to OAuth.

Read more and listed to the podcast

A guide to OAuth grants

OAuth by it’s nature is a very flexible standard and can adapted to work in many different scenarios. The core specification describes four authorisation grants:

  • Authorisation code grant
  • Implicit grant
  • Resource owner credentials grant
  • Client credentials grant

The specification also details another grant called the refresh token grant.

Furthermore there are a number of other grants that have gone through the IETF ratification process (none of which at the time of writing have been formally standardised):

  • Message authentication code (MAC) tokens
  • SAML 2.0 Bearer Assertion Profiles
  • JSON web token grant

The end goal of each of these grants (except the refresh token grant) is for the client application to have an access token (which represents a user’s permission for the client to access their data) which it can use to authenticate a request to an API endpoint.

In this post I’m going to describe each of the above grants and their appropriate use cases.

As a refresher here is a quick glossary of OAuth terms (taken from the core spec):

  • Resource owner (a.k.a. the User) – An entity capable of granting access to a protected resource. When the resource owner is a person, it is referred to as an end-user.
  • Resource server (a.k.a. the API server) – The server hosting the protected resources, capable of accepting and responding to protected resource requests using access tokens.
  • Client – An application making protected resource requests on behalf of the resource owner and with its authorisation. The term client does not imply any particular implementation characteristics (e.g. whether the application executes on a server, a desktop, or other devices).
  • Authorisation server – The server issuing access tokens to the client after successfully authenticating the resource owner and obtaining authorisation.

Authorisation code grant (section 4.1)

The authorisation code grant is the grant that most people think of when OAuth is described.

If you’ve ever signed into a website or application with your Twitter/Facebook/Google/(insert major Internet company here) account then you’ll have experienced using this grant.

Essentially a user will click on a “sign in with Facebook” (or other IdP) and then be redirected from the application/website (the “client”) to the IdP authorisation server. The user will then sign in to the IdP with their credentials, and then – if they haven’t already – authorise the client to allow it to use the user’s data (such as their name, email address, etc). If they authorise the request the user will be redirected back to the client with a token (called the authorisation code) in the query string (e.g. http://client.com/redirect?code=XYZ123) which the client will capture and exchange for an access token in the background.

This grant is suitable where the resource owner is a user and they are using a client which is allows a user to interact with a website in a browser. An obvious example is the client being another website, but desktop applications such as Spotify or Reeder use embedded browsers.

Some mobile applications use this flow and again use an embedded browser (or redirect the user to the native browser and then are redirected back to the app using a custom protocol).

In this grant the access token is kept private from the resource owner.

If you have a mobile application that is for your own service (such as the official Spotify or Facebook apps on iOS) it isn’t appropriate to use this grant as the app itself should already be trusted by your authorisation server and so the _resource owner credentials grant would be more appropriate.

Implicit grant (section 4.2)

The implicit grant is similar to the authentication code grant described above. The user will be redirected in a browser to the IdP authorisation server, sign in, authorise the request but instead of being returned to the client with an authentication code they are redirected with an access token straight away.

The purpose of the implicit grant is for use by clients which are not capable of keeping the client’s own credentials secret; for example a JavaScript only application.

If you decide to implement this grant then you must be aware that the access token should be treated as “public knowledge” (like a public RSA key) and therefore it must have a very limited permissions when interacting with the API server. For example an access token that was granted using the authentication code grant could have permission to be used to delete resources owned by the user, however an access token granted through the implicit flow should only be able to “read” resources and never perform any destructive operations.

Resource owner credentials grant (section 4.3)

When this grant is implemented the client itself will ask the user for their username and password (as opposed to being redirected to an IdP authorisation server to authenticate) and then send these to the authorisation server along with the client’s own credentials. If the authentication is successful then the client will be issued with an access token.

This grant is suitable for trusted clients such as a service’s own mobile client (for example Spotify’s iOS app). You could also use this in software where it’s not easy to implement the authorisation code – for example we bolted this authorisation grant into OwnCloud so we could retrieve details about a user that we couldn’t access over LDAP from the university’s Active Directory server.

Client credentials grant (section 4.4)

This grant is similar to the resource owner credentials grant except only the client’s credentials are used to authenticate a request for an access token. Again this grant should only be allowed to be used by trusted clients.

This grant is suitable for machine-to-machine authentication, for example for use in a cron job which is performing maintenance tasks over an API. Another example would be a client making requests to an API that don’t require user’s permission.

When someone visits a member of staff’s page on the University of Lincoln staff directory the website uses it’s own access token (that was generated using this grant) to authenticate a request to the API server to get the data about the member of staff that is used to build the page. When a member of staff signs in to update their profile however their own access token is used to retrieve and update their data. Therefore there is a good separation of concerns and we can easily restrict permissions that each type of access token has.

Refresh token grant (section 1.5)

The OAuth 2.0 specification details a fifth grant which can be used to “refresh” (i.e. renew) an access token which has expired.

Authorisation servers which support this grant will also issue a “refresh token” when it returns an access token to a client. When the access token expires instead of sending the user back through the authorisation code grant the client can use to the refresh token to retrieve a new access token with the same permissions as the old one.

My problem with the grant is that it means the client has to maintain state of each token and then either on a cron job keep access tokens up to date or when it tries to make a request and it fails then go and update the access token and repeat the request.

I personally prefer to issue access tokens that last longer than the user’s session cookie so that when they next sign in they’ll be issued a new token anyway.


The extension grants

The following grants are currently going through the standardisation process.

N.B. My explanation from here on is how ** I ** (as a developer) am interpreting the specifications – all three of the following grants specs need further work in my opinion to better explain their purpose and how they compare to the core grants.

MAC token grant (draft 3 at time of writing)

The MAC token grant can be used alongside another grant (the specification describes using it alongside the authentication code grant) to further improve the security of requests to the API server by including the addition of a MAC (message authentication code) signature along with the access token in order to both authenticate the request and prove the identity of the client making the request (because it prevents tampering and forgery). In some ways it is similar to the OAuth 1.0 request process.

When the authorisation server returns an access token to a client it also includes a key which the client uses to generate the MAC signature. The MAC signature is generated by combining the parameters of the request and then hashing them against the key.

This grant could be used when additional security measures are needed to ensure that the client querying an API is definitely who it is identifying itself as. It can also prevent access tokens being used by unauthorised clients (if an access token has been compromised by a man in the middle attack) because a valid signature (which can only be generated by the client which knows the MAC key associated with the access token) is required for each request.

SAML 2.0 Bearer Assertion Profiles (draft 15 at time of writing)

A SAML assertion is an XML payload which contains a security token. They are issued by identity providers and consumed by a service provider who relies on its content to identify the assertion’s subject for security-related purposes. It is quite literally an assertion as to who someone (or what something) is.

This grant allows a client to exchange an existing SAML assertion for an access token, meaning the user doesn’t have to authenticate again with an authorisation server. In some ways it resembles the concept of the refresh token grant.

An example use case of this grant would be a publishers website receiving an assertion from the UK Federation (after a user from a UK education institution has authenticated with their institution’s own resource server), converting the assertion into an access token and then querying the users institution’s API server to request additional details about the user.

JSON web token grant (draft 6 at time of writing)

The JSON web token grant is similar to the SAML assertion grant but uses JSON as the serialisation format instead of XML. This means that a JWT (JSON web token) can comfortable fit inside a HTTP authorization header.

A potential use case could be a web based client application that also has a mobile version, the web version could somehow share the existing access token with the mobile application which in turn requests its own access token. Possibly? Maybe? I’ll admit I can’t think of a better example.

Securing your API with OAuth 2.0

In a previous post I announced my new OAuth 2.0 PHP libraries.

In this post I will show you how to use the server library to secure a simple API with OAuth 2.0.


Install the library

The recommended way of installing the library is via Composer.

If you already have a composer.json file in your root then add ”lncd/oauth2”: “*” in the require object. Then run composer update.

Otherwise create a new file in your project root called composer.json add set the contents to:

{
    "require": {
        "lncd/OAuth2": "*"
    }
}

Now, assuming you have installed Composer run composer install.

Ensure now that you’ve set up your project to autoload composer packages.

You could alternatively add the library as a git submodule or download a zip.

Set up the database

To setup the database just import sql/mysql.sql

Create the storage models

In order to retrieve data from the database you should create classes which implement the following interfaces:

  • \OAuth2\Storage\ScopeInterface
  • \OAuth2\Storage\SessionInterface

Hooking it all up

Setting up the library is simple, just create a new instance of \OAuth2\ResourceServer and pass in your storage models.

// Include the storage models
include 'model_scope.php';
include 'model_session.php';

// Initiate the Request handler
$request = new \OAuth2\Util\Request();

// Initiate the auth server with the models
$server = new \OAuth2\ResourceServer(new SessionModel, new ScopeModel);

Checking for valid access tokens

Before your API responds you need to check that an access token has been presented with the request (either in the query string ?access_token=abcdef or as an authorization header Authorization: bearer abcdef).

If you’re using a framework such as Laravel or CodeIgniter you could use a route filter to do this, or have a custom controller which other controllers extend from. In this example I’m using the Slim framework and I’m going to create a simple route middleware which is run before each endpoint function.

$checkToken = function () use ($server) {

    return function() use ($server)
    {
        // Test for token existance and validity
        try {
            $server->isValid();
        }

        // The access token is missing or invalid...
        catch (\OAuth2\Exception\InvalidAccessTokenException $e)
        {
            $app = \Slim\Slim::getInstance();
            $res = $app->response();
            $res['Content-Type'] = 'application/json';
            $res->status(403);

            $res->body(json_encode(array(
                'error' =>  $e->getMessage()
            )));
        }
    };

};

When $server->isValid() is called the library will run the following tasks:

  • Check if an access token is present in the query string
    • If not, check if a base64 encoded access token is contained in an authorization header.
      • If not, throw \OAuth2\Exception\InvalidAccessTokenException
  • Check if the access token is valid with the database
    • If not, throw \OAuth2\Exception\InvalidAccessTokenException
  • If the access token is valid:
    • Get the owner type (e.g. “user” or “client”) and their ID
    • Get a list of any scopes that are associated with the access token

Assuming an exception isn’t thrown you can then use the following functions in your API code:

  • getOwnerType() – This will return the type of the owner of the access token. For example if a user has authorized another client to use their resources the owner type would be “user”.
  • getOwnerId() – This will return the ID of the access token owner. You can use this to check if the owner has permission to do take some sort of action (such as retrieve a document or upload a file to a folder).
  • hasScope() – You can use this function to see if a specific scope (or several scopes) has been associated with the access token. You can use this to limit the contents of an API response or prevent access to an API endpoint without the correct scope.

A simple example

This example endpoint will return a user’s information if a valid access token is present. If the access token has the user.contact it will return additional information.

$app->get('/user/:id', $checkToken(), function ($id) use ($server, $app) {

    $user_model = new UserModel();

    $user = $user_model->getUser($id);

    if ( ! $user)
    {
        $res = $app->response();
        $res->status(404);
        $res['Content-Type'] = 'application/json';
        $res->body(json_encode(array(
            'error' => 'User not found'
        )));
    }

    else
    {
        // Basic response
        $response = array(
            'error' => null,
            'result'    =>  array(
                'user_id'   =>  $user['id'],
                'firstname' =>  $user['firstname'],
                'lastname'  =>  $user['lastname']
            )
        );

        // If the acess token has the "user.contact" access token include
        //  an email address and phone numner
        if ($server->hasScope('user.contact'))
        {
            $response['result']['email'] = $user['email'];
            $response['result']['phone'] = $user['phone'];
        }

        // Respond
        $res = $app->response();
        $res['Content-Type'] = 'application/json';

        $res->body(json_encode($response));
    }

});

Limiting an endpoint to a specific owner type

In this example, only a user’s access token is valid:

$app->get('/user', $checkToken(), function () use ($server, $app) {

    $user_model = new UserModel();

    // Check the access token's owner is a user
    if ($server->getOwnerType() === 'user')
    {
        // Get the access token owner's ID
        $userId = $server->getOwnerId();

        $user = $user_model->getUser($userId);

        // If the user can't be found return 404
        if ( ! $user)
        {
            $res = $app->response();
            $res->status(404);
            $res['Content-Type'] = 'application/json';
            $res->body(json_encode(array(
                'error' => 'Resource owner not found'
            )));
        }

        // A user has been found
        else
        {
            // Basic response
            $response = array(
                'error' => null,
                'result'    =>  array(
                    'user_id'   =>  $user['id'],
                    'firstname' =>  $user['firstname'],
                    'lastname'  =>  $user['lastname']
                )
            );

            // If the acess token has the "user.contact" access token include
            //  an email address and phone numner
            if ($server->hasScope('user.contact'))
            {
                $response['result']['email'] = $user['email'];
                $response['result']['phone'] = $user['phone'];
            }

            // Respond
            $res = $app->response();
            $res['Content-Type'] = 'application/json';

            $res->body(json_encode($response));
        }
    }

    // The access token isn't owned by a user
    else
    {
        $res = $app->response();
        $res->status(403);
        $res['Content-Type'] = 'application/json';
        $res->body(json_encode(array(
            'error' => 'Only access tokens representing users can use this endpoint'
        )));
    }

});

You might use an API function like this to allow a client to discover who a user is after they’ve signed into your authorization endpoint (see an example of how to do this here).

Limiting an endpoint to a specific owner type and scope

In this example, the endpoint will only respond to access tokens that are owner by client applications and that have the scope users.list.

$app->get('/users', $checkToken(), function () use ($server, $app) {

    $user_model = new UserModel();

    $users = $user_model->getUsers();

    // Check the access token owner is a client
    if ($server->getOwnerType() === 'client' && $server->hasScope('users.list'))
    {
        $response = array(
            'error' => null,
            'results'   =>  array()
        );

        $i = 0;
        foreach ($users as $k => $v)
        {
            // Basic details
            $response['results'][$i]['user_id'] = $v['id'];
            $response['results'][$i]['firstname'] = $v['firstname'];
            $response['results'][$i]['lastname'] = $v['lastname'];

            // Include additional details with the right scope
            if ($server->hasScope('user.contact'))
            {
                $response['results'][$i]['email'] = $v['email'];
                $response['results'][$i]['phone'] = $v['phone'];
            }

            $i++;
        }

        $res = $app->response();
        $res['Content-Type'] = 'application/json';

        $res->body(json_encode($response));
    }

    // Access token owner isn't a client or doesn't have the correct scope
    else
    {
        $res = $app->response();
        $res->status(403);
        $res['Content-Type'] = 'application/json';
        $res->body(json_encode(array(
            'error' => 'Only access tokens representing clients can use this endpoint'
        )));
    }

});

You might secure an endpoint in this way to only allow specific clients (such as your applications’ main website) access to private APIs.


Hopefully you will see how easy it is to secure an API with OAuth 2.0 and how you can use scopes to limit response contents or access to endpoints.

You can download a complete working example here – https://github.com/lncd/oauth2-example-resource-server.

Developing an OAuth 2.0 authentication server

This guide will show you how to setup an OAuth 2.0 authentication server which supports the authorization code grant type.

Install the library

The recommended way of installing the library is via Composer.

If you already have a composer.json file in your root then add ”lncd/oauth2”: “*” in the require object. Then run composer update.

Otherwise create a new file in your project root called composer.json add set the contents to:

{
    "require": {
        "lncd\OAuth2": "*"
    }
}

Now, assuming you have installed Composer run composer install.

Ensure now that you’ve set up your project to autoload composer packages.


You could alternatively add the library as a git submodule or download a zip.

Set up the database

To setup the database just import sql/mysql.sql

Create your first client

In OAuth terms a client is an application (it could be a website or a mobile app) that communicates with your API.

Insert a client into the oauth_clients table. It is recommended that you make the id and secret fields random alphanumeric strings – http://randomkeygen.com/ is a useful for this. The auto_approve parameter should be to 1 if you want the user to automatically approve access to the client, otherwise set it to 0.

If you want to use the authorization grant (where a user is redirected to the auth server from the client and the back in order to “sign-in” or “connect” with the client) then in the oauth_client_endpoints add a redirect URI (where the user is redirected back to after signing in). You can add multiple redirect URIs for production and development.

Create the storage models

In order to persist data to the database you should create classes which implement the following three interfaces:

  • \OAuth2\Storage\ClientInterface
  • \OAuth2\Storage\ScopeInterface
  • \OAuth2\Storage\SessionInterface

The authorization code grant

For reference here is an overview of how the authorization code grant works:

+--------+                               +---------------+
|        |--(A)- authorisation Request ->|   Resource    |
|        |                               |     Owner     |
|        |< -(B)-- authorisation Grant ---|               |
|        |                               +---------------+
|        |
|        |                               +---------------+
|        |--(C)-- authorisation Grant -->| authorisation |
| Client |                               |     Server    |
|        |< -(D)----- Access Token -------|               |
|        |                               +---------------+
|        |
|        |                               +---------------+
|        |--(E)----- Access Token ------>|    Resource   |
|        |                               |     Server    |
|        |< -(F)--- Protected Resource ---|               |
+--------+                               +---------------+

(A) The client requests authorisation from the resource owner. The authorisation request can be made directly to the resource owner (as shown), or preferably indirectly via the authorisation server as an intermediary.

(B) The client receives an authorisation grant, which is a credential representing the resource owner’s authorisation, expressed using one of four grant types defined in this specification or using an extension grant type. The authorisation grant type depends on the method used by the client to request authorisation and the types supported by the authorisation server.

(C) The client requests an access token by authenticating with the authorisation server and presenting the authorisation grant.

(D) The authorisation server authenticates the client and validates the authorisation grant, and if valid issues an access token.

(E) The client requests the protected resource from the resource server and authenticates by presenting the access token.

(F) The resource server validates the access token, and if valid, serves the request.

Create an oauth controller

NOTE: This is assuming you’re using a framework that follows an MVC pattern, If you’re using individual files for each page then you create a new page for each controller route listed henceforth.

In your controller constuctor you should instantiate the auth server:

public function __construct()
{
    // Initiate the request handler which deals with $_GET, $_POST, etc
    $request = new \OAuth2\Util\Request();

    // Create the auth server, the three parameters passed are references to the storage models
    $this->authserver = new \OAuth2\AuthServer(new ClientModel, new SessionModel, new ScopeModel);

    // Enable the authorization code grant type
    $this->authserver->addGrantType(new \OAuth2\Grant\AuthCode());

    // Set the TTL of an access token in seconds (default to 3600s / 1 hour)
    $this->authserver->setExpiresIn(86400);
}

Create your first route (for example “index” – which would resolve to /oauth).

public function action_index()
{
    try {

        // Tell the auth server to check the required parameters are in the query string
        $params = $this->authserver->checkAuthoriseParams();

        // Save the verified parameters to the user's session
        Session::put('client_id', $params['client_id']);
        Session::put('client_details', $params['client_details']);
        Session::put('redirect_uri', $params['redirect_uri']);
        Session::put('response_type', $params['response_type']);
        Session::put('scopes', $params['scopes']);

        // Redirect the user to the sign-in route
        return Redirect::to(‘oauth/signin');

    } catch (Oauth2\Exception\ClientException $e) {

        // Throw an error here which says what the problem is with the auth params

    } catch (Exception $e) {

        // Throw an error here which has caught a non-library specific error

    }
}

Next create a sign-in route:

public function action_signin()
{
    // Retrieve the auth params from the user's session
    $params['client_id'] = Session::get('client_id');
    $params['client_details'] = Session::get('client_details');
    $params['redirect_uri'] = Session::get('redirect_uri');
    $params['response_type'] = Session::get('response_type');
    $params['scopes'] = Session::get('scopes');

    // Check that the auth params are all present
    foreach ($params as $key=>$value) {
        if ($value === null) {
            // Throw an error because an auth param is missing - don't continue any further
        }
    }

    // Process the sign-in form submission
    if (Input::get('signin') !== null) {
        try {

            // Get username
            $u = Input::get('username');
            if ($u === null || trim($u) === '') {
                throw new Exception('please enter your username.');
            }

            // Get password
            $p = Input::get('password');
            if ($p === null || trim($p) === '') {
                throw new Exception('please enter your password.');
            }

            // Verify the user's username and password
            // Set the user's ID to a session

        } catch (Exception $e) {
            $params['error_message'] = $e->getMessage();
        }
    }

    // Get the user's ID from their session
    $params['user_id'] = Session::get('user_id');

    // User is signed in
    if ($params['user_id'] !== null) {

        // Redirect the user to /oauth/authorise route
        return Redirect::to('oauth/authorise');

    }

    // User is not signed in, show the sign-in form
    else {
        return View::make('oauth.signin', $params);
    }
}

In the sign-in form HTML page you should tell the user the name of the client that their signing into.

Once the user has signed in (if they didn’t already have an existing session) then they should be redirected the authorise route where the user explicitly gives permission for the client to act on their behalf.

public function action_authorise()
{
    // Retrieve the auth params from the user's session
    $params['client_id'] = Session::get('client_id');
    $params['client_details'] = Session::get('client_details');
    $params['redirect_uri'] = Session::get('redirect_uri');
    $params['response_type'] = Session::get('response_type');
    $params['scopes'] = Session::get('scopes');

    // Check that the auth params are all present
    foreach ($params as $key=>$value) {
        if ($value === null) {
            // Throw an error because an auth param is missing - don't continue any further
        }
    }

    // Get the user ID
    $params['user_id'] = Session::get('user_id');

    // User is not signed in so redirect them to the sign-in route (/oauth/signin)
    if ($params['user_id'] === null) {
        return Redirect::to('signin');
    }

    // Check if the client should be automatically approved
    $autoApprove = ($params['client_details']['auto_approve'] === '1') ? true : false;

    // Process the authorise request if the user's has clicked 'approve' or the client
    if (Input::get('approve') !== null || $autoApprove === true) {

        // Generate an authorization code
        $code = $this->authserver->newAuthoriseRequest('user', $params['user_id'], $params);

        // Redirect the user back to the client with an authorization code
        return Redirect::to(\OAuth2\Util\RedirectUri::make($params['redirect_uri'], array(
            'code'  =>  $code,
            'state' =>  isset($params['state']) ? $params['state'] : ''
        )));
    }

    // If the user has denied the client so redirect them back without an authorization code
    if (Input::get('deny') !== null) {
        return Redirect::to(\OAuth2\Util\RedirectUri::make($params['redirect_uri'], array(
            'error' =>  $this->authserver->exceptionCodes[2],
            'error_message' =>  $this->authserver->errors[$this->authserver->exceptionCodes[2]],
            'state' =>  isset($params['state']) ? $params['state'] : ''
        )));
    }

    // The client shouldn't automatically be approved and the user hasn't yet approved it so show them a form
    return View::make('oauth.authorise', $params);
}

In the authorize form the user should again be told the name of the client and also list all the scopes (permissions) it is requesting.

The final route to create is where the client exchanges the authorization code for an access token.

public function action_access_token()
{
    try {

        // Tell the auth server to issue an access token
        $response = $this->authserver->issueAccessToken();

    } catch (\Oauth2\Exception\ClientException $e) {

        // Throw an exception because there was a problem with a the client's request
        $response = array(
            'error' =>  $this->authserver::getExceptionType($e->getCode()),
            'error_description' => $e->getMessage()
        );

    } catch (Exception $e) {

        // Throw an error when a non-library specific exception has been thrown
        $response = array(
            'error' =>  'undefined_error',
            'error_description' => $e->getMessage()
        );
    }

    header('Content-type: application/json');
    echo json_encode($response);
}

A complete example of an authorisation server can be found at https://github.com/lncd/oauth2-example-auth-server.

Life, Identity and Everything – a chat with Tim Bray and Breno de Madeiros from Google

Last night I took part in the Life, Identity, and Everything Google developer chat with Tim Bray and Breno de Madeiros.

Tim Bray is the Developer Advocate, and Breno de Madeiros is the tech lead, in the group at Google that does authentication and authorization APIs; specifically, those involving OAuth and OpenID. Breno also has his name on the front of a few of the OAuth RFCs. We’re going to talk for a VERY few (less than 10) minutes on why OAuth is a good idea, and a couple of things we’re working on right now to help do away with passwords. After that, ask us anything.

Here is the video of the chat:

To summarise the event they said that Google are committed to OAuth 2.0 and OpenID. They intend on all Google APIs supporting OAuth 2.0 and they believe it has helped developers develop for Android because support for the OAuth dance is baked into the Android APIs making it very simple to integrate with it.

They also feel that Mozilla Persona is a promising (but complicated) protocol; however they feel that on mobile devices it will cause problems in terms of the user flow.

I asked two questions which Tim and Breno answered:

”What do you think the future of OAuth is? Eran Hammer publicly quit as the editor and claiming the v2.0 specification is “more complex, less interoperable, less useful, more incomplete, and most importantly, less secure”. Will there be OAuth 3?”

and

“Could a ./well-known/oauth file published at each providers endpoint help with interoperability? This file would describe their implementation responds with json or text, which grants are supported, etc”