From 3cb71cf041a6d7cec219493ae92a01ae8f7203ce Mon Sep 17 00:00:00 2001
From: Rui Silva <penso@uphold.com>
Date: Thu, 4 Feb 2016 14:06:48 +0000
Subject: [PATCH 01/42] Update README dependencies

---
 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 818e183..4aea3a2 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ Choose your preferred method:
 
 ###### 1. Download `angular-oauth2` dependencies.
 
-* [angular](https://github.com/angular/angular.js)
+* [angular](https://github.com/angular/bower-angular)
 * [angular-cookies](https://github.com/angular/bower-angular-cookies)
 * [query-string](https://github.com/sindresorhus/query-string)
 
@@ -27,7 +27,7 @@ If you're using `bower` they will be automatically downloaded upon installing th
 ```html
 <script src="<VENDOR_FOLDER>/angular/angular.min.js"></script>
 <script src="<VENDOR_FOLDER>/angular-cookies/angular-cookies.min.js"></script>
-<script src="<VENDOR_FOLDER>/query-string/query-string.min.js"></script>
+<script src="<VENDOR_FOLDER>/query-string/query-string.js"></script>
 <script src="<VENDOR_FOLDER>/angular-oauth2/dist/angular-oauth2.min.js"></script>
 ```
 

From ea74fea7eb930c25daa1c6c8f2ebfb28f70a98cf Mon Sep 17 00:00:00 2001
From: Rui Silva <penso@uphold.com>
Date: Thu, 4 Feb 2016 14:38:33 +0000
Subject: [PATCH 02/42] Update peer dependencies

---
 package.json | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/package.json b/package.json
index ea3d80e..c6c45ac 100644
--- a/package.json
+++ b/package.json
@@ -45,7 +45,9 @@
     "karma-should": "0.0.1",
     "karma-sinon": "^1.0.4",
     "lodash": "^2.4.1",
+    "mocha": "^2.4.5",
     "should": "^4.6.0",
+    "sinon": "^1.17.3",
     "yargs": "^3.6.0"
   },
   "browser": {

From 4f3ee8aa96e1409014856c4208f3d4cd382b5153 Mon Sep 17 00:00:00 2001
From: Rui Silva <penso@uphold.com>
Date: Thu, 4 Feb 2016 14:39:14 +0000
Subject: [PATCH 03/42] Remove `browser` and `shims` from `package.json`

This was creating conflicts when using it with webpack.
---
 package.json | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/package.json b/package.json
index c6c45ac..1706e69 100644
--- a/package.json
+++ b/package.json
@@ -50,18 +50,8 @@
     "sinon": "^1.17.3",
     "yargs": "^3.6.0"
   },
-  "browser": {
-    "angular": "./bower_components/angular/angular.js",
-    "angular-cookies": "./bower_components/angular-cookies/angular-cookies.js",
-    "query-string": "./bower_components/query-string/query-string.js"
-  },
   "scripts": {
     "changelog": "./node_modules/.bin/github-changes -o seegno -r angular-oauth2 -a --only-pulls --use-commit-body --title 'Changelog' --date-format '/ YYYY-MM-DD'",
     "test": "./node_modules/.bin/gulp test --browsers Firefox"
-  },
-  "browserify-shim": {
-    "angular": {
-      "exports": "angular"
-    }
   }
 }

From 2586f17035e5a7e6f5db2b8efdb8d9836efa14a6 Mon Sep 17 00:00:00 2001
From: Rui Silva <penso@uphold.com>
Date: Thu, 4 Feb 2016 15:31:29 +0000
Subject: [PATCH 04/42] Update interceptor to allow authorization header to be
 overridden

---
 src/interceptors/oauth-interceptor.js         |  5 +++--
 .../interceptors/oauth-interceptor.spec.js    | 22 +++++++++++++++++++
 2 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/src/interceptors/oauth-interceptor.js b/src/interceptors/oauth-interceptor.js
index 7243646..b6d5abf 100644
--- a/src/interceptors/oauth-interceptor.js
+++ b/src/interceptors/oauth-interceptor.js
@@ -8,9 +8,10 @@
 function oauthInterceptor($q, $rootScope, OAuthToken) {
   return {
     request: function(config) {
+      config.headers = config.headers || {};
+
       // Inject `Authorization` header.
-      if (OAuthToken.getAuthorizationHeader()) {
-        config.headers = config.headers || {};
+      if (!config.headers.hasOwnProperty('Authorization') && OAuthToken.getAuthorizationHeader()) {
         config.headers.Authorization = OAuthToken.getAuthorizationHeader();
       }
 
diff --git a/test/unit/interceptors/oauth-interceptor.spec.js b/test/unit/interceptors/oauth-interceptor.spec.js
index 9611589..4c5e3c7 100644
--- a/test/unit/interceptors/oauth-interceptor.spec.js
+++ b/test/unit/interceptors/oauth-interceptor.spec.js
@@ -37,6 +37,28 @@ describe('oauthInterceptor', function() {
     $httpBackend.flush();
   }));
 
+  it('should not inject `Authorization` header if it already exists', inject(function($http, $httpBackend, OAuthToken) {
+    OAuthToken.setToken({ token_type: 'bearer', access_token: 'foo', expires_in: 3600, refresh_token: 'bar' });
+
+    $httpBackend.expectGET('https://website.com', function(headers) {
+      headers.Authorization = undefined;
+
+      return headers;
+    }).respond(200);
+
+    $http.get('https://website.com').then(function(response) {
+      response.config.headers.should.have.property('Authorization');
+      (undefined === response.config.headers.Authorization).should.be.true;
+    }).catch(function() {
+      should.fail();
+    });
+
+    $httpBackend.flush();
+
+    $httpBackend.verifyNoOutstandingExpectation();
+    $httpBackend.verifyNoOutstandingRequest();
+  }));
+
   it('should remove `token` if an `invalid_request` error occurs', inject(function($http, $httpBackend, OAuthToken) {
     sinon.spy(OAuthToken, 'removeToken');
 

From 75083928fb5ce4e6431b13ca57bfa8d9733bd8ce Mon Sep 17 00:00:00 2001
From: Rui Silva <penso@uphold.com>
Date: Fri, 5 Feb 2016 12:56:59 +0000
Subject: [PATCH 05/42] Remove `Authorization` header from `refresh_token`

---
 src/providers/oauth-provider.js | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/providers/oauth-provider.js b/src/providers/oauth-provider.js
index c00b0cb..3b237da 100644
--- a/src/providers/oauth-provider.js
+++ b/src/providers/oauth-provider.js
@@ -163,7 +163,10 @@ function OAuthProvider() {
         data = queryString.stringify(data);
 
         var options = {
-          headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
+          headers: {
+            'Authorization': undefined,
+            'Content-Type': 'application/x-www-form-urlencoded'
+          }
         };
 
         return $http.post(`${config.baseUrl}${config.grantPath}`, data, options).then((response) => {

From bdb2d960a0135d96836d318a3a36281b724781a5 Mon Sep 17 00:00:00 2001
From: Rui Silva <penso@uphold.com>
Date: Fri, 5 Feb 2016 13:29:54 +0000
Subject: [PATCH 06/42] Add `npm` dependencies

---
 README.md     | 9 ++++-----
 karma.conf.js | 6 +++---
 package.json  | 5 +++++
 3 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/README.md b/README.md
index 818e183..2f181d6 100644
--- a/README.md
+++ b/README.md
@@ -177,11 +177,10 @@ It will be awesome if you can help us evolve `angular-oauth2`. Want to help?
 
 1. [Fork it](https://github.com/seegno/angular-oauth2).
 2. `npm install`.
-3. `bower install`
-4. Do your magic.
-5. Run the tests: `gulp test`.
-6. Build: `gulp build`
-7. Create a [Pull Request](https://github.com/seegno/angular-oauth2/compare).
+3. Do your magic.
+4. Run the tests: `gulp test`.
+5. Build: `gulp build`
+6. Create a [Pull Request](https://github.com/seegno/angular-oauth2/compare).
 
 *The source files are written in ES6.*
 
diff --git a/karma.conf.js b/karma.conf.js
index ac63bc0..d43dca8 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -14,9 +14,9 @@ module.exports = function(config) {
     basePath: './',
     browsers: [argv.browsers || 'Chrome'],
     files: [
-      'bower_components/angular/angular.js',
-      'bower_components/angular-cookies/angular-cookies.js',
-      'bower_components/query-string/query-string.js',
+      'node_modules/angular/angular.js',
+      'node_modules/angular-cookies/angular-cookies.js',
+      'node_modules/query-string/query-string.js',
       'node_modules/lodash/dist/lodash.js',
       'node_modules/angular-mocks/angular-mocks.js',
       'dist/angular-oauth2.js',
diff --git a/package.json b/package.json
index 1706e69..fd5214c 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,11 @@
     "url": "https://github.com/seegno/angular-oauth2/issues"
   },
   "homepage": "https://github.com/seegno/angular-oauth2",
+  "dependencies": {
+    "angular": "^1.4.0",
+    "angular-cookies": "^1.4.0",
+    "query-string": "^1.0.0"
+  },
   "devDependencies": {
     "6to5ify": "^3.1.2",
     "angular-mocks": "^1.3.9",

From af84c5b3b49a09f7b3003287688139fba1e09e9c Mon Sep 17 00:00:00 2001
From: Rui Silva <penso@uphold.com>
Date: Fri, 5 Feb 2016 13:30:57 +0000
Subject: [PATCH 07/42] Update travis config

---
 .travis.yml | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 8e58244..ea427c7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,7 +3,6 @@ language: node_js
 sudo: false
 
 before_script:
-  - ./node_modules/.bin/bower install
   - export DISPLAY=:99.0
   - sh -e /etc/init.d/xvfb start
 
@@ -16,5 +15,3 @@ node_js:
 cache:
   directories:
     - node_modules
-    - bower_components
-

From 4d7a0db6a8de3c3115e51f44ec46aa43f5ca2d86 Mon Sep 17 00:00:00 2001
From: Rui Silva <penso@uphold.com>
Date: Fri, 5 Feb 2016 15:37:34 +0000
Subject: [PATCH 08/42] Update UMD wrapper to include `angular-cookies`

---
 gulpfile.js           | 22 ++++++++++++++++++----
 src/angular-oauth2.js |  4 ++--
 2 files changed, 20 insertions(+), 6 deletions(-)

diff --git a/gulpfile.js b/gulpfile.js
index 1559f62..d6b5057 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -27,10 +27,24 @@ var config = {
   umd: {
     namespace: 'angularOAuth2',
     exports: 'ngModule',
-    deps: [
-      'angular',
-      { name: 'query-string', globalName: 'queryString', paramName: 'queryString' }
-    ]
+    template: `
+      (function(root, factory) {
+        if (typeof define === 'function' && define.amd) {
+          define([ "angular", "angular-cookies", "query-string" ], factory);
+        } else if (typeof exports === 'object') {
+          module.exports = factory(require("angular"), require("angular-cookies"), require("query-string"));
+        } else {
+          root.<%= namespace %> = factory(root.angular, 'ngCookies', root.queryString);
+        }
+      }(this, function(angular, ngCookies, queryString) {
+        <% if (exports) { %>
+          <%= contents %>
+          return <%= exports %>;
+        <% } else { %>
+          return <%= contents %>;
+        <% } %>
+      }));
+    `
   },
   banner: ['/**',
     ' * <%= pkg.name %> - <%= pkg.description %>',
diff --git a/src/angular-oauth2.js b/src/angular-oauth2.js
index befc76f..c2e8b16 100644
--- a/src/angular-oauth2.js
+++ b/src/angular-oauth2.js
@@ -8,10 +8,10 @@ import OAuthProvider from './providers/oauth-provider';
 import OAuthTokenProvider from './providers/oauth-token-provider';
 import oauthConfig from './config/oauth-config';
 import oauthInterceptor from './interceptors/oauth-interceptor';
-import 'angular-cookies';
+import ngCookies from 'angular-cookies';
 
 var ngModule = angular.module('angular-oauth2', [
-    'ngCookies'
+    ngCookies
   ])
   .config(oauthConfig)
   .factory('oauthInterceptor', oauthInterceptor)

From 523060e515f336a4d2430ec0f4b0082ae78aa245 Mon Sep 17 00:00:00 2001
From: tinogomes <tinorj@gmail.com>
Date: Fri, 18 Dec 2015 15:53:35 -0200
Subject: [PATCH 09/42] Add `client_id` and `client_secret` on revoke_token

---
 src/providers/oauth-provider.js            | 11 +++++++++--
 test/unit/providers/oauth-provider.spec.js | 14 +++++++++++---
 2 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/src/providers/oauth-provider.js b/src/providers/oauth-provider.js
index c00b0cb..3bdaf45 100644
--- a/src/providers/oauth-provider.js
+++ b/src/providers/oauth-provider.js
@@ -181,9 +181,16 @@ function OAuthProvider() {
        */
 
       revokeToken() {
-        var data = queryString.stringify({
+        var data = {
+          client_id: config.clientId,
           token: OAuthToken.getRefreshToken() ? OAuthToken.getRefreshToken() : OAuthToken.getAccessToken()
-        });
+        };
+
+        if (null !== config.clientSecret) {
+          data.client_secret = config.clientSecret;
+        }
+
+        data = queryString.stringify(data);
 
         var options = {
           headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
diff --git a/test/unit/providers/oauth-provider.spec.js b/test/unit/providers/oauth-provider.spec.js
index c2af377..29b6888 100644
--- a/test/unit/providers/oauth-provider.spec.js
+++ b/test/unit/providers/oauth-provider.spec.js
@@ -363,6 +363,8 @@ describe('OAuthProvider', function() {
         queryString.stringify.callCount.should.equal(1);
         queryString.stringify.firstCall.args.should.have.lengthOf(1);
         queryString.stringify.firstCall.args[0].should.eql({
+          client_id: defaults.clientId,
+          client_secret: defaults.clientSecret,
           token: 'bar'
         });
         queryString.stringify.restore();
@@ -378,14 +380,18 @@ describe('OAuthProvider', function() {
         queryString.stringify.callCount.should.equal(1);
         queryString.stringify.firstCall.args.should.have.lengthOf(1);
         queryString.stringify.firstCall.args[0].should.eql({
-          token: 'foo'
+          client_id: defaults.clientId,
+          token: 'foo',
+          client_secret: defaults.clientSecret
         });
         queryString.stringify.restore();
       }));
 
       it('should return an error if `token` is missing', inject(function($httpBackend, OAuth) {
         var data = queryString.stringify({
-          token: undefined
+          client_id: defaults.clientId,
+          token: undefined,
+          client_secret: defaults.clientSecret
         });
 
         $httpBackend.expectPOST(defaults.baseUrl + defaults.revokePath, data)
@@ -407,7 +413,9 @@ describe('OAuthProvider', function() {
         OAuthToken.setToken({ token_type: 'bearer', access_token: 'foo', expires_in: 3600, refresh_token: 'bar' });
 
         var data = queryString.stringify({
-          token: 'bar'
+          client_id: defaults.clientId,
+          token: 'bar',
+          client_secret: defaults.clientSecret
         });
 
         $httpBackend.expectPOST(defaults.baseUrl + defaults.revokePath, data)

From cef07df08e528a332ba00ec236f1ae53a61ab022 Mon Sep 17 00:00:00 2001
From: Rui Silva <penso@uphold.com>
Date: Mon, 8 Feb 2016 14:34:09 +0000
Subject: [PATCH 10/42] Update `getAccessToken` to allow custom data

- Previously, we were only allowing `username` and `password`.
---
 src/providers/oauth-provider.js            | 24 +++++------
 test/unit/providers/oauth-provider.spec.js | 48 ----------------------
 2 files changed, 10 insertions(+), 62 deletions(-)

diff --git a/src/providers/oauth-provider.js b/src/providers/oauth-provider.js
index 4c90997..00803c7 100644
--- a/src/providers/oauth-provider.js
+++ b/src/providers/oauth-provider.js
@@ -107,23 +107,16 @@ function OAuthProvider() {
        * Retrieves the `access_token` and stores the `response.data` on cookies
        * using the `OAuthToken`.
        *
-       * @param {object} user - Object with `username` and `password` properties.
-       * @param {object} config - Optional configuration object sent to `POST`.
+       * @param {object} data - Request content, e.g., `username` and `password`.
+       * @param {object} options - Optional configuration.
        * @return {promise} A response promise.
        */
 
-      getAccessToken(user, options) {
-        // Check if `user` has required properties.
-        if (!user || !user.username || !user.password) {
-          throw new Error('`user` must be an object with `username` and `password` properties.');
-        }
-
-        var data = {
+      getAccessToken(data, options) {
+        data = angular.extend({
           client_id: config.clientId,
-          grant_type: 'password',
-          username: user.username,
-          password: user.password
-        };
+          grant_type: 'password'
+        }, data);
 
         if (null !== config.clientSecret) {
           data.client_secret = config.clientSecret;
@@ -132,7 +125,10 @@ function OAuthProvider() {
         data = queryString.stringify(data);
 
         options = angular.extend({
-          headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
+          headers: {
+            'Authorization': undefined,
+            'Content-Type': 'application/x-www-form-urlencoded'
+          }
         }, options);
 
         return $http.post(`${config.baseUrl}${config.grantPath}`, data, options).then((response) => {
diff --git a/test/unit/providers/oauth-provider.spec.js b/test/unit/providers/oauth-provider.spec.js
index 29b6888..4e4e354 100644
--- a/test/unit/providers/oauth-provider.spec.js
+++ b/test/unit/providers/oauth-provider.spec.js
@@ -158,54 +158,6 @@ describe('OAuthProvider', function() {
         client_secret: defaults.clientSecret
       });
 
-      it('should throw an error if `user` is missing', inject(function(OAuth) {
-        try {
-          OAuth.getAccessToken();
-
-          should.fail();
-        } catch(e) {
-          e.should.be.an.instanceOf(Error);
-          e.message.should.match(/user/);
-        }
-      }));
-
-      it('should throw an error if `user` is empty', inject(function(OAuth) {
-        try {
-          OAuth.getAccessToken({});
-
-          should.fail();
-        } catch(e) {
-          e.should.be.an.instanceOf(Error);
-          e.message.should.match(/user/);
-        }
-      }));
-
-      it('should throw an error if `username` is not provided', inject(function(OAuth) {
-        try {
-          OAuth.getAccessToken({
-            password: 'foo'
-          });
-
-          should.fail();
-        } catch(e) {
-          e.should.be.an.instanceOf(Error);
-          e.message.should.match(/user/);
-        }
-      }));
-
-      it('should throw an error if `password` is not provided', inject(function(OAuth) {
-        try {
-          OAuth.getAccessToken({
-            username: 'foo'
-          });
-
-          should.fail();
-        } catch(e) {
-          e.should.be.an.instanceOf(Error);
-          e.message.should.match(/user/);
-        }
-      }));
-
       it('should call `queryString.stringify`', inject(function(OAuth) {
         sinon.spy(queryString, 'stringify');
 

From ae7d5622fbad0c7d729dc243de8db73ef9b4b31c Mon Sep 17 00:00:00 2001
From: Rui Silva <penso@uphold.com>
Date: Mon, 8 Feb 2016 14:46:09 +0000
Subject: [PATCH 11/42] Update `getRefreshToken` to allow `data` and `options`
 override

---
 src/providers/oauth-provider.js | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/src/providers/oauth-provider.js b/src/providers/oauth-provider.js
index 00803c7..985745f 100644
--- a/src/providers/oauth-provider.js
+++ b/src/providers/oauth-provider.js
@@ -142,15 +142,17 @@ function OAuthProvider() {
        * Retrieves the `refresh_token` and stores the `response.data` on cookies
        * using the `OAuthToken`.
        *
+       * @param {object} data - Request content.
+       * @param {object} options - Optional configuration.
        * @return {promise} A response promise.
        */
 
-      getRefreshToken() {
-        var data = {
+      getRefreshToken(data, options) {
+        data = angular.extend({
           client_id: config.clientId,
           grant_type: 'refresh_token',
           refresh_token: OAuthToken.getRefreshToken(),
-        };
+        }, data);
 
         if (null !== config.clientSecret) {
           data.client_secret = config.clientSecret;
@@ -158,12 +160,12 @@ function OAuthProvider() {
 
         data = queryString.stringify(data);
 
-        var options = {
+        options = angular.extend({
           headers: {
             'Authorization': undefined,
             'Content-Type': 'application/x-www-form-urlencoded'
           }
-        };
+        }, options);
 
         return $http.post(`${config.baseUrl}${config.grantPath}`, data, options).then((response) => {
           OAuthToken.setToken(response.data);

From 9c2bd2f52b890c6b501b265cdb9e7f45035f5910 Mon Sep 17 00:00:00 2001
From: Rui Silva <penso@uphold.com>
Date: Mon, 8 Feb 2016 14:47:02 +0000
Subject: [PATCH 12/42] Update `revokeToken` to allow `data` and `options`
 override

---
 src/providers/oauth-provider.js            | 21 ++++++++++++++-------
 test/unit/providers/oauth-provider.spec.js |  8 ++++++--
 2 files changed, 20 insertions(+), 9 deletions(-)

diff --git a/src/providers/oauth-provider.js b/src/providers/oauth-provider.js
index 985745f..df822fc 100644
--- a/src/providers/oauth-provider.js
+++ b/src/providers/oauth-provider.js
@@ -178,14 +178,19 @@ function OAuthProvider() {
        * Revokes the `token` and removes the stored `token` from cookies
        * using the `OAuthToken`.
        *
+       * @param {object} data - Request content.
+       * @param {object} options - Optional configuration.
        * @return {promise} A response promise.
        */
 
-      revokeToken() {
-        var data = {
+      revokeToken(data, options) {
+        var refreshToken = OAuthToken.getRefreshToken();
+
+        data = angular.extend({
           client_id: config.clientId,
-          token: OAuthToken.getRefreshToken() ? OAuthToken.getRefreshToken() : OAuthToken.getAccessToken()
-        };
+          token: refreshToken ? refreshToken : OAuthToken.getAccessToken(),
+          token_type_hint: refreshToken ? 'refresh_token' : 'access_token'
+        }, data);
 
         if (null !== config.clientSecret) {
           data.client_secret = config.clientSecret;
@@ -193,9 +198,11 @@ function OAuthProvider() {
 
         data = queryString.stringify(data);
 
-        var options = {
-          headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
-        };
+        options = angular.extend({
+          headers: {
+            'Content-Type': 'application/x-www-form-urlencoded'
+          }
+        }, options);
 
         return $http.post(`${config.baseUrl}${config.revokePath}`, data, options).then((response) => {
           OAuthToken.removeToken();
diff --git a/test/unit/providers/oauth-provider.spec.js b/test/unit/providers/oauth-provider.spec.js
index 4e4e354..0b56c3c 100644
--- a/test/unit/providers/oauth-provider.spec.js
+++ b/test/unit/providers/oauth-provider.spec.js
@@ -316,8 +316,9 @@ describe('OAuthProvider', function() {
         queryString.stringify.firstCall.args.should.have.lengthOf(1);
         queryString.stringify.firstCall.args[0].should.eql({
           client_id: defaults.clientId,
-          client_secret: defaults.clientSecret,
-          token: 'bar'
+          token: 'bar',
+          token_type_hint: 'refresh_token',
+          client_secret: defaults.clientSecret
         });
         queryString.stringify.restore();
       }));
@@ -334,6 +335,7 @@ describe('OAuthProvider', function() {
         queryString.stringify.firstCall.args[0].should.eql({
           client_id: defaults.clientId,
           token: 'foo',
+          token_type_hint: 'access_token',
           client_secret: defaults.clientSecret
         });
         queryString.stringify.restore();
@@ -343,6 +345,7 @@ describe('OAuthProvider', function() {
         var data = queryString.stringify({
           client_id: defaults.clientId,
           token: undefined,
+          token_type_hint: 'access_token',
           client_secret: defaults.clientSecret
         });
 
@@ -367,6 +370,7 @@ describe('OAuthProvider', function() {
         var data = queryString.stringify({
           client_id: defaults.clientId,
           token: 'bar',
+          token_type_hint: 'refresh_token',
           client_secret: defaults.clientSecret
         });
 

From 46ebcf566f1dedc70b55d172f32fe6f078b290c0 Mon Sep 17 00:00:00 2001
From: Rui Silva <penso@uphold.com>
Date: Wed, 10 Feb 2016 11:13:37 +0000
Subject: [PATCH 13/42] Release 3.1.0

---
 CHANGELOG.md               |   9 +++-
 bower.json                 |   2 +-
 dist/angular-oauth2.js     | 100 +++++++++++++++++++------------------
 dist/angular-oauth2.min.js |   2 +-
 package.json               |   2 +-
 5 files changed, 63 insertions(+), 52 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 23da70e..26570bd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
 ## Changelog
 
+### upcoming / 2016-02-10
+- [#78](https://github.com/seegno/angular-oauth2/pull/78) Update OAuth methods to allow `data` and `options` override (@ruipenso)
+- [#71](https://github.com/seegno/angular-oauth2/pull/71) Add `client_id` and `client_secret` on revoke_token (@tinogomes)
+- [#77](https://github.com/seegno/angular-oauth2/pull/77) Update interceptor to allow authorization header to be overridden (@ruipenso)
+- [#75](https://github.com/seegno/angular-oauth2/pull/75) Update README dependencies (@ruipenso)
+- [#76](https://github.com/seegno/angular-oauth2/pull/76) Update `package.json` (@ruipenso)
+
 ### 3.0.1 / 2015-06-01
 - [#27](https://github.com/seegno/angular-oauth2/pull/27) Add travis-ci configuration (@joaogranado)
 
@@ -32,4 +39,4 @@
 
 ### 1.0.0 / 2015-01-18
 - [#2](https://github.com/seegno/angular-oauth2/pull/2) Fix indentation in README examples (@fixe)
-- [#1](https://github.com/seegno/angular-oauth2/pull/1) Add project structure (@ruipenso)
+- [#1](https://github.com/seegno/angular-oauth2/pull/1) Add project structure (@ruipenso)
\ No newline at end of file
diff --git a/bower.json b/bower.json
index 379a19e..49d1128 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
 {
   "name": "angular-oauth2",
-  "version": "3.0.1",
+  "version": "3.1.0",
   "description": "AngularJS OAuth2",
   "main": "./dist/angular-oauth2.js",
   "authors": [
diff --git a/dist/angular-oauth2.js b/dist/angular-oauth2.js
index 6152c8c..35a1937 100644
--- a/dist/angular-oauth2.js
+++ b/dist/angular-oauth2.js
@@ -1,23 +1,45 @@
 /**
  * angular-oauth2 - Angular OAuth2
- * @version v3.0.1
+ * @version v3.1.0
  * @link https://github.com/seegno/angular-oauth2
  * @license MIT
  */
 (function(root, factory) {
     if (typeof define === "function" && define.amd) {
-        define([ "angular", "query-string" ], factory);
+        define([ "angular", "angular-cookies", "query-string" ], factory);
     } else if (typeof exports === "object") {
-        module.exports = factory(require("angular"), require("query-string"));
+        module.exports = factory(require("angular"), require("angular-cookies"), require("query-string"));
     } else {
-        root.angularOAuth2 = factory(root.angular, root.queryString);
+        root.angularOAuth2 = factory(root.angular, "ngCookies", root.queryString);
     }
-})(this, function(angular, queryString) {
-    var ngModule = angular.module("angular-oauth2", [ "ngCookies" ]).config(oauthConfig).factory("oauthInterceptor", oauthInterceptor).provider("OAuth", OAuthProvider).provider("OAuthToken", OAuthTokenProvider);
+})(this, function(angular, ngCookies, queryString) {
+    var ngModule = angular.module("angular-oauth2", [ ngCookies ]).config(oauthConfig).factory("oauthInterceptor", oauthInterceptor).provider("OAuth", OAuthProvider).provider("OAuthToken", OAuthTokenProvider);
     function oauthConfig($httpProvider) {
         $httpProvider.interceptors.push("oauthInterceptor");
     }
     oauthConfig.$inject = [ "$httpProvider" ];
+    function oauthInterceptor($q, $rootScope, OAuthToken) {
+        return {
+            request: function(config) {
+                config.headers = config.headers || {};
+                if (!config.headers.hasOwnProperty("Authorization") && OAuthToken.getAuthorizationHeader()) {
+                    config.headers.Authorization = OAuthToken.getAuthorizationHeader();
+                }
+                return config;
+            },
+            responseError: function(rejection) {
+                if (400 === rejection.status && rejection.data && ("invalid_request" === rejection.data.error || "invalid_grant" === rejection.data.error)) {
+                    OAuthToken.removeToken();
+                    $rootScope.$emit("oauth:error", rejection);
+                }
+                if (401 === rejection.status && (rejection.data && "invalid_token" === rejection.data.error) || rejection.headers("www-authenticate") && 0 === rejection.headers("www-authenticate").indexOf("Bearer")) {
+                    $rootScope.$emit("oauth:error", rejection);
+                }
+                return $q.reject(rejection);
+            }
+        };
+    }
+    oauthInterceptor.$inject = [ "$q", "$rootScope", "OAuthToken" ];
     var _prototypeProperties = function(child, staticProps, instanceProps) {
         if (staticProps) Object.defineProperties(child, staticProps);
         if (instanceProps) Object.defineProperties(child.prototype, instanceProps);
@@ -73,22 +95,18 @@
                         configurable: true
                     },
                     getAccessToken: {
-                        value: function getAccessToken(user, options) {
-                            if (!user || !user.username || !user.password) {
-                                throw new Error("`user` must be an object with `username` and `password` properties.");
-                            }
-                            var data = {
+                        value: function getAccessToken(data, options) {
+                            data = angular.extend({
                                 client_id: config.clientId,
-                                grant_type: "password",
-                                username: user.username,
-                                password: user.password
-                            };
+                                grant_type: "password"
+                            }, data);
                             if (null !== config.clientSecret) {
                                 data.client_secret = config.clientSecret;
                             }
                             data = queryString.stringify(data);
                             options = angular.extend({
                                 headers: {
+                                    Authorization: undefined,
                                     "Content-Type": "application/x-www-form-urlencoded"
                                 }
                             }, options);
@@ -102,21 +120,22 @@
                         configurable: true
                     },
                     getRefreshToken: {
-                        value: function getRefreshToken() {
-                            var data = {
+                        value: function getRefreshToken(data, options) {
+                            data = angular.extend({
                                 client_id: config.clientId,
                                 grant_type: "refresh_token",
                                 refresh_token: OAuthToken.getRefreshToken()
-                            };
+                            }, data);
                             if (null !== config.clientSecret) {
                                 data.client_secret = config.clientSecret;
                             }
                             data = queryString.stringify(data);
-                            var options = {
+                            options = angular.extend({
                                 headers: {
+                                    Authorization: undefined,
                                     "Content-Type": "application/x-www-form-urlencoded"
                                 }
-                            };
+                            }, options);
                             return $http.post("" + config.baseUrl + "" + config.grantPath, data, options).then(function(response) {
                                 OAuthToken.setToken(response.data);
                                 return response;
@@ -127,15 +146,22 @@
                         configurable: true
                     },
                     revokeToken: {
-                        value: function revokeToken() {
-                            var data = queryString.stringify({
-                                token: OAuthToken.getRefreshToken() ? OAuthToken.getRefreshToken() : OAuthToken.getAccessToken()
-                            });
-                            var options = {
+                        value: function revokeToken(data, options) {
+                            var refreshToken = OAuthToken.getRefreshToken();
+                            data = angular.extend({
+                                client_id: config.clientId,
+                                token: refreshToken ? refreshToken : OAuthToken.getAccessToken(),
+                                token_type_hint: refreshToken ? "refresh_token" : "access_token"
+                            }, data);
+                            if (null !== config.clientSecret) {
+                                data.client_secret = config.clientSecret;
+                            }
+                            data = queryString.stringify(data);
+                            options = angular.extend({
                                 headers: {
                                     "Content-Type": "application/x-www-form-urlencoded"
                                 }
-                            };
+                            }, options);
                             return $http.post("" + config.baseUrl + "" + config.revokePath, data, options).then(function(response) {
                                 OAuthToken.removeToken();
                                 return response;
@@ -240,27 +266,5 @@
         };
         this.$get.$inject = [ "$cookies" ];
     }
-    function oauthInterceptor($q, $rootScope, OAuthToken) {
-        return {
-            request: function(config) {
-                if (OAuthToken.getAuthorizationHeader()) {
-                    config.headers = config.headers || {};
-                    config.headers.Authorization = OAuthToken.getAuthorizationHeader();
-                }
-                return config;
-            },
-            responseError: function(rejection) {
-                if (400 === rejection.status && rejection.data && ("invalid_request" === rejection.data.error || "invalid_grant" === rejection.data.error)) {
-                    OAuthToken.removeToken();
-                    $rootScope.$emit("oauth:error", rejection);
-                }
-                if (401 === rejection.status && (rejection.data && "invalid_token" === rejection.data.error) || rejection.headers("www-authenticate") && 0 === rejection.headers("www-authenticate").indexOf("Bearer")) {
-                    $rootScope.$emit("oauth:error", rejection);
-                }
-                return $q.reject(rejection);
-            }
-        };
-    }
-    oauthInterceptor.$inject = [ "$q", "$rootScope", "OAuthToken" ];
     return ngModule;
 });
\ No newline at end of file
diff --git a/dist/angular-oauth2.min.js b/dist/angular-oauth2.min.js
index 4260312..317d5b4 100644
--- a/dist/angular-oauth2.min.js
+++ b/dist/angular-oauth2.min.js
@@ -1 +1 @@
-!function(e,t){"function"==typeof define&&define.amd?define(["angular","query-string"],t):"object"==typeof exports?module.exports=t(require("angular"),require("query-string")):e.angularOAuth2=t(e.angular,e.queryString)}(this,function(e,t){function r(e){e.interceptors.push("oauthInterceptor")}function n(){var r;this.configure=function(t){if(r)throw new Error("Already configured.");if(!(t instanceof Object))throw new TypeError("Invalid argument: `config` must be an `Object`.");return r=e.extend({},c,t),e.forEach(s,function(e){if(!r[e])throw new Error("Missing parameter: "+e+".")}),"/"===r.baseUrl.substr(-1)&&(r.baseUrl=r.baseUrl.slice(0,-1)),"/"!==r.grantPath[0]&&(r.grantPath="/"+r.grantPath),"/"!==r.revokePath[0]&&(r.revokePath="/"+r.revokePath),r},this.$get=function(n,o){var a=function(){function a(){if(!r)throw new Error("`OAuthProvider` must be configured first.")}return u(a,null,{isAuthenticated:{value:function(){return!!o.getToken()},writable:!0,enumerable:!0,configurable:!0},getAccessToken:{value:function(a,i){if(!a||!a.username||!a.password)throw new Error("`user` must be an object with `username` and `password` properties.");var u={client_id:r.clientId,grant_type:"password",username:a.username,password:a.password};return null!==r.clientSecret&&(u.client_secret=r.clientSecret),u=t.stringify(u),i=e.extend({headers:{"Content-Type":"application/x-www-form-urlencoded"}},i),n.post(""+r.baseUrl+r.grantPath,u,i).then(function(e){return o.setToken(e.data),e})},writable:!0,enumerable:!0,configurable:!0},getRefreshToken:{value:function(){var e={client_id:r.clientId,grant_type:"refresh_token",refresh_token:o.getRefreshToken()};null!==r.clientSecret&&(e.client_secret=r.clientSecret),e=t.stringify(e);var a={headers:{"Content-Type":"application/x-www-form-urlencoded"}};return n.post(""+r.baseUrl+r.grantPath,e,a).then(function(e){return o.setToken(e.data),e})},writable:!0,enumerable:!0,configurable:!0},revokeToken:{value:function(){var e=t.stringify({token:o.getRefreshToken()?o.getRefreshToken():o.getAccessToken()}),a={headers:{"Content-Type":"application/x-www-form-urlencoded"}};return n.post(""+r.baseUrl+r.revokePath,e,a).then(function(e){return o.removeToken(),e})},writable:!0,enumerable:!0,configurable:!0}}),a}();return new a},this.$get.$inject=["$http","OAuthToken"]}function o(){var t={name:"token",options:{secure:!0}};this.configure=function(r){if(!(r instanceof Object))throw new TypeError("Invalid argument: `config` must be an `Object`.");return e.extend(t,r),t},this.$get=function(e){var r=function(){function r(){}return u(r,null,{setToken:{value:function(r){return e.putObject(t.name,r,t.options)},writable:!0,enumerable:!0,configurable:!0},getToken:{value:function(){return e.getObject(t.name)},writable:!0,enumerable:!0,configurable:!0},getAccessToken:{value:function(){return this.getToken()?this.getToken().access_token:void 0},writable:!0,enumerable:!0,configurable:!0},getAuthorizationHeader:{value:function(){return this.getTokenType()&&this.getAccessToken()?""+(this.getTokenType().charAt(0).toUpperCase()+this.getTokenType().substr(1))+" "+this.getAccessToken():void 0},writable:!0,enumerable:!0,configurable:!0},getRefreshToken:{value:function(){return this.getToken()?this.getToken().refresh_token:void 0},writable:!0,enumerable:!0,configurable:!0},getTokenType:{value:function(){return this.getToken()?this.getToken().token_type:void 0},writable:!0,enumerable:!0,configurable:!0},removeToken:{value:function(){return e.remove(t.name,t.options)},writable:!0,enumerable:!0,configurable:!0}}),r}();return new r},this.$get.$inject=["$cookies"]}function a(e,t,r){return{request:function(e){return r.getAuthorizationHeader()&&(e.headers=e.headers||{},e.headers.Authorization=r.getAuthorizationHeader()),e},responseError:function(n){return 400!==n.status||!n.data||"invalid_request"!==n.data.error&&"invalid_grant"!==n.data.error||(r.removeToken(),t.$emit("oauth:error",n)),(401===n.status&&n.data&&"invalid_token"===n.data.error||n.headers("www-authenticate")&&0===n.headers("www-authenticate").indexOf("Bearer"))&&t.$emit("oauth:error",n),e.reject(n)}}}var i=e.module("angular-oauth2",["ngCookies"]).config(r).factory("oauthInterceptor",a).provider("OAuth",n).provider("OAuthToken",o);r.$inject=["$httpProvider"];var u=function(e,t,r){t&&Object.defineProperties(e,t),r&&Object.defineProperties(e.prototype,r)},c={baseUrl:null,clientId:null,clientSecret:null,grantPath:"/oauth2/token",revokePath:"/oauth2/revoke"},s=["baseUrl","clientId","grantPath","revokePath"],u=function(e,t,r){t&&Object.defineProperties(e,t),r&&Object.defineProperties(e.prototype,r)};return a.$inject=["$q","$rootScope","OAuthToken"],i});
\ No newline at end of file
+!function(e,t){"function"==typeof define&&define.amd?define(["angular","angular-cookies","query-string"],t):"object"==typeof exports?module.exports=t(require("angular"),require("angular-cookies"),require("query-string")):e.angularOAuth2=t(e.angular,"ngCookies",e.queryString)}(this,function(e,t,n){function r(e){e.interceptors.push("oauthInterceptor")}function o(e,t,n){return{request:function(e){return e.headers=e.headers||{},!e.headers.hasOwnProperty("Authorization")&&n.getAuthorizationHeader()&&(e.headers.Authorization=n.getAuthorizationHeader()),e},responseError:function(r){return 400!==r.status||!r.data||"invalid_request"!==r.data.error&&"invalid_grant"!==r.data.error||(n.removeToken(),t.$emit("oauth:error",r)),(401===r.status&&r.data&&"invalid_token"===r.data.error||r.headers("www-authenticate")&&0===r.headers("www-authenticate").indexOf("Bearer"))&&t.$emit("oauth:error",r),e.reject(r)}}}function i(){var t;this.configure=function(n){if(t)throw new Error("Already configured.");if(!(n instanceof Object))throw new TypeError("Invalid argument: `config` must be an `Object`.");return t=e.extend({},s,n),e.forEach(l,function(e){if(!t[e])throw new Error("Missing parameter: "+e+".")}),"/"===t.baseUrl.substr(-1)&&(t.baseUrl=t.baseUrl.slice(0,-1)),"/"!==t.grantPath[0]&&(t.grantPath="/"+t.grantPath),"/"!==t.revokePath[0]&&(t.revokePath="/"+t.revokePath),t},this.$get=function(r,o){var i=function(){function i(){if(!t)throw new Error("`OAuthProvider` must be configured first.")}return c(i,null,{isAuthenticated:{value:function(){return!!o.getToken()},writable:!0,enumerable:!0,configurable:!0},getAccessToken:{value:function(i,a){return i=e.extend({client_id:t.clientId,grant_type:"password"},i),null!==t.clientSecret&&(i.client_secret=t.clientSecret),i=n.stringify(i),a=e.extend({headers:{Authorization:void 0,"Content-Type":"application/x-www-form-urlencoded"}},a),r.post(""+t.baseUrl+t.grantPath,i,a).then(function(e){return o.setToken(e.data),e})},writable:!0,enumerable:!0,configurable:!0},getRefreshToken:{value:function(i,a){return i=e.extend({client_id:t.clientId,grant_type:"refresh_token",refresh_token:o.getRefreshToken()},i),null!==t.clientSecret&&(i.client_secret=t.clientSecret),i=n.stringify(i),a=e.extend({headers:{Authorization:void 0,"Content-Type":"application/x-www-form-urlencoded"}},a),r.post(""+t.baseUrl+t.grantPath,i,a).then(function(e){return o.setToken(e.data),e})},writable:!0,enumerable:!0,configurable:!0},revokeToken:{value:function(i,a){var u=o.getRefreshToken();return i=e.extend({client_id:t.clientId,token:u?u:o.getAccessToken(),token_type_hint:u?"refresh_token":"access_token"},i),null!==t.clientSecret&&(i.client_secret=t.clientSecret),i=n.stringify(i),a=e.extend({headers:{"Content-Type":"application/x-www-form-urlencoded"}},a),r.post(""+t.baseUrl+t.revokePath,i,a).then(function(e){return o.removeToken(),e})},writable:!0,enumerable:!0,configurable:!0}}),i}();return new i},this.$get.$inject=["$http","OAuthToken"]}function a(){var t={name:"token",options:{secure:!0}};this.configure=function(n){if(!(n instanceof Object))throw new TypeError("Invalid argument: `config` must be an `Object`.");return e.extend(t,n),t},this.$get=function(e){var n=function(){function n(){}return c(n,null,{setToken:{value:function(n){return e.putObject(t.name,n,t.options)},writable:!0,enumerable:!0,configurable:!0},getToken:{value:function(){return e.getObject(t.name)},writable:!0,enumerable:!0,configurable:!0},getAccessToken:{value:function(){return this.getToken()?this.getToken().access_token:void 0},writable:!0,enumerable:!0,configurable:!0},getAuthorizationHeader:{value:function(){return this.getTokenType()&&this.getAccessToken()?""+(this.getTokenType().charAt(0).toUpperCase()+this.getTokenType().substr(1))+" "+this.getAccessToken():void 0},writable:!0,enumerable:!0,configurable:!0},getRefreshToken:{value:function(){return this.getToken()?this.getToken().refresh_token:void 0},writable:!0,enumerable:!0,configurable:!0},getTokenType:{value:function(){return this.getToken()?this.getToken().token_type:void 0},writable:!0,enumerable:!0,configurable:!0},removeToken:{value:function(){return e.remove(t.name,t.options)},writable:!0,enumerable:!0,configurable:!0}}),n}();return new n},this.$get.$inject=["$cookies"]}var u=e.module("angular-oauth2",[t]).config(r).factory("oauthInterceptor",o).provider("OAuth",i).provider("OAuthToken",a);r.$inject=["$httpProvider"],o.$inject=["$q","$rootScope","OAuthToken"];var c=function(e,t,n){t&&Object.defineProperties(e,t),n&&Object.defineProperties(e.prototype,n)},s={baseUrl:null,clientId:null,clientSecret:null,grantPath:"/oauth2/token",revokePath:"/oauth2/revoke"},l=["baseUrl","clientId","grantPath","revokePath"],c=function(e,t,n){t&&Object.defineProperties(e,t),n&&Object.defineProperties(e.prototype,n)};return u});
\ No newline at end of file
diff --git a/package.json b/package.json
index fd5214c..c1d8e85 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "angular-oauth2",
-  "version": "3.0.1",
+  "version": "3.1.0",
   "description": "Angular OAuth2",
   "main": "./dist/angular-oauth2.js",
   "repository": {

From 02d1a4934e950ccfa344b496f76342088e2435cb Mon Sep 17 00:00:00 2001
From: Rui Silva <rui.penso@gmail.com>
Date: Wed, 10 Feb 2016 23:07:06 +0000
Subject: [PATCH 14/42] Bump lodash version

---
 karma.conf.js | 2 +-
 package.json  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/karma.conf.js b/karma.conf.js
index d43dca8..a8c8903 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -17,7 +17,7 @@ module.exports = function(config) {
       'node_modules/angular/angular.js',
       'node_modules/angular-cookies/angular-cookies.js',
       'node_modules/query-string/query-string.js',
-      'node_modules/lodash/dist/lodash.js',
+      'node_modules/lodash/lodash.js',
       'node_modules/angular-mocks/angular-mocks.js',
       'dist/angular-oauth2.js',
       'test/mocks/**/*.mock.js',
diff --git a/package.json b/package.json
index c1d8e85..68896ed 100644
--- a/package.json
+++ b/package.json
@@ -49,7 +49,7 @@
     "karma-mocha-reporter": "^0.3.1",
     "karma-should": "0.0.1",
     "karma-sinon": "^1.0.4",
-    "lodash": "^2.4.1",
+    "lodash": "^4.0.0",
     "mocha": "^2.4.5",
     "should": "^4.6.0",
     "sinon": "^1.17.3",

From f708bc094199903d6524c4c88b08297b9dbbf05a Mon Sep 17 00:00:00 2001
From: Rui Silva <rui.penso@gmail.com>
Date: Wed, 10 Feb 2016 23:09:54 +0000
Subject: [PATCH 15/42] Bump karma version

---
 gulpfile.js  | 6 ++++--
 package.json | 2 +-
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/gulpfile.js b/gulpfile.js
index d6b5057..333ac5f 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -7,7 +7,7 @@ var concat = require('gulp-concat');
 var gulp = require('gulp');
 var header = require('gulp-header');
 var jshint = require('gulp-jshint');
-var karma = require('karma').server;
+var karma = require('karma').Server;
 var ngAnnotate = require('gulp-ng-annotate');
 var pkg = require('./package.json');
 var rename = require('gulp-rename');
@@ -95,12 +95,14 @@ gulp.task('scripts-lint', function() {
  */
 
 gulp.task('test', ['scripts'], function() {
-  return karma.start({
+  var server = new karma({
     configFile: __dirname + '/karma.conf.js',
     singleRun: true
   }, function(code) {
     console.log('Karma has exited with code', code);
   });
+
+  return server.start();
 });
 
 /**
diff --git a/package.json b/package.json
index 68896ed..89223bd 100644
--- a/package.json
+++ b/package.json
@@ -41,7 +41,7 @@
     "gulp-uglify": "^1.0.2",
     "gulp-wrap-umd": "^0.2.1",
     "jshint-stylish": "^1.0.0",
-    "karma": "^0.12.31",
+    "karma": "^0.13.0",
     "karma-browserify": "^2.0.0",
     "karma-chrome-launcher": "^0.1.7",
     "karma-firefox-launcher": "^0.1.4",

From ef0894c879996836457953aed3b1762cb5671afc Mon Sep 17 00:00:00 2001
From: Rui Silva <rui.penso@gmail.com>
Date: Thu, 11 Feb 2016 00:26:01 +0000
Subject: [PATCH 16/42] Update 6to5 to babel

---
 gulpfile.js  | 4 ++--
 package.json | 3 +--
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/gulpfile.js b/gulpfile.js
index 333ac5f..61ef4f1 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -3,6 +3,7 @@
  * Module dependencies.
  */
 
+var babel = require('gulp-babel');
 var concat = require('gulp-concat');
 var gulp = require('gulp');
 var header = require('gulp-header');
@@ -11,7 +12,6 @@ var karma = require('karma').Server;
 var ngAnnotate = require('gulp-ng-annotate');
 var pkg = require('./package.json');
 var rename = require('gulp-rename');
-var to5 = require('gulp-6to5');
 var uglify = require('gulp-uglify');
 var wrapUmd = require('gulp-wrap-umd');
 
@@ -61,7 +61,7 @@ var config = {
 
 gulp.task('scripts', ['scripts-lint'], function() {
   return gulp.src(config.src)
-    .pipe(to5({ modules: 'ignore', blacklist: ['useStrict'] }))
+    .pipe(babel({ modules: 'ignore', blacklist: ['useStrict'] }))
     .pipe(ngAnnotate({ single_quotes: true, add: true }))
     .pipe(concat(config.name))
     .pipe(wrapUmd(config.umd))
diff --git a/package.json b/package.json
index 89223bd..780c264 100644
--- a/package.json
+++ b/package.json
@@ -27,12 +27,11 @@
     "query-string": "^1.0.0"
   },
   "devDependencies": {
-    "6to5ify": "^3.1.2",
     "angular-mocks": "^1.3.9",
     "bower": "^1.3.12",
     "github-changes": "^1.0.0",
     "gulp": "^3.8.10",
-    "gulp-6to5": "^2.0.2",
+    "gulp-babel": "^5.3.0",
     "gulp-concat": "^2.4.3",
     "gulp-header": "^1.2.2",
     "gulp-jshint": "^1.9.0",

From e522cce6ec469e6e9a0ee6a7b21de38b0df48f2f Mon Sep 17 00:00:00 2001
From: Rui Silva <rui.penso@gmail.com>
Date: Thu, 11 Feb 2016 00:31:33 +0000
Subject: [PATCH 17/42] Remove gulp-ng-annotate

---
 gulpfile.js                           | 2 --
 package.json                          | 1 -
 src/config/oauth-config.js            | 4 ++--
 src/interceptors/oauth-interceptor.js | 4 ++--
 src/providers/oauth-provider.js       | 4 ++--
 src/providers/oauth-token-provider.js | 4 ++--
 6 files changed, 8 insertions(+), 11 deletions(-)

diff --git a/gulpfile.js b/gulpfile.js
index 61ef4f1..c663500 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -9,7 +9,6 @@ var gulp = require('gulp');
 var header = require('gulp-header');
 var jshint = require('gulp-jshint');
 var karma = require('karma').Server;
-var ngAnnotate = require('gulp-ng-annotate');
 var pkg = require('./package.json');
 var rename = require('gulp-rename');
 var uglify = require('gulp-uglify');
@@ -62,7 +61,6 @@ var config = {
 gulp.task('scripts', ['scripts-lint'], function() {
   return gulp.src(config.src)
     .pipe(babel({ modules: 'ignore', blacklist: ['useStrict'] }))
-    .pipe(ngAnnotate({ single_quotes: true, add: true }))
     .pipe(concat(config.name))
     .pipe(wrapUmd(config.umd))
     .pipe(uglify({
diff --git a/package.json b/package.json
index 780c264..a6ed87b 100644
--- a/package.json
+++ b/package.json
@@ -35,7 +35,6 @@
     "gulp-concat": "^2.4.3",
     "gulp-header": "^1.2.2",
     "gulp-jshint": "^1.9.0",
-    "gulp-ng-annotate": "^0.4.4",
     "gulp-rename": "^1.2.0",
     "gulp-uglify": "^1.0.2",
     "gulp-wrap-umd": "^0.2.1",
diff --git a/src/config/oauth-config.js b/src/config/oauth-config.js
index d1df816..3bf8ab4 100644
--- a/src/config/oauth-config.js
+++ b/src/config/oauth-config.js
@@ -1,14 +1,14 @@
 
 /**
  * OAuth config.
- *
- * @ngInject
  */
 
 function oauthConfig($httpProvider) {
   $httpProvider.interceptors.push('oauthInterceptor');
 }
 
+oauthConfig.$inject = ['$httpProvider'];
+
 /**
  * Export `oauthConfig`.
  */
diff --git a/src/interceptors/oauth-interceptor.js b/src/interceptors/oauth-interceptor.js
index b6d5abf..bbdcadb 100644
--- a/src/interceptors/oauth-interceptor.js
+++ b/src/interceptors/oauth-interceptor.js
@@ -1,8 +1,6 @@
 
 /**
  * OAuth interceptor.
- *
- * @ngInject
  */
 
 function oauthInterceptor($q, $rootScope, OAuthToken) {
@@ -41,6 +39,8 @@ function oauthInterceptor($q, $rootScope, OAuthToken) {
   };
 }
 
+oauthInterceptor.$inject = ['$q', '$rootScope', 'OAuthToken'];
+
 /**
  * Export `oauthInterceptor`.
  */
diff --git a/src/providers/oauth-provider.js b/src/providers/oauth-provider.js
index df822fc..eeb3f57 100644
--- a/src/providers/oauth-provider.js
+++ b/src/providers/oauth-provider.js
@@ -75,8 +75,6 @@ function OAuthProvider() {
 
   /**
    * OAuth service.
-   *
-   * @ngInject
    */
 
   this.$get = function($http, OAuthToken) {
@@ -214,6 +212,8 @@ function OAuthProvider() {
 
     return new OAuth();
   };
+
+  this.$get.$inject = ['$http', 'OAuthToken'];
 }
 
 /**
diff --git a/src/providers/oauth-token-provider.js b/src/providers/oauth-token-provider.js
index 271ae9f..060f1dd 100644
--- a/src/providers/oauth-token-provider.js
+++ b/src/providers/oauth-token-provider.js
@@ -37,8 +37,6 @@ function OAuthTokenProvider() {
 
   /**
    * OAuthToken service.
-   *
-   * @ngInject
    */
 
   this.$get = function($cookies) {
@@ -107,6 +105,8 @@ function OAuthTokenProvider() {
 
     return new OAuthToken();
   };
+
+  this.$get.$inject = ['$cookies'];
 }
 
 /**

From 6ea8637867c489365e493782a4b7f5a8566ca0a5 Mon Sep 17 00:00:00 2001
From: Rui Silva <rui.penso@gmail.com>
Date: Thu, 11 Feb 2016 00:33:22 +0000
Subject: [PATCH 18/42] Remove deprecated bower dependency

---
 package.json | 1 -
 1 file changed, 1 deletion(-)

diff --git a/package.json b/package.json
index a6ed87b..a7c640e 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,6 @@
   },
   "devDependencies": {
     "angular-mocks": "^1.3.9",
-    "bower": "^1.3.12",
     "github-changes": "^1.0.0",
     "gulp": "^3.8.10",
     "gulp-babel": "^5.3.0",

From e9a71dc60e0a547209c71face3afead7e9317314 Mon Sep 17 00:00:00 2001
From: Rui Silva <rui.penso@gmail.com>
Date: Thu, 11 Feb 2016 00:59:42 +0000
Subject: [PATCH 19/42] Update travis node version

---
 .travis.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.travis.yml b/.travis.yml
index ea427c7..08635f2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -10,7 +10,7 @@ git:
   depth: 10
 
 node_js:
-  - iojs
+  - 4.3
 
 cache:
   directories:

From 1d6034761b6f3dd83dd3787ebd577a41ca956232 Mon Sep 17 00:00:00 2001
From: Rui Silva <penso@uphold.com>
Date: Fri, 12 Feb 2016 14:49:49 +0000
Subject: [PATCH 20/42] Revert "Merge pull request #77 from
 seegno/enhancement/authorization-header"

---
 src/interceptors/oauth-interceptor.js         |  5 ++---
 .../interceptors/oauth-interceptor.spec.js    | 22 -------------------
 2 files changed, 2 insertions(+), 25 deletions(-)

diff --git a/src/interceptors/oauth-interceptor.js b/src/interceptors/oauth-interceptor.js
index bbdcadb..896ae6d 100644
--- a/src/interceptors/oauth-interceptor.js
+++ b/src/interceptors/oauth-interceptor.js
@@ -6,10 +6,9 @@
 function oauthInterceptor($q, $rootScope, OAuthToken) {
   return {
     request: function(config) {
-      config.headers = config.headers || {};
-
       // Inject `Authorization` header.
-      if (!config.headers.hasOwnProperty('Authorization') && OAuthToken.getAuthorizationHeader()) {
+      if (OAuthToken.getAuthorizationHeader()) {
+        config.headers = config.headers || {};
         config.headers.Authorization = OAuthToken.getAuthorizationHeader();
       }
 
diff --git a/test/unit/interceptors/oauth-interceptor.spec.js b/test/unit/interceptors/oauth-interceptor.spec.js
index 4c5e3c7..9611589 100644
--- a/test/unit/interceptors/oauth-interceptor.spec.js
+++ b/test/unit/interceptors/oauth-interceptor.spec.js
@@ -37,28 +37,6 @@ describe('oauthInterceptor', function() {
     $httpBackend.flush();
   }));
 
-  it('should not inject `Authorization` header if it already exists', inject(function($http, $httpBackend, OAuthToken) {
-    OAuthToken.setToken({ token_type: 'bearer', access_token: 'foo', expires_in: 3600, refresh_token: 'bar' });
-
-    $httpBackend.expectGET('https://website.com', function(headers) {
-      headers.Authorization = undefined;
-
-      return headers;
-    }).respond(200);
-
-    $http.get('https://website.com').then(function(response) {
-      response.config.headers.should.have.property('Authorization');
-      (undefined === response.config.headers.Authorization).should.be.true;
-    }).catch(function() {
-      should.fail();
-    });
-
-    $httpBackend.flush();
-
-    $httpBackend.verifyNoOutstandingExpectation();
-    $httpBackend.verifyNoOutstandingRequest();
-  }));
-
   it('should remove `token` if an `invalid_request` error occurs', inject(function($http, $httpBackend, OAuthToken) {
     sinon.spy(OAuthToken, 'removeToken');
 

From beb1d54b06a75af3999818848431091fe09c13fe Mon Sep 17 00:00:00 2001
From: Rui Silva <penso@uphold.com>
Date: Fri, 12 Feb 2016 16:43:03 +0000
Subject: [PATCH 21/42] Release 3.1.1

---
 CHANGELOG.md               |   7 +-
 bower.json                 |   2 +-
 dist/angular-oauth2.js     | 318 +++++++++++++++++++------------------
 dist/angular-oauth2.min.js |   2 +-
 package.json               |   2 +-
 5 files changed, 168 insertions(+), 163 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 26570bd..5f52cbe 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,9 @@
 ## Changelog
 
-### upcoming / 2016-02-10
+### 3.1.1 / 2016-02-12
+- [#79](https://github.com/seegno/angular-oauth2/pull/79) Update dependencies (@ruipenso)
+
+### 3.1.0 / 2016-02-10
 - [#78](https://github.com/seegno/angular-oauth2/pull/78) Update OAuth methods to allow `data` and `options` override (@ruipenso)
 - [#71](https://github.com/seegno/angular-oauth2/pull/71) Add `client_id` and `client_secret` on revoke_token (@tinogomes)
 - [#77](https://github.com/seegno/angular-oauth2/pull/77) Update interceptor to allow authorization header to be overridden (@ruipenso)
@@ -39,4 +42,4 @@
 
 ### 1.0.0 / 2015-01-18
 - [#2](https://github.com/seegno/angular-oauth2/pull/2) Fix indentation in README examples (@fixe)
-- [#1](https://github.com/seegno/angular-oauth2/pull/1) Add project structure (@ruipenso)
\ No newline at end of file
+- [#1](https://github.com/seegno/angular-oauth2/pull/1) Add project structure (@ruipenso)
diff --git a/bower.json b/bower.json
index 49d1128..47270ea 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
 {
   "name": "angular-oauth2",
-  "version": "3.1.0",
+  "version": "3.1.1",
   "description": "AngularJS OAuth2",
   "main": "./dist/angular-oauth2.js",
   "authors": [
diff --git a/dist/angular-oauth2.js b/dist/angular-oauth2.js
index 35a1937..f8a9b04 100644
--- a/dist/angular-oauth2.js
+++ b/dist/angular-oauth2.js
@@ -1,6 +1,6 @@
 /**
  * angular-oauth2 - Angular OAuth2
- * @version v3.1.0
+ * @version v3.1.1
  * @link https://github.com/seegno/angular-oauth2
  * @license MIT
  */
@@ -20,19 +20,19 @@
     oauthConfig.$inject = [ "$httpProvider" ];
     function oauthInterceptor($q, $rootScope, OAuthToken) {
         return {
-            request: function(config) {
-                config.headers = config.headers || {};
-                if (!config.headers.hasOwnProperty("Authorization") && OAuthToken.getAuthorizationHeader()) {
+            request: function request(config) {
+                if (OAuthToken.getAuthorizationHeader()) {
+                    config.headers = config.headers || {};
                     config.headers.Authorization = OAuthToken.getAuthorizationHeader();
                 }
                 return config;
             },
-            responseError: function(rejection) {
+            responseError: function responseError(rejection) {
                 if (400 === rejection.status && rejection.data && ("invalid_request" === rejection.data.error || "invalid_grant" === rejection.data.error)) {
                     OAuthToken.removeToken();
                     $rootScope.$emit("oauth:error", rejection);
                 }
-                if (401 === rejection.status && (rejection.data && "invalid_token" === rejection.data.error) || rejection.headers("www-authenticate") && 0 === rejection.headers("www-authenticate").indexOf("Bearer")) {
+                if (401 === rejection.status && rejection.data && "invalid_token" === rejection.data.error || rejection.headers("www-authenticate") && 0 === rejection.headers("www-authenticate").indexOf("Bearer")) {
                     $rootScope.$emit("oauth:error", rejection);
                 }
                 return $q.reject(rejection);
@@ -40,10 +40,27 @@
         };
     }
     oauthInterceptor.$inject = [ "$q", "$rootScope", "OAuthToken" ];
-    var _prototypeProperties = function(child, staticProps, instanceProps) {
-        if (staticProps) Object.defineProperties(child, staticProps);
-        if (instanceProps) Object.defineProperties(child.prototype, instanceProps);
-    };
+    var _createClass = function() {
+        function defineProperties(target, props) {
+            for (var i = 0; i < props.length; i++) {
+                var descriptor = props[i];
+                descriptor.enumerable = descriptor.enumerable || false;
+                descriptor.configurable = true;
+                if ("value" in descriptor) descriptor.writable = true;
+                Object.defineProperty(target, descriptor.key, descriptor);
+            }
+        }
+        return function(Constructor, protoProps, staticProps) {
+            if (protoProps) defineProperties(Constructor.prototype, protoProps);
+            if (staticProps) defineProperties(Constructor, staticProps);
+            return Constructor;
+        };
+    }();
+    function _classCallCheck(instance, Constructor) {
+        if (!(instance instanceof Constructor)) {
+            throw new TypeError("Cannot call a class as a function");
+        }
+    }
     var defaults = {
         baseUrl: null,
         clientId: null,
@@ -81,107 +98,112 @@
         this.$get = function($http, OAuthToken) {
             var OAuth = function() {
                 function OAuth() {
+                    _classCallCheck(this, OAuth);
                     if (!config) {
                         throw new Error("`OAuthProvider` must be configured first.");
                     }
                 }
-                _prototypeProperties(OAuth, null, {
-                    isAuthenticated: {
-                        value: function isAuthenticated() {
-                            return !!OAuthToken.getToken();
-                        },
-                        writable: true,
-                        enumerable: true,
-                        configurable: true
-                    },
-                    getAccessToken: {
-                        value: function getAccessToken(data, options) {
-                            data = angular.extend({
-                                client_id: config.clientId,
-                                grant_type: "password"
-                            }, data);
-                            if (null !== config.clientSecret) {
-                                data.client_secret = config.clientSecret;
+                _createClass(OAuth, [ {
+                    key: "isAuthenticated",
+                    value: function isAuthenticated() {
+                        return !!OAuthToken.getToken();
+                    }
+                }, {
+                    key: "getAccessToken",
+                    value: function getAccessToken(data, options) {
+                        data = angular.extend({
+                            client_id: config.clientId,
+                            grant_type: "password"
+                        }, data);
+                        if (null !== config.clientSecret) {
+                            data.client_secret = config.clientSecret;
+                        }
+                        data = queryString.stringify(data);
+                        options = angular.extend({
+                            headers: {
+                                Authorization: undefined,
+                                "Content-Type": "application/x-www-form-urlencoded"
                             }
-                            data = queryString.stringify(data);
-                            options = angular.extend({
-                                headers: {
-                                    Authorization: undefined,
-                                    "Content-Type": "application/x-www-form-urlencoded"
-                                }
-                            }, options);
-                            return $http.post("" + config.baseUrl + "" + config.grantPath, data, options).then(function(response) {
-                                OAuthToken.setToken(response.data);
-                                return response;
-                            });
-                        },
-                        writable: true,
-                        enumerable: true,
-                        configurable: true
-                    },
-                    getRefreshToken: {
-                        value: function getRefreshToken(data, options) {
-                            data = angular.extend({
-                                client_id: config.clientId,
-                                grant_type: "refresh_token",
-                                refresh_token: OAuthToken.getRefreshToken()
-                            }, data);
-                            if (null !== config.clientSecret) {
-                                data.client_secret = config.clientSecret;
+                        }, options);
+                        return $http.post("" + config.baseUrl + config.grantPath, data, options).then(function(response) {
+                            OAuthToken.setToken(response.data);
+                            return response;
+                        });
+                    }
+                }, {
+                    key: "getRefreshToken",
+                    value: function getRefreshToken(data, options) {
+                        data = angular.extend({
+                            client_id: config.clientId,
+                            grant_type: "refresh_token",
+                            refresh_token: OAuthToken.getRefreshToken()
+                        }, data);
+                        if (null !== config.clientSecret) {
+                            data.client_secret = config.clientSecret;
+                        }
+                        data = queryString.stringify(data);
+                        options = angular.extend({
+                            headers: {
+                                Authorization: undefined,
+                                "Content-Type": "application/x-www-form-urlencoded"
                             }
-                            data = queryString.stringify(data);
-                            options = angular.extend({
-                                headers: {
-                                    Authorization: undefined,
-                                    "Content-Type": "application/x-www-form-urlencoded"
-                                }
-                            }, options);
-                            return $http.post("" + config.baseUrl + "" + config.grantPath, data, options).then(function(response) {
-                                OAuthToken.setToken(response.data);
-                                return response;
-                            });
-                        },
-                        writable: true,
-                        enumerable: true,
-                        configurable: true
-                    },
-                    revokeToken: {
-                        value: function revokeToken(data, options) {
-                            var refreshToken = OAuthToken.getRefreshToken();
-                            data = angular.extend({
-                                client_id: config.clientId,
-                                token: refreshToken ? refreshToken : OAuthToken.getAccessToken(),
-                                token_type_hint: refreshToken ? "refresh_token" : "access_token"
-                            }, data);
-                            if (null !== config.clientSecret) {
-                                data.client_secret = config.clientSecret;
+                        }, options);
+                        return $http.post("" + config.baseUrl + config.grantPath, data, options).then(function(response) {
+                            OAuthToken.setToken(response.data);
+                            return response;
+                        });
+                    }
+                }, {
+                    key: "revokeToken",
+                    value: function revokeToken(data, options) {
+                        var refreshToken = OAuthToken.getRefreshToken();
+                        data = angular.extend({
+                            client_id: config.clientId,
+                            token: refreshToken ? refreshToken : OAuthToken.getAccessToken(),
+                            token_type_hint: refreshToken ? "refresh_token" : "access_token"
+                        }, data);
+                        if (null !== config.clientSecret) {
+                            data.client_secret = config.clientSecret;
+                        }
+                        data = queryString.stringify(data);
+                        options = angular.extend({
+                            headers: {
+                                "Content-Type": "application/x-www-form-urlencoded"
                             }
-                            data = queryString.stringify(data);
-                            options = angular.extend({
-                                headers: {
-                                    "Content-Type": "application/x-www-form-urlencoded"
-                                }
-                            }, options);
-                            return $http.post("" + config.baseUrl + "" + config.revokePath, data, options).then(function(response) {
-                                OAuthToken.removeToken();
-                                return response;
-                            });
-                        },
-                        writable: true,
-                        enumerable: true,
-                        configurable: true
+                        }, options);
+                        return $http.post("" + config.baseUrl + config.revokePath, data, options).then(function(response) {
+                            OAuthToken.removeToken();
+                            return response;
+                        });
                     }
-                });
+                } ]);
                 return OAuth;
             }();
             return new OAuth();
         };
         this.$get.$inject = [ "$http", "OAuthToken" ];
     }
-    var _prototypeProperties = function(child, staticProps, instanceProps) {
-        if (staticProps) Object.defineProperties(child, staticProps);
-        if (instanceProps) Object.defineProperties(child.prototype, instanceProps);
-    };
+    var _createClass = function() {
+        function defineProperties(target, props) {
+            for (var i = 0; i < props.length; i++) {
+                var descriptor = props[i];
+                descriptor.enumerable = descriptor.enumerable || false;
+                descriptor.configurable = true;
+                if ("value" in descriptor) descriptor.writable = true;
+                Object.defineProperty(target, descriptor.key, descriptor);
+            }
+        }
+        return function(Constructor, protoProps, staticProps) {
+            if (protoProps) defineProperties(Constructor.prototype, protoProps);
+            if (staticProps) defineProperties(Constructor, staticProps);
+            return Constructor;
+        };
+    }();
+    function _classCallCheck(instance, Constructor) {
+        if (!(instance instanceof Constructor)) {
+            throw new TypeError("Cannot call a class as a function");
+        }
+    }
     function OAuthTokenProvider() {
         var config = {
             name: "token",
@@ -198,68 +220,48 @@
         };
         this.$get = function($cookies) {
             var OAuthToken = function() {
-                function OAuthToken() {}
-                _prototypeProperties(OAuthToken, null, {
-                    setToken: {
-                        value: function setToken(data) {
-                            return $cookies.putObject(config.name, data, config.options);
-                        },
-                        writable: true,
-                        enumerable: true,
-                        configurable: true
-                    },
-                    getToken: {
-                        value: function getToken() {
-                            return $cookies.getObject(config.name);
-                        },
-                        writable: true,
-                        enumerable: true,
-                        configurable: true
-                    },
-                    getAccessToken: {
-                        value: function getAccessToken() {
-                            return this.getToken() ? this.getToken().access_token : undefined;
-                        },
-                        writable: true,
-                        enumerable: true,
-                        configurable: true
-                    },
-                    getAuthorizationHeader: {
-                        value: function getAuthorizationHeader() {
-                            if (!(this.getTokenType() && this.getAccessToken())) {
-                                return;
-                            }
-                            return "" + (this.getTokenType().charAt(0).toUpperCase() + this.getTokenType().substr(1)) + " " + this.getAccessToken();
-                        },
-                        writable: true,
-                        enumerable: true,
-                        configurable: true
-                    },
-                    getRefreshToken: {
-                        value: function getRefreshToken() {
-                            return this.getToken() ? this.getToken().refresh_token : undefined;
-                        },
-                        writable: true,
-                        enumerable: true,
-                        configurable: true
-                    },
-                    getTokenType: {
-                        value: function getTokenType() {
-                            return this.getToken() ? this.getToken().token_type : undefined;
-                        },
-                        writable: true,
-                        enumerable: true,
-                        configurable: true
-                    },
-                    removeToken: {
-                        value: function removeToken() {
-                            return $cookies.remove(config.name, config.options);
-                        },
-                        writable: true,
-                        enumerable: true,
-                        configurable: true
+                function OAuthToken() {
+                    _classCallCheck(this, OAuthToken);
+                }
+                _createClass(OAuthToken, [ {
+                    key: "setToken",
+                    value: function setToken(data) {
+                        return $cookies.putObject(config.name, data, config.options);
+                    }
+                }, {
+                    key: "getToken",
+                    value: function getToken() {
+                        return $cookies.getObject(config.name);
+                    }
+                }, {
+                    key: "getAccessToken",
+                    value: function getAccessToken() {
+                        return this.getToken() ? this.getToken().access_token : undefined;
+                    }
+                }, {
+                    key: "getAuthorizationHeader",
+                    value: function getAuthorizationHeader() {
+                        if (!(this.getTokenType() && this.getAccessToken())) {
+                            return;
+                        }
+                        return this.getTokenType().charAt(0).toUpperCase() + this.getTokenType().substr(1) + " " + this.getAccessToken();
+                    }
+                }, {
+                    key: "getRefreshToken",
+                    value: function getRefreshToken() {
+                        return this.getToken() ? this.getToken().refresh_token : undefined;
+                    }
+                }, {
+                    key: "getTokenType",
+                    value: function getTokenType() {
+                        return this.getToken() ? this.getToken().token_type : undefined;
+                    }
+                }, {
+                    key: "removeToken",
+                    value: function removeToken() {
+                        return $cookies.remove(config.name, config.options);
                     }
-                });
+                } ]);
                 return OAuthToken;
             }();
             return new OAuthToken();
diff --git a/dist/angular-oauth2.min.js b/dist/angular-oauth2.min.js
index 317d5b4..cd22831 100644
--- a/dist/angular-oauth2.min.js
+++ b/dist/angular-oauth2.min.js
@@ -1 +1 @@
-!function(e,t){"function"==typeof define&&define.amd?define(["angular","angular-cookies","query-string"],t):"object"==typeof exports?module.exports=t(require("angular"),require("angular-cookies"),require("query-string")):e.angularOAuth2=t(e.angular,"ngCookies",e.queryString)}(this,function(e,t,n){function r(e){e.interceptors.push("oauthInterceptor")}function o(e,t,n){return{request:function(e){return e.headers=e.headers||{},!e.headers.hasOwnProperty("Authorization")&&n.getAuthorizationHeader()&&(e.headers.Authorization=n.getAuthorizationHeader()),e},responseError:function(r){return 400!==r.status||!r.data||"invalid_request"!==r.data.error&&"invalid_grant"!==r.data.error||(n.removeToken(),t.$emit("oauth:error",r)),(401===r.status&&r.data&&"invalid_token"===r.data.error||r.headers("www-authenticate")&&0===r.headers("www-authenticate").indexOf("Bearer"))&&t.$emit("oauth:error",r),e.reject(r)}}}function i(){var t;this.configure=function(n){if(t)throw new Error("Already configured.");if(!(n instanceof Object))throw new TypeError("Invalid argument: `config` must be an `Object`.");return t=e.extend({},s,n),e.forEach(l,function(e){if(!t[e])throw new Error("Missing parameter: "+e+".")}),"/"===t.baseUrl.substr(-1)&&(t.baseUrl=t.baseUrl.slice(0,-1)),"/"!==t.grantPath[0]&&(t.grantPath="/"+t.grantPath),"/"!==t.revokePath[0]&&(t.revokePath="/"+t.revokePath),t},this.$get=function(r,o){var i=function(){function i(){if(!t)throw new Error("`OAuthProvider` must be configured first.")}return c(i,null,{isAuthenticated:{value:function(){return!!o.getToken()},writable:!0,enumerable:!0,configurable:!0},getAccessToken:{value:function(i,a){return i=e.extend({client_id:t.clientId,grant_type:"password"},i),null!==t.clientSecret&&(i.client_secret=t.clientSecret),i=n.stringify(i),a=e.extend({headers:{Authorization:void 0,"Content-Type":"application/x-www-form-urlencoded"}},a),r.post(""+t.baseUrl+t.grantPath,i,a).then(function(e){return o.setToken(e.data),e})},writable:!0,enumerable:!0,configurable:!0},getRefreshToken:{value:function(i,a){return i=e.extend({client_id:t.clientId,grant_type:"refresh_token",refresh_token:o.getRefreshToken()},i),null!==t.clientSecret&&(i.client_secret=t.clientSecret),i=n.stringify(i),a=e.extend({headers:{Authorization:void 0,"Content-Type":"application/x-www-form-urlencoded"}},a),r.post(""+t.baseUrl+t.grantPath,i,a).then(function(e){return o.setToken(e.data),e})},writable:!0,enumerable:!0,configurable:!0},revokeToken:{value:function(i,a){var u=o.getRefreshToken();return i=e.extend({client_id:t.clientId,token:u?u:o.getAccessToken(),token_type_hint:u?"refresh_token":"access_token"},i),null!==t.clientSecret&&(i.client_secret=t.clientSecret),i=n.stringify(i),a=e.extend({headers:{"Content-Type":"application/x-www-form-urlencoded"}},a),r.post(""+t.baseUrl+t.revokePath,i,a).then(function(e){return o.removeToken(),e})},writable:!0,enumerable:!0,configurable:!0}}),i}();return new i},this.$get.$inject=["$http","OAuthToken"]}function a(){var t={name:"token",options:{secure:!0}};this.configure=function(n){if(!(n instanceof Object))throw new TypeError("Invalid argument: `config` must be an `Object`.");return e.extend(t,n),t},this.$get=function(e){var n=function(){function n(){}return c(n,null,{setToken:{value:function(n){return e.putObject(t.name,n,t.options)},writable:!0,enumerable:!0,configurable:!0},getToken:{value:function(){return e.getObject(t.name)},writable:!0,enumerable:!0,configurable:!0},getAccessToken:{value:function(){return this.getToken()?this.getToken().access_token:void 0},writable:!0,enumerable:!0,configurable:!0},getAuthorizationHeader:{value:function(){return this.getTokenType()&&this.getAccessToken()?""+(this.getTokenType().charAt(0).toUpperCase()+this.getTokenType().substr(1))+" "+this.getAccessToken():void 0},writable:!0,enumerable:!0,configurable:!0},getRefreshToken:{value:function(){return this.getToken()?this.getToken().refresh_token:void 0},writable:!0,enumerable:!0,configurable:!0},getTokenType:{value:function(){return this.getToken()?this.getToken().token_type:void 0},writable:!0,enumerable:!0,configurable:!0},removeToken:{value:function(){return e.remove(t.name,t.options)},writable:!0,enumerable:!0,configurable:!0}}),n}();return new n},this.$get.$inject=["$cookies"]}var u=e.module("angular-oauth2",[t]).config(r).factory("oauthInterceptor",o).provider("OAuth",i).provider("OAuthToken",a);r.$inject=["$httpProvider"],o.$inject=["$q","$rootScope","OAuthToken"];var c=function(e,t,n){t&&Object.defineProperties(e,t),n&&Object.defineProperties(e.prototype,n)},s={baseUrl:null,clientId:null,clientSecret:null,grantPath:"/oauth2/token",revokePath:"/oauth2/revoke"},l=["baseUrl","clientId","grantPath","revokePath"],c=function(e,t,n){t&&Object.defineProperties(e,t),n&&Object.defineProperties(e.prototype,n)};return u});
\ No newline at end of file
+!function(e,t){"function"==typeof define&&define.amd?define(["angular","angular-cookies","query-string"],t):"object"==typeof exports?module.exports=t(require("angular"),require("angular-cookies"),require("query-string")):e.angularOAuth2=t(e.angular,"ngCookies",e.queryString)}(this,function(e,t,n){function r(e){e.interceptors.push("oauthInterceptor")}function o(e,t,n){return{request:function(e){return n.getAuthorizationHeader()&&(e.headers=e.headers||{},e.headers.Authorization=n.getAuthorizationHeader()),e},responseError:function(r){return 400!==r.status||!r.data||"invalid_request"!==r.data.error&&"invalid_grant"!==r.data.error||(n.removeToken(),t.$emit("oauth:error",r)),(401===r.status&&r.data&&"invalid_token"===r.data.error||r.headers("www-authenticate")&&0===r.headers("www-authenticate").indexOf("Bearer"))&&t.$emit("oauth:error",r),e.reject(r)}}}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(){var t;this.configure=function(n){if(t)throw new Error("Already configured.");if(!(n instanceof Object))throw new TypeError("Invalid argument: `config` must be an `Object`.");return t=e.extend({},f,n),e.forEach(h,function(e){if(!t[e])throw new Error("Missing parameter: "+e+".")}),"/"===t.baseUrl.substr(-1)&&(t.baseUrl=t.baseUrl.slice(0,-1)),"/"!==t.grantPath[0]&&(t.grantPath="/"+t.grantPath),"/"!==t.revokePath[0]&&(t.revokePath="/"+t.revokePath),t},this.$get=function(r,o){var a=function(){function a(){if(i(this,a),!t)throw new Error("`OAuthProvider` must be configured first.")}return s(a,[{key:"isAuthenticated",value:function(){return!!o.getToken()}},{key:"getAccessToken",value:function(i,a){return i=e.extend({client_id:t.clientId,grant_type:"password"},i),null!==t.clientSecret&&(i.client_secret=t.clientSecret),i=n.stringify(i),a=e.extend({headers:{Authorization:void 0,"Content-Type":"application/x-www-form-urlencoded"}},a),r.post(""+t.baseUrl+t.grantPath,i,a).then(function(e){return o.setToken(e.data),e})}},{key:"getRefreshToken",value:function(i,a){return i=e.extend({client_id:t.clientId,grant_type:"refresh_token",refresh_token:o.getRefreshToken()},i),null!==t.clientSecret&&(i.client_secret=t.clientSecret),i=n.stringify(i),a=e.extend({headers:{Authorization:void 0,"Content-Type":"application/x-www-form-urlencoded"}},a),r.post(""+t.baseUrl+t.grantPath,i,a).then(function(e){return o.setToken(e.data),e})}},{key:"revokeToken",value:function(i,a){var u=o.getRefreshToken();return i=e.extend({client_id:t.clientId,token:u?u:o.getAccessToken(),token_type_hint:u?"refresh_token":"access_token"},i),null!==t.clientSecret&&(i.client_secret=t.clientSecret),i=n.stringify(i),a=e.extend({headers:{"Content-Type":"application/x-www-form-urlencoded"}},a),r.post(""+t.baseUrl+t.revokePath,i,a).then(function(e){return o.removeToken(),e})}}]),a}();return new a},this.$get.$inject=["$http","OAuthToken"]}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function u(){var t={name:"token",options:{secure:!0}};this.configure=function(n){if(!(n instanceof Object))throw new TypeError("Invalid argument: `config` must be an `Object`.");return e.extend(t,n),t},this.$get=function(e){var n=function(){function n(){i(this,n)}return s(n,[{key:"setToken",value:function(n){return e.putObject(t.name,n,t.options)}},{key:"getToken",value:function(){return e.getObject(t.name)}},{key:"getAccessToken",value:function(){return this.getToken()?this.getToken().access_token:void 0}},{key:"getAuthorizationHeader",value:function(){return this.getTokenType()&&this.getAccessToken()?this.getTokenType().charAt(0).toUpperCase()+this.getTokenType().substr(1)+" "+this.getAccessToken():void 0}},{key:"getRefreshToken",value:function(){return this.getToken()?this.getToken().refresh_token:void 0}},{key:"getTokenType",value:function(){return this.getToken()?this.getToken().token_type:void 0}},{key:"removeToken",value:function(){return e.remove(t.name,t.options)}}]),n}();return new n},this.$get.$inject=["$cookies"]}var c=e.module("angular-oauth2",[t]).config(r).factory("oauthInterceptor",o).provider("OAuth",a).provider("OAuthToken",u);r.$inject=["$httpProvider"],o.$inject=["$q","$rootScope","OAuthToken"];var s=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),f={baseUrl:null,clientId:null,clientSecret:null,grantPath:"/oauth2/token",revokePath:"/oauth2/revoke"},h=["baseUrl","clientId","grantPath","revokePath"],s=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}();return c});
\ No newline at end of file
diff --git a/package.json b/package.json
index a7c640e..f162810 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "angular-oauth2",
-  "version": "3.1.0",
+  "version": "3.1.1",
   "description": "Angular OAuth2",
   "main": "./dist/angular-oauth2.js",
   "repository": {

From 923fce9c006dc96b0d3bb35ffa61e9bd6665e432 Mon Sep 17 00:00:00 2001
From: Rui Silva <penso@uphold.com>
Date: Fri, 12 Feb 2016 17:29:30 +0000
Subject: [PATCH 22/42] Reintroduce authorization header to be overridden

Refs #77.
---
 src/interceptors/oauth-interceptor.js         |  5 +++--
 .../interceptors/oauth-interceptor.spec.js    | 22 +++++++++++++++++++
 2 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/src/interceptors/oauth-interceptor.js b/src/interceptors/oauth-interceptor.js
index 896ae6d..bbdcadb 100644
--- a/src/interceptors/oauth-interceptor.js
+++ b/src/interceptors/oauth-interceptor.js
@@ -6,9 +6,10 @@
 function oauthInterceptor($q, $rootScope, OAuthToken) {
   return {
     request: function(config) {
+      config.headers = config.headers || {};
+
       // Inject `Authorization` header.
-      if (OAuthToken.getAuthorizationHeader()) {
-        config.headers = config.headers || {};
+      if (!config.headers.hasOwnProperty('Authorization') && OAuthToken.getAuthorizationHeader()) {
         config.headers.Authorization = OAuthToken.getAuthorizationHeader();
       }
 
diff --git a/test/unit/interceptors/oauth-interceptor.spec.js b/test/unit/interceptors/oauth-interceptor.spec.js
index 9611589..4c5e3c7 100644
--- a/test/unit/interceptors/oauth-interceptor.spec.js
+++ b/test/unit/interceptors/oauth-interceptor.spec.js
@@ -37,6 +37,28 @@ describe('oauthInterceptor', function() {
     $httpBackend.flush();
   }));
 
+  it('should not inject `Authorization` header if it already exists', inject(function($http, $httpBackend, OAuthToken) {
+    OAuthToken.setToken({ token_type: 'bearer', access_token: 'foo', expires_in: 3600, refresh_token: 'bar' });
+
+    $httpBackend.expectGET('https://website.com', function(headers) {
+      headers.Authorization = undefined;
+
+      return headers;
+    }).respond(200);
+
+    $http.get('https://website.com').then(function(response) {
+      response.config.headers.should.have.property('Authorization');
+      (undefined === response.config.headers.Authorization).should.be.true;
+    }).catch(function() {
+      should.fail();
+    });
+
+    $httpBackend.flush();
+
+    $httpBackend.verifyNoOutstandingExpectation();
+    $httpBackend.verifyNoOutstandingRequest();
+  }));
+
   it('should remove `token` if an `invalid_request` error occurs', inject(function($http, $httpBackend, OAuthToken) {
     sinon.spy(OAuthToken, 'removeToken');
 

From 49341f2fb91860da5c2ab9a052ec36dee18d74b6 Mon Sep 17 00:00:00 2001
From: Rui Silva <penso@uphold.com>
Date: Fri, 12 Feb 2016 17:34:58 +0000
Subject: [PATCH 23/42] Release 4.0.0

---
 CHANGELOG.md               | 5 ++++-
 bower.json                 | 2 +-
 dist/angular-oauth2.js     | 6 +++---
 dist/angular-oauth2.min.js | 2 +-
 package.json               | 2 +-
 5 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5f52cbe..ab24432 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,8 @@
 ## Changelog
 
+### upcoming / 2016-02-12
+- [#80](https://github.com/seegno/angular-oauth2/pull/80) Reintroduce authorization header to be overridden (@ruipenso)
+
 ### 3.1.1 / 2016-02-12
 - [#79](https://github.com/seegno/angular-oauth2/pull/79) Update dependencies (@ruipenso)
 
@@ -42,4 +45,4 @@
 
 ### 1.0.0 / 2015-01-18
 - [#2](https://github.com/seegno/angular-oauth2/pull/2) Fix indentation in README examples (@fixe)
-- [#1](https://github.com/seegno/angular-oauth2/pull/1) Add project structure (@ruipenso)
+- [#1](https://github.com/seegno/angular-oauth2/pull/1) Add project structure (@ruipenso)
\ No newline at end of file
diff --git a/bower.json b/bower.json
index 47270ea..c351c6e 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
 {
   "name": "angular-oauth2",
-  "version": "3.1.1",
+  "version": "4.0.0",
   "description": "AngularJS OAuth2",
   "main": "./dist/angular-oauth2.js",
   "authors": [
diff --git a/dist/angular-oauth2.js b/dist/angular-oauth2.js
index f8a9b04..14bb50e 100644
--- a/dist/angular-oauth2.js
+++ b/dist/angular-oauth2.js
@@ -1,6 +1,6 @@
 /**
  * angular-oauth2 - Angular OAuth2
- * @version v3.1.1
+ * @version v4.0.0
  * @link https://github.com/seegno/angular-oauth2
  * @license MIT
  */
@@ -21,8 +21,8 @@
     function oauthInterceptor($q, $rootScope, OAuthToken) {
         return {
             request: function request(config) {
-                if (OAuthToken.getAuthorizationHeader()) {
-                    config.headers = config.headers || {};
+                config.headers = config.headers || {};
+                if (!config.headers.hasOwnProperty("Authorization") && OAuthToken.getAuthorizationHeader()) {
                     config.headers.Authorization = OAuthToken.getAuthorizationHeader();
                 }
                 return config;
diff --git a/dist/angular-oauth2.min.js b/dist/angular-oauth2.min.js
index cd22831..becd693 100644
--- a/dist/angular-oauth2.min.js
+++ b/dist/angular-oauth2.min.js
@@ -1 +1 @@
-!function(e,t){"function"==typeof define&&define.amd?define(["angular","angular-cookies","query-string"],t):"object"==typeof exports?module.exports=t(require("angular"),require("angular-cookies"),require("query-string")):e.angularOAuth2=t(e.angular,"ngCookies",e.queryString)}(this,function(e,t,n){function r(e){e.interceptors.push("oauthInterceptor")}function o(e,t,n){return{request:function(e){return n.getAuthorizationHeader()&&(e.headers=e.headers||{},e.headers.Authorization=n.getAuthorizationHeader()),e},responseError:function(r){return 400!==r.status||!r.data||"invalid_request"!==r.data.error&&"invalid_grant"!==r.data.error||(n.removeToken(),t.$emit("oauth:error",r)),(401===r.status&&r.data&&"invalid_token"===r.data.error||r.headers("www-authenticate")&&0===r.headers("www-authenticate").indexOf("Bearer"))&&t.$emit("oauth:error",r),e.reject(r)}}}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(){var t;this.configure=function(n){if(t)throw new Error("Already configured.");if(!(n instanceof Object))throw new TypeError("Invalid argument: `config` must be an `Object`.");return t=e.extend({},f,n),e.forEach(h,function(e){if(!t[e])throw new Error("Missing parameter: "+e+".")}),"/"===t.baseUrl.substr(-1)&&(t.baseUrl=t.baseUrl.slice(0,-1)),"/"!==t.grantPath[0]&&(t.grantPath="/"+t.grantPath),"/"!==t.revokePath[0]&&(t.revokePath="/"+t.revokePath),t},this.$get=function(r,o){var a=function(){function a(){if(i(this,a),!t)throw new Error("`OAuthProvider` must be configured first.")}return s(a,[{key:"isAuthenticated",value:function(){return!!o.getToken()}},{key:"getAccessToken",value:function(i,a){return i=e.extend({client_id:t.clientId,grant_type:"password"},i),null!==t.clientSecret&&(i.client_secret=t.clientSecret),i=n.stringify(i),a=e.extend({headers:{Authorization:void 0,"Content-Type":"application/x-www-form-urlencoded"}},a),r.post(""+t.baseUrl+t.grantPath,i,a).then(function(e){return o.setToken(e.data),e})}},{key:"getRefreshToken",value:function(i,a){return i=e.extend({client_id:t.clientId,grant_type:"refresh_token",refresh_token:o.getRefreshToken()},i),null!==t.clientSecret&&(i.client_secret=t.clientSecret),i=n.stringify(i),a=e.extend({headers:{Authorization:void 0,"Content-Type":"application/x-www-form-urlencoded"}},a),r.post(""+t.baseUrl+t.grantPath,i,a).then(function(e){return o.setToken(e.data),e})}},{key:"revokeToken",value:function(i,a){var u=o.getRefreshToken();return i=e.extend({client_id:t.clientId,token:u?u:o.getAccessToken(),token_type_hint:u?"refresh_token":"access_token"},i),null!==t.clientSecret&&(i.client_secret=t.clientSecret),i=n.stringify(i),a=e.extend({headers:{"Content-Type":"application/x-www-form-urlencoded"}},a),r.post(""+t.baseUrl+t.revokePath,i,a).then(function(e){return o.removeToken(),e})}}]),a}();return new a},this.$get.$inject=["$http","OAuthToken"]}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function u(){var t={name:"token",options:{secure:!0}};this.configure=function(n){if(!(n instanceof Object))throw new TypeError("Invalid argument: `config` must be an `Object`.");return e.extend(t,n),t},this.$get=function(e){var n=function(){function n(){i(this,n)}return s(n,[{key:"setToken",value:function(n){return e.putObject(t.name,n,t.options)}},{key:"getToken",value:function(){return e.getObject(t.name)}},{key:"getAccessToken",value:function(){return this.getToken()?this.getToken().access_token:void 0}},{key:"getAuthorizationHeader",value:function(){return this.getTokenType()&&this.getAccessToken()?this.getTokenType().charAt(0).toUpperCase()+this.getTokenType().substr(1)+" "+this.getAccessToken():void 0}},{key:"getRefreshToken",value:function(){return this.getToken()?this.getToken().refresh_token:void 0}},{key:"getTokenType",value:function(){return this.getToken()?this.getToken().token_type:void 0}},{key:"removeToken",value:function(){return e.remove(t.name,t.options)}}]),n}();return new n},this.$get.$inject=["$cookies"]}var c=e.module("angular-oauth2",[t]).config(r).factory("oauthInterceptor",o).provider("OAuth",a).provider("OAuthToken",u);r.$inject=["$httpProvider"],o.$inject=["$q","$rootScope","OAuthToken"];var s=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),f={baseUrl:null,clientId:null,clientSecret:null,grantPath:"/oauth2/token",revokePath:"/oauth2/revoke"},h=["baseUrl","clientId","grantPath","revokePath"],s=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}();return c});
\ No newline at end of file
+!function(e,t){"function"==typeof define&&define.amd?define(["angular","angular-cookies","query-string"],t):"object"==typeof exports?module.exports=t(require("angular"),require("angular-cookies"),require("query-string")):e.angularOAuth2=t(e.angular,"ngCookies",e.queryString)}(this,function(e,t,n){function r(e){e.interceptors.push("oauthInterceptor")}function o(e,t,n){return{request:function(e){return e.headers=e.headers||{},!e.headers.hasOwnProperty("Authorization")&&n.getAuthorizationHeader()&&(e.headers.Authorization=n.getAuthorizationHeader()),e},responseError:function(r){return 400!==r.status||!r.data||"invalid_request"!==r.data.error&&"invalid_grant"!==r.data.error||(n.removeToken(),t.$emit("oauth:error",r)),(401===r.status&&r.data&&"invalid_token"===r.data.error||r.headers("www-authenticate")&&0===r.headers("www-authenticate").indexOf("Bearer"))&&t.$emit("oauth:error",r),e.reject(r)}}}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(){var t;this.configure=function(n){if(t)throw new Error("Already configured.");if(!(n instanceof Object))throw new TypeError("Invalid argument: `config` must be an `Object`.");return t=e.extend({},h,n),e.forEach(f,function(e){if(!t[e])throw new Error("Missing parameter: "+e+".")}),"/"===t.baseUrl.substr(-1)&&(t.baseUrl=t.baseUrl.slice(0,-1)),"/"!==t.grantPath[0]&&(t.grantPath="/"+t.grantPath),"/"!==t.revokePath[0]&&(t.revokePath="/"+t.revokePath),t},this.$get=function(r,o){var a=function(){function a(){if(i(this,a),!t)throw new Error("`OAuthProvider` must be configured first.")}return s(a,[{key:"isAuthenticated",value:function(){return!!o.getToken()}},{key:"getAccessToken",value:function(i,a){return i=e.extend({client_id:t.clientId,grant_type:"password"},i),null!==t.clientSecret&&(i.client_secret=t.clientSecret),i=n.stringify(i),a=e.extend({headers:{Authorization:void 0,"Content-Type":"application/x-www-form-urlencoded"}},a),r.post(""+t.baseUrl+t.grantPath,i,a).then(function(e){return o.setToken(e.data),e})}},{key:"getRefreshToken",value:function(i,a){return i=e.extend({client_id:t.clientId,grant_type:"refresh_token",refresh_token:o.getRefreshToken()},i),null!==t.clientSecret&&(i.client_secret=t.clientSecret),i=n.stringify(i),a=e.extend({headers:{Authorization:void 0,"Content-Type":"application/x-www-form-urlencoded"}},a),r.post(""+t.baseUrl+t.grantPath,i,a).then(function(e){return o.setToken(e.data),e})}},{key:"revokeToken",value:function(i,a){var u=o.getRefreshToken();return i=e.extend({client_id:t.clientId,token:u?u:o.getAccessToken(),token_type_hint:u?"refresh_token":"access_token"},i),null!==t.clientSecret&&(i.client_secret=t.clientSecret),i=n.stringify(i),a=e.extend({headers:{"Content-Type":"application/x-www-form-urlencoded"}},a),r.post(""+t.baseUrl+t.revokePath,i,a).then(function(e){return o.removeToken(),e})}}]),a}();return new a},this.$get.$inject=["$http","OAuthToken"]}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function u(){var t={name:"token",options:{secure:!0}};this.configure=function(n){if(!(n instanceof Object))throw new TypeError("Invalid argument: `config` must be an `Object`.");return e.extend(t,n),t},this.$get=function(e){var n=function(){function n(){i(this,n)}return s(n,[{key:"setToken",value:function(n){return e.putObject(t.name,n,t.options)}},{key:"getToken",value:function(){return e.getObject(t.name)}},{key:"getAccessToken",value:function(){return this.getToken()?this.getToken().access_token:void 0}},{key:"getAuthorizationHeader",value:function(){return this.getTokenType()&&this.getAccessToken()?this.getTokenType().charAt(0).toUpperCase()+this.getTokenType().substr(1)+" "+this.getAccessToken():void 0}},{key:"getRefreshToken",value:function(){return this.getToken()?this.getToken().refresh_token:void 0}},{key:"getTokenType",value:function(){return this.getToken()?this.getToken().token_type:void 0}},{key:"removeToken",value:function(){return e.remove(t.name,t.options)}}]),n}();return new n},this.$get.$inject=["$cookies"]}var c=e.module("angular-oauth2",[t]).config(r).factory("oauthInterceptor",o).provider("OAuth",a).provider("OAuthToken",u);r.$inject=["$httpProvider"],o.$inject=["$q","$rootScope","OAuthToken"];var s=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),h={baseUrl:null,clientId:null,clientSecret:null,grantPath:"/oauth2/token",revokePath:"/oauth2/revoke"},f=["baseUrl","clientId","grantPath","revokePath"],s=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}();return c});
\ No newline at end of file
diff --git a/package.json b/package.json
index f162810..8fe7e25 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "angular-oauth2",
-  "version": "3.1.1",
+  "version": "4.0.0",
   "description": "Angular OAuth2",
   "main": "./dist/angular-oauth2.js",
   "repository": {

From 9152090ec66e6d36a38f2a8d418f5f60cddf48f9 Mon Sep 17 00:00:00 2001
From: LionelB <lionel@lumographe.fr>
Date: Wed, 13 Apr 2016 17:31:04 +0200
Subject: [PATCH 24/42] allow overriding default config

---
 src/providers/oauth-provider.js            | 40 +++++++------
 test/unit/providers/oauth-provider.spec.js | 67 +++++++++++++++++++++-
 2 files changed, 89 insertions(+), 18 deletions(-)

diff --git a/src/providers/oauth-provider.js b/src/providers/oauth-provider.js
index eeb3f57..cd73972 100644
--- a/src/providers/oauth-provider.js
+++ b/src/providers/oauth-provider.js
@@ -26,7 +26,7 @@ var requiredKeys = [
  */
 
 function OAuthProvider() {
-  var config;
+  var defaultConfig;
 
   /**
    * Configure.
@@ -36,7 +36,7 @@ function OAuthProvider() {
 
   this.configure = function(params) {
     // Can only be configured once.
-    if (config) {
+    if (defaultConfig) {
       throw new Error('Already configured.');
     }
 
@@ -46,31 +46,30 @@ function OAuthProvider() {
     }
 
     // Extend default configuration.
-    config = angular.extend({}, defaults, params);
+    defaultConfig = angular.extend({}, defaults, params);
 
     // Check if all required keys are set.
     angular.forEach(requiredKeys, (key) => {
-      if (!config[key]) {
+      if (!defaultConfig[key]) {
         throw new Error(`Missing parameter: ${key}.`);
       }
     });
 
     // Remove `baseUrl` trailing slash.
-    if('/' === config.baseUrl.substr(-1)) {
-      config.baseUrl = config.baseUrl.slice(0, -1);
+    if('/' === defaultConfig.baseUrl.substr(-1)) {
+      defaultConfig.baseUrl = defaultConfig.baseUrl.slice(0, -1);
     }
 
     // Add `grantPath` facing slash.
-    if('/' !== config.grantPath[0]) {
-      config.grantPath = `/${config.grantPath}`;
+    if('/' !== defaultConfig.grantPath[0]) {
+      defaultConfig.grantPath = `/${defaultConfig.grantPath}`;
     }
 
     // Add `revokePath` facing slash.
-    if('/' !== config.revokePath[0]) {
-      config.revokePath = `/${config.revokePath}`;
+    if('/' !== defaultConfig.revokePath[0]) {
+      defaultConfig.revokePath = `/${defaultConfig.revokePath}`;
     }
-
-    return config;
+    return defaultConfig;
   };
 
   /**
@@ -85,7 +84,7 @@ function OAuthProvider() {
        */
 
       constructor() {
-        if (!config) {
+        if (!defaultConfig) {
           throw new Error('`OAuthProvider` must be configured first.');
         }
       }
@@ -110,7 +109,10 @@ function OAuthProvider() {
        * @return {promise} A response promise.
        */
 
-      getAccessToken(data, options) {
+      getAccessToken(data, options, config) {
+        //Override default Oauth config
+        config = angular.extend({}, defaultConfig, config);
+
         data = angular.extend({
           client_id: config.clientId,
           grant_type: 'password'
@@ -145,7 +147,10 @@ function OAuthProvider() {
        * @return {promise} A response promise.
        */
 
-      getRefreshToken(data, options) {
+      getRefreshToken(data, options, config) {
+        //Override default Oauth config
+        config = angular.extend({}, defaultConfig, config);
+
         data = angular.extend({
           client_id: config.clientId,
           grant_type: 'refresh_token',
@@ -181,7 +186,10 @@ function OAuthProvider() {
        * @return {promise} A response promise.
        */
 
-      revokeToken(data, options) {
+      revokeToken(data, options, config) {
+        //Override default Oauth config
+        config = angular.extend(defaultConfig, config);
+
         var refreshToken = OAuthToken.getRefreshToken();
 
         data = angular.extend({
diff --git a/test/unit/providers/oauth-provider.spec.js b/test/unit/providers/oauth-provider.spec.js
index 0b56c3c..39ff315 100644
--- a/test/unit/providers/oauth-provider.spec.js
+++ b/test/unit/providers/oauth-provider.spec.js
@@ -216,6 +216,26 @@ describe('OAuthProvider', function() {
         $httpBackend.verifyNoOutstandingExpectation();
         $httpBackend.verifyNoOutstandingRequest();
       }));
+
+      it('should allow to override oauth server config', inject(function($httpBackend, OAuth, OAuthToken) {
+        var config = {
+          baseUrl: 'https://new.website.com',
+          grantPath: '/oauth2/token',
+        };
+
+        $httpBackend.expectPOST(config.baseUrl + config.grantPath, data)
+          .respond({ token_type: 'bearer', access_token: 'foo', expires_in: 3600, refresh_token: 'bar' });
+
+        OAuth.getAccessToken({
+          username: 'foo',
+          password: 'bar'
+        }, {}, config);
+
+        $httpBackend.flush();
+
+        $httpBackend.verifyNoOutstandingExpectation();
+        $httpBackend.verifyNoOutstandingRequest();
+      }));
     });
 
     describe('refreshToken()', function() {
@@ -293,8 +313,6 @@ describe('OAuthProvider', function() {
             expires_in: 3600,
             refresh_token: 'biz'
           });
-        }).catch(function() {
-          should.fail();
         });
 
         $httpBackend.flush();
@@ -302,6 +320,25 @@ describe('OAuthProvider', function() {
         $httpBackend.verifyNoOutstandingExpectation();
         $httpBackend.verifyNoOutstandingRequest();
       }));
+
+      it('should allow to override oauth server config', inject(function($httpBackend, OAuth, OAuthToken) {
+        var config = {
+          baseUrl: 'https://new.website.com',
+          grantPath: '/oauth2/token/new',
+        };
+
+        OAuthToken.setToken({ token_type: 'bearer', access_token: 'foo', expires_in: 3600, refresh_token: 'bar' });
+
+        $httpBackend.expectPOST(config.baseUrl + config.grantPath, queryString.stringify(data))
+          .respond({ token_type: 'bearer', access_token: 'qux', expires_in: 3600, refresh_token: 'biz' });
+
+        OAuth.getRefreshToken(data, {}, config);
+
+        $httpBackend.flush();
+
+        $httpBackend.verifyNoOutstandingExpectation();
+        $httpBackend.verifyNoOutstandingRequest();
+      }));
     });
 
     describe('revokeToken()', function () {
@@ -388,6 +425,32 @@ describe('OAuthProvider', function() {
         $httpBackend.verifyNoOutstandingExpectation();
         $httpBackend.verifyNoOutstandingRequest();
       }));
+
+      it('should allow to override oauth server config', inject(function($httpBackend, OAuth, OAuthToken) {
+        var data = queryString.stringify({
+          client_id: defaults.clientId,
+          token: 'bar',
+          token_type_hint: 'refresh_token',
+          client_secret: defaults.clientSecret
+        });
+
+        var config = {
+          baseUrl: 'https://new.website.com',
+          revokePath: '/oauth2/revoke/new',
+        };
+
+        OAuthToken.setToken({ token_type: 'bearer', access_token: 'foo', expires_in: 3600, refresh_token: 'bar' });
+
+        $httpBackend.expectPOST(config.baseUrl + config.revokePath, data)
+          .respond(200);
+
+        OAuth.revokeToken(data, {}, config);
+
+        $httpBackend.flush();
+
+        $httpBackend.verifyNoOutstandingExpectation();
+        $httpBackend.verifyNoOutstandingRequest();
+      }));
     });
   });
 });

From 304aeb0134c6c057367b76c646608f00853cd51f Mon Sep 17 00:00:00 2001
From: LionelB <lionel@lumographe.fr>
Date: Sun, 10 Jul 2016 16:31:51 +0200
Subject: [PATCH 25/42] allow OAuth configuration at run time

---
 README.md                                  |  14 ++
 src/providers/oauth-provider.js            |  93 ++++++------
 test/unit/providers/oauth-provider.spec.js | 165 +++++++++++----------
 3 files changed, 152 insertions(+), 120 deletions(-)

diff --git a/README.md b/README.md
index 9ff56ca..5f92ef9 100644
--- a/README.md
+++ b/README.md
@@ -44,6 +44,8 @@ angular.module('myApp', ['angular-oauth2'])
   }]);
 ```
 
+ps: You can also configure service at runtime, in case you retrieve oauth server configuration from a ajax request.
+
 ###### 4. Catch `OAuth` errors and do something with them (optional):
 
 ```js
@@ -84,6 +86,18 @@ OAuthProvider.configure({
 
 #### OAuth
 
+Update configuration defaults
+
+```js
+OAuth.configure({
+  baseUrl: null,
+  clientId: null,
+  clientSecret: null,
+  grantPath: '/oauth2/token',
+  revokePath: '/oauth2/revoke'
+});
+
+```
 Check authentication status:
 
 ```js
diff --git a/src/providers/oauth-provider.js b/src/providers/oauth-provider.js
index cd73972..dff8d0b 100644
--- a/src/providers/oauth-provider.js
+++ b/src/providers/oauth-provider.js
@@ -29,46 +29,51 @@ function OAuthProvider() {
   var defaultConfig;
 
   /**
-   * Configure.
-   *
-   * @param {object} params - An `object` of params to extend.
+   * @private
+   * sanitize configuration parameters
+   * @param {object} an `object` of params to sanitize
+   * @return {object} an sanitize version of the params
    */
-
-  this.configure = function(params) {
-    // Can only be configured once.
-    if (defaultConfig) {
-      throw new Error('Already configured.');
-    }
-
-    // Check if is an `object`.
+  const sanitizeConfigParams = (params) => {
     if (!(params instanceof Object)) {
       throw new TypeError('Invalid argument: `config` must be an `Object`.');
     }
 
     // Extend default configuration.
-    defaultConfig = angular.extend({}, defaults, params);
+    const config = angular.extend({}, defaults, params);
 
     // Check if all required keys are set.
     angular.forEach(requiredKeys, (key) => {
-      if (!defaultConfig[key]) {
+      if (!config[key]) {
         throw new Error(`Missing parameter: ${key}.`);
       }
     });
 
     // Remove `baseUrl` trailing slash.
-    if('/' === defaultConfig.baseUrl.substr(-1)) {
-      defaultConfig.baseUrl = defaultConfig.baseUrl.slice(0, -1);
+    if ('/' === config.baseUrl.substr(-1)) {
+      config.baseUrl = config.baseUrl.slice(0, -1);
     }
 
     // Add `grantPath` facing slash.
-    if('/' !== defaultConfig.grantPath[0]) {
-      defaultConfig.grantPath = `/${defaultConfig.grantPath}`;
+    if ('/' !== config.grantPath[0]) {
+      config.grantPath = `/${config.grantPath}`;
     }
 
     // Add `revokePath` facing slash.
-    if('/' !== defaultConfig.revokePath[0]) {
-      defaultConfig.revokePath = `/${defaultConfig.revokePath}`;
+    if ('/' !== config.revokePath[0]) {
+      config.revokePath = `/${config.revokePath}`;
     }
+
+    return config;
+  };
+  
+  /**
+   * Configure.
+   *
+   * @param {object} params - An `object` of params to extend.
+   */
+  this.configure = function(params) {
+    defaultConfig = sanitizeConfigParams(params);
     return defaultConfig;
   };
 
@@ -88,6 +93,17 @@ function OAuthProvider() {
           throw new Error('`OAuthProvider` must be configured first.');
         }
       }
+      
+      /**
+       * Configure OAuth service during runtime
+       *
+       * @param {Object} params - An object of params to extend
+       */
+      configure(params) {
+        defaultConfig = sanitizeConfigParams(params);
+        return defaultConfig;
+      }
+
 
       /**
        * Verifies if the `user` is authenticated or not based on the `token`
@@ -109,17 +125,14 @@ function OAuthProvider() {
        * @return {promise} A response promise.
        */
 
-      getAccessToken(data, options, config) {
-        //Override default Oauth config
-        config = angular.extend({}, defaultConfig, config);
-
+      getAccessToken(data, options) {
         data = angular.extend({
-          client_id: config.clientId,
+          client_id: defaultConfig.clientId,
           grant_type: 'password'
         }, data);
 
-        if (null !== config.clientSecret) {
-          data.client_secret = config.clientSecret;
+        if (null !== defaultConfig.clientSecret) {
+          data.client_secret = defaultConfig.clientSecret;
         }
 
         data = queryString.stringify(data);
@@ -131,7 +144,7 @@ function OAuthProvider() {
           }
         }, options);
 
-        return $http.post(`${config.baseUrl}${config.grantPath}`, data, options).then((response) => {
+        return $http.post(`${defaultConfig.baseUrl}${defaultConfig.grantPath}`, data, options).then((response) => {
           OAuthToken.setToken(response.data);
 
           return response;
@@ -147,18 +160,15 @@ function OAuthProvider() {
        * @return {promise} A response promise.
        */
 
-      getRefreshToken(data, options, config) {
-        //Override default Oauth config
-        config = angular.extend({}, defaultConfig, config);
-
+      getRefreshToken(data, options) {
         data = angular.extend({
-          client_id: config.clientId,
+          client_id: defaultConfig.clientId,
           grant_type: 'refresh_token',
           refresh_token: OAuthToken.getRefreshToken(),
         }, data);
 
-        if (null !== config.clientSecret) {
-          data.client_secret = config.clientSecret;
+        if (null !== defaultConfig.clientSecret) {
+          data.client_secret = defaultConfig.clientSecret;
         }
 
         data = queryString.stringify(data);
@@ -170,7 +180,7 @@ function OAuthProvider() {
           }
         }, options);
 
-        return $http.post(`${config.baseUrl}${config.grantPath}`, data, options).then((response) => {
+        return $http.post(`${defaultConfig.baseUrl}${defaultConfig.grantPath}`, data, options).then((response) => {
           OAuthToken.setToken(response.data);
 
           return response;
@@ -186,20 +196,17 @@ function OAuthProvider() {
        * @return {promise} A response promise.
        */
 
-      revokeToken(data, options, config) {
-        //Override default Oauth config
-        config = angular.extend(defaultConfig, config);
-
+      revokeToken(data, options) {
         var refreshToken = OAuthToken.getRefreshToken();
 
         data = angular.extend({
-          client_id: config.clientId,
+          client_id: defaultConfig.clientId,
           token: refreshToken ? refreshToken : OAuthToken.getAccessToken(),
           token_type_hint: refreshToken ? 'refresh_token' : 'access_token'
         }, data);
 
-        if (null !== config.clientSecret) {
-          data.client_secret = config.clientSecret;
+        if (null !== defaultConfig.clientSecret) {
+          data.client_secret = defaultConfig.clientSecret;
         }
 
         data = queryString.stringify(data);
@@ -210,7 +217,7 @@ function OAuthProvider() {
           }
         }, options);
 
-        return $http.post(`${config.baseUrl}${config.revokePath}`, data, options).then((response) => {
+        return $http.post(`${defaultConfig.baseUrl}${defaultConfig.revokePath}`, data, options).then((response) => {
           OAuthToken.removeToken();
 
           return response;
diff --git a/test/unit/providers/oauth-provider.spec.js b/test/unit/providers/oauth-provider.spec.js
index 39ff315..26f2eb8 100644
--- a/test/unit/providers/oauth-provider.spec.js
+++ b/test/unit/providers/oauth-provider.spec.js
@@ -13,7 +13,7 @@ describe('OAuthProvider', function() {
   };
 
   describe('configure()', function() {
-    var provider;
+    var provider, testSuite;
 
     beforeEach(function() {
       angular.module('angular-oauth2.test', [])
@@ -37,17 +37,6 @@ describe('OAuthProvider', function() {
       }
     });
 
-    it('should throw an error if already configured', function() {
-      try {
-        provider.configure(defaults);
-        provider.configure(defaults);
-
-        should.fail();
-      } catch(e) {
-        e.should.be.an.instanceOf(Error);
-      }
-    });
-
     it('should throw an error if `baseUrl` param is empty', function() {
       try {
         provider.configure(_.omit(defaults, 'baseUrl'));
@@ -137,6 +126,93 @@ describe('OAuthProvider', function() {
       OAuthToken.removeToken();
     }));
 
+    describe('configure()', function() {
+      it('should throw an error if configuration is not an object', inject(function(OAuth) {
+        try {
+          OAuth.configure(false);
+
+          should.fail();
+        } catch(e) {
+          e.should.be.an.instanceOf(TypeError);
+          e.message.should.match(/config/);
+        }
+      }));
+      
+      it('should throw an error if `baseUrl` param is empty', inject(function(OAuth) {
+        try {
+          OAuth.configure(_.omit(defaults, 'baseUrl'));
+
+          should.fail();
+        } catch(e) {
+          e.should.be.an.instanceOf(Error);
+          e.message.should.match(/baseUrl/);
+        }
+      }));
+
+      it('should throw an error if `clientId` param is empty', inject(function(OAuth) {
+        try {
+          OAuth.configure(_.omit(defaults, 'clientId'));
+
+          should.fail();
+        } catch(e) {
+          e.should.be.an.instanceOf(Error);
+          e.message.should.match(/clientId/);
+        }
+      }));
+
+      it('should not throw an error if `clientSecret` param is empty', inject(function(OAuth) {
+        var config = OAuth.configure(_.omit(defaults, 'clientSecret'));
+
+        (null === config.clientSecret).should.true;
+      }));
+
+      it('should throw an error if `grantPath` param is empty', inject(function(OAuth) {
+        try {
+          OAuth.configure(_.defaults({ grantPath: null }, defaults));
+
+          should.fail();
+        } catch(e) {
+          e.should.be.an.instanceOf(Error);
+          e.message.should.match(/grantPath/);
+        }
+      }));
+
+      it('should remove trailing slash from `baseUrl`', inject(function(OAuth) {
+        var config = OAuth.configure(_.defaults({
+          baseUrl: 'https://api.website.com/'
+        }, defaults));
+
+        config.baseUrl.should.equal('https://api.website.com');
+      }));
+
+      it('should add facing slash from `grantPath`', inject(function(OAuth) {
+        var config = OAuth.configure(_.defaults({
+          grantPath: 'oauth2/token'
+        }, defaults));
+
+        config.grantPath.should.equal('/oauth2/token');
+      }));
+
+      it('should throw an error if `revokePath` param is empty', inject(function(OAuth) {
+        try {
+          OAuth.configure(_.defaults({ revokePath: null }, defaults));
+
+          should.fail();
+        } catch(e) {
+          e.should.be.an.instanceOf(Error);
+          e.message.should.match(/revokePath/);
+        }
+      }));
+
+      it('should add facing slash from `revokePath`', inject(function(OAuth) {
+        var config = OAuth.configure(_.defaults({
+          revokePath: 'oauth2/revoke'
+        }, defaults));
+
+        config.revokePath.should.equal('/oauth2/revoke');
+      }));
+    });
+
     describe('isAuthenticated()', function() {
       it('should be true when there is a stored `token` cookie', inject(function(OAuth, OAuthToken) {
         OAuthToken.setToken({ token_type: 'bearer', access_token: 'foo', expires_in: 3600, refresh_token: 'bar' });
@@ -216,26 +292,6 @@ describe('OAuthProvider', function() {
         $httpBackend.verifyNoOutstandingExpectation();
         $httpBackend.verifyNoOutstandingRequest();
       }));
-
-      it('should allow to override oauth server config', inject(function($httpBackend, OAuth, OAuthToken) {
-        var config = {
-          baseUrl: 'https://new.website.com',
-          grantPath: '/oauth2/token',
-        };
-
-        $httpBackend.expectPOST(config.baseUrl + config.grantPath, data)
-          .respond({ token_type: 'bearer', access_token: 'foo', expires_in: 3600, refresh_token: 'bar' });
-
-        OAuth.getAccessToken({
-          username: 'foo',
-          password: 'bar'
-        }, {}, config);
-
-        $httpBackend.flush();
-
-        $httpBackend.verifyNoOutstandingExpectation();
-        $httpBackend.verifyNoOutstandingRequest();
-      }));
     });
 
     describe('refreshToken()', function() {
@@ -320,25 +376,6 @@ describe('OAuthProvider', function() {
         $httpBackend.verifyNoOutstandingExpectation();
         $httpBackend.verifyNoOutstandingRequest();
       }));
-
-      it('should allow to override oauth server config', inject(function($httpBackend, OAuth, OAuthToken) {
-        var config = {
-          baseUrl: 'https://new.website.com',
-          grantPath: '/oauth2/token/new',
-        };
-
-        OAuthToken.setToken({ token_type: 'bearer', access_token: 'foo', expires_in: 3600, refresh_token: 'bar' });
-
-        $httpBackend.expectPOST(config.baseUrl + config.grantPath, queryString.stringify(data))
-          .respond({ token_type: 'bearer', access_token: 'qux', expires_in: 3600, refresh_token: 'biz' });
-
-        OAuth.getRefreshToken(data, {}, config);
-
-        $httpBackend.flush();
-
-        $httpBackend.verifyNoOutstandingExpectation();
-        $httpBackend.verifyNoOutstandingRequest();
-      }));
     });
 
     describe('revokeToken()', function () {
@@ -425,32 +462,6 @@ describe('OAuthProvider', function() {
         $httpBackend.verifyNoOutstandingExpectation();
         $httpBackend.verifyNoOutstandingRequest();
       }));
-
-      it('should allow to override oauth server config', inject(function($httpBackend, OAuth, OAuthToken) {
-        var data = queryString.stringify({
-          client_id: defaults.clientId,
-          token: 'bar',
-          token_type_hint: 'refresh_token',
-          client_secret: defaults.clientSecret
-        });
-
-        var config = {
-          baseUrl: 'https://new.website.com',
-          revokePath: '/oauth2/revoke/new',
-        };
-
-        OAuthToken.setToken({ token_type: 'bearer', access_token: 'foo', expires_in: 3600, refresh_token: 'bar' });
-
-        $httpBackend.expectPOST(config.baseUrl + config.revokePath, data)
-          .respond(200);
-
-        OAuth.revokeToken(data, {}, config);
-
-        $httpBackend.flush();
-
-        $httpBackend.verifyNoOutstandingExpectation();
-        $httpBackend.verifyNoOutstandingRequest();
-      }));
     });
   });
 });

From b2a878facfd7cb1728fd544f8386c94bfa82cb26 Mon Sep 17 00:00:00 2001
From: Timokasse <Timokasse@users.noreply.github.com>
Date: Tue, 9 Aug 2016 12:42:27 +0200
Subject: [PATCH 26/42] Specify the type of grant used (#96)

Detail the authentication mechanism used
---
 README.md | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/README.md b/README.md
index 9ff56ca..edade1e 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,18 @@
 
 AngularJS OAuth2 authentication module written in ES6.
 
+Currently `angular-oauth2` only uses the [Resouce Owner Password Credential Grant](https://tools.ietf.org/html/rfc6749#section-4.3), i.e, using a credentials combination (username, password), we'll request an access token (using `grant_type='password'`) which, in case of success, will typically return a response such as:
+
+```
+{
+  "access_token": "foobar",
+  "token_type": "Bearer",
+  "expires_in": 3600,
+  "refresh_token": "foobiz"
+}
+```
+Internally we'll automatically store it as a cookie and it will be used in every request adding an `Authorization` header: `Authorization: 'Bearer foobar'`.
+
 ---
 
 ## Installation

From 7cb1dbb2d5d8ed24edcbe3f7cf6881680b5eac95 Mon Sep 17 00:00:00 2001
From: LionelB <lionel@lumographe.fr>
Date: Wed, 17 Aug 2016 15:33:31 +0200
Subject: [PATCH 27/42] fix closure memory leak in OAuth-provider

---
 src/providers/oauth-provider.js            | 41 +++++++++-----------
 test/unit/providers/oauth-provider.spec.js | 44 +++++++++++++---------
 2 files changed, 44 insertions(+), 41 deletions(-)

diff --git a/src/providers/oauth-provider.js b/src/providers/oauth-provider.js
index dff8d0b..2de0045 100644
--- a/src/providers/oauth-provider.js
+++ b/src/providers/oauth-provider.js
@@ -26,7 +26,6 @@ var requiredKeys = [
  */
 
 function OAuthProvider() {
-  var defaultConfig;
 
   /**
    * @private
@@ -72,9 +71,8 @@ function OAuthProvider() {
    *
    * @param {object} params - An `object` of params to extend.
    */
-  this.configure = function(params) {
-    defaultConfig = sanitizeConfigParams(params);
-    return defaultConfig;
+  this.configure = (params) => {
+    this.defaultConfig = sanitizeConfigParams(params);
   };
 
   /**
@@ -88,10 +86,8 @@ function OAuthProvider() {
        * Check if `OAuthProvider` is configured.
        */
 
-      constructor() {
-        if (!defaultConfig) {
-          throw new Error('`OAuthProvider` must be configured first.');
-        }
+      constructor(config) {
+        this.config = config;
       }
       
       /**
@@ -100,8 +96,7 @@ function OAuthProvider() {
        * @param {Object} params - An object of params to extend
        */
       configure(params) {
-        defaultConfig = sanitizeConfigParams(params);
-        return defaultConfig;
+        this.config = sanitizeConfigParams(params);
       }
 
 
@@ -127,12 +122,12 @@ function OAuthProvider() {
 
       getAccessToken(data, options) {
         data = angular.extend({
-          client_id: defaultConfig.clientId,
+          client_id: this.config.clientId,
           grant_type: 'password'
         }, data);
 
-        if (null !== defaultConfig.clientSecret) {
-          data.client_secret = defaultConfig.clientSecret;
+        if (null !== this.config.clientSecret) {
+          data.client_secret = this.config.clientSecret;
         }
 
         data = queryString.stringify(data);
@@ -144,7 +139,7 @@ function OAuthProvider() {
           }
         }, options);
 
-        return $http.post(`${defaultConfig.baseUrl}${defaultConfig.grantPath}`, data, options).then((response) => {
+        return $http.post(`${this.config.baseUrl}${this.config.grantPath}`, data, options).then((response) => {
           OAuthToken.setToken(response.data);
 
           return response;
@@ -162,13 +157,13 @@ function OAuthProvider() {
 
       getRefreshToken(data, options) {
         data = angular.extend({
-          client_id: defaultConfig.clientId,
+          client_id: this.config.clientId,
           grant_type: 'refresh_token',
           refresh_token: OAuthToken.getRefreshToken(),
         }, data);
 
-        if (null !== defaultConfig.clientSecret) {
-          data.client_secret = defaultConfig.clientSecret;
+        if (null !== this.config.clientSecret) {
+          data.client_secret = this.config.clientSecret;
         }
 
         data = queryString.stringify(data);
@@ -180,7 +175,7 @@ function OAuthProvider() {
           }
         }, options);
 
-        return $http.post(`${defaultConfig.baseUrl}${defaultConfig.grantPath}`, data, options).then((response) => {
+        return $http.post(`${this.config.baseUrl}${this.config.grantPath}`, data, options).then((response) => {
           OAuthToken.setToken(response.data);
 
           return response;
@@ -200,13 +195,13 @@ function OAuthProvider() {
         var refreshToken = OAuthToken.getRefreshToken();
 
         data = angular.extend({
-          client_id: defaultConfig.clientId,
+          client_id: this.config.clientId,
           token: refreshToken ? refreshToken : OAuthToken.getAccessToken(),
           token_type_hint: refreshToken ? 'refresh_token' : 'access_token'
         }, data);
 
-        if (null !== defaultConfig.clientSecret) {
-          data.client_secret = defaultConfig.clientSecret;
+        if (null !== this.config.clientSecret) {
+          data.client_secret = this.config.clientSecret;
         }
 
         data = queryString.stringify(data);
@@ -217,7 +212,7 @@ function OAuthProvider() {
           }
         }, options);
 
-        return $http.post(`${defaultConfig.baseUrl}${defaultConfig.revokePath}`, data, options).then((response) => {
+        return $http.post(`${this.config.baseUrl}${this.config.revokePath}`, data, options).then((response) => {
           OAuthToken.removeToken();
 
           return response;
@@ -225,7 +220,7 @@ function OAuthProvider() {
       }
     }
 
-    return new OAuth();
+    return new OAuth(this.defaultConfig);
   };
 
   this.$get.$inject = ['$http', 'OAuthToken'];
diff --git a/test/unit/providers/oauth-provider.spec.js b/test/unit/providers/oauth-provider.spec.js
index 26f2eb8..4e4e076 100644
--- a/test/unit/providers/oauth-provider.spec.js
+++ b/test/unit/providers/oauth-provider.spec.js
@@ -60,9 +60,10 @@ describe('OAuthProvider', function() {
     });
 
     it('should not throw an error if `clientSecret` param is empty', function() {
-      var config = provider.configure(_.omit(defaults, 'clientSecret'));
-
-      (null === config.clientSecret).should.true;
+      try {
+        provider.configure(_.omit(defaults, 'clientSecret'));
+        should.not.fail();
+      } catch(e) {}
     });
 
     it('should throw an error if `grantPath` param is empty', function() {
@@ -77,19 +78,19 @@ describe('OAuthProvider', function() {
     });
 
     it('should remove trailing slash from `baseUrl`', function() {
-      var config = provider.configure(_.defaults({
+      provider.configure(_.defaults({
         baseUrl: 'https://api.website.com/'
       }, defaults));
 
-      config.baseUrl.should.equal('https://api.website.com');
+      provider.defaultConfig.baseUrl.should.equal('https://api.website.com');
     });
 
     it('should add facing slash from `grantPath`', function() {
-      var config = provider.configure(_.defaults({
+      provider.configure(_.defaults({
         grantPath: 'oauth2/token'
       }, defaults));
 
-      config.grantPath.should.equal('/oauth2/token');
+      provider.defaultConfig.grantPath.should.equal('/oauth2/token');
     });
 
     it('should throw an error if `revokePath` param is empty', function() {
@@ -104,11 +105,11 @@ describe('OAuthProvider', function() {
     });
 
     it('should add facing slash from `revokePath`', function() {
-      var config = provider.configure(_.defaults({
+      provider.configure(_.defaults({
         revokePath: 'oauth2/revoke'
       }, defaults));
 
-      config.revokePath.should.equal('/oauth2/revoke');
+      provider.defaultConfig.revokePath.should.equal('/oauth2/revoke');
     });
   });
 
@@ -125,6 +126,11 @@ describe('OAuthProvider', function() {
     afterEach(inject(function(OAuthToken) {
       OAuthToken.removeToken();
     }));
+    describe('construtor', function() {
+      it('should set initialize config with data passed in configure', inject(function(OAuth) {
+        OAuth.config.should.eql(defaults);
+      }))
+    })
 
     describe('configure()', function() {
       it('should throw an error if configuration is not an object', inject(function(OAuth) {
@@ -161,9 +167,11 @@ describe('OAuthProvider', function() {
       }));
 
       it('should not throw an error if `clientSecret` param is empty', inject(function(OAuth) {
-        var config = OAuth.configure(_.omit(defaults, 'clientSecret'));
-
-        (null === config.clientSecret).should.true;
+        try{
+          OAuth.configure(_.omit(defaults, 'clientSecret'));
+          
+          should.not.fail();
+        } catch(e) {}
       }));
 
       it('should throw an error if `grantPath` param is empty', inject(function(OAuth) {
@@ -178,19 +186,19 @@ describe('OAuthProvider', function() {
       }));
 
       it('should remove trailing slash from `baseUrl`', inject(function(OAuth) {
-        var config = OAuth.configure(_.defaults({
+        OAuth.configure(_.defaults({
           baseUrl: 'https://api.website.com/'
         }, defaults));
 
-        config.baseUrl.should.equal('https://api.website.com');
+        OAuth.config.baseUrl.should.equal('https://api.website.com');
       }));
 
       it('should add facing slash from `grantPath`', inject(function(OAuth) {
-        var config = OAuth.configure(_.defaults({
+        OAuth.configure(_.defaults({
           grantPath: 'oauth2/token'
         }, defaults));
 
-        config.grantPath.should.equal('/oauth2/token');
+        OAuth.config.grantPath.should.equal('/oauth2/token');
       }));
 
       it('should throw an error if `revokePath` param is empty', inject(function(OAuth) {
@@ -205,11 +213,11 @@ describe('OAuthProvider', function() {
       }));
 
       it('should add facing slash from `revokePath`', inject(function(OAuth) {
-        var config = OAuth.configure(_.defaults({
+        OAuth.configure(_.defaults({
           revokePath: 'oauth2/revoke'
         }, defaults));
 
-        config.revokePath.should.equal('/oauth2/revoke');
+        OAuth.config.revokePath.should.equal('/oauth2/revoke');
       }));
     });
 

From bda928374062f11471b3d4d824490f6cfc9738bc Mon Sep 17 00:00:00 2001
From: LionelB <lionel@lumographe.fr>
Date: Wed, 17 Aug 2016 15:34:06 +0200
Subject: [PATCH 28/42] update README with runtime configuration docs

---
 README.md | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 5f92ef9..2d372ed 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ If you're using `bower` they will be automatically downloaded upon installing th
 <script src="<VENDOR_FOLDER>/angular-oauth2/dist/angular-oauth2.min.js"></script>
 ```
 
-###### 3. Configure `OAuth` (required) and `OAuthToken` (optional):
+###### 3. Configure `OAuth` (optional) and `OAuthToken` (optional):
 
 ```js
 angular.module('myApp', ['angular-oauth2'])
@@ -44,7 +44,18 @@ angular.module('myApp', ['angular-oauth2'])
   }]);
 ```
 
-ps: You can also configure service at runtime, in case you retrieve oauth server configuration from a ajax request.
+You can also configure `OAuth` service in a `.run()`block, in case you retrieve the Oauth server configuration from a ajax request.
+
+```js
+angular.module('myApp', ['angular-oauth2'])
+  .run(['OAuth', function(OAuth) {
+    OAuth.configure({
+      baseUrl: 'https://api.website.com',
+      clientId: 'CLIENT_ID',
+      clientSecret: 'CLIENT_SECRET' // optional
+    });
+  }]);
+```
 
 ###### 4. Catch `OAuth` errors and do something with them (optional):
 

From 2a71da7bf039509b7690be9e3a339c38884a6617 Mon Sep 17 00:00:00 2001
From: LionelB <lionel@lumographe.fr>
Date: Wed, 2 Nov 2016 21:40:37 +0100
Subject: [PATCH 29/42] remove unused testSuite var

---
 test/unit/providers/oauth-provider.spec.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/unit/providers/oauth-provider.spec.js b/test/unit/providers/oauth-provider.spec.js
index 4e4e076..dbb447c 100644
--- a/test/unit/providers/oauth-provider.spec.js
+++ b/test/unit/providers/oauth-provider.spec.js
@@ -13,7 +13,7 @@ describe('OAuthProvider', function() {
   };
 
   describe('configure()', function() {
-    var provider, testSuite;
+    var provider;
 
     beforeEach(function() {
       angular.module('angular-oauth2.test', [])

From 4109fb8dfe21284815e811ec0b593d9b2918207c Mon Sep 17 00:00:00 2001
From: Rui Silva <rui.penso@gmail.com>
Date: Thu, 3 Nov 2016 11:18:45 +0000
Subject: [PATCH 30/42] Release 4.1.0

---
 CHANGELOG.md               | 55 ++++++++++++++++++-----------------
 dist/angular-oauth2.js     | 59 ++++++++++++++++++++------------------
 dist/angular-oauth2.min.js |  2 +-
 package.json               |  2 +-
 4 files changed, 62 insertions(+), 56 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ab24432..2d6fa99 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,48 +1,51 @@
 ## Changelog
 
-### upcoming / 2016-02-12
-- [#80](https://github.com/seegno/angular-oauth2/pull/80) Reintroduce authorization header to be overridden (@ruipenso)
+### 4.1.0 / 2016-11-03
+- [#87](https://github.com/oauthjs/angular-oauth2/pull/87) allow overriding oauth base config using options (@lionelB)
+
+### 4.0.0 / 2016-02-12
+- [#80](https://github.com/oauthjs/angular-oauth2/pull/80) Reintroduce authorization header to be overridden (@ruipenso)
 
 ### 3.1.1 / 2016-02-12
-- [#79](https://github.com/seegno/angular-oauth2/pull/79) Update dependencies (@ruipenso)
+- [#79](https://github.com/oauthjs/angular-oauth2/pull/79) Update dependencies (@ruipenso)
 
 ### 3.1.0 / 2016-02-10
-- [#78](https://github.com/seegno/angular-oauth2/pull/78) Update OAuth methods to allow `data` and `options` override (@ruipenso)
-- [#71](https://github.com/seegno/angular-oauth2/pull/71) Add `client_id` and `client_secret` on revoke_token (@tinogomes)
-- [#77](https://github.com/seegno/angular-oauth2/pull/77) Update interceptor to allow authorization header to be overridden (@ruipenso)
-- [#75](https://github.com/seegno/angular-oauth2/pull/75) Update README dependencies (@ruipenso)
-- [#76](https://github.com/seegno/angular-oauth2/pull/76) Update `package.json` (@ruipenso)
+- [#78](https://github.com/oauthjs/angular-oauth2/pull/78) Update OAuth methods to allow `data` and `options` override (@ruipenso)
+- [#71](https://github.com/oauthjs/angular-oauth2/pull/71) Add `client_id` and `client_secret` on revoke_token (@tinogomes)
+- [#77](https://github.com/oauthjs/angular-oauth2/pull/77) Update interceptor to allow authorization header to be overridden (@ruipenso)
+- [#75](https://github.com/oauthjs/angular-oauth2/pull/75) Update README dependencies (@ruipenso)
+- [#76](https://github.com/oauthjs/angular-oauth2/pull/76) Update `package.json` (@ruipenso)
 
 ### 3.0.1 / 2015-06-01
-- [#27](https://github.com/seegno/angular-oauth2/pull/27) Add travis-ci configuration (@joaogranado)
+- [#27](https://github.com/oauthjs/angular-oauth2/pull/27) Add travis-ci configuration (@joaogranado)
 
 ### 3.0.0 / 2015-06-01
-- [#49](https://github.com/seegno/angular-oauth2/pull/49) Add changelog task (@ruipenso)
-- [#48](https://github.com/seegno/angular-oauth2/pull/48) Update readme (@ruipenso)
-- [#25](https://github.com/seegno/angular-oauth2/pull/25) Replace ipCookie with ngCookies (@joaogranado)
-- [#28](https://github.com/seegno/angular-oauth2/pull/28) Add methods to get/set `token` property (@joaogranado)
-- [#47](https://github.com/seegno/angular-oauth2/pull/47) Add unauthorized error interception (@ruipenso)
+- [#49](https://github.com/oauthjs/angular-oauth2/pull/49) Add changelog task (@ruipenso)
+- [#48](https://github.com/oauthjs/angular-oauth2/pull/48) Update readme (@ruipenso)
+- [#25](https://github.com/oauthjs/angular-oauth2/pull/25) Replace ipCookie with ngCookies (@joaogranado)
+- [#28](https://github.com/oauthjs/angular-oauth2/pull/28) Add methods to get/set `token` property (@joaogranado)
+- [#47](https://github.com/oauthjs/angular-oauth2/pull/47) Add unauthorized error interception (@ruipenso)
 
 ### 2.1.1 / 2015-05-28
-- [#40](https://github.com/seegno/angular-oauth2/pull/40) Fix missing dependency on readme example (@ruipenso)
+- [#40](https://github.com/oauthjs/angular-oauth2/pull/40) Fix missing dependency on readme example (@ruipenso)
 
 ### 2.1.0 / 2015-03-09
-- [#15](https://github.com/seegno/angular-oauth2/pull/15) Add `clientSecret` as optional (@ruipenso)
-- [#18](https://github.com/seegno/angular-oauth2/pull/18) Remove npm postinstall script (@ruipenso)
-- [#14](https://github.com/seegno/angular-oauth2/pull/14) Fix readme  configuration (@ruipenso)
+- [#15](https://github.com/oauthjs/angular-oauth2/pull/15) Add `clientSecret` as optional (@ruipenso)
+- [#18](https://github.com/oauthjs/angular-oauth2/pull/18) Remove npm postinstall script (@ruipenso)
+- [#14](https://github.com/oauthjs/angular-oauth2/pull/14) Fix readme  configuration (@ruipenso)
 
 ### 2.0.0 / 2015-02-04
-- [#11](https://github.com/seegno/angular-oauth2/pull/11) Add options to `ipCookie.remove()` on OAuthToken (@ruipenso)
-- [#10](https://github.com/seegno/angular-oauth2/pull/10) Update `oauthInterceptor` responseError handling (@ruipenso)
-- [#7](https://github.com/seegno/angular-oauth2/pull/7) Fix readme typo of download url (@brunnolou)
-- [#6](https://github.com/seegno/angular-oauth2/pull/6) README.md: typo fix (@AdirAmsalem)
+- [#11](https://github.com/oauthjs/angular-oauth2/pull/11) Add options to `ipCookie.remove()` on OAuthToken (@ruipenso)
+- [#10](https://github.com/oauthjs/angular-oauth2/pull/10) Update `oauthInterceptor` responseError handling (@ruipenso)
+- [#7](https://github.com/oauthjs/angular-oauth2/pull/7) Fix readme typo of download url (@brunnolou)
+- [#6](https://github.com/oauthjs/angular-oauth2/pull/6) README.md: typo fix (@AdirAmsalem)
 
 ### 1.0.2 / 2015-01-19
-- [#4](https://github.com/seegno/angular-oauth2/pull/4) Add support for higher dependencies versions (@joaogranado)
+- [#4](https://github.com/oauthjs/angular-oauth2/pull/4) Add support for higher dependencies versions (@joaogranado)
 
 ### 1.0.1 / 2015-01-19
-- [#3](https://github.com/seegno/angular-oauth2/pull/3) Fix bower ignore list (@ruipenso)
+- [#3](https://github.com/oauthjs/angular-oauth2/pull/3) Fix bower ignore list (@ruipenso)
 
 ### 1.0.0 / 2015-01-18
-- [#2](https://github.com/seegno/angular-oauth2/pull/2) Fix indentation in README examples (@fixe)
-- [#1](https://github.com/seegno/angular-oauth2/pull/1) Add project structure (@ruipenso)
\ No newline at end of file
+- [#2](https://github.com/oauthjs/angular-oauth2/pull/2) Fix indentation in README examples (@fixe)
+- [#1](https://github.com/oauthjs/angular-oauth2/pull/1) Add project structure (@ruipenso)
diff --git a/dist/angular-oauth2.js b/dist/angular-oauth2.js
index 14bb50e..91122c9 100644
--- a/dist/angular-oauth2.js
+++ b/dist/angular-oauth2.js
@@ -1,6 +1,6 @@
 /**
  * angular-oauth2 - Angular OAuth2
- * @version v4.0.0
+ * @version v4.1.0
  * @link https://github.com/seegno/angular-oauth2
  * @license MIT
  */
@@ -14,10 +14,6 @@
     }
 })(this, function(angular, ngCookies, queryString) {
     var ngModule = angular.module("angular-oauth2", [ ngCookies ]).config(oauthConfig).factory("oauthInterceptor", oauthInterceptor).provider("OAuth", OAuthProvider).provider("OAuthToken", OAuthTokenProvider);
-    function oauthConfig($httpProvider) {
-        $httpProvider.interceptors.push("oauthInterceptor");
-    }
-    oauthConfig.$inject = [ "$httpProvider" ];
     function oauthInterceptor($q, $rootScope, OAuthToken) {
         return {
             request: function request(config) {
@@ -40,6 +36,10 @@
         };
     }
     oauthInterceptor.$inject = [ "$q", "$rootScope", "OAuthToken" ];
+    function oauthConfig($httpProvider) {
+        $httpProvider.interceptors.push("oauthInterceptor");
+    }
+    oauthConfig.$inject = [ "$httpProvider" ];
     var _createClass = function() {
         function defineProperties(target, props) {
             for (var i = 0; i < props.length; i++) {
@@ -70,15 +70,12 @@
     };
     var requiredKeys = [ "baseUrl", "clientId", "grantPath", "revokePath" ];
     function OAuthProvider() {
-        var config;
-        this.configure = function(params) {
-            if (config) {
-                throw new Error("Already configured.");
-            }
+        var _this = this;
+        var sanitizeConfigParams = function sanitizeConfigParams(params) {
             if (!(params instanceof Object)) {
                 throw new TypeError("Invalid argument: `config` must be an `Object`.");
             }
-            config = angular.extend({}, defaults, params);
+            var config = angular.extend({}, defaults, params);
             angular.forEach(requiredKeys, function(key) {
                 if (!config[key]) {
                     throw new Error("Missing parameter: " + key + ".");
@@ -95,15 +92,21 @@
             }
             return config;
         };
+        this.configure = function(params) {
+            _this.defaultConfig = sanitizeConfigParams(params);
+        };
         this.$get = function($http, OAuthToken) {
             var OAuth = function() {
-                function OAuth() {
+                function OAuth(config) {
                     _classCallCheck(this, OAuth);
-                    if (!config) {
-                        throw new Error("`OAuthProvider` must be configured first.");
-                    }
+                    this.config = config;
                 }
                 _createClass(OAuth, [ {
+                    key: "configure",
+                    value: function configure(params) {
+                        this.config = sanitizeConfigParams(params);
+                    }
+                }, {
                     key: "isAuthenticated",
                     value: function isAuthenticated() {
                         return !!OAuthToken.getToken();
@@ -112,11 +115,11 @@
                     key: "getAccessToken",
                     value: function getAccessToken(data, options) {
                         data = angular.extend({
-                            client_id: config.clientId,
+                            client_id: this.config.clientId,
                             grant_type: "password"
                         }, data);
-                        if (null !== config.clientSecret) {
-                            data.client_secret = config.clientSecret;
+                        if (null !== this.config.clientSecret) {
+                            data.client_secret = this.config.clientSecret;
                         }
                         data = queryString.stringify(data);
                         options = angular.extend({
@@ -125,7 +128,7 @@
                                 "Content-Type": "application/x-www-form-urlencoded"
                             }
                         }, options);
-                        return $http.post("" + config.baseUrl + config.grantPath, data, options).then(function(response) {
+                        return $http.post("" + this.config.baseUrl + this.config.grantPath, data, options).then(function(response) {
                             OAuthToken.setToken(response.data);
                             return response;
                         });
@@ -134,12 +137,12 @@
                     key: "getRefreshToken",
                     value: function getRefreshToken(data, options) {
                         data = angular.extend({
-                            client_id: config.clientId,
+                            client_id: this.config.clientId,
                             grant_type: "refresh_token",
                             refresh_token: OAuthToken.getRefreshToken()
                         }, data);
-                        if (null !== config.clientSecret) {
-                            data.client_secret = config.clientSecret;
+                        if (null !== this.config.clientSecret) {
+                            data.client_secret = this.config.clientSecret;
                         }
                         data = queryString.stringify(data);
                         options = angular.extend({
@@ -148,7 +151,7 @@
                                 "Content-Type": "application/x-www-form-urlencoded"
                             }
                         }, options);
-                        return $http.post("" + config.baseUrl + config.grantPath, data, options).then(function(response) {
+                        return $http.post("" + this.config.baseUrl + this.config.grantPath, data, options).then(function(response) {
                             OAuthToken.setToken(response.data);
                             return response;
                         });
@@ -158,12 +161,12 @@
                     value: function revokeToken(data, options) {
                         var refreshToken = OAuthToken.getRefreshToken();
                         data = angular.extend({
-                            client_id: config.clientId,
+                            client_id: this.config.clientId,
                             token: refreshToken ? refreshToken : OAuthToken.getAccessToken(),
                             token_type_hint: refreshToken ? "refresh_token" : "access_token"
                         }, data);
-                        if (null !== config.clientSecret) {
-                            data.client_secret = config.clientSecret;
+                        if (null !== this.config.clientSecret) {
+                            data.client_secret = this.config.clientSecret;
                         }
                         data = queryString.stringify(data);
                         options = angular.extend({
@@ -171,7 +174,7 @@
                                 "Content-Type": "application/x-www-form-urlencoded"
                             }
                         }, options);
-                        return $http.post("" + config.baseUrl + config.revokePath, data, options).then(function(response) {
+                        return $http.post("" + this.config.baseUrl + this.config.revokePath, data, options).then(function(response) {
                             OAuthToken.removeToken();
                             return response;
                         });
@@ -179,7 +182,7 @@
                 } ]);
                 return OAuth;
             }();
-            return new OAuth();
+            return new OAuth(this.defaultConfig);
         };
         this.$get.$inject = [ "$http", "OAuthToken" ];
     }
diff --git a/dist/angular-oauth2.min.js b/dist/angular-oauth2.min.js
index becd693..98e09a3 100644
--- a/dist/angular-oauth2.min.js
+++ b/dist/angular-oauth2.min.js
@@ -1 +1 @@
-!function(e,t){"function"==typeof define&&define.amd?define(["angular","angular-cookies","query-string"],t):"object"==typeof exports?module.exports=t(require("angular"),require("angular-cookies"),require("query-string")):e.angularOAuth2=t(e.angular,"ngCookies",e.queryString)}(this,function(e,t,n){function r(e){e.interceptors.push("oauthInterceptor")}function o(e,t,n){return{request:function(e){return e.headers=e.headers||{},!e.headers.hasOwnProperty("Authorization")&&n.getAuthorizationHeader()&&(e.headers.Authorization=n.getAuthorizationHeader()),e},responseError:function(r){return 400!==r.status||!r.data||"invalid_request"!==r.data.error&&"invalid_grant"!==r.data.error||(n.removeToken(),t.$emit("oauth:error",r)),(401===r.status&&r.data&&"invalid_token"===r.data.error||r.headers("www-authenticate")&&0===r.headers("www-authenticate").indexOf("Bearer"))&&t.$emit("oauth:error",r),e.reject(r)}}}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(){var t;this.configure=function(n){if(t)throw new Error("Already configured.");if(!(n instanceof Object))throw new TypeError("Invalid argument: `config` must be an `Object`.");return t=e.extend({},h,n),e.forEach(f,function(e){if(!t[e])throw new Error("Missing parameter: "+e+".")}),"/"===t.baseUrl.substr(-1)&&(t.baseUrl=t.baseUrl.slice(0,-1)),"/"!==t.grantPath[0]&&(t.grantPath="/"+t.grantPath),"/"!==t.revokePath[0]&&(t.revokePath="/"+t.revokePath),t},this.$get=function(r,o){var a=function(){function a(){if(i(this,a),!t)throw new Error("`OAuthProvider` must be configured first.")}return s(a,[{key:"isAuthenticated",value:function(){return!!o.getToken()}},{key:"getAccessToken",value:function(i,a){return i=e.extend({client_id:t.clientId,grant_type:"password"},i),null!==t.clientSecret&&(i.client_secret=t.clientSecret),i=n.stringify(i),a=e.extend({headers:{Authorization:void 0,"Content-Type":"application/x-www-form-urlencoded"}},a),r.post(""+t.baseUrl+t.grantPath,i,a).then(function(e){return o.setToken(e.data),e})}},{key:"getRefreshToken",value:function(i,a){return i=e.extend({client_id:t.clientId,grant_type:"refresh_token",refresh_token:o.getRefreshToken()},i),null!==t.clientSecret&&(i.client_secret=t.clientSecret),i=n.stringify(i),a=e.extend({headers:{Authorization:void 0,"Content-Type":"application/x-www-form-urlencoded"}},a),r.post(""+t.baseUrl+t.grantPath,i,a).then(function(e){return o.setToken(e.data),e})}},{key:"revokeToken",value:function(i,a){var u=o.getRefreshToken();return i=e.extend({client_id:t.clientId,token:u?u:o.getAccessToken(),token_type_hint:u?"refresh_token":"access_token"},i),null!==t.clientSecret&&(i.client_secret=t.clientSecret),i=n.stringify(i),a=e.extend({headers:{"Content-Type":"application/x-www-form-urlencoded"}},a),r.post(""+t.baseUrl+t.revokePath,i,a).then(function(e){return o.removeToken(),e})}}]),a}();return new a},this.$get.$inject=["$http","OAuthToken"]}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function u(){var t={name:"token",options:{secure:!0}};this.configure=function(n){if(!(n instanceof Object))throw new TypeError("Invalid argument: `config` must be an `Object`.");return e.extend(t,n),t},this.$get=function(e){var n=function(){function n(){i(this,n)}return s(n,[{key:"setToken",value:function(n){return e.putObject(t.name,n,t.options)}},{key:"getToken",value:function(){return e.getObject(t.name)}},{key:"getAccessToken",value:function(){return this.getToken()?this.getToken().access_token:void 0}},{key:"getAuthorizationHeader",value:function(){return this.getTokenType()&&this.getAccessToken()?this.getTokenType().charAt(0).toUpperCase()+this.getTokenType().substr(1)+" "+this.getAccessToken():void 0}},{key:"getRefreshToken",value:function(){return this.getToken()?this.getToken().refresh_token:void 0}},{key:"getTokenType",value:function(){return this.getToken()?this.getToken().token_type:void 0}},{key:"removeToken",value:function(){return e.remove(t.name,t.options)}}]),n}();return new n},this.$get.$inject=["$cookies"]}var c=e.module("angular-oauth2",[t]).config(r).factory("oauthInterceptor",o).provider("OAuth",a).provider("OAuthToken",u);r.$inject=["$httpProvider"],o.$inject=["$q","$rootScope","OAuthToken"];var s=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),h={baseUrl:null,clientId:null,clientSecret:null,grantPath:"/oauth2/token",revokePath:"/oauth2/revoke"},f=["baseUrl","clientId","grantPath","revokePath"],s=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}();return c});
\ No newline at end of file
+!function(e,t){"function"==typeof define&&define.amd?define(["angular","angular-cookies","query-string"],t):"object"==typeof exports?module.exports=t(require("angular"),require("angular-cookies"),require("query-string")):e.angularOAuth2=t(e.angular,"ngCookies",e.queryString)}(this,function(e,t,n){function r(e,t,n){return{request:function(e){return e.headers=e.headers||{},!e.headers.hasOwnProperty("Authorization")&&n.getAuthorizationHeader()&&(e.headers.Authorization=n.getAuthorizationHeader()),e},responseError:function(r){return 400!==r.status||!r.data||"invalid_request"!==r.data.error&&"invalid_grant"!==r.data.error||(n.removeToken(),t.$emit("oauth:error",r)),(401===r.status&&r.data&&"invalid_token"===r.data.error||r.headers("www-authenticate")&&0===r.headers("www-authenticate").indexOf("Bearer"))&&t.$emit("oauth:error",r),e.reject(r)}}}function o(e){e.interceptors.push("oauthInterceptor")}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(){var t=this,r=function(t){if(!(t instanceof Object))throw new TypeError("Invalid argument: `config` must be an `Object`.");var n=e.extend({},f,t);return e.forEach(h,function(e){if(!n[e])throw new Error("Missing parameter: "+e+".")}),"/"===n.baseUrl.substr(-1)&&(n.baseUrl=n.baseUrl.slice(0,-1)),"/"!==n.grantPath[0]&&(n.grantPath="/"+n.grantPath),"/"!==n.revokePath[0]&&(n.revokePath="/"+n.revokePath),n};this.configure=function(e){t.defaultConfig=r(e)},this.$get=function(t,o){var a=function(){function a(e){i(this,a),this.config=e}return s(a,[{key:"configure",value:function(e){this.config=r(e)}},{key:"isAuthenticated",value:function(){return!!o.getToken()}},{key:"getAccessToken",value:function(r,i){return r=e.extend({client_id:this.config.clientId,grant_type:"password"},r),null!==this.config.clientSecret&&(r.client_secret=this.config.clientSecret),r=n.stringify(r),i=e.extend({headers:{Authorization:void 0,"Content-Type":"application/x-www-form-urlencoded"}},i),t.post(""+this.config.baseUrl+this.config.grantPath,r,i).then(function(e){return o.setToken(e.data),e})}},{key:"getRefreshToken",value:function(r,i){return r=e.extend({client_id:this.config.clientId,grant_type:"refresh_token",refresh_token:o.getRefreshToken()},r),null!==this.config.clientSecret&&(r.client_secret=this.config.clientSecret),r=n.stringify(r),i=e.extend({headers:{Authorization:void 0,"Content-Type":"application/x-www-form-urlencoded"}},i),t.post(""+this.config.baseUrl+this.config.grantPath,r,i).then(function(e){return o.setToken(e.data),e})}},{key:"revokeToken",value:function(r,i){var a=o.getRefreshToken();return r=e.extend({client_id:this.config.clientId,token:a?a:o.getAccessToken(),token_type_hint:a?"refresh_token":"access_token"},r),null!==this.config.clientSecret&&(r.client_secret=this.config.clientSecret),r=n.stringify(r),i=e.extend({headers:{"Content-Type":"application/x-www-form-urlencoded"}},i),t.post(""+this.config.baseUrl+this.config.revokePath,r,i).then(function(e){return o.removeToken(),e})}}]),a}();return new a(this.defaultConfig)},this.$get.$inject=["$http","OAuthToken"]}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function u(){var t={name:"token",options:{secure:!0}};this.configure=function(n){if(!(n instanceof Object))throw new TypeError("Invalid argument: `config` must be an `Object`.");return e.extend(t,n),t},this.$get=function(e){var n=function(){function n(){i(this,n)}return s(n,[{key:"setToken",value:function(n){return e.putObject(t.name,n,t.options)}},{key:"getToken",value:function(){return e.getObject(t.name)}},{key:"getAccessToken",value:function(){return this.getToken()?this.getToken().access_token:void 0}},{key:"getAuthorizationHeader",value:function(){if(this.getTokenType()&&this.getAccessToken())return this.getTokenType().charAt(0).toUpperCase()+this.getTokenType().substr(1)+" "+this.getAccessToken()}},{key:"getRefreshToken",value:function(){return this.getToken()?this.getToken().refresh_token:void 0}},{key:"getTokenType",value:function(){return this.getToken()?this.getToken().token_type:void 0}},{key:"removeToken",value:function(){return e.remove(t.name,t.options)}}]),n}();return new n},this.$get.$inject=["$cookies"]}var c=e.module("angular-oauth2",[t]).config(o).factory("oauthInterceptor",r).provider("OAuth",a).provider("OAuthToken",u);r.$inject=["$q","$rootScope","OAuthToken"],o.$inject=["$httpProvider"];var s=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),f={baseUrl:null,clientId:null,clientSecret:null,grantPath:"/oauth2/token",revokePath:"/oauth2/revoke"},h=["baseUrl","clientId","grantPath","revokePath"],s=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}();return c});
\ No newline at end of file
diff --git a/package.json b/package.json
index 8fe7e25..98481f8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "angular-oauth2",
-  "version": "4.0.0",
+  "version": "4.1.0",
   "description": "Angular OAuth2",
   "main": "./dist/angular-oauth2.js",
   "repository": {

From 3ee5abbda423f15fc077d6a01d6a129b0f03ab07 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ant=C3=A9rio=20Vieira?= <anteriovieira@gmail.com>
Date: Tue, 8 Nov 2016 14:56:09 -0200
Subject: [PATCH 31/42] Updated readme

---
 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index ef99b09..758e055 100644
--- a/README.md
+++ b/README.md
@@ -56,7 +56,7 @@ angular.module('myApp', ['angular-oauth2'])
   }]);
 ```
 
-You can also configure `OAuth` service in a `.run()`block, in case you retrieve the Oauth server configuration from a ajax request.
+You can also configure `OAuth` service in a `.run()` block, in case you retrieve the Oauth server configuration from a ajax request.
 
 ```js
 angular.module('myApp', ['angular-oauth2'])
@@ -109,7 +109,7 @@ OAuthProvider.configure({
 
 #### OAuth
 
-Update configuration defaults
+Update configuration defaults:
 
 ```js
 OAuth.configure({

From f1bd93c17bd556c61a12fc733edc6569bf2dda00 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Coelho?= <acoelho@uphold.com>
Date: Mon, 3 Apr 2017 09:38:54 +0100
Subject: [PATCH 32/42] =?UTF-8?q?Fix=20`access=5Ftoken`=C2=A0of=C2=A0undef?=
 =?UTF-8?q?ined?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/providers/oauth-token-provider.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/providers/oauth-token-provider.js b/src/providers/oauth-token-provider.js
index 060f1dd..7688dfb 100644
--- a/src/providers/oauth-token-provider.js
+++ b/src/providers/oauth-token-provider.js
@@ -63,7 +63,9 @@ function OAuthTokenProvider() {
        */
 
       getAccessToken() {
-        return this.getToken() ? this.getToken().access_token : undefined;
+        const { access_token } = this.getToken() || {};
+
+        return access_token;
       }
 
       /**

From bb6c55cb334083d182b158b9dc22b733c33d8f81 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Coelho?= <acoelho@uphold.com>
Date: Mon, 3 Apr 2017 09:40:18 +0100
Subject: [PATCH 33/42] =?UTF-8?q?Fix=20`refresh=5Ftoken`=C2=A0of=C2=A0unde?=
 =?UTF-8?q?fined?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/providers/oauth-token-provider.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/providers/oauth-token-provider.js b/src/providers/oauth-token-provider.js
index 7688dfb..771b890 100644
--- a/src/providers/oauth-token-provider.js
+++ b/src/providers/oauth-token-provider.js
@@ -85,7 +85,9 @@ function OAuthTokenProvider() {
        */
 
       getRefreshToken() {
-        return this.getToken() ? this.getToken().refresh_token : undefined;
+        const { refresh_token } = this.getToken() || {};
+
+        return refresh_token;
       }
 
       /**

From a7ea4a4e83a33172394b10739161b8d6cc0bc0c7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Coelho?= <acoelho@uphold.com>
Date: Mon, 3 Apr 2017 09:42:24 +0100
Subject: [PATCH 34/42] =?UTF-8?q?Fix=20`token=5Ftype`=C2=A0of=C2=A0undefin?=
 =?UTF-8?q?ed?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/providers/oauth-token-provider.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/providers/oauth-token-provider.js b/src/providers/oauth-token-provider.js
index 771b890..5e6095f 100644
--- a/src/providers/oauth-token-provider.js
+++ b/src/providers/oauth-token-provider.js
@@ -95,7 +95,9 @@ function OAuthTokenProvider() {
        */
 
       getTokenType() {
-        return this.getToken() ? this.getToken().token_type : undefined;
+        const { token_type } = this.getToken() || {};
+
+        return token_type;
       }
 
       /**

From e35acfe3f85a6a3bfa40fa04cfede2c22c7fe60a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Coelho?= <acoelho@uphold.com>
Date: Mon, 3 Apr 2017 09:51:39 +0100
Subject: [PATCH 35/42] =?UTF-8?q?Fix=20`charAt`=C2=A0and=20`substr`=C2=A0o?=
 =?UTF-8?q?f=C2=A0undefined?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/providers/oauth-token-provider.js | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/providers/oauth-token-provider.js b/src/providers/oauth-token-provider.js
index 5e6095f..13dca0e 100644
--- a/src/providers/oauth-token-provider.js
+++ b/src/providers/oauth-token-provider.js
@@ -73,11 +73,14 @@ function OAuthTokenProvider() {
        */
 
       getAuthorizationHeader() {
-        if (!(this.getTokenType() && this.getAccessToken())) {
+        const tokenType = this.getTokenType();
+        const accessToken = this.getAccessToken();
+
+        if (!tokenType || !accessToken) {
           return;
         }
 
-        return `${this.getTokenType().charAt(0).toUpperCase() + this.getTokenType().substr(1)} ${this.getAccessToken()}`;
+        return `${tokenType.charAt(0).toUpperCase() + tokenType.substr(1)} ${accessToken}`;
       }
 
       /**

From 726252abb0db7ded425e8bda60925ff8b53bb24a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Coelho?= <acoelho@uphold.com>
Date: Mon, 3 Apr 2017 10:19:16 +0100
Subject: [PATCH 36/42] Bump version of angular packages

---
 package.json | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/package.json b/package.json
index 98481f8..4091d7d 100644
--- a/package.json
+++ b/package.json
@@ -22,12 +22,12 @@
   },
   "homepage": "https://github.com/seegno/angular-oauth2",
   "dependencies": {
-    "angular": "^1.4.0",
-    "angular-cookies": "^1.4.0",
+    "angular": "1.5.9",
+    "angular-cookies": "1.5.9",
     "query-string": "^1.0.0"
   },
   "devDependencies": {
-    "angular-mocks": "^1.3.9",
+    "angular-mocks": "1.5.9",
     "github-changes": "^1.0.0",
     "gulp": "^3.8.10",
     "gulp-babel": "^5.3.0",

From fbb34b38d1f7b8f1b9360c0908ffd7027d980a35 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Coelho?= <acoelho@uphold.com>
Date: Mon, 3 Apr 2017 10:33:50 +0100
Subject: [PATCH 37/42] Fix unsupported `headers` and `status` of undefined

---
 src/interceptors/oauth-interceptor.js | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/interceptors/oauth-interceptor.js b/src/interceptors/oauth-interceptor.js
index bbdcadb..b94be80 100644
--- a/src/interceptors/oauth-interceptor.js
+++ b/src/interceptors/oauth-interceptor.js
@@ -16,6 +16,10 @@ function oauthInterceptor($q, $rootScope, OAuthToken) {
       return config;
     },
     responseError: function(rejection) {
+      if (!rejection) {
+        return $q.reject(rejection);
+      }
+
       // Catch `invalid_request` and `invalid_grant` errors and ensure that the `token` is removed.
       if (400 === rejection.status && rejection.data &&
         ('invalid_request' === rejection.data.error || 'invalid_grant' === rejection.data.error)
@@ -29,7 +33,7 @@ function oauthInterceptor($q, $rootScope, OAuthToken) {
       // The token isn't removed here so it can be refreshed when the `invalid_token` error occurs.
       if (401 === rejection.status &&
         (rejection.data && 'invalid_token' === rejection.data.error) ||
-        (rejection.headers('www-authenticate') && 0 === rejection.headers('www-authenticate').indexOf('Bearer'))
+        (rejection.headers && rejection.headers('www-authenticate') && 0 === rejection.headers('www-authenticate').indexOf('Bearer'))
       ) {
         $rootScope.$emit('oauth:error', rejection);
       }

From e90bd56a8362be46a8d3ecc9320bd03176ce1e75 Mon Sep 17 00:00:00 2001
From: Rui Silva <penso@uphold.com>
Date: Mon, 3 Apr 2017 12:51:03 +0100
Subject: [PATCH 38/42] Release 4.1.1

---
 CHANGELOG.md               |  7 ++++++-
 bower.json                 |  6 +++---
 dist/angular-oauth2.js     | 33 ++++++++++++++++++++++-----------
 dist/angular-oauth2.min.js |  2 +-
 package.json               |  4 ++--
 5 files changed, 34 insertions(+), 18 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2d6fa99..002ccf8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,12 @@
 ## Changelog
 
+### 4.1.1 / 2017-04-03
+- [#118](https://github.com/oauthjs/angular-oauth2/pull/118) Validate object property access (@hitmanmcc)
+- [#107](https://github.com/oauthjs/angular-oauth2/pull/107) Updated readme (@anteriovieira)
+
 ### 4.1.0 / 2016-11-03
 - [#87](https://github.com/oauthjs/angular-oauth2/pull/87) allow overriding oauth base config using options (@lionelB)
+- [#96](https://github.com/oauthjs/angular-oauth2/pull/96) Specify the type of grant used (#96) (@Timokasse)
 
 ### 4.0.0 / 2016-02-12
 - [#80](https://github.com/oauthjs/angular-oauth2/pull/80) Reintroduce authorization header to be overridden (@ruipenso)
@@ -37,7 +42,7 @@
 ### 2.0.0 / 2015-02-04
 - [#11](https://github.com/oauthjs/angular-oauth2/pull/11) Add options to `ipCookie.remove()` on OAuthToken (@ruipenso)
 - [#10](https://github.com/oauthjs/angular-oauth2/pull/10) Update `oauthInterceptor` responseError handling (@ruipenso)
-- [#7](https://github.com/oauthjs/angular-oauth2/pull/7) Fix readme typo of download url (@brunnolou)
+- [#7](https://github.com/oauthjs/angular-oauth2/pull/7) Fix readme typo of download url (@seegno)
 - [#6](https://github.com/oauthjs/angular-oauth2/pull/6) README.md: typo fix (@AdirAmsalem)
 
 ### 1.0.2 / 2015-01-19
diff --git a/bower.json b/bower.json
index c351c6e..137280d 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
 {
   "name": "angular-oauth2",
-  "version": "4.0.0",
+  "version": "4.1.1",
   "description": "AngularJS OAuth2",
   "main": "./dist/angular-oauth2.js",
   "authors": [
@@ -23,8 +23,8 @@
     "test"
   ],
   "dependencies": {
-    "angular": "^1.4.0",
-    "angular-cookies": "^1.4.0",
+    "angular": "1.5.9",
+    "angular-cookies": "1.5.9",
     "query-string": "^1.0.0"
   }
 }
diff --git a/dist/angular-oauth2.js b/dist/angular-oauth2.js
index 91122c9..ab9f75a 100644
--- a/dist/angular-oauth2.js
+++ b/dist/angular-oauth2.js
@@ -1,6 +1,6 @@
 /**
  * angular-oauth2 - Angular OAuth2
- * @version v4.1.0
+ * @version v4.1.1
  * @link https://github.com/seegno/angular-oauth2
  * @license MIT
  */
@@ -14,6 +14,10 @@
     }
 })(this, function(angular, ngCookies, queryString) {
     var ngModule = angular.module("angular-oauth2", [ ngCookies ]).config(oauthConfig).factory("oauthInterceptor", oauthInterceptor).provider("OAuth", OAuthProvider).provider("OAuthToken", OAuthTokenProvider);
+    function oauthConfig($httpProvider) {
+        $httpProvider.interceptors.push("oauthInterceptor");
+    }
+    oauthConfig.$inject = [ "$httpProvider" ];
     function oauthInterceptor($q, $rootScope, OAuthToken) {
         return {
             request: function request(config) {
@@ -24,11 +28,14 @@
                 return config;
             },
             responseError: function responseError(rejection) {
+                if (!rejection) {
+                    return $q.reject(rejection);
+                }
                 if (400 === rejection.status && rejection.data && ("invalid_request" === rejection.data.error || "invalid_grant" === rejection.data.error)) {
                     OAuthToken.removeToken();
                     $rootScope.$emit("oauth:error", rejection);
                 }
-                if (401 === rejection.status && rejection.data && "invalid_token" === rejection.data.error || rejection.headers("www-authenticate") && 0 === rejection.headers("www-authenticate").indexOf("Bearer")) {
+                if (401 === rejection.status && rejection.data && "invalid_token" === rejection.data.error || rejection.headers && rejection.headers("www-authenticate") && 0 === rejection.headers("www-authenticate").indexOf("Bearer")) {
                     $rootScope.$emit("oauth:error", rejection);
                 }
                 return $q.reject(rejection);
@@ -36,10 +43,6 @@
         };
     }
     oauthInterceptor.$inject = [ "$q", "$rootScope", "OAuthToken" ];
-    function oauthConfig($httpProvider) {
-        $httpProvider.interceptors.push("oauthInterceptor");
-    }
-    oauthConfig.$inject = [ "$httpProvider" ];
     var _createClass = function() {
         function defineProperties(target, props) {
             for (var i = 0; i < props.length; i++) {
@@ -239,25 +242,33 @@
                 }, {
                     key: "getAccessToken",
                     value: function getAccessToken() {
-                        return this.getToken() ? this.getToken().access_token : undefined;
+                        var _ref = this.getToken() || {};
+                        var access_token = _ref.access_token;
+                        return access_token;
                     }
                 }, {
                     key: "getAuthorizationHeader",
                     value: function getAuthorizationHeader() {
-                        if (!(this.getTokenType() && this.getAccessToken())) {
+                        var tokenType = this.getTokenType();
+                        var accessToken = this.getAccessToken();
+                        if (!tokenType || !accessToken) {
                             return;
                         }
-                        return this.getTokenType().charAt(0).toUpperCase() + this.getTokenType().substr(1) + " " + this.getAccessToken();
+                        return tokenType.charAt(0).toUpperCase() + tokenType.substr(1) + " " + accessToken;
                     }
                 }, {
                     key: "getRefreshToken",
                     value: function getRefreshToken() {
-                        return this.getToken() ? this.getToken().refresh_token : undefined;
+                        var _ref2 = this.getToken() || {};
+                        var refresh_token = _ref2.refresh_token;
+                        return refresh_token;
                     }
                 }, {
                     key: "getTokenType",
                     value: function getTokenType() {
-                        return this.getToken() ? this.getToken().token_type : undefined;
+                        var _ref3 = this.getToken() || {};
+                        var token_type = _ref3.token_type;
+                        return token_type;
                     }
                 }, {
                     key: "removeToken",
diff --git a/dist/angular-oauth2.min.js b/dist/angular-oauth2.min.js
index 98e09a3..b5ecefe 100644
--- a/dist/angular-oauth2.min.js
+++ b/dist/angular-oauth2.min.js
@@ -1 +1 @@
-!function(e,t){"function"==typeof define&&define.amd?define(["angular","angular-cookies","query-string"],t):"object"==typeof exports?module.exports=t(require("angular"),require("angular-cookies"),require("query-string")):e.angularOAuth2=t(e.angular,"ngCookies",e.queryString)}(this,function(e,t,n){function r(e,t,n){return{request:function(e){return e.headers=e.headers||{},!e.headers.hasOwnProperty("Authorization")&&n.getAuthorizationHeader()&&(e.headers.Authorization=n.getAuthorizationHeader()),e},responseError:function(r){return 400!==r.status||!r.data||"invalid_request"!==r.data.error&&"invalid_grant"!==r.data.error||(n.removeToken(),t.$emit("oauth:error",r)),(401===r.status&&r.data&&"invalid_token"===r.data.error||r.headers("www-authenticate")&&0===r.headers("www-authenticate").indexOf("Bearer"))&&t.$emit("oauth:error",r),e.reject(r)}}}function o(e){e.interceptors.push("oauthInterceptor")}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(){var t=this,r=function(t){if(!(t instanceof Object))throw new TypeError("Invalid argument: `config` must be an `Object`.");var n=e.extend({},f,t);return e.forEach(h,function(e){if(!n[e])throw new Error("Missing parameter: "+e+".")}),"/"===n.baseUrl.substr(-1)&&(n.baseUrl=n.baseUrl.slice(0,-1)),"/"!==n.grantPath[0]&&(n.grantPath="/"+n.grantPath),"/"!==n.revokePath[0]&&(n.revokePath="/"+n.revokePath),n};this.configure=function(e){t.defaultConfig=r(e)},this.$get=function(t,o){var a=function(){function a(e){i(this,a),this.config=e}return s(a,[{key:"configure",value:function(e){this.config=r(e)}},{key:"isAuthenticated",value:function(){return!!o.getToken()}},{key:"getAccessToken",value:function(r,i){return r=e.extend({client_id:this.config.clientId,grant_type:"password"},r),null!==this.config.clientSecret&&(r.client_secret=this.config.clientSecret),r=n.stringify(r),i=e.extend({headers:{Authorization:void 0,"Content-Type":"application/x-www-form-urlencoded"}},i),t.post(""+this.config.baseUrl+this.config.grantPath,r,i).then(function(e){return o.setToken(e.data),e})}},{key:"getRefreshToken",value:function(r,i){return r=e.extend({client_id:this.config.clientId,grant_type:"refresh_token",refresh_token:o.getRefreshToken()},r),null!==this.config.clientSecret&&(r.client_secret=this.config.clientSecret),r=n.stringify(r),i=e.extend({headers:{Authorization:void 0,"Content-Type":"application/x-www-form-urlencoded"}},i),t.post(""+this.config.baseUrl+this.config.grantPath,r,i).then(function(e){return o.setToken(e.data),e})}},{key:"revokeToken",value:function(r,i){var a=o.getRefreshToken();return r=e.extend({client_id:this.config.clientId,token:a?a:o.getAccessToken(),token_type_hint:a?"refresh_token":"access_token"},r),null!==this.config.clientSecret&&(r.client_secret=this.config.clientSecret),r=n.stringify(r),i=e.extend({headers:{"Content-Type":"application/x-www-form-urlencoded"}},i),t.post(""+this.config.baseUrl+this.config.revokePath,r,i).then(function(e){return o.removeToken(),e})}}]),a}();return new a(this.defaultConfig)},this.$get.$inject=["$http","OAuthToken"]}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function u(){var t={name:"token",options:{secure:!0}};this.configure=function(n){if(!(n instanceof Object))throw new TypeError("Invalid argument: `config` must be an `Object`.");return e.extend(t,n),t},this.$get=function(e){var n=function(){function n(){i(this,n)}return s(n,[{key:"setToken",value:function(n){return e.putObject(t.name,n,t.options)}},{key:"getToken",value:function(){return e.getObject(t.name)}},{key:"getAccessToken",value:function(){return this.getToken()?this.getToken().access_token:void 0}},{key:"getAuthorizationHeader",value:function(){if(this.getTokenType()&&this.getAccessToken())return this.getTokenType().charAt(0).toUpperCase()+this.getTokenType().substr(1)+" "+this.getAccessToken()}},{key:"getRefreshToken",value:function(){return this.getToken()?this.getToken().refresh_token:void 0}},{key:"getTokenType",value:function(){return this.getToken()?this.getToken().token_type:void 0}},{key:"removeToken",value:function(){return e.remove(t.name,t.options)}}]),n}();return new n},this.$get.$inject=["$cookies"]}var c=e.module("angular-oauth2",[t]).config(o).factory("oauthInterceptor",r).provider("OAuth",a).provider("OAuthToken",u);r.$inject=["$q","$rootScope","OAuthToken"],o.$inject=["$httpProvider"];var s=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),f={baseUrl:null,clientId:null,clientSecret:null,grantPath:"/oauth2/token",revokePath:"/oauth2/revoke"},h=["baseUrl","clientId","grantPath","revokePath"],s=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}();return c});
\ No newline at end of file
+!function(e,t){"function"==typeof define&&define.amd?define(["angular","angular-cookies","query-string"],t):"object"==typeof exports?module.exports=t(require("angular"),require("angular-cookies"),require("query-string")):e.angularOAuth2=t(e.angular,"ngCookies",e.queryString)}(this,function(e,t,n){function r(e){e.interceptors.push("oauthInterceptor")}function o(e,t,n){return{request:function(e){return e.headers=e.headers||{},!e.headers.hasOwnProperty("Authorization")&&n.getAuthorizationHeader()&&(e.headers.Authorization=n.getAuthorizationHeader()),e},responseError:function(r){return r?(400!==r.status||!r.data||"invalid_request"!==r.data.error&&"invalid_grant"!==r.data.error||(n.removeToken(),t.$emit("oauth:error",r)),(401===r.status&&r.data&&"invalid_token"===r.data.error||r.headers&&r.headers("www-authenticate")&&0===r.headers("www-authenticate").indexOf("Bearer"))&&t.$emit("oauth:error",r),e.reject(r)):e.reject(r)}}}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(){var t=this,r=function(t){if(!(t instanceof Object))throw new TypeError("Invalid argument: `config` must be an `Object`.");var n=e.extend({},f,t);return e.forEach(h,function(e){if(!n[e])throw new Error("Missing parameter: "+e+".")}),"/"===n.baseUrl.substr(-1)&&(n.baseUrl=n.baseUrl.slice(0,-1)),"/"!==n.grantPath[0]&&(n.grantPath="/"+n.grantPath),"/"!==n.revokePath[0]&&(n.revokePath="/"+n.revokePath),n};this.configure=function(e){t.defaultConfig=r(e)},this.$get=function(t,o){var a=function(){function a(e){i(this,a),this.config=e}return s(a,[{key:"configure",value:function(e){this.config=r(e)}},{key:"isAuthenticated",value:function(){return!!o.getToken()}},{key:"getAccessToken",value:function(r,i){return r=e.extend({client_id:this.config.clientId,grant_type:"password"},r),null!==this.config.clientSecret&&(r.client_secret=this.config.clientSecret),r=n.stringify(r),i=e.extend({headers:{Authorization:void 0,"Content-Type":"application/x-www-form-urlencoded"}},i),t.post(""+this.config.baseUrl+this.config.grantPath,r,i).then(function(e){return o.setToken(e.data),e})}},{key:"getRefreshToken",value:function(r,i){return r=e.extend({client_id:this.config.clientId,grant_type:"refresh_token",refresh_token:o.getRefreshToken()},r),null!==this.config.clientSecret&&(r.client_secret=this.config.clientSecret),r=n.stringify(r),i=e.extend({headers:{Authorization:void 0,"Content-Type":"application/x-www-form-urlencoded"}},i),t.post(""+this.config.baseUrl+this.config.grantPath,r,i).then(function(e){return o.setToken(e.data),e})}},{key:"revokeToken",value:function(r,i){var a=o.getRefreshToken();return r=e.extend({client_id:this.config.clientId,token:a?a:o.getAccessToken(),token_type_hint:a?"refresh_token":"access_token"},r),null!==this.config.clientSecret&&(r.client_secret=this.config.clientSecret),r=n.stringify(r),i=e.extend({headers:{"Content-Type":"application/x-www-form-urlencoded"}},i),t.post(""+this.config.baseUrl+this.config.revokePath,r,i).then(function(e){return o.removeToken(),e})}}]),a}();return new a(this.defaultConfig)},this.$get.$inject=["$http","OAuthToken"]}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function u(){var t={name:"token",options:{secure:!0}};this.configure=function(n){if(!(n instanceof Object))throw new TypeError("Invalid argument: `config` must be an `Object`.");return e.extend(t,n),t},this.$get=function(e){var n=function(){function n(){i(this,n)}return s(n,[{key:"setToken",value:function(n){return e.putObject(t.name,n,t.options)}},{key:"getToken",value:function(){return e.getObject(t.name)}},{key:"getAccessToken",value:function(){var e=this.getToken()||{},t=e.access_token;return t}},{key:"getAuthorizationHeader",value:function(){var e=this.getTokenType(),t=this.getAccessToken();if(e&&t)return e.charAt(0).toUpperCase()+e.substr(1)+" "+t}},{key:"getRefreshToken",value:function(){var e=this.getToken()||{},t=e.refresh_token;return t}},{key:"getTokenType",value:function(){var e=this.getToken()||{},t=e.token_type;return t}},{key:"removeToken",value:function(){return e.remove(t.name,t.options)}}]),n}();return new n},this.$get.$inject=["$cookies"]}var c=e.module("angular-oauth2",[t]).config(r).factory("oauthInterceptor",o).provider("OAuth",a).provider("OAuthToken",u);r.$inject=["$httpProvider"],o.$inject=["$q","$rootScope","OAuthToken"];var s=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),f={baseUrl:null,clientId:null,clientSecret:null,grantPath:"/oauth2/token",revokePath:"/oauth2/revoke"},h=["baseUrl","clientId","grantPath","revokePath"],s=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}();return c});
\ No newline at end of file
diff --git a/package.json b/package.json
index 4091d7d..9f833ab 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "angular-oauth2",
-  "version": "4.1.0",
+  "version": "4.1.1",
   "description": "Angular OAuth2",
   "main": "./dist/angular-oauth2.js",
   "repository": {
@@ -53,7 +53,7 @@
     "yargs": "^3.6.0"
   },
   "scripts": {
-    "changelog": "./node_modules/.bin/github-changes -o seegno -r angular-oauth2 -a --only-pulls --use-commit-body --title 'Changelog' --date-format '/ YYYY-MM-DD'",
+    "changelog": "./node_modules/.bin/github-changes -o oauthjs -r angular-oauth2 -a --only-pulls --use-commit-body --title 'Changelog' --date-format '/ YYYY-MM-DD'",
     "test": "./node_modules/.bin/gulp test --browsers Firefox"
   }
 }

From 7758be4a914d252a26b606be0e03642e5f77fa9b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20D=C4=99bowski?=
 <marcin.debowski@profil-software.com>
Date: Thu, 4 Jan 2018 11:24:36 +0100
Subject: [PATCH 39/42] Make angular dev dependecy.

---
 package.json | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/package.json b/package.json
index 9f833ab..b5d8b06 100644
--- a/package.json
+++ b/package.json
@@ -21,12 +21,11 @@
     "url": "https://github.com/seegno/angular-oauth2/issues"
   },
   "homepage": "https://github.com/seegno/angular-oauth2",
-  "dependencies": {
-    "angular": "1.5.9",
-    "angular-cookies": "1.5.9",
-    "query-string": "^1.0.0"
-  },
+  "dependencies": {},
   "devDependencies": {
+    "angular": "^1.5.9",
+    "angular-cookies": "^1.5.9",
+    "query-string": "^1.0.0",
     "angular-mocks": "1.5.9",
     "github-changes": "^1.0.0",
     "gulp": "^3.8.10",

From 83b8de918ea6a1693b872fb58f39536ea6995960 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20D=C4=99bowski?=
 <marcin.debowski@profil-software.com>
Date: Thu, 4 Jan 2018 12:11:40 +0100
Subject: [PATCH 40/42] Update browserify.

---
 package.json | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index b5d8b06..b9dc462 100644
--- a/package.json
+++ b/package.json
@@ -25,8 +25,8 @@
   "devDependencies": {
     "angular": "^1.5.9",
     "angular-cookies": "^1.5.9",
-    "query-string": "^1.0.0",
     "angular-mocks": "1.5.9",
+    "browserify": "^14.5.0",
     "github-changes": "^1.0.0",
     "gulp": "^3.8.10",
     "gulp-babel": "^5.3.0",
@@ -38,7 +38,7 @@
     "gulp-wrap-umd": "^0.2.1",
     "jshint-stylish": "^1.0.0",
     "karma": "^0.13.0",
-    "karma-browserify": "^2.0.0",
+    "karma-browserify": "^5.1.2",
     "karma-chrome-launcher": "^0.1.7",
     "karma-firefox-launcher": "^0.1.4",
     "karma-mocha": "^0.1.10",
@@ -47,8 +47,10 @@
     "karma-sinon": "^1.0.4",
     "lodash": "^4.0.0",
     "mocha": "^2.4.5",
+    "query-string": "^1.0.0",
     "should": "^4.6.0",
     "sinon": "^1.17.3",
+    "watchify": "^3.9.0",
     "yargs": "^3.6.0"
   },
   "scripts": {

From eda48bda4f1ff74551be71bf88350dad22504e14 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20D=C4=99bowski?=
 <marcin.debowski@profil-software.com>
Date: Thu, 4 Jan 2018 12:15:04 +0100
Subject: [PATCH 41/42] Catch http errors to prevent Chrome unhandled
 rejections from failing tests.

---
 .../interceptors/oauth-interceptor.spec.js     | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/test/unit/interceptors/oauth-interceptor.spec.js b/test/unit/interceptors/oauth-interceptor.spec.js
index 4c5e3c7..8503200 100644
--- a/test/unit/interceptors/oauth-interceptor.spec.js
+++ b/test/unit/interceptors/oauth-interceptor.spec.js
@@ -64,7 +64,8 @@ describe('oauthInterceptor', function() {
 
     $httpBackend.expectGET('https://website.com').respond(400, { error: 'invalid_request' });
 
-    $http.get('https://website.com');
+    $http.get('https://website.com')
+      .catch(() => { });
 
     $httpBackend.flush();
 
@@ -77,7 +78,8 @@ describe('oauthInterceptor', function() {
 
     $httpBackend.expectGET('https://website.com').respond(400, { error: 'invalid_request' });
 
-    $http.get('https://website.com');
+    $http.get('https://website.com')
+      .catch(() => { });
 
     $httpBackend.flush();
 
@@ -93,7 +95,8 @@ describe('oauthInterceptor', function() {
 
     $httpBackend.expectGET('https://website.com').respond(400, { error: 'invalid_grant' });
 
-    $http.get('https://website.com');
+    $http.get('https://website.com')
+      .catch(() => { });
 
     $httpBackend.flush();
 
@@ -106,7 +109,8 @@ describe('oauthInterceptor', function() {
 
     $httpBackend.expectGET('https://website.com').respond(400, { error: 'invalid_grant' });
 
-    $http.get('https://website.com');
+    $http.get('https://website.com')
+      .catch(() => { });
 
     $httpBackend.flush();
 
@@ -122,7 +126,8 @@ describe('oauthInterceptor', function() {
 
     $httpBackend.expectGET('https://website.com').respond(401, { error: 'invalid_token' });
 
-    $http.get('https://website.com');
+    $http.get('https://website.com')
+      .catch(() => { });
 
     $httpBackend.flush();
 
@@ -138,7 +143,8 @@ describe('oauthInterceptor', function() {
 
     $httpBackend.expectGET('https://website.com').respond(401, null, { 'www-authenticate': 'Bearer realm="example"' });
 
-    $http.get('https://website.com');
+    $http.get('https://website.com')
+      .catch(() => { });
 
     $httpBackend.flush();
 

From 35c0c03a98153a4c2056b4b9411872cdce90fa0e Mon Sep 17 00:00:00 2001
From: Pedro Gomes <gomes@uphold.com>
Date: Wed, 24 Jan 2018 17:29:46 +0000
Subject: [PATCH 42/42] Release 4.2.0

---
 CHANGELOG.md               | 11 ++++++---
 bower.json                 |  2 +-
 dist/angular-oauth2.js     | 50 +++++++++++++++++++-------------------
 dist/angular-oauth2.min.js |  2 +-
 package.json               |  2 +-
 5 files changed, 35 insertions(+), 32 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 002ccf8..50a318e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,8 @@
 ## Changelog
 
+### 4.2.0 / 2018-01-24
+- [#127](https://github.com/oauthjs/angular-oauth2/pull/127) Prevent loading angular multiple times with webpack (@erhardos)
+
 ### 4.1.1 / 2017-04-03
 - [#118](https://github.com/oauthjs/angular-oauth2/pull/118) Validate object property access (@hitmanmcc)
 - [#107](https://github.com/oauthjs/angular-oauth2/pull/107) Updated readme (@anteriovieira)
@@ -22,13 +25,13 @@
 - [#76](https://github.com/oauthjs/angular-oauth2/pull/76) Update `package.json` (@ruipenso)
 
 ### 3.0.1 / 2015-06-01
-- [#27](https://github.com/oauthjs/angular-oauth2/pull/27) Add travis-ci configuration (@joaogranado)
+- [#27](https://github.com/oauthjs/angular-oauth2/pull/27) Add travis-ci configuration (@seegno)
 
 ### 3.0.0 / 2015-06-01
 - [#49](https://github.com/oauthjs/angular-oauth2/pull/49) Add changelog task (@ruipenso)
 - [#48](https://github.com/oauthjs/angular-oauth2/pull/48) Update readme (@ruipenso)
-- [#25](https://github.com/oauthjs/angular-oauth2/pull/25) Replace ipCookie with ngCookies (@joaogranado)
-- [#28](https://github.com/oauthjs/angular-oauth2/pull/28) Add methods to get/set `token` property (@joaogranado)
+- [#25](https://github.com/oauthjs/angular-oauth2/pull/25) Replace ipCookie with ngCookies (@seegno)
+- [#28](https://github.com/oauthjs/angular-oauth2/pull/28) Add methods to get/set `token` property (@seegno)
 - [#47](https://github.com/oauthjs/angular-oauth2/pull/47) Add unauthorized error interception (@ruipenso)
 
 ### 2.1.1 / 2015-05-28
@@ -46,7 +49,7 @@
 - [#6](https://github.com/oauthjs/angular-oauth2/pull/6) README.md: typo fix (@AdirAmsalem)
 
 ### 1.0.2 / 2015-01-19
-- [#4](https://github.com/oauthjs/angular-oauth2/pull/4) Add support for higher dependencies versions (@joaogranado)
+- [#4](https://github.com/oauthjs/angular-oauth2/pull/4) Add support for higher dependencies versions (@seegno)
 
 ### 1.0.1 / 2015-01-19
 - [#3](https://github.com/oauthjs/angular-oauth2/pull/3) Fix bower ignore list (@ruipenso)
diff --git a/bower.json b/bower.json
index 137280d..81125c0 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
 {
   "name": "angular-oauth2",
-  "version": "4.1.1",
+  "version": "4.2.0",
   "description": "AngularJS OAuth2",
   "main": "./dist/angular-oauth2.js",
   "authors": [
diff --git a/dist/angular-oauth2.js b/dist/angular-oauth2.js
index ab9f75a..3706f93 100644
--- a/dist/angular-oauth2.js
+++ b/dist/angular-oauth2.js
@@ -18,31 +18,6 @@
         $httpProvider.interceptors.push("oauthInterceptor");
     }
     oauthConfig.$inject = [ "$httpProvider" ];
-    function oauthInterceptor($q, $rootScope, OAuthToken) {
-        return {
-            request: function request(config) {
-                config.headers = config.headers || {};
-                if (!config.headers.hasOwnProperty("Authorization") && OAuthToken.getAuthorizationHeader()) {
-                    config.headers.Authorization = OAuthToken.getAuthorizationHeader();
-                }
-                return config;
-            },
-            responseError: function responseError(rejection) {
-                if (!rejection) {
-                    return $q.reject(rejection);
-                }
-                if (400 === rejection.status && rejection.data && ("invalid_request" === rejection.data.error || "invalid_grant" === rejection.data.error)) {
-                    OAuthToken.removeToken();
-                    $rootScope.$emit("oauth:error", rejection);
-                }
-                if (401 === rejection.status && rejection.data && "invalid_token" === rejection.data.error || rejection.headers && rejection.headers("www-authenticate") && 0 === rejection.headers("www-authenticate").indexOf("Bearer")) {
-                    $rootScope.$emit("oauth:error", rejection);
-                }
-                return $q.reject(rejection);
-            }
-        };
-    }
-    oauthInterceptor.$inject = [ "$q", "$rootScope", "OAuthToken" ];
     var _createClass = function() {
         function defineProperties(target, props) {
             for (var i = 0; i < props.length; i++) {
@@ -282,5 +257,30 @@
         };
         this.$get.$inject = [ "$cookies" ];
     }
+    function oauthInterceptor($q, $rootScope, OAuthToken) {
+        return {
+            request: function request(config) {
+                config.headers = config.headers || {};
+                if (!config.headers.hasOwnProperty("Authorization") && OAuthToken.getAuthorizationHeader()) {
+                    config.headers.Authorization = OAuthToken.getAuthorizationHeader();
+                }
+                return config;
+            },
+            responseError: function responseError(rejection) {
+                if (!rejection) {
+                    return $q.reject(rejection);
+                }
+                if (400 === rejection.status && rejection.data && ("invalid_request" === rejection.data.error || "invalid_grant" === rejection.data.error)) {
+                    OAuthToken.removeToken();
+                    $rootScope.$emit("oauth:error", rejection);
+                }
+                if (401 === rejection.status && rejection.data && "invalid_token" === rejection.data.error || rejection.headers && rejection.headers("www-authenticate") && 0 === rejection.headers("www-authenticate").indexOf("Bearer")) {
+                    $rootScope.$emit("oauth:error", rejection);
+                }
+                return $q.reject(rejection);
+            }
+        };
+    }
+    oauthInterceptor.$inject = [ "$q", "$rootScope", "OAuthToken" ];
     return ngModule;
 });
\ No newline at end of file
diff --git a/dist/angular-oauth2.min.js b/dist/angular-oauth2.min.js
index b5ecefe..aed184d 100644
--- a/dist/angular-oauth2.min.js
+++ b/dist/angular-oauth2.min.js
@@ -1 +1 @@
-!function(e,t){"function"==typeof define&&define.amd?define(["angular","angular-cookies","query-string"],t):"object"==typeof exports?module.exports=t(require("angular"),require("angular-cookies"),require("query-string")):e.angularOAuth2=t(e.angular,"ngCookies",e.queryString)}(this,function(e,t,n){function r(e){e.interceptors.push("oauthInterceptor")}function o(e,t,n){return{request:function(e){return e.headers=e.headers||{},!e.headers.hasOwnProperty("Authorization")&&n.getAuthorizationHeader()&&(e.headers.Authorization=n.getAuthorizationHeader()),e},responseError:function(r){return r?(400!==r.status||!r.data||"invalid_request"!==r.data.error&&"invalid_grant"!==r.data.error||(n.removeToken(),t.$emit("oauth:error",r)),(401===r.status&&r.data&&"invalid_token"===r.data.error||r.headers&&r.headers("www-authenticate")&&0===r.headers("www-authenticate").indexOf("Bearer"))&&t.$emit("oauth:error",r),e.reject(r)):e.reject(r)}}}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(){var t=this,r=function(t){if(!(t instanceof Object))throw new TypeError("Invalid argument: `config` must be an `Object`.");var n=e.extend({},f,t);return e.forEach(h,function(e){if(!n[e])throw new Error("Missing parameter: "+e+".")}),"/"===n.baseUrl.substr(-1)&&(n.baseUrl=n.baseUrl.slice(0,-1)),"/"!==n.grantPath[0]&&(n.grantPath="/"+n.grantPath),"/"!==n.revokePath[0]&&(n.revokePath="/"+n.revokePath),n};this.configure=function(e){t.defaultConfig=r(e)},this.$get=function(t,o){var a=function(){function a(e){i(this,a),this.config=e}return s(a,[{key:"configure",value:function(e){this.config=r(e)}},{key:"isAuthenticated",value:function(){return!!o.getToken()}},{key:"getAccessToken",value:function(r,i){return r=e.extend({client_id:this.config.clientId,grant_type:"password"},r),null!==this.config.clientSecret&&(r.client_secret=this.config.clientSecret),r=n.stringify(r),i=e.extend({headers:{Authorization:void 0,"Content-Type":"application/x-www-form-urlencoded"}},i),t.post(""+this.config.baseUrl+this.config.grantPath,r,i).then(function(e){return o.setToken(e.data),e})}},{key:"getRefreshToken",value:function(r,i){return r=e.extend({client_id:this.config.clientId,grant_type:"refresh_token",refresh_token:o.getRefreshToken()},r),null!==this.config.clientSecret&&(r.client_secret=this.config.clientSecret),r=n.stringify(r),i=e.extend({headers:{Authorization:void 0,"Content-Type":"application/x-www-form-urlencoded"}},i),t.post(""+this.config.baseUrl+this.config.grantPath,r,i).then(function(e){return o.setToken(e.data),e})}},{key:"revokeToken",value:function(r,i){var a=o.getRefreshToken();return r=e.extend({client_id:this.config.clientId,token:a?a:o.getAccessToken(),token_type_hint:a?"refresh_token":"access_token"},r),null!==this.config.clientSecret&&(r.client_secret=this.config.clientSecret),r=n.stringify(r),i=e.extend({headers:{"Content-Type":"application/x-www-form-urlencoded"}},i),t.post(""+this.config.baseUrl+this.config.revokePath,r,i).then(function(e){return o.removeToken(),e})}}]),a}();return new a(this.defaultConfig)},this.$get.$inject=["$http","OAuthToken"]}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function u(){var t={name:"token",options:{secure:!0}};this.configure=function(n){if(!(n instanceof Object))throw new TypeError("Invalid argument: `config` must be an `Object`.");return e.extend(t,n),t},this.$get=function(e){var n=function(){function n(){i(this,n)}return s(n,[{key:"setToken",value:function(n){return e.putObject(t.name,n,t.options)}},{key:"getToken",value:function(){return e.getObject(t.name)}},{key:"getAccessToken",value:function(){var e=this.getToken()||{},t=e.access_token;return t}},{key:"getAuthorizationHeader",value:function(){var e=this.getTokenType(),t=this.getAccessToken();if(e&&t)return e.charAt(0).toUpperCase()+e.substr(1)+" "+t}},{key:"getRefreshToken",value:function(){var e=this.getToken()||{},t=e.refresh_token;return t}},{key:"getTokenType",value:function(){var e=this.getToken()||{},t=e.token_type;return t}},{key:"removeToken",value:function(){return e.remove(t.name,t.options)}}]),n}();return new n},this.$get.$inject=["$cookies"]}var c=e.module("angular-oauth2",[t]).config(r).factory("oauthInterceptor",o).provider("OAuth",a).provider("OAuthToken",u);r.$inject=["$httpProvider"],o.$inject=["$q","$rootScope","OAuthToken"];var s=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),f={baseUrl:null,clientId:null,clientSecret:null,grantPath:"/oauth2/token",revokePath:"/oauth2/revoke"},h=["baseUrl","clientId","grantPath","revokePath"],s=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}();return c});
\ No newline at end of file
+!function(e,t){"function"==typeof define&&define.amd?define(["angular","angular-cookies","query-string"],t):"object"==typeof exports?module.exports=t(require("angular"),require("angular-cookies"),require("query-string")):e.angularOAuth2=t(e.angular,"ngCookies",e.queryString)}(this,function(e,t,n){function r(e){e.interceptors.push("oauthInterceptor")}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(){var t=this,r=function(t){if(!(t instanceof Object))throw new TypeError("Invalid argument: `config` must be an `Object`.");var n=e.extend({},f,t);return e.forEach(h,function(e){if(!n[e])throw new Error("Missing parameter: "+e+".")}),"/"===n.baseUrl.substr(-1)&&(n.baseUrl=n.baseUrl.slice(0,-1)),"/"!==n.grantPath[0]&&(n.grantPath="/"+n.grantPath),"/"!==n.revokePath[0]&&(n.revokePath="/"+n.revokePath),n};this.configure=function(e){t.defaultConfig=r(e)},this.$get=function(t,i){var a=function(){function a(e){o(this,a),this.config=e}return s(a,[{key:"configure",value:function(e){this.config=r(e)}},{key:"isAuthenticated",value:function(){return!!i.getToken()}},{key:"getAccessToken",value:function(r,o){return r=e.extend({client_id:this.config.clientId,grant_type:"password"},r),null!==this.config.clientSecret&&(r.client_secret=this.config.clientSecret),r=n.stringify(r),o=e.extend({headers:{Authorization:void 0,"Content-Type":"application/x-www-form-urlencoded"}},o),t.post(""+this.config.baseUrl+this.config.grantPath,r,o).then(function(e){return i.setToken(e.data),e})}},{key:"getRefreshToken",value:function(r,o){return r=e.extend({client_id:this.config.clientId,grant_type:"refresh_token",refresh_token:i.getRefreshToken()},r),null!==this.config.clientSecret&&(r.client_secret=this.config.clientSecret),r=n.stringify(r),o=e.extend({headers:{Authorization:void 0,"Content-Type":"application/x-www-form-urlencoded"}},o),t.post(""+this.config.baseUrl+this.config.grantPath,r,o).then(function(e){return i.setToken(e.data),e})}},{key:"revokeToken",value:function(r,o){var a=i.getRefreshToken();return r=e.extend({client_id:this.config.clientId,token:a?a:i.getAccessToken(),token_type_hint:a?"refresh_token":"access_token"},r),null!==this.config.clientSecret&&(r.client_secret=this.config.clientSecret),r=n.stringify(r),o=e.extend({headers:{"Content-Type":"application/x-www-form-urlencoded"}},o),t.post(""+this.config.baseUrl+this.config.revokePath,r,o).then(function(e){return i.removeToken(),e})}}]),a}();return new a(this.defaultConfig)},this.$get.$inject=["$http","OAuthToken"]}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(){var t={name:"token",options:{secure:!0}};this.configure=function(n){if(!(n instanceof Object))throw new TypeError("Invalid argument: `config` must be an `Object`.");return e.extend(t,n),t},this.$get=function(e){var n=function(){function n(){o(this,n)}return s(n,[{key:"setToken",value:function(n){return e.putObject(t.name,n,t.options)}},{key:"getToken",value:function(){return e.getObject(t.name)}},{key:"getAccessToken",value:function(){var e=this.getToken()||{},t=e.access_token;return t}},{key:"getAuthorizationHeader",value:function(){var e=this.getTokenType(),t=this.getAccessToken();if(e&&t)return e.charAt(0).toUpperCase()+e.substr(1)+" "+t}},{key:"getRefreshToken",value:function(){var e=this.getToken()||{},t=e.refresh_token;return t}},{key:"getTokenType",value:function(){var e=this.getToken()||{},t=e.token_type;return t}},{key:"removeToken",value:function(){return e.remove(t.name,t.options)}}]),n}();return new n},this.$get.$inject=["$cookies"]}function u(e,t,n){return{request:function(e){return e.headers=e.headers||{},!e.headers.hasOwnProperty("Authorization")&&n.getAuthorizationHeader()&&(e.headers.Authorization=n.getAuthorizationHeader()),e},responseError:function(r){return r?(400!==r.status||!r.data||"invalid_request"!==r.data.error&&"invalid_grant"!==r.data.error||(n.removeToken(),t.$emit("oauth:error",r)),(401===r.status&&r.data&&"invalid_token"===r.data.error||r.headers&&r.headers("www-authenticate")&&0===r.headers("www-authenticate").indexOf("Bearer"))&&t.$emit("oauth:error",r),e.reject(r)):e.reject(r)}}}var c=e.module("angular-oauth2",[t]).config(r).factory("oauthInterceptor",u).provider("OAuth",i).provider("OAuthToken",a);r.$inject=["$httpProvider"];var s=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),f={baseUrl:null,clientId:null,clientSecret:null,grantPath:"/oauth2/token",revokePath:"/oauth2/revoke"},h=["baseUrl","clientId","grantPath","revokePath"],s=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}();return u.$inject=["$q","$rootScope","OAuthToken"],c});
\ No newline at end of file
diff --git a/package.json b/package.json
index b9dc462..b3dca55 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "angular-oauth2",
-  "version": "4.1.1",
+  "version": "4.2.0",
   "description": "Angular OAuth2",
   "main": "./dist/angular-oauth2.js",
   "repository": {
