Pure JavaScript (ES5.1) OAuth3 implementation for Browsers and Node.js
Go to file
AJ ONeal 51df65a0dd bugfix: must call atob/btoa from window context 2017-02-21 12:22:38 -07:00
.well-known/oauth3 support logout 2017-02-15 20:09:58 -05:00
README.md add filetree 2017-02-15 11:09:08 -07:00
oauth3.browser.js WIP cleanup and doc 2017-02-13 14:35:48 -05:00
oauth3.cache.js refactor browser-only code 2017-02-08 00:48:07 -05:00
oauth3.core.js .meta -> .token 2017-02-13 12:46:20 -05:00
oauth3.core.provider.js WIP cleanup and doc 2017-02-13 14:35:48 -05:00
oauth3.implicit.js bugfix: must call atob/btoa from window context 2017-02-21 12:19:47 -07:00
oauth3.issuer.js clean up 2017-02-21 12:15:58 -07:00
oauth3.issuer.mock.js bugfix: must call atob/btoa from window context 2017-02-21 12:22:38 -07:00
oauth3.jquery.js fix jQuery JSON POST/PUT/PATCH request, move lint to oauth3.lint.js 2017-02-06 20:10:31 -05:00
oauth3.js create account if none exists 2017-02-15 20:09:31 -05:00
oauth3.lint.js WIP refactor (refreshToken works) 2017-02-08 04:18:15 -05:00
oauth3.ng.js update docs 2017-02-14 16:19:21 -07:00
oauth3.provider.js .meta -> .token 2017-02-13 12:46:20 -05:00
oauth3.scope-check.js WIP provider separation, grant flow 2017-02-09 21:51:22 -05:00

README.md

oauth3.js

The world's smallest, fastest, and most secure OAuth3 (and OAuth2) JavaScript implementation (Yes! works in browsers and node.js with no extra dependencies or bloat and no hacks!)

Instead of bloating your webapp and ruining the mobile experience, you can use a single, small javascript file for all OAuth3 providers (and almost all OAuth2 providers) with a seemless experience.

Also, instead of complicated (or worse - insecure) CLI and Desktop login methods, you can easily integrate an OAuth3 flow (or broker) into any node.js app (i.e. Electron, Node-Webkit) with 0 pain.

Installation

Easy Install for Web Apps (including Mobile):

  1. In your web site / web app folder create a folder called assets
  2. Inside of assets create another folder called org.oauth3
  3. Download oauth.js-v1.zip
  4. Double-click to unzip the folder.
  5. Copy oauth3.js and oauth3.browser.js to assets/org.oauth3

Advanced Installation with git

# Navigate to your web site or web app
pushd /path/to/your/web/app


# clone the project as assets/org.oauth3
mkdir -p assets
git clone git@git.daplie.com:Daplie/oauth3.js.git assets/org.oauth3
pushd assests/org.oauth3
git checkout v1
popd


# symlink `.well-known/oauth3` to `assets/org.oauth3/.well-known/oauth3`
mkdir -p .well-known
ln -sf  ../assets/org.oauth3/.well-known/oauth3 .well-known/oauth3

Advanced Installation with bower

# Install to bower_components
bower install oauth3


# create a `.well-known` folder and an `assets` folder
mkdir -p .well-known assets


# symlink `.well-known/oauth3` to `bower_components/oauth3/.well-known/oauth3`
ln -sf  ../bower_components/oauth3/.well-known/oauth3 .well-known/oauth3


# symlink `assets/org.oauth3` to `bower_components/oauth3`
ln -sf  ../bower_components/oauth3/.well-known/oauth3 .well-known/oauth3
ln -sf  ../bower_components/oauth3 assets/org.oauth3

Example

If you had a simple website / webapp for example.com with only the most necessary files, it might look like this:

example.com
│
│
├── .well-known
│   └── oauth3
│       ├── callback.html
│       ├── directives.json
│       └── index.html
├── assets
│   └── org.oauth3
│       └── oauth3.implicit.js
│
│
├── css
│   └── main.css
├── index.html
└── js
    └── app.js

Usage

Update your HTML to include the the following script tag:

<script src="assets/org.oauth3/oauth3.implicit.js"></script>

You can create a very simple demo application like this:

var providerUri;


// this is any OAuth3-compatible provider, such as oauth3.org
// in v1.1.0 we'll add backwards compatibility for facebook.com, google.com, etc
//
function onChangeProvider(_providerUri) {
  providerUri = _providerUri;
  return OAUTH3.discover(providerUri); // just to cache
}


// This opens up the login window for the specified provider
//
function onClickLogin() {

  var opts = { client_uri: OAuth3.clientUri(window.location) };
  return OAUTH3.implicitGrant(providerUri, opts).then(function (session) {

    console.info('Authentication was Successful:');
    console.log(session);

    // You can use the PPID (or preferrably a hash of it) as the login for your app
    // (it securely functions as both username and password which is known only by your app)
    // If you use a hash of it as an ID, you can also use the PPID itself as a decryption key
    //
    console.info('Secure PPID (aka subject):', session.token.sub);

    return OAUTH3.request({
      url: 'https://oauth3.org/api/org.oauth3.provider/inspect_token'
    , session: session
    }).then(function (resp) {

      console.info("Inspect Token:");
      console.log(resp.data);

    });

  }, function (err) {
    console.error('Authentication Failed:');
    console.log(err);
  });

}

