Merge branch 'issuer-rewrite'
This commit is contained in:
		
						commit
						815ba04d37
					
				
							
								
								
									
										10
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								README.md
									
									
									
									
									
								
							@ -19,13 +19,13 @@ If you have no idea what you're doing
 | 
			
		||||
 | 
			
		||||
1. Create a folder for your project named after your app, such as `example.com/`
 | 
			
		||||
2. Inside of the folder `example.com/` a folder called `assets/`
 | 
			
		||||
3. Inside of the folder `example.com/assets` a folder called `org.oauth3/`
 | 
			
		||||
3. Inside of the folder `example.com/assets` a folder called `oauth3.org/`
 | 
			
		||||
4. Download [oauth3.js-v1.zip](https://git.daplie.com/OAuth3/oauth3.js/repository/archive.zip?ref=v1)
 | 
			
		||||
5. Double-click to unzip the folder.
 | 
			
		||||
6. Copy the file `oauth3.core.js` into the folder `example.com/assets/org.oauth3/`
 | 
			
		||||
6. Copy the file `oauth3.core.js` into the folder `example.com/assets/oauth3.org/`
 | 
			
		||||
7. Copy the folder `well-known` into the folder `example.com/`
 | 
			
		||||
8. Rename the folder `well-known` to `.well-known` (when you do this, it become invisible, that's okay)
 | 
			
		||||
9. Add `<script src="assets/org.oauth3/oauth3.core.js"></script>` to your `index.html`
 | 
			
		||||
9. Add `<script src="assets/oauth3.org/oauth3.core.js"></script>` to your `index.html`
 | 
			
		||||
9. Add `<script src="app.js"></script>` to your `index.html`
 | 
			
		||||
10. Create files in `example.com` called `app.js` and `index.html` and put this in it:
 | 
			
		||||
 | 
			
		||||
@ -44,7 +44,7 @@ If you have no idea what you're doing
 | 
			
		||||
  <script src="https://code.jquery.com/jquery-3.1.1.js"
 | 
			
		||||
    integrity="sha256-16cdPddA6VdVInumRGo6IbivbERE8p7CQR3HzTBuELA="
 | 
			
		||||
    crossorigin="anonymous"></script>
 | 
			
		||||
  <script src="assets/org.oauth3/oauth3.core.js"></script>
 | 
			
		||||
  <script src="assets/oauth3.org/oauth3.core.js"></script>
 | 
			
		||||
  <script src="app.js"></script>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
@ -81,7 +81,7 @@ function onClickLogin() {
 | 
			
		||||
    console.info('Secure PPID (aka subject):', session.token.sub);
 | 
			
		||||
 | 
			
		||||
    return oauth3.request({
 | 
			
		||||
      url: 'https://oauth3.org/api/issuer@oauth3.org/inspect_token'
 | 
			
		||||
      url: 'https://oauth3.org/api/issuer@oauth3.org/inspect'
 | 
			
		||||
    , session: session
 | 
			
		||||
    }).then(function (resp) {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -131,8 +131,8 @@ parseArgs(process.argv, {
 | 
			
		||||
 | 
			
		||||
    // authn / authz
 | 
			
		||||
  , [ 'devices', 'manages devices for your account(s)' ]
 | 
			
		||||
  , [ 'devices:new', 'create a new device (default name is hostname, default ip is the result of :provider/api/org.oauth3.tunnel/checkip)'.replace(/\b:provider\b/, defaults.provider) ]
 | 
			
		||||
  , [ 'devices:set', 'set the ip address of the device (defaults ip is the result of :provider/api/org.oauth3.tunnel/checkip)'.replace(/\b:provider\b/, defaults.provider) ]
 | 
			
		||||
  , [ 'devices:new', 'create a new device (default name is hostname, default ip is the result of :provider/api/tunnel@oauth3.org/checkip)'.replace(/\b:provider\b/, defaults.provider) ]
 | 
			
		||||
  , [ 'devices:set', 'set the ip address of the device (defaults ip is the result of :provider/api/tunnel@oauth3.org/checkip)'.replace(/\b:provider\b/, defaults.provider) ]
 | 
			
		||||
  , [ 'devices:attach', "attach a device to a domain's DNS record" ]
 | 
			
		||||
  , [ 'devices:detach', "detach an account from a domain's DNS record" ]
 | 
			
		||||
  , [ 'devices:select', '(re)claim the specified device as this device (i.e. you re-installed your OS or deleted your ~/.oauth3)' ]
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										317
									
								
								oauth3.core.js
									
									
									
									
									
								
							
							
						
						
									
										317
									
								
								oauth3.core.js
									
									
									
									
									
								
							@ -78,7 +78,7 @@
 | 
			
		||||
  , uri: {
 | 
			
		||||
      normalize: function (uri) {
 | 
			
		||||
        if ('string' !== typeof uri) {
 | 
			
		||||
          throw new Error('must provide a string to OAUTH3.uri.normalize');
 | 
			
		||||
          throw new Error("attempted to normalize non-string URI: "+JSON.stringify(uri));
 | 
			
		||||
        }
 | 
			
		||||
        // tested with
 | 
			
		||||
        //   example.com
 | 
			
		||||
@ -94,7 +94,7 @@
 | 
			
		||||
  , url: {
 | 
			
		||||
      normalize: function (url) {
 | 
			
		||||
        if ('string' !== typeof url) {
 | 
			
		||||
          throw new Error('must provide a string to OAUTH3.url.normalize');
 | 
			
		||||
          throw new Error("attempted to normalize non-string URL: "+JSON.stringify(url));
 | 
			
		||||
        }
 | 
			
		||||
        // tested with
 | 
			
		||||
        //   example.com
 | 
			
		||||
@ -168,9 +168,12 @@
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  , scope: {
 | 
			
		||||
      stringify: function (scope) {
 | 
			
		||||
      parse: function (scope) {
 | 
			
		||||
        return (scope||'').split(/[+, ]+/g);
 | 
			
		||||
      }
 | 
			
		||||
    , stringify: function (scope) {
 | 
			
		||||
        if (Array.isArray(scope)) {
 | 
			
		||||
          scope = scope.join(' ');
 | 
			
		||||
          scope = scope.join(',');
 | 
			
		||||
        }
 | 
			
		||||
        return scope;
 | 
			
		||||
      }
 | 
			
		||||
@ -204,25 +207,68 @@
 | 
			
		||||
    }
 | 
			
		||||
  , jwt: {
 | 
			
		||||
      // decode only (no verification)
 | 
			
		||||
      decode: function (str) {
 | 
			
		||||
      decode: function (token, opts) {
 | 
			
		||||
 | 
			
		||||
        // 'abc.qrs.xyz'
 | 
			
		||||
        // [ 'abc', 'qrs', 'xyz' ]
 | 
			
		||||
        // [ {}, {}, 'foo' ]
 | 
			
		||||
        // { header: {}, payload: {}, signature: '' }
 | 
			
		||||
        var parts = str.split(/\./g);
 | 
			
		||||
        var jsons = parts.slice(0, 2).map(function (urlsafe64) {
 | 
			
		||||
          return JSON.parse(OAUTH3._base64.decodeUrlSafe(urlsafe64));
 | 
			
		||||
        });
 | 
			
		||||
        // {}
 | 
			
		||||
        var parts = token.split(/\./g);
 | 
			
		||||
        var err;
 | 
			
		||||
        if (parts.length !== 3) {
 | 
			
		||||
          err = new Error("Invalid JWT: required 3 '.' separated components not "+parts.length);
 | 
			
		||||
          err.code = 'E_INVALID_JWT';
 | 
			
		||||
          throw err;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return { header: jsons[0], payload: jsons[1] };
 | 
			
		||||
        if (!opts || !opts.complete) {
 | 
			
		||||
          return JSON.parse(OAUTH3._base64.decodeUrlSafe(parts[1]));
 | 
			
		||||
        }
 | 
			
		||||
        return {
 | 
			
		||||
          header:  JSON.parse(OAUTH3._base64.decodeUrlSafe(parts[0]))
 | 
			
		||||
        , payload: JSON.parse(OAUTH3._base64.decodeUrlSafe(parts[1]))
 | 
			
		||||
        };
 | 
			
		||||
      }
 | 
			
		||||
    , verify: function (jwk, token) {
 | 
			
		||||
    , verify: function (token, jwk) {
 | 
			
		||||
        if (!OAUTH3.crypto) {
 | 
			
		||||
          return OAUTH3.PromiseA.reject(new Error("OAuth3 crypto library unavailable"));
 | 
			
		||||
        }
 | 
			
		||||
        jwk = jwk.publicKey || jwk;
 | 
			
		||||
 | 
			
		||||
        var parts = token.split(/\./g);
 | 
			
		||||
        var data = OAUTH3._binStr.binStrToBuffer(parts.slice(0, 2).join('.'));
 | 
			
		||||
        var signature = OAUTH3._base64.urlSafeToBuffer(parts[2]);
 | 
			
		||||
 | 
			
		||||
        return OAUTH3.crypto.core.verify(jwk, data, signature);
 | 
			
		||||
        return OAUTH3.crypto.core.verify(jwk, data, signature).then(function () {
 | 
			
		||||
          return OAUTH3.jwt.decode(token);
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    , sign: function (payload, jwk) {
 | 
			
		||||
        if (!OAUTH3.crypto) {
 | 
			
		||||
          return OAUTH3.PromiseA.reject(new Error("OAuth3 crypto library unavailable"));
 | 
			
		||||
        }
 | 
			
		||||
        jwk = jwk.private_key || jwk.privateKey || jwk;
 | 
			
		||||
 | 
			
		||||
        var prom;
 | 
			
		||||
        if (jwk.kid) {
 | 
			
		||||
          prom = OAUTH3.PromiseA.resolve(jwk.kid);
 | 
			
		||||
        } else {
 | 
			
		||||
          prom = OAUTH3.crypto.thumbprintJwk(jwk);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return prom.then(function (kid) {
 | 
			
		||||
          // Currently the crypto part of the OAuth3 library only supports ES256
 | 
			
		||||
          var header = {type: 'JWT', alg: 'ES256', kid: kid};
 | 
			
		||||
          var input = [
 | 
			
		||||
            OAUTH3._base64.encodeUrlSafe(JSON.stringify(header, null))
 | 
			
		||||
          , OAUTH3._base64.encodeUrlSafe(JSON.stringify(payload, null))
 | 
			
		||||
          ].join('.');
 | 
			
		||||
 | 
			
		||||
          return OAUTH3.crypto.core.sign(jwk, OAUTH3._binStr.binStrToBuffer(input))
 | 
			
		||||
            .then(OAUTH3._base64.bufferToUrlSafe)
 | 
			
		||||
            .then(function (signature) {
 | 
			
		||||
              return input + '.' + signature;
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    , freshness: function (tokenMeta, staletime, now) {
 | 
			
		||||
        // If the token doesn't expire then it's always fresh.
 | 
			
		||||
@ -282,7 +328,7 @@
 | 
			
		||||
        // Example Implicit Grant Request
 | 
			
		||||
        // (for generating a browser-only session, not a session on your server)
 | 
			
		||||
        //
 | 
			
		||||
        // GET https://example.com/api/org.oauth3.provider/authorization_dialog
 | 
			
		||||
        // GET https://example.com/api/issuer@oauth3.org/authorization_dialog
 | 
			
		||||
        //  ?response_type=token
 | 
			
		||||
        //  &scope=`encodeURIComponent('profile.login profile.email')`
 | 
			
		||||
        //  &state=`cryptoutil.random().toString('hex')`
 | 
			
		||||
@ -348,29 +394,36 @@
 | 
			
		||||
        //    , "username": "<<username>>", "password": "password" }
 | 
			
		||||
        //
 | 
			
		||||
        opts = opts || {};
 | 
			
		||||
        var type = 'access_token';
 | 
			
		||||
        var grantType = 'refresh_token';
 | 
			
		||||
        var refresh_token = opts.refresh_token || (opts.session && opts.session.refresh_token);
 | 
			
		||||
        var err;
 | 
			
		||||
        if (!refresh_token) {
 | 
			
		||||
          err = new Error('refreshing a token requires a refresh token');
 | 
			
		||||
          err.code = 'E_NO_TOKEN';
 | 
			
		||||
          throw err;
 | 
			
		||||
        }
 | 
			
		||||
        if (OAUTH3.jwt.freshness(OAUTH3.jwt.decode(refresh_token)) === 'expired') {
 | 
			
		||||
          err = new Error('refresh token has also expired, login required again');
 | 
			
		||||
          err.code = 'E_EXPIRED_TOKEN';
 | 
			
		||||
          throw err;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var scope = opts.scope || directive.authn_scope;
 | 
			
		||||
        var clientSecret = opts.client_secret;
 | 
			
		||||
        var args = directive[type];
 | 
			
		||||
        var args = directive.access_token;
 | 
			
		||||
        var params = {
 | 
			
		||||
          "grant_type": grantType
 | 
			
		||||
        , "refresh_token": opts.refresh_token || (opts.session && opts.session.refresh_token)
 | 
			
		||||
          "grant_type": 'refresh_token'
 | 
			
		||||
        , "refresh_token": refresh_token
 | 
			
		||||
        , "response_type": 'token'
 | 
			
		||||
        , "client_id": opts.client_id || opts.client_uri
 | 
			
		||||
        , "client_uri": opts.client_uri
 | 
			
		||||
        //, "scope": undefined
 | 
			
		||||
        //, "client_secret": undefined
 | 
			
		||||
        , debug: opts.debug || undefined
 | 
			
		||||
        };
 | 
			
		||||
        var uri = args.url;
 | 
			
		||||
        var body;
 | 
			
		||||
 | 
			
		||||
        if (clientSecret) {
 | 
			
		||||
        if (opts.client_secret) {
 | 
			
		||||
          // TODO not allowed in the browser
 | 
			
		||||
          console.warn("if this is a browser, you must not use client_secret");
 | 
			
		||||
          params.client_secret = clientSecret;
 | 
			
		||||
          params.client_secret = opts.client_secret;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (scope) {
 | 
			
		||||
@ -437,42 +490,44 @@
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  , hooks: {
 | 
			
		||||
      directives: {
 | 
			
		||||
        get: function (providerUri) {
 | 
			
		||||
          providerUri = OAUTH3.uri.normalize(providerUri);
 | 
			
		||||
          return OAUTH3.PromiseA.resolve(OAUTH3.hooks.directives._getCached(providerUri)
 | 
			
		||||
            || OAUTH3.hooks.directives._get(providerUri))
 | 
			
		||||
            .then(function (directives) {
 | 
			
		||||
              // or do .then(this._set) to keep DRY?
 | 
			
		||||
            OAUTH3.hooks.directives._cache[providerUri] = directives;
 | 
			
		||||
            return directives;
 | 
			
		||||
          });
 | 
			
		||||
      _checkStorage: function (grpName, funcName) {
 | 
			
		||||
        if (!OAUTH3._hooks) {
 | 
			
		||||
          OAUTH3._hooks = {};
 | 
			
		||||
        }
 | 
			
		||||
      , _getCached: function (providerUri) {
 | 
			
		||||
          providerUri = OAUTH3.uri.normalize(providerUri);
 | 
			
		||||
          if (!OAUTH3.hooks.directives._cache) { OAUTH3.hooks.directives._cache = {}; }
 | 
			
		||||
          return OAUTH3.hooks.directives._cache[providerUri];
 | 
			
		||||
        if (!OAUTH3._hooks[grpName]) {
 | 
			
		||||
          console.log('using default storage for '+grpName+', set OAUTH3._hooks.'+grpName+' for custom storage');
 | 
			
		||||
          OAUTH3._hooks[grpName] = OAUTH3._defaultStorage[grpName];
 | 
			
		||||
        }
 | 
			
		||||
        if (!OAUTH3._hooks[grpName][funcName]) {
 | 
			
		||||
          throw new Error("'"+funcName+"' is not defined for custom "+grpName+" storage");
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    , directives: {
 | 
			
		||||
        get: function (providerUri) {
 | 
			
		||||
          OAUTH3.hooks._checkStorage('directives', 'get');
 | 
			
		||||
 | 
			
		||||
          if (!providerUri) {
 | 
			
		||||
            throw new Error("providerUri is not set");
 | 
			
		||||
          }
 | 
			
		||||
          return OAUTH3.PromiseA.resolve(OAUTH3._hooks.directives.get(OAUTH3.uri.normalize(providerUri)));
 | 
			
		||||
        }
 | 
			
		||||
      , set: function (providerUri, directives) {
 | 
			
		||||
          providerUri = OAUTH3.uri.normalize(providerUri);
 | 
			
		||||
          if (!OAUTH3.hooks.directives._cache) { OAUTH3.hooks.directives._cache = {}; }
 | 
			
		||||
          OAUTH3.hooks.directives._cache[providerUri] = directives;
 | 
			
		||||
          return OAUTH3.PromiseA.resolve(OAUTH3.hooks.directives._set(providerUri, directives));
 | 
			
		||||
        }
 | 
			
		||||
      , _get: function (providerUri) {
 | 
			
		||||
          if (!OAUTH3._hooks || !OAUTH3._hooks.directives || !OAUTH3._hooks.directives.get) {
 | 
			
		||||
            console.warn('[Warn] Please implement OAUTH3._hooks.directives.get = function (providerUri) { return PromiseA<directives>; }');
 | 
			
		||||
            return JSON.parse(window.localStorage.getItem('directives-' + providerUri) || '{}');
 | 
			
		||||
          OAUTH3.hooks._checkStorage('directives', 'set');
 | 
			
		||||
 | 
			
		||||
          if (!providerUri) {
 | 
			
		||||
            throw new Error("providerUri is not set");
 | 
			
		||||
          }
 | 
			
		||||
          return OAUTH3._hooks.directives.get(providerUri);
 | 
			
		||||
          return OAUTH3.PromiseA.resolve(OAUTH3._hooks.directives.set(OAUTH3.uri.normalize(providerUri), directives));
 | 
			
		||||
        }
 | 
			
		||||
      , _set: function (providerUri, directives) {
 | 
			
		||||
          if (!OAUTH3._hooks || !OAUTH3._hooks.directives || !OAUTH3._hooks.directives.set) {
 | 
			
		||||
            console.warn('[Warn] Please implement OAUTH3._hooks.directives.set = function (providerUri, directives) { return PromiseA<directives>; }');
 | 
			
		||||
            window.localStorage.setItem('directives-' + providerUri, JSON.stringify(directives));
 | 
			
		||||
            return directives;
 | 
			
		||||
          }
 | 
			
		||||
          return OAUTH3._hooks.directives.set(providerUri, directives);
 | 
			
		||||
      , all: function () {
 | 
			
		||||
          OAUTH3.hooks._checkStorage('directives', 'all');
 | 
			
		||||
 | 
			
		||||
          return OAUTH3.PromiseA.resolve(OAUTH3._hooks.directives.all());
 | 
			
		||||
        }
 | 
			
		||||
      , clear: function () {
 | 
			
		||||
          OAUTH3.hooks._checkStorage('directives', 'clear');
 | 
			
		||||
 | 
			
		||||
          return OAUTH3.PromiseA.resolve(OAUTH3._hooks.directives.clear());
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    , session: {
 | 
			
		||||
@ -492,7 +547,7 @@
 | 
			
		||||
          oldSession.client_uri = clientUri;      // azp
 | 
			
		||||
 | 
			
		||||
          // info about the newly-discovered token
 | 
			
		||||
          oldSession.token = OAUTH3.jwt.decode(oldSession.access_token).payload;
 | 
			
		||||
          oldSession.token = OAUTH3.jwt.decode(oldSession.access_token);
 | 
			
		||||
 | 
			
		||||
          oldSession.token.sub = oldSession.token.sub
 | 
			
		||||
            || (oldSession.token.acx||{}).id
 | 
			
		||||
@ -503,7 +558,7 @@
 | 
			
		||||
          oldSession.token.provider_uri = providerUri;
 | 
			
		||||
 | 
			
		||||
          if (oldSession.refresh_token) {
 | 
			
		||||
            oldSession.refresh = OAUTH3.jwt.decode(oldSession.refresh_token).payload;
 | 
			
		||||
            oldSession.refresh = OAUTH3.jwt.decode(oldSession.refresh_token);
 | 
			
		||||
            oldSession.refresh.sub = oldSession.refresh.sub
 | 
			
		||||
              || (oldSession.refresh.acx||{}).id
 | 
			
		||||
              || ((oldSession.refresh.axs||[])[0]||{}).appScopedId
 | 
			
		||||
@ -513,7 +568,7 @@
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          // set for a set of audiences
 | 
			
		||||
          return OAUTH3.PromiseA.resolve(OAUTH3.hooks.session.set(providerUri, oldSession));
 | 
			
		||||
          return OAUTH3.hooks.session.set(providerUri, oldSession);
 | 
			
		||||
        }
 | 
			
		||||
      , check: function (preq, opts) {
 | 
			
		||||
          opts = opts || {};
 | 
			
		||||
@ -566,62 +621,40 @@
 | 
			
		||||
            return newSession; // oauth3.hooks.refreshSession(expiredSession, newSession);
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
      , _getCached: function (providerUri, id) {
 | 
			
		||||
          providerUri = OAUTH3.uri.normalize(providerUri);
 | 
			
		||||
          if (!OAUTH3.hooks.session._cache) { OAUTH3.hooks.session._cache = {}; }
 | 
			
		||||
          if (id) {
 | 
			
		||||
            return OAUTH3.hooks.session._cache[providerUri + id];
 | 
			
		||||
          }
 | 
			
		||||
          return OAUTH3.hooks.session._cache[providerUri];
 | 
			
		||||
        }
 | 
			
		||||
      , set: function (providerUri, newSession, id) {
 | 
			
		||||
          OAUTH3.hooks._checkStorage('sessions', 'set');
 | 
			
		||||
 | 
			
		||||
          if (!providerUri) {
 | 
			
		||||
            console.error(new Error('no providerUri').stack);
 | 
			
		||||
            throw new Error("providerUri is not set");
 | 
			
		||||
          }
 | 
			
		||||
          providerUri = OAUTH3.uri.normalize(providerUri);
 | 
			
		||||
          if (!OAUTH3.hooks.session._cache) { OAUTH3.hooks.session._cache = {}; }
 | 
			
		||||
          OAUTH3.hooks.session._cache[providerUri + (id || newSession.id || newSession.token.id || '')] = newSession;
 | 
			
		||||
          if (!id) {
 | 
			
		||||
            OAUTH3.hooks.session._cache[providerUri] = newSession;
 | 
			
		||||
          }
 | 
			
		||||
          return OAUTH3.PromiseA.resolve(OAUTH3.hooks.session._set(providerUri, newSession));
 | 
			
		||||
          return OAUTH3.PromiseA.resolve(OAUTH3._hooks.sessions.set(providerUri, newSession, id));
 | 
			
		||||
        }
 | 
			
		||||
      , get: function (providerUri, id) {
 | 
			
		||||
          providerUri = OAUTH3.uri.normalize(providerUri);
 | 
			
		||||
          OAUTH3.hooks._checkStorage('sessions', 'get');
 | 
			
		||||
 | 
			
		||||
          if (!providerUri) {
 | 
			
		||||
            throw new Error("providerUri is not set");
 | 
			
		||||
          }
 | 
			
		||||
          providerUri = OAUTH3.uri.normalize(providerUri);
 | 
			
		||||
          return OAUTH3.PromiseA.resolve(OAUTH3._hooks.sessions.get(providerUri, id));
 | 
			
		||||
        }
 | 
			
		||||
      , all: function (providerUri) {
 | 
			
		||||
          OAUTH3.hooks._checkStorage('sessions', 'all');
 | 
			
		||||
 | 
			
		||||
          return OAUTH3.PromiseA.resolve(
 | 
			
		||||
            OAUTH3.hooks.session._getCached(providerUri, id) || OAUTH3.hooks.session._get(providerUri, id)
 | 
			
		||||
          ).then(function (session) {
 | 
			
		||||
            var s = session || { token: {} };
 | 
			
		||||
            OAUTH3.hooks.session._cache[providerUri + (id || s.id || s.token.id || '')] = session;
 | 
			
		||||
            if (!id) {
 | 
			
		||||
              OAUTH3.hooks.session._cache[providerUri] = session;
 | 
			
		||||
            }
 | 
			
		||||
            return session;
 | 
			
		||||
          });
 | 
			
		||||
          if (providerUri) {
 | 
			
		||||
            providerUri = OAUTH3.uri.normalize(providerUri);
 | 
			
		||||
          }
 | 
			
		||||
          return OAUTH3.PromiseA.resolve(OAUTH3._hooks.sessions.all(providerUri));
 | 
			
		||||
        }
 | 
			
		||||
      , _get: function (providerUri, id) {
 | 
			
		||||
          if (!OAUTH3._hooks || !OAUTH3._hooks.sessions || !OAUTH3._hooks.sessions.all) {
 | 
			
		||||
            console.warn('[Warn] Please implement OAUTH3._hooks.sessions.all = function ([providerUri]) { return PromiseA<sessions>; }');
 | 
			
		||||
      , clear: function (providerUri) {
 | 
			
		||||
          OAUTH3.hooks._checkStorage('sessions', 'clear');
 | 
			
		||||
 | 
			
		||||
          if (providerUri) {
 | 
			
		||||
            providerUri = OAUTH3.uri.normalize(providerUri);
 | 
			
		||||
          }
 | 
			
		||||
          if (!OAUTH3._hooks || !OAUTH3._hooks.sessions || !OAUTH3._hooks.sessions.get) {
 | 
			
		||||
            console.warn('[Warn] Please implement OAUTH3._hooks.sessions.get = function (providerUri[, id]) { return PromiseA<session>; }');
 | 
			
		||||
            return JSON.parse(window.sessionStorage.getItem('session-' + providerUri + (id || '')) || 'null');
 | 
			
		||||
          }
 | 
			
		||||
          return OAUTH3._hooks.sessions.get(providerUri, id);
 | 
			
		||||
        }
 | 
			
		||||
      , _set: function (providerUri, newSession, id) {
 | 
			
		||||
          if (!OAUTH3._hooks || !OAUTH3._hooks.sessions || !OAUTH3._hooks.sessions.set) {
 | 
			
		||||
            console.warn('[Warn] Please implement OAUTH3._hooks.sessions.set = function (providerUri, newSession[, id]) { return PromiseA<newSession>; }');
 | 
			
		||||
            window.sessionStorage.setItem('session-' + providerUri, JSON.stringify(newSession));
 | 
			
		||||
            window.sessionStorage.setItem('session-' + providerUri + (id || newSession.id || newSession.token.id || ''), JSON.stringify(newSession));
 | 
			
		||||
            return newSession;
 | 
			
		||||
          }
 | 
			
		||||
          return OAUTH3._hooks.sessions.set(providerUri, newSession, id);
 | 
			
		||||
          return OAUTH3.PromiseA.resolve(OAUTH3._hooks.sessions.clear(providerUri));
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
@ -810,7 +843,7 @@
 | 
			
		||||
          return OAUTH3.PromiseA.reject(OAUTH3.error.parse(directives.issuer /*providerUri*/, params));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        OAUTH3.hooks.session._cache = {};
 | 
			
		||||
        OAUTH3.hooks.session.clear();
 | 
			
		||||
        return params;
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
@ -1138,6 +1171,82 @@
 | 
			
		||||
  };
 | 
			
		||||
  OAUTH3.login = OAUTH3.implicitGrant;
 | 
			
		||||
 | 
			
		||||
  OAUTH3._defaultStorage = {
 | 
			
		||||
    _getStorageKeys: function (prefix, storage) {
 | 
			
		||||
      storage = storage || window.localStorage;
 | 
			
		||||
      var matching = [];
 | 
			
		||||
      var ind, key;
 | 
			
		||||
      for (ind = 0; ind < storage.length; ind++) {
 | 
			
		||||
        key = storage.key(ind);
 | 
			
		||||
        if (key.indexOf(prefix || '') === 0) {
 | 
			
		||||
          matching.push(key);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return matching;
 | 
			
		||||
    }
 | 
			
		||||
  , directives: {
 | 
			
		||||
      prefix: 'directives-'
 | 
			
		||||
    , get: function (providerUri) {
 | 
			
		||||
        var result = JSON.parse(window.localStorage.getItem(this.prefix + providerUri) || '{}');
 | 
			
		||||
        return OAUTH3.PromiseA.resolve(result);
 | 
			
		||||
      }
 | 
			
		||||
    , set: function (providerUri, directives) {
 | 
			
		||||
        window.localStorage.setItem(this.prefix + providerUri, JSON.stringify(directives));
 | 
			
		||||
        return this.get(providerUri);
 | 
			
		||||
      }
 | 
			
		||||
    , all: function () {
 | 
			
		||||
        var prefix = this.prefix;
 | 
			
		||||
        var result = {};
 | 
			
		||||
        OAUTH3._defaultStorage._getStorageKeys(prefix).forEach(function (key) {
 | 
			
		||||
          result[key.replace(prefix, '')] = JSON.parse(window.localStorage.getItem(key) || '{}');
 | 
			
		||||
        });
 | 
			
		||||
        return OAUTH3.PromiseA.resolve(result);
 | 
			
		||||
      }
 | 
			
		||||
    , clear: function () {
 | 
			
		||||
        OAUTH3._defaultStorage._getStorageKeys(this.prefix).forEach(function (key) {
 | 
			
		||||
          window.localStorage.removeItem(key);
 | 
			
		||||
        });
 | 
			
		||||
        return OAUTH3.PromiseA.resolve();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  , sessions: {
 | 
			
		||||
      prefix: 'session-'
 | 
			
		||||
    , get: function (providerUri, id) {
 | 
			
		||||
        var result;
 | 
			
		||||
        if (id) {
 | 
			
		||||
          result = JSON.parse(window.sessionStorage.getItem(this.prefix + providerUri+id) || 'null');
 | 
			
		||||
        } else {
 | 
			
		||||
          result = JSON.parse(window.sessionStorage.getItem(this.prefix + providerUri) || 'null');
 | 
			
		||||
        }
 | 
			
		||||
        return OAUTH3.PromiseA.resolve(result);
 | 
			
		||||
      }
 | 
			
		||||
    , set: function (providerUri, newSession, id) {
 | 
			
		||||
        var str = JSON.stringify(newSession);
 | 
			
		||||
        window.sessionStorage.setItem(this.prefix + providerUri, str);
 | 
			
		||||
        id = id || newSession.id || newSession.token.sub || newSession.token.id;
 | 
			
		||||
        if (id) {
 | 
			
		||||
          window.sessionStorage.setItem(this.prefix + providerUri + id, str);
 | 
			
		||||
        }
 | 
			
		||||
        return this.get(providerUri, id);
 | 
			
		||||
      }
 | 
			
		||||
    , all: function (providerUri) {
 | 
			
		||||
        var prefix = this.prefix + (providerUri || '');
 | 
			
		||||
        var result = {};
 | 
			
		||||
        OAUTH3._defaultStorage._getStorageKeys(prefix, window.sessionStorage).forEach(function (key) {
 | 
			
		||||
          result[key.replace(prefix, '')] = JSON.parse(window.sessionStorage.getItem(key) || 'null');
 | 
			
		||||
        });
 | 
			
		||||
        return OAUTH3.PromiseA.resolve(result);
 | 
			
		||||
      }
 | 
			
		||||
    , clear: function (providerUri) {
 | 
			
		||||
        var prefix = this.prefix + (providerUri || '');
 | 
			
		||||
        OAUTH3._defaultStorage._getStorageKeys(prefix, window.sessionStorage).forEach(function (key) {
 | 
			
		||||
          window.sessionStorage.removeItem(key);
 | 
			
		||||
        });
 | 
			
		||||
        return OAUTH3.PromiseA.resolve();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // TODO get rid of these
 | 
			
		||||
  OAUTH3.utils = {
 | 
			
		||||
    clientUri: OAUTH3.clientUri
 | 
			
		||||
@ -1169,7 +1278,7 @@
 | 
			
		||||
    , _resourceProviderUri: null
 | 
			
		||||
    , _identityProviderDirectives: null
 | 
			
		||||
    , _resourceProviderDirectives: null
 | 
			
		||||
    //, _resourceProviderMap: null // map between xyz.com and org.oauth3.domains
 | 
			
		||||
    //, _resourceProviderMap: null // map between xyz.com and domains@oauth3.org
 | 
			
		||||
    , _init: function (location, opts) {
 | 
			
		||||
        var me = this;
 | 
			
		||||
        if (!opts) {
 | 
			
		||||
@ -1238,7 +1347,7 @@
 | 
			
		||||
        var me = this;
 | 
			
		||||
        return me._initClient().then(function () {
 | 
			
		||||
          return me.setIdentityProvider(providerUri).then(function () {
 | 
			
		||||
            // TODO how to say "Use xyz.com for org.oauth3.domains, but abc.com for org.oauth3.dns"?
 | 
			
		||||
            // TODO how to say "Use xyz.com for domains@oauth3.org, but abc.com for dns@oauth3.org"?
 | 
			
		||||
            return me.setResourceProvider(providerUri);
 | 
			
		||||
          });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										100
									
								
								oauth3.crypto.js
									
									
									
									
									
								
							
							
						
						
									
										100
									
								
								oauth3.crypto.js
									
									
									
									
									
								
							@ -1,5 +1,5 @@
 | 
			
		||||
;(function (exports) {
 | 
			
		||||
'use strict';
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  var OAUTH3 = exports.OAUTH3 = exports.OAUTH3 || require('./oauth3.core.js').OAUTH3;
 | 
			
		||||
 | 
			
		||||
@ -82,6 +82,7 @@
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    function checkWebCrypto() {
 | 
			
		||||
      /* global OAUTH3_crypto_fallback */
 | 
			
		||||
      var loadFallback = function() {
 | 
			
		||||
        var prom;
 | 
			
		||||
        loadFallback = function () { return prom; };
 | 
			
		||||
@ -96,17 +97,16 @@
 | 
			
		||||
              resolve();
 | 
			
		||||
            }
 | 
			
		||||
          };
 | 
			
		||||
          script.src = '/assets/org.oauth3/oauth3.crypto.fallback.js';
 | 
			
		||||
          script.src = '/assets/oauth3.org/oauth3.crypto.fallback.js';
 | 
			
		||||
          body.appendChild(script);
 | 
			
		||||
        });
 | 
			
		||||
        return prom;
 | 
			
		||||
      };
 | 
			
		||||
      function checkException(name, func) {
 | 
			
		||||
        new OAUTH3.PromiseA(function (resolve) { resolve(func()); })
 | 
			
		||||
        OAUTH3.PromiseA.resolve().then(func)
 | 
			
		||||
          .then(function () {
 | 
			
		||||
            OAUTH3.crypto.core[name] = webCrypto[name];
 | 
			
		||||
          })
 | 
			
		||||
          .catch(function (err) {
 | 
			
		||||
          }, function (err) {
 | 
			
		||||
            console.warn('error with WebCrypto', name, '- using fallback', err);
 | 
			
		||||
            loadFallback().then(function () {
 | 
			
		||||
              OAUTH3.crypto.core[name] = OAUTH3_crypto_fallback[name];
 | 
			
		||||
@ -195,101 +195,61 @@
 | 
			
		||||
      .then(OAUTH3._base64.bufferToUrlSafe);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  OAUTH3.crypto._createKey = function (ppid) {
 | 
			
		||||
    var saltProm = OAUTH3.crypto.core.randomBytes(16);
 | 
			
		||||
    var kekProm = saltProm.then(function (salt) {
 | 
			
		||||
      return OAUTH3.crypto.core.pbkdf2(ppid, salt);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    var ecdsaProm = OAUTH3.crypto.core.genEcdsaKeyPair()
 | 
			
		||||
    .then(function (keyPair) {
 | 
			
		||||
  OAUTH3.crypto.createKeyPair = function () {
 | 
			
		||||
    // TODO: maybe support other types of key pairs, not just ECDSA P-256
 | 
			
		||||
    return OAUTH3.crypto.core.genEcdsaKeyPair().then(function (keyPair) {
 | 
			
		||||
      return OAUTH3.crypto.thumbprintJwk(keyPair.publicKey).then(function (kid) {
 | 
			
		||||
        keyPair.privateKey.alg = keyPair.publicKey.alg = 'ES256';
 | 
			
		||||
        keyPair.privateKey.kid = keyPair.publicKey.kid = kid;
 | 
			
		||||
        return keyPair;
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  OAUTH3.crypto.encryptKeyPair = function (keyPair, password) {
 | 
			
		||||
    var saltProm = OAUTH3.crypto.core.randomBytes(16);
 | 
			
		||||
    var kekProm = saltProm.then(function (salt) {
 | 
			
		||||
      return OAUTH3.crypto.core.pbkdf2(password, salt);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return OAUTH3.PromiseA.all([
 | 
			
		||||
      kekProm
 | 
			
		||||
    , ecdsaProm
 | 
			
		||||
    , saltProm
 | 
			
		||||
    , OAUTH3.crypto.core.randomBytes(16)
 | 
			
		||||
    , OAUTH3.crypto.core.randomBytes(12)
 | 
			
		||||
    , OAUTH3.crypto.core.randomBytes(12)
 | 
			
		||||
    ]).then(function (results) {
 | 
			
		||||
  , ]).then(function (results) {
 | 
			
		||||
      var kek        = results[0];
 | 
			
		||||
      var keyPair    = results[1];
 | 
			
		||||
      var salt       = results[2];
 | 
			
		||||
      var userSecret = results[3];
 | 
			
		||||
      var ecdsaIv    = results[4];
 | 
			
		||||
      var secretIv   = results[5];
 | 
			
		||||
      var salt       = results[1];
 | 
			
		||||
      var ecdsaIv    = results[2];
 | 
			
		||||
 | 
			
		||||
      return OAUTH3.PromiseA.all([
 | 
			
		||||
        OAUTH3.crypto.core.encrypt(kek, ecdsaIv, OAUTH3._binStr.binStrToBuffer(JSON.stringify(keyPair.privateKey)))
 | 
			
		||||
      , OAUTH3.crypto.core.encrypt(kek, secretIv, userSecret)
 | 
			
		||||
      ])
 | 
			
		||||
      .then(function (encrypted) {
 | 
			
		||||
      var privKeyBuf = OAUTH3._binStr.binStrToBuffer(JSON.stringify(keyPair.privateKey));
 | 
			
		||||
      return OAUTH3.crypto.core.encrypt(kek, ecdsaIv, privKeyBuf).then(function (encrypted) {
 | 
			
		||||
        return {
 | 
			
		||||
          publicKey:  keyPair.publicKey
 | 
			
		||||
        , privateKey: OAUTH3._base64.bufferToUrlSafe(encrypted[0])
 | 
			
		||||
        , userSecret: OAUTH3._base64.bufferToUrlSafe(encrypted[1])
 | 
			
		||||
        , privateKey: OAUTH3._base64.bufferToUrlSafe(encrypted)
 | 
			
		||||
        , salt:       OAUTH3._base64.bufferToUrlSafe(salt)
 | 
			
		||||
        , ecdsaIv:    OAUTH3._base64.bufferToUrlSafe(ecdsaIv)
 | 
			
		||||
        , secretIv:   OAUTH3._base64.bufferToUrlSafe(secretIv)
 | 
			
		||||
        };
 | 
			
		||||
      , };
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  OAUTH3.crypto._decryptKey = function (ppid, storedObj) {
 | 
			
		||||
  OAUTH3.crypto.decryptKeyPair = function (storedObj, password) {
 | 
			
		||||
    var salt   = OAUTH3._base64.urlSafeToBuffer(storedObj.salt);
 | 
			
		||||
    var encJwk = OAUTH3._base64.urlSafeToBuffer(storedObj.privateKey);
 | 
			
		||||
    var iv     = OAUTH3._base64.urlSafeToBuffer(storedObj.ecdsaIv);
 | 
			
		||||
 | 
			
		||||
    return OAUTH3.crypto.core.pbkdf2(ppid, salt)
 | 
			
		||||
    return OAUTH3.crypto.core.pbkdf2(password, salt)
 | 
			
		||||
      .then(function (key) {
 | 
			
		||||
        return OAUTH3.crypto.core.decrypt(key, iv, encJwk);
 | 
			
		||||
      })
 | 
			
		||||
      .then(OAUTH3._binStr.bufferToBinStr)
 | 
			
		||||
      .then(JSON.parse);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  OAUTH3.crypto._getKey = function (ppid) {
 | 
			
		||||
    return OAUTH3.crypto.core.sha256(OAUTH3._binStr.binStrToBuffer(ppid))
 | 
			
		||||
    .then(function (hash) {
 | 
			
		||||
      var name = 'kek-' + OAUTH3._base64.bufferToUrlSafe(hash);
 | 
			
		||||
      var promise;
 | 
			
		||||
 | 
			
		||||
      if (window.localStorage.getItem(name) === null) {
 | 
			
		||||
        promise = OAUTH3.crypto._createKey(ppid).then(function (key) {
 | 
			
		||||
          window.localStorage.setItem(name, JSON.stringify(key));
 | 
			
		||||
          return key;
 | 
			
		||||
        });
 | 
			
		||||
      } else {
 | 
			
		||||
        promise = OAUTH3.PromiseA.resolve(JSON.parse(window.localStorage.getItem(name)));
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return promise.then(function (storedObj) {
 | 
			
		||||
        return OAUTH3.crypto._decryptKey(ppid, storedObj);
 | 
			
		||||
      .then(JSON.parse)
 | 
			
		||||
      .then(function (privateKey) {
 | 
			
		||||
        return {
 | 
			
		||||
          privateKey: privateKey
 | 
			
		||||
        , publicKey:  storedObj.publicKey
 | 
			
		||||
      , };
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  OAUTH3.crypto._signPayload = function (payload) {
 | 
			
		||||
    return OAUTH3.crypto._getKey('some PPID').then(function (key) {
 | 
			
		||||
      var header = {type: 'JWT', alg: key.alg, kid: key.kid};
 | 
			
		||||
      var input = [
 | 
			
		||||
        OAUTH3._base64.encodeUrlSafe(JSON.stringify(header, null))
 | 
			
		||||
      , OAUTH3._base64.encodeUrlSafe(JSON.stringify(payload, null))
 | 
			
		||||
      ].join('.');
 | 
			
		||||
 | 
			
		||||
      return OAUTH3.crypto.core.sign(key, OAUTH3._binStr.binStrToBuffer(input))
 | 
			
		||||
        .then(OAUTH3._base64.bufferToUrlSafe)
 | 
			
		||||
        .then(function (signature) {
 | 
			
		||||
          return input + '.' + signature;
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
}('undefined' !== typeof exports ? exports : window));
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										711
									
								
								oauth3.issuer.js
									
									
									
									
									
								
							
							
						
						
									
										711
									
								
								oauth3.issuer.js
									
									
									
									
									
								
							@ -3,39 +3,6 @@
 | 
			
		||||
 | 
			
		||||
var OAUTH3 = exports.OAUTH3 = exports.OAUTH3 || require('./oauth3.core.js').OAUTH3;
 | 
			
		||||
 | 
			
		||||
OAUTH3.query.parse = function (search) {
 | 
			
		||||
  // parse a query or a hash
 | 
			
		||||
  if (-1 !== ['#', '?'].indexOf(search[0])) {
 | 
			
		||||
    search = search.substring(1);
 | 
			
		||||
  }
 | 
			
		||||
  // Solve for case of search within hash
 | 
			
		||||
  // example: #/authorization_dialog/?state=...&redirect_uri=...
 | 
			
		||||
  var queryIndex = search.indexOf('?');
 | 
			
		||||
  if (-1 !== queryIndex) {
 | 
			
		||||
    search = search.substr(queryIndex + 1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  var args = search.split('&');
 | 
			
		||||
  var argsParsed = {};
 | 
			
		||||
  var i, arg, kvp, key, value;
 | 
			
		||||
 | 
			
		||||
  for (i = 0; i < args.length; i += 1) {
 | 
			
		||||
    arg = args[i];
 | 
			
		||||
    if (-1 === arg.indexOf('=')) {
 | 
			
		||||
      argsParsed[decodeURIComponent(arg).trim()] = true;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      kvp = arg.split('=');
 | 
			
		||||
      key = decodeURIComponent(kvp[0]).trim();
 | 
			
		||||
      value = decodeURIComponent(kvp[1]).trim();
 | 
			
		||||
      argsParsed[key] = value;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return argsParsed;
 | 
			
		||||
};
 | 
			
		||||
OAUTH3.scope.parse = function (scope) {
 | 
			
		||||
  return (scope||'').split(/[, ]/g);
 | 
			
		||||
};
 | 
			
		||||
OAUTH3.url.parse = function (url) {
 | 
			
		||||
  // TODO browser
 | 
			
		||||
  // Node should replace this
 | 
			
		||||
@ -58,8 +25,16 @@ OAUTH3.url._isRedirectHostSafe = function (referrerUrl, redirectUrl) {
 | 
			
		||||
};
 | 
			
		||||
OAUTH3.url.checkRedirect = function (client, query) {
 | 
			
		||||
  console.warn("[security] URL path checking not yet implemented");
 | 
			
		||||
  if (!query) {
 | 
			
		||||
    query = client;
 | 
			
		||||
    client = query.client_uri;
 | 
			
		||||
  }
 | 
			
		||||
  client = client.url || client;
 | 
			
		||||
 | 
			
		||||
  var clientUrl = OAUTH3.url.normalize(client.url);
 | 
			
		||||
  // it doesn't matter who the referrer is as long as the destination
 | 
			
		||||
  // is an authorized destination for the client in question
 | 
			
		||||
  // (though it may not hurt to pass the referrer's info on to the client)
 | 
			
		||||
  var clientUrl = OAUTH3.url.normalize(client);
 | 
			
		||||
  var redirectUrl = OAUTH3.url.normalize(query.redirect_uri);
 | 
			
		||||
 | 
			
		||||
  // General rule:
 | 
			
		||||
@ -72,6 +47,18 @@ OAUTH3.url.checkRedirect = function (client, query) {
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  var callbackUrl = 'https://oauth3.org/docs/errors#E_REDIRECT_ATTACK?'+OAUTH3.query.stringify({
 | 
			
		||||
    'redirect_uri': redirectUrl
 | 
			
		||||
  , 'allowed_urls': clientUrl
 | 
			
		||||
  , 'client_id':    client
 | 
			
		||||
  , 'referrer_uri': OAUTH3.uri.normalize(window.document.referrer)
 | 
			
		||||
  });
 | 
			
		||||
  if (query.debug) {
 | 
			
		||||
    console.log('Redirect Attack');
 | 
			
		||||
    console.log(query);
 | 
			
		||||
    window.alert("DEBUG MODE checkRedirect error encountered. Check the console.");
 | 
			
		||||
  }
 | 
			
		||||
  location.href = callbackUrl;
 | 
			
		||||
  return false;
 | 
			
		||||
};
 | 
			
		||||
OAUTH3.url.redirect = function (clientParams, grants, tokenOrError) {
 | 
			
		||||
@ -110,13 +97,11 @@ OAUTH3.urls.resourceOwnerPassword = function (directive, opts) {
 | 
			
		||||
  // Example Resource Owner Password Request
 | 
			
		||||
  // (generally for 1st party and direct-partner mobile apps, and webapps)
 | 
			
		||||
  //
 | 
			
		||||
  // POST https://example.com/api/org.oauth3.provider/access_token
 | 
			
		||||
  // POST https://example.com/api/issuer@oauth3.org/access_token
 | 
			
		||||
  //    { "grant_type": "password", "client_id": "<<id>>", "scope": "<<scope>>"
 | 
			
		||||
  //    , "username": "<<username>>", "password": "password" }
 | 
			
		||||
  //
 | 
			
		||||
  opts = opts || {};
 | 
			
		||||
  var type = 'access_token';
 | 
			
		||||
  var grantType = 'password';
 | 
			
		||||
 | 
			
		||||
  if (!opts.password) {
 | 
			
		||||
    if (opts.otp) {
 | 
			
		||||
@ -125,16 +110,13 @@ OAUTH3.urls.resourceOwnerPassword = function (directive, opts) {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  var scope = opts.scope || directive.authn_scope;
 | 
			
		||||
  var clientAgreeTos = 'oauth3.org/tos/draft'; // opts.clientAgreeTos || opts.client_agree_tos;
 | 
			
		||||
  var clientUri = opts.client_uri;
 | 
			
		||||
  var args = directive[type];
 | 
			
		||||
  var args = directive.access_token;
 | 
			
		||||
  var otpCode = opts.otp || opts.otpCode || opts.otp_code || opts.otpToken || opts.otp_token || undefined;
 | 
			
		||||
  // TODO require user agent
 | 
			
		||||
  var params = {
 | 
			
		||||
    client_id: opts.client_id || opts.client_uri
 | 
			
		||||
  , client_uri: opts.client_uri
 | 
			
		||||
  , grant_type: grantType
 | 
			
		||||
  , grant_type: 'password'
 | 
			
		||||
  , username: opts.username
 | 
			
		||||
  , password: opts.password || otpCode || undefined
 | 
			
		||||
  , totp: opts.totp || opts.totpToken || opts.totp_token || undefined
 | 
			
		||||
@ -149,23 +131,21 @@ OAUTH3.urls.resourceOwnerPassword = function (directive, opts) {
 | 
			
		||||
  //, "jwt": opts.jwt // TODO sign a proof with a previously loaded public_key
 | 
			
		||||
  , debug: opts.debug || undefined
 | 
			
		||||
  };
 | 
			
		||||
  var uri = args.url;
 | 
			
		||||
  var body;
 | 
			
		||||
  if (opts.totp) {
 | 
			
		||||
    params.totp = opts.totp;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (clientUri) {
 | 
			
		||||
    params.clientAgreeTos = clientAgreeTos;
 | 
			
		||||
    if (!clientAgreeTos) {
 | 
			
		||||
  if (opts.client_uri) {
 | 
			
		||||
    params.clientAgreeTos = 'oauth3.org/tos/draft'; // opts.clientAgreeTos || opts.client_agree_tos;
 | 
			
		||||
    if (!params.clientAgreeTos) {
 | 
			
		||||
      throw new Error('Developer Error: missing clientAgreeTos uri');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  var scope = opts.scope || directive.authn_scope;
 | 
			
		||||
  if (scope) {
 | 
			
		||||
    params.scope = OAUTH3.scope.stringify(scope);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  var uri = args.url;
 | 
			
		||||
  var body;
 | 
			
		||||
  if ('GET' === args.method.toUpperCase()) {
 | 
			
		||||
    uri += '?' + OAUTH3.query.stringify(params);
 | 
			
		||||
  } else {
 | 
			
		||||
@ -181,6 +161,10 @@ OAUTH3.urls.resourceOwnerPassword = function (directive, opts) {
 | 
			
		||||
OAUTH3.urls.grants = function (directive, opts) {
 | 
			
		||||
  // directive = { issuer, authorization_decision }
 | 
			
		||||
  // opts = { response_type, scopes{ granted, requested, pending, accepted } }
 | 
			
		||||
  var grantsDir = directive.grants;
 | 
			
		||||
  if (!grantsDir) {
 | 
			
		||||
    throw new Error("provider doesn't support grants");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!opts) {
 | 
			
		||||
    throw new Error("You must supply a directive and an options object.");
 | 
			
		||||
@ -195,18 +179,19 @@ OAUTH3.urls.grants = function (directive, opts) {
 | 
			
		||||
    console.warn("You should supply options.referrer");
 | 
			
		||||
  }
 | 
			
		||||
  if (!opts.method) {
 | 
			
		||||
    console.warn("You must supply options.method as either 'GET', or 'POST'");
 | 
			
		||||
    console.warn("You should supply options.method as either 'GET', or 'POST'");
 | 
			
		||||
    opts.method = grantsDir.method || 'GET';
 | 
			
		||||
  }
 | 
			
		||||
  if ('POST' === opts.method) {
 | 
			
		||||
    if ('string' !== typeof opts.scope) {
 | 
			
		||||
      console.warn("You should supply options.scope as a space-delimited string of scopes");
 | 
			
		||||
      throw new Error("You must supply options.scope as a comma-delimited string of scopes");
 | 
			
		||||
    }
 | 
			
		||||
    if (-1 === ['token', 'code'].indexOf(opts.response_type)) {
 | 
			
		||||
      throw new Error("You must supply options.response_type as 'token' or 'code'");
 | 
			
		||||
    if ('string' !== typeof opts.sub) {
 | 
			
		||||
      console.log("provide 'sub' to urls.grants to specify the PPID for the client");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  var url = OAUTH3.url.resolve(directive.api, directive.grants.url)
 | 
			
		||||
  var url = OAUTH3.url.resolve(directive.api, grantsDir.url)
 | 
			
		||||
    .replace(/(:azp|:client_id)/g, OAUTH3.uri.normalize(opts.client_id || opts.client_uri))
 | 
			
		||||
    .replace(/(:sub|:account_id)/g, opts.session.token.sub || 'ISSUER:GRANT:TOKEN_SUB:UNDEFINED')
 | 
			
		||||
    ;
 | 
			
		||||
@ -214,16 +199,14 @@ OAUTH3.urls.grants = function (directive, opts) {
 | 
			
		||||
    client_id: opts.client_id
 | 
			
		||||
  , client_uri: opts.client_uri
 | 
			
		||||
  , referrer: opts.referrer
 | 
			
		||||
  , response_type: opts.response_type
 | 
			
		||||
  , scope: opts.scope
 | 
			
		||||
  , tenant_id: opts.tenant_id
 | 
			
		||||
  , sub: opts.sub
 | 
			
		||||
  };
 | 
			
		||||
  var body;
 | 
			
		||||
 | 
			
		||||
  var body;
 | 
			
		||||
  if ('GET' === opts.method) {
 | 
			
		||||
    url += '?' + OAUTH3.query.stringify(data);
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
  } else {
 | 
			
		||||
    body = data;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -234,6 +217,76 @@ OAUTH3.urls.grants = function (directive, opts) {
 | 
			
		||||
  , session: opts.session
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
OAUTH3.urls.clientToken = function (directive, opts) {
 | 
			
		||||
  var tokenDir = directive.access_token;
 | 
			
		||||
  if (!tokenDir) {
 | 
			
		||||
    throw new Error("provider doesn't support getting access tokens");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!opts) {
 | 
			
		||||
    throw new Error("You must supply a directive and an options object.");
 | 
			
		||||
  }
 | 
			
		||||
  if (!(opts.azp || opts.client_id)) {
 | 
			
		||||
    throw new Error("You must supply options.client_id.");
 | 
			
		||||
  }
 | 
			
		||||
  if (!opts.session) {
 | 
			
		||||
    throw new Error("You must supply options.session.");
 | 
			
		||||
  }
 | 
			
		||||
  if (!opts.method) {
 | 
			
		||||
    opts.method = tokenDir.method || 'POST';
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  var params = {
 | 
			
		||||
    grant_type: 'issuer_token'
 | 
			
		||||
  , client_id:  opts.azp || opts.client_id
 | 
			
		||||
  , azp: opts.azp || opts.client_id
 | 
			
		||||
  , aud: opts.aud
 | 
			
		||||
  , exp: opts.exp
 | 
			
		||||
  , refresh_token: opts.refresh_token
 | 
			
		||||
  , refresh_exp: opts.refresh_exp
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  var url = OAUTH3.url.resolve(directive.api, tokenDir.url);
 | 
			
		||||
  var body;
 | 
			
		||||
  if ('GET' === opts.method) {
 | 
			
		||||
    url += '?' + OAUTH3.query.stringify(params);
 | 
			
		||||
  } else {
 | 
			
		||||
    body = params;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    method: opts.method
 | 
			
		||||
  , url: url
 | 
			
		||||
  , data: body
 | 
			
		||||
  , session: opts.session
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
OAUTH3.urls.publishKey = function (directive, opts) {
 | 
			
		||||
  var jwkDir = directive.publish_jwk;
 | 
			
		||||
  if (!jwkDir) {
 | 
			
		||||
    throw new Error("provider doesn't support publishing public keys");
 | 
			
		||||
  }
 | 
			
		||||
  if (!opts) {
 | 
			
		||||
    throw new Error("You must supply a directive and an options object.");
 | 
			
		||||
  }
 | 
			
		||||
  if (!opts.session) {
 | 
			
		||||
    throw new Error("You must supply 'options.session'.");
 | 
			
		||||
  }
 | 
			
		||||
  if (!(opts.public_key || opts.publicKey)) {
 | 
			
		||||
    throw new Error("You must supply 'options.public_key'.");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  var url = OAUTH3.url.resolve(directive.api, jwkDir.url)
 | 
			
		||||
    .replace(/(:sub|:account_id)/g, opts.session.token.sub)
 | 
			
		||||
    ;
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    method: jwkDir.method || opts.method || 'POST'
 | 
			
		||||
  , url: url
 | 
			
		||||
  , data: opts.public_key || opts.publicKey
 | 
			
		||||
  , session: opts.session
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
OAUTH3.authn = {};
 | 
			
		||||
OAUTH3.authn.loginMeta = function (directive, opts) {
 | 
			
		||||
@ -267,112 +320,95 @@ OAUTH3.authn.otp = function (directive, opts) {
 | 
			
		||||
OAUTH3.authn.resourceOwnerPassword = function (directive, opts) {
 | 
			
		||||
  var providerUri = directive.issuer;
 | 
			
		||||
 | 
			
		||||
  //var scope = opts.scope;
 | 
			
		||||
  //var appId = opts.appId;
 | 
			
		||||
  return OAUTH3.discover(providerUri, opts).then(function (directive) {
 | 
			
		||||
    var prequest = OAUTH3.urls.resourceOwnerPassword(directive, opts);
 | 
			
		||||
  return OAUTH3.request(OAUTH3.urls.resourceOwnerPassword(directive, opts)).then(function (resp) {
 | 
			
		||||
    var data = resp.data;
 | 
			
		||||
    data.provider_uri = providerUri;
 | 
			
		||||
    if (data.error) {
 | 
			
		||||
      return OAUTH3.PromiseA.reject(OAUTH3.error.parse(providerUri, data));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO return not the raw request?
 | 
			
		||||
    return OAUTH3.request(prequest).then(function (req) {
 | 
			
		||||
      var data = req.data;
 | 
			
		||||
      data.provider_uri = providerUri;
 | 
			
		||||
      if (data.error) {
 | 
			
		||||
        return OAUTH3.PromiseA.reject(OAUTH3.error.parse(providerUri, data));
 | 
			
		||||
    return OAUTH3.hooks.session.refresh(
 | 
			
		||||
      opts.session || { provider_uri: providerUri, client_uri: opts.client_uri || opts.clientUri }
 | 
			
		||||
    , data
 | 
			
		||||
    );
 | 
			
		||||
  }).then(function (session) {
 | 
			
		||||
    if (!opts.rememberDevice && !opts.remember_device) {
 | 
			
		||||
      return session;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return OAUTH3.PromiseA.resolve().then(function () {
 | 
			
		||||
      if (!OAUTH3.crypto) {
 | 
			
		||||
        throw new Error("OAuth3 crypto library unavailable");
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return OAUTH3.hooks.session.refresh(
 | 
			
		||||
        opts.session || { provider_uri: providerUri, client_uri: opts.client_uri || opts.clientUri }
 | 
			
		||||
      , data
 | 
			
		||||
      );
 | 
			
		||||
      return OAUTH3.crypto.createKeyPair().then(function (keyPair) {
 | 
			
		||||
        return OAUTH3.request(OAUTH3.urls.publishKey(directive, {
 | 
			
		||||
          session: session
 | 
			
		||||
        , publicKey: keyPair.publicKey
 | 
			
		||||
        })).then(function () {
 | 
			
		||||
          return OAUTH3.hooks.keyPairs.set(session.token.sub, keyPair);
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    }).then(function () {
 | 
			
		||||
      return session;
 | 
			
		||||
    }, function (err) {
 | 
			
		||||
      console.error('failed to save keys to remember device', err);
 | 
			
		||||
      window.alert('Failed to remember device');
 | 
			
		||||
      return session;
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
OAUTH3.authz = {};
 | 
			
		||||
OAUTH3.authz.scopes = function (providerUri, session, clientParams) {
 | 
			
		||||
  // OAuth3.requests.grants(providerUri, {});         // return list of grants
 | 
			
		||||
  // OAuth3.checkGrants(providerUri, {});             //
 | 
			
		||||
  var clientUri = OAUTH3.uri.normalize(clientParams.client_uri || OAUTH3._browser.window.document.referrer);
 | 
			
		||||
  var scope = clientParams.scope || '';
 | 
			
		||||
  var clientObj = clientParams;
 | 
			
		||||
 | 
			
		||||
  if (!scope) {
 | 
			
		||||
    scope = 'oauth3_authn';
 | 
			
		||||
  var scope = clientParams.scope || 'oauth3_authn';
 | 
			
		||||
  if ('oauth3_authn' === scope) {
 | 
			
		||||
    // implicit ppid grant is automatic
 | 
			
		||||
    console.warn('[security] fix scope checking on backend so that we can do automatic grants');
 | 
			
		||||
    // TODO check user preference if implicit ppid grant is allowed
 | 
			
		||||
    //return generateToken(session, clientObj);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return OAUTH3.authz.grants(providerUri, {
 | 
			
		||||
    method: 'GET'
 | 
			
		||||
  , client_id: clientUri
 | 
			
		||||
  , client_uri: clientUri
 | 
			
		||||
  , session: session
 | 
			
		||||
  }).then(function (grantResults) {
 | 
			
		||||
    var grants;
 | 
			
		||||
    var grantedScopes;
 | 
			
		||||
    var grantedScopesMap;
 | 
			
		||||
    var pendingScopes;
 | 
			
		||||
    var acceptedScopes;
 | 
			
		||||
    var scopes = scope.split(/[+, ]/g);
 | 
			
		||||
    var callbackUrl;
 | 
			
		||||
 | 
			
		||||
    // it doesn't matter who the referrer is as long as the destination
 | 
			
		||||
    // is an authorized destination for the client in question
 | 
			
		||||
    // (though it may not hurt to pass the referrer's info on to the client)
 | 
			
		||||
    if (!OAUTH3.url.checkRedirect(grantResults.client, clientObj)) {
 | 
			
		||||
      callbackUrl = 'https://oauth3.org/docs/errors#E_REDIRECT_ATTACK'
 | 
			
		||||
        + '?redirect_uri=' + clientObj.redirect_uri
 | 
			
		||||
        + '&allowed_urls=' + grantResults.client.url
 | 
			
		||||
        + '&client_id=' + clientUri
 | 
			
		||||
        + '&referrer_uri=' + OAUTH3.uri.normalize(window.document.referrer)
 | 
			
		||||
        ;
 | 
			
		||||
      if (clientParams.debug) {
 | 
			
		||||
        console.log('grantResults Redirect Attack');
 | 
			
		||||
        console.log(grantResults);
 | 
			
		||||
        console.log(clientObj);
 | 
			
		||||
        window.alert("DEBUG MODE checkRedirect error encountered. Check the console.");
 | 
			
		||||
  return OAUTH3.hooks.grants.get(session.token.sub, clientUri).then(function (granted) {
 | 
			
		||||
    if (granted) {
 | 
			
		||||
      if (typeof granted.scope === 'string') {
 | 
			
		||||
        return OAUTH3.scope.parse(granted.scope);
 | 
			
		||||
      } else if (Array.isArray(granted.scope)) {
 | 
			
		||||
        return granted.scope;
 | 
			
		||||
      }
 | 
			
		||||
      location.href = callbackUrl;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ('oauth3_authn' === scope) {
 | 
			
		||||
      // implicit ppid grant is automatic
 | 
			
		||||
      console.warn('[security] fix scope checking on backend so that we can do automatic grants');
 | 
			
		||||
      // TODO check user preference if implicit ppid grant is allowed
 | 
			
		||||
      //return generateToken(session, clientObj);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    grants = (grantResults).grants.filter(function (grant) {
 | 
			
		||||
      if (clientUri === (grant.azp || grant.oauth_client_id || grant.oauthClientId)) {
 | 
			
		||||
        return true;
 | 
			
		||||
    return OAUTH3.authz.grants(providerUri, {
 | 
			
		||||
      method: 'GET'
 | 
			
		||||
    , client_id: clientUri
 | 
			
		||||
    , client_uri: clientUri
 | 
			
		||||
    , session: session
 | 
			
		||||
    }).then(function (results) {
 | 
			
		||||
      return results.grants;
 | 
			
		||||
    }, function (err) {
 | 
			
		||||
      if (!/no .*grants .*found/i.test(err.message)) {
 | 
			
		||||
        throw err;
 | 
			
		||||
      }
 | 
			
		||||
      return [];
 | 
			
		||||
    });
 | 
			
		||||
  }).then(function (granted) {
 | 
			
		||||
    var requested = OAUTH3.scope.parse(scope);
 | 
			
		||||
    var accepted = [];
 | 
			
		||||
    var pending = [];
 | 
			
		||||
    requested.forEach(function (scp) {
 | 
			
		||||
      if (granted.indexOf(scp) < 0) {
 | 
			
		||||
        pending.push(scp);
 | 
			
		||||
      } else {
 | 
			
		||||
        accepted.push(scp);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    grantedScopesMap = {};
 | 
			
		||||
    acceptedScopes = [];
 | 
			
		||||
    pendingScopes = scopes.filter(function (requestedScope) {
 | 
			
		||||
      return grants.every(function (grant) {
 | 
			
		||||
        if (!grant.scope) {
 | 
			
		||||
          grant.scope = 'oauth3_authn';
 | 
			
		||||
        }
 | 
			
		||||
        var gscopes = grant.scope.split(/[+, ]/g);
 | 
			
		||||
        gscopes.forEach(function (s) { grantedScopesMap[s] = true; });
 | 
			
		||||
        if (-1 !== gscopes.indexOf(requestedScope)) {
 | 
			
		||||
          // already accepted in the past
 | 
			
		||||
          acceptedScopes.push(requestedScope);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
          // true, is pending
 | 
			
		||||
          return true;
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    grantedScopes = Object.keys(grantedScopesMap);
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      pending: pendingScopes    // not yet accepted
 | 
			
		||||
    , granted: grantedScopes    // all granted, ever
 | 
			
		||||
    , requested: scopes         // all requested, now
 | 
			
		||||
    , accepted: acceptedScopes  // granted (ever) and requested (now)
 | 
			
		||||
      requested: requested  // all requested, now
 | 
			
		||||
    , granted:   granted    // all granted, ever
 | 
			
		||||
    , accepted:  accepted   // intersection of granted (ever) and requested (now)
 | 
			
		||||
    , pending:   pending     // not yet accepted
 | 
			
		||||
    };
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
@ -381,73 +417,153 @@ OAUTH3.authz.grants = function (providerUri, opts) {
 | 
			
		||||
    client_id: providerUri
 | 
			
		||||
  , debug: opts.debug
 | 
			
		||||
  }).then(function (directive) {
 | 
			
		||||
    return OAUTH3.request(OAUTH3.urls.grants(directive, opts), opts);
 | 
			
		||||
  }).then(function (grantsResult) {
 | 
			
		||||
    var grants = grantsResult.originalData || grantsResult.data;
 | 
			
		||||
    if (grants.error) {
 | 
			
		||||
      return OAUTH3.PromiseA.reject(OAUTH3.error.parse(providerUri, grants));
 | 
			
		||||
    }
 | 
			
		||||
    // the responses for GET and POST requests are now the same, so we should alway be able to
 | 
			
		||||
    // use the response and save it the same way.
 | 
			
		||||
    if ('GET' !== opts.method && 'POST' !== opts.method) {
 | 
			
		||||
      return grants;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return OAUTH3.request(OAUTH3.urls.grants(directive, opts), opts).then(function (grantsResult) {
 | 
			
		||||
      if ('POST' === opts.method) {
 | 
			
		||||
        // TODO this is clientToken
 | 
			
		||||
        return grantsResult.originalData || grantsResult.data;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      var grants = grantsResult.originalData || grantsResult.data;
 | 
			
		||||
      // TODO
 | 
			
		||||
      if (grants.error) {
 | 
			
		||||
        return OAUTH3.PromiseA.reject(OAUTH3.error.parse(providerUri, grants));
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      OAUTH3.hooks.grants.set(opts.client_id + '-client', grants.client);
 | 
			
		||||
      grants.grants.forEach(function (grant) {
 | 
			
		||||
        var clientId = grant.client_id || grant.oauth_client_id || grant.oauthClientId;
 | 
			
		||||
        // TODO should save as an array
 | 
			
		||||
        OAUTH3.hooks.grants.set(clientId, [ grant ]);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      return {
 | 
			
		||||
        client: OAUTH3.hooks.grants.get(opts.client_id + '-client')
 | 
			
		||||
      , grants: OAUTH3.hooks.grants.get(opts.client_id) || []
 | 
			
		||||
      };
 | 
			
		||||
    });
 | 
			
		||||
    OAUTH3.hooks.grants.set(grants.sub, grants.azp, grants);
 | 
			
		||||
    return {
 | 
			
		||||
      client: grants.azp
 | 
			
		||||
    , clientSub: grants.azpSub
 | 
			
		||||
    , grants: OAUTH3.scope.parse(grants.scope)
 | 
			
		||||
    };
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
function calcExpiration(exp, now) {
 | 
			
		||||
  if (!exp) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (typeof exp === 'string') {
 | 
			
		||||
    var match = /^(\d+\.?\d*) *(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(exp);
 | 
			
		||||
    if (!match) {
 | 
			
		||||
      return now;
 | 
			
		||||
    }
 | 
			
		||||
    var num = parseFloat(match[1]);
 | 
			
		||||
    var type = (match[2] || 's').toLowerCase()[0];
 | 
			
		||||
    switch (type) {
 | 
			
		||||
      case 'y': num *= 365.25; /* falls through */
 | 
			
		||||
      case 'd': num *= 24;     /* falls through */
 | 
			
		||||
      case 'h': num *= 60;     /* falls through */
 | 
			
		||||
      case 'm': num *= 60;     /* falls through */
 | 
			
		||||
      case 's': exp = num;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (typeof exp !== 'number') {
 | 
			
		||||
    throw new Error('invalid expiration provided: '+exp);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  now = now || Math.floor(Date.now() / 1000);
 | 
			
		||||
  if (exp > now) {
 | 
			
		||||
    return exp;
 | 
			
		||||
  } else if (exp > 31557600) {
 | 
			
		||||
    console.warn('tried to set expiration to more that a year');
 | 
			
		||||
    exp = 31557600;
 | 
			
		||||
  }
 | 
			
		||||
  return now + exp;
 | 
			
		||||
}
 | 
			
		||||
OAUTH3.authz.redirectWithToken = function (providerUri, session, clientParams, scopes) {
 | 
			
		||||
  if (!OAUTH3.url.checkRedirect(clientParams.client_uri, clientParams)) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if ('token' !== clientParams.response_type) {
 | 
			
		||||
    var message;
 | 
			
		||||
    if ('code' === clientParams.response_type) {
 | 
			
		||||
      message = "Authorization Code Redirect NOT IMPLEMENTED";
 | 
			
		||||
    } else {
 | 
			
		||||
      message = "Authorization response type '"+clientParams.response_type+"' not supported";
 | 
			
		||||
    }
 | 
			
		||||
    window.alert(message);
 | 
			
		||||
    throw new Error(message);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  scopes.new = scopes.new || [];
 | 
			
		||||
 | 
			
		||||
  if ('token' === clientParams.response_type) {
 | 
			
		||||
    // get token and redirect client-side
 | 
			
		||||
    return OAUTH3.authz.grants(providerUri, {
 | 
			
		||||
      method: 'POST'
 | 
			
		||||
  var prom;
 | 
			
		||||
  if (scopes.new) {
 | 
			
		||||
    prom = OAUTH3.authz.grants(providerUri, {
 | 
			
		||||
      session: session
 | 
			
		||||
    , method: 'POST'
 | 
			
		||||
    , client_id: clientParams.client_uri
 | 
			
		||||
    , client_uri: clientParams.client_uri
 | 
			
		||||
    , scope: scopes.granted.concat(scopes.new).join(',')
 | 
			
		||||
    , response_type: clientParams.response_type
 | 
			
		||||
    , referrer: clientParams.referrer
 | 
			
		||||
    , session: session
 | 
			
		||||
    , subject: clientParams.subject
 | 
			
		||||
    , debug: clientParams.debug
 | 
			
		||||
    }).then(function (results) {
 | 
			
		||||
 | 
			
		||||
      // TODO limit refresh token to an expirable token
 | 
			
		||||
      // TODO inform client not to persist token
 | 
			
		||||
      /*
 | 
			
		||||
      if (clientParams.dnsTxt) {
 | 
			
		||||
        Object.keys(results).forEach(function (key) {
 | 
			
		||||
          if (/refresh/.test(key)) {
 | 
			
		||||
            results[key] = undefined;
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
      */
 | 
			
		||||
      OAUTH3.url.redirect(clientParams, scopes, results);
 | 
			
		||||
    , scope: scopes.accepted.concat(scopes.new).join(',')
 | 
			
		||||
    });
 | 
			
		||||
  } else {
 | 
			
		||||
    prom = OAUTH3.PromiseA.resolve();
 | 
			
		||||
  }
 | 
			
		||||
  else if ('code' === clientParams.response_type) {
 | 
			
		||||
    // get token and redirect server-side
 | 
			
		||||
    // (requires insecure form post as per spec)
 | 
			
		||||
    //OAUTH3.requests.authorizationDecision();
 | 
			
		||||
    window.alert("Authorization Code Redirect NOT IMPLEMENTED");
 | 
			
		||||
    throw new Error("Authorization Code Redirect NOT IMPLEMENTED");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return prom.then(function () {
 | 
			
		||||
    return OAUTH3.hooks.keyPairs.get(session.token.sub);
 | 
			
		||||
  }).then(function (keyPair) {
 | 
			
		||||
    if (!keyPair) {
 | 
			
		||||
      return OAUTH3.discover(providerUri, {
 | 
			
		||||
        client_id: providerUri
 | 
			
		||||
      , debug: clientParams.debug
 | 
			
		||||
      }).then(function (directive) {
 | 
			
		||||
        return OAUTH3.request(OAUTH3.urls.clientToken(directive, {
 | 
			
		||||
          method: 'POST'
 | 
			
		||||
        , session: session
 | 
			
		||||
        , referrer: clientParams.referrer
 | 
			
		||||
        , response_type: clientParams.response_type
 | 
			
		||||
        , client_id:  clientParams.client_uri
 | 
			
		||||
        , azp: clientParams.client_uri
 | 
			
		||||
        , aud: clientParams.aud
 | 
			
		||||
        , exp: clientParams.exp
 | 
			
		||||
        , refresh_token: clientParams.refresh_token
 | 
			
		||||
        , refresh_exp: clientParams.refresh_exp
 | 
			
		||||
        , debug: clientParams.debug
 | 
			
		||||
        })).then(function (result) {
 | 
			
		||||
          return result.originalData || result.data;
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return OAUTH3.hooks.grants.get(keyPair.sub, clientParams.client_uri).then(function (grant) {
 | 
			
		||||
      var now = Math.floor(Date.now()/1000);
 | 
			
		||||
      var payload = {
 | 
			
		||||
        iat: now
 | 
			
		||||
      , iss: providerUri
 | 
			
		||||
      , aud: clientParams.aud || providerUri
 | 
			
		||||
      , azp: clientParams.client_uri
 | 
			
		||||
      , sub: grant.azpSub
 | 
			
		||||
      , scope: OAUTH3.scope.stringify(grant.scope)
 | 
			
		||||
    , };
 | 
			
		||||
 | 
			
		||||
      var signProms = [];
 | 
			
		||||
      signProms.push(OAUTH3.jwt.sign(Object.assign({
 | 
			
		||||
        exp: calcExpiration(clientParams.exp || '1h', now)
 | 
			
		||||
      }, payload), keyPair));
 | 
			
		||||
      // if (clientParams.refresh_token) {
 | 
			
		||||
        signProms.push(OAUTH3.jwt.sign(Object.assign({
 | 
			
		||||
          exp: calcExpiration(clientParams.refresh_exp, now)
 | 
			
		||||
        }, payload), keyPair));
 | 
			
		||||
      // }
 | 
			
		||||
      return OAUTH3.PromiseA.all(signProms).then(function (tokens) {
 | 
			
		||||
        console.log('created new tokens for client');
 | 
			
		||||
        return {
 | 
			
		||||
          access_token: tokens[0]
 | 
			
		||||
        , refresh_token: tokens[1]
 | 
			
		||||
        , scope: OAUTH3.scope.stringify(grant.scope)
 | 
			
		||||
        , token_type: 'bearer'
 | 
			
		||||
        };
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  }).then(function (session) {
 | 
			
		||||
    // TODO limit refresh token to an expirable token
 | 
			
		||||
    // TODO inform client not to persist token
 | 
			
		||||
    OAUTH3.url.redirect(clientParams, scopes, session);
 | 
			
		||||
  }, function (err) {
 | 
			
		||||
    console.error('unexpected error creating client tokens', err);
 | 
			
		||||
    OAUTH3.url.redirect(clientParams, scopes, {error: err});
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
OAUTH3.requests = {};
 | 
			
		||||
OAUTH3.requests.accounts = {};
 | 
			
		||||
OAUTH3.requests.accounts.update = function (directive, session, opts) {
 | 
			
		||||
@ -512,25 +628,178 @@ OAUTH3.requests.accounts.create = function (directive, session, account) {
 | 
			
		||||
  , data: data
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
OAUTH3.hooks.grants = {
 | 
			
		||||
  // Provider Only
 | 
			
		||||
  set: function (clientUri, newGrants) {
 | 
			
		||||
    clientUri = OAUTH3.uri.normalize(clientUri);
 | 
			
		||||
    console.warn('[oauth3.hooks.setGrants] PLEASE IMPLEMENT -- Your Fault');
 | 
			
		||||
    console.warn(newGrants);
 | 
			
		||||
    if (!this._cache) { this._cache = {}; }
 | 
			
		||||
    console.log('clientUri, newGrants');
 | 
			
		||||
    console.log(clientUri, newGrants);
 | 
			
		||||
    this._cache[clientUri] = newGrants;
 | 
			
		||||
    return newGrants;
 | 
			
		||||
  get: function (id, clientUri) {
 | 
			
		||||
    OAUTH3.hooks._checkStorage('grants', 'get');
 | 
			
		||||
 | 
			
		||||
    if (!id) {
 | 
			
		||||
      throw new Error("id is not set");
 | 
			
		||||
    }
 | 
			
		||||
    if (!clientUri) {
 | 
			
		||||
      throw new Error("clientUri is not set");
 | 
			
		||||
    }
 | 
			
		||||
    return OAUTH3.PromiseA.resolve(OAUTH3._hooks.grants.get(id, OAUTH3.uri.normalize(clientUri)));
 | 
			
		||||
  }
 | 
			
		||||
, get: function (clientUri) {
 | 
			
		||||
    clientUri = OAUTH3.uri.normalize(clientUri);
 | 
			
		||||
    console.warn('[oauth3.hooks.getGrants] PLEASE IMPLEMENT -- Your Fault');
 | 
			
		||||
    if (!this._cache) { this._cache = {}; }
 | 
			
		||||
    console.log('clientUri, existingGrants');
 | 
			
		||||
    console.log(clientUri, this._cache[clientUri]);
 | 
			
		||||
    return this._cache[clientUri];
 | 
			
		||||
, set: function (id, clientUri, grants) {
 | 
			
		||||
    OAUTH3.hooks._checkStorage('grants', 'set');
 | 
			
		||||
 | 
			
		||||
    if (!id) {
 | 
			
		||||
      throw new Error("id is not set");
 | 
			
		||||
    }
 | 
			
		||||
    if (!clientUri) {
 | 
			
		||||
      throw new Error("clientUri is not set");
 | 
			
		||||
    }
 | 
			
		||||
    return OAUTH3.PromiseA.resolve(OAUTH3._hooks.grants.set(id, OAUTH3.uri.normalize(clientUri), grants));
 | 
			
		||||
  }
 | 
			
		||||
, all: function () {
 | 
			
		||||
    OAUTH3.hooks._checkStorage('grants', 'all');
 | 
			
		||||
 | 
			
		||||
    return OAUTH3.PromiseA.resolve(OAUTH3._hooks.grants.all());
 | 
			
		||||
  }
 | 
			
		||||
, clear: function () {
 | 
			
		||||
    OAUTH3.hooks._checkStorage('grants', 'clear');
 | 
			
		||||
 | 
			
		||||
    return OAUTH3.PromiseA.resolve(OAUTH3._hooks.grants.clear());
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
OAUTH3.hooks.keyPairs = {
 | 
			
		||||
  get: function (id) {
 | 
			
		||||
    OAUTH3.hooks._checkStorage('keyPairs', 'get');
 | 
			
		||||
 | 
			
		||||
    if (!id) {
 | 
			
		||||
      throw new Error("id is not set");
 | 
			
		||||
    }
 | 
			
		||||
    return OAUTH3.PromiseA.resolve(OAUTH3._hooks.keyPairs.get(id));
 | 
			
		||||
  }
 | 
			
		||||
, set: function (id, keyPair) {
 | 
			
		||||
    OAUTH3.hooks._checkStorage('keyPairs', 'set');
 | 
			
		||||
 | 
			
		||||
    if (!keyPair && id.privateKey && id.publicKey && id.sub) {
 | 
			
		||||
      keyPair = id;
 | 
			
		||||
      id = keyPair.sub;
 | 
			
		||||
    }
 | 
			
		||||
    if (!keyPair) {
 | 
			
		||||
      return OAUTH3.PromiseA.reject(new Error("no key pair provided to save"));
 | 
			
		||||
    }
 | 
			
		||||
    if (!id) {
 | 
			
		||||
      throw new Error("id is not set");
 | 
			
		||||
    }
 | 
			
		||||
    keyPair.sub = keyPair.sub || id;
 | 
			
		||||
 | 
			
		||||
    return OAUTH3.PromiseA.resolve(OAUTH3._hooks.keyPairs.set(id, keyPair));
 | 
			
		||||
  }
 | 
			
		||||
, all: function () {
 | 
			
		||||
    OAUTH3.hooks._checkStorage('keyPairs', 'all');
 | 
			
		||||
 | 
			
		||||
    return OAUTH3.PromiseA.resolve(OAUTH3._hooks.keyPairs.all());
 | 
			
		||||
  }
 | 
			
		||||
, clear: function () {
 | 
			
		||||
    OAUTH3.hooks._checkStorage('keyPairs', 'clear');
 | 
			
		||||
 | 
			
		||||
    return OAUTH3.PromiseA.resolve(OAUTH3._hooks.keyPairs.clear());
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
OAUTH3.hooks.session.get = function (providerUri, id) {
 | 
			
		||||
  OAUTH3.hooks._checkStorage('sessions', 'get');
 | 
			
		||||
  var sessProm = OAUTH3.PromiseA.resolve(OAUTH3._hooks.sessions.get(providerUri, id));
 | 
			
		||||
  if (providerUri !== OAUTH3.clientUri(window.location)) {
 | 
			
		||||
    return sessProm;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return sessProm.then(function (session) {
 | 
			
		||||
    if (session && OAUTH3.jwt.freshness(session.token) === 'fresh') {
 | 
			
		||||
      return session;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return OAUTH3.hooks.keyPairs.all().then(function (keyPairs) {
 | 
			
		||||
      var pair;
 | 
			
		||||
      if (id) {
 | 
			
		||||
        pair = keyPairs[id];
 | 
			
		||||
      } else if (Object.keys(keyPairs).length === 1) {
 | 
			
		||||
        id = Object.keys(keyPairs)[0];
 | 
			
		||||
        pair = keyPairs[id];
 | 
			
		||||
      } else if (Object.keys(keyPairs).length > 1) {
 | 
			
		||||
        console.error("too many users, don't know which key to use");
 | 
			
		||||
      }
 | 
			
		||||
      if (!pair) {
 | 
			
		||||
        // even if the access token isn't fresh, the session might have a refresh token
 | 
			
		||||
        return session;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      var now = Math.floor(Date.now()/1000);
 | 
			
		||||
      var payload = {
 | 
			
		||||
        iat: now
 | 
			
		||||
      , iss: providerUri
 | 
			
		||||
      , aud: providerUri
 | 
			
		||||
      , azp: providerUri
 | 
			
		||||
      , sub: pair.sub || id
 | 
			
		||||
      , scope: ''
 | 
			
		||||
      , exp: now + 3600
 | 
			
		||||
      };
 | 
			
		||||
      return OAUTH3.jwt.sign(payload, pair.privateKey).then(function (token) {
 | 
			
		||||
        console.log('created new token for provider');
 | 
			
		||||
        return OAUTH3.hooks.session.refresh(
 | 
			
		||||
          { provider_uri: providerUri, client_uri: providerUri || providerUri }
 | 
			
		||||
        , { access_token: token }
 | 
			
		||||
        );
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
OAUTH3._defaultStorage.grants = {
 | 
			
		||||
  prefix: 'grants-'
 | 
			
		||||
, get: function (id, clientUri) {
 | 
			
		||||
    var key = this.prefix + id+'/'+clientUri;
 | 
			
		||||
    var result = JSON.parse(window.localStorage.getItem(key) || 'null');
 | 
			
		||||
    return OAUTH3.PromiseA.resolve(result);
 | 
			
		||||
  }
 | 
			
		||||
, set: function (id, clientUri, grants) {
 | 
			
		||||
    var key = this.prefix + id+'/'+clientUri;
 | 
			
		||||
    window.localStorage.setItem(key, JSON.stringify(grants));
 | 
			
		||||
    return this.get(clientUri);
 | 
			
		||||
  }
 | 
			
		||||
, all: function () {
 | 
			
		||||
    var prefix = this.prefix;
 | 
			
		||||
    var result = {};
 | 
			
		||||
    OAUTH3._defaultStorage._getStorageKeys(prefix, window.localStorage).forEach(function (key) {
 | 
			
		||||
      var split = key.replace(prefix, '').split('/');
 | 
			
		||||
      if (!result[split[0]]) { result[split[0]] = {}; }
 | 
			
		||||
      result[split[0]][split[1]] = JSON.parse(window.localStorage.getItem(key) || 'null');
 | 
			
		||||
    });
 | 
			
		||||
    return OAUTH3.PromiseA.resolve(result);
 | 
			
		||||
  }
 | 
			
		||||
, clear: function () {
 | 
			
		||||
    OAUTH3._defaultStorage._getStorageKeys(this.prefix, window.localStorage).forEach(function (key) {
 | 
			
		||||
      window.localStorage.removeItem(key);
 | 
			
		||||
    });
 | 
			
		||||
    return OAUTH3.PromiseA.resolve();
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
OAUTH3._defaultStorage.keyPairs = {
 | 
			
		||||
  prefix: 'key_pairs-'
 | 
			
		||||
, get: function (id) {
 | 
			
		||||
    var result = JSON.parse(window.localStorage.getItem(this.prefix + id) || 'null');
 | 
			
		||||
    return OAUTH3.PromiseA.resolve(result);
 | 
			
		||||
  }
 | 
			
		||||
, set: function (id, keyPair) {
 | 
			
		||||
    window.localStorage.setItem(this.prefix + id, JSON.stringify(keyPair));
 | 
			
		||||
    return this.get(id);
 | 
			
		||||
  }
 | 
			
		||||
, all: function () {
 | 
			
		||||
    var prefix = this.prefix;
 | 
			
		||||
    var result = {};
 | 
			
		||||
    OAUTH3._defaultStorage._getStorageKeys(prefix, window.localStorage).forEach(function (key) {
 | 
			
		||||
      result[key.replace(prefix, '')] = JSON.parse(window.localStorage.getItem(key) || 'null');
 | 
			
		||||
    });
 | 
			
		||||
    return OAUTH3.PromiseA.resolve(result);
 | 
			
		||||
  }
 | 
			
		||||
, clear: function () {
 | 
			
		||||
    OAUTH3._defaultStorage._getStorageKeys(this.prefix, window.localStorage).forEach(function (key) {
 | 
			
		||||
      window.localStorage.removeItem(key);
 | 
			
		||||
    });
 | 
			
		||||
    return OAUTH3.PromiseA.resolve();
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -28,6 +28,7 @@ OAUTH3._base64.atob = function (base64) {
 | 
			
		||||
OAUTH3._base64.btoa = function (text) {
 | 
			
		||||
  return new Buffer(text, 'utf8').toString('base64');
 | 
			
		||||
};
 | 
			
		||||
OAUTH3._defaultStorage = require('./oauth3.node.storage');
 | 
			
		||||
 | 
			
		||||
OAUTH3._node = {};
 | 
			
		||||
OAUTH3._node.discover = function(providerUri/*, opts*/) {
 | 
			
		||||
 | 
			
		||||
@ -67,10 +67,9 @@ module.exports = {
 | 
			
		||||
 | 
			
		||||
, sessions: {
 | 
			
		||||
    all: function (providerUri) {
 | 
			
		||||
      var dirname = path.join(oauth3dir, 'sessions');
 | 
			
		||||
      return fs.readdirAsync(dirname).then(function (nodes) {
 | 
			
		||||
      return fs.readdirAsync(sessionsdir).then(function (nodes) {
 | 
			
		||||
        return nodes.map(function (node) {
 | 
			
		||||
          var result = require(path.join(dirname, node));
 | 
			
		||||
          var result = require(path.join(sessionsdir, node));
 | 
			
		||||
          if (result.link) {
 | 
			
		||||
            return null;
 | 
			
		||||
          }
 | 
			
		||||
@ -91,7 +90,7 @@ module.exports = {
 | 
			
		||||
          result = require(path.join(sessionsdir, providerUri + '.json'));
 | 
			
		||||
          // TODO make safer
 | 
			
		||||
          if (result.link && '/' !== result.link[0] && !/\.\./.test(result.link)) {
 | 
			
		||||
            result = require(path.join(oauth3dir, 'sessions', result.link));
 | 
			
		||||
            result = require(path.join(sessionsdir, result.link));
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      } catch(e) {
 | 
			
		||||
@ -113,10 +112,9 @@ module.exports = {
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  , clear: function () {
 | 
			
		||||
      var dirname = path.join(oauth3dir, 'sessions');
 | 
			
		||||
      return fs.readdirAsync(dirname).then(function (nodes) {
 | 
			
		||||
      return fs.readdirAsync(sessionsdir).then(function (nodes) {
 | 
			
		||||
        return PromiseA.all(nodes.map(function (node) {
 | 
			
		||||
          return fs.unlinkAsync(path.join(dirname, node));
 | 
			
		||||
          return fs.unlinkAsync(path.join(sessionsdir, node));
 | 
			
		||||
        }));
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,7 @@ OAUTH3.api['tunnel.token'] = function (providerUri, opts) {
 | 
			
		||||
  return OAUTH3.request({
 | 
			
		||||
    method: 'POST'
 | 
			
		||||
  , url: OAUTH3.url.normalize(providerUri)
 | 
			
		||||
      + '/api/org.oauth3.tunnel/accounts/' + session.token.sub + '/token'
 | 
			
		||||
      + '/api/tunnel@oauth3.org/accounts/' + session.token.sub + '/token'
 | 
			
		||||
  , session: session
 | 
			
		||||
  , data: {
 | 
			
		||||
      domains: opts.data.domains
 | 
			
		||||
 | 
			
		||||
@ -1,13 +1,12 @@
 | 
			
		||||
{ "terms": [ "oauth3.org/tos/draft" ]
 | 
			
		||||
, "api": "api.:hostname"
 | 
			
		||||
, "authorization_dialog": { "url": "#/authorization_dialog" }
 | 
			
		||||
, "access_token": { "method": "POST", "url": "api/issuer@oauth3.org/access_token" }
 | 
			
		||||
, "otp": { "method": "POST", "url": "api/issuer@oauth3.org/otp" }
 | 
			
		||||
, "credential_otp": { "method": "POST", "url": "api/issuer@oauth3.org/otp" }
 | 
			
		||||
, "credential_meta": { "url": "api/issuer@oauth3.org/logins/meta/:type/:id" }
 | 
			
		||||
, "credential_create": { "method": "POST", "url": "api/issuer@oauth3.org/logins" }
 | 
			
		||||
, "grants": { "method": "GET", "url": "api/issuer@oauth3.org/grants/:azp/:sub" }
 | 
			
		||||
, "authorization_decision": { "method": "POST", "url": "api/issuer@oauth3.org/authorization_decision" }
 | 
			
		||||
, "callback": { "method": "GET", "url": ".well-known/oauth3/callback.html#/" }
 | 
			
		||||
, "logout": { "method": "GET", "url": "#/logout/" }
 | 
			
		||||
, "access_token":   { "method": "POST", "url": "api/issuer@oauth3.org/access_token" }
 | 
			
		||||
, "otp":            { "method": "POST", "url": "api/issuer@oauth3.org/access_token/send_otp" }
 | 
			
		||||
, "credential_otp": { "method": "POST", "url": "api/issuer@oauth3.org/access_token/send_otp" }
 | 
			
		||||
, "grants":         { "method": "GET",  "url": "api/issuer@oauth3.org/grants/:sub/:azp" }
 | 
			
		||||
, "publish_jwk":    { "method": "POST", "url": "api/issuer@oauth3.org/jwks/:sub" }
 | 
			
		||||
, "retrieve_jwk":   { "method": "GET",  "url": "api/issuer@oauth3.org/jwks/:sub/:kid.json" }
 | 
			
		||||
, "callback":       { "method": "GET",  "url": ".well-known/oauth3/callback.html#/" }
 | 
			
		||||
, "logout":         { "method": "GET",  "url": "#/logout/" }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user