├── .gitignore ├── .jshintrc ├── package.json ├── index.html ├── README.md ├── LICENSE.txt ├── auth.server.js └── auth.client.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "esnext": true, 4 | "bitwise": true, 5 | "curly": true, 6 | "eqeqeq": true, 7 | "immed": true, 8 | "indent": 2, 9 | "latedef": true, 10 | "newcap": true, 11 | "noarg": true, 12 | "quotmark": "single", 13 | "regexp": true, 14 | "undef": true, 15 | "strict": false, 16 | "smarttabs": true, 17 | "expr": true, 18 | 19 | 20 | "evil": true, 21 | "browser": true, 22 | "regexdash": true, 23 | "wsh": true, 24 | "trailing": true, 25 | "sub": true, 26 | "unused": true, 27 | "laxcomma": true, 28 | 29 | "globals": { 30 | "after": false, 31 | "before": false, 32 | "afterEach": false, 33 | "beforeEach": false, 34 | "describe": false, 35 | "it": false, 36 | "angular": false, 37 | "Auth0Widget": false, 38 | "Auth0": false 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-token-auth", 3 | "version": "0.1.0", 4 | "dependencies": { 5 | "body-parser": "^1.9.0", 6 | "express": "~4.9.0", 7 | "express-jwt": "~0.2.1", 8 | "jsonwebtoken": "~0.4.0" 9 | }, 10 | "description": "Example of Token-based authentication in [AngularJS](http://angularjs.org) with [Express](http://expressjs.com).", 11 | "main": "auth.server.js", 12 | "devDependencies": {}, 13 | "scripts": { 14 | "test": "echo \"Error: no test specified\" && exit 1" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/auth0/angular-token-auth.git" 19 | }, 20 | "keywords": [ 21 | "angular", 22 | "auth", 23 | "jwt", 24 | "express" 25 | ], 26 | "author": "", 27 | "license": "MIT", 28 | "bugs": { 29 | "url": "https://github.com/auth0/angular-token-auth/issues" 30 | }, 31 | "homepage": "https://github.com/auth0/angular-token-auth" 32 | } 33 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Angular Authentication 6 | 7 | 8 | 9 | 10 |
11 | {{welcome}} 12 |
13 | 14 | 15 | 16 |
17 |
{{error}}
18 |
19 | Shh, this is private! 20 |
21 |
{{message}}
22 | Logout 23 |
24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## angular-token-auth 2 | 3 | Example of Token-based authentication in [AngularJS](http://angularjs.org) with [Express](http://expressjs.com). 4 | 5 | ### Build and Run 6 | 7 | First, install dependencies using npm: 8 | 9 | ```sh 10 | npm install 11 | ``` 12 | 13 | Run the example: 14 | 15 | ```sh 16 | node auth.server.js 17 | ``` 18 | 19 | and go to [localhost:8080](http://localhost:8080). 20 | 21 | 22 | ### More advanced scenarios? 23 | 24 | For a complete example handling social providers, enterprise authentication with LDAP/Active Directory, and user/password, check out [Auth0 Angular integration](https://github.com/auth0/auth0-angular). 25 | 26 | ## Issue Reporting 27 | 28 | If you have found a bug or if you have a feature request, please report them at this repository issues section. Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://auth0.com/whitehat) details the procedure for disclosing security issues. 29 | 30 | ## Author 31 | 32 | [Auth0](auth0.com) 33 | 34 | ## License 35 | 36 | This project is licensed under the MIT license. See the [LICENSE](LICENSE) file for more info. 37 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Auth0, Inc. (http://auth0.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /auth.server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var bodyParser = require('body-parser'); 3 | 4 | var jwt = require('jsonwebtoken'); //https://npmjs.org/package/node-jsonwebtoken 5 | var expressJwt = require('express-jwt'); //https://npmjs.org/package/express-jwt 6 | 7 | 8 | var secret = 'this is the secret secret secret 12356'; 9 | 10 | var app = express(); 11 | 12 | // We are going to protect /api routes with JWT 13 | app.use('/api', expressJwt({secret: secret})); 14 | 15 | app.use(bodyParser.json()); 16 | app.use('/', express.static(__dirname + '/')); 17 | 18 | app.use(function(err, req, res, next){ 19 | if (err.constructor.name === 'UnauthorizedError') { 20 | res.status(401).send('Unauthorized'); 21 | } 22 | }); 23 | 24 | app.post('/authenticate', function (req, res) { 25 | //TODO validate req.body.username and req.body.password 26 | //if is invalid, return 401 27 | if (!(req.body.username === 'john.doe' && req.body.password === 'foobar')) { 28 | res.status(401).send('Wrong user or password'); 29 | return; 30 | } 31 | 32 | var profile = { 33 | first_name: 'John', 34 | last_name: 'Doe', 35 | email: 'john@doe.com', 36 | id: 123 37 | }; 38 | 39 | // We are sending the profile inside the token 40 | var token = jwt.sign(profile, secret, { expiresInMinutes: 60*5 }); 41 | 42 | res.json({ token: token }); 43 | }); 44 | 45 | app.get('/api/restricted', function (req, res) { 46 | console.log('user ' + req.user.email + ' is calling /api/restricted'); 47 | res.json({ 48 | name: 'foo' 49 | }); 50 | }); 51 | 52 | app.listen(8080, function () { 53 | console.log('listening on http://localhost:8080'); 54 | }); 55 | -------------------------------------------------------------------------------- /auth.client.js: -------------------------------------------------------------------------------- 1 | var myApp = angular.module('myApp', []); 2 | 3 | //this is used to parse the profile 4 | function url_base64_decode(str) { 5 | var output = str.replace('-', '+').replace('_', '/'); 6 | switch (output.length % 4) { 7 | case 0: 8 | break; 9 | case 2: 10 | output += '=='; 11 | break; 12 | case 3: 13 | output += '='; 14 | break; 15 | default: 16 | throw 'Illegal base64url string!'; 17 | } 18 | return window.atob(output); //polifyll https://github.com/davidchambers/Base64.js 19 | } 20 | 21 | myApp.controller('UserCtrl', function ($scope, $http, $window) { 22 | $scope.user = {username: 'john.doe', password: 'foobar'}; 23 | $scope.isAuthenticated = false; 24 | $scope.welcome = ''; 25 | $scope.message = ''; 26 | 27 | $scope.submit = function () { 28 | $http 29 | .post('/authenticate', $scope.user) 30 | .success(function (data, status, headers, config) { 31 | $window.sessionStorage.token = data.token; 32 | $scope.isAuthenticated = true; 33 | var encodedProfile = data.token.split('.')[1]; 34 | var profile = JSON.parse(url_base64_decode(encodedProfile)); 35 | $scope.welcome = 'Welcome ' + profile.first_name + ' ' + profile.last_name; 36 | }) 37 | .error(function (data, status, headers, config) { 38 | // Erase the token if the user fails to log in 39 | delete $window.sessionStorage.token; 40 | $scope.isAuthenticated = false; 41 | 42 | // Handle login errors here 43 | $scope.error = 'Error: Invalid user or password'; 44 | $scope.welcome = ''; 45 | }); 46 | }; 47 | 48 | $scope.logout = function () { 49 | $scope.welcome = ''; 50 | $scope.message = ''; 51 | $scope.isAuthenticated = false; 52 | delete $window.sessionStorage.token; 53 | }; 54 | 55 | $scope.callRestricted = function () { 56 | $http({url: '/api/restricted', method: 'GET'}) 57 | .success(function (data, status, headers, config) { 58 | $scope.message = $scope.message + ' ' + data.name; // Should log 'foo' 59 | }) 60 | .error(function (data, status, headers, config) { 61 | alert(data); 62 | }); 63 | }; 64 | 65 | }); 66 | 67 | myApp.factory('authInterceptor', function ($rootScope, $q, $window) { 68 | return { 69 | request: function (config) { 70 | config.headers = config.headers || {}; 71 | if ($window.sessionStorage.token) { 72 | config.headers.Authorization = 'Bearer ' + $window.sessionStorage.token; 73 | } 74 | return config; 75 | }, 76 | responseError: function (rejection) { 77 | if (rejection.status === 401) { 78 | // handle the case where the user is not authenticated 79 | } 80 | return $q.reject(rejection); 81 | } 82 | }; 83 | }); 84 | 85 | myApp.config(function ($httpProvider) { 86 | $httpProvider.interceptors.push('authInterceptor'); 87 | }); 88 | --------------------------------------------------------------------------------