// initialize the provider to be oauth3.org (or any compatible provider)
//
onChangeProvider('oauth3.org');

Compatibility with Frameworks and Libraries

jQuery:

You're all set. Nothing else is needed.

Angular 1:

We've created an Oauth3 service just for you:

<script src="assets/org.oauth3/oauth3.ng.js"></script>

You can include that in addition to the standard file or, if you don't want an extra request, just paste it into your app.js.

Stable API

OAUTH3.utils.clientUri(window.location);                    // produces the default `client_uri` of your app (also used as `client_id`)

OAUTH3.discover(providerUri, { client_id: clientUri });     // Promises the config file for the provider and caches it in memory.

OAUTH3.implicitGrant(providerUri, { client_id: clientUri }) // returns a `session` with `session.token.sub` as the secure ppid.
  // debug: true - will cause the windows to not refresh automatically
  // windowType: 'popup' - will use a popup window to ask user for new permissions, if any
  // windowType: 'background' - will automatically log the user in (if all permissions have been accepted)

OAUTH3.request({ method: 'GET', url: '', session: '', data: '' })       // make an authenticated request to a resource

OAUTH3.logout(providerUri, { client_id: clientUri, session: session })  // opens a popup to confirm logout from the provider
  // Note: you should probably clear your own storage (i.e. localStorage, indexedDb) whenever you call this

OAUTH3.urls
  .discover(providerUri, { client_id: clientUri })          // generates a correctly parameterized url
  .implicitGrant(directives, { client_id: clientUri })      // generates a correctly parameterized url
  .refreshToken(directives, opts)                           // generates a correctly parameterized url
      // opts.client_id = clientUri
      // opts.access_token = <jwt>
      // opts.refresh_token = <jwt>

Staging API

These APIs are NOT yet public, stable APIs, but they are good to be aware of and may help with debugging.

DO NOT rely on them. Many of them WILL change (we just wanted to publish with things as they are).

Public utilities for browser and node.js:

OAUTH3.jwt
  .decode('<urlSafeBase64-encoded-json-web-token>');          // { iat, iss, aud, sub, exp, ttl }

OAUTH3.utils
  .query.stringify({ access_token: '...', debug: true });     // access_token=...&debug=true
  .scope.stringify([ 'profile', 'contacts' ]);                // 'profile,contacts'
  .uri.normalize('https://oauth3.org/connect/');              // 'oauth3.org/connect'
  .url.normalize('oauth3.org/connect/');                      // 'https://oauth3.org/connect'
  .url.resolve('oauth3.org/connect/', '/api/');               // 'https://oauth3.org/connect/api'
  .atob('<non-urlsafe-base64-string>');                       // '<binary-string>' (typically json ascii)

Internal API

This APIs will absolutely change before they are made public (at the very least the leading _ will be removed)

OAUTH3.jwt
  .freshness(tokenMeta, staletimeSeconds, _now);        // returns 'fresh', 'stale', or 'expired' (by seconds before expiry / ttl)

OAUTH3.utils
  .url._normalizePath('oauth3.org/connect/');           // 'oauth3.org/connect'
  ._urlSafeBase64ToBase64(b64);                         // makes base64 safe for window.atob
  .randomState();                                       // a 128-bit crypto-random string
  ._insecureRandomState();                              // a fallback for randomState() in old browsers

OAUTH3._browser                                         // a collection of things a browser needs to perform requests

Roadmap

  • v1.0 - "implicit grant" authorization with examples
    • popup
    • iframe
    • documentation
  • v1.1 - cleanup
    • in-flow discovery
    • smallest possible size
    • inline windowing (non-promisable callback)
    • async set/get
    • logout
  • v1.2 - features
    • "authorization code" flow
    • "broker" flow
  • v1.3 - features
    • remove grants

URL generation:

  • authorizationCode
  • authorizationRedirect
  • implicitGrant
  • loginCode
  • resourceOwnerPassword

URI vs URL

See https://danielmiessler.com/study/url-uri/#gs.=MngfAk

Since we do not require the protocol to be specified, it is a URI

However, we do have a problem of disambiguation since a URI may look like a path:

  1. https://example.com/api/org.oauth3.provider
  2. example.com/api/org.oauth.provider/ (not unique)
  3. /api/org.oauth3.provider
  4. api/org.oauth3.provider (not unique)

Therefore anywhere a URI or a Path could be used, the URI must be a URL. We eliminate #2.

As a general rule I don't like rules that sometimes apply and sometimes don't, so I may need to rethink this. However, there are cases where including the protocol can be very ugly and confusing and we definitely need to allow relative paths.

A potential work-around would be to assume all paths are relative (elimitate #4 instead) and have the path always key off of the base URL - if oauth3 directives are to be found at https://example.com/username/.well-known/oauth3/directives.json then /api/whatever would refer to https://example.com/username/api/whatever.