Compare commits
	
		
			No commits in common. "master" and "v1.0.2" have entirely different histories.
		
	
	
		
	
		
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -1,5 +1 @@
 | 
			
		||||
prefactor
 | 
			
		||||
.well-known
 | 
			
		||||
node_modules/
 | 
			
		||||
DS_Store
 | 
			
		||||
.vscode
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										16
									
								
								.jshintrc
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								.jshintrc
									
									
									
									
									
								
							@ -1,16 +0,0 @@
 | 
			
		||||
{ "node": true
 | 
			
		||||
, "browser": true
 | 
			
		||||
, "jquery": true
 | 
			
		||||
, "strict": true
 | 
			
		||||
, "indent": 2
 | 
			
		||||
, "onevar": true
 | 
			
		||||
, "laxcomma": true
 | 
			
		||||
, "laxbreak": true
 | 
			
		||||
, "eqeqeq": true
 | 
			
		||||
, "immed": true
 | 
			
		||||
, "undef": true
 | 
			
		||||
, "unused": true
 | 
			
		||||
, "latedef": true
 | 
			
		||||
, "curly": true
 | 
			
		||||
, "trailing": true
 | 
			
		||||
}
 | 
			
		||||
@ -1 +1 @@
 | 
			
		||||
_apis
 | 
			
		||||
well-known
 | 
			
		||||
@ -1,7 +0,0 @@
 | 
			
		||||
v1.2.2 - Works in browsers and node.js for some oauth3 exchanges
 | 
			
		||||
  * Resource Owner Password
 | 
			
		||||
  * Implicit Grant
 | 
			
		||||
  * Client-side public/private keypair generation
 | 
			
		||||
  * Server-side public key authentication
 | 
			
		||||
  * Server-side grant storage
 | 
			
		||||
  * BUG: Does not support app:// urls
 | 
			
		||||
							
								
								
									
										41
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								LICENSE
									
									
									
									
									
								
							@ -1,41 +0,0 @@
 | 
			
		||||
Copyright 2017 Daplie, Inc
 | 
			
		||||
 | 
			
		||||
This is open source software; you can redistribute it and/or modify it under the
 | 
			
		||||
terms of either:
 | 
			
		||||
 | 
			
		||||
   a) the "MIT License"
 | 
			
		||||
   b) the "Apache-2.0 License"
 | 
			
		||||
 | 
			
		||||
MIT License
 | 
			
		||||
 | 
			
		||||
   Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
   of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
   in the Software without restriction, including without limitation the rights
 | 
			
		||||
   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
   copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
   furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
   The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
   copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
   SOFTWARE.
 | 
			
		||||
 | 
			
		||||
Apache-2.0 License Summary
 | 
			
		||||
 | 
			
		||||
   Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
   you may not use this file except in compliance with the License.
 | 
			
		||||
   You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
   Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
   distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
   See the License for the specific language governing permissions and
 | 
			
		||||
   limitations under the License.
 | 
			
		||||
							
								
								
									
										158
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										158
									
								
								README.md
									
									
									
									
									
								
							@ -1,12 +1,6 @@
 | 
			
		||||
oauth3.js
 | 
			
		||||
=========
 | 
			
		||||
 | 
			
		||||
| *oauth3.js*
 | 
			
		||||
