OAuth2 for Browser Clients

by Michael Krotscheck

Dunning-Kruger Effect

... a cognitive bias wherein people of low ability suffer from illusory superiority, mistakenly assessing their cognitive ability as greater than it is. -- Wikipedia

Introductions!

Speaker:
Michael Krotscheck
VMware
krotscheck
https://github.com/krotscheck
https://www.krotscheck.net
Source:
https://github.com/krotscheck/presentations

Career OAuth2 Implementations

6

Correct Implementations

0

TL/DR

A resource owner asks the authorization server to authorize a client to access a protected resource server.

A user grants their app permission
to access data on a server.

Caveat Emptor

OAuth2 is an Authorization framework,
not an Authentication framework.

Tokens

1: How to get a token

2: How to use a token

What is a token?


HTTP/1.1 200 OK
Content-Type: application/json
Date: Tue, 5 Dec 2017 06:25:29 GMT

{
    "token_type": "Bearer",
    "access_token": "4c451ff3edf854b73da1bc624641fd54",
    "expires_in": 600,
    "refresh_token": "66fe4faef3fe1bf7ab9f1f7d4ca20bca",
    "scope": "email public_profile"
}
          

Using a token


const token = {
    "token_type": "Bearer",
    "access_token": "4c451ff3edf854b73da1bc624641fd54",
    "expires_in": 600,
    "refresh_token": "66fe4faef3fe1bf7ab9f1f7d4ca20bca",
    "scope": "email public_profile"
};

const xhr = new XMLHttpRequest();
xhr.setRequestHeader('Authorization', 'Bearer ' + token.access_token);
xhr.open('GET', 'https://example.com/resource', true);
xhr.send(null);

GET /resource
Host: example.com
Accept: application/json
Authorization: Bearer 4c451ff3edf854b73da1bc624641fd54

Anatomy of an OAuth2 Server

API endpoints

  • https://oauth2.example.com/authorize
  • https://oauth2.example.com/token

Clients

Public vs. Private

Error Format


HTTP/1.1 400 Bad Request
Content-Type: application/json
Date: Tue, 5 Dec 2017 06:25:29 GMT

{
    "error": "short_error_code",
    "error_description": "Something Human Readable"
}
          

TLS

It's required.

Scopes

  • user:email
  • user:public_profile
  • https://www.googleapis.com/auth/userinfo.email

Registration

  • redirect_uri: https://app.example.com/redirect
  • client_id: 505d7ad2f35a55aabc72471423b729d5
  • client_secret: 3b9da6fd0822b0efb02fc71bad27fdd5

Use Case: Social Login


Server application

Cookie-based sessions

Authorization Code (Section 4.1)

  • Client is trustworthy
  • Trust established via shared secret
  • Server makes all API calls
  • Tokens are held only by the server
  • Supports token refresh

Step 1: Link to Authorization


<a href="https://accounts.google.com/o/oauth2/v2/auth?....">Login</a>

https://accounts.google.com/o/oauth2/v2/auth
        ?response_type=code
        &client_id=505d7ad2f35a55aabc72471423b729d5
        &scope=https://www.googleapis.com/auth/userinfo.email%20https://www.googleapis.com/auth/userinfo.profile
        &state=5df23932458041acb94f95eeeffa49b8
        &redirect_uri=https://server.example.com/redirect

Step 2: Receive Authorization Code


https://server.example.com/redirect
        ?code=2997bc5ae3a0a2cbd6d00dc78f7a0d98
        &state=5df23932458041acb94f95eeeffa49b8

Step 3: Exchange Code for Token


POST https://www.googleapis.com/oauth2/v4/token
Authorization: Basic NTA1ZDdhZDJmMzVhNTVhYWJjNzI0NzE0MjNiNzI5ZDU6M2I5ZGE2ZmQwODIyYjBlZmIwMmZjNzFiYWQyN2ZkZDU=
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
      &code=2997bc5ae3a0a2cbd6d00dc78f7a0d98
      &redirect_uri=http%3A%2F%server.example.com%2Fredirect
      &state=2987fd6f218cf23c6128ab695cbcc824

HTTP/1.1 200 OK
Content-Type: application/json
Date: Tue, 5 Dec 2017 06:25:29 GMT

{
    "token_type": "Bearer",
    "access_token": "4c451ff3edf854b73da1bc624641fd54",
    "expires_in": 600,
    "refresh_token": "66fe4faef3fe1bf7ab9f1f7d4ca20bca",
    "scope": "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile"
}
          

