Go!

Mission

"Write an OAuth 2.0
client library for
Société Géniale"

Constraints

  • Security: the access_token has a lifetime of only 8 minutes (need token renewal)
     
  • Stability: the network fails sometimes (renewal must be resilient)
     
  • Frameworks: AngularJS, Angular, Vue, also React and Vanilla JS
     
  • Browsers: Chrome, FF, IE11, Edge
     
  • UX: limit the number of visible redirects

WTF is OAuth?

I read on the Internetz that it's some kind of a protocol for authorization.

OAuth is amazing!

You can use it to give people access to your data without giving them the password to that data!

Who invented it?

In 2005 Blaine Cook and Chris Messina were unhappy with OpenID, so they started to work on Yadis (yet-another-distributed-identity-system)

Blaine Cook

Chris Messina

Cool! Anybody else?

Eran Hammer worked on the OAuth 2.0 specification before he suddenly quit for many reasons.

Eran Hammer

Wait a second...

WTF is OpenID?

I read on the Internetz that it's some kind of a protocol for authentication.

WTF is the difference?

OAuth vs. OpenID

OpenID is an authentication protocol.

OAuth is the authorization protocol.

 

That's pretty simple, huh!

It's actually not that simple, but do you even care?

How does it work?

Implicit grant

Problem #1

How to formulate the authorization request

response_type ask from user
client_id ask from user
redirect_uri ask from user
scope ask from user
state generate
nonce* generate
prompt* depends on request

Problem #1.1

How to generate
crypto-strong random values

const crypto = window.crypto || window.msCrypto;
const array = new Uint32Array(5);
const randomValues = crypto.getRandomValues(array);

let base36RandomValue = '';
for (let i = 0; i < randomValues.length; i += 1) {
  base36RandomValue += randomValues[i].toString(36);
}
return base36RandomValue;

Problem #2

How to send the authorization request
and retrieve the response

Option Decision Reason
XMLHttpRequest NO unable to catch 302
FetchAPI NO unable to catch 302
iframe Not really styling and UX issues
pop-up Maybe UX and IE11 bug
hard redirect YES the only acceptable option

Problem #3

Where to store the access_token

Storage Decision Reason
session storage NO new tabs lose access
to the token
cookies Maybe CSRF possibilities
local storage YES XSS-vulnerable, but risk is acceptable
websql NO no universal support

Problem #4

How to silently renew the access_token

Option Decision Reason
XMLHttpRequest NO unable to catch 302
FetchAPI NO unable to catch 302
iframe YES the only acceptable option
pop-up NO UX and IE11 bug
hard redirect NO hard redirect is visible

iframe?

seriously?

Problem #5

How to write the API

connect.signIn();

People love convenience. They all specify their app's homepage as the redirect_uri which makes the library more complex.

myapp.com

auth endpoint

connect.signIn();

myapp.com

redirect to

redirect to

execute script

execute script

auth endpoint

redirect to

Problem #6

How to send the Authorization header

  1. in Vanilla JS

  2. in AngularJS

  3. in Angular

Problem #6.1

How to send the Authorization header
in Vanilla JS

import {connect} from './connect';

fetch('https://api/users', {
  method: 'GET',
  headers: new Headers({
    Authorization: connect.getAuthHttpHeader()
               // "Bearer dbl1h9t1g3z0rbctal812"
  })
});

Didn't want to hi-jack the XMLHttpRequest API,
so it's all manual...

Problem #6.2

How to send the Authorization header
in AngularJS 1.x

import {Connect, ConnectProvider} from 'connect-angular1';

import {connect} from './connect';

angular.module('app', [Connect])
  .config((connectProvider: ConnectProvider) => {
    connectProvider.use(connect);
  });

AngularJS is easy thanks to interceptors,

although there are caveats...

Problem #6.3

How to send the Authorization header
in Angular

import {NgModule} from '@angular/core';
import {ConnectModule} from 'connect-angular';

import {connect} from './connect';

@NgModule({
  imports: [ConnectModule.forRoot(connect)]
})
export class AppModule {}

Interceptors in Angular do not exist.
The HTTP class is extended instead.

It's cool, right?

Solving problem #6
was an epic failure

People are really stupid. They manage to figure out where the access_token is stored (localStorage) even if it is not documented. Then they parse it, prefix with "Bearer", add to query params, send to their API...

And then
they complain
to support...

Problem #7

How to prevent the "flickering"

Existing solutions work from the inside of the bootstrapped framework, allowing it to render content before redirecting.

We have researched:

if (typeof window.stop === 'function') {
  window.stop();
} else if (typeof document.execCommand === 'function') {
  window.document.execCommand('Stop');
}

Unfortunately this is problematic because it causes a bug in Chrome where some assets disappear completely.

So many bugs...

The end result

It kinda works...

  • Security is hard, but because RFCs are awesome it isn't that hard after all.
     
  • Browsers have bugs. Chrome has bugs. Firefox has bugs. IE has bugs. It sucks.
     
  • Writing APIs is hard. People do all kinds of stupid things with your lib.
     
  • People are reluctant to switch to the new connect library. They only switch if they experience problems with the old one.

What we've learned

The team

@Florian

@Douglas

@Thomas

@Romain

And to all the guinea pigs beta testers.

thanks

Made with Slides.com