{"id":13463642,"url":"https://github.com/lynndylanhurley/ng-token-auth","last_synced_at":"2025-05-15T01:04:17.604Z","repository":{"id":43960841,"uuid":"21258726","full_name":"lynndylanhurley/ng-token-auth","owner":"lynndylanhurley","description":"Token based authentication module for angular.js.","archived":false,"fork":false,"pushed_at":"2022-12-06T18:43:58.000Z","size":1731,"stargazers_count":1791,"open_issues_count":118,"forks_count":231,"subscribers_count":56,"default_branch":"master","last_synced_at":"2025-04-13T22:39:24.248Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"CoffeeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"wtfpl","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lynndylanhurley.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-06-27T00:04:50.000Z","updated_at":"2025-03-29T12:05:55.000Z","dependencies_parsed_at":"2022-09-09T12:10:35.601Z","dependency_job_id":null,"html_url":"https://github.com/lynndylanhurley/ng-token-auth","commit_stats":null,"previous_names":[],"tags_count":50,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lynndylanhurley%2Fng-token-auth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lynndylanhurley%2Fng-token-auth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lynndylanhurley%2Fng-token-auth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lynndylanhurley%2Fng-token-auth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lynndylanhurley","download_url":"https://codeload.github.com/lynndylanhurley/ng-token-auth/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254035892,"owners_count":22003649,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T14:00:25.088Z","updated_at":"2025-05-15T01:04:17.478Z","avatar_url":"https://github.com/lynndylanhurley.png","language":"CoffeeScript","funding_links":[],"categories":["User Manager","Web Development"],"sub_categories":["Angular"],"readme":"# Simple, secure authentication for AngularJS.\n\n ![Serious Trust](https://raw.githubusercontent.com/lynndylanhurley/ng-token-auth/master/test/app/images/serious-trust.jpg \"Serious Trust\")\n\n[![Bower version](https://badge.fury.io/bo/ng-token-auth.svg)](http://badge.fury.io/bo/ng-token-auth)\n[![Build Status](https://travis-ci.org/lynndylanhurley/ng-token-auth.svg?branch=master)](https://travis-ci.org/lynndylanhurley/ng-token-auth)\n[![Test Coverage](https://codeclimate.com/github/lynndylanhurley/ng-token-auth/coverage.png)](https://codeclimate.com/github/lynndylanhurley/ng-token-auth)\n\nThis module provides the following features:\n\n* Oauth2 authentication\n* Email authentication, including:\n  * [User registration](#authsubmitregistration)\n  * [Password reset](#authrequestpasswordreset)\n  * [Account updates](#authupdateaccount)\n  * [Account deletion](#authdestroyaccount)\n* Seamless integration with the [devise token auth](https://github.com/lynndylanhurley/devise_token_auth) Rails gem\n* Extensive [event notifications](#events)\n* Allows for extensive [configuration](#configuration) to work with any API\n* Session support using cookies, localStorage, or sessionStorage\n* Tested with Chrome, Safari, Firefox and [IE8+](#internet-explorer)\n\n# [Live Demo](http://ng-token-auth-demo.herokuapp.com/)\n\nThis project comes bundled with a test app. You can run the demo locally by following [these instructions](#development), or you can use it [here in production](http://ng-token-auth-demo.herokuapp.com/).\n\n\n# Table of Contents\n\n* [About this module](#about-this-module)\n* [Installation](#installation)\n* [Configuration](#configuration)\n* [API](#api)\n  * [`$auth.authenticate`](#authauthenticate)\n  * [`$auth.validateUser`](#authvalidateuser)\n  * [`$auth.submitRegistration`](#authsubmitregistration)\n  * [`$auth.submitLogin`](#authsubmitlogin)\n  * [`$auth.signOut`](#authsignout)\n  * [`$auth.requestPasswordReset`](#authrequestpasswordreset)\n  * [`$auth.updatePassword`](#authupdatepassword)\n  * [`$auth.updateAccount`](#authupdateaccount)\n  * [`$auth.destroyAccount`](#authdestroyaccount)\n* [Events](#events)\n  * [`auth:login-success`](#authlogin-success)\n  * [`auth:login-error`](#authlogin-error)\n  * [`auth:invalid`](#authinvalid)\n  * [`auth:validation-success`](#authvalidation-success)\n  * [`auth:validation-error`](#authvalidation-error)\n  * [`auth:logout-success`](#authlogout-success)\n  * [`auth:logout-error`](#authlogout-error)\n  * [`auth:oauth-registration`](#authoauth-registration)\n  * [`auth:registration-email-success`](#authregistration-email-success)\n  * [`auth:registration-email-error`](#authregistration-email-error)\n  * [`auth:email-confirmation-success`](#authemail-confirmation-success)\n  * [`auth:email-confirmation-error`](#authemail-confirmation-error)\n  * [`auth:password-reset-request-success`](#authpassword-reset-request-success)\n  * [`auth:password-reset-request-error`](#authpassword-reset-request-error)\n  * [`auth:password-reset-confirm-success`](#authpassword-reset-confirm-success)\n  * [`auth:password-reset-confirm-error`](#authpassword-reset-confirm-error)\n  * [`auth:password-change-success`](#authpassword-change-success)\n  * [`auth:password-change-error`](#authpassword-change-error)\n  * [`auth:account-update-success`](#authaccount-update-success)\n  * [`auth:account-update-error`](#authaccount-update-error)\n  * [`auth:account-destroy-success`](#authaccount-destroy-success)\n  * [`auth:account-destroy-error`](#authaccount-destroy-error)\n  * [`auth:session-expired`](#authsession-expired)\n* [Using alternate response formats](#using-alternate-response-formats)\n* [Multiple user types](#using-multiple-user-types)\n* [File uploads](#file-uploads)\n* [Conceptual Diagrams](#conceptual)\n  * [OAuth2 Authentication](#oauth2-authentication-flow)\n  * [Token Validation](#token-validation-flow)\n  * [Email Registration](#email-registration-flow)\n  * [Email Sign In](#email-sign-in-flow)\n  * [Password Reset Request](#password-reset-flow)\n* [Notes on Token Management](#about-token-management)\n* [Notes on Batch Requests](#about-batch-requests)\n* [Notes on Token Formatting](#identifying-users-on-the-server)\n* [iOS Caveats](#ios)\n* [Internet Explorer Caveats](#internet-explorer)\n* [FAQ](#faq)\n* [Development](#development)\n* [Contribution Guidelines](#contributing)\n* [Alteratives to This Module](#alternatives)\n* [Callouts](#callouts)\n\n# About this module\n\nThis module relies on [token based authentication](http://stackoverflow.com/questions/1592534/what-is-token-based-authentication). This requires coordination between the client and the server. [Diagrams](#conceptual) are included to illustrate this relationship.\n\nThis module was designed to work out of the box with the outstanding [devise token auth](https://github.com/lynndylanhurley/devise_token_auth) gem, but it's seen use in other environments as well ([go](https://golang.org/), [gorm](https://github.com/jinzhu/gorm) and [gomniauth](https://github.com/stretchr/gomniauth) for example).\n\nNot using AngularJS? Use [jToker](https://github.com/lynndylanhurley/j-toker) (jQuery) or [Angular2-Token](https://github.com/neroniaky/angular2-token) (Angular2) instead!\n\n**About security**: [read here](http://stackoverflow.com/questions/18605294/is-devises-token-authenticatable-secure) for more information on securing your token auth system. The [devise token auth](https://github.com/lynndylanhurley/devise_token_auth#security) gem has adequate security measures in place, and the gem works seamlessly with this module.\n\n\n\n\n# Installation\n\n\n* Download this module and its dependencies:\u2028\n  ~~~bash\n  # from the terminal at the root of your project\n  bower install ng-token-auth --save\n  ~~~\n\n* Ensure that [angularjs](https://github.com/angular/angular.js), [angular-cookie](https://github.com/ivpusic/angular-cookie), and ng-token-auth are included on your page:\u2028\n  ~~~html\n  \u003c!-- in your index.html file --\u003e\n  \u003cscript src=\"/js/angular/angular.js\"\u003e\u003c/script\u003e\n  \u003cscript src=\"/js/angular-cookie/angular-cookie.js\"\u003e\u003c/script\u003e\n  \u003cscript src=\"/js/ng-token-auth/dist/ng-token-auth.js\"\u003e\u003c/script\u003e\n  ~~~\n\n* Include `ng-token-auth` in your module's dependencies:\n  ~~~javascript\n  // in your js app's module definition\n  angular.module('myApp', ['ng-token-auth'])\n  ~~~\n\n## Configuration\n\nThe `$authProvider` is available for injection during the app's configuration phase. Use `$authProvider.configure` to configure the module for use with your server.\n\nThe following settings correspond to the paths that are available when using the [devise token auth](https://github.com/lynndylanhurley/devise_token_auth#usage) gem for Rails. If you're using this gem, you will only need to set the `apiUrl` option.\n\n##### Example configuration when using devise token auth\n~~~javascript\nangular.module('myApp', ['ng-token-auth'])\n\t.config(function($authProvider) {\n\t\t$authProvider.configure({\n\t\t\tapiUrl: 'http://api.example.com'\n\t\t});\n\t});\n~~~\n\n##### Complete config example\n~~~javascript\nangular.module('myApp', ['ng-token-auth'])\n\n  .config(function($authProvider) {\n\n    // the following shows the default values. values passed to this method\n    // will extend the defaults using angular.extend\n\n    $authProvider.configure({\n      apiUrl:                  '/api',\n      tokenValidationPath:     '/auth/validate_token',\n      signOutUrl:              '/auth/sign_out',\n      emailRegistrationPath:   '/auth',\n      accountUpdatePath:       '/auth',\n      accountDeletePath:       '/auth',\n      confirmationSuccessUrl:  window.location.href,\n      passwordResetPath:       '/auth/password',\n      passwordUpdatePath:      '/auth/password',\n      passwordResetSuccessUrl: window.location.href,\n      emailSignInPath:         '/auth/sign_in',\n      storage:                 'cookies',\n      forceValidateToken:      false,\n      validateOnPageLoad:      true,\n      proxyIf:                 function() { return false; },\n      proxyUrl:                '/proxy',\n      omniauthWindowType:      'sameWindow',\n      authProviderPaths: {\n        github:   '/auth/github',\n        facebook: '/auth/facebook',\n        google:   '/auth/google',\n        apple:    '/auth/apple'\n      },\n      tokenFormat: {\n        \"access-token\": \"{{ token }}\",\n        \"token-type\":   \"Bearer\",\n        \"client\":       \"{{ clientId }}\",\n        \"expiry\":       \"{{ expiry }}\",\n        \"uid\":          \"{{ uid }}\"\n      },\n      cookieOps: {\n        path: \"/\",\n        expires: 9999,\n        expirationUnit: 'days',\n        secure: false,\n        domain: 'domain.com'\n      },\n      createPopup: function(url) {\n        return window.open(url, '_blank', 'closebuttoncaption=Cancel');\n      },\n      parseExpiry: function(headers) {\n        // convert from UTC ruby (seconds) to UTC js (milliseconds)\n        return (parseInt(headers['expiry']) * 1000) || null;\n      },\n      handleLoginResponse: function(response) {\n        return response.data;\n      },\n      handleAccountUpdateResponse: function(response) {\n        return response.data;\n      },\n      handleTokenValidationResponse: function(response) {\n        return response.data;\n      }\n    });\n  });\n~~~\n\n##### Config options:\n| param | description |\n|---|---|\n| **apiUrl** | the base route to your api. Each of the following paths will be relative to this URL. Authentication headers will only be added to requests with this value as the base URL. |\n| **authProviderPaths** | an object containing paths to auth endpoints. keys are names of the providers, values are their auth paths relative to the `apiUrl`. [Read more](#oauth2-authentication-flow). |\n| **tokenValidationPath** | relative path to validate authentication tokens. [Read more](#token-validation-flow). |\n| **signOutUrl** | relative path to sign user out. this will destroy the user's token both server-side and client-side. |\n| **emailRegistrationPath** | path for submitting new email registrations. [Read more](#email-registration-flow). |\n| **accountUpdatePath** | path for submitting account update requests. [Read more](#authupdateaccount). |\n| **accountDeletePath** | path for submitting account deletion requests. [Read more](#authdestroyaccount). |\n| **confirmationSuccessUrl** | the url to which the API should redirect after users visit the link contained in email-registration emails. [Read more](#email-registration-flow). |\n| **emailSignInPath** | path for signing in using email credentials. [Read more](#email-sign-in-flow) |\n| **passwordResetPath** | path for requesting password reset emails. [Read more](#password-reset-flow). |\n| **passwordUpdatePath** | path for submitting new passwords for authenticated users. [Read more](#password-reset-flow) |\n| **passwordResetSuccessUrl** | the URL to which the API should redirect after users visit the links contained in password-reset emails. [Read more](#password-reset-flow). |\n| **storage** | the method used to persist tokens between sessions. cookies are used by default, but `window.localStorage` and `window.sessionStorage` can be used as well. A custom object can also be used. Allowed strings are `cookies`, `localStorage`, and `sessionStorage`, otherwise an object implementing the interface defined below|\n| **forceValidateToken** | if this flag is set, the API's token validation will be called even if the auth token is not saved in `storage`. This can be useful for implementing a single sign-on (SSO) system.|\n| **proxyIf** | older browsers have trouble with CORS ([read more](#internet-explorer)). pass a method here to determine whether or not a proxy should be used. example: `function() { return !Modernizr.cors }` |\n| **proxyUrl** | proxy url if proxy is to be used |\n| **tokenFormat** | a template for authentication tokens. the template will be provided a context with the following params:\u003cbr\u003e\u003cul\u003e\u003cli\u003etoken\u003c/li\u003e\u003cli\u003eclientId\u003c/li\u003e\u003cli\u003euid\u003c/li\u003e\u003cli\u003eexpiry\u003c/li\u003e\u003c/ul\u003eDefaults to the [RFC 6750 Bearer Token](http://tools.ietf.org/html/rfc6750) format. [Read more](#using-alternate-header-formats). |\n| **createPopup** | a function that will open OmniAuth window by `url`. [Read more](#example-newwindow-redirect_uri-destination). |\n| **parseExpiry** | a function that will return the token's expiry from the current headers. Returns null if no headers or expiry are found. [Read more](#using-alternate-header-formats). |\n| **handleLoginResponse** | a function that will identify and return the current user's info (id, username, etc.) in the response of a successful login request. [Read more](#using-alternate-response-formats). |\n| **handleAccountUpdateResponse** | a function that will identify and return the current user's info (id, username, etc.) in the response of a successful account update request. [Read more](#using-alternate-response-formats). |\n| **handleTokenValidationResponse** | a function that will identify and return the current user's info (id, username, etc.) in the response of a successful token validation request. [Read more](#using-alternate-response-formats). |\n| **omniauthWindowType** | Dictates the methodolgy of the OAuth login flow. One of: `sameWindow` (default), `newWindow`, `inAppBrowser` [Read more](#oauth2-authentication-flow). |\n\n#### Custom Storage Object\nMust implement the following interface:\n```javascript\n{\n  function persistData(key, val) {}\n  function retrieveData(key) {}\n  function deleteData(key) {}\n}\n```\n\n# Usage\n\n## API\n\nThe `$auth` module is available for dependency injection during your app's run phase (for controllers, directives, filters, etc.). Each API method returns a [$q deferred promise](https://docs.angularjs.org/api/ng/service/$q) that will be resolved on success,\n\n\n### $auth.authenticate\nInitiate an OAuth2 authentication. This method accepts 2 arguments:\n\n* **provider**: a string that is also the name of the target provider service. For example, to authenticate using github:\u2028\n  ~~~javascript\n  $auth.authenticate('github')\n  ~~~\n\n* **options**: _(optional)_ an object containing the following params:\n  *  **params**: additional params to be passed to the OAuth provider. For example, to pass the user's favorite color on sign up:\n\n     ~~~javascript\n     $auth.authenticate('github', {params: {favorite_color: 'green'}})\n     ~~~\n\nThis method is also added to the `$rootScope` for use in templates. [Read more](#oauth2-authentication-flow).\n\nThis method emits the following events:\n\n* [`auth:login-success`](#authlogin-success)\n* [`auth:login-error`](#authlogin-error)\n* [`auth:oauth-registration`](#authoauth-registration)\n\n#### Example use in a controller\n~~~javascript\nangular.module('ngTokenAuthTestApp')\n  .controller('IndexCtrl', function($scope, $auth) {\n    $scope.handleBtnClick = function() {\n      $auth.authenticate('github')\n        .then(function(resp) {\n          // handle success\n        })\n        .catch(function(resp) {\n          // handle errors\n        });\n    };\n  });\n~~~\n\n#### Example use in a template\n~~~html\n\u003cbutton ng-click=\"authenticate('github')\"\u003e\n  Sign in with Github\n\u003c/button\u003e\n~~~\n\n### $auth.validateUser\nThis method returns a promise that will resolve if a user's auth token exists and is valid. This method does not accept any arguments. [Read more](#token-validation-flow)\n\nThis method automatically is called on page load during the app's run phase so that returning users will not need to manually re-authenticate themselves.\n\nYou can disable this automatic check by setting `validateOnPageLoad: false` in the configuration phase.\n\nThis method will broadcast the following events:\n\n* On page load:\n  * [`auth:validation-success`](#authvalidation-success)\n  * [`auth:validation-error`](#authvalidation-error)\n  * [`auth:session-expired`](#authsession-expired)\n* When visiting email confirmation links:\n  * [`auth:email-confirmation-success`](#authemail-confirmation-success)\n  * [`auth:email-confirmation-error`](#authemail-confirmation-error)\n* When visiting password reset confirmation links:\n  * [`auth:password-reset-confirm-success`](#authpassword-reset-confirm-success)\n  * [`auth:password-reset-confirm-error`](#authpassword-reset-confirm-error)\n\nThe promise returned by this method can be used to prevent users from viewing certain pages when using [angular ui router](https://github.com/angular-ui/ui-router) [resolvers](http://angular-ui.github.io/ui-router/site/#/api/ui.router.util.$resolve).\n\n#### Example using angular ui router\n\n~~~coffeescript\nangular.module('myApp', [\n  'ui.router',\n  'ng-token-auth'\n])\n  .config(function($stateProvider) {\n    $stateProvider\n      // this state will be visible to everyone\n      .state('index', {\n        url: '/',\n        templateUrl: 'index.html',\n        controller: 'IndexCtrl'\n      })\n\n      // only authenticated users will be able to see routes that are\n      // children of this state\n      .state('admin', {\n        url: '/admin',\n        abstract: true,\n        template: '\u003cui-view/\u003e',\n        resolve: {\n          auth: function($auth) {\n            return $auth.validateUser();\n          }\n        }\n      })\n\n      // this route will only be available to authenticated users\n      .state('admin.dashboard', {\n        url: '/dash',\n        templateUrl: '/admin/dash.html',\n        controller: 'AdminDashCtrl'\n      });\n  });\n~~~\n\nThis example shows how to implement access control on the client side, however access to restricted information should be limited on the server as well (using something like [pundit](https://github.com/elabs/pundit) if you're using Rails).\n\n### $auth.submitRegistration\nUsers can register by email using this method. [Read more](#email-registration-flow). Accepts an object with the following params:\n\n* **email**\n* **password**\n* **password_confirmation**\n\nThis method broadcasts the following events:\n\n* [`auth:registration-email-success`](#authregistration-email-success)\n* [`auth:registration-email-error`](#authregistration-email-error)\n\n##### Example use in a controller:\n~~~javascript\nangular.module('ngTokenAuthTestApp')\n  .controller('IndexCtrl', function($scope, $auth) {\n    $scope.handleRegBtnClick = function() {\n      $auth.submitRegistration($scope.registrationForm)\n        .then(function(resp) {\n          // handle success response\n        })\n        .catch(function(resp) {\n          // handle error response\n        });\n    };\n  });\n~~~\n\n##### Example use in a template:\n\n~~~html\n\u003cform ng-submit=\"submitRegistration(registrationForm)\" role=\"form\" ng-init=\"registrationForm = {}\"\u003e\n  \u003cdiv class=\"form-group\"\u003e\n    \u003clabel\u003eemail\u003c/label\u003e\n    \u003cinput type=\"email\" name=\"email\" ng-model=\"registrationForm.email\" required=\"required\" class=\"form-control\"/\u003e\n  \u003c/div\u003e\n\n  \u003cdiv class=\"form-group\"\u003e\n    \u003clabel\u003epassword\u003c/label\u003e\n    \u003cinput type=\"password\" name=\"password\" ng-model=\"registrationForm.password\" required=\"required\" class=\"form-control\"/\u003e\n  \u003c/div\u003e\n\n  \u003cdiv class=\"form-group\"\u003e\n    \u003clabel\u003epassword confirmation\u003c/label\u003e\n    \u003cinput type=\"password\" name=\"password_confirmation\" ng-model=\"registrationForm.password_confirmation\" required=\"required\" class=\"form-control\"/\u003e\n  \u003c/div\u003e\n\n  \u003cbutton type=\"submit\" class=\"btn btn-primary btn-lg\"\u003eRegister\u003c/button\u003e\n\u003c/form\u003e\n~~~\n\n### $auth.submitLogin\nAuthenticate a user that registered via email. [Read more](#email-sign-in-flow). Accepts an object with the following params:\n\n* **email**\n* **password**\n\nThis method broadcasts the following events:\n\n* [`auth:login-success`](#authlogin-success)\n* [`auth:login-error`](#authlogin-error)\n\n##### Example use in a controller:\n~~~javascript\nangular.module('ngTokenAuthTestApp')\n  .controller('IndexCtrl', function($scope, $auth) {\n    $scope.handleLoginBtnClick = function() {\n      $auth.submitLogin($scope.loginForm)\n        .then(function(resp) {\n          // handle success response\n        })\n        .catch(function(resp) {\n          // handle error response\n        });\n    };\n  });\n~~~\n\n##### Example use in a template:\n~~~html\n\u003cform ng-submit=\"submitLogin(loginForm)\" role=\"form\" ng-init=\"loginForm = {}\"\u003e\n  \u003cdiv class=\"form-group\"\u003e\n    \u003clabel\u003eemail\u003c/label\u003e\n    \u003cinput type=\"email\" name=\"email\" ng-model=\"loginForm.email\" required=\"required\" class=\"form-control\"/\u003e\n  \u003c/div\u003e\n\n  \u003cdiv class=\"form-group\"\u003e\n    \u003clabel\u003epassword\u003c/label\u003e\n    \u003cinput type=\"password\" name=\"password\" ng-model=\"loginForm.password\" required=\"required\" class=\"form-control\"/\u003e\n  \u003c/div\u003e\n\n  \u003cbutton type=\"submit\" class=\"btn btn-primary btn-lg\"\u003eSign in\u003c/button\u003e\n\u003c/form\u003e\n~~~\n\n### $auth.signOut\nDe-authenticate a user. This method does not take any arguments. This method will change the user's `auth_token` server-side, and it will destroy the `uid` and `auth_token` cookies saved client-side.\n\nThis method broadcasts the following events:\n\n* [`auth:logout-success`](#authlogout-success)\n* [`auth:logout-error`](#authlogout-error)\n\n##### Example use in a controller:\n~~~javascript\nangular.module('ngTokenAuthTestApp')\n  .controller('IndexCtrl', function($scope, $auth) {\n    $scope.handleSignOutBtnClick = function() {\n      $auth.signOut()\n        .then(function(resp) {\n          // handle success response\n        })\n        .catch(function(resp) {\n          // handle error response\n        });\n    };\n  });\n~~~\n\n##### Example use in a template:\n~~~html\n\u003cbutton class=\"btn btn-primary btn-lg\" ng-click='signOut()'\u003eSign out\u003c/button\u003e\n~~~\n\n### $auth.requestPasswordReset\nSend password reset instructions to a user. This only applies to users that have registered using email. This method accepts an object with the following param:\n\n* **email**\n\nThis method broadcasts the following events:\n\n* [`auth:password-reset-request-success`](#authpassword-reset-request-success)\n* [`auth:password-reset-request-error`](#authpassword-reset-request-error)\n\n##### Example use in a controller:\n~~~javascript\nangular.module('ngTokenAuthTestApp')\n  .controller('IndexCtrl', function($scope, $auth) {\n    $scope.handlePwdResetBtnClick = function() {\n      $auth.requestPasswordReset($scope.pwdResetForm)\n        .then(function(resp) {\n          // handle success response\n        })\n        .catch(function(resp) {\n          // handle error response\n        });\n    };\n  });\n~~~\n\n##### Example use in a template:\n~~~html\n\u003cform ng-submit=\"requestPasswordReset(passwordResetForm)\" role=\"form\" ng-init=\"passwordResetForm = {}\"\u003e\n  \u003cdiv class=\"form-group\"\u003e\n    \u003clabel\u003eemail\u003c/label\u003e\n    \u003cinput type=\"email\" name=\"email\" ng-model=\"passwordResetForm.email\" required=\"required\" class=\"form-control\"/\u003e\n  \u003c/div\u003e\n\n  \u003cbutton type=\"submit\" class=\"btn btn-primary btn-lg\"\u003eRequest password reset\u003c/button\u003e\n\u003c/form\u003e\n~~~\n\n### $auth.updatePassword\nChange an authenticated user's password. This only applies to users that have registered using email. This method accepts an object with the following params:\n\n* **current_password**\n* **password**\n* **password_confirmation**\n\nThe `password` and `password_confirmation` params must match. `current_password` param is optional - depends on the server configuration. It might be checked before password update.\n\nThis method broadcasts the following events:\n\n* [`auth:password-change-success`](#authpassword-change-success)\n* [`auth:password-change-error`](#authpassword-change-error)\n\n##### Example use in a controller:\n~~~javascript\nangular.module('ngTokenAuthTestApp')\n  .controller('IndexCtrl', function($scope, $auth) {\n    $scope.handleUpdatePasswordBtnClick = function() {\n      $auth.updatePassword($scope.updatePasswordForm)\n        .then(function(resp) {\n          // handle success response\n        })\n        .catch(function(resp) {\n          // handle error response\n        });\n    };\n  });\n~~~\n\n##### Example use in a template\n~~~html\n\u003cform ng-submit=\"updatePassword(changePasswordForm)\" role=\"form\" ng-init=\"changePasswordForm = {}\"\u003e\n  \u003cdiv class=\"form-group\"\u003e\n    \u003clabel\u003epassword\u003c/label\u003e\n    \u003cinput type=\"password\" name=\"password\" ng-model=\"changePasswordForm.password\" required=\"required\" class=\"form-control\"\u003e\n  \u003c/div\u003e\n\n  \u003cdiv class=\"form-group\"\u003e\n    \u003clabel\u003epassword confirmation\u003c/label\u003e\n    \u003cinput type=\"password\" name=\"password_confirmation\" ng-model=\"changePasswordForm.password_confirmation\" required=\"required\" class=\"form-control\"\u003e\n  \u003c/div\u003e\n\n  \u003cbutton type=\"submit\"\u003eChange your password\u003c/button\u003e\n\u003c/form\u003e\n~~~\n\n### $auth.updateAccount\nChange an authenticated user's account info. This method accepts an object that contains valid params for your API's user model. When `password` and `password_confirmation` params are supported it updates the password as well. Depending on the server configuration `current_password` param might be needed. The following shows how to update a user's `zodiac_sign` param:\n\n##### Example use in a template:\n~~~html\n\u003cform ng-submit=\"updateAccount(updateAccountForm)\" role=\"form\" ng-init=\"updateAccountForm = {zodiac_sign: null}\"\u003e\n  \u003cfieldset ng-disabled=\"!user.signedIn\"\u003e\n    \u003cdiv\u003e\n      \u003clabel\u003ezodiac sign\u003c/label\u003e\n      \u003cinput type=\"text\" name=\"text\" ng-model=\"updateAccountForm.zodiac_sign\"\u003e\n    \u003c/div\u003e\n\n    \u003cbutton type=\"submit\"\u003eUpdate your zodiac sign\u003c/button\u003e\n  \u003c/fieldset\u003e\n\u003c/form\u003e\n~~~\n\nThis method broadcasts the following events:\n\n* [`auth:account-update-success`](#authaccount-update-success)\n* [`auth:account-update-error`](#authaccount-update-error)\n\n##### Example use in a controller:\n~~~javascript\nangular.module('ngTokenAuthTestApp')\n  .controller('IndexCtrl', function($scope, $auth) {\n    $scope.handleUpdateAccountBtnClick = function() {\n      $auth.updateAccount($scope.updateAccountForm)\n        .then(function(resp) {\n          // handle success response\n        })\n        .catch(function(resp) {\n          // handle error response\n        });\n    };\n  });\n~~~\n\n\n\n### $auth.destroyAccount\nDestroy a logged in user's account. This method does not accept any params.\n\nThis method broadcasts the following events:\n\n* [`auth:account-destroy-success`](#authaccount-destroy-success)\n* [`auth:account-destroy-error`](#authaccount-destroy-error)\n\n##### Example use in a controller:\n~~~javascript\nangular.module('ngTokenAuthTestApp')\n  .controller('IndexCtrl', function($scope, $auth) {\n    $scope.handleDestroyAccountBtnClick = function() {\n      $auth.destroyAccount()\n        .then(function(resp) {\n          // handle success response\n        })\n        .catch(function(resp) {\n          // handle error response\n        });\n    };\n  });\n~~~\n\n##### Example use in a template:\n~~~html\n\u003cbutton ng-click=\"destroyAccount()\" ng-class=\"{disabled: !user.signedIn}\"\u003e\n  Close my account\n\u003c/button\u003e\n~~~\n\n## Events\n\nThis module broadcasts events after the success or failure of each API method. Using these events to build your app can result in more flexibility while reducing code spaghetti.\n\nFor example, any template can initiate an authentication, and any controller can subscribe to the `auth:login-success` event to provide success notifications, redirects, etc.\n\n### auth:login-success\nBroadcast after successful user authentication. Event message contains the user object. This event is broadcast by the following methods:\n\n* [`$auth.submitLogin`](#authsubmitlogin)\n* [`$auth.authenticate`](#authauthenticate)\n\n##### Example:\n~~~javascript\n$rootScope.$on('auth:login-success', function(ev, user) {\n    alert('Welcome ', user.email);\n});\n~~~\n\n### auth:login-error\nBroadcast after user fails authentication. This event is broadcast by the following methods:\n\n* [`$auth.submitLogin`](#authsubmitlogin)\n* [`$auth.authenticate`](#authauthenticate)\n\n##### Example:\n~~~javascript\n$rootScope.$on('auth:login-error', function(ev, reason) {\n    alert('auth failed because', reason.errors[0]);\n});\n~~~\n\n### auth:oauth-registration\nBroadcast when the message posted after an oauth login as the new_record attribute set to `true`. This event is broadcast by the following methods:\n\n* [`$auth.authenticate`](#authauthenticate)\n\n##### Example:\n~~~javascript\n$rootScope.$on('auth:oauth-registration', function(ev, user) {\n    alert('new user registered through oauth:' + user.email);\n});\n~~~\n\n### auth:validation-success\nBroadcast when a user's token is successfully verified using the [`$auth.validateUser`](#authvalidateuser) method.\n\n### auth:validation-error\nBroadcast when the [`$auth.validateUser`](#authvalidateuser) method fails (network error, etc). Note that this does not indicate an invalid token, but an error in the validation process. See the [`auth:invalid`](#authinvalid) event for invalid token notification.\n\n### auth:invalid\nBroadcast when a user's token fails validation using the [`$auth.validateUser`](#authvalidateuser) method. This is different from the [`auth:validation-error`](#authvalidation-error) in that it indicates an invalid token, whereas the [`auth:validation-error`](#authvalidation-error) event indicates an error in the validation process.\n\n### auth:logout-success\nBroadcast after user is successfully logged out using the [`$auth.signOut`](#authsignout) method. This event does not contain a message.\n\n##### Example:\n~~~javascript\n$rootScope.$on('auth:logout-success', function(ev) {\n    alert('goodbye');\n});\n~~~\n\n### auth:logout-error\nBroadcast after failed logout attempts using the [`$auth.signOut`](#authsignout) method. Message contains the failed logout response.\n\n##### Example:\n~~~javascript\n$rootScope.$on('auth:logout-error', function(ev, reason) {\n    alert('logout failed because ' + reason.errors[0]);\n});\n~~~\n\n### auth:registration-email-success\nBroadcast after email registration requests complete successfully using the [`$auth.submitRegistration`](#authsubmitregistration) method. Message contains the params that were sent to the server.\n\n##### Example:\n~~~javascript\n$scope.$on('auth:registration-email-success', function(ev, message) {\n    alert(\"A registration email was sent to \" + message.email);\n});\n~~~\n\n### auth:registration-email-error\nBroadcast after failed email registration requests using the `$auth.submitRegistration` method. Message contains the error response.\n\nThis event is broadcast by the [`$auth.submitRegistration`](#authsubmitregistration) method.\n\n##### Example:\n~~~javascript\n$scope.$on('auth:registration-email-error', function(ev, reason) {\n    alert(\"Registration failed: \" + reason.errors[0]);\n});\n~~~\n\n### auth:email-confirmation-success\nBroadcast when users arrive from links contained in password-reset emails. This can be used to trigger \"welcome\" notifications to new users.\n\nThis event is broadcast by the [`$auth.validateUser`](#authvalidateuser) method.\n\n##### Example:\n~~~javascript\n$scope.$on('auth:email-confirmation-success', function(ev, user) {\n    alert(\"Welcome, \"+user.email+\". Your account has been verified.\");\n});\n~~~\n\n### auth:email-confirmation-error\nBroadcast when a user arrives from a link contained in a confirmation email, but the confirmation token fails to validate.\n\nThis event is broadcast by the [`$auth.validateUser`](#authvalidateuser) method.\n\n##### Example:\n~~~javascript\n$scope.$on('auth:email-confirmation-error', function(ev, reason) {\n    alert(\"There was an error with your registration.\");\n});\n~~~\n\n### auth:password-reset-request-success\nBroadcast when users successfully submit the password reset form using the [`$auth.requestPasswordReset`](#authrequestpasswordreset) method.\n\n##### Password reset request example:\n~~~javascript\n$scope.$on('auth:password-reset-request-success', function(ev, data) {\n    alert(\"Password reset instructions were sent to \" + data.email);\n});\n~~~\n\n### auth:password-reset-request-error\nBroadcast after failed requests using the [`$auth.requestPasswordReset`](#authrequestpasswordreset) method. Message contains the error response.\n\n##### Example:\n~~~javascript\n$scope.$on('auth:password-reset-request-error', function(ev, resp) {\n    alert(\"Password reset request failed: \" + resp.errors[0]);\n});\n~~~\n\n### auth:password-reset-confirm-success\nBroadcast when users arrive from links contained in password reset emails. This will be the signal for your app to prompt the user to reset their password. [Read more](#password-reset-flow).\n\nThis event is broadcast by the [`$auth.validateUser`](#authvalidateuser) method.\n\nThe following example demonstrates one way to handle an `auth:password-reset-confirm-success` event. This example assumes that [angular ui-router](https://github.com/angular-ui/ui-router) is used for routing, and that there is a state called `account.password-reset` that contains instructions for changing the user's password.\n\n##### Password reset prompt example:\n~~~javascript\nangular.module('myApp')\n  .run(function($rootScope, $state) {\n    $rootScope.$on('auth:password-reset-confirm-success', function() {\n      $state.go('account.password-reset');\n    });\n  });\n~~~\n\nYou could also choose to display a modal, or you can ignore the event completely. What you do with the `auth:password-reset-confirm-success` event is entirely your choice.\n\n### auth:password-reset-confirm-error\nBroadcast when users arrive from links contained in password reset emails, but the server fails to validate their password reset token.\n\nThis event is broadcast by the [`$auth.validateUser`](#authvalidateuser) method.\n\n##### Example:\n~~~javascript\n$scope.$on('auth:password-reset-confirm-error', function(ev, reason) {\n    alert(\"Unable to verify your account. Please try again.\");\n});\n~~~\n\n### auth:password-change-success\nBroadcast when users successfully update their password using the [`$auth.updatePassword`](#authupdatepassword) method. [Read more](#password-reset-flow).\n\n##### Example:\n~~~javascript\n$scope.$on('auth:password-change-success', function(ev) {\n  alert(\"Your password has been successfully updated!\");\n});\n~~~\n\n### auth:password-change-error\nBroadcast when requests resulting from the [`$auth.updatePassword`](#authupdatepassword) method fail. [Read more](#password-reset-flow).\n\n##### Example:\n~~~javascript\n$scope.$on('auth:password-change-error', function(ev, reason) {\n  alert(\"Registration failed: \" + reason.errors[0]);\n});\n~~~\n\n### auth:account-update-success\nBroadcast when users successfully update their account info using the [`$auth.updateAccount`](#authupdateaccount) method.\n\n##### Example:\n~~~javascript\n$scope.$on('auth:account-update-success', function(ev) {\n  alert(\"Your account has been successfully updated!\");\n});\n~~~\n\n### auth:account-update-error\nBroadcast when requests resulting from the [`$auth.updateAccount`](#authupdateaccount) method fail.\n\n##### Example:\n~~~javascript\n$scope.$on('auth:account-update-error', function(ev, reason) {\n  alert(\"Registration failed: \" + reason.errors[0]);\n});\n~~~\n\n### auth:account-destroy-success\nBroadcast when users successfully delete their account info using the [`$auth.destroyAccount`](#authdestroyaccount) method.\n\n##### Example:\n~~~javascript\n$scope.$on('auth:account-destroy-success', function(ev) {\n  alert(\"Your account has been successfully destroyed!\");\n});\n~~~\n\n### auth:account-destroy-error\nBroadcast when requests resulting from the [`$auth.destroyAccount`](#authdestroyaccount) method fail.\n\n##### Example:\n~~~javascript\n$scope.$on('auth:account-destroy-error', function(ev, reason) {\n  alert(\"Account deletion failed: \" + reason.errors[0]);\n});\n~~~\n\n### auth:session-expired\nBroadcast when the [`$auth.validateUser`](#authvalidateuser) method fails because a user's token has expired.\n\n##### Example:\n~~~javascript\n$scope.$on('auth:session-expired', function(ev) {\n  alert('Session has expired');\n});\n~~~\n\n## Using alternate header formats\n\nBy default, this module (and the [devise token auth](https://github.com/lynndylanhurley/devise_token_auth) gem) use the [RFC 6750 Bearer Token](http://tools.ietf.org/html/rfc6750) format. You can customize this using the `tokenFormat` and `parseExpiry` config params.\n\nThe following example will provide support for this header format:\n~~~\nAuthorization: token={{ token }} expiry={{ expiry }} uid={{ uid }}\n~~~\n\n##### Example with alternate token format**:\n~~~javascript\nangular.module('myApp', ['ng-token-auth'])\n  .config(function($authProvider) {\n    $authProvider.configure({\n      apiUrl: 'http://api.example.com'\n\n      // provide the header template\n      tokenFormat: {\n        \"Authorization\": \"token={{ token }} expiry={{ expiry }} uid={{ uid }}\"\n      },\n\n      // parse the expiry from the 'Authorization' param\n      parseExpiry: function(headers) {\n        return (parseInt(headers['Authorization'].match(/expiry=([^ ]+) /)[1], 10)) || null\n\n      }\n    });\n  });\n~~~\n\nThe `tokenFormat` param accepts an object as an argument. Each param of the object will be added as an auth header to requests made to the API url provided. Each value of the object will be interpolated using the following context:\n\n* **token**: the user's valid access token\n* **uid**: the user's id\n* **expiry**: the expiration date of the token\n* **clientId**: the id of the current device\n\nThe `parseExpiry` param accepts a method that will be used to parse the expiration date from the auth headers. The current valid headers will be provided as an argument.\n\n### Using alternate response formats\n\nBy default, this module expects user info (`id`, `name`, etc.) to be contained within the `data` param of successful login / token-validation responses. The following example shows an example of an expected response:\n\n##### Expected API login response example\n~~~\nHTTP/1.1 200 OK\nContent-Type: application/json;charset=UTF-8\n{\n  \"data\": {\n    \"id\":\"123\",\n    \"name\": \"Slemp Diggler\",\n    \"etc\": \"...\"\n  }\n}\n~~~\n\nThe above example follows the format used by the [devise token gem](https://github.com/lynndylanhurley/devise_token_auth). This format requires no additional configuration.\n\nBut not all APIs use this format. Some APIs simply return the serialized user model with no container params:\n\n##### Alternate API login response example\n~~~\nHTTP/1.1 200 OK\nContent-Type: application/json;charset=UTF-8\n{\n  \"id\":\"123\",\n  \"name\": \"Slemp Diggler\",\n  \"etc\": \"...\"\n}\n~~~\n\nFunctions can be provided to identify and return the relevant user data from successful authentication responses. The above example response can be handled with the following configuration:\n\n##### Example alternate login response handler format:\n\n~~~javascript\nangular.module('myApp', ['ng-token-auth'])\n  .config(function($authProvider) {\n    $authProvider.configure({\n      apiUrl: 'http://api.example.com'\n\n      handleLoginResponse: function(response) {\n        return response;\n      },\n\n      handleAccountUpdateResponse: function(response) {\n        return response;\n      },\n\n      handleTokenValidationResponse: function(response) {\n        return response;\n      }\n    })\n  });\n~~~\n\n## Using multiple user types\n\n### [View Live Multi-User Demo](http://ng-token-auth-demo.herokuapp.com/multi-user)\n\nThis module allows for the use of multiple user authentication configurations. The following example assumes that the API supports two user classes, `User` an `EvilUser`. The following examples assume that `User` authentication routes are mounted at `/auth`, and the `EvilUser` authentication routes are mounted at `evil_user_auth`.\n\n### Multiple user type configuration\n\nTo set up an application for multiple users, pass an array of configuration objects to the [`$auth.configure`](#configure) method. The keys of these configuration objects (`default` and `evilUser` in this example) will be used to select the desired configuration for authentication.\n\n##### Multiple user configuration example\n~~~javascript\n$authProvider.configure([\n  {\n    default: {\n      apiUrl:  CONFIG.apiUrl,\n      proxyIf: function() { window.isOldIE() },\n      authProviderPaths: {\n        github:    '/auth/github',\n        facebook:  '/auth/facebook',\n        google:    '/auth/google_oauth2',\n        apple:     '/auth/apple'\n      }\n    }\n  }, {\n    evilUser: {\n      apiUrl:                CONFIG.apiUrl,\n      proxyIf:               function() { window.isOldIE() },\n      signOutUrl:            '/evil_user_auth/sign_out',\n      emailSignInPath:       '/evil_user_auth/sign_in',\n      emailRegistrationPath: '/evil_user_auth',\n      accountUpdatePath:     '/evil_user_auth',\n      accountDeletePath:     '/evil_user_auth',\n      passwordResetPath:     '/evil_user_auth/password',\n      passwordUpdatePath:    '/evil_user_auth/password',\n      tokenValidationPath:   '/evil_user_auth/validate_token',\n      authProviderPaths: {\n        github:    '/evil_user_auth/github',\n        facebook:  '/evil_user_auth/facebook',\n        google:    '/evil_user_auth/google_oauth2',\n        apple:     '/evil_user_auth/apple'\n      }\n    }\n  }\n]);\n~~~\n\n### Multiple user type usage\n\nThe following API methods accept a `config` option that can be used to specify the desired configuration.\n\n* [`$auth.authenticate`](#authauthenticate)\n* [`$auth.validateUser`](#authvalidateuser)\n* [`$auth.submitRegistration`](#authsubmitregistration)\n* [`$auth.submitLogin`](#authsubmitlogin)\n* [`$auth.requestPasswordReset`](#authrequestpasswordreset)\n\nAll other methods (`$auth.signOut`, `$auth.updateAccount`, etc.) derive the configuration type from the current signed-in user.\n\nThe first available configuration will be used if none is provided (`default` in this example).\n\n##### Examples using an alternate user type\n\n~~~javascript\n// OAuth\n$auth.authenticate('github', {\n  config: 'evilUser',\n  params: {\n    favorite_color: $scope.favoriteColor\n  }\n});\n\n// Email Registration\n$auth.submitRegistration({\n  email:                 $scope.email,\n  password:              $scope.password,\n  password_confirmation: $scope.passwordConfirmation,\n  favorite_color:        $scope.favoriteColor\n}, {\n  config: 'evilUser'\n});\n\n// Email Sign In\n$auth.submitLogin({\n  email: $scope.email,\n  password: $scope.password\n}, {\n  config: 'evilUser'\n});\n\n// Password reset request\n$auth.requestPasswordReset({\n  email: $scope.passwordResetEmail\n}, {\n  config: 'evilUser'\n});\n~~~\n\n## File uploads\n\nSome file upload libraries interfere with the authentication headers set by this module. Workarounds are documented below:\n\n### [angular-file-upload](https://github.com/danialfarid/ng-file-upload)#\n\nThe `upload` method accepts a `headers` option. Manually pass the current auth headers to the `upload` method as follows:\n\n~~~javascript\n$scope.onFileSelect = function($files, $auth) {\n    var file = $files[0];\n    $scope.upload = $upload.upload({\n        url:     'api/users/update_image',\n        method:  'POST',\n        headers: $auth.retrieveData('auth_headers'),\n        file:    file\n    });\n}\n~~~\n\n# Conceptual\n\nThe following is a high-level overview of this module's implementation.\n\n## Oauth2 authentication flow\n\nThe following diagram illustrates the steps necessary to authenticate a client using an oauth2 provider.\n\n![oauth flow](https://github.com/lynndylanhurley/ng-token-auth/raw/master/test/app/images/flow/omniauth-flow.jpg)\n\nWhen authenticating with a 3rd party provider, the following steps will take place, assuming the backend server is configured appropriately. [devise token auth](https://github.com/lynndylanhurley/devise_token_auth) already accounts for these flows.\n\n- `sameWindow` Mode\n  1. The existing window will be used to access the provider's authentication page.\n  2. Once the user signs in, they will be redirected back to the API using the same window, with the user and authentication tokens being set.\n\n- `newWindow` Mode\n  1. An external window will be opened to the provider's authentication page.\n  2. Once the user signs in, they will be redirected back to the API at the callback uri that was registered with the oauth2 provider.\n  3. The API will send the user's info back to the client via `postMessage` event, and then close the external window.\n\n- `inAppBrowser` Mode\n  - This mode is virtually identical to the `newWindow` flow, except the flow varies slightly to account for limitations with the [Cordova inAppBrowser Plugin](https://github.com/apache/cordova-plugin-inappbrowser) and the `postMessage` API. Note: In order for this mode to work out of the box, inAppBrowser is assumed to be registered with any external window.open calls. eg - `window.open = window.cordova.InAppBrowser.open;`. \n  \n  _Note: With legacy versions of inAppBrowser, the `executeScript` API is used to ferry data to the parent window. However, there are payload size limitations with this mechanism. As of version `3.1.x`, the `postMessage` API\n  has better support and is utilized for sending data to the parent window, as it lacks these payload limitations._\n\nThe `postMessage` event (utilized for both `newWindow` and `inAppBrowser` modes) must include the following a parameters:\n* **message** - this must contain the value `\"deliverCredentials\"`\n* **auth_token** - a unique token set by your server.\n* **uid** - the id that was returned by the provider. For example, the user's facebook id, twitter id, etc.\n\nRails `newWindow` example: [controller](https://github.com/lynndylanhurley/ng-token-auth-api-rails/blob/master/app/controllers/users/auth_controller.rb#L21), [layout](https://github.com/lynndylanhurley/ng-token-auth-api-rails/blob/master/app/views/layouts/oauth_response.html.erb), [view](https://github.com/lynndylanhurley/ng-token-auth-api-rails/blob/master/app/views/users/auth/oauth_success.html.erb).\n\n##### Example newWindow redirect_uri destination:\n\n~~~html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003cscript\u003e\n      window.addEventListener(\"message\", function(ev) {\n\n        // this page must respond to \"requestCredentials\"\n        if (ev.data === \"requestCredentials\") {\n\n          ev.source.postMessage({\n             message: \"deliverCredentials\", // required\n             auth_token: 'xxxx', // required\n             uid: 'yyyy', // required\n\n             // additional params will be added to the user object\n             name: 'Slemp Diggler'\n             // etc.\n\n          }, '*');\n\n          // close window after message is sent\n          window.close();\n        }\n      });\n    \u003c/script\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003cpre\u003e\n      Redirecting...\n    \u003c/pre\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n~~~\n\n##### Provider-Specific Caveats\n\n- Apple\n  - The use of `localhost` during redirects is not allowed. ng-token-auth will typically use the `window.location.href` value to construct the redirect URL. However, for local testing, or within a proxied\n  WKWebView implementation in Cordova, the use of `localhost` may be unavoidable. For this reason, you may specify\n  an alternative redirect via the `opts.auth_origin_url` value during `authenticate()`, using a validated domain, to get around this restriction.\n\n## Token validation flow\n\nThe client's tokens are stored in cookies using the ipCookie module. This is done so that users won't need to re-authenticate each time they return to the site or refresh the page.\n\n![validation flow](https://github.com/lynndylanhurley/ng-token-auth/raw/master/test/app/images/flow/validation-flow.jpg)\n\n## Email registration flow\n\nThis module also provides support for email registration. The following diagram illustrates this process.\n\n![email registration flow](https://github.com/lynndylanhurley/ng-token-auth/raw/master/test/app/images/flow/email-registration-flow.jpg)\n\n## Email sign in flow\n\n![email sign in flow](https://github.com/lynndylanhurley/ng-token-auth/raw/master/test/app/images/flow/email-sign-in-flow.jpg)\n\n## Password reset flow\n\nThe password reset flow is similar to the email registration flow.\n\n![password reset flow](https://github.com/lynndylanhurley/ng-token-auth/raw/master/test/app/images/flow/password-reset-flow.jpg)\n\nWhen the user visits the link contained in the resulting email, they will be authenticated for a single session. An event will be broadcast that can be used to prompt the user to update their password. See the [`auth:password-reset-confirm-success`](#events) event for details.\n\n## About token management\n\nTokens should be invalidated after each request to the API. The following diagram illustrates this concept:\n\n![password reset flow](https://github.com/lynndylanhurley/ng-token-auth/raw/master/test/app/images/flow/token-update-detail.jpg)\n\nDuring each request, a new token is generated. The `access-token` header that should be used in the next request is returned in the `access-token` header of the response to the previous request. The last request in the diagram fails because it tries to use a token that was invalidated by the previous request.\n\nThe benefit of this measure is that if a user's token is compromised, the user will immediately be forced to re-authenticate. This will invalidate the token that is now in use by the attacker.\n\nThe only case where an expired token is allowed is during [batch requests](#about-batch-requests).\n\nToken management is handled by default when using this module with the [devise token auth](https://github.com/lynndylanhurley/devise_token_auth) gem.\n\n## About batch requests\n\nBy default, the API should update the auth token for each request ([read more](#about-token-management)). But sometimes it's neccessary to make several concurrent requests to the API, for example:\n\n##### Batch request example\n~~~javascript\n$scope.getResourceData = function() {\n\n  $http.get('/api/restricted_resource_1').then(function(resp) {\n    // handle response\n    $scope.resource1 = resp.data;\n  });\n\n  $http.get('/api/restricted_resource_2').then(function(resp) {\n    // handle response\n    $scope.resource2 = resp.data;\n  });\n};\n~~~\n\nIn this case, it's impossible to update the `access-token` header for the second request with the `access-token` header of the first response because the second request will begin before the first one is complete. The server must allow these batches of concurrent requests to share the same auth token. This diagram illustrates how batch requests are identified by the server:\n\n![batch request overview](https://github.com/lynndylanhurley/ng-token-auth/raw/master/test/app/images/flow/batch-request-overview.jpg)\n\nThe \"5 second\" buffer in the diagram is the default used by the [devise token auth](https://github.com/lynndylanhurley/devise_token_auth) gem.\n\nThe following diagram details the relationship between the client, server, and access tokens used over time when dealing with batch requests:\n\n![batch request detail](https://github.com/lynndylanhurley/ng-token-auth/raw/master/test/app/images/flow/batch-request-detail.jpg)\n\nNote that when the server identifies that a request is part of a batch request, the user's auth token is not updated. The auth token will be updated for the first request in the batch, and then that same token will be returned in the responses for each subsequent request in the batch (as shown in the diagram).\n\nThe [devise token auth](https://github.com/lynndylanhurley/devise_token_auth) gem automatically manages batch requests, and it provides settings to fine-tune how batch request groups are identified.\n\n# Identifying users on the server.\n\nThe user's authentication information is included by the client in the `access-token` header of each request. If you're using the [devise token auth](https://github.com/lynndylanhurley/devise_token_auth) gem, the header must follow the [RFC 6750 Bearer Token](http://tools.ietf.org/html/rfc6750) format:\n\n~~~\n\"access-token\": \"wwwww\",\n\"token-type\":   \"Bearer\",\n\"client\":       \"xxxxx\",\n\"expiry\":       \"yyyyy\",\n\"uid\":          \"zzzzz\"\n~~~\n\nReplace `xxxxx` with the user's `auth_token` and `zzzzz` with the user's `uid`. The `client` field exists to allow for multiple simultaneous sessions per user. The `client` field defaults to `default` if omitted. `expiry` is used by the client to invalidate expired tokens without making an API request. A more in depth explanation of these values is [here](https://github.com/lynndylanhurley/devise_token_auth#identifying-users-in-controllers).\n\nThis will all happen automatically when using this module.\n\n**Note**: You can customize the auth headers however you like. [Read more](#using-alternate-header-formats).\n\n# iOS\n\n * localStoage may not writable in Private Browsing mode. You may wish to configure `storage` to use a generic object store or temporary cookie store. See also: [Frustration](https://spin.atomicobject.com/2013/01/23/ios-private-browsing-localstorage/)\n\n# Internet Explorer\n\nInternet Explorer (8, 9, 10, \u0026 11) present the following obstacles:\n\n* IE8 \u0026 IE9 don't really support cross origin requests (CORS).\n* IE8+ `postMessage` implementations don't work for our purposes.\n* IE8 \u0026 IE9 both try to cache ajax requests.\n\nThe following measures are necessary when dealing with these older browsers.\n\n#### AJAX cache must be disabled for IE8 + IE9\n\nIE8 + IE9 will try to cache ajax requests. This results in an issue where the request return 304 status with `Content-Type` set to `html` and everything goes haywire.\n\nThe solution to this problem is to set the `If-Modified-Since` headers to `'0'` on each of the request methods that we use in our app. This is done by default when using this module.\n\nThe solution was lifted from [this stackoverflow post](http://stackoverflow.com/questions/16098430/angular-ie-caching-issue-for-http).\n\n#### IE8 and IE9 must proxy CORS requests\n\nYou will need to set up an API proxy if the following conditions are both true:\n\n* your API lives on a different domain than your client\n* you wish to support IE8 and IE9\n\n##### Example proxy using express for node.js\n~~~javascript\nvar express   = require('express');\nvar request   = require('request');\nvar httpProxy = require('http-proxy');\nvar CONFIG    = require('config');\n\n// proxy api requests (for older IE browsers)\napp.all('/proxy/*', function(req, res, next) {\n  // transform request URL into remote URL\n  var apiUrl = 'http:'+CONFIG.API_URL+req.params[0];\n  var r = null;\n\n  // preserve GET params\n  if (req._parsedUrl.search) {\n    apiUrl += req._parsedUrl.search;\n  }\n\n  // handle POST / PUT\n  if (req.method === 'POST' || req.method === 'PUT') {\n    r = request[req.method.toLowerCase()]({\n      uri: apiUrl,\n      json: req.body\n    });\n  } else {\n    r = request(apiUrl);\n  }\n\n  // pipe request to remote API\n  req.pipe(r).pipe(res);\n});\n~~~\n\nThe above example assumes that you're using [express](http://expressjs.com/), [request](https://github.com/request/request), and [http-proxy](https://github.com/nodejitsu/node-http-proxy), and that you have set the API_URL value using [node-config](https://github.com/lorenwest/node-config).\n\n#### IE8-11 / iOS 8.2 must use `sameWindow` for provider authentication\n\nMost modern browsers can communicate across tabs and windows using [postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage). This doesn't work for certain flawed browsers. In these instances, it's recommended to always use `sameWindow` mode. If you are configured to use `newWindow` mode, you will most likely wish to handle this in the configuration. Eg:\n\n```javascript\n      $authProvider.configure({\n        omniauthWindowType: isIE ? `sameWindow` : `newWindow`\n      })\n```\n\n---\n\n# FAQ\n\n### Why does this module use `ipCookies` instead of `ngCookies`?\n\nIt's impossible to control cookies' path values using `ngCookies`. This results in the creation of multiple auth tokens, and it becomes impossible to send the correct token to the API.\n\nThe only options were to re-implement cookie storage from scratch, or to use the [ipCookie module](https://github.com/ivpusic/angular-cookie). The ipCookie module seemed like the better choice, and it's been working well so far.\n\nPlease direct complaints regarding this problem to [this angular issue](https://github.com/angular/angular.js/issues/1786).\n\n# Development\n\n### Running the dev server\n\nThere is a test project in the `test` directory of this app. To start a dev server, perform the following steps.\n\n1. `cd` to the root of this project.\n1. `npm install`\n1. `cd test \u0026\u0026 bower install`\n1. `cd ..`\n1. `gem install sass`\n1. `gulp dev`\n\nA dev server will start on [localhost:7777](http://localhost:7777).\n\n### Running the tests\n\nAssuming the [dev server](#running-the-dev-server) has already been set up, start karma using the following commands:\n\n1. `sudo npm install -g karma-cli`\n1. `karma start test/test/karma.conf.coffee`\n\n### Testing against a live API\n\nThis module was built against [this API](https://github.com/lynndylanhurley/devise_token_auth_demo). You can use this, or feel free to use your own.\n\nThere are more detailed instructions in `test/README.md`.\n\n# Contributing\n\nJust send a pull request. You will be granted commit access if you send quality pull requests.\n\n#### Contribution guidelines:\n\n* Make sure that you make changes to the CoffeeScript source file (`src/ng-token-auth.coffee`) and not the compiled distribution file (`dist/ng-token-auth.js`). If the [dev server](#running-the-dev-server) is running, the coffescript will be compiled automatically. You can also run `gulp transpile` from the project root to compile the code.\n* Pull requests that include tests will receive prioirity. Read how to run the tests [here](#running-the-tests).\n\n# Alternatives\n\n### [Satellizer](https://github.com/sahat/satellizer)\n\nSatellizer occupies the same problem domain as ng-token-auth. Advantages of ng-token-auth (at the time of this writing) include:\n  * [Events](#events).\n  * Seamless, out-of-the-box integration with the [devise token auth](https://github.com/lynndylanhurley/devise_token_auth) gem. This gem provides a high level of security with minimal configuration.\n  * [Auth header customization](#using-alternate-header-formats).\n  * [Auth response customization](#using-alternate-response-formats).\n  * Supports both cookies and localStorage for session persistence.\n  * Supports [password reset](#authrequestpasswordreset) and [password update](#authupdatepassword) for users that registered by email.\n  * Supports [account updates](#authupdateaccount) and [account deletion](#authdestroyaccount).\n  * Supports [changing tokens with each request](#about-token-management).\n  * Supports [multiple user types](#using-multiple-user-types).\n\n# Callouts\n\nThanks to the following contributors:\n\n* [@booleanbetrayal](https://github.com/booleanbetrayal)\n* [@guilhermesimoes](https://github.com/guilhermesimoes)\n* [@jasonswett](https://github.com/jasonswett)\n* [@m2omou](https://github.com/m2omou)\n* [@smarquez1](https://github.com/smarquez1)\n* [@jartek](https://github.com/jartek)\n* [@flaviogranero](https://github.com/flaviogranero)\n* [@askobara](https://github.com/askobara)\n\nSpecial thanks to [@jasonswett](https://github.com/jasonswett) for [this helpful guide](https://www.airpair.com/ruby-on-rails-4/posts/authentication-with-angularjs-and-ruby-on-rails)!\n\nThis module has been featured by [http://angular-js.in](http://angular-js.in/).\n\n# License\n\nThis project uses the WTFPL\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flynndylanhurley%2Fng-token-auth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flynndylanhurley%2Fng-token-auth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flynndylanhurley%2Fng-token-auth/lists"}