Questions?

...on Authorization Codes?

Use Case: Browser Application


Browser-only

No server sessions

Implicit Code Grant (Section 4.2)

  • Client is not trustworthy
  • Client makes all API calls
  • No token refresh

Step 1: Link to Authorization


<a href="https://accounts.google.com/o/oauth2/v2/auth?....">Login</a>

https://accounts.google.com/o/oauth2/v2/auth
        ?response_type=token
        &client_id=505d7ad2f35a55aabc72471423b729d5
        &scope=https://www.googleapis.com/auth/userinfo.email%20https://www.googleapis.com/auth/userinfo.profile
        &state=5df23932458041acb94f95eeeffa49b8
        &redirect_uri=https://app.example.com/redirect

Step 2: Get a token


HTTP/1.1 302
Location: https://app.example.com/redirect
          #access_token=7c53393ba731f6c07452f68f22af2043
          &token_type=Bearer
          &expires_in=600
          &state=015e6183679ba49505d6fd6bbb3006c6
          &scope=https://www.googleapis.com/auth/userinfo.email%20https://www.googleapis.com/auth/userinfo.profile

Gotchas

  • IE URL limit (2,048)
  • No Refresh
  • Redirect-based flow
  • OAuth2 Predates CORS

Questions?

...on Implicit Flow?

Use Case: Direct Login


Server application

Cookie-based sessions

Resource Owner (Section 4.3)

  • Client may be trustworthy
  • Tokens are held only by the client
  • Supports token refresh

Submit Username & Password


POST https://oauth2.example.com/token
Authorization: Basic NTA1ZDdhZDJmMzVhNTVhYWJjNzI0NzE0MjNiNzI5ZDU6M2I5ZGE2ZmQwODIyYjBlZmIwMmZjNzFiYWQyN2ZkZDU=
Content-Type: application/x-www-form-urlencoded

grant_type=password
          &username=valid_user
          &password=valid_password
          &scope=email public_profile

HTTP/1.1 200 OK
Content-Type: application/json
Date: Tue, 5 Dec 2017 06:25:29 GMT

{
    "token_type": "Bearer",
    "access_token": "4c451ff3edf854b73da1bc624641fd54",
    "expires_in": 600,
    "refresh_token": "66fe4faef3fe1bf7ab9f1f7d4ca20bca",
    "scope": "email public_profile"
}

Questions?

...on Resource Owners?

Use Case: Server to Server

Client Credentials (Section 4.4)

  • The actor is the client itself
  • Client is trustworthy
  • Tokens are held only by the client
  • Does not support token refresh

Request and Receive token


POST https://oauth2.example.com/token
Authorization: Basic MjNkYjUyZDk1Njc2NGIwMTY1Yjc5Nzc2YTM0NjA1NjY6MTk2MjMwNjhhZDM0NzQ1Y2Q2MGY3MDMzNDljNzNhZWI=
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
          &scope=email public_profile

HTTP/1.1 200
Content-Type: application/json

{
        "access_token":"65cc4cacbb8dba2418161183836b98c5",
        "token_type":"Bearer",
        "expires_in":600,
        "scope":"email public_profile"
}

Questions?

...on Client Credentials?

Refreshing a token (Section 6)

Authorization and Owner Credentials flow only.

POST https://oauth2.example.com/token
Authorization: Basic NTA1ZDdhZDJmMzVhNTVhYWJjNzI0NzE0MjNiNzI5ZDU6M2I5ZGE2ZmQwODIyYjBlZmIwMmZjNzFiYWQyN2ZkZDU=

grant_type=refresh_token
        &refresh_token=754636d20cf3ad85434cd3230ad2e048


HTTP/1.1 200
Content-Type: application/json

{
    "token_type": "Bearer",
    "access_token": "20b8b4b9372483ce453495541e4359fe",
    "expires_in": 600,
    "refresh_token": "460e5aa311f1dfb3bf994a121cb8c2f8",
    "scope": "email public_profile"
}

Extension Grants (Section 4.5)

Questions?

Speaker:
Michael Krotscheck
VMware
krotscheck
https://github.com/krotscheck
https://www.krotscheck.net
Source:
https://github.com/krotscheck/presentations
License:
Creative Commons Attribution 4.0 International