| [issuer.html](https://git.oauth3.org/OAuth3/issuer.html)
 | 
			
		||||
| [issuer.rest.walnut.js](https://git.oauth3.org/OAuth3/issuer.rest.walnut.js)
 | 
			
		||||
| [issuer.srv](https://git.oauth3.org/OAuth3/issuer.srv)
 | 
			
		||||
| Sponsored by [ppl](https://ppl.family)
 | 
			
		||||
 | 
			
		||||
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!)
 | 
			
		||||
 | 
			
		||||
@ -25,12 +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 `oauth3.org/`
 | 
			
		||||
4. Download [oauth3.js-v1.zip](https://git.oauth3.org/OAuth3/oauth3.js/repository/archive.zip?ref=v1)
 | 
			
		||||
3. Inside of the folder `example.com/assets` a folder called `org.oauth3/`
 | 
			
		||||
4. Download [oauth.js-v1.zip](https://git.daplie.com/Daplie/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/oauth3.org/`
 | 
			
		||||
7. Copy the folder `_apis` into the folder `example.com/`
 | 
			
		||||
9. Add `<script src="assets/oauth3.org/oauth3.core.js"></script>` to your `index.html`
 | 
			
		||||
6. Copy the file `oauth3.core.js` into the folder `example.com/assets/org.oauth3/`
 | 
			
		||||
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="app.js"></script>` to your `index.html`
 | 
			
		||||
10. Create files in `example.com` called `app.js` and `index.html` and put this in it:
 | 
			
		||||
 | 
			
		||||
@ -49,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/oauth3.org/oauth3.core.js"></script>
 | 
			
		||||
  <script src="assets/org.oauth3/oauth3.core.js"></script>
 | 
			
		||||
  <script src="app.js"></script>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
@ -58,15 +53,15 @@ If you have no idea what you're doing
 | 
			
		||||
`app.js`:
 | 
			
		||||
```js
 | 
			
		||||
var OAUTH3 = window.OAUTH3;
 | 
			
		||||
var oauth3 = OAUTH3.create(window.location); // use window.location to set Client URI (your app's id)
 | 
			
		||||
var auth = OAUTH3.create(window.location); // use window.location to set Client URI (your app's id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// 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) {
 | 
			
		||||
function onChangeProvider(_providerUri) {
 | 
			
		||||
  // example https://oauth3.org
 | 
			
		||||
  return oauth3.setIdentityProvider(providerUri);
 | 
			
		||||
  return auth.setProvider(providerUri);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -74,7 +69,7 @@ function onChangeProvider(providerUri) {
 | 
			
		||||
//
 | 
			
		||||
function onClickLogin() {
 | 
			
		||||
 | 
			
		||||
  return oauth3.authenticate().then(function (session) {
 | 
			
		||||
  return auth.authenticate().then(function (session) {
 | 
			
		||||
 | 
			
		||||
    console.info('Authentication was Successful:');
 | 
			
		||||
    console.log(session);
 | 
			
		||||
@ -85,14 +80,12 @@ function onClickLogin() {
 | 
			
		||||
    //
 | 
			
		||||
    console.info('Secure PPID (aka subject):', session.token.sub);
 | 
			
		||||
 | 
			
		||||
    return oauth3.request({
 | 
			
		||||
      url: 'https://api.oauth3.org/api/issuer@oauth3.org/jwks/:sub/:kid'
 | 
			
		||||
        .replace(/:sub/g, session.token.sub)
 | 
			
		||||
        .replace(/:kid/g, session.token.kid || session.token.iss)
 | 
			
		||||
    return auth.request({
 | 
			
		||||
      url: 'https://oauth3.org/api/org.oauth3.provider/inspect'
 | 
			
		||||
    , session: session
 | 
			
		||||
    }).then(function (resp) {
 | 
			
		||||
 | 
			
		||||
      console.info("Signing Public Key JWK:");
 | 
			
		||||
      console.info("Inspect Token:");
 | 
			
		||||
      console.log(resp.data);
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
@ -109,7 +102,7 @@ function onClickLogin() {
 | 
			
		||||
//
 | 
			
		||||
function onClickLogout() {
 | 
			
		||||
 | 
			
		||||
  return oauth3.logout().then(function () {
 | 
			
		||||
  return auth.logout().then(function () {
 | 
			
		||||
    localStorage.clear();
 | 
			
		||||
 | 
			
		||||
    console.info('Logout was Successful');
 | 
			
		||||
@ -145,13 +138,13 @@ it might look like this:
 | 
			
		||||
example.com
 | 
			
		||||
│
 | 
			
		||||
│
 | 
			
		||||
├── _apis
 | 
			
		||||
│   └── oauth3.org
 | 
			
		||||
├── .well-known (hidden)
 | 
			
		||||
│   └── oauth3
 | 
			
		||||
│       ├── callback.html
 | 
			
		||||
│       ├── directives.json
 | 
			
		||||
│       └── index.html
 | 
			
		||||
├── assets
 | 
			
		||||
│   └── oauth3.org
 | 
			
		||||
│   └── org.oauth3
 | 
			
		||||
│       └── oauth3.core.js
 | 
			
		||||
│
 | 
			
		||||
│
 | 
			
		||||
@ -172,17 +165,17 @@ Installation (if you know what you're doing)
 | 
			
		||||
pushd /path/to/your/web/app
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# clone the project as assets/oauth3.org
 | 
			
		||||
# clone the project as assets/org.oauth3
 | 
			
		||||
mkdir -p assets
 | 
			
		||||
git clone git@git.oauth3.org:OAuth3/oauth3.js.git assets/oauth3.org
 | 
			
		||||
pushd assets/oauth3.org
 | 
			
		||||
git clone git@git.daplie.com:Daplie/oauth3.js.git assets/org.oauth3
 | 
			
		||||
pushd assets/org.oauth3
 | 
			
		||||
git checkout v1
 | 
			
		||||
popd
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# symlink `_apis/oauth3.org` to `assets/oauth3.org/_apis/oauth3.org`
 | 
			
		||||
mkdir -p _apis
 | 
			
		||||
ln -sf  ../assets/oauth3.org/_apis/oauth3 _apis/oauth3.org
 | 
			
		||||
# 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`**
 | 
			
		||||
@ -192,17 +185,17 @@ ln -sf  ../assets/oauth3.org/_apis/oauth3 _apis/oauth3.org
 | 
			
		||||
bower install oauth3
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# create a `_apis` folder and an `assets` folder
 | 
			
		||||
mkdir -p _apis assets
 | 
			
		||||
# create a `.well-known` folder and an `assets` folder
 | 
			
		||||
mkdir -p .well-known assets
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# symlink `_apis/oauth3.org` to `bower_components/oauth3.org/_apis/oauth3.org`
 | 
			
		||||
ln -sf  ../bower_components/oauth3.org/_apis/oauth3.org _apis/oauth3.org
 | 
			
		||||
# symlink `.well-known/oauth3` to `bower_components/oauth3/.well-known/oauth3`
 | 
			
		||||
ln -sf  ../bower_components/oauth3/.well-known/oauth3 .well-known/oauth3
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# symlink `assets/oauth3.org` to `bower_components/oauth3.org`
 | 
			
		||||
ln -sf  ../bower_components/oauth3.org/_apis/oauth3.org _apis/oauth3.org
 | 
			
		||||
ln -sf  ../bower_components/oauth3.org assets/oauth3.org
 | 
			
		||||
# 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
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Usage
 | 
			
		||||
@ -211,7 +204,7 @@ Usage
 | 
			
		||||
Update your HTML to include the the following script tag:
 | 
			
		||||
 | 
			
		||||
```html
 | 
			
		||||
<script src="assets/oauth3.org/oauth3.core.js"></script>
 | 
			
		||||
<script src="assets/org.oauth3/oauth3.core.js"></script>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
You can create a very simple demo application like this:
 | 
			
		||||
@ -246,7 +239,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/org.oauth3.provider/inspect_token'
 | 
			
		||||
    , session: session
 | 
			
		||||
    }).then(function (resp) {
 | 
			
		||||
 | 
			
		||||
@ -267,18 +260,6 @@ function onClickLogin() {
 | 
			
		||||
onChangeProvider('oauth3.org');
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
A user's e-mail can be passed into the clientParams object as `clientParams.subject`.
 | 
			
		||||
 | 
			
		||||
To auto-populate the e-mail input of the login popup, make sure the input has the class `js-oauth3-email`.
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
```js
 | 
			
		||||
if (clientParams.subject) {
 | 
			
		||||
        $('.js-oauth3-email').val(clientParams.subject);
 | 
			
		||||
        $('.js-authn-show').prop('disabled', false);
 | 
			
		||||
      }
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Compatibility with Frameworks and Libraries
 | 
			
		||||
 | 
			
		||||
**jQuery**:
 | 
			
		||||
@ -290,19 +271,7 @@ You're all set. Nothing else is needed.
 | 
			
		||||
We've created an `Oauth3` service just for you:
 | 
			
		||||
 | 
			
		||||
```html
 | 
			
		||||
<script src="assets/oauth3.org/oauth3.ng.js"></script>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
// Require the module as 'oauth3.org'
 | 
			
		||||
var app = angular.module('myAppName', [ 'ui.router', 'oauth3.org' ]);
 | 
			
		||||
 | 
			
		||||
// Require services and other submodules in the form {modulename}@oauth3.org
 | 
			
		||||
app.controller('authCtrl', [ '$scope', 'azp@oauth3.org', function ($scope, Oauth3) { /* ... */ } ]);
 | 
			
		||||
 | 
			
		||||
// For backwards compatibility with older angular applications that rely on string-name introspection
 | 
			
		||||
// you can also use the camel case version of the names in the format {Modulename}Oauth3
 | 
			
		||||
app.controller('authCtrl', function ($scope, AzpOauth3) { /* ... */ });
 | 
			
		||||
<script src="assets/org.oauth3/oauth3.ng.js"></script>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
You can include that in addition to the standard file or,
 | 
			
		||||
@ -315,48 +284,31 @@ We include a small wrapper function of just a few lines in the bottom of `oauth3
 | 
			
		||||
which exposes a `create` method to make using the underlying library require typing fewer keystrokes.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
oauth3 = OAUTH3.create(location);                   // takes a location object, such as window.location
 | 
			
		||||
                                                    // to create the Client URI (your app's id)
 | 
			
		||||
                                                    // and save it to an internal state
 | 
			
		||||
auth = OAUTH3.create(location);                   // takes a location object, such as window.location
 | 
			
		||||
                                                  // to create the Client URI (your app's id)
 | 
			
		||||
                                                  // and save it to an internal state
 | 
			
		||||
 | 
			
		||||
promise = oauth3.init(opts);                        // set and fetch your own site/app's configuration details
 | 
			
		||||
// promises your site's config                      // opts = { location, session, issuer, audience }
 | 
			
		||||
promise = auth.init(location);                    // set and fetch your own site/app's configuration details
 | 
			
		||||
// promises your site's config
 | 
			
		||||
 | 
			
		||||
promise = oauth3.setIdentityProvider(url);          // changes the Identity Provider URI (the site you're logging into),
 | 
			
		||||
// promises the provider's config                   // gets the config for that site (from their _apis/oauth3.org),
 | 
			
		||||
                                                    // and caches it in internal state as the default
 | 
			
		||||
promise = auth.setProvider(url);                  // changes the Provider URI (the site you're logging into),
 | 
			
		||||
// promises the provider's config                 // gets the config for that site (from their .well-known/oauth3),
 | 
			
		||||
                                                  // and caches it in internal state as the default
 | 
			
		||||
 | 
			
		||||
promise = oauth3.setResourceProvider(url);          // changes the Resource Provider URI (the site you're getting stuff from)
 | 
			
		||||
promise = auth.authenticate();                    // opens login window for the provider and returns a session
 | 
			
		||||
                                                  // (must be called after the setProvider promise has completed)
 | 
			
		||||
 | 
			
		||||
promise = oauth3.setProvider(url);                  // changes the both Identity and Resource Provider URI together
 | 
			
		||||
promise = auth.authorize(permissions);            // authenticates (if not authenticated) and opens a window to
 | 
			
		||||
                                                  // authorize a particular scope (contacts, photos, whatever)
 | 
			
		||||
 | 
			
		||||
promise = oauth3.authenticate();                    // opens login window for the provider and returns a session
 | 
			
		||||
                                                    // (must be called after the setIdentityProvider promise has completed)
 | 
			
		||||
promise = auth.request({ url, method, data });    // make an (authorized) request to a provider's resource
 | 
			
		||||
                                                  // (contacts, photos, whatever)
 | 
			
		||||
 | 
			
		||||
promise = oauth3.authorize(permissions);            // authenticates (if not authenticated) and opens a window to
 | 
			
		||||
                                                    // authorize a particular scope (contacts, photos, whatever)
 | 
			
		||||
promise = auth.logout();                          // opens logout window for the provider
 | 
			
		||||
 | 
			
		||||
promise = oauth3.request({ url, method, data });    // make an (authorized) arbitrary request to an audience's resource
 | 
			
		||||
                                                    // (contacts, photos, whatever)
 | 
			
		||||
 | 
			
		||||
promise = oauth3.api(apiname, opts);                // make an (authorized) well-known api call to an audience
 | 
			
		||||
                                                    // Ex: oauth3.api('dns.list', { sld: 'example', tld: 'com' });
 | 
			
		||||
 | 
			
		||||
// TODO
 | 
			
		||||
api = await oauth3.package(audience, schemaname);   // make an (authorized) well-known api call to an audience
 | 
			
		||||
                                                    // Ex: api = await oauth3.package('domains.example.com', 'dns@oauth3.org');
 | 
			
		||||
                                                    //     api.list({ sld: 'mydomain', tld: 'com' });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
promise = oauth3.logout();                          // opens logout window for the provider
 | 
			
		||||
 | 
			
		||||
oauth3.session();                                   // returns the current session, if any
 | 
			
		||||
auth.session();                                   // returns the current session, if any
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
<!-- TODO
 | 
			
		||||
Track down the old https://labs.daplie.com/docs/ for API schemas
 | 
			
		||||
--
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Real API
 | 
			
		||||
----------
 | 
			
		||||
@ -484,10 +436,10 @@ 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/issuer@oauth3.org
 | 
			
		||||
2. example.com/api/issuer@oauth3.org/ (not unique)
 | 
			
		||||
3. /api/issuer@oauth3.org
 | 
			
		||||
4. api/issuer@oauth3.org (not unique)
 | 
			
		||||
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.
 | 
			
		||||
@ -498,5 +450,5 @@ 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 (eliminate #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/_apis/oauth3.org/index.json then /api/whatever would refer
 | 
			
		||||
https://example.com/username/.well-known/oauth3/directives.json then /api/whatever would refer
 | 
			
		||||
to https://example.com/username/api/whatever.
 | 
			
		||||
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 43 B  | 
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 43 B  | 
@ -1,12 +0,0 @@
 | 
			
		||||
{ "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/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/" }
 | 
			
		||||
}
 | 
			
		||||
@ -1,140 +0,0 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
  <head>
 | 
			
		||||
    <style>
 | 
			
		||||
      body {
 | 
			
		||||
        background-color: #ffcccc;
 | 
			
		||||
      }
 | 
			
		||||
    </style>
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
  OAuth3 RPC
 | 
			
		||||
 | 
			
		||||
  <script src="../../assets/oauth3.org/oauth3.core.js"></script>
 | 
			
		||||
  <script>
 | 
			
		||||
    ;(function () {
 | 
			
		||||
    'use strict';
 | 
			
		||||
 | 
			
		||||
    // Taken from oauth3.core.js
 | 
			
		||||
 | 
			
		||||
    // TODO what about search within hash?
 | 
			
		||||
    var prefix = "(" + window.location.hostname + ") [.well-known/oauth3/]";
 | 
			
		||||
    var params = OAUTH3.query.parse(window.location.hash || window.location.search);
 | 
			
		||||
    var urlsafe64;
 | 
			
		||||
    var redirect;
 | 
			
		||||
    var err;
 | 
			
		||||
    var oldRpc;
 | 
			
		||||
    var sub = params.sub || params.subject;
 | 
			
		||||
    var subData;
 | 
			
		||||
 | 
			
		||||
    function doRedirect(redirect) {
 | 
			
		||||
      if (params.debug) {
 | 
			
		||||
        console.log(prefix, 'params.redirect_uri:', params.redirect_uri);
 | 
			
		||||
        console.log(prefix, 'redirect');
 | 
			
		||||
        console.log(redirect);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!params.debug) {
 | 
			
		||||
        window.location = redirect;
 | 
			
		||||
      } else {
 | 
			
		||||
        // yes, we're violating the security lint with purpose
 | 
			
		||||
        document.body.innerHTML += window.location.host + window.location.pathname
 | 
			
		||||
          + '<br/><br/>You\'ve passed the \'debug\' parameter so we\'re pausing'
 | 
			
		||||
          + ' to let you look at logs or whatever it is that you intended to do.'
 | 
			
		||||
          + '<br/><br/>Continue with redirect: <a href="' + redirect + '">' + redirect + '</' + 'a>';
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function onError(err) {
 | 
			
		||||
      var redirect = params.redirect_uri + '?' + OAUTH3.query.stringify({
 | 
			
		||||
        state: params.state
 | 
			
		||||
      , error: err.code
 | 
			
		||||
      , error_description: err.message
 | 
			
		||||
      , error_uri: err.uri
 | 
			
		||||
      , debug: params.debug || undefined
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      doRedirect(redirect);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function onSuccess(urlsafe64, hasSub) {
 | 
			
		||||
      if (params.debug) {
 | 
			
		||||
        console.log(prefix, 'directives');
 | 
			
		||||
        console.log(resp);
 | 
			
		||||
 | 
			
		||||
        console.log(prefix, 'base64');
 | 
			
		||||
        console.log(urlsafe64);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // TODO try postMessage back to redirect_uri domain right here
 | 
			
		||||
      // window.postMessage();
 | 
			
		||||
 | 
			
		||||
      // TODO SECURITY make sure it's https NOT http
 | 
			
		||||
      // NOTE: this can be only up to 2,083 characters
 | 
			
		||||
      redirect = params.redirect_uri + '?' + OAUTH3.query.stringify({
 | 
			
		||||
        state: params.state
 | 
			
		||||
      , directives: oldRpc ? urlsafe64 : undefined
 | 
			
		||||
      , data: !oldRpc ? urlsafe64 : undefined
 | 
			
		||||
      , sub: hasSub && sub || undefined
 | 
			
		||||
      , debug: params.debug || undefined
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      doRedirect(redirect);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (params.debug) {
 | 
			
		||||
      console.warn(prefix, "DEBUG MODE ENABLED. Automatic redirects disabled.");
 | 
			
		||||
 | 
			
		||||
      console.log(prefix, 'hash||search:');
 | 
			
		||||
      console.log(window.location.hash || window.location.search);
 | 
			
		||||
 | 
			
		||||
      console.log(prefix, 'params:');
 | 
			
		||||
      console.log(params);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ('rpc' !== params.response_type) {
 | 
			
		||||
      err = new Error("response_type '" + params.response_type + "' is not supported");
 | 
			
		||||
      err.code = "E_RESPONSE_TYPE";
 | 
			
		||||
      // TODO err.uri
 | 
			
		||||
      onError(err);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (params.action) {
 | 
			
		||||
      oldRpc = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var loco = window.location.href.replace(/\/\.well-known.*/, '');
 | 
			
		||||
    //var loco = 'sso.hellabit.com';
 | 
			
		||||
    var resp;
 | 
			
		||||
    if (/localstorage/i.test(params._scheme)) {
 | 
			
		||||
      if (sub) {
 | 
			
		||||
        subData = localStorage.getItem(sub + '@oauth3.org:issuer');
 | 
			
		||||
      }
 | 
			
		||||
      resp = subData || localStorage.getItem('oauth3.org:issuer') || loco;
 | 
			
		||||
      onSuccess(resp, subData && true);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var fileWhiteList = [
 | 
			
		||||
      '.well-known/oauth3/directives.json'
 | 
			
		||||
    , '.well-known/oauth3/scopes.json'
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    if (-1 === fileWhiteList.indexOf(params._pathname)) {
 | 
			
		||||
      err = new Error("No access to requested file: " + params._pathname);
 | 
			
		||||
      err.code = "E_ACCESS_DENIED"
 | 
			
		||||
      // TODO err.uri
 | 
			
		||||
      onError(err);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    OAUTH3.request({ url: params._pathname.replace(/^\.well-known\/oauth3\//, '') }).then(function (resp) {
 | 
			
		||||
      urlsafe64 = OAUTH3._base64.encodeUrlSafe(JSON.stringify(resp.data, null, 0));
 | 
			
		||||
 | 
			
		||||
      onSuccess(urlsafe64);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    }());
 | 
			
		||||
  </script>
 | 
			
		||||
  </body>
 | 
			
		||||
</html>
 | 
			
		||||
@ -1,26 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
      "oauth3_authn": "Basic secure authentication"
 | 
			
		||||
    , "auth@oauth3.org": "Basic secure authentication"
 | 
			
		||||
    , "wallet": "Access to payments and subscriptions"
 | 
			
		||||
    , "bucket": "Access to file storage"
 | 
			
		||||
    , "db": "Access to app data"
 | 
			
		||||
    , "domains": "Domain registration (and Glue and NS records)"
 | 
			
		||||
    , "domains@oauth3.org": "Domain registration (and Glue and NS records)" 
 | 
			
		||||
    , "domains:glue": "Glue Record management (for vanity nameservers)"
 | 
			
		||||
    , "domains:ns": "Name Server management"
 | 
			
		||||
    , "dns": "DNS records (A/AAAA, TXT, SRV, MX, etc)"
 | 
			
		||||
 | 
			
		||||
    , "hello@example.com": "Hello World Example Access"
 | 
			
		||||
    , "authn@oauth3.org": "Basic secure authentication"
 | 
			
		||||
    , "wallet@oauth3.org": "Access to payments and subscriptions"
 | 
			
		||||
    , "bucket@oauth3.org": "Access to file storage"
 | 
			
		||||
    , "db@oauth3.org": "Access to app data"
 | 
			
		||||
    , "domains@oauth3.org": "Domain registration (and Glue and NS records)" 
 | 
			
		||||
    , "domains:glue@oauth3.org": "Glue Record management (for vanity nameservers)"
 | 
			
		||||
    , "domains:ns@oauth3.org": "Name Server management"
 | 
			
		||||
    , "dns@oauth3.org": "DNS records (A/AAAA, TXT, SRV, MX, etc)"
 | 
			
		||||
    , "www@daplie.com": "Websites and webapps"
 | 
			
		||||
 | 
			
		||||
    , "*": "FULL ACCOUNT ACCESS"
 | 
			
		||||
    }
 | 
			
		||||
							
								
								
									
										217
									
								
								bin/cli.js
									
									
									
									
									
								
							
							
						
						
									
										217
									
								
								bin/cli.js
									
									
									
									
									
								
							@ -1,217 +0,0 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
var oauth3 = require('./oauth3.js');
 | 
			
		||||
var defaults = {
 | 
			
		||||
  main: 'oauth3'
 | 
			
		||||
, provider: 'oauth3.org'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function parseArgs(argv, opts) {
 | 
			
		||||
  var args = Array.prototype.slice.call(argv);
 | 
			
		||||
  var sep = /[:\.\-]/;
 | 
			
		||||
 | 
			
		||||
  args.shift(); // 'node' is the first parameter
 | 
			
		||||
  args.shift(); // 'oauth3.js' will be the
 | 
			
		||||
 | 
			
		||||
  var command = args.shift() || 'help';
 | 
			
		||||
  var cmdpair = command.split(sep);
 | 
			
		||||
  var cmd = cmdpair[0];
 | 
			
		||||
  var sub = cmdpair[1];
 | 
			
		||||
  var COMMAND = 'COMMAND';
 | 
			
		||||
  var maxCmdLen = COMMAND.length;
 | 
			
		||||
  var maxPairLen = 0;
 | 
			
		||||
  var arg1 = args[0];
 | 
			
		||||
 | 
			
		||||
  // build top-level commands (tlcs) list
 | 
			
		||||
  // also count the word-width (for the space needed to print the commands)
 | 
			
		||||
  var pairsMap = {};
 | 
			
		||||
  var tlcs = opts.commands.filter(function (desc) {
 | 
			
		||||
    var pair = desc[0].split(/\s+/)[0];
 | 
			
		||||
    var psub = pair.split(sep)[0];
 | 
			
		||||
    pairsMap[pair] = true;
 | 
			
		||||
    maxPairLen = Math.max(maxPairLen, pair.length);
 | 
			
		||||
    if (pair === psub) {
 | 
			
		||||
      maxCmdLen = Math.max(maxCmdLen, psub.length);
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // right pad (for making the printed lines longer)
 | 
			
		||||
  function rpad(str, len) {
 | 
			
		||||
    while (str.length < len) {
 | 
			
		||||
      str += ' ';
 | 
			
		||||
    }
 | 
			
		||||
    return str;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // oauth3.js help
 | 
			
		||||
  // oauth3.js help <command>
 | 
			
		||||
  // oauth3.js help <command:sub> (alias of `oauth3.js <command:sub> --help')
 | 
			
		||||
  function help() {
 | 
			
		||||
    var status = 0;
 | 
			
		||||
 | 
			
		||||
    function printCmd(desc) {
 | 
			
		||||
      var pcmd = rpad(desc[0].split(/\s+/)[0], maxCmdLen);
 | 
			
		||||
      var pdesc = desc[1];
 | 
			
		||||
      console.info('\t' + defaults.main + ' ' + pcmd, ' # ' + pdesc);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function printCmds(cmds) {
 | 
			
		||||
      console.info('');
 | 
			
		||||
 | 
			
		||||
      var title = defaults.main + ' ' + rpad(COMMAND, maxCmdLen) + '  # description';
 | 
			
		||||
      var bars = title.replace(/./g, '-').split('');
 | 
			
		||||
      bars[bars.length - ' # description'.length] = ' ';
 | 
			
		||||
      bars[bars.length - (' # description'.length + 1)] = ' ';
 | 
			
		||||
      console.info('\t' + title);
 | 
			
		||||
      console.info('\t' + bars.join(''));
 | 
			
		||||
      cmds.forEach(printCmd);
 | 
			
		||||
      console.info('');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function helpMain() {
 | 
			
		||||
      console.info('');
 | 
			
		||||
      console.info('Here are all the top-level commands:');
 | 
			
		||||
 | 
			
		||||
      printCmds(tlcs);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (arg1 && -1 === Object.keys(pairsMap).indexOf(arg1)) {
 | 
			
		||||
      status = 1;
 | 
			
		||||
      console.info('');
 | 
			
		||||
      console.info(defaults.main + ": Unknown command '" + arg1 + "'");
 | 
			
		||||
      console.info('');
 | 
			
		||||
      console.info("Try '" + defaults.main + " help'");
 | 
			
		||||
      console.info('');
 | 
			
		||||
      arg1 = null;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // the case of "oauth3 help --something"
 | 
			
		||||
    if (!arg1 || '-' === arg1[0]) {
 | 
			
		||||
      helpMain();
 | 
			
		||||
      process.exit(status);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // the case of "oauth3 help help"
 | 
			
		||||
    if ('help' === arg1) {
 | 
			
		||||
      helpMain();
 | 
			
		||||
      console.info("no more help available for 'help'");
 | 
			
		||||
      process.exit(status);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // matches the first part of the command
 | 
			
		||||
    // and has second parts
 | 
			
		||||
    if (arg1 === arg1.split(':')[0] && opts.commands.filter(function (desc) {
 | 
			
		||||
      return arg1 === desc[0].split(/\s+/)[0].split(':')[0] && desc[0].split(/\s+/)[0].split(':');
 | 
			
		||||
    }).length > 1) {
 | 
			
		||||
      console.info('');
 | 
			
		||||
      console.info("Here are all the '" + command + "'-related commands:");
 | 
			
		||||
      printCmds(
 | 
			
		||||
        opts.commands.filter(function (desc) {
 | 
			
		||||
          var pair = desc[0].split(/\s+/)[0];
 | 
			
		||||
          var psub = pair.split(sep)[0];
 | 
			
		||||
          maxPairLen = Math.max(maxPairLen, pair.length);
 | 
			
		||||
          if (arg1 === psub || arg1 === pair) {
 | 
			
		||||
            maxCmdLen = Math.max(maxCmdLen, pair.length);
 | 
			
		||||
            return true;
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
      console.info('');
 | 
			
		||||
    } else {
 | 
			
		||||
      console.info('');
 | 
			
		||||
      console.info("Here are all the options and flags for '" + arg1 + "':");
 | 
			
		||||
      console.info('');
 | 
			
		||||
      opts.commands.some(function (desc) {
 | 
			
		||||
        var pair = desc[0].split(/\s+/)[0];
 | 
			
		||||
        var psub = pair.split(sep)[0];
 | 
			
		||||
        maxPairLen = Math.max(maxPairLen, pair.length);
 | 
			
		||||
        if (arg1 !== psub && arg1 !== pair) {
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
        maxCmdLen = Math.max(maxCmdLen, pair.length);
 | 
			
		||||
        console.log('\t' + desc[0] + '\t# ' + desc[1]);
 | 
			
		||||
        (desc[2]||[]).forEach(function (flag) {
 | 
			
		||||
          var pair = flag.split(', ');
 | 
			
		||||
          var f = pair.shift();
 | 
			
		||||
          var d = pair.join(', ');
 | 
			
		||||
          console.log('\t\t' + f + ' # ' + d);
 | 
			
		||||
        });
 | 
			
		||||
        return true;
 | 
			
		||||
      });
 | 
			
		||||
      console.info('');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // If the command is not in the list of commands
 | 
			
		||||
  if (-1 === Object.keys(pairsMap).indexOf(cmd)) {
 | 
			
		||||
    arg1 = cmd;
 | 
			
		||||
    cmd = 'help';
 | 
			
		||||
    help();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // If help is explictly requested
 | 
			
		||||
  if (-1 !== [ 'help', '-h', '--help' ].indexOf(command) || -1 !== args.indexOf('-h') || -1 !== args.indexOf('--help')) {
 | 
			
		||||
    help();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // If we're ready to rock and roll!
 | 
			
		||||
  console.log('RUN', cmd, sub || '(n/a)', arg1 || '(n/a)', '... not yet implemented');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
parseArgs(process.argv, {
 | 
			
		||||
  // CLI goals:
 | 
			
		||||
  //
 | 
			
		||||
  // whoami / login: you are now logged in as
 | 
			
		||||
  //   * john@example.com [current] (just now)
 | 
			
		||||
  //   * john@work.net (2 minutes ago)
 | 
			
		||||
  //   * john@family.me (2 weeks ago)
 | 
			
		||||
  commands: [
 | 
			
		||||
    [ 'login [email or cloud address]', 'alias of session:attach', [
 | 
			
		||||
        "--auto, create a new account without asking if none exists"
 | 
			
		||||
      //, "--exclusive, logout all other ids, removing access to their accounts"
 | 
			
		||||
      , "--provider, specify an authentication provider (default: :provider)".replace(/\b:provider\b/, defaults.provider)
 | 
			
		||||
      //, "--email [addr], use the given id as an email address, even if it works as a cloud address"
 | 
			
		||||
      //, "--cloud [addr], use the given id as a cloud address or fail (don't fallback to email)"
 | 
			
		||||
      ]
 | 
			
		||||
    ]
 | 
			
		||||
  , [ 'logout', 'alias of session:detach' ]
 | 
			
		||||
  , [ 'whoami', 'show current account(s) and login(s) and device(s)' ]
 | 
			
		||||
 | 
			
		||||
    // authn
 | 
			
		||||
  , [ 'session', 'Manage your ids (credentials / logins)' ]
 | 
			
		||||
  , [ 'session:new', 'alias of `login --exclusive`' ]
 | 
			
		||||
  , [ 'session:attach', 'Create a session (and account if needed) for a given email address or cloud address' ]
 | 
			
		||||
  , [ 'session:detach', 'remove login from session' ]
 | 
			
		||||
  , [ 'session:list', 'show all of the ids in the current session' ]
 | 
			
		||||
 | 
			
		||||
    // authz
 | 
			
		||||
  , [ 'accounts', 'Manage your accounts (authorization / profiles)' ]
 | 
			
		||||
  , [ 'accounts:new', 'create a new account attached to the credentials of the current session' ]
 | 
			
		||||
  , [ 'accounts:set', 'change account details' ] // todo changing the name should be restricted john@provider.net -> jonathan@provider.net would be bad
 | 
			
		||||
  , [ 'accounts:list', 'show all of the accounts in the current session' ]
 | 
			
		||||
  , [ 'accounts:attach', 'attach an account to an id' ]
 | 
			
		||||
  , [ 'accounts:detach', 'detach an account from an id' ]
 | 
			
		||||
  , [ 'accounts:select', 'select an account to use as the primary account for this session' ]
 | 
			
		||||
  , [ 'accounts:update', '(deprecated) alias of set' ]
 | 
			
		||||
  , [ 'accounts:login', '(deprecated) alias of login' ]
 | 
			
		||||
  , [ 'accounts:whoami', '(deprecated) alias of whoami' ]
 | 
			
		||||
 | 
			
		||||
    // 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/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)' ]
 | 
			
		||||
  , [ 'devices:list', 'show all devices for your account(s)' ]
 | 
			
		||||
 | 
			
		||||
    // help
 | 
			
		||||
  , [ 'help', "show this menu; use '" + defaults.main + " help COMMAND' (even 'help') for options and sub-commands" ]
 | 
			
		||||
  ]
 | 
			
		||||
});
 | 
			
		||||
@ -189,6 +189,8 @@ module.exports.login = function (options) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return getSession().then(function (sessionResult) {
 | 
			
		||||
      console.log('sessionResult');
 | 
			
		||||
      console.log(sessionResult);
 | 
			
		||||
      var session = sessionResult.result;
 | 
			
		||||
      var id = require('crypto').createHash('sha256').update(session.token.sub || '').digest('hex');
 | 
			
		||||
 | 
			
		||||
@ -199,6 +201,8 @@ module.exports.login = function (options) {
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  }).then(function (session) {
 | 
			
		||||
    form.println('session:');
 | 
			
		||||
    form.println(session);
 | 
			
		||||
    oauth3.__session = session;
 | 
			
		||||
    return oauth3;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,7 @@
 | 
			
		||||
    "sign"
 | 
			
		||||
  ],
 | 
			
		||||
  "license": "MIT",
 | 
			
		||||
  "homepage": "https://git.oauth3.org/OAuth3/oauth3.js",
 | 
			
		||||
  "homepage": "https://git.daplie.com/OAuth3/oauth3.js",
 | 
			
		||||
  "ignore": [
 | 
			
		||||
    "**/.*",
 | 
			
		||||
    "browserify",
 | 
			
		||||
@ -39,5 +39,5 @@
 | 
			
		||||
    "test",
 | 
			
		||||
    "tests"
 | 
			
		||||
  ],
 | 
			
		||||
  "version": "1.0.10"
 | 
			
		||||
  "version": "1.0.2"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										14
									
								
								bump-versions.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								bump-versions.sh
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
			
		||||
git push --tags
 | 
			
		||||
 | 
			
		||||
git checkout v1.0
 | 
			
		||||
git push
 | 
			
		||||
 | 
			
		||||
git checkout v1
 | 
			
		||||
git merge v1.0
 | 
			
		||||
git push
 | 
			
		||||
 | 
			
		||||
git checkout master
 | 
			
		||||
git merge v1
 | 
			
		||||
git push
 | 
			
		||||
 | 
			
		||||
git checkout v1.0
 | 
			
		||||
@ -1,96 +0,0 @@
 | 
			
		||||
(function () {
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
function create(myOpts) {
 | 
			
		||||
  return {
 | 
			
		||||
    requestScope: function (opts) {
 | 
			
		||||
      // TODO pre-generate URL
 | 
			
		||||
 | 
			
		||||
      // deliver existing session if it exists
 | 
			
		||||
      var scope = opts && opts.scope || [];
 | 
			
		||||
      if (myOpts.session) {
 | 
			
		||||
        if (!scope.length || scope.every(function (scp) {
 | 
			
		||||
          return -1 !== opts.myOpts.session.scope.indexOf(scp);
 | 
			
		||||
        })) {
 | 
			
		||||
          return OAUTH3.PromiseA.resolve(myOpts.session);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // request a new session otherwise
 | 
			
		||||
      return OAUTH3.implicitGrant(myOpts.directives, {
 | 
			
		||||
        client_id: myOpts.conf.client_uri
 | 
			
		||||
      , client_uri: myOpts.conf.client_uri
 | 
			
		||||
        // maybe use inline instead?
 | 
			
		||||
      , windowType: 'popup'
 | 
			
		||||
      , scope: scope
 | 
			
		||||
      }).then(function (session) {
 | 
			
		||||
        return session;
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  , session: function () {
 | 
			
		||||
      return myOpts.session;
 | 
			
		||||
    }
 | 
			
		||||
  , refresh: function (session) {
 | 
			
		||||
      return OAUTH3.implicitGrant(myOpts.directives, {
 | 
			
		||||
        client_id: myOpts.conf.client_uri
 | 
			
		||||
      , client_uri: myOpts.conf.client_uri
 | 
			
		||||
      , windowType: 'background'
 | 
			
		||||
      }).then(function (_session) {
 | 
			
		||||
        session = _session;
 | 
			
		||||
        return session;
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  , logout: function () {
 | 
			
		||||
      return OAUTH3.logout(myOpts.directives, {
 | 
			
		||||
        client_id: myOpts.conf.client_uri
 | 
			
		||||
      , client_uri: myOpts.conf.client_uri
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  , switchUser: function () {
 | 
			
		||||
      // should open dialog with user selection dialog
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
window.navigator.auth = {
 | 
			
		||||
  getUserAuthenticator: function (opts) {
 | 
			
		||||
    var conf = {};
 | 
			
		||||
    var directives;
 | 
			
		||||
    var session;
 | 
			
		||||
 | 
			
		||||
    opts = opts || {};
 | 
			
		||||
    conf.client_uri = opts.client_uri || OAUTH3.clientUri(opts.location || window.location);
 | 
			
		||||
 | 
			
		||||
    return OAUTH3.issuer({ broker: opts.issuer_uri || 'https://new.oauth3.org' }).then(function (issuer) {
 | 
			
		||||
      conf.issuer_uri = issuer;
 | 
			
		||||
      conf.provider_uri = issuer;
 | 
			
		||||
 | 
			
		||||
      return OAUTH3.directives(conf.provider_uri, {
 | 
			
		||||
        client_id: conf.client_uri
 | 
			
		||||
      , client_uri: conf.client_uri
 | 
			
		||||
      }).then(function (_directives) {
 | 
			
		||||
        directives = _directives;
 | 
			
		||||
        var myOpts = {
 | 
			
		||||
          directives: directives
 | 
			
		||||
        , conf: conf
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return OAUTH3.implicitGrant(directives, {
 | 
			
		||||
          client_id: conf.client_uri
 | 
			
		||||
        , client_uri: conf.client_uri
 | 
			
		||||
        , windowType: 'background'
 | 
			
		||||
        }).then(function (_session) {
 | 
			
		||||
          session = _session;
 | 
			
		||||
          myOpts.session = session;
 | 
			
		||||
          return create(myOpts);
 | 
			
		||||
        }, function (err) {
 | 
			
		||||
          console.error('[DEBUG] implicitGrant err:');
 | 
			
		||||
          console.error(err);
 | 
			
		||||
          return create(myOpts);
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}());
 | 
			
		||||
							
								
								
									
										1
									
								
								node_modules/terminal-forms.js
									
									
									
										generated
									
									
										vendored
									
									
										Submodule
									
								
							
							
								
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								node_modules/terminal-forms.js
									
									
									
										generated
									
									
										vendored
									
									
										Submodule
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Subproject commit 91efb1bbf2ab8f4db86f3e93a5c82bf541f1cc67
 | 
			
		||||
@ -1,86 +0,0 @@
 | 
			
		||||
;(function (exports) {
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
var OAUTH3 = exports.OAUTH3 = exports.OAUTH3 || require('./oauth3.core.js').OAUTH3;
 | 
			
		||||
 | 
			
		||||
OAUTH3.api['account.listCards'] = function (providerUri, opts) {
 | 
			
		||||
  var session = opts.session;
 | 
			
		||||
 | 
			
		||||
  return OAUTH3.request({
 | 
			
		||||
    method: 'GET'
 | 
			
		||||
  , url: OAUTH3.url.normalize(providerUri)
 | 
			
		||||
      + '/api/com.daplie.payments/accounts/' + session.token.sub + '/cards'
 | 
			
		||||
  , session: session
 | 
			
		||||
  }).then(function (res) {
 | 
			
		||||
    return res;
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
OAUTH3.api['account.addCard'] = function (providerUri, opts) {
 | 
			
		||||
  var session = opts.session;
 | 
			
		||||
 | 
			
		||||
  return OAUTH3.request({
 | 
			
		||||
    method: 'POST'
 | 
			
		||||
  , url: OAUTH3.url.normalize(providerUri)
 | 
			
		||||
      + '/api/com.daplie.payments/accounts/' + session.token.sub + '/cards'
 | 
			
		||||
  , session: session
 | 
			
		||||
  , data: opts.data
 | 
			
		||||
  }).then(function (res) {
 | 
			
		||||
    return res;
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
OAUTH3.api['account.removeCard'] = function (providerUri, opts) {
 | 
			
		||||
  var session = opts.session;
 | 
			
		||||
 | 
			
		||||
  return OAUTH3.request({
 | 
			
		||||
    method: 'DELETE'
 | 
			
		||||
  , url: OAUTH3.url.normalize(providerUri)
 | 
			
		||||
      + '/api/com.daplie.payments/accounts/' + session.token.sub + '/cards/' + opts.last4 + '/' + opts.brand
 | 
			
		||||
  , session: session
 | 
			
		||||
  }).then(function (res) {
 | 
			
		||||
    return res;
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
OAUTH3.api['account.listAddresses'] = function (providerUri, opts) {
 | 
			
		||||
  var session = opts.session;
 | 
			
		||||
 | 
			
		||||
  return OAUTH3.request({
 | 
			
		||||
    method: 'GET'
 | 
			
		||||
  , url: OAUTH3.url.normalize(providerUri)
 | 
			
		||||
      + '/api/com.daplie.me/accounts/' + session.token.sub + '/addresses'
 | 
			
		||||
  , session: session
 | 
			
		||||
  }).then(function (res) {
 | 
			
		||||
    return res;
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
OAUTH3.api['account.addAddress'] = function (providerUri, opts) {
 | 
			
		||||
  var session = opts.session;
 | 
			
		||||
 | 
			
		||||
  return OAUTH3.request({
 | 
			
		||||
    method: 'POST'
 | 
			
		||||
  , url: OAUTH3.url.normalize(providerUri)
 | 
			
		||||
      + '/api/com.daplie.me/accounts/' + session.token.sub + '/addresses'
 | 
			
		||||
  , session: session
 | 
			
		||||
  , data: opts.addAddress
 | 
			
		||||
  }).then(function (res) {
 | 
			
		||||
    return res;
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
OAUTH3.api['account.removeAddress'] = function (providerUri, opts) {
 | 
			
		||||
  var session = opts.session;
 | 
			
		||||
 | 
			
		||||
  return OAUTH3.request({
 | 
			
		||||
    method: 'DELETE'
 | 
			
		||||
  , url: OAUTH3.url.normalize(providerUri)
 | 
			
		||||
      + '/api/com.daplie.me/accounts/' + session.token.sub + '/addresses/' + opts.addressId
 | 
			
		||||
  , session: session
 | 
			
		||||
  }).then(function (res) {
 | 
			
		||||
    return res;
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}('undefined' !== typeof exports ? exports : window));
 | 
			
		||||
							
								
								
									
										898
									
								
								oauth3.core.js
									
									
									
									
									
								
							
							
						
						
									
										898
									
								
								oauth3.core.js
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -10983,7 +10983,7 @@ module.exports={
 | 
			
		||||
        "spec": ">=6.4.0 <7.0.0",
 | 
			
		||||
        "type": "range"
 | 
			
		||||
      },
 | 
			
		||||
      "/Users/aj/git.daplie.com/OAuth3/oauth3.js"
 | 
			
		||||
      "/home/seth/js/git.daplie.com/OAuth3/oauth3.js"
 | 
			
		||||
    ]
 | 
			
		||||
  ],
 | 
			
		||||
  "_from": "elliptic@>=6.4.0 <7.0.0",
 | 
			
		||||
@ -11011,16 +11011,13 @@ module.exports={
 | 
			
		||||
    "type": "range"
 | 
			
		||||
  },
 | 
			
		||||
  "_requiredBy": [
 | 
			
		||||
    "#USER",
 | 
			
		||||
    "/",
 | 
			
		||||
    "/browserify-sign",
 | 
			
		||||
    "/create-ecdh"
 | 
			
		||||
    "/"
 | 
			
		||||
  ],
 | 
			
		||||
  "_resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz",
 | 
			
		||||
  "_shasum": "cac9af8762c85836187003c8dfe193e5e2eae5df",
 | 
			
		||||
  "_shrinkwrap": null,
 | 
			
		||||
  "_spec": "elliptic@^6.4.0",
 | 
			
		||||
  "_where": "/Users/aj/git.daplie.com/OAuth3/oauth3.js",
 | 
			
		||||
  "_where": "/home/seth/js/git.daplie.com/OAuth3/oauth3.js",
 | 
			
		||||
  "author": {
 | 
			
		||||
    "name": "Fedor Indutny",
 | 
			
		||||
    "email": "fedor@indutny.com"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										10
									
								
								oauth3.crypto.fallback.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								oauth3.crypto.fallback.min.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										158
									
								
								oauth3.crypto.js
									
									
									
									
									
								
							
							
						
						
									
										158
									
								
								oauth3.crypto.js
									
									
									
									
									
								
							@ -1,5 +1,5 @@
 | 
			
		||||
;(function (exports) {
 | 
			
		||||
  'use strict';
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
  var OAUTH3 = exports.OAUTH3 = exports.OAUTH3 || require('./oauth3.core.js').OAUTH3;
 | 
			
		||||
 | 
			
		||||
@ -8,9 +8,6 @@
 | 
			
		||||
    OAUTH3.crypto.core = require('./oauth3.node.crypto');
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    OAUTH3.crypto.core = {};
 | 
			
		||||
    OAUTH3.crypto.core.ready = false;
 | 
			
		||||
    var finishBeforeReady = [];
 | 
			
		||||
    var deferedCalls = [];
 | 
			
		||||
 | 
			
		||||
    // We don't currently have a fallback method for this function, so we assign
 | 
			
		||||
    // it directly to the core object instead of the webCrypto object.
 | 
			
		||||
@ -20,31 +17,10 @@
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    var webCrypto = {};
 | 
			
		||||
 | 
			
		||||
    var deferCryptoCall = function(name) {
 | 
			
		||||
      return function() {
 | 
			
		||||
        var args = arguments;
 | 
			
		||||
        return new OAUTH3.PromiseA(function(resolve, reject) {
 | 
			
		||||
          deferedCalls.push(function(){
 | 
			
		||||
            try {
 | 
			
		||||
              webCrypto[name].apply(webCrypto, args)
 | 
			
		||||
                .then(function(result){
 | 
			
		||||
                  resolve(result);
 | 
			
		||||
                });
 | 
			
		||||
            } catch(e) {
 | 
			
		||||
              reject(e);
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
        });
 | 
			
		||||
      };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    OAUTH3.crypto.core.sha256 = deferCryptoCall("sha256");
 | 
			
		||||
    webCrypto.sha256 = function (buf) {
 | 
			
		||||
      return OAUTH3._browser.window.crypto.subtle.digest({name: 'SHA-256'}, buf);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    OAUTH3.crypto.core.pbkdf2 = deferCryptoCall("pbkdf2");
 | 
			
		||||
    webCrypto.pbkdf2 = function (password, salt) {
 | 
			
		||||
      return OAUTH3._browser.window.crypto.subtle.importKey('raw', OAUTH3._binStr.binStrToBuffer(password), {name: 'PBKDF2'}, false, ['deriveKey'])
 | 
			
		||||
        .then(function (key) {
 | 
			
		||||
@ -56,15 +32,12 @@
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    OAUTH3.crypto.core.encrypt = deferCryptoCall("encrypt");
 | 
			
		||||
    webCrypto.encrypt = function (rawKey, iv, data) {
 | 
			
		||||
      return OAUTH3._browser.window.crypto.subtle.importKey('raw', rawKey, {name: 'AES-GCM'}, false, ['encrypt'])
 | 
			
		||||
        .then(function (key) {
 | 
			
		||||
          return OAUTH3._browser.window.crypto.subtle.encrypt({name: 'AES-GCM', iv: iv}, key, data);
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    OAUTH3.crypto.core.decrypt = deferCryptoCall("decrypt");
 | 
			
		||||
    webCrypto.decrypt = function (rawKey, iv, data) {
 | 
			
		||||
      return OAUTH3._browser.window.crypto.subtle.importKey('raw', rawKey, {name: 'AES-GCM'}, false, ['decrypt'])
 | 
			
		||||
        .then(function (key) {
 | 
			
		||||
@ -72,7 +45,6 @@
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    OAUTH3.crypto.core.genEcdsaKeyPair = deferCryptoCall("genEcdsaKeyPair");
 | 
			
		||||
    webCrypto.genEcdsaKeyPair = function () {
 | 
			
		||||
      return OAUTH3._browser.window.crypto.subtle.generateKey({name: 'ECDSA', namedCurve: 'P-256'}, true, ['sign', 'verify'])
 | 
			
		||||
        .then(function (keyPair) {
 | 
			
		||||
@ -85,7 +57,6 @@
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    OAUTH3.crypto.core.sign = deferCryptoCall("sign");
 | 
			
		||||
    webCrypto.sign = function (jwk, msg) {
 | 
			
		||||
      return OAUTH3._browser.window.crypto.subtle.importKey('jwk', jwk, {name: 'ECDSA', namedCurve: jwk.crv}, false, ['sign'])
 | 
			
		||||
        .then(function (key) {
 | 
			
		||||
@ -95,8 +66,6 @@
 | 
			
		||||
          return new Uint8Array(sig);
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    OAUTH3.crypto.core.verify = deferCryptoCall("verify");
 | 
			
		||||
    webCrypto.verify = function (jwk, msg, signature) {
 | 
			
		||||
      // If the JWK has properties that should only exist on the private key or is missing
 | 
			
		||||
      // "verify" in the key_ops, importing in as a public key won't work.
 | 
			
		||||
@ -113,7 +82,6 @@
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    function checkWebCrypto() {
 | 
			
		||||
      /* global OAUTH3_crypto_fallback */
 | 
			
		||||
      var loadFallback = function() {
 | 
			
		||||
        var prom;
 | 
			
		||||
        loadFallback = function () { return prom; };
 | 
			
		||||
@ -128,25 +96,25 @@
 | 
			
		||||
              resolve();
 | 
			
		||||
            }
 | 
			
		||||
          };
 | 
			
		||||
          script.src = '/assets/oauth3.org/oauth3.crypto.fallback.js';
 | 
			
		||||
          script.src = '/assets/org.oauth3/oauth3.crypto.fallback.js';
 | 
			
		||||
          body.appendChild(script);
 | 
			
		||||
        });
 | 
			
		||||
        return prom;
 | 
			
		||||
      };
 | 
			
		||||
      function checkException(name, func) {
 | 
			
		||||
        return OAUTH3.PromiseA.resolve().then(func)
 | 
			
		||||
        new OAUTH3.PromiseA(function (resolve) { resolve(func()); })
 | 
			
		||||
          .then(function () {
 | 
			
		||||
            OAUTH3.crypto.core[name] = webCrypto[name];
 | 
			
		||||
          }, function (err) {
 | 
			
		||||
          })
 | 
			
		||||
          .catch(function (err) {
 | 
			
		||||
            console.warn('error with WebCrypto', name, '- using fallback', err);
 | 
			
		||||
            return loadFallback().then(function () {
 | 
			
		||||
            loadFallback().then(function () {
 | 
			
		||||
              OAUTH3.crypto.core[name] = OAUTH3_crypto_fallback[name];
 | 
			
		||||
            });
 | 
			
		||||
          });
 | 
			
		||||
      }
 | 
			
		||||
      function checkResult(name, expected, func) {
 | 
			
		||||
        
 | 
			
		||||
        finishBeforeReady.push(checkException(name, function () {
 | 
			
		||||
        checkException(name, function () {
 | 
			
		||||
          return func()
 | 
			
		||||
            .then(function (result) {
 | 
			
		||||
              if (typeof expected === typeof result) {
 | 
			
		||||
@ -159,7 +127,7 @@
 | 
			
		||||
                throw new Error("result ("+result+") doesn't match expectation ("+expected+")");
 | 
			
		||||
              }
 | 
			
		||||
            });
 | 
			
		||||
        }));
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      var zeroBuf = new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
 | 
			
		||||
@ -191,20 +159,12 @@
 | 
			
		||||
        return webCrypto.verify(jwk, dataBuf, sig);
 | 
			
		||||
      });
 | 
			
		||||
      // The results of these functions are less predictable, so we can't check their return value.
 | 
			
		||||
      finishBeforeReady.push(checkException('genEcdsaKeyPair', function () {
 | 
			
		||||
      checkException('genEcdsaKeyPair', function () {
 | 
			
		||||
        return webCrypto.genEcdsaKeyPair();
 | 
			
		||||
      }));
 | 
			
		||||
      finishBeforeReady.push(checkException('sign', function () {
 | 
			
		||||
      });
 | 
			
		||||
      checkException('sign', function () {
 | 
			
		||||
        return webCrypto.sign(jwk, dataBuf);
 | 
			
		||||
      }));
 | 
			
		||||
      
 | 
			
		||||
      OAUTH3.PromiseA.all(finishBeforeReady)
 | 
			
		||||
        .then(function(results) {
 | 
			
		||||
          OAUTH3.crypto.core.ready = true;
 | 
			
		||||
          deferedCalls.forEach(function(request) {
 | 
			
		||||
            request();
 | 
			
		||||
          });
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    checkWebCrypto();
 | 
			
		||||
  }
 | 
			
		||||
@ -228,68 +188,106 @@
 | 
			
		||||
      return OAUTH3.PromiseA.reject(new Error('JWK of type '+jwk.kty+' missing fields ' + missing));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // I'm not actually 100% sure this behavior is guaranteed, but when we use an array as the
 | 
			
		||||
    // replacer argument the keys are always in the order they appeared in the array.
 | 
			
		||||
    var jwkStr = JSON.stringify(jwk, keys);
 | 
			
		||||
    var jwkStr = '{' + keys.map(function (name) { return name+':'+jwk[name]; }).join(',') + '}';
 | 
			
		||||
    return OAUTH3.crypto.core.sha256(OAUTH3._binStr.binStrToBuffer(jwkStr))
 | 
			
		||||
      .then(OAUTH3._base64.bufferToUrlSafe);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  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) {
 | 
			
		||||
  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) {
 | 
			
		||||
      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)
 | 
			
		||||
  , ]).then(function (results) {
 | 
			
		||||
    , OAUTH3.crypto.core.randomBytes(12)
 | 
			
		||||
    ]).then(function (results) {
 | 
			
		||||
      var kek        = results[0];
 | 
			
		||||
      var salt       = results[1];
 | 
			
		||||
      var ecdsaIv    = results[2];
 | 
			
		||||
      var keyPair    = results[1];
 | 
			
		||||
      var salt       = results[2];
 | 
			
		||||
      var userSecret = results[3];
 | 
			
		||||
      var ecdsaIv    = results[4];
 | 
			
		||||
      var secretIv   = results[5];
 | 
			
		||||
 | 
			
		||||
      var privKeyBuf = OAUTH3._binStr.binStrToBuffer(JSON.stringify(keyPair.privateKey));
 | 
			
		||||
      return OAUTH3.crypto.core.encrypt(kek, ecdsaIv, privKeyBuf).then(function (encrypted) {
 | 
			
		||||
      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) {
 | 
			
		||||
        return {
 | 
			
		||||
          publicKey:  keyPair.publicKey
 | 
			
		||||
        , privateKey: OAUTH3._base64.bufferToUrlSafe(encrypted)
 | 
			
		||||
        , privateKey: OAUTH3._base64.bufferToUrlSafe(encrypted[0])
 | 
			
		||||
        , userSecret: OAUTH3._base64.bufferToUrlSafe(encrypted[1])
 | 
			
		||||
        , salt:       OAUTH3._base64.bufferToUrlSafe(salt)
 | 
			
		||||
        , ecdsaIv:    OAUTH3._base64.bufferToUrlSafe(ecdsaIv)
 | 
			
		||||
      , };
 | 
			
		||||
        , secretIv:   OAUTH3._base64.bufferToUrlSafe(secretIv)
 | 
			
		||||
        };
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  OAUTH3.crypto.decryptKeyPair = function (storedObj, password) {
 | 
			
		||||
  OAUTH3.crypto._decryptKey = function (ppid, storedObj) {
 | 
			
		||||
    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(password, salt)
 | 
			
		||||
    return OAUTH3.crypto.core.pbkdf2(ppid, salt)
 | 
			
		||||
      .then(function (key) {
 | 
			
		||||
        return OAUTH3.crypto.core.decrypt(key, iv, encJwk);
 | 
			
		||||
      })
 | 
			
		||||
      .then(OAUTH3._binStr.bufferToBinStr)
 | 
			
		||||
      .then(JSON.parse)
 | 
			
		||||
      .then(function (privateKey) {
 | 
			
		||||
        return {
 | 
			
		||||
          privateKey: privateKey
 | 
			
		||||
        , publicKey:  storedObj.publicKey
 | 
			
		||||
      , };
 | 
			
		||||
      .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);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  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));
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										104
									
								
								oauth3.dns.js
									
									
									
									
									
								
							
							
						
						
									
										104
									
								
								oauth3.dns.js
									
									
									
									
									
								
							@ -31,23 +31,15 @@ OAUTH3.api['devices.list'] = function (providerUri, opts) {
 | 
			
		||||
 | 
			
		||||
OAUTH3.api['devices.attach'] = function (providerUri, opts) {
 | 
			
		||||
  var session = opts.session;
 | 
			
		||||
  var device = opts.device;
 | 
			
		||||
  var tld = opts.tld;
 | 
			
		||||
  var sld = opts.sld;
 | 
			
		||||
  var sub = opts.sub;
 | 
			
		||||
  var ip = opts.ip;
 | 
			
		||||
  var ttl = opts.ttl;
 | 
			
		||||
 | 
			
		||||
  return OAUTH3.request({
 | 
			
		||||
    url: OAUTH3.url.normalize(providerUri)
 | 
			
		||||
      + '/api/com.daplie.domains/accounts/' + session.token.sub + '/devices/'
 | 
			
		||||
      + device + '/' + tld + '/' + sld + '/' + (sub || '')
 | 
			
		||||
      + '/api/com.daplie.domains/accounts/' + session.token.sub
 | 
			
		||||
      //+ '/devices/' + device + '/'
 | 
			
		||||
      + '/devices/' + (opts.data.uid || '_') + '/' + opts.data.device
 | 
			
		||||
			+ '/' + opts.data.tld + '/' + opts.data.sld + '/' + (opts.data.sub || '')
 | 
			
		||||
    , method: 'POST'
 | 
			
		||||
    , session: session
 | 
			
		||||
    , data: {
 | 
			
		||||
        addresses: ip
 | 
			
		||||
      , ttl: ttl
 | 
			
		||||
    }
 | 
			
		||||
  }, {}).then(function (res) {
 | 
			
		||||
    return res.data.devices || res.data;
 | 
			
		||||
  });
 | 
			
		||||
@ -55,15 +47,28 @@ OAUTH3.api['devices.attach'] = function (providerUri, opts) {
 | 
			
		||||
 | 
			
		||||
OAUTH3.api['devices.detach'] = function (providerUri, opts) {
 | 
			
		||||
  var session = opts.session;
 | 
			
		||||
  var device = opts.device;
 | 
			
		||||
  var tld = opts.tld;
 | 
			
		||||
  var sld = opts.sld;
 | 
			
		||||
  var sub = opts.sub;
 | 
			
		||||
 | 
			
		||||
  return OAUTH3.request({
 | 
			
		||||
    url: OAUTH3.url.normalize(providerUri)
 | 
			
		||||
      + '/api/com.daplie.domains/accounts/' + session.token.sub
 | 
			
		||||
			+ '/devices/' + device + '/' + tld + '/' + sld + '/' + (sub || '')
 | 
			
		||||
      //+ '/devices/' + device + '/'
 | 
			
		||||
      + '/devices/' + (opts.data.uid || '_') + '/' + opts.data.device
 | 
			
		||||
			+ '/' + opts.data.tld + '/' + opts.data.sld + '/' + (opts.data.sub || '')
 | 
			
		||||
  , method: 'DELETE'
 | 
			
		||||
  , session: session
 | 
			
		||||
  }, {}).then(function (res) {
 | 
			
		||||
    return res.data.devices || res.data;
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
OAUTH3.api['devices.detach'] = function (providerUri, opts) {
 | 
			
		||||
  var session = opts.session;
 | 
			
		||||
 | 
			
		||||
  return OAUTH3.request({
 | 
			
		||||
    url: OAUTH3.url.normalize(providerUri)
 | 
			
		||||
      + '/api/com.daplie.domains/accounts/' + session.token.sub
 | 
			
		||||
			+ '/devices/' + opts.data.device
 | 
			
		||||
			+ '/' + opts.data.tld + '/' + opts.data.sld + '/' + (opts.data.sub || '')
 | 
			
		||||
  , method: 'DELETE'
 | 
			
		||||
  , session: session
 | 
			
		||||
  }, {}).then(function (res) {
 | 
			
		||||
@ -71,69 +76,4 @@ OAUTH3.api['devices.detach'] = function (providerUri, opts) {
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
OAUTH3.api['devices.destroy'] = function (providerUri, opts) {
 | 
			
		||||
  var session = opts.session;
 | 
			
		||||
  var device = opts.device;
 | 
			
		||||
 | 
			
		||||
  return OAUTH3.request({
 | 
			
		||||
    url: OAUTH3.url.normalize(providerUri)
 | 
			
		||||
      + '/api/com.daplie.domains/accounts/' + session.token.sub
 | 
			
		||||
			+ '/devices/' + device
 | 
			
		||||
  , method: 'DELETE'
 | 
			
		||||
  , session: session
 | 
			
		||||
  }, {}).then(function (res) {
 | 
			
		||||
    return res.data.device || res.data;
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
OAUTH3.api['dns.set'] = function (providerUri, opts) {
 | 
			
		||||
  var session = opts.session;
 | 
			
		||||
  var tld = opts.tld;
 | 
			
		||||
  var sld = opts.sld;
 | 
			
		||||
  var sub = opts.sub;
 | 
			
		||||
  var type = opts.type;
 | 
			
		||||
  var value = opts.value;
 | 
			
		||||
  var ttl = opts.ttl;
 | 
			
		||||
  var priority = (opts.priority || '');
 | 
			
		||||
  var weight = (opts.weight || '');
 | 
			
		||||
  var port = (opts.port || '');
 | 
			
		||||
 | 
			
		||||
  return OAUTH3.request({
 | 
			
		||||
    url: OAUTH3.url.normalize(providerUri)
 | 
			
		||||
      + '/api/com.daplie.domains/accounts/' + session.token.sub
 | 
			
		||||
      + '/dns/' + tld + '/' + sld + '/' + sub
 | 
			
		||||
  , method: 'POST'
 | 
			
		||||
  , session: session
 | 
			
		||||
  , data: [{
 | 
			
		||||
    type: type
 | 
			
		||||
  , value: value
 | 
			
		||||
  , ttl: ttl
 | 
			
		||||
  , priority: priority
 | 
			
		||||
  , weight: weight
 | 
			
		||||
  , port: port
 | 
			
		||||
  }]
 | 
			
		||||
  }, {}).then(function (res) {
 | 
			
		||||
    return res.data || res;
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
OAUTH3.api['dns.unset'] = function (providerUri, opts) {
 | 
			
		||||
  var session = opts.session;
 | 
			
		||||
  var tld = opts.tld;
 | 
			
		||||
  var sld = opts.sld;
 | 
			
		||||
  var sub = (opts.sub || '@');
 | 
			
		||||
  var type = opts.type;
 | 
			
		||||
  var value = opts.value;
 | 
			
		||||
 | 
			
		||||
  return OAUTH3.request({
 | 
			
		||||
    url: OAUTH3.url.normalize(providerUri)
 | 
			
		||||
      + '/api/com.daplie.domains/accounts/' + session.token.sub
 | 
			
		||||
      + '/dns/' + tld + '/' + sld + '/' + sub + '/' + type + '/' + value
 | 
			
		||||
  , method: 'DELETE'
 | 
			
		||||
  , session: session
 | 
			
		||||
  }, {}).then(function (res) {
 | 
			
		||||
    return res.data || res;
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}('undefined' !== typeof exports ? exports : window));
 | 
			
		||||
 | 
			
		||||
@ -3,35 +3,6 @@
 | 
			
		||||
 | 
			
		||||
var OAUTH3 = exports.OAUTH3 = exports.OAUTH3 || require('./oauth3.core.js').OAUTH3;
 | 
			
		||||
 | 
			
		||||
OAUTH3.api['domains.checkAvailability'] = function (providerUri, opts) {
 | 
			
		||||
  var session = opts.session;
 | 
			
		||||
  var sld = opts.sld;
 | 
			
		||||
  var tld = opts.tld;
 | 
			
		||||
 | 
			
		||||
  return OAUTH3.request({
 | 
			
		||||
    method: 'GET'
 | 
			
		||||
  , url: OAUTH3.url.normalize(providerUri)
 | 
			
		||||
      + '/api/com.daplie.domains/check-availability/' + sld + '/' + tld
 | 
			
		||||
  , session: session
 | 
			
		||||
  }).then(function (res) {
 | 
			
		||||
    return res;
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
OAUTH3.api['domains.purchase'] = function (providerUri, opts) {
 | 
			
		||||
  var session = opts.session;
 | 
			
		||||
 | 
			
		||||
  return OAUTH3.request({
 | 
			
		||||
    method: 'POST'
 | 
			
		||||
  , url: OAUTH3.url.normalize(providerUri)
 | 
			
		||||
      + '/api/com.daplie.domains/accounts/' + session.token.sub + '/registrations'
 | 
			
		||||
  , session: session
 | 
			
		||||
  , data: opts.data
 | 
			
		||||
  }).then(function (res) {
 | 
			
		||||
    return res;
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
OAUTH3.api['domains.list'] = function (providerUri, opts) {
 | 
			
		||||
  var session = opts.session;
 | 
			
		||||
 | 
			
		||||
@ -45,102 +16,4 @@ OAUTH3.api['domains.list'] = function (providerUri, opts) {
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// TODO: Manual Renew Function
 | 
			
		||||
OAUTH3.api['domains.extend'] = function (providerUri, opts) {
 | 
			
		||||
  var session = opts.session;
 | 
			
		||||
 | 
			
		||||
  return OAUTH3.request({
 | 
			
		||||
    method: 'POST'
 | 
			
		||||
  , url: OAUTH3.url.normalize(providerUri)
 | 
			
		||||
      + '/api/com.daplie.domains/accounts/' + session.token.sub + '/registrations/' + opts.data.tld + '/' + opts.data.sld + '/extend'
 | 
			
		||||
  , session: session
 | 
			
		||||
  , data: opts.data
 | 
			
		||||
  }).then(function (res) {
 | 
			
		||||
    return res;
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
OAUTH3.api['ns.list'] = function (providerUri, opts) {
 | 
			
		||||
  var session = opts.session;
 | 
			
		||||
  var domain = opts.domain;
 | 
			
		||||
  var nameArr = domain.split('.');
 | 
			
		||||
  var reverseNameArr = nameArr.reverse();
 | 
			
		||||
  var nameSubArr = reverseNameArr.slice(3);
 | 
			
		||||
  var tld;
 | 
			
		||||
  var sld;
 | 
			
		||||
  var sub;
 | 
			
		||||
 | 
			
		||||
  if (reverseNameArr[0] === 'me' && reverseNameArr[1] === 'daplie') {
 | 
			
		||||
    tld = 'daplie.me';
 | 
			
		||||
    sld = reverseNameArr[2];
 | 
			
		||||
    sub = nameSubArr.reverse().join('.') || '';
 | 
			
		||||
  } else {
 | 
			
		||||
    tld = nameArr[0];
 | 
			
		||||
    sld = nameArr[1];
 | 
			
		||||
    sub = reverseNameArr.slice(2).reverse().join('.') || '';
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return OAUTH3.request({
 | 
			
		||||
    method: 'GET'
 | 
			
		||||
  , url: OAUTH3.url.normalize(providerUri)
 | 
			
		||||
      + '/api/com.daplie.domains/accounts/' + session.token.sub + '/ns/'
 | 
			
		||||
      + tld + '/' + sld + '/' + sub
 | 
			
		||||
  , session: session
 | 
			
		||||
  }).then(function (res) {
 | 
			
		||||
    return res.data;
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
OAUTH3.api['ns.add'] = function (providerUri, opts) {
 | 
			
		||||
  var session = opts.session;
 | 
			
		||||
  var server = opts.server;
 | 
			
		||||
  var tld =  opts.tld;
 | 
			
		||||
  var sld = opts.sld;
 | 
			
		||||
  var sub = opts.sub;
 | 
			
		||||
 | 
			
		||||
  return OAUTH3.request({
 | 
			
		||||
    method: 'POST'
 | 
			
		||||
  , url: OAUTH3.url.normalize(providerUri)
 | 
			
		||||
      + '/api/com.daplie.domains/accounts/' + session.token.sub + '/ns/'
 | 
			
		||||
      + tld + '/' + sld + '/' + sub
 | 
			
		||||
  , session: session
 | 
			
		||||
  , data: { nameservers: [server] }
 | 
			
		||||
  }).then(function (res) {
 | 
			
		||||
    return res;
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
OAUTH3.api['glue.list'] = function (providerUri, opts) {
 | 
			
		||||
  var session = opts.session;
 | 
			
		||||
 | 
			
		||||
  return OAUTH3.request({
 | 
			
		||||
    method: 'GET'
 | 
			
		||||
  , url: OAUTH3.url.normalize(providerUri)
 | 
			
		||||
      + '/api/com.daplie.domains/accounts/' + session.token.sub + '/glue'
 | 
			
		||||
  , session: session
 | 
			
		||||
  }).then(function (res) {
 | 
			
		||||
    return res.data;
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
OAUTH3.api['glue.add'] = function (providerUri, opts) {
 | 
			
		||||
  var session = opts.session;
 | 
			
		||||
  var ip = opts.ip;
 | 
			
		||||
  var tld =  opts.tld;
 | 
			
		||||
  var sld = opts.sld;
 | 
			
		||||
  var sub = (opts.sub || '@');
 | 
			
		||||
 | 
			
		||||
  return OAUTH3.request({
 | 
			
		||||
    method: 'POST'
 | 
			
		||||
  , url: OAUTH3.url.normalize(providerUri)
 | 
			
		||||
      + '/api/com.daplie.domains/accounts/' + session.token.sub + '/glue/'
 | 
			
		||||
      + tld + '/' + sld + '/' + sub
 | 
			
		||||
  , session: session
 | 
			
		||||
  , data: { ip: ip }
 | 
			
		||||
  }, {}).then(function (res) {
 | 
			
		||||
    return res;
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}('undefined' !== typeof exports ? exports : window));
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										839
									
								
								oauth3.issuer.js
									
									
									
									
									
								
							
							
						
						
									
										839
									
								
								oauth3.issuer.js
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -27,10 +27,10 @@
 | 
			
		||||
 | 
			
		||||
  OAUTH3.authz.scopes = function () {
 | 
			
		||||
    return OAUTH3.PromiseA.resolve({
 | 
			
		||||
      pending: [ 'authn@oauth3.org' ]     // not yet accepted
 | 
			
		||||
    , granted: []                         // all granted, ever
 | 
			
		||||
    , requested: [ 'authn@oauth3.org' ]   // all requested, now
 | 
			
		||||
    , accepted: []                        // granted (ever) and requested (now)
 | 
			
		||||
      pending: ['oauth3_authn']   // not yet accepted
 | 
			
		||||
    , granted: []                 // all granted, ever
 | 
			
		||||
    , requested: ['oauth3_authn'] // all requested, now
 | 
			
		||||
    , accepted: []                // granted (ever) and requested (now)
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
  OAUTH3.authz.grants = function (providerUri, opts) {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										62
									
								
								oauth3.ng.js
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								oauth3.ng.js
									
									
									
									
									
								
							@ -1,48 +1,38 @@
 | 
			
		||||
;(function () {
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
var modules = {
 | 
			
		||||
  azp: [
 | 
			
		||||
angular
 | 
			
		||||
  .module('org.oauth3', [])
 | 
			
		||||
  .service('Oauth3', [
 | 
			
		||||
    '$timeout'
 | 
			
		||||
  , '$q'
 | 
			
		||||
  , '$rootScope'
 | 
			
		||||
  , function Oauth3($timeout, $q, $rootScope) {
 | 
			
		||||
      var OAUTH3 = window.OAUTH3;
 | 
			
		||||
  , function Oauth3($timeout, $q) {
 | 
			
		||||
 | 
			
		||||
      // We need to make angular's $q appear to be a standard Promise/A+
 | 
			
		||||
      // fortunately, this is pretty easy
 | 
			
		||||
      function PromiseAngularQ(fn) {
 | 
			
		||||
        var d = $q.defer();
 | 
			
		||||
    var OAUTH3 = window.OAUTH3;
 | 
			
		||||
 | 
			
		||||
        //$timeout(function () {
 | 
			
		||||
          fn(d.resolve, d.reject);
 | 
			
		||||
        //}, 0);
 | 
			
		||||
    // We need to make angular's $q appear to be a standard Promise/A+
 | 
			
		||||
    // fortunately, this is pretty easy
 | 
			
		||||
    function PromiseAngularQ(fn) {
 | 
			
		||||
      var d = $q.defer();
 | 
			
		||||
 | 
			
		||||
        //this.then = d.promise.then;
 | 
			
		||||
        //this.catch = d.promise.catch;
 | 
			
		||||
        return d.promise;
 | 
			
		||||
      }
 | 
			
		||||
      //$timeout(function () {
 | 
			
		||||
        fn(d.resolve, d.reject);
 | 
			
		||||
      //}, 0);
 | 
			
		||||
 | 
			
		||||
      //PromiseAngularQ.create = PromiseAngularQ;
 | 
			
		||||
      PromiseAngularQ.resolve = $q.when;
 | 
			
		||||
      PromiseAngularQ.reject = $q.reject;
 | 
			
		||||
      PromiseAngularQ.all = $q.all;
 | 
			
		||||
 | 
			
		||||
      OAUTH3.PromiseA = PromiseAngularQ;
 | 
			
		||||
      OAUTH3._digest = function () {
 | 
			
		||||
        $rootScope.$digest();
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      window.ngOauth3 = OAUTH3;
 | 
			
		||||
 | 
			
		||||
      return OAUTH3;
 | 
			
		||||
      //this.then = d.promise.then;
 | 
			
		||||
      //this.catch = d.promise.catch;
 | 
			
		||||
      return d.promise;
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
angular
 | 
			
		||||
  .module('oauth3.org', [])
 | 
			
		||||
  .service('azp@oauth3.org', modules.azp)
 | 
			
		||||
  .service('AzpOauth3', modules.azp)
 | 
			
		||||
  ;
 | 
			
		||||
    //PromiseAngularQ.create = PromiseAngularQ;
 | 
			
		||||
    PromiseAngularQ.resolve = $q.when;
 | 
			
		||||
    PromiseAngularQ.reject = $q.reject;
 | 
			
		||||
    PromiseAngularQ.all = $q.all;
 | 
			
		||||
 | 
			
		||||
    OAUTH3.PromiseA = PromiseAngularQ;
 | 
			
		||||
 | 
			
		||||
    window.ngOauth3 = OAUTH3;
 | 
			
		||||
 | 
			
		||||
    return OAUTH3;
 | 
			
		||||
  }]);
 | 
			
		||||
}());
 | 
			
		||||
 | 
			
		||||
@ -28,7 +28,6 @@ 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*/) {
 | 
			
		||||
@ -44,7 +43,6 @@ OAUTH3._node.request = function(preq/*, _sys*/) {
 | 
			
		||||
    method: preq.method
 | 
			
		||||
  , url: preq.url || preq.uri
 | 
			
		||||
  , headers: preq.headers
 | 
			
		||||
  , timeout: preq.timeout || undefined
 | 
			
		||||
  , json: preq.data || preq.body || preq.json || undefined // TODO which to use?
 | 
			
		||||
  , formData: preq.formData || undefined
 | 
			
		||||
  };
 | 
			
		||||
@ -61,7 +59,10 @@ OAUTH3._node._parseJson = function (resp) {
 | 
			
		||||
 | 
			
		||||
  // TODO toCamelCase
 | 
			
		||||
  if (!(resp.statusCode >= 200 && resp.statusCode < 400)) {
 | 
			
		||||
    // console.log('[A3] DEBUG', resp.body);
 | 
			
		||||
    err = new Error("bad response code: " + resp.statusCode);
 | 
			
		||||
    err.result = resp.body;
 | 
			
		||||
    return PromiseA.reject(err);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //console.log('resp.body', typeof resp.body);
 | 
			
		||||
@ -69,16 +70,15 @@ OAUTH3._node._parseJson = function (resp) {
 | 
			
		||||
    try {
 | 
			
		||||
      json = JSON.parse(json);
 | 
			
		||||
    } catch(e) {
 | 
			
		||||
      err = err || (new Error('response not parsable: ' + resp.body));
 | 
			
		||||
      err = new Error('response not parsable:' + resp.body);
 | 
			
		||||
      err.result = resp.body;
 | 
			
		||||
      return PromiseA.reject(err);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // handle both Oauth2- and node-style errors
 | 
			
		||||
  if (json && json.error) {
 | 
			
		||||
    err = new Error(json.error.message || json.error_description || JSON.stringify(json.error));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (err) {
 | 
			
		||||
  if (json.error) {
 | 
			
		||||
    err = new Error(json.error && json.error.message || json.error_description || json.error);
 | 
			
		||||
    err.result = json;
 | 
			
		||||
    return PromiseA.reject(err);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -9,11 +9,6 @@ var sessionsdir = path.join(oauth3dir, 'sessions');
 | 
			
		||||
var directivesdir = path.join(oauth3dir, 'directives');
 | 
			
		||||
var metadir = path.join(oauth3dir, 'meta');
 | 
			
		||||
 | 
			
		||||
// We can reasonably assume the existence of the home directory, but we can't assume
 | 
			
		||||
// that there will already be a `.oauth3` directory or anything inside of it.
 | 
			
		||||
if (!fs.existsSync(path.join(oauth3dir, '..'))) {
 | 
			
		||||
  fs.mkdirSync(path.join(oauth3dir, '..'));
 | 
			
		||||
}
 | 
			
		||||
if (!fs.existsSync(oauth3dir)) {
 | 
			
		||||
  fs.mkdirSync(oauth3dir);
 | 
			
		||||
}
 | 
			
		||||
@ -67,9 +62,10 @@ module.exports = {
 | 
			
		||||
 | 
			
		||||
, sessions: {
 | 
			
		||||
    all: function (providerUri) {
 | 
			
		||||
      return fs.readdirAsync(sessionsdir).then(function (nodes) {
 | 
			
		||||
      var dirname = path.join(oauth3dir, 'sessions');
 | 
			
		||||
      return fs.readdirAsync(dirname).then(function (nodes) {
 | 
			
		||||
        return nodes.map(function (node) {
 | 
			
		||||
          var result = require(path.join(sessionsdir, node));
 | 
			
		||||
          var result = require(path.join(dirname, node));
 | 
			
		||||
          if (result.link) {
 | 
			
		||||
            return null;
 | 
			
		||||
          }
 | 
			
		||||
@ -90,7 +86,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(sessionsdir, result.link));
 | 
			
		||||
            result = require(path.join(oauth3dir, 'sessions', result.link));
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      } catch(e) {
 | 
			
		||||
@ -112,9 +108,10 @@ module.exports = {
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  , clear: function () {
 | 
			
		||||
      return fs.readdirAsync(sessionsdir).then(function (nodes) {
 | 
			
		||||
      var dirname = path.join(oauth3dir, 'sessions');
 | 
			
		||||
      return fs.readdirAsync(dirname).then(function (nodes) {
 | 
			
		||||
        return PromiseA.all(nodes.map(function (node) {
 | 
			
		||||
          return fs.unlinkAsync(path.join(sessionsdir, node));
 | 
			
		||||
          return fs.unlinkAsync(path.join(dirname, node));
 | 
			
		||||
        }));
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										66
									
								
								oauth3.org.directives.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								oauth3.org.directives.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,66 @@
 | 
			
		||||
{
 | 
			
		||||
  "provider_uri": "https://oauth3.org",
 | 
			
		||||
  "client_uri": "oauth3.org",
 | 
			
		||||
  "token_type": "bearer",
 | 
			
		||||
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJqdGkiOiIzOTZjMzJlNzE1NmIzNGI1ZWY1ZWYwZWU4Zjk3Y2NiMyIsImlhdCI6MTQ5MDE5NzUyNywiaXNzIjoib2F1dGgzLm9yZyIsImF1ZCI6Im9hdXRoMy5vcmciLCJhenAiOiJvYXV0aDMub3JnIiwic3ViIjoiIiwia2lkIjoib2F1dGgzLm9yZyIsInNjcCI6IiIsImFzIjoibG9naW4iLCJncnQiOiJwYXNzd29yZCIsInNydiI6ZmFsc2UsImsiOiJvYXV0aDMub3JnIiwiYXBwIjoib2F1dGgzLm9yZyIsImFjeCI6eyJpZCI6IjE1LUxhM3JnZXBFelBCR0xITHlrdEZOT1NDZFNVOXZJdjlKc2EzTkMxYVJUc3ZmUTZ5cDJuVFFfZWxmdkhzYTEifSwiYXhzIjpbXSwidXNyIjoiYTM3YWVkYTk5ZDQ5MThhMDM0YzM0MmQ2NGNkZjRiN2VkMjM0ZGZlNSIsImFjYyI6eyJpZCI6IjUzZTUwMTk2LTE4ZTMtNGJlNi04NDcyLTQ1ZDBjNDMxZjdhZCJ9LCJhY3MiOltdLCJpZHgiOiJuZnZ1bHRETE0tT0EzVUV3dVJHTDE3RFY1UXpIbWhac005Z2xMdnFLVGJacGh1T0NqMnBEUzByRk9XSXhaRjZLIiwidG9rZW5UeXBlIjoiYmVhcmVyIiwiZXhwIjoxNDkwMTk5MzI3LCJpcCI6IjIwNy4xNzMuMTY1LjUwIn0.qQu6NdsU4oVucv4uV_jusfL2HKgnPpfwF6iVG0H-P08akDtGgDoXcyVfl6hQdpVL9DGYVwvCPPUkLT0bJztM08lWhg69dVs-2e2I2BhjClsKeLsrFDBrUMwWVqqzCNVj8WBzcULLtl_mEgZc1qwVpZvXXiu0vmrRl3gtzVRaLL0",
 | 
			
		||||
  "scope": "",
 | 
			
		||||
  "expires_in": 1800,
 | 
			
		||||
  "refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJqdGkiOiI0OGJhNjJhNTQxNGFlODQ3OWJhMzA0MGQ1Mzc5NmY3MiIsImlhdCI6MTQ5MDE5NzUyNywiaXNzIjoib2F1dGgzLm9yZyIsImF1ZCI6Im9hdXRoMy5vcmciLCJhenAiOiJvYXV0aDMub3JnIiwic3ViIjoiIiwia2lkIjoib2F1dGgzLm9yZyIsInNjcCI6IiIsImFzIjoibG9naW4iLCJncnQiOiJwYXNzd29yZCIsInNydiI6ZmFsc2UsImsiOiJvYXV0aDMub3JnIiwiYXBwIjoib2F1dGgzLm9yZyIsImFjeCI6eyJpZCI6IjE1LUxhM3JnZXBFelBCR0xITHlrdEZOT1NDZFNVOXZJdjlKc2EzTkMxYVJUc3ZmUTZ5cDJuVFFfZWxmdkhzYTEifSwiYXhzIjpbXSwidXNyIjoiYTM3YWVkYTk5ZDQ5MThhMDM0YzM0MmQ2NGNkZjRiN2VkMjM0ZGZlNSIsImFjYyI6eyJpZCI6IjUzZTUwMTk2LTE4ZTMtNGJlNi04NDcyLTQ1ZDBjNDMxZjdhZCJ9LCJhY3MiOltdLCJpZHgiOiJuZnZ1bHRETE0tT0EzVUV3dVJHTDE3RFY1UXpIbWhac005Z2xMdnFLVGJacGh1T0NqMnBEUzByRk9XSXhaRjZLIiwicmVmcmVzaCI6dHJ1ZX0.QfufVyAGit2YOy9Hs9mv4eoCuyCYb9FDT_UXGd3JaFZe6MwqxLLnq2fWkkV2jgzDAK5t0JMu2Vk91jPP2IBXMkpZSzjaEKJ3-Eokb14Mo5GIrp54ndM20gWVZc-ReQtOUtSVG28bfnOBT5ceUM6SBrTxfz1ENOfmAiWl5591roQ",
 | 
			
		||||
  "token": {
 | 
			
		||||
    "jti": "396c32e7156b34b5ef5ef0ee8f97ccb3",
 | 
			
		||||
    "iat": 1490197527,
 | 
			
		||||
    "iss": "oauth3.org",
 | 
			
		||||
    "aud": "oauth3.org",
 | 
			
		||||
    "azp": "oauth3.org",
 | 
			
		||||
    "sub": "15-La3rgepEzPBGLHLyktFNOSCdSU9vIv9Jsa3NC1aRTsvfQ6yp2nTQ_elfvHsa1",
 | 
			
		||||
    "kid": "oauth3.org",
 | 
			
		||||
    "scp": "",
 | 
			
		||||
    "as": "login",
 | 
			
		||||
    "grt": "password",
 | 
			
		||||
    "srv": false,
 | 
			
		||||
    "k": "oauth3.org",
 | 
			
		||||
    "app": "oauth3.org",
 | 
			
		||||
    "acx": {
 | 
			
		||||
      "id": "15-La3rgepEzPBGLHLyktFNOSCdSU9vIv9Jsa3NC1aRTsvfQ6yp2nTQ_elfvHsa1"
 | 
			
		||||
    },
 | 
			
		||||
    "axs": [],
 | 
			
		||||
    "usr": "a37aeda99d4918a034c342d64cdf4b7ed234dfe5",
 | 
			
		||||
    "acc": {
 | 
			
		||||
      "id": "53e50196-18e3-4be6-8472-45d0c431f7ad"
 | 
			
		||||
    },
 | 
			
		||||
    "acs": [],
 | 
			
		||||
    "idx": "nfvultDLM-OA3UEwuRGL17DV5QzHmhZsM9glLvqKTbZphuOCj2pDS0rFOWIxZF6K",
 | 
			
		||||
    "tokenType": "bearer",
 | 
			
		||||
    "exp": 1490199327,
 | 
			
		||||
    "ip": "207.173.165.50",
 | 
			
		||||
    "client_uri": "oauth3.org",
 | 
			
		||||
    "provider_uri": "https://oauth3.org"
 | 
			
		||||
  },
 | 
			
		||||
  "refresh": {
 | 
			
		||||
    "jti": "48ba62a5414ae8479ba3040d53796f72",
 | 
			
		||||
    "iat": 1490197527,
 | 
			
		||||
    "iss": "oauth3.org",
 | 
			
		||||
    "aud": "oauth3.org",
 | 
			
		||||
    "azp": "oauth3.org",
 | 
			
		||||
    "sub": "15-La3rgepEzPBGLHLyktFNOSCdSU9vIv9Jsa3NC1aRTsvfQ6yp2nTQ_elfvHsa1",
 | 
			
		||||
    "kid": "oauth3.org",
 | 
			
		||||
    "scp": "",
 | 
			
		||||
    "as": "login",
 | 
			
		||||
    "grt": "password",
 | 
			
		||||
    "srv": false,
 | 
			
		||||
    "k": "oauth3.org",
 | 
			
		||||
    "app": "oauth3.org",
 | 
			
		||||
    "acx": {
 | 
			
		||||
      "id": "15-La3rgepEzPBGLHLyktFNOSCdSU9vIv9Jsa3NC1aRTsvfQ6yp2nTQ_elfvHsa1"
 | 
			
		||||
    },
 | 
			
		||||
    "axs": [],
 | 
			
		||||
    "usr": "a37aeda99d4918a034c342d64cdf4b7ed234dfe5",
 | 
			
		||||
    "acc": {
 | 
			
		||||
      "id": "53e50196-18e3-4be6-8472-45d0c431f7ad"
 | 
			
		||||
    },
 | 
			
		||||
    "acs": [],
 | 
			
		||||
    "idx": "nfvultDLM-OA3UEwuRGL17DV5QzHmhZsM9glLvqKTbZphuOCj2pDS0rFOWIxZF6K",
 | 
			
		||||
    "refresh": true,
 | 
			
		||||
    "provider_uri": "https://oauth3.org"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										66
									
								
								oauth3.org.session.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								oauth3.org.session.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,66 @@
 | 
			
		||||
{
 | 
			
		||||
  "provider_uri": "https://oauth3.org",
 | 
			
		||||
  "client_uri": "oauth3.org",
 | 
			
		||||
  "token_type": "bearer",
 | 
			
		||||
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJqdGkiOiI4MmU5MDhlMTljMmIxM2IxNmM3N2JlMTNkMTljZmEzOSIsImlhdCI6MTQ5MDEzODg2MiwiaXNzIjoib2F1dGgzLm9yZyIsImF1ZCI6Im9hdXRoMy5vcmciLCJhenAiOiJvYXV0aDMub3JnIiwic3ViIjoiIiwia2lkIjoib2F1dGgzLm9yZyIsInNjcCI6IiIsImFzIjoibG9naW4iLCJncnQiOiJwYXNzd29yZCIsInNydiI6ZmFsc2UsImsiOiJvYXV0aDMub3JnIiwiYXBwIjoib2F1dGgzLm9yZyIsImFjeCI6eyJpZCI6IjE1LUxhM3JnZXBFelBCR0xITHlrdEZOT1NDZFNVOXZJdjlKc2EzTkMxYVJUc3ZmUTZ5cDJuVFFfZWxmdkhzYTEifSwiYXhzIjpbXSwidXNyIjoiYTM3YWVkYTk5ZDQ5MThhMDM0YzM0MmQ2NGNkZjRiN2VkMjM0ZGZlNSIsImFjYyI6eyJpZCI6IjUzZTUwMTk2LTE4ZTMtNGJlNi04NDcyLTQ1ZDBjNDMxZjdhZCJ9LCJhY3MiOltdLCJpZHgiOiJuZnZ1bHRETE0tT0EzVUV3dVJHTDE3RFY1UXpIbWhac005Z2xMdnFLVGJacGh1T0NqMnBEUzByRk9XSXhaRjZLIiwidG9rZW5UeXBlIjoiYmVhcmVyIiwiZXhwIjoxNDkwMTQwNjYyLCJpcCI6IjIwNy4xNzMuMTY1LjUwIn0.luWnALBIv9TD_mGHUIddRpOnbVAhkYO-DJtEQitODQX2IEC7cIcbSrvJBKI3i_djeMj69fm-ctr6XFUU7WiEVnBsMh55WK1gkdFqFzImo67apQ5kAV8GTGGbG___kisjl12AMvL09_shU1Sp8F8cHayTZTmSbyyWbbFKT3cZCsg",
 | 
			
		||||
  "scope": "",
 | 
			
		||||
  "expires_in": 1800,
 | 
			
		||||
  "refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJqdGkiOiI1ZDBjMGMyNTkyNmZhNmMxOTM0MjlhZWRkMjJjOTEyYiIsImlhdCI6MTQ5MDEzODg2MiwiaXNzIjoib2F1dGgzLm9yZyIsImF1ZCI6Im9hdXRoMy5vcmciLCJhenAiOiJvYXV0aDMub3JnIiwic3ViIjoiIiwia2lkIjoib2F1dGgzLm9yZyIsInNjcCI6IiIsImFzIjoibG9naW4iLCJncnQiOiJwYXNzd29yZCIsInNydiI6ZmFsc2UsImsiOiJvYXV0aDMub3JnIiwiYXBwIjoib2F1dGgzLm9yZyIsImFjeCI6eyJpZCI6IjE1LUxhM3JnZXBFelBCR0xITHlrdEZOT1NDZFNVOXZJdjlKc2EzTkMxYVJUc3ZmUTZ5cDJuVFFfZWxmdkhzYTEifSwiYXhzIjpbXSwidXNyIjoiYTM3YWVkYTk5ZDQ5MThhMDM0YzM0MmQ2NGNkZjRiN2VkMjM0ZGZlNSIsImFjYyI6eyJpZCI6IjUzZTUwMTk2LTE4ZTMtNGJlNi04NDcyLTQ1ZDBjNDMxZjdhZCJ9LCJhY3MiOltdLCJpZHgiOiJuZnZ1bHRETE0tT0EzVUV3dVJHTDE3RFY1UXpIbWhac005Z2xMdnFLVGJacGh1T0NqMnBEUzByRk9XSXhaRjZLIiwicmVmcmVzaCI6dHJ1ZX0.XljVX_QXgnYw8gyIZjQdxfyDlAAwWtU-kLitibw2_3xo9muLFPCL_dAk5XnMRygyyh5B9H4p4qB2Gb5BEJKJRfAtQ6TeZadTBtxwoY7zcns9f4Nx59VNii4k_Xp3uhJ6fQp8ERvkMgBy52Sj5ag0PFnuIwk35wLdSfiikDGnwKo",
 | 
			
		||||
  "token": {
 | 
			
		||||
    "jti": "82e908e19c2b13b16c77be13d19cfa39",
 | 
			
		||||
    "iat": 1490138862,
 | 
			
		||||
    "iss": "oauth3.org",
 | 
			
		||||
    "aud": "oauth3.org",
 | 
			
		||||
    "azp": "oauth3.org",
 | 
			
		||||
    "sub": "15-La3rgepEzPBGLHLyktFNOSCdSU9vIv9Jsa3NC1aRTsvfQ6yp2nTQ_elfvHsa1",
 | 
			
		||||
    "kid": "oauth3.org",
 | 
			
		||||
    "scp": "",
 | 
			
		||||
    "as": "login",
 | 
			
		||||
    "grt": "password",
 | 
			
		||||
    "srv": false,
 | 
			
		||||
    "k": "oauth3.org",
 | 
			
		||||
    "app": "oauth3.org",
 | 
			
		||||
    "acx": {
 | 
			
		||||
      "id": "15-La3rgepEzPBGLHLyktFNOSCdSU9vIv9Jsa3NC1aRTsvfQ6yp2nTQ_elfvHsa1"
 | 
			
		||||
    },
 | 
			
		||||
    "axs": [],
 | 
			
		||||
    "usr": "a37aeda99d4918a034c342d64cdf4b7ed234dfe5",
 | 
			
		||||
    "acc": {
 | 
			
		||||
      "id": "53e50196-18e3-4be6-8472-45d0c431f7ad"
 | 
			
		||||
    },
 | 
			
		||||
    "acs": [],
 | 
			
		||||
    "idx": "nfvultDLM-OA3UEwuRGL17DV5QzHmhZsM9glLvqKTbZphuOCj2pDS0rFOWIxZF6K",
 | 
			
		||||
    "tokenType": "bearer",
 | 
			
		||||
    "exp": 1490140662,
 | 
			
		||||
    "ip": "207.173.165.50",
 | 
			
		||||
    "client_uri": "oauth3.org",
 | 
			
		||||
    "provider_uri": "https://oauth3.org"
 | 
			
		||||
  },
 | 
			
		||||
  "refresh": {
 | 
			
		||||
    "jti": "5d0c0c25926fa6c193429aedd22c912b",
 | 
			
		||||
    "iat": 1490138862,
 | 
			
		||||
    "iss": "oauth3.org",
 | 
			
		||||
    "aud": "oauth3.org",
 | 
			
		||||
    "azp": "oauth3.org",
 | 
			
		||||
    "sub": "15-La3rgepEzPBGLHLyktFNOSCdSU9vIv9Jsa3NC1aRTsvfQ6yp2nTQ_elfvHsa1",
 | 
			
		||||
    "kid": "oauth3.org",
 | 
			
		||||
    "scp": "",
 | 
			
		||||
    "as": "login",
 | 
			
		||||
    "grt": "password",
 | 
			
		||||
    "srv": false,
 | 
			
		||||
    "k": "oauth3.org",
 | 
			
		||||
    "app": "oauth3.org",
 | 
			
		||||
    "acx": {
 | 
			
		||||
      "id": "15-La3rgepEzPBGLHLyktFNOSCdSU9vIv9Jsa3NC1aRTsvfQ6yp2nTQ_elfvHsa1"
 | 
			
		||||
    },
 | 
			
		||||
    "axs": [],
 | 
			
		||||
    "usr": "a37aeda99d4918a034c342d64cdf4b7ed234dfe5",
 | 
			
		||||
    "acc": {
 | 
			
		||||
      "id": "53e50196-18e3-4be6-8472-45d0c431f7ad"
 | 
			
		||||
    },
 | 
			
		||||
    "acs": [],
 | 
			
		||||
    "idx": "nfvultDLM-OA3UEwuRGL17DV5QzHmhZsM9glLvqKTbZphuOCj2pDS0rFOWIxZF6K",
 | 
			
		||||
    "refresh": true,
 | 
			
		||||
    "provider_uri": "https://oauth3.org"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -9,7 +9,7 @@ OAUTH3.api['tunnel.token'] = function (providerUri, opts) {
 | 
			
		||||
  return OAUTH3.request({
 | 
			
		||||
    method: 'POST'
 | 
			
		||||
  , url: OAUTH3.url.normalize(providerUri)
 | 
			
		||||
      + '/api/tunnel@oauth3.org/accounts/' + session.token.sub + '/token'
 | 
			
		||||
      + '/api/org.oauth3.tunnel/accounts/' + session.token.sub + '/token'
 | 
			
		||||
  , session: session
 | 
			
		||||
  , data: {
 | 
			
		||||
      domains: opts.data.domains
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										10
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								package.json
									
									
									
									
									
								
							@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "oauth3.js",
 | 
			
		||||
  "version": "1.2.2",
 | 
			
		||||
  "version": "1.0.2",
 | 
			
		||||
  "description": "The world's smallest, fastest, and most secure OAuth3 (and OAuth2) JavaScript implementation.",
 | 
			
		||||
  "main": "oauth3.node.js",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
@ -9,7 +9,7 @@
 | 
			
		||||
  },
 | 
			
		||||
  "repository": {
 | 
			
		||||
    "type": "git",
 | 
			
		||||
    "url": "git@git.oauth3.org:OAuth3/oauth3.js.git"
 | 
			
		||||
    "url": "git@git.daplie.com:OAuth3/oauth3.js.git"
 | 
			
		||||
  },
 | 
			
		||||
  "keywords": [
 | 
			
		||||
    "oauth",
 | 
			
		||||
@ -32,10 +32,8 @@
 | 
			
		||||
    "sign"
 | 
			
		||||
  ],
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "bluebird": "^3.5.0",
 | 
			
		||||
    "elliptic": "^6.4.0",
 | 
			
		||||
    "request": "^2.81.0",
 | 
			
		||||
    "terminal-forms.js": "git+https://git.oauth3.org/OAuth3/terminal-forms.js.git#v1"
 | 
			
		||||
    "terminal-forms.js": "git+https://git.daplie.com/OAuth3/terminal-forms.js.git#v1"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "browserify-aes": "^1.0.6",
 | 
			
		||||
@ -49,6 +47,6 @@
 | 
			
		||||
    "gulp-uglify": "^2.1.0",
 | 
			
		||||
    "vinyl-source-stream": "^1.1.0"
 | 
			
		||||
  },
 | 
			
		||||
  "author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
 | 
			
		||||
  "author": "AJ ONeal <aj@daplie.com> (https://daplie.com/)",
 | 
			
		||||
  "license": "(MIT OR Apache-2.0)"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										560
									
								
								prefactor/oauth3.browser.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										560
									
								
								prefactor/oauth3.browser.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,560 @@
 | 
			
		||||
;(function (exports) {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  var OAUTH3 = exports.OAUTH3;
 | 
			
		||||
  var OAUTH3_CORE = exports.OAUTH3_CORE;
 | 
			
		||||
 | 
			
		||||
  function getDefaultAppUrl() {
 | 
			
		||||
    console.warn('[deprecated] using window.location.{protocol, host, pathname} when opts.client_id should be used');
 | 
			
		||||
    return window.location.protocol
 | 
			
		||||
      + '//' + window.location.host
 | 
			
		||||
      + (window.location.pathname).replace(/\/?$/, '')
 | 
			
		||||
      ;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  var browser = exports.OAUTH3_BROWSER = {
 | 
			
		||||
    window: window
 | 
			
		||||
  , clientUri: function (location) {
 | 
			
		||||
      return OAUTH3_CORE.normalizeUri(location.host + location.pathname);
 | 
			
		||||
    }
 | 
			
		||||
  , discover: function (providerUri, opts) {
 | 
			
		||||
      if (!providerUri) {
 | 
			
		||||
        throw new Error('oauth3.discover(providerUri, opts) received providerUri as ' + providerUri);
 | 
			
		||||
      }
 | 
			
		||||
      var directives = OAUTH3.hooks.getDirectives(providerUri);
 | 
			
		||||
      if (directives && directives.issuer) {
 | 
			
		||||
        return OAUTH3.PromiseA.resolve(directives);
 | 
			
		||||
      }
 | 
			
		||||
      return browser._discoverHelper(providerUri, opts).then(function (directives) {
 | 
			
		||||
        directives.issuer = directives.issuer || OAUTH3_CORE.normalizeUrl(providerUri);
 | 
			
		||||
        console.log('discoverHelper', directives);
 | 
			
		||||
        return OAUTH3.hooks.setDirectives(providerUri, directives);
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  , _discoverHelper: function (providerUri, opts) {
 | 
			
		||||
      opts = opts || {};
 | 
			
		||||
      //opts.debug = true;
 | 
			
		||||
      providerUri = OAUTH3_CORE.normalizeUrl(providerUri);
 | 
			
		||||
      if (window.location.hostname.match(providerUri)) {
 | 
			
		||||
        console.warn("It looks like you're a provider checking for your own directive,"
 | 
			
		||||
          + " so we we're just gonna use OAUTH3.request({ method: 'GET', url: '.well-known/oauth3/directive.json' })");
 | 
			
		||||
        return OAUTH3.request({
 | 
			
		||||
          method: 'GET'
 | 
			
		||||
        , url: OAUTH3.core.normalizeUrl(providerUri) + '/.well-known/oauth3/directives.json'
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!window.location.hostname.match(opts.client_id || opts.client_uri)) {
 | 
			
		||||
        console.warn("It looks like your client_id doesn't match your current window... this probably won't end well");
 | 
			
		||||
        console.warn(opts.client_id || opts.client_uri, window.location.hostname);
 | 
			
		||||
      }
 | 
			
		||||
      var discObj = OAUTH3_CORE.urls.discover(providerUri, { client_id: (opts.client_id || opts.client_uri || getDefaultAppUrl()), debug: opts.debug });
 | 
			
		||||
 | 
			
		||||
      // TODO ability to reuse iframe instead of closing
 | 
			
		||||
      return browser.insertIframe(discObj.url, discObj.state, opts).then(function (params) {
 | 
			
		||||
        if (params.error) {
 | 
			
		||||
          return OAUTH3_CORE.formatError(providerUri, params.error);
 | 
			
		||||
        }
 | 
			
		||||
        var directives = JSON.parse(atob(OAUTH3_CORE.utils.urlSafeBase64ToBase64(params.result || params.directives)));
 | 
			
		||||
        return directives;
 | 
			
		||||
      }, function (err) {
 | 
			
		||||
        return OAUTH3.PromiseA.reject(err);
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  , discoverAuthorizationDialog: function(providerUri, opts) {
 | 
			
		||||
      var discObj = OAUTH3.core.discover(providerUri, opts);
 | 
			
		||||
 | 
			
		||||
      // hmm... we're gonna need a broker for this since switching windows is distracting,
 | 
			
		||||
      // popups are obnoxious, iframes are sometimes blocked, and most servers don't implement CORS
 | 
			
		||||
      // eventually it should be the browser (and postMessage may be a viable option now), but whatever...
 | 
			
		||||
 | 
			
		||||
      // TODO allow postMessage from providerUri in addition to callback
 | 
			
		||||
      var discWin = OAUTH3.openWindow(discObj.url, discObj.state, { reuseWindow: 'conquerer' });
 | 
			
		||||
      return discWin.then(function (params) {
 | 
			
		||||
        console.log('discwin params');
 | 
			
		||||
        console.log(params);
 | 
			
		||||
        // discWin.child
 | 
			
		||||
        // TODO params should have response_type indicating json, binary, etc
 | 
			
		||||
        var directives = JSON.parse(atob(OAUTH3.core.utils.urlSafeBase64ToBase64(params.result || params.directives)));
 | 
			
		||||
        console.log('directives');
 | 
			
		||||
        console.log(directives);
 | 
			
		||||
 | 
			
		||||
        // Do some stuff
 | 
			
		||||
        var authObj = OAUTH3.core.implicitGrant(
 | 
			
		||||
          directives
 | 
			
		||||
        , { redirect_uri: opts.redirect_uri
 | 
			
		||||
          , debug: opts.debug
 | 
			
		||||
          , client_id: opts.client_id || opts.client_uri
 | 
			
		||||
          , client_uri: opts.client_uri || opts.client_id
 | 
			
		||||
          }
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if (params.debug) {
 | 
			
		||||
          window.alert("DEBUG MODE: Pausing so you can look at logs and whatnot :) Fire at will!");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new OAUTH3.PromiseA(function (resolve, reject) {
 | 
			
		||||
          // TODO check if authObj.url is relative or full
 | 
			
		||||
          discWin.child.location = OAUTH3.core.urls.resolve(providerUri, authObj.url);
 | 
			
		||||
 | 
			
		||||
          if (params.debug) {
 | 
			
		||||
            discWin.child.focus();
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          window['--oauth3-callback-' + authObj.state] = function (tokens) {
 | 
			
		||||
            if (tokens.error) {
 | 
			
		||||
              return reject(OAUTH3.core.formatError(tokens.error));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (params.debug || tokens.debug) {
 | 
			
		||||
              if (window.confirm("DEBUG MODE: okay to close oauth3 window?")) {
 | 
			
		||||
                discWin.child.close();
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
              discWin.child.close();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            resolve(tokens);
 | 
			
		||||
          };
 | 
			
		||||
        });
 | 
			
		||||
      }).then(function (tokens) {
 | 
			
		||||
        return OAUTH3.hooks.refreshSession(
 | 
			
		||||
          opts.session || {
 | 
			
		||||
            provider_uri: providerUri
 | 
			
		||||
          , client_id: opts.client_id
 | 
			
		||||
          , client_uri: opts.client_uri || opts.clientUri
 | 
			
		||||
          }
 | 
			
		||||
        , tokens
 | 
			
		||||
        );
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  , frameRequest: function (url, state, opts) {
 | 
			
		||||
      var promise;
 | 
			
		||||
 | 
			
		||||
      if (!opts.windowType) {
 | 
			
		||||
        opts.windowType = 'popup';
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if ('background' === opts.windowType) {
 | 
			
		||||
        promise = browser.insertIframe(url, state, opts);
 | 
			
		||||
      } else if ('popup' === opts.windowType) {
 | 
			
		||||
        promise = browser.openWindow(url, state, opts);
 | 
			
		||||
      } else if ('inline' === opts.windowType) {
 | 
			
		||||
        // callback function will never execute and would need to redirect back to current page
 | 
			
		||||
        // rather than the callback.html
 | 
			
		||||
        url += '&original_url=' + browser.window.location.href;
 | 
			
		||||
        promise = browser.window.location = url;
 | 
			
		||||
      } else {
 | 
			
		||||
        throw new Error("login framing method options.windowType not specified or not type yet implemented");
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return promise.then(function (params) {
 | 
			
		||||
        var err;
 | 
			
		||||
 | 
			
		||||
        if (params.error || params.error_description) {
 | 
			
		||||
          err = new Error(params.error_description || "Unknown response error");
 | 
			
		||||
          err.code = params.error || "E_UKNOWN_ERROR";
 | 
			
		||||
          err.params = params;
 | 
			
		||||
          return OAUTH3.PromiseA.reject(err);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return params;
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  , insertIframe: function (url, state, opts) {
 | 
			
		||||
      opts = opts || {};
 | 
			
		||||
      if (opts.debug) {
 | 
			
		||||
        opts.timeout = opts.timeout || 15 * 60 * 1000;
 | 
			
		||||
      }
 | 
			
		||||
      var promise = new OAUTH3.PromiseA(function (resolve, reject) {
 | 
			
		||||
        var tok;
 | 
			
		||||
        var iframeDiv;
 | 
			
		||||
 | 
			
		||||
        function cleanup() {
 | 
			
		||||
          delete window['--oauth3-callback-' + state];
 | 
			
		||||
          iframeDiv.remove();
 | 
			
		||||
          clearTimeout(tok);
 | 
			
		||||
          tok = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        window['--oauth3-callback-' + state] = function (params) {
 | 
			
		||||
          resolve(params);
 | 
			
		||||
          cleanup();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        tok = setTimeout(function () {
 | 
			
		||||
          var err = new Error("the iframe request did not complete within 15 seconds");
 | 
			
		||||
          err.code = "E_TIMEOUT";
 | 
			
		||||
          reject(err);
 | 
			
		||||
          cleanup();
 | 
			
		||||
        }, opts.timeout || 15 * 1000);
 | 
			
		||||
 | 
			
		||||
        // TODO hidden / non-hidden (via directive even)
 | 
			
		||||
        var framesrc = '<iframe class="js-oauth3-iframe" src="' + url + '" ';
 | 
			
		||||
        if (opts.debug) {
 | 
			
		||||
          framesrc += ' width="800px" height="800px" style="opacity: 0.8;" frameborder="1"';
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
          framesrc += ' width="1px" height="1px" frameborder="0"';
 | 
			
		||||
        }
 | 
			
		||||
        framesrc += '></iframe>';
 | 
			
		||||
 | 
			
		||||
        iframeDiv = window.document.createElement('div');
 | 
			
		||||
        iframeDiv.innerHTML = framesrc;
 | 
			
		||||
        window.document.body.appendChild(iframeDiv);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      // TODO periodically garbage collect expired handlers from window object
 | 
			
		||||
      return promise;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  , openWindow: function (url, state, opts) {
 | 
			
		||||
      if (opts.debug) {
 | 
			
		||||
        opts.timeout = opts.timeout || 15 * 60 * 1000;
 | 
			
		||||
      }
 | 
			
		||||
      var promise = new OAUTH3.PromiseA(function (resolve, reject) {
 | 
			
		||||
        var tok;
 | 
			
		||||
 | 
			
		||||
        function cleanup() {
 | 
			
		||||
          clearTimeout(tok);
 | 
			
		||||
          tok = null;
 | 
			
		||||
          delete window['--oauth3-callback-' + state];
 | 
			
		||||
          // this is last in case the window self-closes synchronously
 | 
			
		||||
          // (should never happen, but that's a negotiable implementation detail)
 | 
			
		||||
          if (!opts.reuseWindow) {
 | 
			
		||||
            promise.child.close();
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        window['--oauth3-callback-' + state] = function (params) {
 | 
			
		||||
          console.log('YOLO!!');
 | 
			
		||||
          resolve(params);
 | 
			
		||||
          cleanup();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        tok = setTimeout(function () {
 | 
			
		||||
          var err = new Error("the windowed request did not complete within 3 minutes");
 | 
			
		||||
          err.code = "E_TIMEOUT";
 | 
			
		||||
          reject(err);
 | 
			
		||||
          cleanup();
 | 
			
		||||
        }, opts.timeout || 3 * 60 * 1000);
 | 
			
		||||
 | 
			
		||||
        setTimeout(function () {
 | 
			
		||||
          if (!promise.child) {
 | 
			
		||||
            reject("TODO: open the iframe first and discover oauth3 directives before popup");
 | 
			
		||||
            cleanup();
 | 
			
		||||
          }
 | 
			
		||||
        }, 0);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      // TODO allow size changes (via directive even)
 | 
			
		||||
      promise.child = window.open(
 | 
			
		||||
        url
 | 
			
		||||
      , 'oauth3-login-' + (opts.reuseWindow || state)
 | 
			
		||||
      , 'height=' + (opts.height || 720) + ',width=' + (opts.width || 620)
 | 
			
		||||
      );
 | 
			
		||||
      // TODO periodically garbage collect expired handlers from window object
 | 
			
		||||
      return promise;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // Logins
 | 
			
		||||
    //
 | 
			
		||||
  , authn: {
 | 
			
		||||
      authorizationRedirect: function (providerUri, opts) {
 | 
			
		||||
        // TODO get own directives
 | 
			
		||||
 | 
			
		||||
        return OAUTH3.discover(providerUri, opts).then(function (directive) {
 | 
			
		||||
          var prequest = OAUTH3_CORE.urls.authorizationRedirect(
 | 
			
		||||
            directive
 | 
			
		||||
          , opts
 | 
			
		||||
          );
 | 
			
		||||
 | 
			
		||||
          if (!prequest.state) {
 | 
			
		||||
            throw new Error("[Devolper Error] [authorization redirect] prequest.state is empty");
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          return browser.frameRequest(prequest.url, prequest.state, opts);
 | 
			
		||||
        }).then(function (tokens) {
 | 
			
		||||
          return OAUTH3.hooks.refreshSession(
 | 
			
		||||
            opts.session || {
 | 
			
		||||
              provider_uri: providerUri
 | 
			
		||||
            , client_id: opts.client_id
 | 
			
		||||
            , client_uri: opts.client_uri || opts.clientUri
 | 
			
		||||
            }
 | 
			
		||||
          , tokens
 | 
			
		||||
          );
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    , implicitGrant: function (providerUri, opts) {
 | 
			
		||||
        // TODO let broker=true change behavior to open discover inline with frameRequest
 | 
			
		||||
        // TODO OAuth3 provider should use the redirect URI as the appId?
 | 
			
		||||
        return OAUTH3.discover(providerUri, opts).then(function (directive) {
 | 
			
		||||
          var prequest = OAUTH3_CORE.urls.implicitGrant(
 | 
			
		||||
            directive
 | 
			
		||||
            // TODO OAuth3 provider should referrer / referer / origin as the appId?
 | 
			
		||||
          , opts
 | 
			
		||||
          );
 | 
			
		||||
 | 
			
		||||
          if (!prequest.state) {
 | 
			
		||||
            throw new Error("[Devolper Error] [implicit grant] prequest.state is empty");
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          return browser.frameRequest(prequest.url, prequest.state, opts);
 | 
			
		||||
        }).then(function (tokens) {
 | 
			
		||||
          return OAUTH3.hooks.refreshSession(
 | 
			
		||||
            opts.session || {
 | 
			
		||||
              provider_uri: providerUri
 | 
			
		||||
            , client_id: opts.client_id
 | 
			
		||||
            , client_uri: opts.client_uri || opts.clientUri
 | 
			
		||||
            }
 | 
			
		||||
          , tokens
 | 
			
		||||
          );
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    , logout: function (providerUri, opts) {
 | 
			
		||||
        opts = opts || {};
 | 
			
		||||
 | 
			
		||||
        return OAUTH3.discover(providerUri, opts).then(function (directive) {
 | 
			
		||||
          var prequest = OAUTH3_CORE.urls.logout(
 | 
			
		||||
            directive
 | 
			
		||||
          , opts
 | 
			
		||||
          );
 | 
			
		||||
          // Oauth3.init({ logout: function () {} });
 | 
			
		||||
          //return Oauth3.logout();
 | 
			
		||||
 | 
			
		||||
          var redirectUri = opts.redirect_uri || opts.redirectUri
 | 
			
		||||
            || (window.location.protocol + '//' + (window.location.host + window.location.pathname) + 'oauth3.html')
 | 
			
		||||
            ;
 | 
			
		||||
          var params = {
 | 
			
		||||
            // logout=true for all logins/accounts
 | 
			
		||||
            // logout=app-scoped-login-id for a single login
 | 
			
		||||
            action: 'logout'
 | 
			
		||||
            // TODO specify specific accounts / logins to delete from session
 | 
			
		||||
          , accounts: true
 | 
			
		||||
          , logins: true
 | 
			
		||||
          , redirect_uri: redirectUri
 | 
			
		||||
          , state: prequest.state
 | 
			
		||||
          , debug: opts.debug
 | 
			
		||||
          };
 | 
			
		||||
 | 
			
		||||
          if (prequest.url === params.redirect_uri) {
 | 
			
		||||
            return OAUTH3.PromiseA.resolve();
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          prequest.url += '#' + OAUTH3_CORE.querystringify(params);
 | 
			
		||||
 | 
			
		||||
          return OAUTH3.insertIframe(prequest.url, prequest.state, opts);
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  , isIframe: function isIframe () {
 | 
			
		||||
      try {
 | 
			
		||||
        return window.self !== window.top;
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        return true;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  , parseUrl: function (url) {
 | 
			
		||||
      var parser = document.createElement('a');
 | 
			
		||||
      parser.href = url;
 | 
			
		||||
      return parser;
 | 
			
		||||
    }
 | 
			
		||||
  , isRedirectHostSafe: function (referrerUrl, redirectUrl) {
 | 
			
		||||
      var src = browser.parseUrl(referrerUrl);
 | 
			
		||||
      var dst = browser.parseUrl(redirectUrl);
 | 
			
		||||
 | 
			
		||||
      // TODO how should we handle subdomains?
 | 
			
		||||
      // It should be safe for api.example.com to redirect to example.com
 | 
			
		||||
      // But it may not be safe for to example.com to redirect to aj.example.com
 | 
			
		||||
      // It is also probably not safe for sally.example.com to redirect to john.example.com
 | 
			
		||||
      // The client should have a list of allowed URLs to choose from and perhaps a wildcard will do
 | 
			
		||||
      //
 | 
			
		||||
      // api.example.com.evil.com SHOULD NOT match example.com
 | 
			
		||||
      return dst.hostname === src.hostname;
 | 
			
		||||
    }
 | 
			
		||||
  , checkRedirect: function (client, query) {
 | 
			
		||||
      console.warn("[security] URL path checking not yet implemented");
 | 
			
		||||
 | 
			
		||||
      var clientUrl = OAUTH3.core.normalizeUrl(client.url);
 | 
			
		||||
      var redirectUrl = OAUTH3.core.normalizeUrl(query.redirect_uri);
 | 
			
		||||
 | 
			
		||||
      // General rule:
 | 
			
		||||
      // I can callback to a shorter domain (fewer subs) or a shorter path (on the same domain)
 | 
			
		||||
      // but not a longer (more subs) or different domain or a longer path (on the same domain)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      // We can callback to an explicitly listed domain (TODO and path)
 | 
			
		||||
      if (browser.isRedirectHostSafe(clientUrl, redirectUrl)) {
 | 
			
		||||
        return true;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
  /*
 | 
			
		||||
  , redirect: function (redirect) {
 | 
			
		||||
      if (parser.search) {
 | 
			
		||||
        parser.search += '&';
 | 
			
		||||
      } else {
 | 
			
		||||
        parser.search += '?';
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      parser.search += 'error=E_NO_SESSION';
 | 
			
		||||
      redirectUri = parser.href;
 | 
			
		||||
 | 
			
		||||
      window.location.href = redirectUri;
 | 
			
		||||
    }
 | 
			
		||||
  */
 | 
			
		||||
 | 
			
		||||
  , hackFormSubmit: function (opts) {
 | 
			
		||||
      opts = opts || {};
 | 
			
		||||
      scope.authorizationDecisionUri = DaplieApiConfig.providerUri + '/api/org.oauth3.provider/authorization_decision';
 | 
			
		||||
      scope.updateScope();
 | 
			
		||||
 | 
			
		||||
      var redirectUri = scope.appQuery.redirect_uri.replace(/^https?:\/\//i, 'https://');
 | 
			
		||||
      var separator;
 | 
			
		||||
 | 
			
		||||
      // TODO check that we appropriately use '#' for implicit and '?' for code
 | 
			
		||||
      // (server-side) in an OAuth2 backwards-compatible way
 | 
			
		||||
      if ('token' === scope.appQuery.response_type) {
 | 
			
		||||
        separator = '#';
 | 
			
		||||
      }
 | 
			
		||||
      else if ('code' === scope.appQuery.response_type) {
 | 
			
		||||
        separator = '?';
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        separator = '#';
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (scope.pendingScope.length && !opts.allow) {
 | 
			
		||||
        redirectUri += separator + Oauth3.querystringify({
 | 
			
		||||
          error: 'access_denied'
 | 
			
		||||
          , error_description: 'None of the permissions were accepted'
 | 
			
		||||
          , error_uri: 'https://oauth3.org/docs/errors#access_denied'
 | 
			
		||||
          , state: scope.appQuery.state
 | 
			
		||||
        });
 | 
			
		||||
        window.location.href = redirectUri;
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // TODO move to Oauth3? or not?
 | 
			
		||||
      // this could be implementation-specific,
 | 
			
		||||
      // but it may still be nice to provide it as de-facto
 | 
			
		||||
      var url = DaplieApiConfig.apiBaseUri + '/api/org.oauth3.provider/grants/:client_id/:account_id'
 | 
			
		||||
        .replace(/:client_id/g, scope.appQuery.client_id || scope.appQuery.client_uri)
 | 
			
		||||
        .replace(/:account_id/g, scope.selectedAccountId)
 | 
			
		||||
        ;
 | 
			
		||||
 | 
			
		||||
      var account = scope.sessionAccount;
 | 
			
		||||
      var session = { accessToken: account.token, refreshToken: account.refreshToken };
 | 
			
		||||
      var preq = {
 | 
			
		||||
        url: url
 | 
			
		||||
      , method: 'POST'
 | 
			
		||||
      , data: {
 | 
			
		||||
          scope: updateAccepted()
 | 
			
		||||
        , response_type: scope.appQuery.response_type
 | 
			
		||||
        , referrer: document.referrer || document.referer || ''
 | 
			
		||||
        , referer: document.referrer || document.referer || ''
 | 
			
		||||
        , tenant_id: scope.appQuery.tenant_id
 | 
			
		||||
        , client_id: scope.appQuery.client_id
 | 
			
		||||
        , client_uri: scope.appQuery.client_uri
 | 
			
		||||
        }
 | 
			
		||||
      , session: session
 | 
			
		||||
      };
 | 
			
		||||
      preq.clientId = preq.appId = DaplieApiConfig.appId || DaplieApiConfig.clientId;
 | 
			
		||||
      preq.clientUri = preq.appUri = DaplieApiConfig.appUri || DaplieApiConfig.clientUri;
 | 
			
		||||
      // TODO need a way to have middleware in Oauth3.request for TherapySession et al
 | 
			
		||||
 | 
			
		||||
      return Oauth3.request(preq).then(function (resp) {
 | 
			
		||||
        var err;
 | 
			
		||||
        var data = resp.data || {};
 | 
			
		||||
 | 
			
		||||
        if (data.error) {
 | 
			
		||||
          err = new Error(data.error.message || data.errorDescription);
 | 
			
		||||
          err.message = data.error.message || data.errorDescription;
 | 
			
		||||
          err.code = resp.data.error.code || resp.data.error;
 | 
			
		||||
          err.uri = 'https://oauth3.org/docs/errors#' + (resp.data.error.code || resp.data.error);
 | 
			
		||||
          return $q.reject(err);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!(data.code || data.accessToken)) {
 | 
			
		||||
          err = new Error("No grant code");
 | 
			
		||||
          return $q.reject(err);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return data;
 | 
			
		||||
      }).then(function (data) {
 | 
			
		||||
        redirectUri += separator + Oauth3.querystringify({
 | 
			
		||||
          state: scope.appQuery.state
 | 
			
		||||
 | 
			
		||||
        , code: data.code
 | 
			
		||||
 | 
			
		||||
        , access_token: data.access_token
 | 
			
		||||
        , expires_at: data.expires_at
 | 
			
		||||
        , expires_in: data.expires_in
 | 
			
		||||
        , scope: data.scope
 | 
			
		||||
 | 
			
		||||
        , refresh_token: data.refresh_token
 | 
			
		||||
        , refresh_expires_at: data.refresh_expires_at
 | 
			
		||||
        , refresh_expires_in: data.refresh_expires_in
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if ('token' === scope.appQuery.response_type) {
 | 
			
		||||
          window.location.href = redirectUri;
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
        else if ('code' === scope.appQuery.response_type) {
 | 
			
		||||
          scope.hackFormSubmitHelper(redirectUri);
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
          console.warn("Grant Code NOT IMPLEMENTED for '" + scope.appQuery.response_type + "'");
 | 
			
		||||
          console.warn(redirectUri);
 | 
			
		||||
          throw new Error("Grant Code NOT IMPLEMENTED for '" + scope.appQuery.response_type + "'");
 | 
			
		||||
        }
 | 
			
		||||
      }, function (err) {
 | 
			
		||||
        redirectUri += separator + Oauth3.querystringify({
 | 
			
		||||
          error: err.code || 'server_error'
 | 
			
		||||
        , error_description: err.message || "Server Error: It's not your fault"
 | 
			
		||||
        , error_uri: err.uri || 'https://oauth3.org/docs/errors#server_error'
 | 
			
		||||
        , state: scope.appQuery.state
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        console.error('Grant Code Error NOT IMPLEMENTED');
 | 
			
		||||
        console.error(err);
 | 
			
		||||
        console.error(redirectUri);
 | 
			
		||||
        //window.location.href = redirectUri;
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  , hackFormSubmitHelper: function (uri) {
 | 
			
		||||
      // TODO de-jQuerify
 | 
			
		||||
      //window.location.href = redirectUri;
 | 
			
		||||
      //return;
 | 
			
		||||
 | 
			
		||||
      // the only way to do a POST that redirects the current window
 | 
			
		||||
      window.jQuery('form.js-hack-hidden-form').attr('action', uri);
 | 
			
		||||
 | 
			
		||||
      // give time for the apply to take place
 | 
			
		||||
      window.setTimeout(function () {
 | 
			
		||||
        window.jQuery('form.js-hack-hidden-form').submit();
 | 
			
		||||
      }, 50);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
  browser.requests = browser.authn;
 | 
			
		||||
 | 
			
		||||
  Object.keys(browser).forEach(function (key) {
 | 
			
		||||
    if ('requests' === key) {
 | 
			
		||||
      Object.keys(browser.requests).forEach(function (key) {
 | 
			
		||||
        OAUTH3.requests[key] = browser.requests[key];
 | 
			
		||||
      });
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    OAUTH3[key] = browser[key];
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
}('undefined' !== typeof exports ? exports : window));
 | 
			
		||||
							
								
								
									
										42
									
								
								prefactor/oauth3.cache.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								prefactor/oauth3.cache.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
			
		||||
  oauth3.discover = function (providerUri, opts) {
 | 
			
		||||
    opts = opts || {};
 | 
			
		||||
 | 
			
		||||
    console.log('DEBUG oauth3.discover', providerUri);
 | 
			
		||||
    console.log(opts);
 | 
			
		||||
    if (opts.directives) {
 | 
			
		||||
      return oauth3.PromiseA.resolve(opts.directives);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var promise;
 | 
			
		||||
    var promise2;
 | 
			
		||||
    var directives;
 | 
			
		||||
    var updatedAt;
 | 
			
		||||
    var fresh;
 | 
			
		||||
 | 
			
		||||
    providerUri = oauth3.normalizeUrl(providerUri);
 | 
			
		||||
    try {
 | 
			
		||||
      directives = JSON.parse(localStorage.getItem('oauth3.' + providerUri + '.directives'));
 | 
			
		||||
      console.log('DEBUG oauth3.discover cache', directives);
 | 
			
		||||
      updatedAt = localStorage.getItem('oauth3.' + providerUri + '.directives.updated_at');
 | 
			
		||||
      console.log('DEBUG oauth3.discover updatedAt', updatedAt);
 | 
			
		||||
      updatedAt = new Date(updatedAt).valueOf();
 | 
			
		||||
      console.log('DEBUG oauth3.discover updatedAt', updatedAt);
 | 
			
		||||
    } catch(e) {
 | 
			
		||||
      // ignore
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fresh = (Date.now() - updatedAt) < (24 * 60 * 60 * 1000);
 | 
			
		||||
 | 
			
		||||
    if (directives) {
 | 
			
		||||
      promise = oauth3.PromiseA.resolve(directives);
 | 
			
		||||
 | 
			
		||||
      if (fresh) {
 | 
			
		||||
        //console.log('[local] [fresh directives]', directives);
 | 
			
		||||
        return promise;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    promise2 = oauth3._discoverHelper(providerUri, opts);
 | 
			
		||||
 | 
			
		||||
    return promise || promise2;
 | 
			
		||||
  };
 | 
			
		||||
							
								
								
									
										473
									
								
								prefactor/oauth3.core.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										473
									
								
								prefactor/oauth3.core.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,473 @@
 | 
			
		||||
;(function (exports) {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  // NOTE: we assume that directive.provider_uri exists
 | 
			
		||||
 | 
			
		||||
  var core = {};
 | 
			
		||||
  core.urls = core;
 | 
			
		||||
 | 
			
		||||
  function getDefaultAppApiBase() {
 | 
			
		||||
    console.warn('[deprecated] using window.location.host when opts.appApiBase should be used');
 | 
			
		||||
    return 'https://' + window.location.host;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  core.parsescope = function (scope) {
 | 
			
		||||
    return (scope||'').split(/[+, ]/g);
 | 
			
		||||
  };
 | 
			
		||||
  core.stringifyscope = function (scope) {
 | 
			
		||||
    if (Array.isArray(scope)) {
 | 
			
		||||
      scope = scope.join(' ');
 | 
			
		||||
    }
 | 
			
		||||
    return scope;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  core.querystringify = function (params) {
 | 
			
		||||
    var qs = [];
 | 
			
		||||
 | 
			
		||||
    Object.keys(params).forEach(function (key) {
 | 
			
		||||
      // TODO nullify instead?
 | 
			
		||||
      if ('undefined' === typeof params[key]) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if ('scope' === key) {
 | 
			
		||||
        params[key] = core.stringifyscope(params[key]);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      qs.push(encodeURIComponent(key) + '=' + encodeURIComponent(params[key]));
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return qs.join('&');
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // Modified from http://stackoverflow.com/a/7826782
 | 
			
		||||
  core.queryparse = 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;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  core.formatError = function (providerUri, params) {
 | 
			
		||||
    var err = new Error(params.error_description || params.error.message || "Unknown error when discoving provider '" + providerUri + "'");
 | 
			
		||||
    err.uri = params.error_uri || params.error.uri;
 | 
			
		||||
    err.code = params.error.code || params.error;
 | 
			
		||||
    return err;
 | 
			
		||||
  };
 | 
			
		||||
  core.normalizePath = function (path) {
 | 
			
		||||
    return path.replace(/^\//, '').replace(/\/$/, '');
 | 
			
		||||
  };
 | 
			
		||||
  core.normalizeUri = function (providerUri) {
 | 
			
		||||
    // tested with
 | 
			
		||||
    //   example.com
 | 
			
		||||
    //   example.com/
 | 
			
		||||
    //   http://example.com
 | 
			
		||||
    //   https://example.com/
 | 
			
		||||
    return providerUri
 | 
			
		||||
      .replace(/^(https?:\/\/)?/i, '')
 | 
			
		||||
      .replace(/\/?$/, '')
 | 
			
		||||
      ;
 | 
			
		||||
  };
 | 
			
		||||
  core.normalizeUrl = function (providerUri) {
 | 
			
		||||
    // tested with
 | 
			
		||||
    //   example.com
 | 
			
		||||
    //   example.com/
 | 
			
		||||
    //   http://example.com
 | 
			
		||||
    //   https://example.com/
 | 
			
		||||
    return providerUri
 | 
			
		||||
      .replace(/^(https?:\/\/)?/i, 'https://')
 | 
			
		||||
      .replace(/\/?$/, '')
 | 
			
		||||
      ;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // these might not really belong in core... not sure
 | 
			
		||||
  // there should be node.js- and browser-specific versions probably
 | 
			
		||||
  core.utils = {
 | 
			
		||||
    urlSafeBase64ToBase64: function (b64) {
 | 
			
		||||
      // URL-safe Base64 to Base64
 | 
			
		||||
      // https://en.wikipedia.org/wiki/Base64
 | 
			
		||||
      // https://gist.github.com/catwell/3046205
 | 
			
		||||
      var mod = b64.length % 4;
 | 
			
		||||
      if (2 === mod) { b64 += '=='; }
 | 
			
		||||
      if (3 === mod) { b64 += '='; }
 | 
			
		||||
      b64 = b64.replace(/-/g, '+').replace(/_/g, '/');
 | 
			
		||||
      return b64;
 | 
			
		||||
    }
 | 
			
		||||
  , base64ToUrlSafeBase64: function (b64) {
 | 
			
		||||
      // Base64 to URL-safe Base64
 | 
			
		||||
      b64 = b64.replace(/\+/g, '-').replace(/\//g, '_');
 | 
			
		||||
      b64 = b64.replace(/=+/g, '');
 | 
			
		||||
      return b64;
 | 
			
		||||
    }
 | 
			
		||||
  , randomState: function () {
 | 
			
		||||
      var i;
 | 
			
		||||
      var ch;
 | 
			
		||||
      var str;
 | 
			
		||||
 | 
			
		||||
      // TODO put in different file for browser vs node
 | 
			
		||||
      try {
 | 
			
		||||
        return Array.prototype.slice.call(window.crypto.getRandomValues(new Uint8Array(16))).map(function (ch) { return (ch).toString(16); }).join('');
 | 
			
		||||
      } catch(e) {
 | 
			
		||||
        // TODO use fisher-yates on 0..255 and select [0] 16 times
 | 
			
		||||
        // [security] https://medium.com/@betable/tifu-by-using-math-random-f1c308c4fd9d#.5qx0bf95a
 | 
			
		||||
        // https://github.com/v8/v8/blob/b0e4dce6091a8777bda80d962df76525dc6c5ea9/src/js/math.js#L135-L144
 | 
			
		||||
        // Note: newer versions of v8 do not have this bug, but other engines may still
 | 
			
		||||
        console.warn('[security] crypto.getRandomValues() failed, falling back to Math.random()');
 | 
			
		||||
        str = '';
 | 
			
		||||
        for (i = 0; i < 32; i += 1) {
 | 
			
		||||
          ch = Math.round(Math.random() * 255).toString(16);
 | 
			
		||||
          if (ch.length < 2) { ch = '0' + ch; }
 | 
			
		||||
          str += ch;
 | 
			
		||||
        }
 | 
			
		||||
        return str;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
  core.jwt = {
 | 
			
		||||
    // decode only (no verification)
 | 
			
		||||
    decode: function (str) {
 | 
			
		||||
 | 
			
		||||
      // 'abc.qrs.xyz'
 | 
			
		||||
      // [ 'abc', 'qrs', 'xyz' ]
 | 
			
		||||
      // [ {}, {}, 'foo' ]
 | 
			
		||||
      // { header: {}, payload: {}, signature: }
 | 
			
		||||
      var parts = str.split(/\./g);
 | 
			
		||||
      var jsons = parts.slice(0, 2).map(function (urlsafe64) {
 | 
			
		||||
        var atob = exports.atob || require('atob');
 | 
			
		||||
        var b64 = core.utils.urlSafeBase64ToBase64(urlsafe64);
 | 
			
		||||
        return atob(b64);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      return {
 | 
			
		||||
        header: JSON.parse(jsons[0])
 | 
			
		||||
      , payload: JSON.parse(jsons[1])
 | 
			
		||||
      , signature: parts[2] // should remain url-safe base64
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  , getFreshness: function (tokenMeta, staletime, now) {
 | 
			
		||||
      staletime = staletime || (15 * 60);
 | 
			
		||||
      now = now || Date.now();
 | 
			
		||||
      var fresh = ((parseInt(tokenMeta.exp, 10) || 0) - Math.round(now / 1000));
 | 
			
		||||
 | 
			
		||||
      if (fresh >= staletime) {
 | 
			
		||||
        return 'fresh';
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (fresh <= 0) {
 | 
			
		||||
        return 'expired';
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return 'stale';
 | 
			
		||||
    }
 | 
			
		||||
    // encode-only (no signature)
 | 
			
		||||
  , encode: function (parts) {
 | 
			
		||||
      parts.header = parts.header || { alg: 'none', typ: 'jwt' };
 | 
			
		||||
      parts.signature = parts.signature || '';
 | 
			
		||||
 | 
			
		||||
      var btoa = exports.btoa || require('btoa');
 | 
			
		||||
      var result = [
 | 
			
		||||
        core.utils.base64ToUrlSafeBase64(btoa(JSON.stringify(parts.header, null)))
 | 
			
		||||
      , core.utils.base64ToUrlSafeBase64(btoa(JSON.stringify(parts.payload, null)))
 | 
			
		||||
      , parts.signature // should already be url-safe base64
 | 
			
		||||
      ].join('.');
 | 
			
		||||
 | 
			
		||||
      return result;
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  core.urls.discover = function (providerUri, opts) {
 | 
			
		||||
    if (!providerUri) {
 | 
			
		||||
      throw new Error("cannot discover without providerUri");
 | 
			
		||||
    }
 | 
			
		||||
    if (!opts.client_id) {
 | 
			
		||||
      throw new Error("cannot discover without options.client_id");
 | 
			
		||||
    }
 | 
			
		||||
    var clientId = core.normalizeUrl(opts.client_id || opts.client_uri);
 | 
			
		||||
    providerUri = core.normalizeUrl(providerUri);
 | 
			
		||||
 | 
			
		||||
    var params = {
 | 
			
		||||
      action: 'directives'
 | 
			
		||||
    , state: core.utils.randomState()
 | 
			
		||||
    , redirect_uri: clientId + (opts.client_callback_path || '/.well-known/oauth3/callback.html')
 | 
			
		||||
    , response_type: 'rpc'
 | 
			
		||||
    , _method: 'GET'
 | 
			
		||||
    , _pathname: '.well-known/oauth3/directives.json'
 | 
			
		||||
    , debug: opts.debug || undefined
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    var result = {
 | 
			
		||||
      url: providerUri + '/.well-known/oauth3/#/?' + core.querystringify(params)
 | 
			
		||||
    , state: params.state
 | 
			
		||||
    , method: 'GET'
 | 
			
		||||
    , query: params
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
  };
 | 
			
		||||
  core.urls.authorizationCode = function (/*directive, scope, redirectUri, clientId*/) {
 | 
			
		||||
    //
 | 
			
		||||
    // Example Authorization Code Request
 | 
			
		||||
    // (not for use in the browser)
 | 
			
		||||
    //
 | 
			
		||||
    // GET https://example.com/api/org.oauth3.provider/authorization_dialog
 | 
			
		||||
    //  ?response_type=code
 | 
			
		||||
    //  &scope=`encodeURIComponent('profile.login profile.email')`
 | 
			
		||||
    //  &state=`cryptoutil.random().toString('hex')`
 | 
			
		||||
    //  &client_id=xxxxxxxxxxx
 | 
			
		||||
    //  &redirect_uri=`encodeURIComponent('https://myapp.com/oauth3.html')`
 | 
			
		||||
    //
 | 
			
		||||
    // NOTE: `redirect_uri` itself may also contain URI-encoded components
 | 
			
		||||
    //
 | 
			
		||||
    // NOTE: This probably shouldn't be done in the browser because the server
 | 
			
		||||
    //   needs to initiate the state. If it is done in a browser, the browser
 | 
			
		||||
    //   should probably request 'state' from the server beforehand
 | 
			
		||||
    //
 | 
			
		||||
 | 
			
		||||
    throw new Error("not implemented");
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  core.urls.authorizationRedirect = function (directive, opts) {
 | 
			
		||||
    //console.log('[authorizationRedirect]');
 | 
			
		||||
    //
 | 
			
		||||
    // Example Authorization Redirect - from Browser to Consumer API
 | 
			
		||||
    // (for generating a session securely on your own server)
 | 
			
		||||
    //
 | 
			
		||||
    // i.e. GET https://<<CONSUMER>>.com/api/org.oauth3.consumer/authorization_redirect/<<PROVIDER>>.com
 | 
			
		||||
    //
 | 
			
		||||
    // GET https://myapp.com/api/org.oauth3.consumer/authorization_redirect/`encodeURIComponent('example.com')`
 | 
			
		||||
    //  &scope=`encodeURIComponent('profile.login profile.email')`
 | 
			
		||||
    //
 | 
			
		||||
    // (optional)
 | 
			
		||||
    //  &state=`cryptoutil.random().toString('hex')`
 | 
			
		||||
    //  &redirect_uri=`encodeURIComponent('https://myapp.com/oauth3.html')`
 | 
			
		||||
    //
 | 
			
		||||
    // NOTE: This is not a request sent to the provider, but rather a request sent to the
 | 
			
		||||
    // consumer (your own API) which then sets some state and redirects.
 | 
			
		||||
    // This will initiate the `authorization_code` request on your server
 | 
			
		||||
    //
 | 
			
		||||
    opts = opts || {};
 | 
			
		||||
 | 
			
		||||
    var scope = opts.scope || directive.authn_scope;
 | 
			
		||||
    var providerUri = directive.provider_uri;
 | 
			
		||||
    var params = {
 | 
			
		||||
      state: core.utils.randomState()
 | 
			
		||||
    , debug: opts.debug || undefined
 | 
			
		||||
    };
 | 
			
		||||
    var slimProviderUri = encodeURIComponent(providerUri.replace(/^(https?|spdy):\/\//, ''));
 | 
			
		||||
    var authorizationRedirect = opts.authorizationRedirect;
 | 
			
		||||
 | 
			
		||||
    if (scope) {
 | 
			
		||||
      params.scope = scope;
 | 
			
		||||
    }
 | 
			
		||||
    if (opts.redirectUri) {
 | 
			
		||||
      // this is really only for debugging
 | 
			
		||||
      params.redirect_uri = opts.redirectUri;
 | 
			
		||||
    }
 | 
			
		||||
    // Note: the type check is necessary because we allow 'true'
 | 
			
		||||
    // as an automatic mechanism when it isn't necessary to specify
 | 
			
		||||
    if ('string' !== typeof authorizationRedirect) {
 | 
			
		||||
      // TODO oauth3.json for self?
 | 
			
		||||
      authorizationRedirect = (opts.appApiBase || getDefaultAppApiBase())
 | 
			
		||||
        + '/api/org.oauth3.consumer/authorization_redirect/:provider_uri';
 | 
			
		||||
    }
 | 
			
		||||
    authorizationRedirect = authorizationRedirect
 | 
			
		||||
      .replace(/!(provider_uri)/, slimProviderUri)
 | 
			
		||||
      .replace(/:provider_uri/, slimProviderUri)
 | 
			
		||||
      .replace(/#{provider_uri}/, slimProviderUri)
 | 
			
		||||
      .replace(/{{provider_uri}}/, slimProviderUri)
 | 
			
		||||
      ;
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      url: authorizationRedirect + '?' + core.querystringify(params)
 | 
			
		||||
    , method: 'GET'
 | 
			
		||||
    , state: params.state    // this becomes browser_state
 | 
			
		||||
    , params: params  // includes scope, final redirect_uri?
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  core.urls.implicitGrant = function (directive, opts) {
 | 
			
		||||
    //console.log('[implicitGrant]');
 | 
			
		||||
    //
 | 
			
		||||
    // 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
 | 
			
		||||
    //  ?response_type=token
 | 
			
		||||
    //  &scope=`encodeURIComponent('profile.login profile.email')`
 | 
			
		||||
    //  &state=`cryptoutil.random().toString('hex')`
 | 
			
		||||
    //  &client_id=xxxxxxxxxxx
 | 
			
		||||
    //  &redirect_uri=`encodeURIComponent('https://myapp.com/oauth3.html')`
 | 
			
		||||
    //
 | 
			
		||||
    // NOTE: `redirect_uri` itself may also contain URI-encoded components
 | 
			
		||||
    //
 | 
			
		||||
 | 
			
		||||
    opts = opts || {};
 | 
			
		||||
    var type = 'authorization_dialog';
 | 
			
		||||
    var responseType = 'token';
 | 
			
		||||
 | 
			
		||||
    var redirectUri = opts.redirect_uri;
 | 
			
		||||
    var scope = opts.scope || directive.authn_scope;
 | 
			
		||||
    var args = directive[type];
 | 
			
		||||
    var uri = args.url;
 | 
			
		||||
    var state = core.utils.randomState();
 | 
			
		||||
    var params = {
 | 
			
		||||
      debug: opts.debug || undefined
 | 
			
		||||
    , client_uri: opts.client_uri || opts.clientUri || undefined
 | 
			
		||||
    , client_id: opts.client_id || opts.client_uri || undefined
 | 
			
		||||
    };
 | 
			
		||||
    var result;
 | 
			
		||||
 | 
			
		||||
    params.state = state;
 | 
			
		||||
    params.response_type = responseType;
 | 
			
		||||
    if (scope) {
 | 
			
		||||
      params.scope = core.stringifyscope(scope);
 | 
			
		||||
    }
 | 
			
		||||
    if (!redirectUri) {
 | 
			
		||||
      // TODO consider making this optional
 | 
			
		||||
      console.error('missing redirect_uri');
 | 
			
		||||
    }
 | 
			
		||||
    params.redirect_uri = redirectUri;
 | 
			
		||||
 | 
			
		||||
    uri += '?' + core.querystringify(params);
 | 
			
		||||
 | 
			
		||||
    result = {
 | 
			
		||||
      url: uri
 | 
			
		||||
    , state: state
 | 
			
		||||
    , method: args.method
 | 
			
		||||
    , query: params
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  core.urls.resolve = function (base, next) {
 | 
			
		||||
    if (/^https:\/\//i.test(next)) {
 | 
			
		||||
      return next;
 | 
			
		||||
    }
 | 
			
		||||
    return core.normalizeUrl(base) + '/' + core.normalizePath(next);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  core.urls.refreshToken = function (directive, opts) {
 | 
			
		||||
    // grant_type=refresh_token
 | 
			
		||||
 | 
			
		||||
    // Example Refresh Token Request
 | 
			
		||||
    // (generally for 1st or 3rd party server-side, mobile, and desktop apps)
 | 
			
		||||
    //
 | 
			
		||||
    // POST https://example.com/api/oauth3/access_token
 | 
			
		||||
    //    { "grant_type": "refresh_token", "client_id": "<<id>>", "scope": "<<scope>>"
 | 
			
		||||
    //    , "username": "<<username>>", "password": "password" }
 | 
			
		||||
    //
 | 
			
		||||
    opts = opts || {};
 | 
			
		||||
    var type = 'access_token';
 | 
			
		||||
    var grantType = 'refresh_token';
 | 
			
		||||
 | 
			
		||||
    var scope = opts.scope || directive.authn_scope;
 | 
			
		||||
    var clientSecret = opts.appSecret || opts.clientSecret;
 | 
			
		||||
    var args = directive[type];
 | 
			
		||||
    var params = {
 | 
			
		||||
      "grant_type": grantType
 | 
			
		||||
    , "refresh_token": opts.refresh_token || opts.refreshToken || (opts.session && opts.session.refresh_token)
 | 
			
		||||
    , "response_type": 'token'
 | 
			
		||||
    , "client_id": opts.appId || opts.app_id || opts.client_id || opts.clientId || opts.client_id || opts.clientId
 | 
			
		||||
    , "client_uri": opts.client_uri || opts.clientUri
 | 
			
		||||
    //, "scope": undefined
 | 
			
		||||
    //, "client_secret": undefined
 | 
			
		||||
    , debug: opts.debug || undefined
 | 
			
		||||
    };
 | 
			
		||||
    var uri = args.url;
 | 
			
		||||
    var body;
 | 
			
		||||
 | 
			
		||||
    // TODO not allowed in the browser
 | 
			
		||||
    if (clientSecret) {
 | 
			
		||||
      params.client_secret = clientSecret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (scope) {
 | 
			
		||||
      params.scope = core.stringifyscope(scope);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ('GET' === args.method.toUpperCase()) {
 | 
			
		||||
      uri += '?' + core.querystringify(params);
 | 
			
		||||
    } else {
 | 
			
		||||
      body = params;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      url: uri
 | 
			
		||||
    , method: args.method
 | 
			
		||||
    , data: body
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  core.urls.logout = function (directive, opts) {
 | 
			
		||||
    opts = opts || {};
 | 
			
		||||
    var type = 'logout';
 | 
			
		||||
    var clientId = opts.appId || opts.clientId || opts.client_id;
 | 
			
		||||
    var args = directive[type];
 | 
			
		||||
    var params = {
 | 
			
		||||
      client_id: opts.clientUri || opts.client_uri
 | 
			
		||||
    , debug: opts.debug || undefined
 | 
			
		||||
    };
 | 
			
		||||
    var uri = args.url;
 | 
			
		||||
    var body;
 | 
			
		||||
 | 
			
		||||
    if (opts.clientUri) {
 | 
			
		||||
      params.client_uri = opts.clientUri;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (clientId) {
 | 
			
		||||
      params.client_id = clientId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    args.method = (args.method || 'GET').toUpperCase();
 | 
			
		||||
    if ('GET' === args.method) {
 | 
			
		||||
      uri += '?' + core.querystringify(params);
 | 
			
		||||
    } else {
 | 
			
		||||
      body = params;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      url: uri
 | 
			
		||||
    , method: args.method || 'GET'
 | 
			
		||||
    , data: body
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  exports.OAUTH3 = exports.OAUTH3 || { core: core };
 | 
			
		||||
  exports.OAUTH3_CORE = core.OAUTH3_CORE = core;
 | 
			
		||||
 | 
			
		||||
  if ('undefined' !== typeof module) {
 | 
			
		||||
    module.exports = core;
 | 
			
		||||
  }
 | 
			
		||||
}('undefined' !== typeof exports ? exports : window));
 | 
			
		||||
							
								
								
									
										302
									
								
								prefactor/oauth3.core.provider.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										302
									
								
								prefactor/oauth3.core.provider.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,302 @@
 | 
			
		||||
;(function (exports) {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  var core = window.OAUTH3_CORE;
 | 
			
		||||
 | 
			
		||||
  // Provider-Only
 | 
			
		||||
  core.urls.loginCode = function (directive, opts) {
 | 
			
		||||
    //
 | 
			
		||||
    // Example Resource Owner Password Request
 | 
			
		||||
    // (generally for 1st party and direct-partner mobile apps, and webapps)
 | 
			
		||||
    //
 | 
			
		||||
    // POST https://api.example.com/api/org.oauth3.provider/otp
 | 
			
		||||
    //    { "request_otp": true, "client_id": "<<id>>", "scope": "<<scope>>"
 | 
			
		||||
    //    , "username": "<<username>>" }
 | 
			
		||||
    //
 | 
			
		||||
    opts = opts || {};
 | 
			
		||||
    var clientId = opts.appId || opts.clientId;
 | 
			
		||||
 | 
			
		||||
    var args = directive.credential_otp;
 | 
			
		||||
    if (!directive.credential_otp) {
 | 
			
		||||
      console.log('[debug] loginCode directive:');
 | 
			
		||||
      console.log(directive);
 | 
			
		||||
    }
 | 
			
		||||
    var params = {
 | 
			
		||||
      "username": opts.id || opts.username
 | 
			
		||||
    , "request_otp": true // opts.requestOtp || undefined
 | 
			
		||||
    //, "jwt": opts.jwt // TODO sign a proof
 | 
			
		||||
    , debug: opts.debug || undefined
 | 
			
		||||
    };
 | 
			
		||||
    var uri = args.url;
 | 
			
		||||
    var body;
 | 
			
		||||
    if (opts.clientUri) {
 | 
			
		||||
      params.client_uri = opts.clientUri;
 | 
			
		||||
    }
 | 
			
		||||
    if (opts.clientAgreeTos) {
 | 
			
		||||
      params.client_agree_tos = opts.clientAgreeTos;
 | 
			
		||||
    }
 | 
			
		||||
    if (clientId) {
 | 
			
		||||
      params.client_id = clientId;
 | 
			
		||||
    }
 | 
			
		||||
    if ('GET' === args.method.toUpperCase()) {
 | 
			
		||||
      uri += '?' + core.querystringify(params);
 | 
			
		||||
    } else {
 | 
			
		||||
      body = params;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      url: uri
 | 
			
		||||
    , method: args.method
 | 
			
		||||
    , data: body
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  core.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
 | 
			
		||||
    //    { "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) {
 | 
			
		||||
        // for backwards compat
 | 
			
		||||
        opts.password = opts.otp; // 'otp:' + opts.otpUuid + ':' + opts.otp;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var scope = opts.scope || directive.authn_scope;
 | 
			
		||||
    var clientId = opts.appId || opts.clientId || opts.client_id;
 | 
			
		||||
    var clientAgreeTos = opts.clientAgreeTos || opts.client_agree_tos;
 | 
			
		||||
    var clientUri = opts.clientUri || opts.client_uri || opts.clientUrl || opts.client_url;
 | 
			
		||||
    var args = directive[type];
 | 
			
		||||
    var otpCode = opts.otp || opts.otpCode || opts.otp_code || opts.otpToken || opts.otp_token || undefined;
 | 
			
		||||
    var params = {
 | 
			
		||||
      "grant_type": grantType
 | 
			
		||||
    , "username": opts.username
 | 
			
		||||
    , "password": opts.password || otpCode || undefined
 | 
			
		||||
    , "totp": opts.totp || opts.totpToken || opts.totp_token || undefined
 | 
			
		||||
    , "otp": otpCode
 | 
			
		||||
    , "password_type": otpCode && 'otp'
 | 
			
		||||
    , "otp_code": otpCode
 | 
			
		||||
    , "otp_uuid": opts.otpUuid || opts.otp_uuid || undefined
 | 
			
		||||
    , "user_agent": opts.userAgent || opts.useragent || opts.user_agent || undefined // AJ's Macbook
 | 
			
		||||
    , "jwk": (opts.rememberDevice || opts.remember_device) && opts.jwk || undefined
 | 
			
		||||
    //, "public_key": opts.rememberDevice && opts.publicKey || undefined
 | 
			
		||||
    //, "public_key_type":  opts.rememberDevice && opts.publicKeyType || undefined // RSA/ECDSA
 | 
			
		||||
    //, "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 (clientId) {
 | 
			
		||||
      params.clientId = clientId;
 | 
			
		||||
    }
 | 
			
		||||
    if (clientUri) {
 | 
			
		||||
      params.clientUri = clientUri;
 | 
			
		||||
      params.clientAgreeTos = clientAgreeTos;
 | 
			
		||||
      if (!clientAgreeTos) {
 | 
			
		||||
        throw new Error('Developer Error: missing clientAgreeTos uri');
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (scope) {
 | 
			
		||||
      params.scope = core.stringifyscope(scope);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ('GET' === args.method.toUpperCase()) {
 | 
			
		||||
      uri += '?' + core.querystringify(params);
 | 
			
		||||
    } else {
 | 
			
		||||
      body = params;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      url: uri
 | 
			
		||||
    , method: args.method
 | 
			
		||||
    , data: body
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  core.urls.grants = function (directive, opts) {
 | 
			
		||||
    // directive = { issuer, authorization_decision }
 | 
			
		||||
    // opts = { response_type, scopes{ granted, requested, pending, accepted } }
 | 
			
		||||
 | 
			
		||||
    if (!opts) {
 | 
			
		||||
      throw new Error("You must supply a directive and an options object.");
 | 
			
		||||
    }
 | 
			
		||||
    if (!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.referrer) {
 | 
			
		||||
      console.warn("You should supply options.referrer");
 | 
			
		||||
    }
 | 
			
		||||
    if (!opts.method) {
 | 
			
		||||
      console.warn("You must supply options.method as either 'GET', or 'POST'");
 | 
			
		||||
    }
 | 
			
		||||
    if ('POST' === opts.method) {
 | 
			
		||||
      if ('string' !== typeof opts.scope) {
 | 
			
		||||
        console.warn("You should supply options.scope as a space-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'");
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var url = core.urls.resolve(directive.issuer, directive.grants.url)
 | 
			
		||||
      .replace(/(:azp|:client_id)/g, core.normalizeUri(opts.client_id || opts.client_uri))
 | 
			
		||||
      .replace(/(:sub|:account_id)/g, opts.session.token.sub)
 | 
			
		||||
      ;
 | 
			
		||||
    var data = {
 | 
			
		||||
      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
 | 
			
		||||
    };
 | 
			
		||||
    var body;
 | 
			
		||||
 | 
			
		||||
    if ('GET' === opts.method) {
 | 
			
		||||
      url += '?' + core.querystringify(data);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      body = data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      method: opts.method
 | 
			
		||||
    , url: url
 | 
			
		||||
    , data: body
 | 
			
		||||
    , session: opts.session
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
  core.urls.authorizationDecision = function (directive, opts) {
 | 
			
		||||
    var url = core.urls.resolve(directive.issuer, directive.authorization_decision.url);
 | 
			
		||||
    if (!opts) {
 | 
			
		||||
      throw new Error("You must supply a directive and an options object");
 | 
			
		||||
    }
 | 
			
		||||
    console.info(url);
 | 
			
		||||
    throw new Error("NOT IMPLEMENTED authorization_decision");
 | 
			
		||||
  };
 | 
			
		||||
  core.authz = core.authz || {};
 | 
			
		||||
  core.authz.scopes = function (session, clientParams) {
 | 
			
		||||
    // OAuth3.requests.grants(providerUri, {});         // return list of grants
 | 
			
		||||
    // OAuth3.checkGrants(providerUri, {});             //
 | 
			
		||||
    var clientUri = OAUTH3.core.normalizeUri(clientParams.client_uri || window.document.referrer);
 | 
			
		||||
    var scope = clientParams.scope || '';
 | 
			
		||||
    var clientObj = clientParams;
 | 
			
		||||
 | 
			
		||||
    if (!scope) {
 | 
			
		||||
      scope = 'oauth3_authn';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //$('.js-user-avatar').attr('src', userAvatar);
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    console.log('grants options');
 | 
			
		||||
    console.log(loc.hash);
 | 
			
		||||
    console.log(loc.search);
 | 
			
		||||
    console.log(clientObj);
 | 
			
		||||
    console.log(session.token);
 | 
			
		||||
    console.log(window.document.referrer);
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    return OAUTH3.requests.grants(CONFIG.host, {
 | 
			
		||||
      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;
 | 
			
		||||
 | 
			
		||||
      console.log('previous grants:');
 | 
			
		||||
      console.log(grantResults);
 | 
			
		||||
 | 
			
		||||
      if (grantResults.data.error) {
 | 
			
		||||
        window.alert('grantResults: ' + grantResults.data.error_description || grantResults.data.error.message);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // 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.checkRedirect(grantResults.data.client, clientObj)) {
 | 
			
		||||
        callbackUrl = 'https://oauth3.org/docs/errors#E_REDIRECT_ATTACK'
 | 
			
		||||
          + '?redirect_uri=' + clientObj.redirect_uri
 | 
			
		||||
          + '&allowed_urls=' + grantResults.data.client.url
 | 
			
		||||
          + '&client_id=' + clientUri
 | 
			
		||||
          + '&referrer_uri=' + OAUTH3.core.normalizeUri(window.document.referrer)
 | 
			
		||||
          ;
 | 
			
		||||
        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.originalData||grantResults.data).grants.filter(function (grant) {
 | 
			
		||||
        if (clientUri === (grant.azp || grant.oauth_client_id || grant.oauthClientId)) {
 | 
			
		||||
          return true;
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      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)
 | 
			
		||||
      };
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  exports.OAUTH3_CORE_PROVIDER = core;
 | 
			
		||||
 | 
			
		||||
  if ('undefined' !== typeof module) {
 | 
			
		||||
    module.exports = core;
 | 
			
		||||
  }
 | 
			
		||||
}('undefined' !== typeof exports ? exports : window));
 | 
			
		||||
							
								
								
									
										109
									
								
								prefactor/oauth3.jquery.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								prefactor/oauth3.jquery.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,109 @@
 | 
			
		||||
(function () {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  // I did try to shim jQuery's deferred, but it's just too clunky.
 | 
			
		||||
  // Here I use es6-promise which lacks asynchrity, but it's the smallest Promise implementation.
 | 
			
		||||
  // Only Opera Mini and MSIE (even on 11) will use this shim, so no biggie;
 | 
			
		||||
 | 
			
		||||
  var oauth3 = window.OAUTH3;
 | 
			
		||||
  var count = 0;
 | 
			
		||||
 | 
			
		||||
  function inject() {
 | 
			
		||||
    count += 1;
 | 
			
		||||
 | 
			
		||||
    if (count >= 100) {
 | 
			
		||||
      throw new Error("you forgot to include rsvp.js, methinks");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     *
 | 
			
		||||
        [window.Promise, window.ES6Promise, window.RSVP.Promise].forEach(function (PromiseA) {
 | 
			
		||||
          var x = 1; new PromiseA(function (resolve, reject) { console.log('x', 1 === x); resolve(); }); x = 2; void null;
 | 
			
		||||
          var y = 1; PromiseA.resolve().then(function () { console.log('y', 2 === x); }); y = 2; void null;
 | 
			
		||||
        });
 | 
			
		||||
     */
 | 
			
		||||
    var PromiseA = /*(window.RSVP && window.RSVP.Promise) || window.ES6Promise || */window.Promise;
 | 
			
		||||
    if ('undefined' !== typeof PromiseA) {
 | 
			
		||||
      oauth3.providePromise(PromiseA).then(function () {
 | 
			
		||||
        // ignore
 | 
			
		||||
        window.jqOauth3 = oauth3;
 | 
			
		||||
      }, function (err) {
 | 
			
		||||
        console.error(err);
 | 
			
		||||
        console.error("Bad Promise Implementation!");
 | 
			
		||||
      });
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // because MSIE can't tell when a script is loaded
 | 
			
		||||
    setTimeout(inject, 100);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ('undefined' === typeof Promise) {
 | 
			
		||||
    // support Opera Mini and MSIE 11+ (which doesn't support <!-- [if IE]> detection)
 | 
			
		||||
    /* jshint ignore: start */
 | 
			
		||||
    document.write('<script src="bower_components/es6-promise/promise.min.js"></script>');
 | 
			
		||||
    /* jshint ignore: end */
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    // I would have used this, but it turns out that
 | 
			
		||||
    // MSIE can't tell when a script has loaded
 | 
			
		||||
    var js = document.createElement("script");
 | 
			
		||||
    js.setAttribute("src", "bower_components/es6-promise/promise.js");
 | 
			
		||||
    js.setAttribute("type", "text/javascript");
 | 
			
		||||
    document.getElementsByTagName("head")[0].appendChild(js);
 | 
			
		||||
    */
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  inject();
 | 
			
		||||
 | 
			
		||||
  function Request(opts) {
 | 
			
		||||
    if (!opts.method) {
 | 
			
		||||
      throw new Error("Developer Error: you must set method as one of 'GET', 'POST', 'DELETE', etc");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var req = {
 | 
			
		||||
      url: opts.url
 | 
			
		||||
      // Noted: jQuery 1.9 finally added 'method' as an alias of 'type'
 | 
			
		||||
    , method: opts.method
 | 
			
		||||
      // leaving type for backwards compat
 | 
			
		||||
    , type: opts.method
 | 
			
		||||
    , headers: opts.headers || {}
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // don't allow accidetal querystring via 'data'
 | 
			
		||||
    if (opts.data && !/get|delete/i.test(opts.method)) {
 | 
			
		||||
      req.data = opts.data;
 | 
			
		||||
      if (opts.data && 'object' === typeof opts.data) {
 | 
			
		||||
        req.data = JSON.stringify(req.data);
 | 
			
		||||
        req.headers['Content-Type'] = 'application/json; charset=utf-8';
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // I don't trust jQuery promises...
 | 
			
		||||
    return new oauth3.PromiseA(function (resolve, reject) {
 | 
			
		||||
      $.ajax(req).then(function (data, textStatus, jqXhr) {
 | 
			
		||||
        var resp = {};
 | 
			
		||||
 | 
			
		||||
        Object.keys(jqXhr).forEach(function (key) {
 | 
			
		||||
          // particularly we have to get rid of .then
 | 
			
		||||
          if ('function' !== typeof jqXhr[key]) {
 | 
			
		||||
            resp[key] = jqXhr[key];
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        resp.data = data;
 | 
			
		||||
        resp.status = textStatus;
 | 
			
		||||
        resp.request = jqXhr;
 | 
			
		||||
        resolve(resp);
 | 
			
		||||
      }, function (jqXhr, textStatus, errorThrown) {
 | 
			
		||||
        errorThrown.request = jqXhr;
 | 
			
		||||
        errorThrown.response = jqXhr;
 | 
			
		||||
        errorThrown.status = textStatus;
 | 
			
		||||
        reject(errorThrown);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  oauth3.provideRequest(Request);
 | 
			
		||||
}());
 | 
			
		||||
							
								
								
									
										445
									
								
								prefactor/oauth3.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										445
									
								
								prefactor/oauth3.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,445 @@
 | 
			
		||||
/* global Promise */
 | 
			
		||||
(function (exports) {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  var oauth3 = {};
 | 
			
		||||
 | 
			
		||||
  var core = exports.OAUTH3_CORE || require('./oauth3.core.js');
 | 
			
		||||
 | 
			
		||||
  oauth3.requests = {};
 | 
			
		||||
 | 
			
		||||
  if ('undefined' !== typeof Promise) {
 | 
			
		||||
    oauth3.PromiseA = Promise;
 | 
			
		||||
  } else {
 | 
			
		||||
    console.warn("[oauth3.js] Remember to call oauth3.providePromise(Promise) with a proper Promise implementation");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  oauth3.providePromise = function (PromiseA) {
 | 
			
		||||
    oauth3.PromiseA = PromiseA;
 | 
			
		||||
    if (oauth3._testPromise) {
 | 
			
		||||
      return oauth3._testPromise(PromiseA).then(function () {
 | 
			
		||||
        oauth3.PromiseA = PromiseA;
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    oauth3.PromiseA = PromiseA;
 | 
			
		||||
    return PromiseA.resolve();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // TODO move recase out
 | 
			
		||||
  /*
 | 
			
		||||
  oauth3._recaseRequest = function (recase, req) {
 | 
			
		||||
    // convert JavaScript camelCase to oauth3/ruby snake_case
 | 
			
		||||
    if (req.data && 'object' === typeof req.data) {
 | 
			
		||||
      req.originalData = req.data;
 | 
			
		||||
      req.data = recase.snakeCopy(req.data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return req;
 | 
			
		||||
  };
 | 
			
		||||
  oauth3._recaseResponse = function (recase, resp) {
 | 
			
		||||
    // convert oauth3/ruby snake_case to JavaScript camelCase
 | 
			
		||||
    if (resp.data && 'object' === typeof resp.data) {
 | 
			
		||||
      resp.originalData = resp.data;
 | 
			
		||||
      resp.data = recase.camelCopy(resp.data);
 | 
			
		||||
    }
 | 
			
		||||
    return resp;
 | 
			
		||||
  };
 | 
			
		||||
  */
 | 
			
		||||
 | 
			
		||||
  oauth3.hooks = {
 | 
			
		||||
    checkSession: function (preq, opts) {
 | 
			
		||||
      if (!preq.session) {
 | 
			
		||||
        console.warn('[oauth3.hooks.checkSession] no session');
 | 
			
		||||
        return oauth3.PromiseA.resolve(null);
 | 
			
		||||
      }
 | 
			
		||||
      var freshness = oauth3.core.jwt.getFreshness(preq.session.token, opts.staletime);
 | 
			
		||||
      console.info('[oauth3.hooks.checkSession] freshness', freshness, preq.session);
 | 
			
		||||
 | 
			
		||||
      switch (freshness) {
 | 
			
		||||
        case 'stale':
 | 
			
		||||
          return oauth3.hooks.sessionStale(preq.session);
 | 
			
		||||
        case 'expired':
 | 
			
		||||
          return oauth3.hooks.sessionExpired(preq.session).then(function (newSession) {
 | 
			
		||||
            preq.session = newSession;
 | 
			
		||||
            return newSession;
 | 
			
		||||
          });
 | 
			
		||||
        //case 'fresh':
 | 
			
		||||
        default:
 | 
			
		||||
          return oauth3.PromiseA.resolve(preq.session);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  , sessionStale: function (staleSession) {
 | 
			
		||||
      console.info('[oauth3.hooks.sessionStale] called');
 | 
			
		||||
      if (oauth3.hooks._stalePromise) {
 | 
			
		||||
        return oauth3.PromiseA.resolve(staleSession);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      oauth3.hooks._stalePromise = oauth3.requests.refreshToken(
 | 
			
		||||
        staleSession.provider_uri
 | 
			
		||||
      , { client_uri: staleSession.client_uri
 | 
			
		||||
        , session: staleSession
 | 
			
		||||
        , debug: staleSession.debug
 | 
			
		||||
        }
 | 
			
		||||
      ).then(function (newSession) {
 | 
			
		||||
        oauth3.hooks._stalePromise = null;
 | 
			
		||||
        return newSession; // oauth3.hooks.refreshSession(staleSession, newSession);
 | 
			
		||||
      }, function () {
 | 
			
		||||
        oauth3.hooks._stalePromise = null;
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      return oauth3.PromiseA.resolve(staleSession);
 | 
			
		||||
    }
 | 
			
		||||
  , sessionExpired: function (expiredSession) {
 | 
			
		||||
      console.info('[oauth3.hooks.sessionExpired] called');
 | 
			
		||||
      return oauth3.requests.refreshToken(
 | 
			
		||||
        expiredSession.provider_uri
 | 
			
		||||
      , { client_uri: expiredSession.client_uri
 | 
			
		||||
        , session: expiredSession
 | 
			
		||||
        , debug: expiredSession.debug
 | 
			
		||||
        }
 | 
			
		||||
      ).then(function (newSession) {
 | 
			
		||||
        return newSession; // oauth3.hooks.refreshSession(expiredSession, newSession);
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  , refreshSession: function (oldSession, newSession) {
 | 
			
		||||
      var providerUri = oldSession.provider_uri;
 | 
			
		||||
      var clientUri = oldSession.client_uri;
 | 
			
		||||
 | 
			
		||||
      console.info('[oauth3.hooks.refreshSession] oldSession', JSON.parse(JSON.stringify(oldSession)));
 | 
			
		||||
      console.info('[oauth3.hooks.refreshSession] newSession', newSession);
 | 
			
		||||
      // shim for account create which does not return new refresh_token
 | 
			
		||||
      newSession.refresh_token = newSession.refresh_token || oldSession.refresh_token;
 | 
			
		||||
      Object.keys(oldSession).forEach(function (key) {
 | 
			
		||||
        oldSession[key] = undefined;
 | 
			
		||||
      });
 | 
			
		||||
      Object.keys(newSession).forEach(function (key) {
 | 
			
		||||
        oldSession[key] = newSession[key];
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      // info about the session of this API call
 | 
			
		||||
      oldSession.provider_uri = providerUri;  // aud
 | 
			
		||||
      oldSession.client_uri = clientUri;      // azp
 | 
			
		||||
 | 
			
		||||
      // info about the newly-discovered token
 | 
			
		||||
      oldSession.token = oldSession.meta = core.jwt.decode(oldSession.access_token).payload;
 | 
			
		||||
 | 
			
		||||
      oldSession.token.sub = oldSession.token.sub
 | 
			
		||||
        || (oldSession.token.acx && oldSession.token.acx.id)
 | 
			
		||||
        || (oldSession.token.axs && oldSession.token.axs.length && oldSession.token.axs[0].appScopedId)
 | 
			
		||||
        ;
 | 
			
		||||
      oldSession.token.client_uri = clientUri;
 | 
			
		||||
      oldSession.token.provider_uri = providerUri;
 | 
			
		||||
 | 
			
		||||
      if (!oldSession.token.sub) {
 | 
			
		||||
        // TODO this is broken hard
 | 
			
		||||
        console.warn('TODO implementation for OAUTH3.hooks.accounts.create (GUI, CLI, or API)');
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (oldSession.refresh_token) {
 | 
			
		||||
        oldSession.refresh = core.jwt.decode(oldSession.refresh_token).payload;
 | 
			
		||||
        oldSession.refresh.sub = oldSession.refresh.sub
 | 
			
		||||
          || (oldSession.refresh.acx && oldSession.refresh.acx.id)
 | 
			
		||||
          || (oldSession.refresh.axs && oldSession.refresh.axs.length && oldSession.refresh.axs[0].appScopedId)
 | 
			
		||||
          ;
 | 
			
		||||
        oldSession.refresh.provider_uri = providerUri;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      console.info('[oauth3.hooks.refreshSession] refreshedSession', oldSession);
 | 
			
		||||
 | 
			
		||||
      // set for a set of audiences
 | 
			
		||||
      return oauth3.PromiseA.resolve(oauth3.hooks.setSession(providerUri, oldSession));
 | 
			
		||||
    }
 | 
			
		||||
  , setSession: function (providerUri, newSession) {
 | 
			
		||||
      if (!providerUri) {
 | 
			
		||||
        console.error(new Error('no providerUri').stack);
 | 
			
		||||
      }
 | 
			
		||||
      providerUri = oauth3.core.normalizeUri(providerUri);
 | 
			
		||||
      console.warn('[ERROR] Please implement OAUTH3.hooks.setSession = function (providerUri, newSession) { return newSession; }');
 | 
			
		||||
      console.warn(newSession);
 | 
			
		||||
      if (!oauth3.hooks._sessions) { oauth3.hooks._sessions = {}; }
 | 
			
		||||
      oauth3.hooks._sessions[providerUri] = newSession;
 | 
			
		||||
      return newSession;
 | 
			
		||||
    }
 | 
			
		||||
  , getSession: function (providerUri) {
 | 
			
		||||
      providerUri = oauth3.core.normalizeUri(providerUri);
 | 
			
		||||
      console.warn('[ERROR] Please implement OAUTH3.hooks.getSession = function (providerUri) { return savedSession; }');
 | 
			
		||||
      if (!oauth3.hooks._sessions) { oauth3.hooks._sessions = {}; }
 | 
			
		||||
      return oauth3.hooks._sessions[providerUri];
 | 
			
		||||
    }
 | 
			
		||||
  , setDirectives: function (providerUri, directives) {
 | 
			
		||||
      providerUri = oauth3.core.normalizeUri(providerUri);
 | 
			
		||||
      console.warn('[oauth3.hooks.setDirectives] PLEASE IMPLEMENT -- Your Fault');
 | 
			
		||||
      console.warn(directives);
 | 
			
		||||
      if (!oauth3.hooks._directives) { oauth3.hooks._directives = {}; }
 | 
			
		||||
      window.localStorage.setItem('directives-' + providerUri, JSON.stringify(directives));
 | 
			
		||||
      oauth3.hooks._directives[providerUri] = directives;
 | 
			
		||||
      return directives;
 | 
			
		||||
    }
 | 
			
		||||
  , getDirectives: function (providerUri) {
 | 
			
		||||
      providerUri = oauth3.core.normalizeUri(providerUri);
 | 
			
		||||
      console.warn('[oauth3.hooks.getDirectives] PLEASE IMPLEMENT -- Your Fault');
 | 
			
		||||
      if (!oauth3.hooks._directives) { oauth3.hooks._directives = {}; }
 | 
			
		||||
      return JSON.parse(window.localStorage.getItem('directives-' + providerUri) || '{}');
 | 
			
		||||
      //return oauth3.hooks._directives[providerUri];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Provider Only
 | 
			
		||||
  , setGrants: function (clientUri, newGrants) {
 | 
			
		||||
      clientUri = oauth3.core.normalizeUri(clientUri);
 | 
			
		||||
      console.warn('[oauth3.hooks.setGrants] PLEASE IMPLEMENT -- Your Fault');
 | 
			
		||||
      console.warn(newGrants);
 | 
			
		||||
      if (!oauth3.hooks._grants) { oauth3.hooks._grants = {}; }
 | 
			
		||||
      console.log('clientUri, newGrants');
 | 
			
		||||
      console.log(clientUri, newGrants);
 | 
			
		||||
      oauth3.hooks._grants[clientUri] = newGrants;
 | 
			
		||||
      return newGrants;
 | 
			
		||||
    }
 | 
			
		||||
  , getGrants: function (clientUri) {
 | 
			
		||||
      clientUri = oauth3.core.normalizeUri(clientUri);
 | 
			
		||||
      console.warn('[oauth3.hooks.getGrants] PLEASE IMPLEMENT -- Your Fault');
 | 
			
		||||
      if (!oauth3.hooks._grants) { oauth3.hooks._grants = {}; }
 | 
			
		||||
      console.log('clientUri, existingGrants');
 | 
			
		||||
      console.log(clientUri, oauth3.hooks._grants[clientUri]);
 | 
			
		||||
      return oauth3.hooks._grants[clientUri];
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // TODO simplify (nix recase)
 | 
			
		||||
  oauth3.provideRequest = function (rawRequest, opts) {
 | 
			
		||||
    opts = opts || {};
 | 
			
		||||
    //var Recase = exports.Recase || require('recase');
 | 
			
		||||
    // TODO make insensitive to providing exceptions
 | 
			
		||||
    //var recase = Recase.create({ exceptions: {} });
 | 
			
		||||
 | 
			
		||||
    function lintAndRequest(preq) {
 | 
			
		||||
      function goGetHer() {
 | 
			
		||||
        if (preq.session) {
 | 
			
		||||
          // TODO check session.token.aud against preq.url to make sure they match
 | 
			
		||||
          console.warn("[security] session audience checking has not been implemented yet (it's up to you to check)");
 | 
			
		||||
          preq.headers = preq.headers || {};
 | 
			
		||||
          preq.headers.Authorization = 'Bearer ' + preq.session.access_token;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!oauth3._lintRequest) {
 | 
			
		||||
          return rawRequest(preq);
 | 
			
		||||
        }
 | 
			
		||||
        return oauth3._lintRequest(preq, opts).then(function (preq) {
 | 
			
		||||
          return rawRequest(preq);
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!preq.session) {
 | 
			
		||||
        return goGetHer();
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      console.warn('lintAndRequest checkSession', preq);
 | 
			
		||||
      return oauth3.hooks.checkSession(preq, opts).then(goGetHer);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (opts.rawCase) {
 | 
			
		||||
      oauth3.request = lintAndRequest;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Wrap oauth3 api calls in snake_case / camelCase conversion
 | 
			
		||||
    oauth3.request = function (req, opts) {
 | 
			
		||||
      //console.log('[D] [oauth3 req.url]', req.url);
 | 
			
		||||
      opts = opts || {};
 | 
			
		||||
 | 
			
		||||
      if (opts.rawCase) {
 | 
			
		||||
        return lintAndRequest(req, opts);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      //req = oauth3._recaseRequest(recase, req);
 | 
			
		||||
      return lintAndRequest(req, opts).then(function (res) {
 | 
			
		||||
        //return oauth3._recaseResponse(recase, res);
 | 
			
		||||
        return res;
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    return oauth3._testRequest(request).then(function () {
 | 
			
		||||
      oauth3.request = request;
 | 
			
		||||
    });
 | 
			
		||||
    */
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // TODO merge with regular token access point and new response_type=federated ?
 | 
			
		||||
  oauth3.requests.clientToken = function (providerUri, opts) {
 | 
			
		||||
    return oauth3.discover(providerUri, opts).then(function (directive) {
 | 
			
		||||
      return oauth3.request(core.urls.grants(directive, opts)).then(function (grantsResult) {
 | 
			
		||||
        return grantsResult.originalData || grantsResult.data;
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
  oauth3.requests.grants = function (providerUri, opts) {
 | 
			
		||||
    return oauth3.discover(providerUri, {
 | 
			
		||||
      client_id: providerUri
 | 
			
		||||
    , debug: opts.debug
 | 
			
		||||
    }).then(function (directive) {
 | 
			
		||||
      return oauth3.request(core.urls.grants(directive, 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.core.formatError(grants.error));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        console.warn('requests.grants', grants);
 | 
			
		||||
 | 
			
		||||
        oauth3.hooks.setGrants(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.setGrants(clientId, [ grant ]);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
          client: oauth3.hooks.getGrants(opts.client_id + '-client')
 | 
			
		||||
        , grants: oauth3.hooks.getGrants(opts.client_id) || []
 | 
			
		||||
        };
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
  oauth3.requests.loginCode = function (providerUri, opts) {
 | 
			
		||||
    return oauth3.discover(providerUri, opts).then(function (directive) {
 | 
			
		||||
      var prequest = core.urls.loginCode(directive, opts);
 | 
			
		||||
 | 
			
		||||
      return oauth3.request(prequest).then(function (res) {
 | 
			
		||||
        // result = { uuid, expires_at }
 | 
			
		||||
        return {
 | 
			
		||||
          otpUuid: res.data.uuid
 | 
			
		||||
        , otpExpires: res.data.expires_at
 | 
			
		||||
        };
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
  oauth3.loginCode = oauth3.requests.loginCode;
 | 
			
		||||
 | 
			
		||||
  oauth3.requests.resourceOwnerPassword = function (providerUri, opts) {
 | 
			
		||||
    //var scope = opts.scope;
 | 
			
		||||
    //var appId = opts.appId;
 | 
			
		||||
    return oauth3.discover(providerUri, opts).then(function (directive) {
 | 
			
		||||
      var prequest = core.urls.resourceOwnerPassword(directive, opts);
 | 
			
		||||
 | 
			
		||||
      return oauth3.request(prequest).then(function (req) {
 | 
			
		||||
        var data = (req.originalData || req.data);
 | 
			
		||||
        data.provider_uri = providerUri;
 | 
			
		||||
        if (data.error) {
 | 
			
		||||
          return oauth3.PromiseA.reject(oauth3.core.formatError(providerUri, data.error));
 | 
			
		||||
        }
 | 
			
		||||
        return oauth3.hooks.refreshSession(
 | 
			
		||||
          opts.session || { provider_uri: providerUri, client_uri: opts.client_uri || opts.clientUri }
 | 
			
		||||
        , data
 | 
			
		||||
        );
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
  oauth3.resourceOwnerPassword = oauth3.requests.resourceOwnerPassword;
 | 
			
		||||
 | 
			
		||||
  oauth3.requests.refreshToken = function (providerUri, opts) {
 | 
			
		||||
    console.info('[oauth3.requests.refreshToken] called', providerUri, opts);
 | 
			
		||||
    return oauth3.discover(providerUri, opts).then(function (directive) {
 | 
			
		||||
      var prequest = core.urls.refreshToken(directive, opts);
 | 
			
		||||
 | 
			
		||||
      return oauth3.request(prequest).then(function (req) {
 | 
			
		||||
        var data = (req.originalData || req.data);
 | 
			
		||||
        data.provider_uri = providerUri;
 | 
			
		||||
        if (data.error) {
 | 
			
		||||
          return oauth3.PromiseA.reject(oauth3.core.formatError(providerUri, data));
 | 
			
		||||
        }
 | 
			
		||||
        return oauth3.hooks.refreshSession(opts, data);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
  oauth3.refreshToken = oauth3.requests.refreshToken;
 | 
			
		||||
 | 
			
		||||
  // TODO It'll be very interesting to see if we can do some browser popup stuff from the CLI
 | 
			
		||||
  oauth3.requests._error_description = 'Not Implemented: Please override by including <script src="oauth3.browser.js"></script>';
 | 
			
		||||
  oauth3.requests.authorizationRedirect = function (/*providerUri, opts*/) {
 | 
			
		||||
    throw new Error(oauth3.requests._error_description);
 | 
			
		||||
  };
 | 
			
		||||
  oauth3.requests.implicitGrant = function (/*providerUri, opts*/) {
 | 
			
		||||
    throw new Error(oauth3.requests._error_description);
 | 
			
		||||
  };
 | 
			
		||||
  oauth3.requests.logout = function (/*providerUri, opts*/) {
 | 
			
		||||
    throw new Error(oauth3.requests._error_description);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  oauth3.login = function (providerUri, opts) {
 | 
			
		||||
    // Four styles of login:
 | 
			
		||||
    //   * background (hidden iframe)
 | 
			
		||||
    //   * iframe (visible iframe, needs border color and width x height params)
 | 
			
		||||
    //   * popup (needs width x height and positioning? params)
 | 
			
		||||
    //   * window (params?)
 | 
			
		||||
 | 
			
		||||
    // Two strategies
 | 
			
		||||
    //  * authorization_redirect (to server authorization code)
 | 
			
		||||
    //  * implicit_grant (default, browser-only)
 | 
			
		||||
    // If both are selected, implicit happens first and then the other happens in background
 | 
			
		||||
 | 
			
		||||
    var promise;
 | 
			
		||||
 | 
			
		||||
    if (opts.username || opts.password) {
 | 
			
		||||
      /* jshint ignore:start */
 | 
			
		||||
      // ingore "confusing use of !"
 | 
			
		||||
      if (!opts.username !== !(opts.password || opts.otp)) {
 | 
			
		||||
        throw new Error("you did not specify both username and password");
 | 
			
		||||
      }
 | 
			
		||||
      /* jshint ignore:end */
 | 
			
		||||
 | 
			
		||||
      return oauth3.requests.resourceOwnerPassword(providerUri, opts).then(function (resp) {
 | 
			
		||||
        if (!resp || !resp.data) {
 | 
			
		||||
          var err = new Error("bad response");
 | 
			
		||||
          err.response = resp;
 | 
			
		||||
          err.data = resp && resp.data || undefined;
 | 
			
		||||
          return oauth3.PromiseA.reject(err);
 | 
			
		||||
        }
 | 
			
		||||
        return resp.data;
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO support dual-strategy login
 | 
			
		||||
    // by default, always get implicitGrant (for client)
 | 
			
		||||
    // and optionally do authorizationCode (for server session)
 | 
			
		||||
    if ('background' === opts.type || opts.background) {
 | 
			
		||||
      opts.type = 'background';
 | 
			
		||||
      opts.background = true;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      opts.type = 'popup';
 | 
			
		||||
      opts.popup = true;
 | 
			
		||||
    }
 | 
			
		||||
    if (opts.authorizationRedirect) {
 | 
			
		||||
      promise = oauth3.requests.authorizationRedirect(providerUri, opts);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      promise = oauth3.requests.implicitGrant(providerUri, opts);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return promise;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  oauth3.backgroundLogin = function (providerUri, opts) {
 | 
			
		||||
    opts = opts || {};
 | 
			
		||||
    opts.type = 'background';
 | 
			
		||||
    return oauth3.login(providerUri, opts);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  oauth3.core = core;
 | 
			
		||||
  oauth3.querystringify = core.querystringify;
 | 
			
		||||
  oauth3.scopestringify = core.stringifyscope;
 | 
			
		||||
  oauth3.stringifyscope = core.stringifyscope;
 | 
			
		||||
 | 
			
		||||
  exports.OAUTH3 = oauth3.oauth3 = oauth3.OAUTH3 = oauth3;
 | 
			
		||||
  exports.oauth3 = exports.OAUTH3;
 | 
			
		||||
 | 
			
		||||
  if ('undefined' !== typeof module) {
 | 
			
		||||
    module.exports = oauth3;
 | 
			
		||||
  }
 | 
			
		||||
}('undefined' !== typeof exports ? exports : window));
 | 
			
		||||
							
								
								
									
										158
									
								
								prefactor/oauth3.lint.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								prefactor/oauth3.lint.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,158 @@
 | 
			
		||||
 | 
			
		||||
  // TODO move to a test / lint suite?
 | 
			
		||||
  oauth3._lintPromise = function (PromiseA) {
 | 
			
		||||
    var promise;
 | 
			
		||||
    var x = 1;
 | 
			
		||||
 | 
			
		||||
    // tests that this promise has all of the necessary api
 | 
			
		||||
    promise = new PromiseA(function (resolve, reject) {
 | 
			
		||||
      //console.log('x [2]', x);
 | 
			
		||||
      if (x !== 1) {
 | 
			
		||||
        throw new Error("bad promise, create not Synchronous [0]");
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      PromiseA.resolve().then(function () {
 | 
			
		||||
        var promise2;
 | 
			
		||||
 | 
			
		||||
        //console.log('x resolve', x);
 | 
			
		||||
        if (x !== 2) {
 | 
			
		||||
          throw new Error("bad promise, resolve not Asynchronous [1]");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        promise2 = PromiseA.reject().then(reject, function () {
 | 
			
		||||
          //console.log('x reject', x);
 | 
			
		||||
          if (x !== 4) {
 | 
			
		||||
            throw new Error("bad promise, reject not Asynchronous [2]");
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if ('undefined' === typeof angular) {
 | 
			
		||||
            throw new Error("[NOT AN ERROR] Dear angular users: ignore this error-handling test");
 | 
			
		||||
          } else {
 | 
			
		||||
            return PromiseA.reject(new Error("[NOT AN ERROR] ignore this error-handling test"));
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        x = 4;
 | 
			
		||||
 | 
			
		||||
        return promise2;
 | 
			
		||||
      }).catch(function (e) {
 | 
			
		||||
        if (e.message.match('NOT AN ERROR')) {
 | 
			
		||||
          resolve({ success: true });
 | 
			
		||||
        } else {
 | 
			
		||||
          reject(e);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      x = 3;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    x = 2;
 | 
			
		||||
    return promise;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  oauth3._lintDirectives = function (providerUri, directives) {
 | 
			
		||||
    var params = { directives: directives };
 | 
			
		||||
    console.log('DEBUG oauth3._discoverHelper', directives);
 | 
			
		||||
    var err;
 | 
			
		||||
    if (!params.directives) {
 | 
			
		||||
      err = new Error(params.error_description || "Unknown error when discoving provider '" + providerUri + "'");
 | 
			
		||||
      err.code = params.error || "E_UNKNOWN_ERROR";
 | 
			
		||||
      return OAUTH3.PromiseA.reject(err);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      directives = JSON.parse(atob(params.directives));
 | 
			
		||||
      console.log('DEBUG oauth3._discoverHelper directives', directives);
 | 
			
		||||
    } catch(e) {
 | 
			
		||||
      err = new Error(params.error_description || "could not parse directives for provider '" + providerUri + "'");
 | 
			
		||||
      err.code = params.error || "E_PARSE_DIRECTIVE";
 | 
			
		||||
      return OAUTH3.PromiseA.reject(err);
 | 
			
		||||
    }
 | 
			
		||||
    if (
 | 
			
		||||
        (directives.authorization_dialog && directives.authorization_dialog.url)
 | 
			
		||||
      || (directives.access_token && directives.access_token.url)
 | 
			
		||||
    ) {
 | 
			
		||||
      // TODO lint directives
 | 
			
		||||
      // TODO self-reference in directive for providerUri?
 | 
			
		||||
      directives.provider_uri = providerUri;
 | 
			
		||||
      localStorage.setItem('oauth3.' + providerUri + '.directives', JSON.stringify(directives));
 | 
			
		||||
      localStorage.setItem('oauth3.' + providerUri + '.directives.updated_at', new Date().toISOString());
 | 
			
		||||
 | 
			
		||||
      return OAUTH3.PromiseA.resolve(directives);
 | 
			
		||||
    } else {
 | 
			
		||||
      // ignore
 | 
			
		||||
      console.error("the directives provided by '" + providerUri + "' were invalid.");
 | 
			
		||||
      params.error = params.error || "E_INVALID_DIRECTIVE";
 | 
			
		||||
      params.error_description = params.error_description
 | 
			
		||||
        || "directives did not include authorization_dialog.url";
 | 
			
		||||
      err = new Error(params.error_description || "Unknown error when discoving provider '" + providerUri + "'");
 | 
			
		||||
      err.code = params.error;
 | 
			
		||||
      return OAUTH3.PromiseA.reject(err);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  core.tokenState = function (session) {
 | 
			
		||||
    var fresh;
 | 
			
		||||
    fresh = (Date.now() / 1000) >= (parseInt(session._accessTokenData.exp) || 0);
 | 
			
		||||
    if (!fresh) {
 | 
			
		||||
      console.log("[os] isn't fresh", session._accessTokenData.exp);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
  oauth3._lintRequest = function (preq, opts) {
 | 
			
		||||
    var providerUri;
 | 
			
		||||
 | 
			
		||||
    console.log('[os] request meta opts', opts);
 | 
			
		||||
 | 
			
		||||
    // check that the JWT is not expired
 | 
			
		||||
    // TODO check that this request applies to the aud and azp
 | 
			
		||||
    if (!(preq.session && preq.session.accessToken)) {
 | 
			
		||||
      console.log('[os] no session/accessTokenData');
 | 
			
		||||
      return oauth3.PromiseA.resolve(preq);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    preq.headers = preq.headers || {};
 | 
			
		||||
    preq.headers.Authorization = 'Bearer ' + preq.session.accessToken;
 | 
			
		||||
 | 
			
		||||
    if (!preq.session._accessTokenData) {
 | 
			
		||||
      console.log('[os] no _accessTokenData');
 | 
			
		||||
      preq.session._accessTokenData = core.jwt.decode(preq.session.accessToken).payload;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!preq.url.match(preq.session._accessTokenData.aud)) {
 | 
			
		||||
      console.log("[os] doesn't match audience", preq.session._accessTokenData.aud);
 | 
			
		||||
      return oauth3.PromiseA.resolve(preq);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (core.tokenState(session)) {
 | 
			
		||||
      case 'fresh':
 | 
			
		||||
        return oauth3.PromiseA.resolve(preq);
 | 
			
		||||
      case 'stale':
 | 
			
		||||
      case 'useless':
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!preq.session.refreshToken) {
 | 
			
		||||
      console.log("[os] can't refresh", preq.session);
 | 
			
		||||
      return oauth3.PromiseA.resolve(preq);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    opts.refreshToken = preq.session.refreshToken;
 | 
			
		||||
    console.log('[oauth3.js] refreshToken attempt');
 | 
			
		||||
 | 
			
		||||
    // TODO include directive?
 | 
			
		||||
    providerUri = preq.session.providerUri || preq.session._accessTokenData.iss;
 | 
			
		||||
    //opts.
 | 
			
		||||
    return oauth3.refreshToken(providerUri, opts).then(function (res) {
 | 
			
		||||
      console.log('[oauth3.js] refreshToken result:', res);
 | 
			
		||||
 | 
			
		||||
      if (!res.data.accessToken) {
 | 
			
		||||
        return preq;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // TODO fire session update event
 | 
			
		||||
      res.data.providerUri = preq.session.providerUri;
 | 
			
		||||
      preq.session = res.data;
 | 
			
		||||
      preq.headers.Authorization = 'Bearer ' + preq.session.accessToken;
 | 
			
		||||
      return preq;
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
							
								
								
									
										97
									
								
								prefactor/oauth3.provider.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								prefactor/oauth3.provider.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,97 @@
 | 
			
		||||
;(function (exports) {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  var OAUTH3 = window.OAUTH3 || require('./oauth3.js');
 | 
			
		||||
 | 
			
		||||
  OAUTH3.authz = OAUTH3.authz || {};
 | 
			
		||||
  OAUTH3.authz.scopes = function (providerUri, session, clientParams) {
 | 
			
		||||
    // OAuth3.requests.grants(providerUri, {});         // return list of grants
 | 
			
		||||
    // OAuth3.checkGrants(providerUri, {});             //
 | 
			
		||||
    var clientUri = OAUTH3.core.normalizeUri(clientParams.client_id || clientParams.client_uri);
 | 
			
		||||
    var scope = clientParams.scope || '';
 | 
			
		||||
    var clientObj = clientParams;
 | 
			
		||||
 | 
			
		||||
    if (!scope) {
 | 
			
		||||
      scope = 'oauth3_authn';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return OAUTH3.requests.grants(providerUri, {
 | 
			
		||||
      method: 'GET'
 | 
			
		||||
    , client_id: clientUri
 | 
			
		||||
    , client_uri: clientUri
 | 
			
		||||
    , session: session
 | 
			
		||||
    }).then(function (grants) {
 | 
			
		||||
      var myGrants;
 | 
			
		||||
      var grantedScopes;
 | 
			
		||||
      var grantedScopesMap;
 | 
			
		||||
      var pendingScopes;
 | 
			
		||||
      var acceptedScopes;
 | 
			
		||||
      var acceptedScopesMap;
 | 
			
		||||
      var scopes = OAUTH3.core.parsescope(scope);
 | 
			
		||||
      var callbackUrl;
 | 
			
		||||
 | 
			
		||||
      console.log('previous grants:');
 | 
			
		||||
      console.log(grants);
 | 
			
		||||
 | 
			
		||||
      // 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.checkRedirect(grants.client, clientObj)) {
 | 
			
		||||
        callbackUrl = 'https://oauth3.org/docs/errors#E_REDIRECT_ATTACK'
 | 
			
		||||
          + '?redirect_uri=' + clientObj.redirect_uri
 | 
			
		||||
          + '&allowed_urls=' + grants.client.url
 | 
			
		||||
          + '&client_id=' + clientUri
 | 
			
		||||
          + '&referrer_uri=' + OAUTH3.core.normalizeUri(window.document.referrer)
 | 
			
		||||
          ;
 | 
			
		||||
        location.href = callbackUrl;
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      console.warn("What are grants? Baby don't hurt me. Don't hurt me. No more.");
 | 
			
		||||
      console.warn(grants);
 | 
			
		||||
 | 
			
		||||
      myGrants = grants.grants.filter(function (grant) {
 | 
			
		||||
        if (clientUri === (grant.azp || grant.oauth_client_id || grant.oauthClientId)) {
 | 
			
		||||
          return true;
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      grantedScopesMap = {};
 | 
			
		||||
      acceptedScopesMap = {};
 | 
			
		||||
      pendingScopes = scopes.filter(function (requestedScope) {
 | 
			
		||||
        return myGrants.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
 | 
			
		||||
            acceptedScopesMap[requestedScope] = true;
 | 
			
		||||
          }
 | 
			
		||||
          else {
 | 
			
		||||
            // true, is pending
 | 
			
		||||
            return true;
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
      grantedScopes = Object.keys(grantedScopesMap);
 | 
			
		||||
      acceptedScopes = Object.keys(acceptedScopesMap);
 | 
			
		||||
 | 
			
		||||
      return {
 | 
			
		||||
        pending: pendingScopes    // not yet accepted
 | 
			
		||||
      , granted: grantedScopes    // all granted, ever
 | 
			
		||||
      , requested: scopes         // all requested, now
 | 
			
		||||
      , accepted: acceptedScopes  // granted (ever) and requested (now)
 | 
			
		||||
      , client: grants.client
 | 
			
		||||
      , grants: grants.grants
 | 
			
		||||
      };
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  exports.OAUTH3_PROVIDER = OAUTH3;
 | 
			
		||||
 | 
			
		||||
  if ('undefined' !== typeof module) {
 | 
			
		||||
    module.exports = OAUTH3;
 | 
			
		||||
  }
 | 
			
		||||
}('undefined' !== typeof exports ? exports : window));
 | 
			
		||||
							
								
								
									
										24
									
								
								prefactor/oauth3.scope-check.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								prefactor/oauth3.scope-check.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
    var separator;
 | 
			
		||||
 | 
			
		||||
    // TODO check that we appropriately use '#' for implicit and '?' for code
 | 
			
		||||
    // (server-side) in an OAuth2 backwards-compatible way
 | 
			
		||||
    if ('token' === scope.appQuery.response_type) {
 | 
			
		||||
      separator = '#';
 | 
			
		||||
    }
 | 
			
		||||
    else if ('code' === scope.appQuery.response_type) {
 | 
			
		||||
      separator = '?';
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      separator = '#';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (scope.pendingScope.length && !opts.allow) {
 | 
			
		||||
      redirectUri += separator + Oauth3.querystringify({
 | 
			
		||||
        error: 'access_denied'
 | 
			
		||||
        , error_description: 'None of the permissions were accepted'
 | 
			
		||||
        , error_uri: 'https://oauth3.org/docs/errors#access_denied'
 | 
			
		||||
        , state: scope.appQuery.state
 | 
			
		||||
      });
 | 
			
		||||
      $window.location.href = redirectUri;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
@ -1 +0,0 @@
 | 
			
		||||
_apis
 | 
			
		||||
@ -15,7 +15,7 @@
 | 
			
		||||
 | 
			
		||||
        <!-- TODO permanently cache with appcache (or service worker?) -->
 | 
			
		||||
        <!-- TODO slim this all down to a single file -->
 | 
			
		||||
        <script src="../../assets/oauth3.org/oauth3.core.js"></script>
 | 
			
		||||
        <script src="/assets/org.oauth3/oauth3.core.js"></script>
 | 
			
		||||
        <script>
 | 
			
		||||
          ;(function () {
 | 
			
		||||
            'use strict';
 | 
			
		||||
							
								
								
									
										12
									
								
								well-known/oauth3/directives.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								well-known/oauth3/directives.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
{ "terms": [ "oauth3.org/tos/draft" ]
 | 
			
		||||
, "authorization_dialog": { "url": "#/authorization_dialog" }
 | 
			
		||||
, "access_token": { "method": "POST", "url": "api/org.oauth3.provider/access_token" }
 | 
			
		||||
, "otp": { "method": "POST" , "url": "api/org.oauth3.provider/otp" }
 | 
			
		||||
, "credential_otp": { "method": "POST" , "url": "api/org.oauth3.provider/otp" }
 | 
			
		||||
, "credential_meta": { "url": "api/org.oauth3.provider/logins/meta/:type/:id" }
 | 
			
		||||
, "credential_create": { "method": "POST" , "url": "api/org.oauth3.provider/logins" }
 | 
			
		||||
, "grants": { "method": "GET", "url": "api/org.oauth3.provider/grants/:azp/:sub" }
 | 
			
		||||
, "authorization_decision": { "method": "POST", "url": "api/org.oauth3.provider/authorization_decision" }
 | 
			
		||||
, "callback": { "method": "GET", "url": ".well-known/oauth3/callback.html#/" }
 | 
			
		||||
, "logout": { "method": "GET", "url": "#/logout/" }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										71
									
								
								well-known/oauth3/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								well-known/oauth3/index.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,71 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
  <head>
 | 
			
		||||
    <style>
 | 
			
		||||
      body {
 | 
			
		||||
        background-color: #ffcccc;
 | 
			
		||||
      }
 | 
			
		||||
    </style>
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
  OAuth3 RPC
 | 
			
		||||
 | 
			
		||||
  <script src="/assets/org.oauth3/oauth3.core.js"></script>
 | 
			
		||||
  <script>
 | 
			
		||||
    ;(function () {
 | 
			
		||||
    'use strict';
 | 
			
		||||
 | 
			
		||||
    // Taken from oauth3.core.js
 | 
			
		||||
 | 
			
		||||
    // TODO what about search within hash?
 | 
			
		||||
    var prefix = "(" + window.location.hostname + ") [.well-known/oauth3/]";
 | 
			
		||||
    var params = OAUTH3.query.parse(window.location.hash || window.location.search);
 | 
			
		||||
    if (params.debug) {
 | 
			
		||||
      console.warn(prefix, "DEBUG MODE ENABLED. Automatic redirects disabled.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    console.log(prefix, 'hash||search:');
 | 
			
		||||
    console.log(window.location.hash || window.location.search);
 | 
			
		||||
 | 
			
		||||
    console.log(prefix, 'params:');
 | 
			
		||||
    console.log(params);
 | 
			
		||||
 | 
			
		||||
    OAUTH3.request({ url: 'directives.json' }).then(function (resp) {
 | 
			
		||||
      var urlsafe64 = OAUTH3._base64.encodeUrlSafe(JSON.stringify(resp.data, null, 0));
 | 
			
		||||
      var redirect;
 | 
			
		||||
 | 
			
		||||
      console.log(prefix, 'directives');
 | 
			
		||||
      console.log(resp);
 | 
			
		||||
 | 
			
		||||
      console.log(prefix, 'base64');
 | 
			
		||||
      console.log(urlsafe64);
 | 
			
		||||
 | 
			
		||||
      // TODO try postMessage back to redirect_uri domain right here
 | 
			
		||||
      // window.postMessage();
 | 
			
		||||
 | 
			
		||||
      // TODO make sure it's https NOT http
 | 
			
		||||
      // NOTE: this can be only up to 2,083 characters
 | 
			
		||||
      console.log(prefix, 'params.redirect_uri:', params.redirect_uri);
 | 
			
		||||
      redirect = params.redirect_uri + '?' + OAUTH3.query.stringify({
 | 
			
		||||
        state: params.state
 | 
			
		||||
      , directives: urlsafe64
 | 
			
		||||
      , debug: params.debug || undefined
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      console.log(prefix, 'redirect');
 | 
			
		||||
      console.log(redirect);
 | 
			
		||||
      if (!params.debug) {
 | 
			
		||||
        window.location = redirect;
 | 
			
		||||
      } else {
 | 
			
		||||
        // yes, we're violating the security lint with purpose
 | 
			
		||||
        document.body.innerHTML += window.location.host + window.location.pathname
 | 
			
		||||
          + '<br/><br/>You\'ve passed the \'debug\' parameter so we\'re pausing'
 | 
			
		||||
          + ' to let you look at logs or whatever it is that you intended to do.'
 | 
			
		||||
          + '<br/><br/>Continue with redirect: <a href="' + redirect + '">' + redirect + '</' + 'a>';
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    }());
 | 
			
		||||
  </script>
 | 
			
		||||
  </body>
 | 
			
		||||
</html>
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user