diff --git a/.travis.yml b/.travis.yml
index 8e58244..08635f2 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
 
@@ -11,10 +10,8 @@ git:
   depth: 10
 
 node_js:
-  - iojs
+  - 4.3
 
 cache:
   directories:
     - node_modules
-    - bower_components
-
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 23da70e..50a318e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,35 +1,59 @@
 ## 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)
+
+### 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)
+
+### 3.1.1 / 2016-02-12
+- [#79](https://github.com/oauthjs/angular-oauth2/pull/79) Update dependencies (@ruipenso)
+
+### 3.1.0 / 2016-02-10
+- [#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 (@seegno)
 
 ### 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 (@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
-- [#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 (@seegno)
+- [#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 (@seegno)
 
 ### 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)
+- [#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/README.md b/README.md
index 818e183..758e055 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
@@ -16,7 +28,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,11 +39,11 @@ 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>
 ```
 
-###### 3. Configure `OAuth` (required) and `OAuthToken` (optional):
+###### 3. Configure `OAuth` (optional) and `OAuthToken` (optional):
 
 ```js
 angular.module('myApp', ['angular-oauth2'])
@@ -44,6 +56,19 @@ 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.
+
+```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):
 
 ```js
@@ -84,6 +109,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
@@ -177,11 +214,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/bower.json b/bower.json
index 379a19e..81125c0 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
 {
   "name": "angular-oauth2",
-  "version": "3.0.1",
+  "version": "4.2.0",
   "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 6152c8c..3706f93 100644
--- a/dist/angular-oauth2.js
+++ b/dist/angular-oauth2.js
@@ -1,27 +1,44 @@
 /**
  * angular-oauth2 - Angular OAuth2
- * @version v3.0.1
+ * @version v4.1.1
  * @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" ];
-    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,
@@ -31,15 +48,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 + ".");
@@ -56,106 +70,121 @@
             }
             return config;
         };
+        this.configure = function(params) {
+            _this.defaultConfig = sanitizeConfigParams(params);
+        };
         this.$get = function($http, OAuthToken) {
             var OAuth = function() {
-                function OAuth() {
-                    if (!config) {
-                        throw new Error("`OAuthProvider` must be configured first.");
-                    }
+                function OAuth(config) {
+                    _classCallCheck(this, OAuth);
+                    this.config = config;
                 }
-                _prototypeProperties(OAuth, null, {
-                    isAuthenticated: {
-                        value: function isAuthenticated() {
-                            return !!OAuthToken.getToken();
-                        },
-                        writable: true,
-                        enumerable: true,
-                        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.");
+                _createClass(OAuth, [ {
+                    key: "configure",
+                    value: function configure(params) {
+                        this.config = sanitizeConfigParams(params);
+                    }
+                }, {
+                    key: "isAuthenticated",
+                    value: function isAuthenticated() {
+                        return !!OAuthToken.getToken();
+                    }
+                }, {
+                    key: "getAccessToken",
+                    value: function getAccessToken(data, options) {
+                        data = angular.extend({
+                            client_id: this.config.clientId,
+                            grant_type: "password"
+                        }, data);
+                        if (null !== this.config.clientSecret) {
+                            data.client_secret = this.config.clientSecret;
+                        }
+                        data = queryString.stringify(data);
+                        options = angular.extend({
+                            headers: {
+                                Authorization: undefined,
+                                "Content-Type": "application/x-www-form-urlencoded"
                             }
-                            var data = {
-                                client_id: config.clientId,
-                                grant_type: "password",
-                                username: user.username,
-                                password: user.password
-                            };
-                            if (null !== config.clientSecret) {
-                                data.client_secret = config.clientSecret;
+                        }, options);
+                        return $http.post("" + this.config.baseUrl + this.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: this.config.clientId,
+                            grant_type: "refresh_token",
+                            refresh_token: OAuthToken.getRefreshToken()
+                        }, data);
+                        if (null !== this.config.clientSecret) {
+                            data.client_secret = this.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: {
-                                    "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() {
-                            var data = {
-                                client_id: config.clientId,
-                                grant_type: "refresh_token",
-                                refresh_token: OAuthToken.getRefreshToken()
-                            };
-                            if (null !== config.clientSecret) {
-                                data.client_secret = config.clientSecret;
+                        }, options);
+                        return $http.post("" + this.config.baseUrl + this.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: this.config.clientId,
+                            token: refreshToken ? refreshToken : OAuthToken.getAccessToken(),
+                            token_type_hint: refreshToken ? "refresh_token" : "access_token"
+                        }, data);
+                        if (null !== this.config.clientSecret) {
+                            data.client_secret = this.config.clientSecret;
+                        }
+                        data = queryString.stringify(data);
+                        options = angular.extend({
+                            headers: {
+                                "Content-Type": "application/x-www-form-urlencoded"
                             }
-                            data = queryString.stringify(data);
-                            var options = {
-                                headers: {
-                                    "Content-Type": "application/x-www-form-urlencoded"
-                                }
-                            };
-                            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() {
-                            var data = queryString.stringify({
-                                token: OAuthToken.getRefreshToken() ? OAuthToken.getRefreshToken() : OAuthToken.getAccessToken()
-                            });
-                            var options = {
-                                headers: {
-                                    "Content-Type": "application/x-www-form-urlencoded"
-                                }
-                            };
-                            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("" + this.config.baseUrl + this.config.revokePath, data, options).then(function(response) {
+                            OAuthToken.removeToken();
+                            return response;
+                        });
                     }
-                });
+                } ]);
                 return OAuth;
             }();
-            return new OAuth();
+            return new OAuth(this.defaultConfig);
         };
         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",
@@ -172,68 +201,56 @@
         };
         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() {
+                        var _ref = this.getToken() || {};
+                        var access_token = _ref.access_token;
+                        return access_token;
+                    }
+                }, {
+                    key: "getAuthorizationHeader",
+                    value: function getAuthorizationHeader() {
+                        var tokenType = this.getTokenType();
+                        var accessToken = this.getAccessToken();
+                        if (!tokenType || !accessToken) {
+                            return;
+                        }
+                        return tokenType.charAt(0).toUpperCase() + tokenType.substr(1) + " " + accessToken;
+                    }
+                }, {
+                    key: "getRefreshToken",
+                    value: function getRefreshToken() {
+                        var _ref2 = this.getToken() || {};
+                        var refresh_token = _ref2.refresh_token;
+                        return refresh_token;
+                    }
+                }, {
+                    key: "getTokenType",
+                    value: function getTokenType() {
+                        var _ref3 = this.getToken() || {};
+                        var token_type = _ref3.token_type;
+                        return token_type;
+                    }
+                }, {
+                    key: "removeToken",
+                    value: function removeToken() {
+                        return $cookies.remove(config.name, config.options);
+                    }
+                } ]);
                 return OAuthToken;
             }();
             return new OAuthToken();
@@ -242,19 +259,22 @@
     }
     function oauthInterceptor($q, $rootScope, OAuthToken) {
         return {
-            request: function(config) {
-                if (OAuthToken.getAuthorizationHeader()) {
-                    config.headers = config.headers || {};
+            request: function request(config) {
+                config.headers = config.headers || {};
+                if (!config.headers.hasOwnProperty("Authorization") && OAuthToken.getAuthorizationHeader()) {
                     config.headers.Authorization = OAuthToken.getAuthorizationHeader();
                 }
                 return config;
             },
-            responseError: function(rejection) {
+            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);
diff --git a/dist/angular-oauth2.min.js b/dist/angular-oauth2.min.js
index 4260312..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","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){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/gulpfile.js b/gulpfile.js
index 1559f62..c663500 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -3,15 +3,14 @@
  * Module dependencies.
  */
 
+var babel = require('gulp-babel');
 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 ngAnnotate = require('gulp-ng-annotate');
+var karma = require('karma').Server;
 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');
 
@@ -27,10 +26,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 %>',
@@ -47,8 +60,7 @@ var config = {
 
 gulp.task('scripts', ['scripts-lint'], function() {
   return gulp.src(config.src)
-    .pipe(to5({ modules: 'ignore', blacklist: ['useStrict'] }))
-    .pipe(ngAnnotate({ single_quotes: true, add: true }))
+    .pipe(babel({ modules: 'ignore', blacklist: ['useStrict'] }))
     .pipe(concat(config.name))
     .pipe(wrapUmd(config.umd))
     .pipe(uglify({
@@ -81,12 +93,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/karma.conf.js b/karma.conf.js
index ac63bc0..a8c8903 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -14,10 +14,10 @@ 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/lodash/dist/lodash.js',
+      'node_modules/angular/angular.js',
+      'node_modules/angular-cookies/angular-cookies.js',
+      'node_modules/query-string/query-string.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 ea3d80e..b3dca55 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "angular-oauth2",
-  "version": "3.0.1",
+  "version": "4.2.0",
   "description": "Angular OAuth2",
   "main": "./dist/angular-oauth2.js",
   "repository": {
@@ -21,45 +21,40 @@
     "url": "https://github.com/seegno/angular-oauth2/issues"
   },
   "homepage": "https://github.com/seegno/angular-oauth2",
+  "dependencies": {},
   "devDependencies": {
-    "6to5ify": "^3.1.2",
-    "angular-mocks": "^1.3.9",
-    "bower": "^1.3.12",
+    "angular": "^1.5.9",
+    "angular-cookies": "^1.5.9",
+    "angular-mocks": "1.5.9",
+    "browserify": "^14.5.0",
     "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",
-    "gulp-ng-annotate": "^0.4.4",
     "gulp-rename": "^1.2.0",
     "gulp-uglify": "^1.0.2",
     "gulp-wrap-umd": "^0.2.1",
     "jshint-stylish": "^1.0.0",
-    "karma": "^0.12.31",
-    "karma-browserify": "^2.0.0",
+    "karma": "^0.13.0",
+    "karma-browserify": "^5.1.2",
     "karma-chrome-launcher": "^0.1.7",
     "karma-firefox-launcher": "^0.1.4",
     "karma-mocha": "^0.1.10",
     "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",
+    "query-string": "^1.0.0",
     "should": "^4.6.0",
+    "sinon": "^1.17.3",
+    "watchify": "^3.9.0",
     "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'",
+    "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"
-  },
-  "browserify-shim": {
-    "angular": {
-      "exports": "angular"
-    }
   }
 }
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)
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 7243646..b94be80 100644
--- a/src/interceptors/oauth-interceptor.js
+++ b/src/interceptors/oauth-interceptor.js
@@ -1,22 +1,25 @@
 
 /**
  * OAuth interceptor.
- *
- * @ngInject
  */
 
 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();
       }
 
       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)
@@ -30,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);
       }
@@ -40,6 +43,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 c00b0cb..2de0045 100644
--- a/src/providers/oauth-provider.js
+++ b/src/providers/oauth-provider.js
@@ -26,27 +26,20 @@ var requiredKeys = [
  */
 
 function OAuthProvider() {
-  var config;
 
   /**
-   * 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 (config) {
-      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.
-    config = angular.extend({}, defaults, params);
+    const config = angular.extend({}, defaults, params);
 
     // Check if all required keys are set.
     angular.forEach(requiredKeys, (key) => {
@@ -56,27 +49,34 @@ function OAuthProvider() {
     });
 
     // Remove `baseUrl` trailing slash.
-    if('/' === config.baseUrl.substr(-1)) {
+    if ('/' === config.baseUrl.substr(-1)) {
       config.baseUrl = config.baseUrl.slice(0, -1);
     }
 
     // Add `grantPath` facing slash.
-    if('/' !== config.grantPath[0]) {
+    if ('/' !== config.grantPath[0]) {
       config.grantPath = `/${config.grantPath}`;
     }
 
     // Add `revokePath` facing slash.
-    if('/' !== config.revokePath[0]) {
+    if ('/' !== config.revokePath[0]) {
       config.revokePath = `/${config.revokePath}`;
     }
 
     return config;
   };
+  
+  /**
+   * Configure.
+   *
+   * @param {object} params - An `object` of params to extend.
+   */
+  this.configure = (params) => {
+    this.defaultConfig = sanitizeConfigParams(params);
+  };
 
   /**
    * OAuth service.
-   *
-   * @ngInject
    */
 
   this.$get = function($http, OAuthToken) {
@@ -86,12 +86,20 @@ function OAuthProvider() {
        * Check if `OAuthProvider` is configured.
        */
 
-      constructor() {
-        if (!config) {
-          throw new Error('`OAuthProvider` must be configured first.');
-        }
+      constructor(config) {
+        this.config = config;
+      }
+      
+      /**
+       * Configure OAuth service during runtime
+       *
+       * @param {Object} params - An object of params to extend
+       */
+      configure(params) {
+        this.config = sanitizeConfigParams(params);
       }
 
+
       /**
        * Verifies if the `user` is authenticated or not based on the `token`
        * cookie.
@@ -107,35 +115,31 @@ 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 = {
-          client_id: config.clientId,
-          grant_type: 'password',
-          username: user.username,
-          password: user.password
-        };
+      getAccessToken(data, options) {
+        data = angular.extend({
+          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({
-          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) => {
+        return $http.post(`${this.config.baseUrl}${this.config.grantPath}`, data, options).then((response) => {
           OAuthToken.setToken(response.data);
 
           return response;
@@ -146,27 +150,32 @@ 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 = {
-          client_id: config.clientId,
+      getRefreshToken(data, options) {
+        data = angular.extend({
+          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);
 
-        var options = {
-          headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
-        };
+        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) => {
+        return $http.post(`${this.config.baseUrl}${this.config.grantPath}`, data, options).then((response) => {
           OAuthToken.setToken(response.data);
 
           return response;
@@ -177,19 +186,33 @@ 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 = queryString.stringify({
-          token: OAuthToken.getRefreshToken() ? OAuthToken.getRefreshToken() : OAuthToken.getAccessToken()
-        });
+      revokeToken(data, options) {
+        var refreshToken = OAuthToken.getRefreshToken();
 
-        var options = {
-          headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
-        };
+        data = angular.extend({
+          client_id: this.config.clientId,
+          token: refreshToken ? refreshToken : OAuthToken.getAccessToken(),
+          token_type_hint: refreshToken ? 'refresh_token' : 'access_token'
+        }, data);
 
-        return $http.post(`${config.baseUrl}${config.revokePath}`, data, options).then((response) => {
+        if (null !== this.config.clientSecret) {
+          data.client_secret = this.config.clientSecret;
+        }
+
+        data = queryString.stringify(data);
+
+        options = angular.extend({
+          headers: {
+            'Content-Type': 'application/x-www-form-urlencoded'
+          }
+        }, options);
+
+        return $http.post(`${this.config.baseUrl}${this.config.revokePath}`, data, options).then((response) => {
           OAuthToken.removeToken();
 
           return response;
@@ -197,8 +220,10 @@ function OAuthProvider() {
       }
     }
 
-    return new OAuth();
+    return new OAuth(this.defaultConfig);
   };
+
+  this.$get.$inject = ['$http', 'OAuthToken'];
 }
 
 /**
diff --git a/src/providers/oauth-token-provider.js b/src/providers/oauth-token-provider.js
index 271ae9f..13dca0e 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) {
@@ -65,7 +63,9 @@ function OAuthTokenProvider() {
        */
 
       getAccessToken() {
-        return this.getToken() ? this.getToken().access_token : undefined;
+        const { access_token } = this.getToken() || {};
+
+        return access_token;
       }
 
       /**
@@ -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}`;
       }
 
       /**
@@ -85,7 +88,9 @@ function OAuthTokenProvider() {
        */
 
       getRefreshToken() {
-        return this.getToken() ? this.getToken().refresh_token : undefined;
+        const { refresh_token } = this.getToken() || {};
+
+        return refresh_token;
       }
 
       /**
@@ -93,7 +98,9 @@ function OAuthTokenProvider() {
        */
 
       getTokenType() {
-        return this.getToken() ? this.getToken().token_type : undefined;
+        const { token_type } = this.getToken() || {};
+
+        return token_type;
       }
 
       /**
@@ -107,6 +114,8 @@ function OAuthTokenProvider() {
 
     return new OAuthToken();
   };
+
+  this.$get.$inject = ['$cookies'];
 }
 
 /**
diff --git a/test/unit/interceptors/oauth-interceptor.spec.js b/test/unit/interceptors/oauth-interceptor.spec.js
index 9611589..8503200 100644
--- a/test/unit/interceptors/oauth-interceptor.spec.js
+++ b/test/unit/interceptors/oauth-interceptor.spec.js
@@ -37,12 +37,35 @@ 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');
 
     $httpBackend.expectGET('https://website.com').respond(400, { error: 'invalid_request' });
 
-    $http.get('https://website.com');
+    $http.get('https://website.com')
+      .catch(() => { });
 
     $httpBackend.flush();
 
@@ -55,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();
 
@@ -71,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();
 
@@ -84,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();
 
@@ -100,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();
 
@@ -116,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();
 
diff --git a/test/unit/providers/oauth-provider.spec.js b/test/unit/providers/oauth-provider.spec.js
index c2af377..dbb447c 100644
--- a/test/unit/providers/oauth-provider.spec.js
+++ b/test/unit/providers/oauth-provider.spec.js
@@ -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'));
@@ -71,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() {
@@ -88,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() {
@@ -115,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');
     });
   });
 
@@ -136,76 +126,122 @@ 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) {
+        try {
+          OAuth.configure(false);
 
-    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' });
-
-        OAuth.isAuthenticated().should.be.true;
-      }));
-
-      it('should be false when there is no stored `token` cookie', inject(function(OAuth) {
-        OAuth.isAuthenticated().should.be.false;
+          should.fail();
+        } catch(e) {
+          e.should.be.an.instanceOf(TypeError);
+          e.message.should.match(/config/);
+        }
       }));
-    });
-
-    describe('getAccessToken()', function() {
-      var data = queryString.stringify({
-        client_id: defaults.clientId,
-        grant_type: 'password',
-        username: 'foo',
-        password: 'bar',
-        client_secret: defaults.clientSecret
-      });
-
-      it('should throw an error if `user` is missing', inject(function(OAuth) {
+      
+      it('should throw an error if `baseUrl` param is empty', inject(function(OAuth) {
         try {
-          OAuth.getAccessToken();
+          OAuth.configure(_.omit(defaults, 'baseUrl'));
 
           should.fail();
         } catch(e) {
           e.should.be.an.instanceOf(Error);
-          e.message.should.match(/user/);
+          e.message.should.match(/baseUrl/);
         }
       }));
 
-      it('should throw an error if `user` is empty', inject(function(OAuth) {
+      it('should throw an error if `clientId` param is empty', inject(function(OAuth) {
         try {
-          OAuth.getAccessToken({});
+          OAuth.configure(_.omit(defaults, 'clientId'));
 
           should.fail();
         } catch(e) {
           e.should.be.an.instanceOf(Error);
-          e.message.should.match(/user/);
+          e.message.should.match(/clientId/);
         }
       }));
 
-      it('should throw an error if `username` is not provided', inject(function(OAuth) {
+      it('should not throw an error if `clientSecret` param is empty', inject(function(OAuth) {
+        try{
+          OAuth.configure(_.omit(defaults, 'clientSecret'));
+          
+          should.not.fail();
+        } catch(e) {}
+      }));
+
+      it('should throw an error if `grantPath` param is empty', inject(function(OAuth) {
         try {
-          OAuth.getAccessToken({
-            password: 'foo'
-          });
+          OAuth.configure(_.defaults({ grantPath: null }, defaults));
 
           should.fail();
         } catch(e) {
           e.should.be.an.instanceOf(Error);
-          e.message.should.match(/user/);
+          e.message.should.match(/grantPath/);
         }
       }));
 
-      it('should throw an error if `password` is not provided', inject(function(OAuth) {
+      it('should remove trailing slash from `baseUrl`', inject(function(OAuth) {
+        OAuth.configure(_.defaults({
+          baseUrl: 'https://api.website.com/'
+        }, defaults));
+
+        OAuth.config.baseUrl.should.equal('https://api.website.com');
+      }));
+
+      it('should add facing slash from `grantPath`', inject(function(OAuth) {
+        OAuth.configure(_.defaults({
+          grantPath: 'oauth2/token'
+        }, defaults));
+
+        OAuth.config.grantPath.should.equal('/oauth2/token');
+      }));
+
+      it('should throw an error if `revokePath` param is empty', inject(function(OAuth) {
         try {
-          OAuth.getAccessToken({
-            username: 'foo'
-          });
+          OAuth.configure(_.defaults({ revokePath: null }, defaults));
 
           should.fail();
         } catch(e) {
           e.should.be.an.instanceOf(Error);
-          e.message.should.match(/user/);
+          e.message.should.match(/revokePath/);
         }
       }));
 
+      it('should add facing slash from `revokePath`', inject(function(OAuth) {
+        OAuth.configure(_.defaults({
+          revokePath: 'oauth2/revoke'
+        }, defaults));
+
+        OAuth.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' });
+
+        OAuth.isAuthenticated().should.be.true;
+      }));
+
+      it('should be false when there is no stored `token` cookie', inject(function(OAuth) {
+        OAuth.isAuthenticated().should.be.false;
+      }));
+    });
+
+    describe('getAccessToken()', function() {
+      var data = queryString.stringify({
+        client_id: defaults.clientId,
+        grant_type: 'password',
+        username: 'foo',
+        password: 'bar',
+        client_secret: defaults.clientSecret
+      });
+
       it('should call `queryString.stringify`', inject(function(OAuth) {
         sinon.spy(queryString, 'stringify');
 
@@ -341,8 +377,6 @@ describe('OAuthProvider', function() {
             expires_in: 3600,
             refresh_token: 'biz'
           });
-        }).catch(function() {
-          should.fail();
         });
 
         $httpBackend.flush();
@@ -363,7 +397,10 @@ 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: 'bar'
+          client_id: defaults.clientId,
+          token: 'bar',
+          token_type_hint: 'refresh_token',
+          client_secret: defaults.clientSecret
         });
         queryString.stringify.restore();
       }));
@@ -378,14 +415,20 @@ 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',
+          token_type_hint: 'access_token',
+          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,
+          token_type_hint: 'access_token',
+          client_secret: defaults.clientSecret
         });
 
         $httpBackend.expectPOST(defaults.baseUrl + defaults.revokePath, data)
@@ -407,7 +450,10 @@ 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',
+          token_type_hint: 'refresh_token',
+          client_secret: defaults.clientSecret
         });
 
         $httpBackend.expectPOST(defaults.baseUrl + defaults.revokePath, data)
