Repository: stormpath/stormpath-sdk-angularjs Branch: master Commit: 8b2a1caf71ce Files: 212 Total size: 563.4 KB Directory structure: gitextract_pr66l4um/ ├── .gitignore ├── .gitmodules ├── .jshintrc ├── .npmignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Gruntfile.js ├── LICENSE ├── OLD-README.md ├── README.md ├── TROUBLESHOOTING.md ├── bower.json ├── dist/ │ ├── stormpath-sdk-angularjs.js │ └── stormpath-sdk-angularjs.tpls.js ├── docs/ │ ├── Makefile │ ├── render │ └── source/ │ ├── access_control.rst │ ├── conf.py │ ├── configure_angular.rst │ ├── create_new_project.rst │ ├── create_tenant.rst │ ├── customize_menu.rst │ ├── index.rst │ ├── introduction.rst │ ├── login.rst │ ├── password_reset.rst │ ├── protect_api.rst │ ├── register.rst │ ├── support.rst │ ├── user_profile.rst │ └── wait_for_user.rst ├── example/ │ ├── cors-app/ │ │ ├── .bowerrc │ │ ├── README.md │ │ ├── bower.json │ │ ├── client/ │ │ │ ├── client.js │ │ │ └── index.html │ │ ├── package.json │ │ └── server.js │ ├── dashboard-app/ │ │ ├── .bowerrc │ │ ├── .buildignore │ │ ├── .editorconfig │ │ ├── .gitattributes │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── .yo-rc.json │ │ ├── Gruntfile.js │ │ ├── README.md │ │ ├── bower.json │ │ ├── client/ │ │ │ ├── .htaccess │ │ │ ├── .jshintrc │ │ │ ├── app/ │ │ │ │ ├── app.css │ │ │ │ ├── app.js │ │ │ │ ├── forgot/ │ │ │ │ │ ├── forgot.controller.js │ │ │ │ │ ├── forgot.controller.spec.js │ │ │ │ │ ├── forgot.css │ │ │ │ │ ├── forgot.html │ │ │ │ │ └── forgot.js │ │ │ │ ├── login/ │ │ │ │ │ ├── login.controller.js │ │ │ │ │ ├── login.controller.spec.js │ │ │ │ │ ├── login.css │ │ │ │ │ ├── login.html │ │ │ │ │ └── login.js │ │ │ │ ├── main/ │ │ │ │ │ ├── main.controller.js │ │ │ │ │ ├── main.controller.spec.js │ │ │ │ │ ├── main.css │ │ │ │ │ ├── main.html │ │ │ │ │ └── main.js │ │ │ │ ├── profile/ │ │ │ │ │ ├── profile.controller.js │ │ │ │ │ ├── profile.controller.spec.js │ │ │ │ │ ├── profile.css │ │ │ │ │ ├── profile.html │ │ │ │ │ └── profile.js │ │ │ │ ├── register/ │ │ │ │ │ ├── register.controller.js │ │ │ │ │ ├── register.controller.spec.js │ │ │ │ │ ├── register.css │ │ │ │ │ ├── register.html │ │ │ │ │ └── register.js │ │ │ │ ├── reset/ │ │ │ │ │ ├── reset.controller.js │ │ │ │ │ ├── reset.controller.spec.js │ │ │ │ │ ├── reset.css │ │ │ │ │ ├── reset.html │ │ │ │ │ └── reset.js │ │ │ │ └── verify/ │ │ │ │ ├── verify.controller.js │ │ │ │ ├── verify.controller.spec.js │ │ │ │ ├── verify.css │ │ │ │ ├── verify.html │ │ │ │ └── verify.js │ │ │ ├── components/ │ │ │ │ └── navbar/ │ │ │ │ ├── navbar.controller.js │ │ │ │ └── navbar.html │ │ │ ├── index.html │ │ │ └── robots.txt │ │ ├── e2e/ │ │ │ └── main/ │ │ │ ├── main.po.js │ │ │ └── main.spec.js │ │ ├── karma.conf.js │ │ ├── package.json │ │ ├── protractor.conf.js │ │ └── server/ │ │ ├── .jshintrc │ │ ├── .jshintrc-spec │ │ ├── api/ │ │ │ └── thing/ │ │ │ ├── index.js │ │ │ ├── thing.controller.js │ │ │ └── thing.spec.js │ │ ├── app.js │ │ ├── components/ │ │ │ └── errors/ │ │ │ └── index.js │ │ ├── config/ │ │ │ ├── environment/ │ │ │ │ ├── development.js │ │ │ │ ├── index.js │ │ │ │ ├── production.js │ │ │ │ └── test.js │ │ │ ├── express.js │ │ │ └── local.env.sample.js │ │ ├── routes.js │ │ └── views/ │ │ └── 404.html │ └── ng-route-app/ │ ├── .bowerrc │ ├── .buildignore │ ├── .editorconfig │ ├── .gitattributes │ ├── .gitignore │ ├── .travis.yml │ ├── .yo-rc.json │ ├── Gruntfile.js │ ├── README.md │ ├── bower.json │ ├── client/ │ │ ├── .htaccess │ │ ├── .jshintrc │ │ ├── app/ │ │ │ ├── app.css │ │ │ ├── app.js │ │ │ ├── forgot/ │ │ │ │ ├── forgot.controller.js │ │ │ │ ├── forgot.controller.spec.js │ │ │ │ ├── forgot.css │ │ │ │ ├── forgot.html │ │ │ │ └── forgot.js │ │ │ ├── login/ │ │ │ │ ├── login.controller.js │ │ │ │ ├── login.controller.spec.js │ │ │ │ ├── login.css │ │ │ │ ├── login.html │ │ │ │ └── login.js │ │ │ ├── main/ │ │ │ │ ├── main.controller.js │ │ │ │ ├── main.controller.spec.js │ │ │ │ ├── main.css │ │ │ │ ├── main.html │ │ │ │ └── main.js │ │ │ ├── profile/ │ │ │ │ ├── profile.controller.js │ │ │ │ ├── profile.controller.spec.js │ │ │ │ ├── profile.css │ │ │ │ ├── profile.html │ │ │ │ └── profile.js │ │ │ ├── register/ │ │ │ │ ├── register.controller.js │ │ │ │ ├── register.controller.spec.js │ │ │ │ ├── register.css │ │ │ │ ├── register.html │ │ │ │ └── register.js │ │ │ ├── reset/ │ │ │ │ ├── reset.controller.js │ │ │ │ ├── reset.controller.spec.js │ │ │ │ ├── reset.css │ │ │ │ ├── reset.html │ │ │ │ └── reset.js │ │ │ └── verify/ │ │ │ ├── verify.controller.js │ │ │ ├── verify.controller.spec.js │ │ │ ├── verify.css │ │ │ ├── verify.html │ │ │ └── verify.js │ │ ├── components/ │ │ │ └── navbar/ │ │ │ ├── navbar.controller.js │ │ │ └── navbar.html │ │ ├── index.html │ │ └── robots.txt │ ├── e2e/ │ │ └── main/ │ │ ├── main.po.js │ │ └── main.spec.js │ ├── karma.conf.js │ ├── package.json │ ├── protractor.conf.js │ └── server/ │ ├── .jshintrc │ ├── .jshintrc-spec │ ├── api/ │ │ └── thing/ │ │ ├── index.js │ │ ├── thing.controller.js │ │ └── thing.spec.js │ ├── app.js │ ├── components/ │ │ └── errors/ │ │ └── index.js │ ├── config/ │ │ ├── environment/ │ │ │ ├── development.js │ │ │ ├── index.js │ │ │ ├── production.js │ │ │ └── test.js │ │ ├── express.js │ │ └── local.env.sample.js │ ├── routes.js │ └── views/ │ └── 404.html ├── index.js ├── ngdoc_assets/ │ ├── example/ │ │ └── index.ngdoc │ ├── index.ngdoc │ ├── nav.html │ ├── server/ │ │ └── index.ngdoc │ └── stormpath-angular.css ├── package.json ├── protractor.conf.js ├── src/ │ ├── module.js │ ├── spEmailVerification.tpl.html │ ├── spLoginForm.tpl.html │ ├── spPasswordResetForm.tpl.html │ ├── spPasswordResetRequestForm.tpl.html │ ├── spRegistrationForm.tpl.html │ ├── stormpath.auth.js │ ├── stormpath.config.js │ ├── stormpath.emailverification.js │ ├── stormpath.login.js │ ├── stormpath.oauth.js │ ├── stormpath.passwordreset.js │ ├── stormpath.registration.js │ ├── stormpath.social-login.js │ ├── stormpath.tokenStore.js │ ├── stormpath.user.js │ ├── stormpath.utils.js │ └── stormpath.view-model.js └── test/ ├── .jshintrc └── protractor/ └── login.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ node_modules bower_components build .tmp .idea ================================================ FILE: .gitmodules ================================================ [submodule "docs/source/_themes/stormpath"] path = docs/source/_themes/stormpath url = https://github.com/stormpath/stormpath-sphinx-theme.git ================================================ FILE: .jshintrc ================================================ { "node": true, "browser": true, "esnext": true, "bitwise": true, "camelcase": true, "curly": true, "eqeqeq": true, "immed": true, "indent": 2, "latedef": true, "newcap": true, "noarg": true, "quotmark": "single", "regexp": true, "undef": true, "unused": true, "strict": true, "trailing": true, "smarttabs": true, "globals": { "angular": false } } ================================================ FILE: .npmignore ================================================ app docs example ngdoc_assets test ================================================ FILE: CHANGELOG.md ================================================ # 2.0.1 Fixed a bug in `$isCurrentDomain` that would incorrectly report a cross-domain situation in IE11. # 2.0.0 This release adds support for the [Stormpath Client API][], which allows you to authenticate the user directly with Stormpath (authentication does not require special software in your server). The user receives an access token, which can be used to authorize requests on your server. If you need to authorize requests on your server, you will will want to use one of our SDKs to make that process simpler. Please see the Readme for the new instructions for using the Client API. ## Breaking Changes Social Login now requires the use of the Client API, you will need to enable the Client API for your Stormpath Application. This can be found under the Policies section of the application in our Admin Console. # 1.1.1 Our UI Router integration will now look for `data.authorities` on UI state configuration, and apply the same behavior as `sp.authorize.group`. This is a convenience for [JHipster](https://jhipster.github.io/) users, as they already use `data.authorities` to declare the roles that a user must have in order to access a view. # 1.1.0 * The `$user.get()` method now takes a boolean `bypassCache` option, e.g. `$user.get(true)` will make a new request of the `/me` endpoint, instead of returning the user data that was already cached on login. * Logout requests (which makes a POST to `/logout` on our framework integration) are now using `Content-Type: application/x-www-form-urlencoded`, in order to prevent an un-necessary OPTIONS request. * Fixed #151, an un-intended catching of error responses on all HTTP responses. Now we only handle error responses that come from our known framework endpoints. # 1.0.0 This version needs at least version `3.0.0` of [express-stormpath](https://github.com/stormpath/express-stormpath). * Added support for the registration view model ([#119](https://github.com/stormpath/stormpath-sdk-angularjs/pull/119)) This allows you to customize the registration form using the Stormpath Express configuration: * Added support for the login view model ([#118](https://github.com/stormpath/stormpath-sdk-angularjs/pull/118)) This allows you to customize the login form using the Stormpath Express configuration: * Changed logout request to use POST ([#117](https://github.com/stormpath/stormpath-sdk-angularjs/pull/117)) * Added support for new error structure ([#114](https://github.com/stormpath/stormpath-sdk-angularjs/pull/114)) * Added X-Stormpath-Agent header to requests ([#107](https://github.com/stormpath/stormpath-sdk-angularjs/pull/107)) # 0.9.0 * Adding support for ngRoute. Please see the API Documentation and the new `ng-route-app` in the examples folder! * Bug fixed: you could navigate to the login form, even when logged in. * Updating `express-stormpath` versions in example projects. # 0.8.2 * Bug fixed: `stormpath-sdk-angularjs` now exports 'stormpath' instead of 'ui.router' # 0.8.1 * Fixed bug that displayed social login titles without any social login providers * Updated grunt-html2js to version 0.3.5 * Updated grunt-contrib-clean to version 0.7.0 * Updated grunt-contrib-uglify to version 0.10.1 * Removed dist folder from the ignored file so we can use this lib with webpack or browserify # 0.8.0 * Added support for social login with Google and Facebook (requires use of [Express-Stormpath][] as your backend) # 0.7.2 **Released on October 26th, 2015** * Updating `grunt-contrib` dependencies # 0.7.1 **Released on October 8th, 2015** Fixed the [ifUserInGroup][] and [ifUserNotInGroup][] directives to expect the new data format (expanded `account.groups`) from the `/me` route # 0.7.0 ### ! ** Many Breaking Changes ** ! This library has been upgraded to conform to our framework specification, this means that many of the endpoints and default URLs have changed. If you have built a server integration by hand, you will need to read this changelog to know which URLs and HTTP responses that you will need to change. If you are using [Stormpath Express SDK][], that module is being deprecated. You will need to start using [Express-Stormpath][] if you want to use this and future versions of this Angular SDK. We suggest that you read the [Server Configuration][] section of our [Yeoman Guide][], it will show you the new way of initializing [Express-Stormpath][] and attaching it to your application #### Login Changes The login feature now uses `/login` as the location where posts the login form. Previously this was `/oauth/token`. This can be configured with the `AUTHENTICATION_ENDPOINT` property in `STORMPATH_CONFIG` #### User Context Changes This SDK now uses the `/me` URL to determine the context of the current user. Previously this was `/api/users/current`. This can be configured with the `CURRENT_USER_URI` property in `STORMPATH_CONFIG` #### Email Verification Changes This SDK now uses the `/verify` URL to consume email verification tokens. Previously this was `/api/emailVerificationTokens`. This can be configured with the `EMAIL_VERIFICATION_ENDPOINT` property in `STORMPATH_CONFIG` The same URL, `/verify`, is now used to request a re-send of a verification email. POST a JSON body with an `email` property to trigger this action. Previously this was `/api/verificationEmails` and the `RESEND_EMAIL_VERIFICATION_ENDPOINT` property has been removed from `STORMPATH_CONFIG`. #### Password Reset Changes This SDK now uses `/forgot` to request a password reset email, POST a JSON body to this endpoint with an `email` property to trigger this action. Previously this was `/api/passwordResetTokens` and the `PASSWORD_RESET_TOKEN_COLLECTION_ENDPOINT` property has been removed from `STORMPATH_CONFIG`. The new property is `FORGOT_PASSWORD_ENDPOINT` The `/change` URL is now used to POST a new password, with a valid `sptoken`. Previously this feature was supported with the `/api/passwordResetTokens` URL and the `PASSWORD_RESET_TOKEN_COLLECTION_ENDPOINT` property has been removed from `STORMPATH_CONFIG`. The new property is `CHANGE_PASSWORD_ENDPOINT` The default login form now links to the forgot password flow via an href link to `/forgot`. Previously it was using a UI Router state name of `passwordResetRequest` #### Registration Changes The SDK now uses `/register` to POST data for a new account, previously this was `/api/users` and the `USER_COLLECTION_URI` property has been removed from `STORMPATH_CONFIG`. The new property is `REGISTER_URI`. The callback for `$user.create()` will now give you the entire account object. Previously it gave you a truthy value that would indicate if the account required email verification. Now that the entire account object is passed, you need to inspect the account object's `status` property to see if it is `UNVERIFIED`. If your registration form is passing data for the new account's custom data object, you will need to change your `ng-model` references from `formModel.customData.` to `formModel.`. We will automaticaly find the properties that are not part of the base account object, and place them on the custom data object for you. #### Error responses This SDK now expects any HTTP call which results in a `4xx` error to supply an `error` property on the JSON body of the response. Previously it expected an `errorMessage` property # 0.6.0 ### Breaking Changes Stormpath's password reset API accepts email address only, not username. This library was allowing a username to be sent, but that will not work. If using the [Stormpath Express SDK] you will also need to update that library to `>=0.5.0` # 0.5.5 Fix a redirect loop in the state change interceptor # 0.5.4 Fix an undefined attribute bug with the group membership directives # 0.5.3 Adding social login support. **NOTE**: this is overloading oauth grant_type, and we will change this API in the future so use with this disclaimer. ### Bug Fixes # 0.5.2 ### Improvements * Remove un-used `$cookieStore` dependency ### Bug Fixes * The config option FORM_CONTENT_TYPE was not being used to modify the body of the request (only the content-type header) # 0.5.1 ### Bug Fixes The `defaultPostLoginState` option was not being used by the SDK, but now it is! # 0.5.0 ### New Features XHR requests now set the `withCredentials` option to `true`, allowing you to make cross-domain requests that will send the `access_token` and `XSRF-TOKEN` cookies. Your server must respond with the necessary [Cross-Origin-Resource-Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS) headers. If you are using our Express SDK this is done by specifying [Allowed Origins on spConfig](https://github.com/stormpath/stormpath-sdk-express#allowedOrigins) # 0.4.1 ### New Features The UI Router integration now accepts a `forbiddenState` option, this is the state we will send the user to if they are unauthorized for a given state. This is useful if you want to show a default "Forbidden" view in these situations. Added documentation for the [$stateChangeUnauthenticated] and [$stateChangeUnauthorized] events! # 0.4.0 ### Breaking Changes The [ifUserInGroup] and [ifUserNotInGroup] directives now requires you to pass either a string expression or a reference to a scope variable. I.E. this will now throw a parse exception unless `admin` is a reference to a scope property: ```html
Hello, Administrator
``` It should be re-written to be a string expression with quotes: ```html
Hello, Administrator
``` ## New Features The [ifUserInGroup] and [ifUserNotInGroup] directives now support regular expressions :) See the documentation of [ifUserInGroup] for more information # 0.3.0 ### Breaking Changes * The `logout` directive is renamed to [`spLogout`](https://docs.stormpath.com/angularjs/sdk/#/api/stormpath.spLogout:spLogout) * The registration form now uses property names of `givenName` and `surname`, instead of `firstName` and `lastName` * Form submissions now use `application/x-www-form-urlencoded` as the Content Type. Your server needs to negotiate this type, if you are using our server SDKs this happens for you. If you wish to continue using `application/json` as the Content Type you can define `STORMPATH_CONFIG.FORM_CONTENT_TYPE='application/json'` in a config block ## Deprecation Notices * [`whileResolvingUser`](https://docs.stormpath.com/angularjs/sdk/#/api/stormpath.whileResolvingUser:while-resolving-user) is deprecatead. Use [`ifUserStateKnown`](https://docs.stormpath.com/angularjs/sdk/#/api/stormpath.ifUserStateKnown:ifUserStateKnown) instead. ### New Features * **Custom Data on Registration**. You can now pass custom data during registration, simply reference `formModel.customData.myCustomProperty` in your `ng-model` directive. This is only possible if you are supplying a custom template to the directive. See the [`spRegistrationForm`](https://docs.stormpath.com/angularjs/sdk/#/api/stormpath.spRegistrationForm:spRegistrationForm) directive for more detail. * **Group-Based Access Control**. This can now control access to UI Routes, based on group membership. See [`SpStateConfig`](https://docs.stormpath.com/angularjs/sdk/#/api/stormpath.SpStateConfig:SpStateConfig) for examples. We've also introduced the [`ifUserInGroup`](https://docs.stormpath.com/angularjs/sdk/#/api/stormpath.ifUserInGroup:ifUserInGroup) and [`ifUserNotInGroup`](https://docs.stormpath.com/angularjs/sdk/#/api/stormpath.ifUserNotInGroup:ifUserNotInGroup) directives. ### Bug Fixes * [`whileResolvingUser`](https://docs.stormpath.com/angularjs/sdk/#/api/stormpath.whileResolvingUser:while-resolving-user) would break after logout (user state was not properly reflected after logout) [Stormpath Express SDK]: https://github.com/stormpath/stormpath-sdk-express [$stateChangeUnauthenticated]: https://docs.stormpath.com/angularjs/sdk/#/api/stormpath.$stormpath#events_$statechangeunauthenticated [$stateChangeUnauthorized]: https://docs.stormpath.com/angularjs/sdk/#/api/stormpath.$stormpath#events_$statechangeunauthorized [ifUserInGroup]: https://docs.stormpath.com/angularjs/sdk/#/api/stormpath.ifUserInGroup:ifUserInGroup [ifUserNotInGroup]: https://docs.stormpath.com/angularjs/sdk/#/api/stormpath.ifUserNotInGroup:ifUserNotInGroup [Express-Stormpath]: https://github.com/stormpath/express-stormpath [Server Configuration]: http://docs.stormpath.com/angularjs/guide/protect_api.html [Yeoman Guide]: http://docs.stormpath.com/angularjs/guide/ [Stormpath Client API]: https://docs.stormpath.com/client-api/product-guide/latest/index.html ================================================ FILE: CONTRIBUTING.md ================================================ # Contribution Guide We love pull requests! Here are a few things you'll need to know if you want to make a contribution to this library. ### Development Workflow All modifications should happen to the source files in the `src/` directory, not the files minified in the `dist/` directory. The latter is used for keeping a minified version of the current, tagged build. We give this to [Bower](http://bower.io). You can submit a PR after modifying the source files. We will publish a new version in `dist/` when we are ready to cut a new release. ### API Documentation The API documentation is auto-generated from the the JS-Doc style comments that you find in the `src/` files. Do not edit files in `dist/`. You can regenerate the API docs by running `grunt docs`. If you want to see your changes as you edit them you can use `grunt serve` to start a livereload server that will reload your changes as you make them. When editing the JS Doc comments, please follow this order for tags: ``` @ngdoc @name @methodOf (or eventOf, etc) @eventType (if applicable) @param @returns @description @example ``` After you have made your edits, commit your changes and make a pull request. And put a newline before and after the line that includes the tag. ### Product Guide Documentation The source files for the product guide are located in the `docs/source` directory. The format is RST and they are compiled by Sphinx. To work with the product guide, run `grunt guide`. This will start a livereload server which reloads the documents as you edit them. After you have made your edits, commit the changes to the RST files and make a pull request. ================================================ FILE: Gruntfile.js ================================================ // Generated on 2015-01-02 using generator-angular-fullstack 2.0.13 'use strict'; module.exports = function (grunt) { // Load grunt tasks automatically require('load-grunt-tasks')(grunt); grunt.initConfig({ builddir: '.tmp/build', tmpdir: '.tmp', srcDir: './src', src: '<%= srcDir %>/**/*.js', pkg: grunt.file.readJSON('package.json'), buildtag: '-dev-' + grunt.template.today('yyyy-mm-dd'), meta: { banner: '/**\n' + ' * <%= pkg.name %>\n' + ' * Copyright <%= pkg.author %> '+grunt.template.today('yyyy')+'\n' + ' * <%= pkg.description %>\n' + ' * @version v<%= pkg.version %><%= buildtag %>\n' + ' * @link <%= pkg.homepage %>\n' + ' * @license <%= pkg.license %>\n' + ' */' }, clean: { ngdocs: '<%= tmpdir %>/site/**/*' }, replace: { dist: { options: { patterns: [ { match: 'PACKAGE_VERSION', replacement: '<%= pkg.version %>' }, { match: 'PACKAGE_NAME', replacement: '<%= pkg.name %>' } ] }, files: [ {expand: true, flatten: true, src: ['<%= builddir %>/<%= pkg.name %>*.js'], dest: '<%= builddir %>'} ] } }, concat: { options: { banner: '<%= meta.banner %>\n\n'+ '/* commonjs package manager support (eg componentjs) */\n'+ 'if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){\n'+ ' module.exports = \'stormpath\';\n'+ '}\n\n'+ '(function (window, angular, undefined) {\n', footer: '})(window, window.angular);' }, build: { src: '<%= src %>', dest: '<%= builddir %>/<%= pkg.name %>.js' } }, uglify: { options: { banner: '<%= meta.banner %>\n' }, build: { files: { '<%= builddir %>/<%= pkg.name %>.min.js': ['', '<%= concat.build.dest %>'], '<%= builddir %>/<%= pkg.name %>.tpls.min.js': ['', '<%= builddir %>/<%= pkg.name %>.tpls.js'] } } }, release: { files: ['<%= pkg.name %>.js', '<%= pkg.name %>.min.js'], src: '<%= builddir %>', dest: 'release' }, jshint: { all: ['Gruntfile.js', 'src/*.js', '<%= builddir %>/<%= pkg.name %>.js'], options: { eqnull: true } }, watch: { gruntfile: { files: ['Gruntfile.js'] }, ngdocs: { files: ['<%= src %>','ngdoc_assets/**/*'], tasks: ['docs'], options: { livereload: 35730, spawn: false } }, guide: { files: ['docs/source/*.rst','docs/source/_themes/stormpath/*/**'], tasks: ['shell:guide'], options: { livereload: 35731, spawn: false } }, src: { files: ['<%= src %>'], tasks: ['build'] }, develop: { files: ['<%= srcDir %>/*.{js,html}'], tasks: ['dist'] } }, connect: { ngdocs: { options: { port: 9001, base: '<%= tmpdir %>/site', livereload: 35730 } }, guide: { options: { port: 9002, livereload: 35731, base: ['docs/build/html'] } } }, open: { docs: { url: 'http://localhost:<%= connect.ngdocs.options.port %>' }, guide:{ url: 'http://localhost:<%= connect.guide.options.port %>' } }, ngdocs: { options: { dest: '<%= tmpdir %>/site', scripts: [ 'angular.js' ], styles: [ 'ngdoc_assets/stormpath-angular.css' ], html5Mode: false, title: 'stormpath-sdk-angularjs', startPage: '/api', navTemplate: 'ngdoc_assets/nav.html' }, api: { src: ['<%= src %>','ngdoc_assets/index.ngdoc'], title: 'API Reference', api: true } }, copy: { dist: { files:[{ expand: true, flatten: true, src: '<%= builddir %>/*', dest: 'dist/' }] } }, html2js: { options: { module: 'stormpath.templates', fileHeaderString: '<%= meta.banner %>\n\n'+ '/* commonjs package manager support (eg componentjs) */\n'+ 'if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){\n'+ ' module.exports = \'stormpath.templates\';\n'+ '}\n\n'+ '(function (window, angular, undefined) {\n', fileFooterString: '})(window, window.angular);', htmlmin: { collapseBooleanAttributes: false, collapseWhitespace: true, removeAttributeQuotes: true, removeComments: true, removeEmptyAttributes: false, removeRedundantAttributes: true, removeScriptTypeAttributes: false, removeStyleLinkTypeAttributes: false } }, main: { src: ['src/**/*.tpl.html'], dest: '<%= builddir %>/<%= pkg.name %>.tpls.js' } }, shell: { guide: { command: 'make clean && make html', options:{ execOptions: { cwd: 'docs/' } } } } }); grunt.registerTask('docs', 'Builds the Guide and ngDocs for deployment',function () { grunt.task.run( ['clean:ngdocs','ngdocs','shell:guide'] ); }); grunt.registerTask('serve', 'Serves the API documentation, and live reloads as you edit it', ['ngdocs','connect:ngdocs','open:docs','watch:ngdocs'] ); grunt.registerTask('build', 'Perform a normal build', ['concat', 'html2js','uglify','docs']); grunt.registerTask('dist', 'Perform a distribution', ['build', 'replace:dist', 'copy:dist']); grunt.registerTask('develop', 'Build source and distribution, useful if you are modifying this module as a linked modue while developging another module', ['watch:develop'] ); grunt.registerTask('guide', 'Serve and livereload the Guide from the docs/ folder', ['connect:guide','open:guide','watch:guide'] ); }; ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2015 Stormpath, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: OLD-README.md ================================================ # Stormpath is Joining Okta We are incredibly excited to announce that [Stormpath is joining forces with Okta](https://stormpath.com/blog/stormpaths-new-path?utm_source=github&utm_medium=readme&utm-campaign=okta-announcement). Please visit [the Migration FAQs](https://stormpath.com/oktaplusstormpath?utm_source=github&utm_medium=readme&utm-campaign=okta-announcement) for a detailed look at what this means for Stormpath users. We're available to answer all questions at [support@stormpath.com](mailto:support@stormpath.com). # Stormpath AngularJS SDK [![NPM Version](https://img.shields.io/npm/v/stormpath-sdk-angularjs.svg?style=flat)](https://npmjs.org/package/stormpath-sdk-angularjs) [![NPM Downloads](http://img.shields.io/npm/dm/stormpath-sdk-angularjs.svg?style=flat)](https://npmjs.org/package/stormpath-sdk-angularjs) [![Bower Version](https://img.shields.io/bower/v/stormpath-sdk-angularjs.svg?style=flat)](https://bower.io) [![Build Status](https://img.shields.io/travis/stormpath/stormpath-sdk-angularjs.svg?style=flat)](https://travis-ci.org/stormpath/stormpath-sdk-angularjs) This module provides services and directives for AngularJS that will allow you to solve common user management tasks using [Stormpath](https://stormpath.com/), such as *login* and *signup*. *Stormpath is a User Management API that reduces development time with instant-on, scalable user infrastructure. Stormpath's intuitive API and expert support make it easy for developers to authenticate, manage and secure users and roles in any application.* * [Getting Started](#getting-started) * [Documentation](#documentation) * [Example](#example) * [Help](#help) * [Contributing](#contributing) * [License](#license) ## Getting Started Follow these steps to add Stormpath user authentication to your AngularJS app. 1. **Install or Download the Stormpath Angular SDK** If you are using Bower or NPM, you can install this module with the respective command: ``` npm install stormpath-sdk-angularjs --save ``` ``` bower install stormpath-sdk-angularjs --save ``` If you are not using a package manager, you can download the latest source from our Github CDN by using these links: * [stormpath-sdk-angularjs.min.js](https://raw.githubusercontent.com/stormpath/stormpath-sdk-angularjs/master/dist/stormpath-sdk-angularjs.min.js) * [stormpath-sdk-angularjs.tpls.min.js](https://raw.githubusercontent.com/stormpath/stormpath-sdk-angularjs/master/dist/stormpath-sdk-angularjs.tpls.min.js) Then include them in your *index.html* file: ```html ``` 2. **Add the Module to Your App's Dependencies** Add the `stormpath` module and templates to your app's dependencies in *app.js*: ```javascript var app = angular.module('myApp', [..., 'stormpath', 'stormpath.templates']); ``` 3. **Configure Stormpath** The Angular SDK leverages the [Stormpath Client API][] for its authentication needs. Login to your Stormpath Tenant, and find your Client API domain (inside your application's policy section). Add your Client API domain as the `ENDPOINT_PREFIX` setting, via your `.config()` function: ```javascript angular.module('myApp', [..., 'stormpath', 'stormpath.templates']) .config(function (STORMPATH_CONFIG) { // Specify your Client API domain here: STORMPATH_CONFIG.ENDPOINT_PREFIX = 'https://{{clientApiDomainName}}'; }); ``` You will need to tell Stormpath where your front-end application is running, by adding its domain to the list of Authorized Origin URIs on your Stormpath Application. This can be done from the Stormpath Admin Console. For example, if you are developing on a local sever that runs your front-end app at `http://localhost:3000`, you need to add that URI to the list If this is not done, you will see the error `Origin 'http://localhost:3000' is therefore not allowed access.` in the browser error log. If you will be using social login, you will also need to add this URI to the list of Authorized Callback URIs, otherwise you will see the error `Specified redirect_uri is not in the application's configured authorized callback uri's.` when you attempt social login. 4. **Configure Routing** In your app's `run()` block, configure the login state and the default state after login. For `ngRouter`: ```javascript angular.module('myApp') .run(function($stormpath){ $stormpath.ngRouter({ forbiddenRoute: '/forbidden', defaultPostLoginRoute: '/home', loginRoute: '/login' }); }); ``` For `uiRouter`: ```javascript app.run(function($stormpath){ $stormpath.uiRouter({ loginState: 'login', defaultPostLoginState: 'home' }); }); ``` Set `loginState` to your login state. If you don't have one, create one. Set `defaultPostLoginState` to your default state after login. 5. **Insert the Login and Registration Forms** You can use the [`sp-login-form`][] and [`sp-registration-form`][] directives to inject these default forms into your application, you should put this in the views/states where you want them to appear: ```html
``` ```html
```

These forms will read their configuration from the Client API and allow you to login or register for your application. You should now be able to use these forms to login to your application. 6. **Add Login and Logout Links** Use the [`sp-logout`][] directive to end the session: ```html Logout ``` For the login link, just point the user to your login state: ```html Login ``` 7. **Hide Elements When Logged In** Use the [`if-user`][] directive: ```html Logout ``` 8. **Hide Elements When Logged Out** Use the [`if-not-user`][] directive: ```html Login ``` 9. **Protect Your States** On all states that you want to protect, add: ```javascript sp: { authenticate: true } ``` For `ngRouter`: ```javascript angular.module('myApp') .config(function ($routeProvider) { $routeProvider .when('/profile', { templateUrl: 'app/profile/profile.html', controller: 'ProfileCtrl', sp: { authenticate: true } }); }); ``` For `uiRouter`: ```javascript angular.module('myApp') .config(function ($stateProvider) { $stateProvider .state('profile', { url: '/profile', templateUrl: 'app/profile/profile.html', controller: 'ProfileCtrl', sp: { authenticate: true } }); }); ``` 10. **Login!** That's it! You just added user authentication to your app with Stormpath. See the [API Documentation][] for further information on how Stormpath can be used with your AngularJS app. Looking for social login? Simply configure the directories in your Stormpath tenant, and the buttons will automatically appear in the login form. For more reading, please see the [Social Login Product Guide][]. 11. **Making Authenticated Requests** Once you are able to successfully authenticate (log in) from your application, you will want to authorize access to API endpoints on your server. The Angular SDK provides methods for getting the current authenticated access token, and using it to authenticate requests. Imagine you have an API on your server, such as `http://localhost:3000/api/subscription`, and you want to authorize requests to this endpoint and know who the user is. If you want to manually construct a request, using the `$http` library, you can use our access token getter to add the access token to the request: ```javascript StormpathOAuthToken.getAccessToken() .then(function(accessToken){ $http({ url: 'http://localhost:3000/api/subscription', method: 'GET', headers: { Authorization: 'Bearer ' + accessToken } }); }) .catch(function() { // No access token, the user is not logged in }); ``` If you don't want to manually add the access token to every request, you can white-list URLs by expression and the Angular SDK will automatically add this token to all requests that have a matching URL: ```javascript angular.module('myApp', [..., 'stormpath', 'stormpath.templates']) .config(function (STORMPATH_CONFIG) { // Automatically add access token to all /api requests STORMPATH_CONFIG.AUTO_AUTHORIZED_URIS.push(new RegExp('/api')); }); ``` 12. **Authorizing Requests Server-Side** Once your app has made the request with the access token, your server will need to read the token and make an authorization decision. We provide SDKs for your backend server that make this easy. Please follow one of the following links for a language-specific or framework-specific guide: **Java** Spring Boot developers should make use of our Spring Boot plugin, and see the [Token Management Documentation](https://docs.stormpath.com/java/spring-boot-web/tutorial.html#token-management). **.NET** ASP.NET developers can leverage our [ASP.NET](https://docs.stormpath.com/dotnet/aspnet/latest/) and [ASP.NET Core](https://docs.stormpath.com/dotnet/aspnetcore/latest/) libraries to achieve authorization in their applications, please see the Authorization section of each guide. **Node.js** Express developers can use our [Express-Stormpath](https://docs.stormpath.com/nodejs/express/latest/) library to easily authenticate requests with access tokens and make authorization decisions, please see the [Token Authentication](https://docs.stormpath.com/nodejs/express/latest/authentication.html#token-authentication) documentation. Node applications can generically use the [Stormpath Node SDK](https://docs.stormpath.com/nodejs/jsdoc/) to validate tokens, using the [JwtAuthenticator](https://docs.stormpath.com/nodejs/jsdoc/JwtAuthenticator.html). **PHP** Laravel developers can use our Stormpath-Laravel or [Stormpath-Lumen](https://docs.stormpath.com/php/lumen/latest/index.html) libraries and their respective `stormpath.auth` middleware to authenticate requests, please see the User Data section of the documentation for each library. **Other** Don't see your environment listed? Not a problem! Our access tokens are simple JWTs, that can be validated with most generic JWT validation libraries. Our product guide can walk you through the process, [Validating an Access Token](https://docs.stormpath.com/rest/product-guide/latest/auth_n.html#validating-an-access-token"). Need more assistance? Feel free to contact our support channel, details are below. ## Documentation For all available directives and services, see the [API Documentation][]. ## Example See the [example app][] in this repository for an example application that uses Yeoman as it's boilerplate. For a simplified example that does not use a boilerplate system, please see this repository: [Stormpath Angular + Express Fullstack Sample Project](https://github.com/stormpath/express-stormpath-angular-sample-project) If you are hosting your API on a different domain than your Angular application, please see the [CORS example app][] in this repository. ## Browserify This module can be used with Browserify. Please add the following lines to your `package.json` file: ```json "browser": { "stormpath": "./node_modules/stormpath-sdk-angularjs/dist/stormpath-sdk-angularjs.js", "stormpath.templates": "./node_modules/stormpath-sdk-angularjs/dist/stormpath-sdk-angularjs.tpls.js" } ``` You should also install the package `angular-ui-router`, as our library currently depends on it. Then in your application you can use `require` to require our modules: ```javascript var app = angular.module('todoApp', [ require('angular-ui-router'), require('stormpath'), require('stormpath.templates') ]); ``` ## Support We're here to help if you get stuck. There are several ways that you an get in touch with a member of our team: * Send an email to [support@stormpath.com](mailto:support@stormpath.com) * Open a Github Issue on this repository. * Join us on our Slack channel: [https://talkstormpath.shipit.xyz/](https://talkstormpath.shipit.xyz/) [Stormpath AngularJS SDK]: https://github.com/stormpath/stormpath-sdk-angularjs [Stormpath Product Guide]: https://docs.stormpath.com/rest/product-guide/latest/ [Stormpath React SDK]: https://github.com/stormpath/stormpath-sdk-react [express-stormpath]: https://docs.stormpath.com/nodejs/express/latest/ ## Contributing Found something you want to change? Please see the [Contribution Guide](CONTRIBUTING.md), we love your input! ## License Apache 2.0, see [LICENSE](LICENSE). [`if-user`]: https://docs.stormpath.com/angularjs/sdk/#/api/stormpath.ifUser:ifUser [`if-not-user`]: https://docs.stormpath.com/angularjs/sdk/#/api/stormpath.ifNotUser:ifNotUser [`sp-login-form`]: https://docs.stormpath.com/angularjs/sdk/#/api/stormpath.spLoginForm:spLoginForm [`sp-logout`]: https://docs.stormpath.com/angularjs/sdk/#/api/stormpath.spLogout:spLogout [`sp-registration-form`]: https://docs.stormpath.com/angularjs/sdk/#/api/stormpath.spRegistrationForm:spRegistrationForm [example app]: https://github.com/stormpath/stormpath-sdk-angularjs/tree/master/example/dashboard-app [API Documentation]: https://docs.stormpath.com/angularjs/sdk/ [Server Integration Guide]: https://docs.stormpath.com/angularjs/sdk/#/server [express-stormpath]: https://github.com/stormpath/express-stormpath [Stormpath SPA Development Server]: https://github.com/stormpath/stormpath-spa-dev-server [UI-Router]: https://github.com/angular-ui/ui-router [Yeoman Guide]: https://docs.stormpath.com/angularjs/guide [support center]: https://support.stormpath.com [CORS example app]: https://github.com/stormpath/stormpath-sdk-angularjs/tree/master/example/cors-app [Stormpath Client API]: https://docs.stormpath.com/client-api/product-guide/latest/index.html [Social Login Product Guide]: https://docs.stormpath.com/rest/product-guide/latest/auth_n.html#how-social-authentication-works ================================================ FILE: README.md ================================================ # Stormpath is Joining Okta We are incredibly excited to announce that [Stormpath is joining forces with Okta](https://stormpath.com/blog/stormpaths-new-path?utm_source=github&utm_medium=readme&utm-campaign=okta-announcement). Please visit [the Migration FAQs](https://stormpath.com/oktaplusstormpath?utm_source=github&utm_medium=readme&utm-campaign=okta-announcement) for a detailed look at what this means for Stormpath users. We're available to answer all questions at [support@stormpath.com](mailto:support@stormpath.com). ## What does this mean for developers who are using this library? * If you have upgraded to the 2.x series from 1.x, you should downgrade to 1.1.1. Why? The 2.x series depends on the Stormpath Client API, which will not be migrated to the Okta platform. * When downgrading to 1.1.1 you will need to use one of our backend framework integrations to serve the APIs that the 1.x series depends on. * These backend integrations are being patched to work with Okta: - [Java Spring](https://docs.stormpath.com/java/#tab3) - [Java Spring Boot](https://docs.stormpath.com/java/#tab1) - [Node Express](https://docs.stormpath.com/nodejs/) - [ASP.NET 4.x](https://docs.stormpath.com/dotnet/#tab3) - [ASP.NET Core](https://docs.stormpath.com/dotnet/#tab2) * If you are using the Express integration, please see the [Express-Stormpath Angular Sample Project][], it can be used to test your migration to Okta. # README If you are actively using this library, you can find the old readme in [OLD-README.md](OLD-README.md). It is not possible to register for new Stormpath tenants at this time, so you must already have a Stormpath tenant if you wish to use this library during the migration period. [Express-Stormpath Angular Sample Project]: https://github.com/stormpath/express-stormpath-angular-sample-project ================================================ FILE: TROUBLESHOOTING.md ================================================ # Troubleshooting This document contains a list of common problems that you may run into while working with this library. #### "Invalid username or password." when using Social Login If you are using [express-stormpath][] as your back-end you may see this error when attempting a social login. This happens if the Body Parser module is configured before the Express Stormpath module. If you must do this, please ensure that you are enabling the extended option for the parser: ```javascript app.use(bodyParser.urlencoded({ extended: true })); ``` This issue is being tracked here: https://github.com/stormpath/express-stormpath/issues/194 [express-stormpath]: https://github.com/stormpath/express-stormpath ================================================ FILE: bower.json ================================================ { "name": "stormpath-sdk-angularjs", "version": "2.0.1", "main": [ "dist/stormpath-sdk-angularjs.min.js", "dist/stormpath-sdk-angularjs.tpls.min.js" ], "keywords": [ "stormpath", "jwt", "token", "user", "angular", "token based authentication", "login", "password", "registration", "user management" ], "ignore": [ ".tmp", "build", "docs", "example", "ngdoc_assets", "src" ], "dependencies": { "angular": ">=1.2.*" } } ================================================ FILE: dist/stormpath-sdk-angularjs.js ================================================ /** * stormpath-sdk-angularjs * Copyright Stormpath, Inc. 2017 * * @version v2.0.1-dev-2017-04-04 * @link https://github.com/stormpath/stormpath-sdk-angularjs * @license Apache-2.0 */ /* commonjs package manager support (eg componentjs) */ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){ module.exports = 'stormpath'; } (function (window, angular, undefined) { 'use strict'; /** * @ngdoc object * * @name stormpath.SpStateConfig:SpStateConfig * * @property {boolean} authenticate * * If `true`, the user must be authenticated in order to view this state. * If the user is not authenticated, they will * be redirected to the `login` state. After they login, they will be redirected to * the state that was originally requested. * * @property {object} authorize * * An object that defines access control rules. Currently, it supports a group-based * check. See the example below. * * @property {boolean} waitForUser * * If `true`, delay the state transition until we know * if the user is authenticated or not. This is useful for situations where * you want everyone to see this state, but the state may look different * depending on the user's authentication state. * * * @description * * The Stormpath State Config is an object that you can define on a UI Router * state. Use this configuration to define access control for your routes, as * defined by UI Router. * * You will need to be using the UI Router module, and you need * to enable the integration by calling * {@link stormpath.$stormpath#methods_uiRouter $stormpath.uiRouter()} in your * application's config block. * * If you're using Angular's built-in `$routeProvider` instead of UI Router, please * use {@link stormpath.$stormpath#methods_ngRouter $stormpath.ngRouter()} instead. * * **NOTE:** Do not define this configuration on a abstract state, it must go on * the child state. However, the controller of the abstract state will be * initialized AFTER any configuration rules of the child state have been met. * * # Support for `data.authorities` * * If you have used [JHipster](https://jhipster.github.io/) to generate your * project, you are likely using the `data.authorities` property to define * authorization for your views. This library will look for the `data.authorities` * property and apply the same logic as our own `sp.authorize` property. * * @example * *
 *
 * angular.module('myApp')
 *   .config(function ($stateProvider) {
 *
 *     // Wait until we know if the user is logged in before showing the homepage
 *     $stateProvider
 *       .state('main', {
 *         url: '/',
 *         sp: {
 *           waitForUser: true
 *         }
 *       });
 *
 *     // Require a user to be authenticated in order to see this state
 *     $stateProvider
 *       .state('secrets', {
 *         url: '/secrets',
 *         controller: 'SecretsCtrl',
 *         sp: {
 *           authenticate: true
 *         }
 *       });
 *
 *     // Require a user to be in the admins group in order to see this state
 *     $stateProvider
 *       .state('secrets', {
 *         url: '/admin',
 *         controller: 'AdminCtrl',
 *         sp: {
 *           authorize: {
 *             group: 'admins'
 *           }
 *         }
 *       });
 * });
 * 
* * If using JHipster generated code: * *
 *     // Require a user to be in the admins group in order to see this state
 *     $stateProvider
 *       .state('secrets', {
 *         url: '/admin',
 *         controller: 'AdminCtrl',
 *         data: {
 *           authorities: ['admins']
 *         }
 *       });
 *  
*/ /** * @ngdoc object * * @name stormpath.SpRouteConfig:SpRouteConfig * * @property {boolean} authenticate * * If `true`, the user must be authenticated in order to view this route. * If the user is not authenticated, they will * be redirected to the `login` route. After they login, they will be redirected to * the route that was originally requested. * * @property {object} authorize * * An object that defines access control rules. Currently, it supports a group-based * check. See the example below. * * @property {boolean} waitForUser * * If `true`, delay the route transition until we know * if the user is authenticated or not. This is useful for situations where * you want everyone to see this route, but the route may look different * depending on the user's authentication route. * * * @description * * The Stormpath Route Config is an object that you can define on a route. * Use this configuration to define access control for your routes, as * defined by the ngRoute module. * * You will need to be using the ngRoute module, and you need * to enable the integration by calling * {@link stormpath.$stormpath#methods_ngRouter $stormpath.ngRouter()} in your * application's config block. * * If you're using UI Router instead of Angular's built-in `$routeProvider`, please * use {@link stormpath.$stormpath#methods_uiRouter $stormpath.uiRouter()} instead. * * @example * *
 *
 * angular.module('myApp')
 *   .config(function ($routeProvider) {
 *     // Wait until we know if the user is logged in before showing the homepage
 *     $routeProvider
 *       .when('/main', {
*         controller: 'MainCtrl',
 *         sp: {
 *           waitForUser: true
 *         }
 *       });
 *
 *     // Require a user to be authenticated in order to see this route
 *     $routeProvider
 *       .when('/secrets', {
 *         controller: 'SecretsCtrl',
 *         sp: {
 *           authenticate: true
 *         }
 *       });
 *
 *     // Require a user to be in the admins group in order to see this route
 *     $routeProvider
 *       .when('/secrets', {
 *         controller: 'AdminCtrl',
 *         sp: {
 *           authorize: {
 *             group: 'admins'
 *           }
 *         }
 *       });
 * });
 * 
*/ angular.module('stormpath', [ 'stormpath.CONFIG', 'stormpath.utils', 'stormpath.auth', 'stormpath.userService', 'stormpath.viewModelService', 'stormpath.socialLogin', 'stormpath.oauth' ]) .factory('StormpathAgentInterceptor',['$isCurrentDomain', '$spHeaders', function($isCurrentDomain, $spHeaders){ function StormpathAgentInterceptor(){ } /** * Adds the X-Stormpath-Agent header, if the requested URL is on the same * domain as the current document. * * @param {Object} config $http config object. * @return {Object} config $http config object. */ StormpathAgentInterceptor.prototype.request = function(config){ var uriExpressions = [ '/change$', '/forgot$', '/login$', '/logout$', '/me$', '/oauth/token$', '/oauth/token$', '/register$', '/revoke$', '/verify$' ]; if (uriExpressions.some(function(expr){ return new RegExp(expr).test(config.url); })) { config.headers = angular.extend(config.headers, $spHeaders); } return config; }; return new StormpathAgentInterceptor(); }]) .config(['$httpProvider',function($httpProvider){ $httpProvider.interceptors.push('StormpathAgentInterceptor'); }]) .provider('$stormpath', [function $stormpathProvider(){ /** * @ngdoc object * * @name stormpath.$stormpath * * @description * * This service allows you to enable application-wide features of the library. * * At the moment the only feature is the UI Router integration, which is * documented below. */ this.$get = [ '$user', '$injector', 'STORMPATH_CONFIG', '$rootScope', '$location', function stormpathServiceFactory($user, $injector, STORMPATH_CONFIG, $rootScope, $location) { var $state; var $route; function StormpathService(){ var encoder = new UrlEncodedFormParser(); this.encodeUrlForm = encoder.encode.bind(encoder); if ($injector.has('$state')) { $state = $injector.get('$state'); } if ($injector.has('$route')) { $route = $injector.get('$route'); } return this; } function stateChangeUnauthenticatedEvent(toState, toParams){ /** * @ngdoc event * * @name stormpath.$stormpath#$stateChangeUnauthenticated * * @eventOf stormpath.$stormpath * * @eventType broadcast on root scope * * @param {Object} event * * Angular event object. * * @param {Object} toState The state that the user attempted to access. * * @param {Object} toParams The state params of the state that the user * attempted to access. * * @description * * This event is broadcast when a UI state change is prevented, * because the user is not logged in. * * Use this event if you want to implement your own strategy for * presenting the user with a login form. * * To receive this event, you must be using the UI Router integration. * * @example * *
         *   $rootScope.$on('$stateChangeUnauthenticated',function(e,toState,toParams){
         *     // Your custom logic for deciding how the user should login, and
         *     // if you want to redirect them to the desired state afterwards
         *   });
         * 
*/ $rootScope.$broadcast(STORMPATH_CONFIG.STATE_CHANGE_UNAUTHENTICATED,toState,toParams); } function stateChangeUnauthorizedEvent(toState,toParams){ /** * @ngdoc event * * @name stormpath.$stormpath#$stateChangeUnauthorized * * @eventOf stormpath.$stormpath * * @eventType broadcast on root scope * * @param {Object} event * * Angular event object. * * @param {Object} toState The state that the user attempted to access. * * @param {Object} toParams The state params of the state that the user * attempted to access. * * @description * * This event is broadcast when a UI state change is prevented, * because the user is not authorized by the rules defined in the * {@link stormpath.SpStateConfig:SpStateConfig Stormpath State Configuration} * for the requested state. * * Use this event if you want to implement your own strategy for telling * the user that they are forbidden from viewing that state. * * To receive this event, you must be using the UI Router integration. * * @example * *
         *   $rootScope.$on('$stateChangeUnauthorized',function(e,toState,toParams){
         *     // Your custom logic for deciding how the user should be
         *     // notified that they are forbidden from this state
         *   });
         * 
*/ $rootScope.$broadcast(STORMPATH_CONFIG.STATE_CHANGE_UNAUTHORIZED,toState,toParams); } StormpathService.prototype.stateChangeInterceptor = function stateChangeInterceptor(config) { $rootScope.$on('$stateChangeStart', function(e,toState,toParams){ var sp = toState.sp || {}; // Grab the sp config for this state var authorities = (toState.data && toState.data.authorities) ? toState.data.authorities : undefined; if((sp.authenticate || sp.authorize || (authorities && authorities.length)) && (!$user.currentUser)){ e.preventDefault(); $user.get().then(function(){ // The user is authenticated, continue to the requested state if(sp.authorize || (authorities && authorities.length)){ if(authorizeStateConfig(sp, authorities)){ $state.go(toState.name,toParams); }else{ stateChangeUnauthorizedEvent(toState,toParams); } }else{ $state.go(toState.name,toParams); } },function(){ // The user is not authenticated, emit the necessary event stateChangeUnauthenticatedEvent(toState,toParams); }); }else if(sp.waitForUser && ($user.currentUser===null)){ e.preventDefault(); $user.get().finally(function(){ $state.go(toState.name,toParams); }); } else if($user.currentUser && (sp.authorize || (authorities && authorities.length))){ if(!authorizeStateConfig(sp, authorities)){ e.preventDefault(); stateChangeUnauthorizedEvent(toState,toParams); } }else if(toState.name===config.loginState){ /* If the user is already logged in, we will redirect away from the login page and send the user to the post login state. */ if($user.currentUser!==false){ e.preventDefault(); $user.get().finally(function(){ if($user.currentUser && $user.currentUser.href){ $state.go(config.defaultPostLoginState); } else { $state.go(toState.name,toParams); } }); } } }); }; function authorizeStateConfig(spStateConfig, authorities){ var sp = spStateConfig; if(sp && sp.authorize && sp.authorize.group) { return $user.currentUser.inGroup(sp.authorize.group); }else if(authorities){ // add support for reading from JHipster's data: { authorities: ['ROLE_ADMIN'] } // https://github.com/stormpath/stormpath-sdk-angularjs/issues/190 var roles = authorities.filter(function(authority){ return $user.currentUser.inGroup(authority); }); return roles.length > 0; }else{ console.error('Unknown authorize configuration for spStateConfig',spStateConfig); return false; } } function routeChangeUnauthenticatedEvent(toRoute) { /** * @ngdoc event * * @name stormpath.$stormpath#$routeChangeUnauthenticated * * @eventOf stormpath.$stormpath * * @eventType broadcast on root scope * * @param {Object} event * * Angular event object. * * @param {Object} toRoute The route that the user attempted to access. * * @description * * This event is broadcast when a route change is prevented, * because the user is not logged in. * * Use this event if you want to implement your own strategy for * presenting the user with a login form. * * To receive this event, you must be using the ngRoute module. * * @example * *
         *   $rootScope.$on('$routeChangeUnauthenticated', function(event, toRoute) {
         *     // Your custom logic for deciding how the user should login, and
         *     // if you want to redirect them to the desired route afterwards
         *   });
         * 
*/ $rootScope.$broadcast(STORMPATH_CONFIG.ROUTE_CHANGE_UNAUTHENTICATED, toRoute); } function routeChangeUnauthorizedEvent(toRoute) { /** * @ngdoc event * * @name stormpath.$stormpath#$routeChangeUnauthorized * * @eventOf stormpath.$stormpath * * @eventType broadcast on root scope * * @param {Object} event * * Angular event object. * * @param {Object} toRoute The route that the user attempted to access. * * @description * * This event is broadcast when a route change is prevented, * because the user is not authorized by the rules defined in the * {@link stormpath.SpRouteConfig:SpRouteConfig Stormpath Route Configuration} * for the requested route. * * Use this event if you want to implement your own strategy for telling * the user that they are forbidden from viewing that route. * * To receive this event, you must be using the ngRoute module. * * @example * *
         *   $rootScope.$on('$routeChangeUnauthorized', function(event, toRoute) {
         *     // Your custom logic for deciding how the user should be
         *     // notified that they are forbidden from this route
         *   });
         * 
*/ $rootScope.$broadcast(STORMPATH_CONFIG.ROUTE_CHANGE_UNAUTHORIZED, toRoute); } StormpathService.prototype.routeChangeInterceptor = function routeChangeInterceptor(config) { function goToRoute(route) { setTimeout(function() { if (route.$$route.originalPath === $location.path()) { $route.reload(); } else { $location.path(route); } }); } $rootScope.$on('$routeChangeStart', function(event, toRoute) { if (!toRoute.$$route) { return; } var sp = toRoute.$$route.sp || {}; // Grab the sp config for this route if ((sp.authenticate || sp.authorize) && !$user.currentUser) { event.preventDefault(); $user.get().then(function() { // The user is authenticated, continue to the requested route if (sp.authorize) { if (authorizeStateConfig(sp)) { goToRoute(toRoute); } else { stateChangeUnauthorizedEvent(toRoute); } } else { goToRoute(toRoute); } }, function() { // The user is not authenticated, emit the necessary event routeChangeUnauthenticatedEvent(toRoute); }); } else if (sp.waitForUser && $user.currentUser === null) { event.preventDefault(); $user.get().finally(function() { goToRoute(toRoute); }); } else if ($user.currentUser && sp.authorize) { if (!authorizeStateConfig(sp)) { event.preventDefault(); routeChangeUnauthorizedEvent(toRoute); } } else if (toRoute.$$route.originalPath === config.loginRoute) { /* If the user is already logged in, we will redirect away from the login page and send the user to the post login route. */ if ($user.currentUser && $user.currentUser.href) { event.preventDefault(); goToRoute(config.defaultPostLoginRoute); } } }); }; function authorizeRouteConfig(spRouteConfig) { var sp = spRouteConfig; if (sp && sp.authorize && sp.authorize.group) { return $user.currentUser.inGroup(sp.authorize.group); } console.error('Unknown authorize configuration for spRouteConfig', sp); return false; } /** * @ngdoc function * * @name stormpath#uiRouter * * @methodOf stormpath.$stormpath * * @param {object} config * * * **`autoRedirect`** - Defaults to true. After the user logs in at * the state defined by `loginState`, they will be redirected back to the * state that was originally requested. * * * **`defaultPostLoginState`** - Where the user should be sent, after login, * if they have visited the login page directly. If you do not define a value, * nothing will happen at the login state. You can alternatively use the * {@link stormpath.authService.$auth#events_$authenticated $authenticated} event to know * that login is successful. * * * **`forbiddenState`** - The UI state name that we should send the user * to if they try to an access a view that they are not authorized to view. * This happens in response to an `authorize` rule in one of your * {@link stormpath.SpStateConfig:SpStateConfig Stormpath State Configurations} * * * **`loginState`** - The UI state name that we should send the user * to if they need to login. You'll probably use `login` for this value. * * @description * * Call this method to enable the integration with the UI Router module. * * When enabled, you can define {@link stormpath.SpStateConfig:SpStateConfig Stormpath State Configurations} on your UI states. * This object allows you to define access control for the state. * * You can pass config options to this integration, the options control the * default behavior around "need to login" and "forbidden" situations. * If you wish to implement your own logic for these situations, simply * omit the options and use the events (documented below) to know * what is happening in the application. * * @example * *
       * angular.module('myApp')
       *   .run(function($stormpath){
       *     $stormpath.uiRouter({
       *       forbiddenState: 'forbidden',
       *       defaultPostLoginState: 'main',
       *       loginState: 'login'
       *     });
       *   });
       * 
*/ StormpathService.prototype.uiRouter = function uiRouter(config){ var self = this; config = typeof config === 'object' ? config : {}; this.stateChangeInterceptor(config); if(config.loginState){ self.unauthenticatedWather = $rootScope.$on(STORMPATH_CONFIG.STATE_CHANGE_UNAUTHENTICATED,function(e,toState,toParams){ self.postLogin = { toState: toState, toParams: toParams }; $state.go(config.loginState); }); } $rootScope.$on(STORMPATH_CONFIG.AUTHENTICATION_SUCCESS_EVENT_NAME,function(){ if(self.postLogin && (config.autoRedirect !== false)){ $state.go(self.postLogin.toState,self.postLogin.toParams).then(function(){ self.postLogin = null; }); }else if(config.defaultPostLoginState){ $state.go(config.defaultPostLoginState); } }); if(config.forbiddenState){ self.forbiddenWatcher = $rootScope.$on(STORMPATH_CONFIG.STATE_CHANGE_UNAUTHORIZED,function(){ $state.go(config.forbiddenState); }); } }; /** * @ngdoc function * * @name stormpath#ngRouter * * @methodOf stormpath.$stormpath * * @param {object} config * * * **`autoRedirect`** - Defaults to true. After the user logs in at * the route defined by `loginRoute`, they will be redirected back to the * route that was originally requested. * * * **`defaultPostLoginRoute`** - Where the user should be sent, after login, * if they have visited the login page directly. If you do not define a value, * nothing will happen at the login route. You can alternatively use the * {@link stormpath.authService.$auth#events_$authenticated $authenticated} event to know * that login is successful. * * * **`forbiddenRoute`** - The route that we should send the user * to if they try to an access a view that they are not authorized to view. * This happens in response to an `authorize` rule in one of your * {@link stormpath.SpRouteConfig:SpRouteConfig Stormpath Route Configurations} * * * **`loginRoute`** - The route name that we should send the user * to if they need to login. You'll probably use `login` for this value. * * @description * * Call this method to enable the integration with the ngRoute module. * * When enabled, you can define {@link stormpath.SpRouteConfig:SpRouteConfig Stormpath Route Configurations} on your routes. * This object allows you to define access control for the route. * * You can pass config options to this integration, the options control the * default behavior around "need to login" and "forbidden" situations. * If you wish to implement your own logic for these situations, simply * omit the options and use the events (documented below) to know * what is happening in the application. * * @example * *
       * angular.module('myApp')
       *   .run(function($stormpath){
       *     $stormpath.ngRouter({
       *       forbiddenRoute: '/forbidden',
       *       defaultPostLoginRoute: '/home',
       *       loginRoute: '/login'
       *     });
       *   });
       * 
*/ StormpathService.prototype.ngRouter = function ngRouter(config) { var self = this; config = typeof config === 'object' ? config : {}; this.routeChangeInterceptor(config); if (config.loginRoute) { this.unauthenticatedWather = $rootScope.$on(STORMPATH_CONFIG.ROUTE_CHANGE_UNAUTHENTICATED, function(event, toRoute) { self.postLogin = { toRoute: toRoute }; $location.path(config.loginRoute); }); } $rootScope.$on(STORMPATH_CONFIG.AUTHENTICATION_SUCCESS_EVENT_NAME, function() { if (self.postLogin && config.autoRedirect !== false) { $location.path(self.postLogin.toRoute); self.postLogin = null; } else if (config.defaultPostLoginRoute) { $location.path(config.defaultPostLoginRoute); } }); if (config.forbiddenRoute) { this.forbiddenWatcher = $rootScope.$on(STORMPATH_CONFIG.ROUTE_CHANGE_UNAUTHORIZED, function() { $location.path(config.forbiddenRoute); }); } }; StormpathService.prototype.regexAttrParser = function regexAttrParser(value){ var expr; if(value instanceof RegExp){ expr = value; }else if(value && /^\/.+\/[gim]?$/.test(value)){ expr = new RegExp(value.split('/')[1],value.split('/')[2]); }else{ expr = value; } return expr; }; function UrlEncodedFormParser(){ // Copy & modify from https://github.com/hapijs/qs/blob/master/lib/stringify.js this.delimiter = '&'; this.arrayPrefixGenerators = { brackets: function (prefix) { return prefix + '[]'; }, indices: function (prefix, key) { return prefix + '[' + key + ']'; }, repeat: function (prefix) { return prefix; } }; return this; } UrlEncodedFormParser.prototype.stringify = function stringify(obj, prefix, generateArrayPrefix) { if (obj instanceof Date) { obj = obj.toISOString(); } else if (obj === null) { obj = ''; } if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean') { return [encodeURIComponent(prefix) + '=' + encodeURIComponent(obj)]; } var values = []; if (typeof obj === 'undefined') { return values; } var objKeys = Object.keys(obj); for (var i = 0, il = objKeys.length; i < il; ++i) { var key = objKeys[i]; if (Array.isArray(obj)) { values = values.concat(this.stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix)); } else { values = values.concat(this.stringify(obj[key], prefix + '[' + key + ']', generateArrayPrefix)); } } return values; }; UrlEncodedFormParser.prototype.encode = function encode(obj, options) { options = options || {}; var delimiter = typeof options.delimiter === 'undefined' ? this.delimiter : options.delimiter; var keys = []; if (typeof obj !== 'object' || obj === null) { return ''; } var arrayFormat; if (options.arrayFormat in this.arrayPrefixGenerators) { arrayFormat = options.arrayFormat; } else if ('indices' in options) { arrayFormat = options.indices ? 'indices' : 'repeat'; } else { arrayFormat = 'indices'; } var generateArrayPrefix = this.arrayPrefixGenerators[arrayFormat]; var objKeys = Object.keys(obj); for (var i = 0, il = objKeys.length; i < il; ++i) { var key = objKeys[i]; keys = keys.concat(this.stringify(obj[key], key, generateArrayPrefix)); } return keys.join(delimiter); }; return new StormpathService(); } ]; }]) .run(['$rootScope','$user','STORMPATH_CONFIG',function($rootScope,$user,STORMPATH_CONFIG){ $rootScope.user = $user.currentUser || null; $user.get().finally(function(){ $rootScope.user = $user.currentUser; }); $rootScope.$on(STORMPATH_CONFIG.GET_USER_EVENT,function(){ $rootScope.user = $user.currentUser; }); $rootScope.$on(STORMPATH_CONFIG.SESSION_END_EVENT,function(){ $rootScope.user = $user.currentUser; }); $rootScope.$on(STORMPATH_CONFIG.SESSION_END_ERROR_EVENT,function(event, error){ console.error('Logout error', error); }); $rootScope.$on(STORMPATH_CONFIG.UNAUTHENTICATED_EVENT,function(event, error){ console.error('UNAUTHENTICATED_EVENT'); }); }]) /** * @ngdoc directive * * @name stormpath.ifUser:ifUser * * @description * * Use this directive to conditionally show an element if the user is logged in. * * @example * *
 * 
*

Hello, {{user.fullName}}

*
*
*/ .directive('ifUser',['$user','$rootScope',function($user,$rootScope){ return { link: function(scope,element){ $rootScope.$watch('user',function(user){ if(user && user.href){ element.removeClass('ng-hide'); }else{ element.addClass('ng-hide'); } }); } }; }]) /** * @ngdoc directive * * @name stormpath.ifNotUser:ifNotUser * * @description * * Use this directive to conditionally show an element if the user is NOT logged in. * * @example * *
 * 
*

Hello, you need to login

*
*
*/ .directive('ifNotUser',['$user','$rootScope',function($user,$rootScope){ return { link: function(scope,element){ $rootScope.$watch('user',function(user){ if(user && user.href){ element.addClass('ng-hide'); }else{ element.removeClass('ng-hide'); } }); } }; }]) /** * @ngdoc directive * * @name stormpath.ifUserInGroup:ifUserInGroup * * @description * * Use this directive to conditionally show an element if the user is logged in * and is a member of the group that is specified by the expression. * * The attribute value MUST be one of: * * * A string expression, surrounded by quotes * * A reference to a property on the $scope. That property can be a string or * regular expression. * * **Note**: This feature depends on the data that is returned by the * {@link api/stormpath.STORMPATH_CONFIG:STORMPATH_CONFIG#properties_CURRENT_USER_URI CURRENT_USER_URI}. * Your server should expand the account's groups before returning the user. * If you are using [express-stormpath](https://github.com/stormpath/express-stormpath), simply use * [Automatic Expansion](http://docs.stormpath.com/nodejs/express/latest/user_data.html#automatic-expansion) * * # Using Regular Expressions * * If using a string expression as the attribute value, you can pass a regular * expression by wrapping it in the literal * syntax, e.g. * * `'/admins/'` would match any group which has *admins* in the name * * `'/admin$/'` would match any group were the name **ends with** *admin* * * If referencing a scope property, you should create the value as a RegExp type, * e.g.: * *
 *    $scope.matchGroup = new RegExp(/admins/);
 *  
* * All regular expressions are evaluated via * [RegExp.prototype.test](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test) * * @example * *
 *   
  
  
  
  






================================================
FILE: example/cors-app/package.json
================================================
{
  "name": "cors-app",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "cors": "^2.7.1",
    "express": "^4.13.3",
    "express-stormpath": "^3.0.0",
    "open": "0.0.5"
  }
}


================================================
FILE: example/cors-app/server.js
================================================
var cors = require('cors');
var express = require('express');
var stormpath = require('express-stormpath');
var open = require('open');
var path = require('path');

/*
  The websiteService is a naive Express app, it's only
  purpose is to serve the assets for our Angular application
 */

var websiteDomain = 'http://a.localhost:3000';

var websiteService = express();

websiteService.use('/',express.static(path.join(__dirname,'client')));

websiteService.listen(3000,function () {
  open(websiteDomain);  // Opens the Angular app in your browser
});

/*
  The apiService is your API that you want to protect with
  Stormpath.  We use the CORS module to whitelist the website
  domain, thus allowing it to communicate with the API domain
 */

var apiService = express();

apiService.use(cors({
  origin: websiteDomain,
  credentials: true
}));

apiService.use(stormpath.init(apiService,{ }));

/*
  We use the loginRequired middleware, as that will assert
  that the Angular client has authenticated and has valid
  OAuth tokens in the cookies that were stpored on the API domain
 */

apiService.get('/api/thing', stormpath.loginRequired, function (req,res) {
  res.json(req.user);
});

apiService.on('stormpath.ready',function() {
  apiService.listen(4000);
});

================================================
FILE: example/dashboard-app/.bowerrc
================================================
{
    "directory": "client/bower_components"
}


================================================
FILE: example/dashboard-app/.buildignore
================================================
*.coffee

================================================
FILE: example/dashboard-app/.editorconfig
================================================
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org

root = true


[*]

# Change these settings to your own preference
indent_style = space
indent_size = 2

# We recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false


================================================
FILE: example/dashboard-app/.gitattributes
================================================
* text=auto

================================================
FILE: example/dashboard-app/.gitignore
================================================
node_modules
public
.tmp
.idea
client/bower_components
dist
/server/config/local.env.js
stormpath.yml


================================================
FILE: example/dashboard-app/.travis.yml
================================================
language: node_js
node_js:
  - '0.10'
  - '0.11'
before_script:
  - npm install -g bower grunt-cli
  - bower install
services: mongodb

================================================
FILE: example/dashboard-app/.yo-rc.json
================================================
{
  "generator-angular-fullstack": {
    "insertRoutes": true,
    "registerRoutesFile": "server/routes.js",
    "routesNeedle": "// Insert routes below",
    "routesBase": "/api/",
    "pluralizeRoutes": true,
    "insertSockets": true,
    "registerSocketsFile": "server/config/socketio.js",
    "socketsNeedle": "// Insert sockets below",
    "filters": {
      "js": true,
      "html": true,
      "css": true,
      "uirouter": true,
      "bootstrap": true,
      "uibootstrap": false
    }
  },
  "generator-ng-component": {
    "routeDirectory": "client/app/",
    "directiveDirectory": "client/app/",
    "filterDirectory": "client/app/",
    "serviceDirectory": "client/app/",
    "basePath": "client",
    "moduleName": "",
    "filters": [
      "uirouter"
    ],
    "extensions": [
      "js",
      "html",
      "css"
    ],
    "directiveSimpleTemplates": "",
    "directiveComplexTemplates": "",
    "filterTemplates": "",
    "serviceTemplates": "",
    "factoryTemplates": "",
    "controllerTemplates": "",
    "decoratorTemplates": "",
    "providerTemplates": "",
    "routeTemplates": ""
  }
}

================================================
FILE: example/dashboard-app/Gruntfile.js
================================================
// Generated on 2015-09-24 using generator-angular-fullstack 2.0.13
'use strict';

module.exports = function (grunt) {
  var localConfig;
  try {
    localConfig = require('./server/config/local.env');
  } catch(e) {
    localConfig = {};
  }

  // Load grunt tasks automatically, when needed
  require('jit-grunt')(grunt, {
    express: 'grunt-express-server',
    useminPrepare: 'grunt-usemin',
    ngtemplates: 'grunt-angular-templates',
    cdnify: 'grunt-google-cdn',
    protractor: 'grunt-protractor-runner',
    injector: 'grunt-asset-injector',
    buildcontrol: 'grunt-build-control'
  });

  // Time how long tasks take. Can help when optimizing build times
  require('time-grunt')(grunt);

  // Define the configuration for all the tasks
  grunt.initConfig({

    // Project settings
    pkg: grunt.file.readJSON('package.json'),
    yeoman: {
      // configurable paths
      client: require('./bower.json').appPath || 'client',
      dist: 'dist'
    },
    express: {
      options: {
        port: process.env.PORT || 9000,
        // output option will force Grunt to wait for this
        // line to be seen in server output, before continuing
        output: 'Stormpath Ready'
      },
      dev: {
        options: {
          script: 'server/app.js',
          debug: true
        }
      },
      prod: {
        options: {
          script: 'dist/server/app.js'
        }
      }
    },
    open: {
      server: {
        url: 'http://localhost:<%= express.options.port %>'
      }
    },
    watch: {
      injectJS: {
        files: [
          '<%= yeoman.client %>/{app,components}/**/*.js',
          '!<%= yeoman.client %>/{app,components}/**/*.spec.js',
          '!<%= yeoman.client %>/{app,components}/**/*.mock.js',
          '!<%= yeoman.client %>/app/app.js'],
        tasks: ['injector:scripts']
      },
      injectCss: {
        files: [
          '<%= yeoman.client %>/{app,components}/**/*.css'
        ],
        tasks: ['injector:css']
      },
      mochaTest: {
        files: ['server/**/*.spec.js'],
        tasks: ['env:test', 'mochaTest']
      },
      jsTest: {
        files: [
          '<%= yeoman.client %>/{app,components}/**/*.spec.js',
          '<%= yeoman.client %>/{app,components}/**/*.mock.js'
        ],
        tasks: ['newer:jshint:all', 'karma']
      },
      gruntfile: {
        files: ['Gruntfile.js']
      },
      livereload: {
        files: [
          '{.tmp,<%= yeoman.client %>}/{app,components}/**/*.css',
          '{.tmp,<%= yeoman.client %>}/{app,components}/**/*.html',
          '{.tmp,<%= yeoman.client %>}/{app,components}/**/*.js',
          '!{.tmp,<%= yeoman.client %>}{app,components}/**/*.spec.js',
          '!{.tmp,<%= yeoman.client %>}/{app,components}/**/*.mock.js',
          '<%= yeoman.client %>/assets/images/{,*//*}*.{png,jpg,jpeg,gif,webp,svg}'
        ],
        options: {
          livereload: true
        }
      },
      express: {
        files: [
          'server/**/*.{js,json}'
        ],
        tasks: ['express:dev', 'wait'],
        options: {
          livereload: true,
          nospawn: true //Without this option specified express won't be reloaded
        }
      }
    },

    // Make sure code styles are up to par and there are no obvious mistakes
    jshint: {
      options: {
        jshintrc: '<%= yeoman.client %>/.jshintrc',
        reporter: require('jshint-stylish')
      },
      server: {
        options: {
          jshintrc: 'server/.jshintrc'
        },
        src: [
          'server/**/*.js',
          '!server/**/*.spec.js'
        ]
      },
      serverTest: {
        options: {
          jshintrc: 'server/.jshintrc-spec'
        },
        src: ['server/**/*.spec.js']
      },
      all: [
        '<%= yeoman.client %>/{app,components}/**/*.js',
        '!<%= yeoman.client %>/{app,components}/**/*.spec.js',
        '!<%= yeoman.client %>/{app,components}/**/*.mock.js'
      ],
      test: {
        src: [
          '<%= yeoman.client %>/{app,components}/**/*.spec.js',
          '<%= yeoman.client %>/{app,components}/**/*.mock.js'
        ]
      }
    },

    // Empties folders to start fresh
    clean: {
      dist: {
        files: [{
          dot: true,
          src: [
            '.tmp',
            '<%= yeoman.dist %>/*',
            '!<%= yeoman.dist %>/.git*',
            '!<%= yeoman.dist %>/.openshift',
            '!<%= yeoman.dist %>/Procfile'
          ]
        }]
      },
      server: '.tmp'
    },

    // Add vendor prefixed styles
    autoprefixer: {
      options: {
        browsers: ['last 1 version']
      },
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/',
          src: '{,*/}*.css',
          dest: '.tmp/'
        }]
      }
    },

    // Debugging with node inspector
    'node-inspector': {
      custom: {
        options: {
          'web-host': 'localhost'
        }
      }
    },

    // Use nodemon to run server in debug mode with an initial breakpoint
    nodemon: {
      debug: {
        script: 'server/app.js',
        options: {
          nodeArgs: ['--debug-brk'],
          env: {
            PORT: process.env.PORT || 9000
          },
          callback: function (nodemon) {
            nodemon.on('log', function (event) {
              console.log(event.colour);
            });

            // opens browser on initial server start
            nodemon.on('config:update', function () {
              setTimeout(function () {
                require('open')('http://localhost:8080/debug?port=5858');
              }, 500);
            });
          }
        }
      }
    },

    // Automatically inject Bower components into the app
    wiredep: {
      target: {
        src: '<%= yeoman.client %>/index.html',
        ignorePath: '<%= yeoman.client %>/',
        exclude: [/bootstrap-sass-official/, /bootstrap.js/, '/json3/', '/es5-shim/']
      }
    },

    // Renames files for browser caching purposes
    rev: {
      dist: {
        files: {
          src: [
            '<%= yeoman.dist %>/public/{,*/}*.js',
            '<%= yeoman.dist %>/public/{,*/}*.css',
            '<%= yeoman.dist %>/public/assets/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
            '<%= yeoman.dist %>/public/assets/fonts/*'
          ]
        }
      }
    },

    // Reads HTML for usemin blocks to enable smart builds that automatically
    // concat, minify and revision files. Creates configurations in memory so
    // additional tasks can operate on them
    useminPrepare: {
      html: ['<%= yeoman.client %>/index.html'],
      options: {
        dest: '<%= yeoman.dist %>/public'
      }
    },

    // Performs rewrites based on rev and the useminPrepare configuration
    usemin: {
      html: ['<%= yeoman.dist %>/public/{,*/}*.html'],
      css: ['<%= yeoman.dist %>/public/{,*/}*.css'],
      js: ['<%= yeoman.dist %>/public/{,*/}*.js'],
      options: {
        assetsDirs: [
          '<%= yeoman.dist %>/public',
          '<%= yeoman.dist %>/public/assets/images'
        ],
        // This is so we update image references in our ng-templates
        patterns: {
          js: [
            [/(assets\/images\/.*?\.(?:gif|jpeg|jpg|png|webp|svg))/gm, 'Update the JS to reference our revved images']
          ]
        }
      }
    },

    // The following *-min tasks produce minified files in the dist folder
    imagemin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.client %>/assets/images',
          src: '{,*/}*.{png,jpg,jpeg,gif}',
          dest: '<%= yeoman.dist %>/public/assets/images'
        }]
      }
    },

    svgmin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.client %>/assets/images',
          src: '{,*/}*.svg',
          dest: '<%= yeoman.dist %>/public/assets/images'
        }]
      }
    },

    // Allow the use of non-minsafe AngularJS files. Automatically makes it
    // minsafe compatible so Uglify does not destroy the ng references
    ngAnnotate: {
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/concat',
          src: '*/**.js',
          dest: '.tmp/concat'
        }]
      }
    },

    // Package all the html partials into a single javascript payload
    ngtemplates: {
      options: {
        // This should be the name of your apps angular module
        module: 'dashboardApp',
        htmlmin: {
          collapseBooleanAttributes: true,
          collapseWhitespace: true,
          removeAttributeQuotes: true,
          removeEmptyAttributes: true,
          removeRedundantAttributes: true,
          removeScriptTypeAttributes: true,
          removeStyleLinkTypeAttributes: true
        },
        usemin: 'app/app.js'
      },
      main: {
        cwd: '<%= yeoman.client %>',
        src: ['{app,components}/**/*.html'],
        dest: '.tmp/templates.js'
      },
      tmp: {
        cwd: '.tmp',
        src: ['{app,components}/**/*.html'],
        dest: '.tmp/tmp-templates.js'
      }
    },

    // Replace Google CDN references
    cdnify: {
      dist: {
        html: ['<%= yeoman.dist %>/public/*.html']
      }
    },

    // Copies remaining files to places other tasks can use
    copy: {
      dist: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= yeoman.client %>',
          dest: '<%= yeoman.dist %>/public',
          src: [
            '*.{ico,png,txt}',
            '.htaccess',
            'bower_components/**/*',
            'assets/images/{,*/}*.{webp}',
            'assets/fonts/**/*',
            'index.html'
          ]
        }, {
          expand: true,
          cwd: '.tmp/images',
          dest: '<%= yeoman.dist %>/public/assets/images',
          src: ['generated/*']
        }, {
          expand: true,
          dest: '<%= yeoman.dist %>',
          src: [
            'package.json',
            'server/**/*'
          ]
        }]
      },
      styles: {
        expand: true,
        cwd: '<%= yeoman.client %>',
        dest: '.tmp/',
        src: ['{app,components}/**/*.css']
      }
    },

    buildcontrol: {
      options: {
        dir: 'dist',
        commit: true,
        push: true,
        connectCommits: false,
        message: 'Built %sourceName% from commit %sourceCommit% on branch %sourceBranch%'
      },
      heroku: {
        options: {
          remote: 'heroku',
          branch: 'master'
        }
      },
      openshift: {
        options: {
          remote: 'openshift',
          branch: 'master'
        }
      }
    },

    // Run some tasks in parallel to speed up the build process
    concurrent: {
      server: [
      ],
      test: [
      ],
      debug: {
        tasks: [
          'nodemon',
          'node-inspector'
        ],
        options: {
          logConcurrentOutput: true
        }
      },
      dist: [
        'imagemin',
        'svgmin'
      ]
    },

    // Test settings
    karma: {
      unit: {
        configFile: 'karma.conf.js',
        singleRun: true
      }
    },

    mochaTest: {
      options: {
        reporter: 'spec'
      },
      src: ['server/**/*.spec.js']
    },

    protractor: {
      options: {
        configFile: 'protractor.conf.js'
      },
      chrome: {
        options: {
          args: {
            browser: 'chrome'
          }
        }
      }
    },

    env: {
      test: {
        NODE_ENV: 'test'
      },
      prod: {
        NODE_ENV: 'production'
      },
      all: localConfig
    },

    injector: {
      options: {

      },
      // Inject application script files into index.html (doesn't include bower)
      scripts: {
        options: {
          transform: function(filePath) {
            filePath = filePath.replace('/client/', '');
            filePath = filePath.replace('/.tmp/', '');
            return '';
          },
          starttag: '',
          endtag: ''
        },
        files: {
          '<%= yeoman.client %>/index.html': [
              ['{.tmp,<%= yeoman.client %>}/{app,components}/**/*.js',
               '!{.tmp,<%= yeoman.client %>}/app/app.js',
               '!{.tmp,<%= yeoman.client %>}/{app,components}/**/*.spec.js',
               '!{.tmp,<%= yeoman.client %>}/{app,components}/**/*.mock.js']
            ]
        }
      },

      // Inject component css into index.html
      css: {
        options: {
          transform: function(filePath) {
            filePath = filePath.replace('/client/', '');
            filePath = filePath.replace('/.tmp/', '');
            return '';
          },
          starttag: '',
          endtag: ''
        },
        files: {
          '<%= yeoman.client %>/index.html': [
            '<%= yeoman.client %>/{app,components}/**/*.css'
          ]
        }
      }
    },
  });

  // Used for delaying livereload until after server has restarted
  grunt.registerTask('wait', function () {
    grunt.log.ok('Waiting for server reload...');

    var done = this.async();

    setTimeout(function () {
      grunt.log.writeln('Done waiting!');
      done();
    }, 1500);
  });

  grunt.registerTask('express-keepalive', 'Keep grunt running', function() {
    this.async();
  });

  grunt.registerTask('serve', function (target) {
    if (target === 'dist') {
      return grunt.task.run(['build', 'env:all', 'env:prod', 'express:prod', 'wait', 'open', 'express-keepalive']);
    }

    if (target === 'debug') {
      return grunt.task.run([
        'clean:server',
        'env:all',
        'concurrent:server',
        'injector',
        'wiredep',
        'autoprefixer',
        'concurrent:debug'
      ]);
    }

    grunt.task.run([
      'clean:server',
      'env:all',
      'concurrent:server',
      'injector',
      'wiredep',
      'autoprefixer',
      'express:dev',
      'wait',
      'open',
      'watch'
    ]);
  });

  grunt.registerTask('server', function () {
    grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
    grunt.task.run(['serve']);
  });

  grunt.registerTask('test', function(target) {
    if (target === 'server') {
      return grunt.task.run([
        'env:all',
        'env:test',
        'mochaTest'
      ]);
    }

    else if (target === 'client') {
      return grunt.task.run([
        'clean:server',
        'env:all',
        'concurrent:test',
        'injector',
        'autoprefixer',
        'karma'
      ]);
    }

    else if (target === 'e2e') {
      return grunt.task.run([
        'clean:server',
        'env:all',
        'env:test',
        'concurrent:test',
        'injector',
        'wiredep',
        'autoprefixer',
        'express:dev',
        'protractor'
      ]);
    }

    else grunt.task.run([
      'test:server',
      'test:client'
    ]);
  });

  grunt.registerTask('build', [
    'clean:dist',
    'concurrent:dist',
    'injector',
    'wiredep',
    'useminPrepare',
    'autoprefixer',
    'ngtemplates',
    'concat',
    'ngAnnotate',
    'copy:dist',
    'cdnify',
    'cssmin',
    'uglify',
    'rev',
    'usemin'
  ]);

  grunt.registerTask('default', [
    'newer:jshint',
    'test',
    'build'
  ]);
};


================================================
FILE: example/dashboard-app/README.md
================================================
# Stormpath Angular SDK Example

This folder contains an example application that is built with the [Stormpath Angular SDK][]
and [Stormpath Express][].

This application is the same one that is built when you follow the [Yeoman Guide][].

#### Running the Example Application

1. To run this application, you will need Bower and Grunt as global packages:

  ```bash
  npm install -g grunt bower
  ```

2. Clone this repo to your computer, and enter the directory for this example:

  ```bash
  git clone https://github.com/stormpath/stormpath-sdk-angularjs.git
  cd stormpath-sdk-angularjs/example/dashboard-app
  ```

3. Install the dependencies:

  ```bash
  npm install
  bower install
  ```
4. Export your environment variables for your Stormpath Tenant and Application:

  ```bash
  export STORMPATH_CLIENT_APIKEY_ID=xxx
  export STORMPATH_CLIENT_APIKEY_SECRET=xxx
  export STORMPATH_APPLICATION_HREF=xxx
  ```

5. Start the server with Grunt, this should start the server and open the Angular
  application in your browser:

  ```bash
  grunt serve
  ```

[Stormpath Angular SDK]: https://github.com/stormpath/stormpath-sdk-angularjs
[Stormpath Express]: https://github.com/stormpath/stormpath-express
[Yeoman Guide]: https://docs.stormpath.com/angularjs/guide


================================================
FILE: example/dashboard-app/bower.json
================================================
{
  "name": "dashboard",
  "version": "0.0.0",
  "dependencies": {
    "angular": ">=1.2.*",
    "json3": "~3.3.1",
    "es5-shim": "~3.0.1",
    "jquery": "~1.11.0",
    "bootstrap": "~3.1.1",
    "angular-resource": ">=1.2.*",
    "angular-cookies": ">=1.2.*",
    "angular-sanitize": ">=1.2.*",
    "font-awesome": ">=4.1.0",
    "lodash": "~2.4.1",
    "angular-ui-router": "~0.2.10",
    "stormpath-sdk-angularjs": "~1.0.0"
  },
  "devDependencies": {
    "angular-mocks": ">=1.2.*",
    "angular-scenario": ">=1.2.*"
  }
}


================================================
FILE: example/dashboard-app/client/.htaccess
================================================
# Apache Configuration File

# (!) Using `.htaccess` files slows down Apache, therefore, if you have access
# to the main server config file (usually called `httpd.conf`), you should add
# this logic there: http://httpd.apache.org/docs/current/howto/htaccess.html.

# ##############################################################################
# # CROSS-ORIGIN RESOURCE SHARING (CORS)                                       #
# ##############################################################################

# ------------------------------------------------------------------------------
# | Cross-domain AJAX requests                                                 |
# ------------------------------------------------------------------------------

# Enable cross-origin AJAX requests.
# http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity
# http://enable-cors.org/

# 
#    Header set Access-Control-Allow-Origin "*"
# 

# ------------------------------------------------------------------------------
# | CORS-enabled images                                                        |
# ------------------------------------------------------------------------------

# Send the CORS header for images when browsers request it.
# https://developer.mozilla.org/en/CORS_Enabled_Image
# http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html
# http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/


    
        
            SetEnvIf Origin ":" IS_CORS
            Header set Access-Control-Allow-Origin "*" env=IS_CORS
        
    


# ------------------------------------------------------------------------------
# | Web fonts access                                                           |
# ------------------------------------------------------------------------------

# Allow access from all domains for web fonts


    
        Header set Access-Control-Allow-Origin "*"
    



# ##############################################################################
# # ERRORS                                                                     #
# ##############################################################################

# ------------------------------------------------------------------------------
# | 404 error prevention for non-existing redirected folders                   |
# ------------------------------------------------------------------------------

# Prevent Apache from returning a 404 error for a rewrite if a directory
# with the same name does not exist.
# http://httpd.apache.org/docs/current/content-negotiation.html#multiviews
# http://www.webmasterworld.com/apache/3808792.htm

Options -MultiViews

# ------------------------------------------------------------------------------
# | Custom error messages / pages                                              |
# ------------------------------------------------------------------------------

# You can customize what Apache returns to the client in case of an error (see
# http://httpd.apache.org/docs/current/mod/core.html#errordocument), e.g.:

ErrorDocument 404 /404.html


# ##############################################################################
# # INTERNET EXPLORER                                                          #
# ##############################################################################

# ------------------------------------------------------------------------------
# | Better website experience                                                  |
# ------------------------------------------------------------------------------

# Force IE to render pages in the highest available mode in the various
# cases when it may not: http://hsivonen.iki.fi/doctype/ie-mode.pdf.


    Header set X-UA-Compatible "IE=edge"
    # `mod_headers` can't match based on the content-type, however, we only
    # want to send this header for HTML pages and not for the other resources
    
        Header unset X-UA-Compatible
    


# ------------------------------------------------------------------------------
# | Cookie setting from iframes                                                |
# ------------------------------------------------------------------------------

# Allow cookies to be set from iframes in IE.

# 
#   Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\""
# 

# ------------------------------------------------------------------------------
# | Screen flicker                                                             |
# ------------------------------------------------------------------------------

# Stop screen flicker in IE on CSS rollovers (this only works in
# combination with the `ExpiresByType` directives for images from below).

# BrowserMatch "MSIE" brokenvary=1
# BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1
# BrowserMatch "Opera" !brokenvary
# SetEnvIf brokenvary 1 force-no-vary


# ##############################################################################
# # MIME TYPES AND ENCODING                                                    #
# ##############################################################################

# ------------------------------------------------------------------------------
# | Proper MIME types for all files                                            |
# ------------------------------------------------------------------------------



  # Audio
    AddType audio/mp4                                   m4a f4a f4b
    AddType audio/ogg                                   oga ogg

  # JavaScript
    # Normalize to standard type (it's sniffed in IE anyways):
    # http://tools.ietf.org/html/rfc4329#section-7.2
    AddType application/javascript                      js jsonp
    AddType application/json                            json

  # Video
    AddType video/mp4                                   mp4 m4v f4v f4p
    AddType video/ogg                                   ogv
    AddType video/webm                                  webm
    AddType video/x-flv                                 flv

  # Web fonts
    AddType application/font-woff                       woff
    AddType application/vnd.ms-fontobject               eot

    # Browsers usually ignore the font MIME types and sniff the content,
    # however, Chrome shows a warning if other MIME types are used for the
    # following fonts.
    AddType application/x-font-ttf                      ttc ttf
    AddType font/opentype                               otf

    # Make SVGZ fonts work on iPad:
    # https://twitter.com/FontSquirrel/status/14855840545
    AddType     image/svg+xml                           svg svgz
    AddEncoding gzip                                    svgz

  # Other
    AddType application/octet-stream                    safariextz
    AddType application/x-chrome-extension              crx
    AddType application/x-opera-extension               oex
    AddType application/x-shockwave-flash               swf
    AddType application/x-web-app-manifest+json         webapp
    AddType application/x-xpinstall                     xpi
    AddType application/xml                             atom rdf rss xml
    AddType image/webp                                  webp
    AddType image/x-icon                                ico
    AddType text/cache-manifest                         appcache manifest
    AddType text/vtt                                    vtt
    AddType text/x-component                            htc
    AddType text/x-vcard                                vcf



# ------------------------------------------------------------------------------
# | UTF-8 encoding                                                             |
# ------------------------------------------------------------------------------

# Use UTF-8 encoding for anything served as `text/html` or `text/plain`.
AddDefaultCharset utf-8

# Force UTF-8 for certain file formats.

    AddCharset utf-8 .atom .css .js .json .rss .vtt .webapp .xml



# ##############################################################################
# # URL REWRITES                                                               #
# ##############################################################################

# ------------------------------------------------------------------------------
# | Rewrite engine                                                             |
# ------------------------------------------------------------------------------

# Turning on the rewrite engine and enabling the `FollowSymLinks` option is
# necessary for the following directives to work.

# If your web host doesn't allow the `FollowSymlinks` option, you may need to
# comment it out and use `Options +SymLinksIfOwnerMatch` but, be aware of the
# performance impact: http://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks

# Also, some cloud hosting services require `RewriteBase` to be set:
# http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-mod-rewrite-not-working-on-my-site


    Options +FollowSymlinks
  # Options +SymLinksIfOwnerMatch
    RewriteEngine On
  # RewriteBase /


# ------------------------------------------------------------------------------
# | Suppressing / Forcing the "www." at the beginning of URLs                  |
# ------------------------------------------------------------------------------

# The same content should never be available under two different URLs especially
# not with and without "www." at the beginning. This can cause SEO problems
# (duplicate content), therefore, you should choose one of the alternatives and
# redirect the other one.

# By default option 1 (no "www.") is activated:
# http://no-www.org/faq.php?q=class_b

# If you'd prefer to use option 2, just comment out all the lines from option 1
# and uncomment the ones from option 2.

# IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME!

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# Option 1: rewrite www.example.com → example.com


    RewriteCond %{HTTPS} !=on
    RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
    RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# Option 2: rewrite example.com → www.example.com

# Be aware that the following might not be a good idea if you use "real"
# subdomains for certain parts of your website.

# 
#    RewriteCond %{HTTPS} !=on
#    RewriteCond %{HTTP_HOST} !^www\..+$ [NC]
#    RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
# 


# ##############################################################################
# # SECURITY                                                                   #
# ##############################################################################

# ------------------------------------------------------------------------------
# | Content Security Policy (CSP)                                              |
# ------------------------------------------------------------------------------

# You can mitigate the risk of cross-site scripting and other content-injection
# attacks by setting a Content Security Policy which whitelists trusted sources
# of content for your site.

# The example header below allows ONLY scripts that are loaded from the current
# site's origin (no inline scripts, no CDN, etc). This almost certainly won't
# work as-is for your site!

# To get all the details you'll need to craft a reasonable policy for your site,
# read: http://html5rocks.com/en/tutorials/security/content-security-policy (or
# see the specification: http://w3.org/TR/CSP).

# 
#    Header set Content-Security-Policy "script-src 'self'; object-src 'self'"
#    
#        Header unset Content-Security-Policy
#    
# 

# ------------------------------------------------------------------------------
# | File access                                                                |
# ------------------------------------------------------------------------------

# Block access to directories without a default document.
# Usually you should leave this uncommented because you shouldn't allow anyone
# to surf through every directory on your server (which may includes rather
# private places like the CMS's directories).


    Options -Indexes


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# Block access to hidden files and directories.
# This includes directories used by version control systems such as Git and SVN.


    RewriteCond %{SCRIPT_FILENAME} -d [OR]
    RewriteCond %{SCRIPT_FILENAME} -f
    RewriteRule "(^|/)\." - [F]


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# Block access to backup and source files.
# These files may be left by some text editors and can pose a great security
# danger when anyone has access to them.


    Order allow,deny
    Deny from all
    Satisfy All


# ------------------------------------------------------------------------------
# | Secure Sockets Layer (SSL)                                                 |
# ------------------------------------------------------------------------------

# Rewrite secure requests properly to prevent SSL certificate warnings, e.g.:
# prevent `https://www.example.com` when your certificate only allows
# `https://secure.example.com`.

# 
#    RewriteCond %{SERVER_PORT} !^443
#    RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L]
# 

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# Force client-side SSL redirection.

# If a user types "example.com" in his browser, the above rule will redirect him
# to the secure version of the site. That still leaves a window of opportunity
# (the initial HTTP connection) for an attacker to downgrade or redirect the
# request. The following header ensures that browser will ONLY connect to your
# server via HTTPS, regardless of what the users type in the address bar.
# http://www.html5rocks.com/en/tutorials/security/transport-layer-security/

# 
#    Header set Strict-Transport-Security max-age=16070400;
# 

# ------------------------------------------------------------------------------
# | Server software information                                                |
# ------------------------------------------------------------------------------

# Avoid displaying the exact Apache version number, the description of the
# generic OS-type and the information about Apache's compiled-in modules.

# ADD THIS DIRECTIVE IN THE `httpd.conf` AS IT WILL NOT WORK IN THE `.htaccess`!

# ServerTokens Prod


# ##############################################################################
# # WEB PERFORMANCE                                                            #
# ##############################################################################

# ------------------------------------------------------------------------------
# | Compression                                                                |
# ------------------------------------------------------------------------------



    # Force compression for mangled headers.
    # http://developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping
    
        
            SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
            RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
        
    

    # Compress all output labeled with one of the following MIME-types
    # (for Apache versions below 2.3.7, you don't need to enable `mod_filter`
    #  and can remove the `` and `` lines
    #  as `AddOutputFilterByType` is still in the core directives).
    
        AddOutputFilterByType DEFLATE application/atom+xml \
                                      application/javascript \
                                      application/json \
                                      application/rss+xml \
                                      application/vnd.ms-fontobject \
                                      application/x-font-ttf \
                                      application/x-web-app-manifest+json \
                                      application/xhtml+xml \
                                      application/xml \
                                      font/opentype \
                                      image/svg+xml \
                                      image/x-icon \
                                      text/css \
                                      text/html \
                                      text/plain \
                                      text/x-component \
                                      text/xml
    



# ------------------------------------------------------------------------------
# | Content transformations                                                    |
# ------------------------------------------------------------------------------

# Prevent some of the mobile network providers from modifying the content of
# your site: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5.

# 
#    Header set Cache-Control "no-transform"
# 

# ------------------------------------------------------------------------------
# | ETag removal                                                               |
# ------------------------------------------------------------------------------

# Since we're sending far-future expires headers (see below), ETags can
# be removed: http://developer.yahoo.com/performance/rules.html#etags.

# `FileETag None` is not enough for every server.

    Header unset ETag


FileETag None

# ------------------------------------------------------------------------------
# | Expires headers (for better cache control)                                 |
# ------------------------------------------------------------------------------

# The following expires headers are set pretty far in the future. If you don't
# control versioning with filename-based cache busting, consider lowering the
# cache time for resources like CSS and JS to something like 1 week.



    ExpiresActive on
    ExpiresDefault                                      "access plus 1 month"

  # CSS
    ExpiresByType text/css                              "access plus 1 year"

  # Data interchange
    ExpiresByType application/json                      "access plus 0 seconds"
    ExpiresByType application/xml                       "access plus 0 seconds"
    ExpiresByType text/xml                              "access plus 0 seconds"

  # Favicon (cannot be renamed!)
    ExpiresByType image/x-icon                          "access plus 1 week"

  # HTML components (HTCs)
    ExpiresByType text/x-component                      "access plus 1 month"

  # HTML
    ExpiresByType text/html                             "access plus 0 seconds"

  # JavaScript
    ExpiresByType application/javascript                "access plus 1 year"

  # Manifest files
    ExpiresByType application/x-web-app-manifest+json   "access plus 0 seconds"
    ExpiresByType text/cache-manifest                   "access plus 0 seconds"

  # Media
    ExpiresByType audio/ogg                             "access plus 1 month"
    ExpiresByType image/gif                             "access plus 1 month"
    ExpiresByType image/jpeg                            "access plus 1 month"
    ExpiresByType image/png                             "access plus 1 month"
    ExpiresByType video/mp4                             "access plus 1 month"
    ExpiresByType video/ogg                             "access plus 1 month"
    ExpiresByType video/webm                            "access plus 1 month"

  # Web feeds
    ExpiresByType application/atom+xml                  "access plus 1 hour"
    ExpiresByType application/rss+xml                   "access plus 1 hour"

  # Web fonts
    ExpiresByType application/font-woff                 "access plus 1 month"
    ExpiresByType application/vnd.ms-fontobject         "access plus 1 month"
    ExpiresByType application/x-font-ttf                "access plus 1 month"
    ExpiresByType font/opentype                         "access plus 1 month"
    ExpiresByType image/svg+xml                         "access plus 1 month"



# ------------------------------------------------------------------------------
# | Filename-based cache busting                                               |
# ------------------------------------------------------------------------------

# If you're not using a build process to manage your filename version revving,
# you might want to consider enabling the following directives to route all
# requests such as `/css/style.12345.css` to `/css/style.css`.

# To understand why this is important and a better idea than `*.css?v231`, read:
# http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring

# 
#    RewriteCond %{REQUEST_FILENAME} !-f
#    RewriteCond %{REQUEST_FILENAME} !-d
#    RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L]
# 

# ------------------------------------------------------------------------------
# | File concatenation                                                         |
# ------------------------------------------------------------------------------

# Allow concatenation from within specific CSS and JS files, e.g.:
# Inside of `script.combined.js` you could have
#   
#   
# and they would be included into this single file.

# 
#    
#        Options +Includes
#        AddOutputFilterByType INCLUDES application/javascript application/json
#        SetOutputFilter INCLUDES
#    
#    
#        Options +Includes
#        AddOutputFilterByType INCLUDES text/css
#        SetOutputFilter INCLUDES
#    
# 

# ------------------------------------------------------------------------------
# | Persistent connections                                                     |
# ------------------------------------------------------------------------------

# Allow multiple requests to be sent over the same TCP connection:
# http://httpd.apache.org/docs/current/en/mod/core.html#keepalive.

# Enable if you serve a lot of static content but, be aware of the
# possible disadvantages!

# 
#    Header set Connection Keep-Alive
# 


================================================
FILE: example/dashboard-app/client/.jshintrc
================================================
{
  "node": true,
  "browser": true,
  "esnext": true,
  "bitwise": true,
  "camelcase": true,
  "curly": true,
  "eqeqeq": true,
  "immed": true,
  "indent": 2,
  "latedef": true,
  "newcap": true,
  "noarg": true,
  "quotmark": "single",
  "regexp": true,
  "undef": true,
  "unused": true,
  "strict": true,
  "trailing": true,
  "smarttabs": true,
  "globals": {
    "jQuery": true,
    "angular": true,
    "console": true,
    "$": true,
    "_": true,
    "moment": true,
    "describe": true,
    "beforeEach": true,
    "module": true,
    "inject": true,
    "it": true,
    "expect": true,
    "browser": true,
    "element": true,
    "by": true
  }
}


================================================
FILE: example/dashboard-app/client/app/app.css
================================================

/**
 * Bootstrap Fonts
 */

@font-face {
    font-family: 'Glyphicons Halflings';
    src: url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.eot');
    src: url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),
    url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.woff') format('woff'),
    url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.ttf') format('truetype'),
    url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
}

/**
 *Font Awesome Fonts
 */

@font-face {
    font-family: 'FontAwesome';
    src: url('../bower_components/font-awesome/fonts/fontawesome-webfont.eot?v=4.1.0');
    src: url('../bower_components/font-awesome/fonts/fontawesome-webfont.eot?#iefix&v=4.1.0') format('embedded-opentype'),
    url('../bower_components/font-awesome/fonts/fontawesome-webfont.woff?v=4.1.0') format('woff'),
    url('../bower_components/font-awesome/fonts/fontawesome-webfont.ttf?v=4.1.0') format('truetype'),
    url('../bower_components/font-awesome/fonts/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular') format('svg');
    font-weight: normal;
    font-style: normal;
}

/**
 * App-wide Styles
 */

.browsehappy {
    margin: 0.2em 0;
    background: #ccc;
    color: #000;
    padding: 0.2em 0;
}


================================================
FILE: example/dashboard-app/client/app/app.js
================================================
'use strict';

angular.module('dashboardApp', [
  'ngCookies',
  'ngResource',
  'ngSanitize',
  'ui.router',
  'stormpath',
  'stormpath.templates'
])
  .config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
    $urlRouterProvider
      .otherwise('/');

    $locationProvider.html5Mode(true);
  })
  .run(function($stormpath,$rootScope,$state){

    /*
      In this example we use UI router, and this
      is how we tell the Stormpath module which
      states we would like to use to use for login
      and post-login
     */

    $stormpath.uiRouter({
      loginState: 'login',
      defaultPostLoginState: 'main'
    });

    /*
      We want to redirect users back to the login
      state after they logout, so we watch for the
      logout event and then transition them to the
      login state
     */
    $rootScope.$on('$sessionEnd',function () {
      $state.transitionTo('login');
    });
  });

================================================
FILE: example/dashboard-app/client/app/forgot/forgot.controller.js
================================================
'use strict';

angular.module('dashboardApp')
  .controller('ForgotCtrl', function ($scope) {
    $scope.message = 'Hello';
  });


================================================
FILE: example/dashboard-app/client/app/forgot/forgot.controller.spec.js
================================================
'use strict';

describe('Controller: ForgotCtrl', function () {

  // load the controller's module
  beforeEach(module('dashboardApp'));

  var ForgotCtrl, scope;

  // Initialize the controller and a mock scope
  beforeEach(inject(function ($controller, $rootScope) {
    scope = $rootScope.$new();
    ForgotCtrl = $controller('ForgotCtrl', {
      $scope: scope
    });
  }));

  it('should ...', function () {
    expect(1).toEqual(1);
  });
});


================================================
FILE: example/dashboard-app/client/app/forgot/forgot.css
================================================


================================================
FILE: example/dashboard-app/client/app/forgot/forgot.html
================================================

Forgot Password


================================================ FILE: example/dashboard-app/client/app/forgot/forgot.js ================================================ 'use strict'; angular.module('dashboardApp') .config(function ($stateProvider) { $stateProvider .state('forgot', { url: '/forgot', templateUrl: 'app/forgot/forgot.html', controller: 'ForgotCtrl' }); }); ================================================ FILE: example/dashboard-app/client/app/login/login.controller.js ================================================ 'use strict'; angular.module('dashboardApp') .controller('LoginCtrl', function ($scope) { $scope.message = 'Hello'; }); ================================================ FILE: example/dashboard-app/client/app/login/login.controller.spec.js ================================================ 'use strict'; describe('Controller: LoginCtrl', function () { // load the controller's module beforeEach(module('dashboardApp')); var LoginCtrl, scope; // Initialize the controller and a mock scope beforeEach(inject(function ($controller, $rootScope) { scope = $rootScope.$new(); LoginCtrl = $controller('LoginCtrl', { $scope: scope }); })); it('should ...', function () { expect(1).toEqual(1); }); }); ================================================ FILE: example/dashboard-app/client/app/login/login.css ================================================ ================================================ FILE: example/dashboard-app/client/app/login/login.html ================================================

Login


================================================ FILE: example/dashboard-app/client/app/login/login.js ================================================ 'use strict'; angular.module('dashboardApp') .config(function ($stateProvider) { $stateProvider .state('login', { url: '/login', templateUrl: 'app/login/login.html', controller: 'LoginCtrl' }); }); ================================================ FILE: example/dashboard-app/client/app/main/main.controller.js ================================================ 'use strict'; angular.module('dashboardApp') .controller('MainCtrl', function ($scope, $http) { $scope.awesomeThings = []; $http.get('/api/things').success(function(awesomeThings) { $scope.awesomeThings = awesomeThings; }); }); ================================================ FILE: example/dashboard-app/client/app/main/main.controller.spec.js ================================================ 'use strict'; describe('Controller: MainCtrl', function () { // load the controller's module beforeEach(module('dashboardApp')); var MainCtrl, scope, $httpBackend; // Initialize the controller and a mock scope beforeEach(inject(function (_$httpBackend_, $controller, $rootScope) { $httpBackend = _$httpBackend_; $httpBackend.expectGET('/api/things') .respond(['HTML5 Boilerplate', 'AngularJS', 'Karma', 'Express']); scope = $rootScope.$new(); MainCtrl = $controller('MainCtrl', { $scope: scope }); })); it('should attach a list of things to the scope', function () { $httpBackend.flush(); expect(scope.awesomeThings.length).toBe(4); }); }); ================================================ FILE: example/dashboard-app/client/app/main/main.css ================================================ .thing-form { margin: 20px 0; } #banner { border-bottom: none; margin-top: -20px; } #banner h1 { font-size: 60px; line-height: 1; letter-spacing: -1px; } .hero-unit { position: relative; padding: 30px 15px; color: #F5F5F5; text-align: center; text-shadow: 0 1px 0 rgba(0, 0, 0, 0.1); background: #4393B9; } .footer { text-align: center; padding: 30px 0; margin-top: 70px; border-top: 1px solid #E5E5E5; } ================================================ FILE: example/dashboard-app/client/app/main/main.html ================================================

Features:

================================================ FILE: example/dashboard-app/client/app/main/main.js ================================================ 'use strict'; angular.module('dashboardApp') .config(function ($stateProvider) { $stateProvider .state('main', { url: '/', templateUrl: 'app/main/main.html', controller: 'MainCtrl' }); }); ================================================ FILE: example/dashboard-app/client/app/profile/profile.controller.js ================================================ 'use strict'; angular.module('dashboardApp') .controller('ProfileCtrl', function ($scope) { $scope.message = 'Hello'; }); ================================================ FILE: example/dashboard-app/client/app/profile/profile.controller.spec.js ================================================ 'use strict'; describe('Controller: ProfileCtrl', function () { // load the controller's module beforeEach(module('dashboardApp')); var ProfileCtrl, scope; // Initialize the controller and a mock scope beforeEach(inject(function ($controller, $rootScope) { scope = $rootScope.$new(); ProfileCtrl = $controller('ProfileCtrl', { $scope: scope }); })); it('should ...', function () { expect(1).toEqual(1); }); }); ================================================ FILE: example/dashboard-app/client/app/profile/profile.css ================================================ ================================================ FILE: example/dashboard-app/client/app/profile/profile.html ================================================

My Profile



    
================================================ FILE: example/dashboard-app/client/app/profile/profile.js ================================================ 'use strict'; angular.module('dashboardApp') .config(function ($stateProvider) { $stateProvider .state('profile', { url: '/profile', templateUrl: 'app/profile/profile.html', controller: 'ProfileCtrl', sp: { authenticate: true } }); }); ================================================ FILE: example/dashboard-app/client/app/register/register.controller.js ================================================ 'use strict'; angular.module('dashboardApp') .controller('RegisterCtrl', function ($scope) { $scope.message = 'Hello'; }); ================================================ FILE: example/dashboard-app/client/app/register/register.controller.spec.js ================================================ 'use strict'; describe('Controller: RegisterCtrl', function () { // load the controller's module beforeEach(module('dashboardApp')); var RegisterCtrl, scope; // Initialize the controller and a mock scope beforeEach(inject(function ($controller, $rootScope) { scope = $rootScope.$new(); RegisterCtrl = $controller('RegisterCtrl', { $scope: scope }); })); it('should ...', function () { expect(1).toEqual(1); }); }); ================================================ FILE: example/dashboard-app/client/app/register/register.css ================================================ ================================================ FILE: example/dashboard-app/client/app/register/register.html ================================================

Registration


================================================ FILE: example/dashboard-app/client/app/register/register.js ================================================ 'use strict'; angular.module('dashboardApp') .config(function ($stateProvider) { $stateProvider .state('register', { url: '/register', templateUrl: 'app/register/register.html', controller: 'RegisterCtrl' }); }); ================================================ FILE: example/dashboard-app/client/app/reset/reset.controller.js ================================================ 'use strict'; angular.module('dashboardApp') .controller('ResetCtrl', function ($scope) { $scope.message = 'Hello'; }); ================================================ FILE: example/dashboard-app/client/app/reset/reset.controller.spec.js ================================================ 'use strict'; describe('Controller: ResetCtrl', function () { // load the controller's module beforeEach(module('dashboardApp')); var ResetCtrl, scope; // Initialize the controller and a mock scope beforeEach(inject(function ($controller, $rootScope) { scope = $rootScope.$new(); ResetCtrl = $controller('ResetCtrl', { $scope: scope }); })); it('should ...', function () { expect(1).toEqual(1); }); }); ================================================ FILE: example/dashboard-app/client/app/reset/reset.css ================================================ ================================================ FILE: example/dashboard-app/client/app/reset/reset.html ================================================

Reset Your Password


================================================ FILE: example/dashboard-app/client/app/reset/reset.js ================================================ 'use strict'; angular.module('dashboardApp') .config(function ($stateProvider) { $stateProvider .state('reset', { url: '/reset?sptoken', templateUrl: 'app/reset/reset.html', controller: 'ResetCtrl' }); }); ================================================ FILE: example/dashboard-app/client/app/verify/verify.controller.js ================================================ 'use strict'; angular.module('dashboardApp') .controller('VerifyCtrl', function ($scope) { $scope.message = 'Hello'; }); ================================================ FILE: example/dashboard-app/client/app/verify/verify.controller.spec.js ================================================ 'use strict'; describe('Controller: VerifyCtrl', function () { // load the controller's module beforeEach(module('dashboardApp')); var VerifyCtrl, scope; // Initialize the controller and a mock scope beforeEach(inject(function ($controller, $rootScope) { scope = $rootScope.$new(); VerifyCtrl = $controller('VerifyCtrl', { $scope: scope }); })); it('should ...', function () { expect(1).toEqual(1); }); }); ================================================ FILE: example/dashboard-app/client/app/verify/verify.css ================================================ ================================================ FILE: example/dashboard-app/client/app/verify/verify.html ================================================

Verify Your Account


================================================ FILE: example/dashboard-app/client/app/verify/verify.js ================================================ 'use strict'; angular.module('dashboardApp') .config(function ($stateProvider) { $stateProvider .state('verify', { url: '/verify?sptoken', templateUrl: 'app/verify/verify.html', controller: 'VerifyCtrl' }); }); ================================================ FILE: example/dashboard-app/client/components/navbar/navbar.controller.js ================================================ 'use strict'; angular.module('dashboardApp') .controller('NavbarCtrl', function ($scope, $location) { $scope.menu = [{ 'title': 'Home', 'link': '/' }]; $scope.isCollapsed = true; $scope.isActive = function(route) { return route === $location.path(); }; }); ================================================ FILE: example/dashboard-app/client/components/navbar/navbar.html ================================================ ================================================ FILE: example/dashboard-app/client/index.html ================================================
================================================ FILE: example/dashboard-app/client/robots.txt ================================================ # robotstxt.org User-agent: * ================================================ FILE: example/dashboard-app/e2e/main/main.po.js ================================================ /** * This file uses the Page Object pattern to define the main page for tests * https://docs.google.com/presentation/d/1B6manhG0zEXkC-H-tPo2vwU06JhL8w9-XCF9oehXzAQ */ 'use strict'; var MainPage = function() { this.heroEl = element(by.css('.hero-unit')); this.h1El = this.heroEl.element(by.css('h1')); this.imgEl = this.heroEl.element(by.css('img')); }; module.exports = new MainPage(); ================================================ FILE: example/dashboard-app/e2e/main/main.spec.js ================================================ 'use strict'; describe('Main View', function() { var page; beforeEach(function() { browser.get('/'); page = require('./main.po'); }); it('should include jumbotron with correct data', function() { expect(page.h1El.getText()).toBe('\'Allo, \'Allo!'); expect(page.imgEl.getAttribute('src')).toMatch(/assets\/images\/yeoman.png$/); expect(page.imgEl.getAttribute('alt')).toBe('I\'m Yeoman'); }); }); ================================================ FILE: example/dashboard-app/karma.conf.js ================================================ // Karma configuration // http://karma-runner.github.io/0.10/config/configuration-file.html module.exports = function(config) { config.set({ // base path, that will be used to resolve files and exclude basePath: '', // testing framework to use (jasmine/mocha/qunit/...) frameworks: ['jasmine'], // list of files / patterns to load in the browser files: [ 'client/bower_components/jquery/dist/jquery.js', 'client/bower_components/angular/angular.js', 'client/bower_components/angular-mocks/angular-mocks.js', 'client/bower_components/angular-resource/angular-resource.js', 'client/bower_components/angular-cookies/angular-cookies.js', 'client/bower_components/angular-sanitize/angular-sanitize.js', 'client/bower_components/angular-route/angular-route.js', 'client/bower_components/lodash/dist/lodash.compat.js', 'client/bower_components/angular-ui-router/release/angular-ui-router.js', 'client/app/app.js', 'client/app/app.coffee', 'client/app/**/*.js', 'client/app/**/*.coffee', 'client/components/**/*.js', 'client/components/**/*.coffee', 'client/app/**/*.jade', 'client/components/**/*.jade', 'client/app/**/*.html', 'client/components/**/*.html' ], preprocessors: { '**/*.jade': 'ng-jade2js', '**/*.html': 'html2js', '**/*.coffee': 'coffee', }, ngHtml2JsPreprocessor: { stripPrefix: 'client/' }, ngJade2JsPreprocessor: { stripPrefix: 'client/' }, // list of files / patterns to exclude exclude: [], // web server port port: 8080, // level of logging // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG logLevel: config.LOG_INFO, // enable / disable watching file and executing tests whenever any file changes autoWatch: false, // Start these browsers, currently available: // - Chrome // - ChromeCanary // - Firefox // - Opera // - Safari (only Mac) // - PhantomJS // - IE (only Windows) browsers: ['PhantomJS'], // Continuous Integration mode // if true, it capture browsers, run tests and exit singleRun: false }); }; ================================================ FILE: example/dashboard-app/package.json ================================================ { "name": "dashboard", "version": "0.0.0", "main": "server/app.js", "dependencies": { "body-parser": "~1.5.0", "composable-middleware": "^0.3.0", "compression": "~1.0.1", "connect-mongo": "^0.4.1", "cookie-parser": "~1.0.1", "ejs": "~0.8.4", "errorhandler": "~1.0.0", "express": "^4.13.3", "express-session": "~1.0.2", "express-stormpath": "^3.0.0", "lodash": "~2.4.1", "method-override": "~1.0.0", "morgan": "~1.0.0", "serve-favicon": "~2.0.1" }, "devDependencies": { "grunt": "~0.4.4", "grunt-autoprefixer": "~0.7.2", "grunt-wiredep": "~1.8.0", "grunt-concurrent": "~0.5.0", "grunt-contrib-clean": "~0.5.0", "grunt-contrib-concat": "~0.4.0", "grunt-contrib-copy": "~0.5.0", "grunt-contrib-cssmin": "~0.9.0", "grunt-contrib-htmlmin": "~0.2.0", "grunt-contrib-imagemin": "~0.7.1", "grunt-contrib-jshint": "~0.10.0", "grunt-contrib-uglify": "~0.4.0", "grunt-contrib-watch": "~0.6.1", "grunt-google-cdn": "~0.4.0", "grunt-newer": "~0.7.0", "grunt-ng-annotate": "^0.2.3", "grunt-rev": "~0.1.0", "grunt-svgmin": "~0.4.0", "grunt-usemin": "~2.1.1", "grunt-env": "~0.4.1", "grunt-node-inspector": ">=0.2.0", "grunt-nodemon": "~0.2.0", "grunt-angular-templates": "^0.5.4", "grunt-dom-munger": "^3.4.0", "grunt-protractor-runner": "^1.1.0", "grunt-asset-injector": "^0.1.0", "grunt-karma": "~0.8.2", "grunt-build-control": "DaftMonk/grunt-build-control", "grunt-mocha-test": "~0.10.2", "jit-grunt": "^0.5.0", "time-grunt": "~0.3.1", "grunt-express-server": "~0.4.17", "grunt-open": "~0.2.3", "open": "~0.0.4", "jshint-stylish": "~0.1.5", "connect-livereload": "~0.4.0", "karma-ng-scenario": "~0.1.0", "karma-firefox-launcher": "~0.1.3", "karma-script-launcher": "~0.1.0", "karma-html2js-preprocessor": "~0.1.0", "karma-ng-jade2js-preprocessor": "^0.1.2", "karma-jasmine": "~0.1.5", "karma-chrome-launcher": "~0.1.3", "requirejs": "~2.1.11", "karma-requirejs": "~0.2.1", "karma-coffee-preprocessor": "~0.2.1", "karma-jade-preprocessor": "0.0.11", "karma-phantomjs-launcher": "~0.1.4", "karma": "~0.12.9", "karma-ng-html2js-preprocessor": "~0.1.0", "supertest": "~0.11.0", "should": "~3.3.1" }, "engines": { "node": ">=0.10.0" }, "scripts": { "start": "node server/app.js", "test": "grunt test", "update-webdriver": "node node_modules/grunt-protractor-runner/node_modules/protractor/bin/webdriver-manager update" }, "private": true } ================================================ FILE: example/dashboard-app/protractor.conf.js ================================================ // Protractor configuration // https://github.com/angular/protractor/blob/master/referenceConf.js 'use strict'; exports.config = { // The timeout for each script run on the browser. This should be longer // than the maximum time your application needs to stabilize between tasks. allScriptsTimeout: 110000, // A base URL for your application under test. Calls to protractor.get() // with relative paths will be prepended with this. baseUrl: 'http://localhost:' + (process.env.PORT || '9000'), // If true, only chromedriver will be started, not a standalone selenium. // Tests for browsers other than chrome will not run. chromeOnly: true, // list of files / patterns to load in the browser specs: [ 'e2e/**/*.spec.js' ], // Patterns to exclude. exclude: [], // ----- Capabilities to be passed to the webdriver instance ---- // // For a full list of available capabilities, see // https://code.google.com/p/selenium/wiki/DesiredCapabilities // and // https://code.google.com/p/selenium/source/browse/javascript/webdriver/capabilities.js capabilities: { 'browserName': 'chrome' }, // ----- The test framework ----- // // Jasmine and Cucumber are fully supported as a test and assertion framework. // Mocha has limited beta support. You will need to include your own // assertion framework if working with mocha. framework: 'jasmine', // ----- Options to be passed to minijasminenode ----- // // See the full list at https://github.com/juliemr/minijasminenode jasmineNodeOpts: { defaultTimeoutInterval: 30000 } }; ================================================ FILE: example/dashboard-app/server/.jshintrc ================================================ { "node": true, "esnext": true, "bitwise": true, "eqeqeq": true, "immed": true, "latedef": "nofunc", "newcap": true, "noarg": true, "regexp": true, "undef": true, "smarttabs": true, "asi": true, "debug": true } ================================================ FILE: example/dashboard-app/server/.jshintrc-spec ================================================ { "extends": ".jshintrc", "globals": { "describe": true, "it": true, "before": true, "beforeEach": true, "after": true, "afterEach": true } } ================================================ FILE: example/dashboard-app/server/api/thing/index.js ================================================ 'use strict'; var express = require('express'); var controller = require('./thing.controller'); var router = express.Router(); router.get('/', controller.index); module.exports = router; ================================================ FILE: example/dashboard-app/server/api/thing/thing.controller.js ================================================ /** * Using Rails-like standard naming convention for endpoints. * GET /things -> index * POST /things -> create * GET /things/:id -> show * PUT /things/:id -> update * DELETE /things/:id -> destroy */ 'use strict'; var _ = require('lodash'); // Get list of things exports.index = function(req, res) { res.json([ { name : 'Development Tools', info : 'Integration with popular tools such as Bower, Grunt, Karma, Mocha, JSHint, Node Inspector, Livereload, Protractor, Jade, Stylus, Sass, CoffeeScript, and Less.' }, { name : 'Server and Client integration', info : 'Built with a powerful and fun stack: MongoDB, Express, AngularJS, and Node.' }, { name : 'Smart Build System', info : 'Build system ignores `spec` files, allowing you to keep tests alongside code. Automatic injection of scripts and styles into your index.html' }, { name : 'Modular Structure', info : 'Best practice client and server structures allow for more code reusability and maximum scalability' }, { name : 'Optimized Build', info : 'Build process packs up your templates as a single JavaScript payload, minifies your scripts/css/images, and rewrites asset names for caching.' },{ name : 'Deployment Ready', info : 'Easily deploy your app to Heroku or Openshift with the heroku and openshift subgenerators' } ]); }; ================================================ FILE: example/dashboard-app/server/api/thing/thing.spec.js ================================================ 'use strict'; var should = require('should'); var app = require('../../app'); var request = require('supertest'); describe('GET /api/things', function() { it('should respond with JSON array', function(done) { request(app) .get('/api/things') .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { if (err) return done(err); res.body.should.be.instanceof(Array); done(); }); }); }); ================================================ FILE: example/dashboard-app/server/app.js ================================================ /** * Main application file */ 'use strict'; // Set default node environment to development process.env.NODE_ENV = process.env.NODE_ENV || 'development'; var express = require('express'); var config = require('./config/environment'); var ExpressStormpath = require('express-stormpath'); // Setup server var app = express(); var server = require('http').createServer(app); /* The config/express file is setting up the static file server which serves your angular application assets. We don't need to authenticate those requests, so we do this before calling Stormpath. */ require('./config/express')(app); console.log('Initializing Stormpath'); /* Now we initialize Stormpath, any middleware that is registered after this point will be protected by Stormpath. The spa setting tells the Stormpath library where your Angular app is, as it will need to serve it for the default routes like /login and /register. The appPath property is provided by the configuration parser in the Yeoman boilerplate. */ app.use(ExpressStormpath.init(app,{ web: { produces: ['application/json'], spa: { enabled: true, view: app.get('appPath') }, register: { form: { fields: { /* We can define custom form fields here, and they will appear in the front-end. The Angular SDK will read this registration view model from the Express server by making a JSON GET request to /register */ favoriteColor: { enabled: true, label: 'Favorite Color?', placeholder: 'e.g. Blue', required: true, type: 'text' } } } }, me: { /* For security purposes, we don't expose user information by default. To facilitate our Profile page example, we will need to enable the expansion of the user's custom data object. */ expand: { customData: true } } } })); require('./routes')(app); app.on('stormpath.ready',function() { console.log('Stormpath Ready'); }); // Start server server.listen(config.port, config.ip, function () { console.log('Express server listening on %d, in %s mode', config.port, app.get('env')); }); // Expose app exports = module.exports = app; ================================================ FILE: example/dashboard-app/server/components/errors/index.js ================================================ /** * Error responses */ 'use strict'; module.exports[404] = function pageNotFound(req, res) { var viewFilePath = '404'; var statusCode = 404; var result = { status: statusCode }; res.status(result.status); res.render(viewFilePath, function (err) { if (err) { return res.json(result, result.status); } res.render(viewFilePath); }); }; ================================================ FILE: example/dashboard-app/server/config/environment/development.js ================================================ 'use strict'; // Development specific configuration // ================================== module.exports = { // MongoDB connection options mongo: { uri: 'mongodb://localhost/dashboard-dev' }, seedDB: true }; ================================================ FILE: example/dashboard-app/server/config/environment/index.js ================================================ 'use strict'; var path = require('path'); var _ = require('lodash'); function requiredProcessEnv(name) { if(!process.env[name]) { throw new Error('You must set the ' + name + ' environment variable'); } return process.env[name]; } // All configurations will extend these options // ============================================ var all = { env: process.env.NODE_ENV, // Root path of server root: path.normalize(__dirname + '/../../..'), // Server port port: process.env.PORT || 9000, // Should we populate the DB with sample data? seedDB: false, // Secret for session, you will want to change this and make it an environment variable secrets: { session: 'dashboard-secret' }, // List of user roles userRoles: ['guest', 'user', 'admin'], // MongoDB connection options mongo: { options: { db: { safe: true } } }, }; // Export the config object based on the NODE_ENV // ============================================== module.exports = _.merge( all, require('./' + process.env.NODE_ENV + '.js') || {}); ================================================ FILE: example/dashboard-app/server/config/environment/production.js ================================================ 'use strict'; // Production specific configuration // ================================= module.exports = { // Server IP ip: process.env.OPENSHIFT_NODEJS_IP || process.env.IP || undefined, // Server port port: process.env.OPENSHIFT_NODEJS_PORT || process.env.PORT || 8080, // MongoDB connection options mongo: { uri: process.env.MONGOLAB_URI || process.env.MONGOHQ_URL || process.env.OPENSHIFT_MONGODB_DB_URL+process.env.OPENSHIFT_APP_NAME || 'mongodb://localhost/dashboard' } }; ================================================ FILE: example/dashboard-app/server/config/environment/test.js ================================================ 'use strict'; // Test specific configuration // =========================== module.exports = { // MongoDB connection options mongo: { uri: 'mongodb://localhost/dashboard-test' } }; ================================================ FILE: example/dashboard-app/server/config/express.js ================================================ /** * Express configuration */ 'use strict'; var express = require('express'); var favicon = require('serve-favicon'); var morgan = require('morgan'); var compression = require('compression'); var bodyParser = require('body-parser'); var methodOverride = require('method-override'); var cookieParser = require('cookie-parser'); var errorHandler = require('errorhandler'); var path = require('path'); var config = require('./environment'); module.exports = function(app) { var env = app.get('env'); app.set('views', config.root + '/server/views'); app.engine('html', require('ejs').renderFile); app.set('view engine', 'html'); app.set('trust proxy', true); app.use(compression()); app.use(methodOverride()); app.use(cookieParser()); if ('production' === env) { app.use(favicon(path.join(config.root, 'public', 'favicon.ico'))); app.use(express.static(path.join(config.root, 'public'))); app.set('appPath', path.join(config.root, 'public', 'index.html')); app.use(morgan('dev')); } if ('development' === env || 'test' === env) { app.use(require('connect-livereload')()); app.use(express.static(path.join(config.root, '.tmp'))); app.use(express.static(path.join(config.root, 'client'))); app.set('appPath', path.join(config.root, 'client', 'index.html')); app.use(morgan('dev')); app.use(errorHandler()); // Error handler - has to be last } }; ================================================ FILE: example/dashboard-app/server/config/local.env.sample.js ================================================ 'use strict'; // Use local.env.js for environment variables that grunt will set when the server starts locally. // Use for your api keys, secrets, etc. This file should not be tracked by git. // // You will need to set these on the server you deploy to. module.exports = { DOMAIN: 'http://localhost:9000', SESSION_SECRET: 'dashboard-secret', // Control debug level for modules using visionmedia/debug DEBUG: '' }; ================================================ FILE: example/dashboard-app/server/routes.js ================================================ /** * Main application routes */ 'use strict'; var errors = require('./components/errors'); var ExpressStormpath = require('express-stormpath'); module.exports = function(app) { // Insert routes below app.use('/api/things', ExpressStormpath.loginRequired, require('./api/thing')); // All undefined asset or api routes should return a 404 app.route('/:url(api|auth|components|app|bower_components|assets)/*') .get(errors[404]); // All other routes should redirect to the index.html app.route('/*') .get(function(req, res) { res.sendFile(app.get('appPath')); }); }; ================================================ FILE: example/dashboard-app/server/views/404.html ================================================ Page Not Found :(

Not found :(

Sorry, but the page you were trying to view does not exist.

It looks like this was the result of either:

  • a mistyped address
  • an out-of-date link
================================================ FILE: example/ng-route-app/.bowerrc ================================================ { "directory": "client/bower_components" } ================================================ FILE: example/ng-route-app/.buildignore ================================================ *.coffee ================================================ FILE: example/ng-route-app/.editorconfig ================================================ # EditorConfig helps developers define and maintain consistent # coding styles between different editors and IDEs # editorconfig.org root = true [*] # Change these settings to your own preference indent_style = space indent_size = 2 # We recommend you to keep these unchanged end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.md] trim_trailing_whitespace = false ================================================ FILE: example/ng-route-app/.gitattributes ================================================ * text=auto ================================================ FILE: example/ng-route-app/.gitignore ================================================ node_modules public .tmp .idea client/bower_components dist /server/config/local.env.js stormpath.yml ================================================ FILE: example/ng-route-app/.travis.yml ================================================ language: node_js node_js: - '0.10' - '0.11' before_script: - npm install -g bower grunt-cli - bower install services: mongodb ================================================ FILE: example/ng-route-app/.yo-rc.json ================================================ { "generator-angular-fullstack": { "insertRoutes": true, "registerRoutesFile": "server/routes.js", "routesNeedle": "// Insert routes below", "routesBase": "/api/", "pluralizeRoutes": true, "insertSockets": true, "registerSocketsFile": "server/config/socketio.js", "socketsNeedle": "// Insert sockets below", "filters": { "js": true, "html": true, "css": true, "uirouter": true, "bootstrap": true, "uibootstrap": false } }, "generator-ng-component": { "routeDirectory": "client/app/", "directiveDirectory": "client/app/", "filterDirectory": "client/app/", "serviceDirectory": "client/app/", "basePath": "client", "moduleName": "", "filters": [ "uirouter" ], "extensions": [ "js", "html", "css" ], "directiveSimpleTemplates": "", "directiveComplexTemplates": "", "filterTemplates": "", "serviceTemplates": "", "factoryTemplates": "", "controllerTemplates": "", "decoratorTemplates": "", "providerTemplates": "", "routeTemplates": "" } } ================================================ FILE: example/ng-route-app/Gruntfile.js ================================================ // Generated on 2015-09-24 using generator-angular-fullstack 2.0.13 'use strict'; module.exports = function (grunt) { var localConfig; try { localConfig = require('./server/config/local.env'); } catch(e) { localConfig = {}; } // Load grunt tasks automatically, when needed require('jit-grunt')(grunt, { express: 'grunt-express-server', useminPrepare: 'grunt-usemin', ngtemplates: 'grunt-angular-templates', cdnify: 'grunt-google-cdn', protractor: 'grunt-protractor-runner', injector: 'grunt-asset-injector', buildcontrol: 'grunt-build-control' }); // Time how long tasks take. Can help when optimizing build times require('time-grunt')(grunt); // Define the configuration for all the tasks grunt.initConfig({ // Project settings pkg: grunt.file.readJSON('package.json'), yeoman: { // configurable paths client: require('./bower.json').appPath || 'client', dist: 'dist' }, express: { options: { port: process.env.PORT || 9000, // output option will force Grunt to wait for this // line to be seen in server output, before continuing output: 'Stormpath Ready' }, dev: { options: { script: 'server/app.js', debug: true } }, prod: { options: { script: 'dist/server/app.js' } } }, open: { server: { url: 'http://localhost:<%= express.options.port %>' } }, watch: { injectJS: { files: [ '<%= yeoman.client %>/{app,components}/**/*.js', '!<%= yeoman.client %>/{app,components}/**/*.spec.js', '!<%= yeoman.client %>/{app,components}/**/*.mock.js', '!<%= yeoman.client %>/app/app.js'], tasks: ['injector:scripts'] }, injectCss: { files: [ '<%= yeoman.client %>/{app,components}/**/*.css' ], tasks: ['injector:css'] }, mochaTest: { files: ['server/**/*.spec.js'], tasks: ['env:test', 'mochaTest'] }, jsTest: { files: [ '<%= yeoman.client %>/{app,components}/**/*.spec.js', '<%= yeoman.client %>/{app,components}/**/*.mock.js' ], tasks: ['newer:jshint:all', 'karma'] }, gruntfile: { files: ['Gruntfile.js'] }, livereload: { files: [ '{.tmp,<%= yeoman.client %>}/{app,components}/**/*.css', '{.tmp,<%= yeoman.client %>}/{app,components}/**/*.html', '{.tmp,<%= yeoman.client %>}/{app,components}/**/*.js', '!{.tmp,<%= yeoman.client %>}{app,components}/**/*.spec.js', '!{.tmp,<%= yeoman.client %>}/{app,components}/**/*.mock.js', '<%= yeoman.client %>/assets/images/{,*//*}*.{png,jpg,jpeg,gif,webp,svg}' ], options: { livereload: true } }, express: { files: [ 'server/**/*.{js,json}' ], tasks: ['express:dev', 'wait'], options: { livereload: true, nospawn: true //Without this option specified express won't be reloaded } } }, // Make sure code styles are up to par and there are no obvious mistakes jshint: { options: { jshintrc: '<%= yeoman.client %>/.jshintrc', reporter: require('jshint-stylish') }, server: { options: { jshintrc: 'server/.jshintrc' }, src: [ 'server/**/*.js', '!server/**/*.spec.js' ] }, serverTest: { options: { jshintrc: 'server/.jshintrc-spec' }, src: ['server/**/*.spec.js'] }, all: [ '<%= yeoman.client %>/{app,components}/**/*.js', '!<%= yeoman.client %>/{app,components}/**/*.spec.js', '!<%= yeoman.client %>/{app,components}/**/*.mock.js' ], test: { src: [ '<%= yeoman.client %>/{app,components}/**/*.spec.js', '<%= yeoman.client %>/{app,components}/**/*.mock.js' ] } }, // Empties folders to start fresh clean: { dist: { files: [{ dot: true, src: [ '.tmp', '<%= yeoman.dist %>/*', '!<%= yeoman.dist %>/.git*', '!<%= yeoman.dist %>/.openshift', '!<%= yeoman.dist %>/Procfile' ] }] }, server: '.tmp' }, // Add vendor prefixed styles autoprefixer: { options: { browsers: ['last 1 version'] }, dist: { files: [{ expand: true, cwd: '.tmp/', src: '{,*/}*.css', dest: '.tmp/' }] } }, // Debugging with node inspector 'node-inspector': { custom: { options: { 'web-host': 'localhost' } } }, // Use nodemon to run server in debug mode with an initial breakpoint nodemon: { debug: { script: 'server/app.js', options: { nodeArgs: ['--debug-brk'], env: { PORT: process.env.PORT || 9000 }, callback: function (nodemon) { nodemon.on('log', function (event) { console.log(event.colour); }); // opens browser on initial server start nodemon.on('config:update', function () { setTimeout(function () { require('open')('http://localhost:8080/debug?port=5858'); }, 500); }); } } } }, // Automatically inject Bower components into the app wiredep: { target: { src: '<%= yeoman.client %>/index.html', ignorePath: '<%= yeoman.client %>/', exclude: [/bootstrap-sass-official/, /bootstrap.js/, '/json3/', '/es5-shim/'] } }, // Renames files for browser caching purposes rev: { dist: { files: { src: [ '<%= yeoman.dist %>/public/{,*/}*.js', '<%= yeoman.dist %>/public/{,*/}*.css', '<%= yeoman.dist %>/public/assets/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}', '<%= yeoman.dist %>/public/assets/fonts/*' ] } } }, // Reads HTML for usemin blocks to enable smart builds that automatically // concat, minify and revision files. Creates configurations in memory so // additional tasks can operate on them useminPrepare: { html: ['<%= yeoman.client %>/index.html'], options: { dest: '<%= yeoman.dist %>/public' } }, // Performs rewrites based on rev and the useminPrepare configuration usemin: { html: ['<%= yeoman.dist %>/public/{,*/}*.html'], css: ['<%= yeoman.dist %>/public/{,*/}*.css'], js: ['<%= yeoman.dist %>/public/{,*/}*.js'], options: { assetsDirs: [ '<%= yeoman.dist %>/public', '<%= yeoman.dist %>/public/assets/images' ], // This is so we update image references in our ng-templates patterns: { js: [ [/(assets\/images\/.*?\.(?:gif|jpeg|jpg|png|webp|svg))/gm, 'Update the JS to reference our revved images'] ] } } }, // The following *-min tasks produce minified files in the dist folder imagemin: { dist: { files: [{ expand: true, cwd: '<%= yeoman.client %>/assets/images', src: '{,*/}*.{png,jpg,jpeg,gif}', dest: '<%= yeoman.dist %>/public/assets/images' }] } }, svgmin: { dist: { files: [{ expand: true, cwd: '<%= yeoman.client %>/assets/images', src: '{,*/}*.svg', dest: '<%= yeoman.dist %>/public/assets/images' }] } }, // Allow the use of non-minsafe AngularJS files. Automatically makes it // minsafe compatible so Uglify does not destroy the ng references ngAnnotate: { dist: { files: [{ expand: true, cwd: '.tmp/concat', src: '*/**.js', dest: '.tmp/concat' }] } }, // Package all the html partials into a single javascript payload ngtemplates: { options: { // This should be the name of your apps angular module module: 'dashboardApp', htmlmin: { collapseBooleanAttributes: true, collapseWhitespace: true, removeAttributeQuotes: true, removeEmptyAttributes: true, removeRedundantAttributes: true, removeScriptTypeAttributes: true, removeStyleLinkTypeAttributes: true }, usemin: 'app/app.js' }, main: { cwd: '<%= yeoman.client %>', src: ['{app,components}/**/*.html'], dest: '.tmp/templates.js' }, tmp: { cwd: '.tmp', src: ['{app,components}/**/*.html'], dest: '.tmp/tmp-templates.js' } }, // Replace Google CDN references cdnify: { dist: { html: ['<%= yeoman.dist %>/public/*.html'] } }, // Copies remaining files to places other tasks can use copy: { dist: { files: [{ expand: true, dot: true, cwd: '<%= yeoman.client %>', dest: '<%= yeoman.dist %>/public', src: [ '*.{ico,png,txt}', '.htaccess', 'bower_components/**/*', 'assets/images/{,*/}*.{webp}', 'assets/fonts/**/*', 'index.html' ] }, { expand: true, cwd: '.tmp/images', dest: '<%= yeoman.dist %>/public/assets/images', src: ['generated/*'] }, { expand: true, dest: '<%= yeoman.dist %>', src: [ 'package.json', 'server/**/*' ] }] }, styles: { expand: true, cwd: '<%= yeoman.client %>', dest: '.tmp/', src: ['{app,components}/**/*.css'] } }, buildcontrol: { options: { dir: 'dist', commit: true, push: true, connectCommits: false, message: 'Built %sourceName% from commit %sourceCommit% on branch %sourceBranch%' }, heroku: { options: { remote: 'heroku', branch: 'master' } }, openshift: { options: { remote: 'openshift', branch: 'master' } } }, // Run some tasks in parallel to speed up the build process concurrent: { server: [ ], test: [ ], debug: { tasks: [ 'nodemon', 'node-inspector' ], options: { logConcurrentOutput: true } }, dist: [ 'imagemin', 'svgmin' ] }, // Test settings karma: { unit: { configFile: 'karma.conf.js', singleRun: true } }, mochaTest: { options: { reporter: 'spec' }, src: ['server/**/*.spec.js'] }, protractor: { options: { configFile: 'protractor.conf.js' }, chrome: { options: { args: { browser: 'chrome' } } } }, env: { test: { NODE_ENV: 'test' }, prod: { NODE_ENV: 'production' }, all: localConfig }, injector: { options: { }, // Inject application script files into index.html (doesn't include bower) scripts: { options: { transform: function(filePath) { filePath = filePath.replace('/client/', ''); filePath = filePath.replace('/.tmp/', ''); return ''; }, starttag: '', endtag: '' }, files: { '<%= yeoman.client %>/index.html': [ ['{.tmp,<%= yeoman.client %>}/{app,components}/**/*.js', '!{.tmp,<%= yeoman.client %>}/app/app.js', '!{.tmp,<%= yeoman.client %>}/{app,components}/**/*.spec.js', '!{.tmp,<%= yeoman.client %>}/{app,components}/**/*.mock.js'] ] } }, // Inject component css into index.html css: { options: { transform: function(filePath) { filePath = filePath.replace('/client/', ''); filePath = filePath.replace('/.tmp/', ''); return ''; }, starttag: '', endtag: '' }, files: { '<%= yeoman.client %>/index.html': [ '<%= yeoman.client %>/{app,components}/**/*.css' ] } } }, }); // Used for delaying livereload until after server has restarted grunt.registerTask('wait', function () { grunt.log.ok('Waiting for server reload...'); var done = this.async(); setTimeout(function () { grunt.log.writeln('Done waiting!'); done(); }, 1500); }); grunt.registerTask('express-keepalive', 'Keep grunt running', function() { this.async(); }); grunt.registerTask('serve', function (target) { if (target === 'dist') { return grunt.task.run(['build', 'env:all', 'env:prod', 'express:prod', 'wait', 'open', 'express-keepalive']); } if (target === 'debug') { return grunt.task.run([ 'clean:server', 'env:all', 'concurrent:server', 'injector', 'wiredep', 'autoprefixer', 'concurrent:debug' ]); } grunt.task.run([ 'clean:server', 'env:all', 'concurrent:server', 'injector', 'wiredep', 'autoprefixer', 'express:dev', 'wait', 'open', 'watch' ]); }); grunt.registerTask('server', function () { grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.'); grunt.task.run(['serve']); }); grunt.registerTask('test', function(target) { if (target === 'server') { return grunt.task.run([ 'env:all', 'env:test', 'mochaTest' ]); } else if (target === 'client') { return grunt.task.run([ 'clean:server', 'env:all', 'concurrent:test', 'injector', 'autoprefixer', 'karma' ]); } else if (target === 'e2e') { return grunt.task.run([ 'clean:server', 'env:all', 'env:test', 'concurrent:test', 'injector', 'wiredep', 'autoprefixer', 'express:dev', 'protractor' ]); } else grunt.task.run([ 'test:server', 'test:client' ]); }); grunt.registerTask('build', [ 'clean:dist', 'concurrent:dist', 'injector', 'wiredep', 'useminPrepare', 'autoprefixer', 'ngtemplates', 'concat', 'ngAnnotate', 'copy:dist', 'cdnify', 'cssmin', 'uglify', 'rev', 'usemin' ]); grunt.registerTask('default', [ 'newer:jshint', 'test', 'build' ]); }; ================================================ FILE: example/ng-route-app/README.md ================================================ # Stormpath Angular SDK Example This folder contains an example application that is built with the [Stormpath Angular SDK][] and [Stormpath Express][]. This application is the same one that is built when you follow the [Yeoman Guide][], but instead of [UI Router][], it uses the [ngRoute][] module. #### Running the Example Application 1. To run this application, you will need Bower and Grunt as global packages: ```bash npm install -g grunt bower ``` 2. Clone this repo to your computer, and enter the directory for this example: ```bash git clone https://github.com/stormpath/stormpath-sdk-angularjs.git cd stormpath-sdk-angularjs/example/ng-route-app ``` 3. Install the dependencies: ```bash npm install bower install ``` 4. Export your environment variables for your Stormpath Tenant and Application: ```bash export STORMPATH_CLIENT_APIKEY_ID=xxx export STORMPATH_CLIENT_APIKEY_SECRET=xxx export STORMPATH_APPLICATION_HREF=xxx ``` 5. Start the server with Grunt, this should start the server and open the Angular application in your browser: ```bash grunt serve ``` [Stormpath Angular SDK]: https://github.com/stormpath/stormpath-sdk-angularjs [Stormpath Express]: https://github.com/stormpath/stormpath-express [Yeoman Guide]: https://docs.stormpath.com/angularjs/guide [UI Router]: https://github.com/angular-ui/ui-router [ngRoute]: https://docs.angularjs.org/api/ngRoute ================================================ FILE: example/ng-route-app/bower.json ================================================ { "name": "dashboard", "version": "0.0.0", "dependencies": { "angular": ">=1.2.*", "json3": "~3.3.1", "es5-shim": "~3.0.1", "jquery": "~1.11.0", "bootstrap": "~3.1.1", "angular-route": "~1.4.7", "angular-resource": ">=1.2.*", "angular-cookies": ">=1.2.*", "angular-sanitize": ">=1.2.*", "font-awesome": ">=4.1.0", "lodash": "~2.4.1", "stormpath-sdk-angularjs": "~1.0.0" }, "devDependencies": { "angular-mocks": ">=1.2.*", "angular-scenario": ">=1.2.*" } } ================================================ FILE: example/ng-route-app/client/.htaccess ================================================ # Apache Configuration File # (!) Using `.htaccess` files slows down Apache, therefore, if you have access # to the main server config file (usually called `httpd.conf`), you should add # this logic there: http://httpd.apache.org/docs/current/howto/htaccess.html. # ############################################################################## # # CROSS-ORIGIN RESOURCE SHARING (CORS) # # ############################################################################## # ------------------------------------------------------------------------------ # | Cross-domain AJAX requests | # ------------------------------------------------------------------------------ # Enable cross-origin AJAX requests. # http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity # http://enable-cors.org/ # # Header set Access-Control-Allow-Origin "*" # # ------------------------------------------------------------------------------ # | CORS-enabled images | # ------------------------------------------------------------------------------ # Send the CORS header for images when browsers request it. # https://developer.mozilla.org/en/CORS_Enabled_Image # http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html # http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/ SetEnvIf Origin ":" IS_CORS Header set Access-Control-Allow-Origin "*" env=IS_CORS # ------------------------------------------------------------------------------ # | Web fonts access | # ------------------------------------------------------------------------------ # Allow access from all domains for web fonts Header set Access-Control-Allow-Origin "*" # ############################################################################## # # ERRORS # # ############################################################################## # ------------------------------------------------------------------------------ # | 404 error prevention for non-existing redirected folders | # ------------------------------------------------------------------------------ # Prevent Apache from returning a 404 error for a rewrite if a directory # with the same name does not exist. # http://httpd.apache.org/docs/current/content-negotiation.html#multiviews # http://www.webmasterworld.com/apache/3808792.htm Options -MultiViews # ------------------------------------------------------------------------------ # | Custom error messages / pages | # ------------------------------------------------------------------------------ # You can customize what Apache returns to the client in case of an error (see # http://httpd.apache.org/docs/current/mod/core.html#errordocument), e.g.: ErrorDocument 404 /404.html # ############################################################################## # # INTERNET EXPLORER # # ############################################################################## # ------------------------------------------------------------------------------ # | Better website experience | # ------------------------------------------------------------------------------ # Force IE to render pages in the highest available mode in the various # cases when it may not: http://hsivonen.iki.fi/doctype/ie-mode.pdf. Header set X-UA-Compatible "IE=edge" # `mod_headers` can't match based on the content-type, however, we only # want to send this header for HTML pages and not for the other resources Header unset X-UA-Compatible # ------------------------------------------------------------------------------ # | Cookie setting from iframes | # ------------------------------------------------------------------------------ # Allow cookies to be set from iframes in IE. # # Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"" # # ------------------------------------------------------------------------------ # | Screen flicker | # ------------------------------------------------------------------------------ # Stop screen flicker in IE on CSS rollovers (this only works in # combination with the `ExpiresByType` directives for images from below). # BrowserMatch "MSIE" brokenvary=1 # BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1 # BrowserMatch "Opera" !brokenvary # SetEnvIf brokenvary 1 force-no-vary # ############################################################################## # # MIME TYPES AND ENCODING # # ############################################################################## # ------------------------------------------------------------------------------ # | Proper MIME types for all files | # ------------------------------------------------------------------------------ # Audio AddType audio/mp4 m4a f4a f4b AddType audio/ogg oga ogg # JavaScript # Normalize to standard type (it's sniffed in IE anyways): # http://tools.ietf.org/html/rfc4329#section-7.2 AddType application/javascript js jsonp AddType application/json json # Video AddType video/mp4 mp4 m4v f4v f4p AddType video/ogg ogv AddType video/webm webm AddType video/x-flv flv # Web fonts AddType application/font-woff woff AddType application/vnd.ms-fontobject eot # Browsers usually ignore the font MIME types and sniff the content, # however, Chrome shows a warning if other MIME types are used for the # following fonts. AddType application/x-font-ttf ttc ttf AddType font/opentype otf # Make SVGZ fonts work on iPad: # https://twitter.com/FontSquirrel/status/14855840545 AddType image/svg+xml svg svgz AddEncoding gzip svgz # Other AddType application/octet-stream safariextz AddType application/x-chrome-extension crx AddType application/x-opera-extension oex AddType application/x-shockwave-flash swf AddType application/x-web-app-manifest+json webapp AddType application/x-xpinstall xpi AddType application/xml atom rdf rss xml AddType image/webp webp AddType image/x-icon ico AddType text/cache-manifest appcache manifest AddType text/vtt vtt AddType text/x-component htc AddType text/x-vcard vcf # ------------------------------------------------------------------------------ # | UTF-8 encoding | # ------------------------------------------------------------------------------ # Use UTF-8 encoding for anything served as `text/html` or `text/plain`. AddDefaultCharset utf-8 # Force UTF-8 for certain file formats. AddCharset utf-8 .atom .css .js .json .rss .vtt .webapp .xml # ############################################################################## # # URL REWRITES # # ############################################################################## # ------------------------------------------------------------------------------ # | Rewrite engine | # ------------------------------------------------------------------------------ # Turning on the rewrite engine and enabling the `FollowSymLinks` option is # necessary for the following directives to work. # If your web host doesn't allow the `FollowSymlinks` option, you may need to # comment it out and use `Options +SymLinksIfOwnerMatch` but, be aware of the # performance impact: http://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks # Also, some cloud hosting services require `RewriteBase` to be set: # http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-mod-rewrite-not-working-on-my-site Options +FollowSymlinks # Options +SymLinksIfOwnerMatch RewriteEngine On # RewriteBase / # ------------------------------------------------------------------------------ # | Suppressing / Forcing the "www." at the beginning of URLs | # ------------------------------------------------------------------------------ # The same content should never be available under two different URLs especially # not with and without "www." at the beginning. This can cause SEO problems # (duplicate content), therefore, you should choose one of the alternatives and # redirect the other one. # By default option 1 (no "www.") is activated: # http://no-www.org/faq.php?q=class_b # If you'd prefer to use option 2, just comment out all the lines from option 1 # and uncomment the ones from option 2. # IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME! # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Option 1: rewrite www.example.com → example.com RewriteCond %{HTTPS} !=on RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L] # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Option 2: rewrite example.com → www.example.com # Be aware that the following might not be a good idea if you use "real" # subdomains for certain parts of your website. # # RewriteCond %{HTTPS} !=on # RewriteCond %{HTTP_HOST} !^www\..+$ [NC] # RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L] # # ############################################################################## # # SECURITY # # ############################################################################## # ------------------------------------------------------------------------------ # | Content Security Policy (CSP) | # ------------------------------------------------------------------------------ # You can mitigate the risk of cross-site scripting and other content-injection # attacks by setting a Content Security Policy which whitelists trusted sources # of content for your site. # The example header below allows ONLY scripts that are loaded from the current # site's origin (no inline scripts, no CDN, etc). This almost certainly won't # work as-is for your site! # To get all the details you'll need to craft a reasonable policy for your site, # read: http://html5rocks.com/en/tutorials/security/content-security-policy (or # see the specification: http://w3.org/TR/CSP). # # Header set Content-Security-Policy "script-src 'self'; object-src 'self'" # # Header unset Content-Security-Policy # # # ------------------------------------------------------------------------------ # | File access | # ------------------------------------------------------------------------------ # Block access to directories without a default document. # Usually you should leave this uncommented because you shouldn't allow anyone # to surf through every directory on your server (which may includes rather # private places like the CMS's directories). Options -Indexes # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Block access to hidden files and directories. # This includes directories used by version control systems such as Git and SVN. RewriteCond %{SCRIPT_FILENAME} -d [OR] RewriteCond %{SCRIPT_FILENAME} -f RewriteRule "(^|/)\." - [F] # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Block access to backup and source files. # These files may be left by some text editors and can pose a great security # danger when anyone has access to them. Order allow,deny Deny from all Satisfy All # ------------------------------------------------------------------------------ # | Secure Sockets Layer (SSL) | # ------------------------------------------------------------------------------ # Rewrite secure requests properly to prevent SSL certificate warnings, e.g.: # prevent `https://www.example.com` when your certificate only allows # `https://secure.example.com`. # # RewriteCond %{SERVER_PORT} !^443 # RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L] # # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Force client-side SSL redirection. # If a user types "example.com" in his browser, the above rule will redirect him # to the secure version of the site. That still leaves a window of opportunity # (the initial HTTP connection) for an attacker to downgrade or redirect the # request. The following header ensures that browser will ONLY connect to your # server via HTTPS, regardless of what the users type in the address bar. # http://www.html5rocks.com/en/tutorials/security/transport-layer-security/ # # Header set Strict-Transport-Security max-age=16070400; # # ------------------------------------------------------------------------------ # | Server software information | # ------------------------------------------------------------------------------ # Avoid displaying the exact Apache version number, the description of the # generic OS-type and the information about Apache's compiled-in modules. # ADD THIS DIRECTIVE IN THE `httpd.conf` AS IT WILL NOT WORK IN THE `.htaccess`! # ServerTokens Prod # ############################################################################## # # WEB PERFORMANCE # # ############################################################################## # ------------------------------------------------------------------------------ # | Compression | # ------------------------------------------------------------------------------ # Force compression for mangled headers. # http://developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding # Compress all output labeled with one of the following MIME-types # (for Apache versions below 2.3.7, you don't need to enable `mod_filter` # and can remove the `` and `` lines # as `AddOutputFilterByType` is still in the core directives). AddOutputFilterByType DEFLATE application/atom+xml \ application/javascript \ application/json \ application/rss+xml \ application/vnd.ms-fontobject \ application/x-font-ttf \ application/x-web-app-manifest+json \ application/xhtml+xml \ application/xml \ font/opentype \ image/svg+xml \ image/x-icon \ text/css \ text/html \ text/plain \ text/x-component \ text/xml # ------------------------------------------------------------------------------ # | Content transformations | # ------------------------------------------------------------------------------ # Prevent some of the mobile network providers from modifying the content of # your site: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5. # # Header set Cache-Control "no-transform" # # ------------------------------------------------------------------------------ # | ETag removal | # ------------------------------------------------------------------------------ # Since we're sending far-future expires headers (see below), ETags can # be removed: http://developer.yahoo.com/performance/rules.html#etags. # `FileETag None` is not enough for every server. Header unset ETag FileETag None # ------------------------------------------------------------------------------ # | Expires headers (for better cache control) | # ------------------------------------------------------------------------------ # The following expires headers are set pretty far in the future. If you don't # control versioning with filename-based cache busting, consider lowering the # cache time for resources like CSS and JS to something like 1 week. ExpiresActive on ExpiresDefault "access plus 1 month" # CSS ExpiresByType text/css "access plus 1 year" # Data interchange ExpiresByType application/json "access plus 0 seconds" ExpiresByType application/xml "access plus 0 seconds" ExpiresByType text/xml "access plus 0 seconds" # Favicon (cannot be renamed!) ExpiresByType image/x-icon "access plus 1 week" # HTML components (HTCs) ExpiresByType text/x-component "access plus 1 month" # HTML ExpiresByType text/html "access plus 0 seconds" # JavaScript ExpiresByType application/javascript "access plus 1 year" # Manifest files ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds" ExpiresByType text/cache-manifest "access plus 0 seconds" # Media ExpiresByType audio/ogg "access plus 1 month" ExpiresByType image/gif "access plus 1 month" ExpiresByType image/jpeg "access plus 1 month" ExpiresByType image/png "access plus 1 month" ExpiresByType video/mp4 "access plus 1 month" ExpiresByType video/ogg "access plus 1 month" ExpiresByType video/webm "access plus 1 month" # Web feeds ExpiresByType application/atom+xml "access plus 1 hour" ExpiresByType application/rss+xml "access plus 1 hour" # Web fonts ExpiresByType application/font-woff "access plus 1 month" ExpiresByType application/vnd.ms-fontobject "access plus 1 month" ExpiresByType application/x-font-ttf "access plus 1 month" ExpiresByType font/opentype "access plus 1 month" ExpiresByType image/svg+xml "access plus 1 month" # ------------------------------------------------------------------------------ # | Filename-based cache busting | # ------------------------------------------------------------------------------ # If you're not using a build process to manage your filename version revving, # you might want to consider enabling the following directives to route all # requests such as `/css/style.12345.css` to `/css/style.css`. # To understand why this is important and a better idea than `*.css?v231`, read: # http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring # # RewriteCond %{REQUEST_FILENAME} !-f # RewriteCond %{REQUEST_FILENAME} !-d # RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L] # # ------------------------------------------------------------------------------ # | File concatenation | # ------------------------------------------------------------------------------ # Allow concatenation from within specific CSS and JS files, e.g.: # Inside of `script.combined.js` you could have # # # and they would be included into this single file. # # # Options +Includes # AddOutputFilterByType INCLUDES application/javascript application/json # SetOutputFilter INCLUDES # # # Options +Includes # AddOutputFilterByType INCLUDES text/css # SetOutputFilter INCLUDES # # # ------------------------------------------------------------------------------ # | Persistent connections | # ------------------------------------------------------------------------------ # Allow multiple requests to be sent over the same TCP connection: # http://httpd.apache.org/docs/current/en/mod/core.html#keepalive. # Enable if you serve a lot of static content but, be aware of the # possible disadvantages! # # Header set Connection Keep-Alive # ================================================ FILE: example/ng-route-app/client/.jshintrc ================================================ { "node": true, "browser": true, "esnext": true, "bitwise": true, "camelcase": true, "curly": true, "eqeqeq": true, "immed": true, "indent": 2, "latedef": true, "newcap": true, "noarg": true, "quotmark": "single", "regexp": true, "undef": true, "unused": true, "strict": true, "trailing": true, "smarttabs": true, "globals": { "jQuery": true, "angular": true, "console": true, "$": true, "_": true, "moment": true, "describe": true, "beforeEach": true, "module": true, "inject": true, "it": true, "expect": true, "browser": true, "element": true, "by": true } } ================================================ FILE: example/ng-route-app/client/app/app.css ================================================ /** * Bootstrap Fonts */ @font-face { font-family: 'Glyphicons Halflings'; src: url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.eot'); src: url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.woff') format('woff'), url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); } /** *Font Awesome Fonts */ @font-face { font-family: 'FontAwesome'; src: url('../bower_components/font-awesome/fonts/fontawesome-webfont.eot?v=4.1.0'); src: url('../bower_components/font-awesome/fonts/fontawesome-webfont.eot?#iefix&v=4.1.0') format('embedded-opentype'), url('../bower_components/font-awesome/fonts/fontawesome-webfont.woff?v=4.1.0') format('woff'), url('../bower_components/font-awesome/fonts/fontawesome-webfont.ttf?v=4.1.0') format('truetype'), url('../bower_components/font-awesome/fonts/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular') format('svg'); font-weight: normal; font-style: normal; } /** * App-wide Styles */ .browsehappy { margin: 0.2em 0; background: #ccc; color: #000; padding: 0.2em 0; } ================================================ FILE: example/ng-route-app/client/app/app.js ================================================ 'use strict'; angular.module('dashboardApp', [ 'ngCookies', 'ngResource', 'ngSanitize', 'ngRoute', 'stormpath', 'stormpath.templates' ]) .config(function ($routeProvider, $locationProvider) { $routeProvider.otherwise('/'); $locationProvider.html5Mode(true); }) .run(function($stormpath, $rootScope, $location) { $stormpath.ngRouter({ loginRoute: '/login', defaultPostLoginRoute: '/' }); $rootScope.$on('$sessionEnd', function() { $location.path('/login'); }); }); ================================================ FILE: example/ng-route-app/client/app/forgot/forgot.controller.js ================================================ 'use strict'; angular.module('dashboardApp') .controller('ForgotCtrl', function ($scope) { $scope.message = 'Hello'; }); ================================================ FILE: example/ng-route-app/client/app/forgot/forgot.controller.spec.js ================================================ 'use strict'; describe('Controller: ForgotCtrl', function () { // load the controller's module beforeEach(module('dashboardApp')); var ForgotCtrl, scope; // Initialize the controller and a mock scope beforeEach(inject(function ($controller, $rootScope) { scope = $rootScope.$new(); ForgotCtrl = $controller('ForgotCtrl', { $scope: scope }); })); it('should ...', function () { expect(1).toEqual(1); }); }); ================================================ FILE: example/ng-route-app/client/app/forgot/forgot.css ================================================ ================================================ FILE: example/ng-route-app/client/app/forgot/forgot.html ================================================

Forgot Password


================================================ FILE: example/ng-route-app/client/app/forgot/forgot.js ================================================ 'use strict'; angular.module('dashboardApp') .config(function ($routeProvider) { $routeProvider .when('/forgot', { templateUrl: 'app/forgot/forgot.html', controller: 'ForgotCtrl' }); }); ================================================ FILE: example/ng-route-app/client/app/login/login.controller.js ================================================ 'use strict'; angular.module('dashboardApp') .controller('LoginCtrl', function ($scope) { $scope.message = 'Hello'; }); ================================================ FILE: example/ng-route-app/client/app/login/login.controller.spec.js ================================================ 'use strict'; describe('Controller: LoginCtrl', function () { // load the controller's module beforeEach(module('dashboardApp')); var LoginCtrl, scope; // Initialize the controller and a mock scope beforeEach(inject(function ($controller, $rootScope) { scope = $rootScope.$new(); LoginCtrl = $controller('LoginCtrl', { $scope: scope }); })); it('should ...', function () { expect(1).toEqual(1); }); }); ================================================ FILE: example/ng-route-app/client/app/login/login.css ================================================ ================================================ FILE: example/ng-route-app/client/app/login/login.html ================================================

Login


================================================ FILE: example/ng-route-app/client/app/login/login.js ================================================ 'use strict'; angular.module('dashboardApp') .config(function ($routeProvider) { $routeProvider .when('/login', { templateUrl: 'app/login/login.html', controller: 'LoginCtrl' }); }); ================================================ FILE: example/ng-route-app/client/app/main/main.controller.js ================================================ 'use strict'; angular.module('dashboardApp') .controller('MainCtrl', function ($scope, $http) { $scope.awesomeThings = []; $http.get('/api/things').success(function(awesomeThings) { $scope.awesomeThings = awesomeThings; }); }); ================================================ FILE: example/ng-route-app/client/app/main/main.controller.spec.js ================================================ 'use strict'; describe('Controller: MainCtrl', function () { // load the controller's module beforeEach(module('dashboardApp')); var MainCtrl, scope, $httpBackend; // Initialize the controller and a mock scope beforeEach(inject(function (_$httpBackend_, $controller, $rootScope) { $httpBackend = _$httpBackend_; $httpBackend.expectGET('/api/things') .respond(['HTML5 Boilerplate', 'AngularJS', 'Karma', 'Express']); scope = $rootScope.$new(); MainCtrl = $controller('MainCtrl', { $scope: scope }); })); it('should attach a list of things to the scope', function () { $httpBackend.flush(); expect(scope.awesomeThings.length).toBe(4); }); }); ================================================ FILE: example/ng-route-app/client/app/main/main.css ================================================ .thing-form { margin: 20px 0; } #banner { border-bottom: none; margin-top: -20px; } #banner h1 { font-size: 60px; line-height: 1; letter-spacing: -1px; } .hero-unit { position: relative; padding: 30px 15px; color: #F5F5F5; text-align: center; text-shadow: 0 1px 0 rgba(0, 0, 0, 0.1); background: #4393B9; } .footer { text-align: center; padding: 30px 0; margin-top: 70px; border-top: 1px solid #E5E5E5; } ================================================ FILE: example/ng-route-app/client/app/main/main.html ================================================

Features:

================================================ FILE: example/ng-route-app/client/app/main/main.js ================================================ 'use strict'; angular.module('dashboardApp') .config(function ($routeProvider) { $routeProvider .when('/', { templateUrl: 'app/main/main.html', controller: 'MainCtrl' }); }); ================================================ FILE: example/ng-route-app/client/app/profile/profile.controller.js ================================================ 'use strict'; angular.module('dashboardApp') .controller('ProfileCtrl', function ($scope) { $scope.message = 'Hello'; }); ================================================ FILE: example/ng-route-app/client/app/profile/profile.controller.spec.js ================================================ 'use strict'; describe('Controller: ProfileCtrl', function () { // load the controller's module beforeEach(module('dashboardApp')); var ProfileCtrl, scope; // Initialize the controller and a mock scope beforeEach(inject(function ($controller, $rootScope) { scope = $rootScope.$new(); ProfileCtrl = $controller('ProfileCtrl', { $scope: scope }); })); it('should ...', function () { expect(1).toEqual(1); }); }); ================================================ FILE: example/ng-route-app/client/app/profile/profile.css ================================================ ================================================ FILE: example/ng-route-app/client/app/profile/profile.html ================================================

My Profile



    
================================================ FILE: example/ng-route-app/client/app/profile/profile.js ================================================ 'use strict'; angular.module('dashboardApp') .config(function ($routeProvider) { $routeProvider .when('/profile', { templateUrl: 'app/profile/profile.html', controller: 'ProfileCtrl', sp: { authenticate: true } }); }); ================================================ FILE: example/ng-route-app/client/app/register/register.controller.js ================================================ 'use strict'; angular.module('dashboardApp') .controller('RegisterCtrl', function ($scope) { $scope.message = 'Hello'; }); ================================================ FILE: example/ng-route-app/client/app/register/register.controller.spec.js ================================================ 'use strict'; describe('Controller: RegisterCtrl', function () { // load the controller's module beforeEach(module('dashboardApp')); var RegisterCtrl, scope; // Initialize the controller and a mock scope beforeEach(inject(function ($controller, $rootScope) { scope = $rootScope.$new(); RegisterCtrl = $controller('RegisterCtrl', { $scope: scope }); })); it('should ...', function () { expect(1).toEqual(1); }); }); ================================================ FILE: example/ng-route-app/client/app/register/register.css ================================================ ================================================ FILE: example/ng-route-app/client/app/register/register.html ================================================

Registration


================================================ FILE: example/ng-route-app/client/app/register/register.js ================================================ 'use strict'; angular.module('dashboardApp') .config(function ($routeProvider) { $routeProvider .when('/register', { templateUrl: 'app/register/register.html', controller: 'RegisterCtrl' }); }); ================================================ FILE: example/ng-route-app/client/app/reset/reset.controller.js ================================================ 'use strict'; angular.module('dashboardApp') .controller('ResetCtrl', function ($scope) { $scope.message = 'Hello'; }); ================================================ FILE: example/ng-route-app/client/app/reset/reset.controller.spec.js ================================================ 'use strict'; describe('Controller: ResetCtrl', function () { // load the controller's module beforeEach(module('dashboardApp')); var ResetCtrl, scope; // Initialize the controller and a mock scope beforeEach(inject(function ($controller, $rootScope) { scope = $rootScope.$new(); ResetCtrl = $controller('ResetCtrl', { $scope: scope }); })); it('should ...', function () { expect(1).toEqual(1); }); }); ================================================ FILE: example/ng-route-app/client/app/reset/reset.css ================================================ ================================================ FILE: example/ng-route-app/client/app/reset/reset.html ================================================

Reset Your Password


================================================ FILE: example/ng-route-app/client/app/reset/reset.js ================================================ 'use strict'; angular.module('dashboardApp') .config(function ($routeProvider) { $routeProvider .when('/reset', { templateUrl: 'app/reset/reset.html', controller: 'ResetCtrl' }); }); ================================================ FILE: example/ng-route-app/client/app/verify/verify.controller.js ================================================ 'use strict'; angular.module('dashboardApp') .controller('VerifyCtrl', function ($scope) { $scope.message = 'Hello'; }); ================================================ FILE: example/ng-route-app/client/app/verify/verify.controller.spec.js ================================================ 'use strict'; describe('Controller: VerifyCtrl', function () { // load the controller's module beforeEach(module('dashboardApp')); var VerifyCtrl, scope; // Initialize the controller and a mock scope beforeEach(inject(function ($controller, $rootScope) { scope = $rootScope.$new(); VerifyCtrl = $controller('VerifyCtrl', { $scope: scope }); })); it('should ...', function () { expect(1).toEqual(1); }); }); ================================================ FILE: example/ng-route-app/client/app/verify/verify.css ================================================ ================================================ FILE: example/ng-route-app/client/app/verify/verify.html ================================================

Verify Your Account


================================================ FILE: example/ng-route-app/client/app/verify/verify.js ================================================ 'use strict'; angular.module('dashboardApp') .config(function ($routeProvider) { $routeProvider .when('/verify', { templateUrl: 'app/verify/verify.html', controller: 'VerifyCtrl' }); }); ================================================ FILE: example/ng-route-app/client/components/navbar/navbar.controller.js ================================================ 'use strict'; angular.module('dashboardApp') .controller('NavbarCtrl', function ($scope, $location) { $scope.menu = [{ 'title': 'Home', 'link': '/' }]; $scope.isCollapsed = true; $scope.isActive = function(route) { return route === $location.path(); }; }); ================================================ FILE: example/ng-route-app/client/components/navbar/navbar.html ================================================ ================================================ FILE: example/ng-route-app/client/index.html ================================================
================================================ FILE: example/ng-route-app/client/robots.txt ================================================ # robotstxt.org User-agent: * ================================================ FILE: example/ng-route-app/e2e/main/main.po.js ================================================ /** * This file uses the Page Object pattern to define the main page for tests * https://docs.google.com/presentation/d/1B6manhG0zEXkC-H-tPo2vwU06JhL8w9-XCF9oehXzAQ */ 'use strict'; var MainPage = function() { this.heroEl = element(by.css('.hero-unit')); this.h1El = this.heroEl.element(by.css('h1')); this.imgEl = this.heroEl.element(by.css('img')); }; module.exports = new MainPage(); ================================================ FILE: example/ng-route-app/e2e/main/main.spec.js ================================================ 'use strict'; describe('Main View', function() { var page; beforeEach(function() { browser.get('/'); page = require('./main.po'); }); it('should include jumbotron with correct data', function() { expect(page.h1El.getText()).toBe('\'Allo, \'Allo!'); expect(page.imgEl.getAttribute('src')).toMatch(/assets\/images\/yeoman.png$/); expect(page.imgEl.getAttribute('alt')).toBe('I\'m Yeoman'); }); }); ================================================ FILE: example/ng-route-app/karma.conf.js ================================================ // Karma configuration // http://karma-runner.github.io/0.10/config/configuration-file.html module.exports = function(config) { config.set({ // base path, that will be used to resolve files and exclude basePath: '', // testing framework to use (jasmine/mocha/qunit/...) frameworks: ['jasmine'], // list of files / patterns to load in the browser files: [ 'client/bower_components/jquery/dist/jquery.js', 'client/bower_components/angular/angular.js', 'client/bower_components/angular-mocks/angular-mocks.js', 'client/bower_components/angular-resource/angular-resource.js', 'client/bower_components/angular-cookies/angular-cookies.js', 'client/bower_components/angular-sanitize/angular-sanitize.js', 'client/bower_components/angular-route/angular-route.js', 'client/bower_components/lodash/dist/lodash.compat.js', 'client/bower_components/angular-ui-router/release/angular-ui-router.js', 'client/app/app.js', 'client/app/app.coffee', 'client/app/**/*.js', 'client/app/**/*.coffee', 'client/components/**/*.js', 'client/components/**/*.coffee', 'client/app/**/*.jade', 'client/components/**/*.jade', 'client/app/**/*.html', 'client/components/**/*.html' ], preprocessors: { '**/*.jade': 'ng-jade2js', '**/*.html': 'html2js', '**/*.coffee': 'coffee', }, ngHtml2JsPreprocessor: { stripPrefix: 'client/' }, ngJade2JsPreprocessor: { stripPrefix: 'client/' }, // list of files / patterns to exclude exclude: [], // web server port port: 8080, // level of logging // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG logLevel: config.LOG_INFO, // enable / disable watching file and executing tests whenever any file changes autoWatch: false, // Start these browsers, currently available: // - Chrome // - ChromeCanary // - Firefox // - Opera // - Safari (only Mac) // - PhantomJS // - IE (only Windows) browsers: ['PhantomJS'], // Continuous Integration mode // if true, it capture browsers, run tests and exit singleRun: false }); }; ================================================ FILE: example/ng-route-app/package.json ================================================ { "name": "dashboard", "version": "0.0.0", "main": "server/app.js", "dependencies": { "body-parser": "~1.5.0", "composable-middleware": "^0.3.0", "compression": "~1.0.1", "connect-mongo": "^0.4.1", "cookie-parser": "~1.0.1", "ejs": "~0.8.4", "errorhandler": "~1.0.0", "express": "^4.13.3", "express-session": "~1.0.2", "express-stormpath": "^3.0.0", "lodash": "~2.4.1", "method-override": "~1.0.0", "morgan": "~1.0.0", "serve-favicon": "~2.0.1" }, "devDependencies": { "grunt": "~0.4.4", "grunt-autoprefixer": "~0.7.2", "grunt-wiredep": "~1.8.0", "grunt-concurrent": "~0.5.0", "grunt-contrib-clean": "~0.5.0", "grunt-contrib-concat": "~0.4.0", "grunt-contrib-copy": "~0.5.0", "grunt-contrib-cssmin": "~0.9.0", "grunt-contrib-htmlmin": "~0.2.0", "grunt-contrib-imagemin": "~0.7.1", "grunt-contrib-jshint": "~0.10.0", "grunt-contrib-uglify": "~0.4.0", "grunt-contrib-watch": "~0.6.1", "grunt-google-cdn": "~0.4.0", "grunt-newer": "~0.7.0", "grunt-ng-annotate": "^0.2.3", "grunt-rev": "~0.1.0", "grunt-svgmin": "~0.4.0", "grunt-usemin": "~2.1.1", "grunt-env": "~0.4.1", "grunt-node-inspector": "~0.1.5", "grunt-nodemon": "~0.2.0", "grunt-angular-templates": "^0.5.4", "grunt-dom-munger": "^3.4.0", "grunt-protractor-runner": "^1.1.0", "grunt-asset-injector": "^0.1.0", "grunt-karma": "~0.8.2", "grunt-build-control": "DaftMonk/grunt-build-control", "grunt-mocha-test": "~0.10.2", "jit-grunt": "^0.5.0", "time-grunt": "~0.3.1", "grunt-express-server": "~0.4.17", "grunt-open": "~0.2.3", "open": "~0.0.4", "jshint-stylish": "~0.1.5", "connect-livereload": "~0.4.0", "karma-ng-scenario": "~0.1.0", "karma-firefox-launcher": "~0.1.3", "karma-script-launcher": "~0.1.0", "karma-html2js-preprocessor": "~0.1.0", "karma-ng-jade2js-preprocessor": "^0.1.2", "karma-jasmine": "~0.1.5", "karma-chrome-launcher": "~0.1.3", "requirejs": "~2.1.11", "karma-requirejs": "~0.2.1", "karma-coffee-preprocessor": "~0.2.1", "karma-jade-preprocessor": "0.0.11", "karma-phantomjs-launcher": "~0.1.4", "karma": "~0.12.9", "karma-ng-html2js-preprocessor": "~0.1.0", "supertest": "~0.11.0", "should": "~3.3.1" }, "engines": { "node": ">=0.10.0" }, "scripts": { "start": "node server/app.js", "test": "grunt test", "update-webdriver": "node node_modules/grunt-protractor-runner/node_modules/protractor/bin/webdriver-manager update" }, "private": true } ================================================ FILE: example/ng-route-app/protractor.conf.js ================================================ // Protractor configuration // https://github.com/angular/protractor/blob/master/referenceConf.js 'use strict'; exports.config = { // The timeout for each script run on the browser. This should be longer // than the maximum time your application needs to stabilize between tasks. allScriptsTimeout: 110000, // A base URL for your application under test. Calls to protractor.get() // with relative paths will be prepended with this. baseUrl: 'http://localhost:' + (process.env.PORT || '9000'), // If true, only chromedriver will be started, not a standalone selenium. // Tests for browsers other than chrome will not run. chromeOnly: true, // list of files / patterns to load in the browser specs: [ 'e2e/**/*.spec.js' ], // Patterns to exclude. exclude: [], // ----- Capabilities to be passed to the webdriver instance ---- // // For a full list of available capabilities, see // https://code.google.com/p/selenium/wiki/DesiredCapabilities // and // https://code.google.com/p/selenium/source/browse/javascript/webdriver/capabilities.js capabilities: { 'browserName': 'chrome' }, // ----- The test framework ----- // // Jasmine and Cucumber are fully supported as a test and assertion framework. // Mocha has limited beta support. You will need to include your own // assertion framework if working with mocha. framework: 'jasmine', // ----- Options to be passed to minijasminenode ----- // // See the full list at https://github.com/juliemr/minijasminenode jasmineNodeOpts: { defaultTimeoutInterval: 30000 } }; ================================================ FILE: example/ng-route-app/server/.jshintrc ================================================ { "node": true, "esnext": true, "bitwise": true, "eqeqeq": true, "immed": true, "latedef": "nofunc", "newcap": true, "noarg": true, "regexp": true, "undef": true, "smarttabs": true, "asi": true, "debug": true } ================================================ FILE: example/ng-route-app/server/.jshintrc-spec ================================================ { "extends": ".jshintrc", "globals": { "describe": true, "it": true, "before": true, "beforeEach": true, "after": true, "afterEach": true } } ================================================ FILE: example/ng-route-app/server/api/thing/index.js ================================================ 'use strict'; var express = require('express'); var controller = require('./thing.controller'); var router = express.Router(); router.get('/', controller.index); module.exports = router; ================================================ FILE: example/ng-route-app/server/api/thing/thing.controller.js ================================================ /** * Using Rails-like standard naming convention for endpoints. * GET /things -> index * POST /things -> create * GET /things/:id -> show * PUT /things/:id -> update * DELETE /things/:id -> destroy */ 'use strict'; var _ = require('lodash'); // Get list of things exports.index = function(req, res) { res.json([ { name : 'Development Tools', info : 'Integration with popular tools such as Bower, Grunt, Karma, Mocha, JSHint, Node Inspector, Livereload, Protractor, Jade, Stylus, Sass, CoffeeScript, and Less.' }, { name : 'Server and Client integration', info : 'Built with a powerful and fun stack: MongoDB, Express, AngularJS, and Node.' }, { name : 'Smart Build System', info : 'Build system ignores `spec` files, allowing you to keep tests alongside code. Automatic injection of scripts and styles into your index.html' }, { name : 'Modular Structure', info : 'Best practice client and server structures allow for more code reusability and maximum scalability' }, { name : 'Optimized Build', info : 'Build process packs up your templates as a single JavaScript payload, minifies your scripts/css/images, and rewrites asset names for caching.' },{ name : 'Deployment Ready', info : 'Easily deploy your app to Heroku or Openshift with the heroku and openshift subgenerators' } ]); }; ================================================ FILE: example/ng-route-app/server/api/thing/thing.spec.js ================================================ 'use strict'; var should = require('should'); var app = require('../../app'); var request = require('supertest'); describe('GET /api/things', function() { it('should respond with JSON array', function(done) { request(app) .get('/api/things') .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { if (err) return done(err); res.body.should.be.instanceof(Array); done(); }); }); }); ================================================ FILE: example/ng-route-app/server/app.js ================================================ /** * Main application file */ 'use strict'; // Set default node environment to development process.env.NODE_ENV = process.env.NODE_ENV || 'development'; var express = require('express'); var config = require('./config/environment'); var ExpressStormpath = require('express-stormpath'); // Setup server var app = express(); var server = require('http').createServer(app); /* The config/express file is setting up the static file server which serves your angular application assets. We don't need to authenticate those requests, so we do this before calling Stormpath. */ require('./config/express')(app); console.log('Initializing Stormpath'); /* Now we initialize Stormpath, any middleware that is registered after this point will be protected by Stormpath. The spaRoot setting tells the Stormpath library where your Angular app is, as it will need to serve it for the default routes like /login and /register. The appPath property is provided by the configuration parser in the Yeoman boilerplate. */ app.use(ExpressStormpath.init(app,{ web: { produces: ['application/json'], spa: { enabled: true, view: app.get('appPath') } } })); require('./routes')(app); app.on('stormpath.ready',function() { console.log('Stormpath Ready'); }); // Start server server.listen(config.port, config.ip, function () { console.log('Express server listening on %d, in %s mode', config.port, app.get('env')); }); // Expose app exports = module.exports = app; ================================================ FILE: example/ng-route-app/server/components/errors/index.js ================================================ /** * Error responses */ 'use strict'; module.exports[404] = function pageNotFound(req, res) { var viewFilePath = '404'; var statusCode = 404; var result = { status: statusCode }; res.status(result.status); res.render(viewFilePath, function (err) { if (err) { return res.json(result, result.status); } res.render(viewFilePath); }); }; ================================================ FILE: example/ng-route-app/server/config/environment/development.js ================================================ 'use strict'; // Development specific configuration // ================================== module.exports = { // MongoDB connection options mongo: { uri: 'mongodb://localhost/dashboard-dev' }, seedDB: true }; ================================================ FILE: example/ng-route-app/server/config/environment/index.js ================================================ 'use strict'; var path = require('path'); var _ = require('lodash'); function requiredProcessEnv(name) { if(!process.env[name]) { throw new Error('You must set the ' + name + ' environment variable'); } return process.env[name]; } // All configurations will extend these options // ============================================ var all = { env: process.env.NODE_ENV, // Root path of server root: path.normalize(__dirname + '/../../..'), // Server port port: process.env.PORT || 9000, // Should we populate the DB with sample data? seedDB: false, // Secret for session, you will want to change this and make it an environment variable secrets: { session: 'dashboard-secret' }, // List of user roles userRoles: ['guest', 'user', 'admin'], // MongoDB connection options mongo: { options: { db: { safe: true } } }, }; // Export the config object based on the NODE_ENV // ============================================== module.exports = _.merge( all, require('./' + process.env.NODE_ENV + '.js') || {}); ================================================ FILE: example/ng-route-app/server/config/environment/production.js ================================================ 'use strict'; // Production specific configuration // ================================= module.exports = { // Server IP ip: process.env.OPENSHIFT_NODEJS_IP || process.env.IP || undefined, // Server port port: process.env.OPENSHIFT_NODEJS_PORT || process.env.PORT || 8080, // MongoDB connection options mongo: { uri: process.env.MONGOLAB_URI || process.env.MONGOHQ_URL || process.env.OPENSHIFT_MONGODB_DB_URL+process.env.OPENSHIFT_APP_NAME || 'mongodb://localhost/dashboard' } }; ================================================ FILE: example/ng-route-app/server/config/environment/test.js ================================================ 'use strict'; // Test specific configuration // =========================== module.exports = { // MongoDB connection options mongo: { uri: 'mongodb://localhost/dashboard-test' } }; ================================================ FILE: example/ng-route-app/server/config/express.js ================================================ /** * Express configuration */ 'use strict'; var express = require('express'); var favicon = require('serve-favicon'); var morgan = require('morgan'); var compression = require('compression'); var bodyParser = require('body-parser'); var methodOverride = require('method-override'); var cookieParser = require('cookie-parser'); var errorHandler = require('errorhandler'); var path = require('path'); var config = require('./environment'); module.exports = function(app) { var env = app.get('env'); app.set('views', config.root + '/server/views'); app.engine('html', require('ejs').renderFile); app.set('view engine', 'html'); app.set('trust proxy', true); app.use(compression()); app.use(methodOverride()); app.use(cookieParser()); if ('production' === env) { app.use(favicon(path.join(config.root, 'public', 'favicon.ico'))); app.use(express.static(path.join(config.root, 'public'))); app.set('appPath', path.join(config.root, 'public', 'index.html')); app.use(morgan('dev')); } if ('development' === env || 'test' === env) { app.use(require('connect-livereload')()); app.use(express.static(path.join(config.root, '.tmp'))); app.use(express.static(path.join(config.root, 'client'))); app.set('appPath', path.join(config.root, 'client', 'index.html')); app.use(morgan('dev')); app.use(errorHandler()); // Error handler - has to be last } }; ================================================ FILE: example/ng-route-app/server/config/local.env.sample.js ================================================ 'use strict'; // Use local.env.js for environment variables that grunt will set when the server starts locally. // Use for your api keys, secrets, etc. This file should not be tracked by git. // // You will need to set these on the server you deploy to. module.exports = { DOMAIN: 'http://localhost:9000', SESSION_SECRET: 'dashboard-secret', // Control debug level for modules using visionmedia/debug DEBUG: '' }; ================================================ FILE: example/ng-route-app/server/routes.js ================================================ /** * Main application routes */ 'use strict'; var errors = require('./components/errors'); var ExpressStormpath = require('express-stormpath'); module.exports = function(app) { // Insert routes below app.use('/api/things', ExpressStormpath.loginRequired, require('./api/thing')); // All undefined asset or api routes should return a 404 app.route('/:url(api|auth|components|app|bower_components|assets)/*') .get(errors[404]); // All other routes should redirect to the index.html app.route('/*') .get(function(req, res) { res.sendFile(app.get('appPath')); }); }; ================================================ FILE: example/ng-route-app/server/views/404.html ================================================ Page Not Found :(

Not found :(

Sorry, but the page you were trying to view does not exist.

It looks like this was the result of either:

  • a mistyped address
  • an out-of-date link
================================================ FILE: index.js ================================================ var name = require('./dist/stormpath-sdk-angularjs'); require('./dist/stormpath-sdk-angularjs.tpls'); module.exports = name; ================================================ FILE: ngdoc_assets/example/index.ngdoc ================================================ @ngdoc overview @name Example @description ## Example We have a complete sample application which shows you how to create an AngularJS application with an Express backend. Please see the example folder in the repo: https://github.com/stormpath/stormpath-sdk-angularjs/tree/master/example ================================================ FILE: ngdoc_assets/index.ngdoc ================================================ @ngdoc overview @name API Documentation @description # Stormpath Angular JS SDK Ng Docs [![Bower Version](https://img.shields.io/bower/v/stormpath-sdk-angularjs.svg?style=flat)](https://bower.io) ## Welcome! This is the API documentation for the Stormpath AngularJS SDK. Here you will find the detailed description of all the compontents that this module provides to your application. With this SDK you can: * Authenticate users * Get the current user object * Know if the user is logged in * Use [`ui-router`][ui-router] to control access to your view states (authentication and authorization) ## Quickstart & Getting Started If you are just getting started with this library, we suggest the [README](https://github.com/stormpath/stormpath-sdk-angularjs) on Github. ================================================ FILE: ngdoc_assets/nav.html ================================================ ================================================ FILE: ngdoc_assets/server/index.ngdoc ================================================ @ngdoc overview @name Server Integration Guide @description # Server Integration Guide Front-end applications can not communicate with the Stormpath API directly, and thus need an integrated back-end. This page has information about how to integrate Stormpath into your back-end. ## With Express.js If you are using Express.js as your server, you can add our [Stormpath Express Module][] to your server. ## With other environments If you are using another type of server you will have to manually setup some server endpoints to support this module. Please visit [Stormpath Docs](https://docs.stormpath.com) to find the language SDK of your server environment. Using the SDK of your choice, you need to implement the endpoints in the following table. The expected URLs can be changed by modifying the URLs in the {@link api/stormpath.STORMPATH_CONFIG:STORMPATH_CONFIG STORMPATH_CONFIG} **Endpoints to implement:**
Server Endpoint Description Expected Endpoint Stormpath SDK API to use

**Current User Endpoint**

Returns a JSON object of the currently logged-in Stormpath account

`/me`
*N/A*

**Email Verification**

Used to verify and consume email verification tokens

`/verify` [Account Verification](http://docs.stormpath.com/rest/product-guide/#verify-an-email-address)

**Forgot Password**

Used to create password reset tokens, triggering a password reset email

`/forgot` [Password Reset](http://docs.stormpath.com/rest/product-guide/#reset-an-accounts-password)

**Login**

Used to authenticate a user and generate an access token which is stored in a secure cookie. Also used to get a view model of how to render the login form. Read more about the view model [here](https://docs.stormpath.com/nodejs/express/latest/login.html#json-login-api).

`/login` [Login Attempts](http://docs.stormpath.com/rest/product-guide/#authenticate-an-account)

**Logout**

Used to remove any session-related cookies, such as the access token.

`/logout` *N/A*

**Password Reset**

Used to complete the password reset workflow, with an sp token that was issued by the forgot password flow

`/change` [Password Reset](http://docs.stormpath.com/rest/product-guide/#reset-an-accounts-password)

**User Registration**

Used to create new Stormpath accounts. Also used to get a view model of how to render the registration form. Read more about the view model [here](https://docs.stormpath.com/nodejs/express/latest/registration.html#json-registration-api).

`/register` [Account Creation](http://docs.stormpath.com/rest/product-guide/#create-an-account)
## How Sessions Work The Angular SDK is agnostic to the authentication strategy used, it assumes that you are using a cookie-based system for authenticating the browser as a client. However we do suggest that you use JWT access tokens. You can read more in our blog post, [Token Based Authentication for Single Page Apps](https://stormpath.com/blog/token-auth-spa). Our [Stormpath Express Module] does this for you. Most of our SDKs have methods for issuing JWTs, and can be used to implement this strategy in your server. If you do not want to use JWT Access Tokens, you need to ensure the following on your server: * The {@link api/stormpath.STORMPATH_CONFIG:STORMPATH_CONFIG#properties_authentication_endpoint AUTHENTICATION_ENDPOINT} should set a cookie that identifies a session for the user * Your protected API endpoints should respond with `401 Unauthorized` if the user does not have a valid session ## How Our Token Authentication Works ### Token Creation and Storage When a user logs in, the server will issue an access token in the form of a JWT. This access token will be stored in an HTTP-only cookie and will also set the Secure flag (so that it is only sent over HTTPS) in your production environment. A secondary refresh token will also be stored in a secure cookie, and this token is used to get more access tokens as they expire. The refresh tokens can be revoked, using the Stormpath REST API. You may have heard that cookies are insecure, but that is a vague statement. The truth is that HTTP-only cookies, especially Secure-only cookies, are the most secure place to store authentication information because the JavaScript environment cannot access them. This is the best way to protect yourself from XSS attacks. ### Token Expiration and Revocation Access tokens are valid until they expire. You should set an expiration (TTL) that makes sense for your application. If you are using Stormpath to manage your access tokens, you can modify the TTL by modifying the OAuth Policy of the Stormpath Application that you are using for your Angular Application. For more information, please see [Using Stormpath for OAuth 2.0 and Access/Refresh Token Management][] [docs.stormpath.com]: https://docs.stormpath.com [ui-router]: https://github.com/angular-ui/ui-router [stormpath-sdk-node]: https://github.com/stormpath/stormpath-sdk-node "Stormpath Node SDK" [Stormpath Express Module]: https://github.com/stormpath/express-stormpath [Using Stormpath for OAuth 2.0 and Access/Refresh Token Management]: http://docs.stormpath.com/guides/token-management/ ================================================ FILE: ngdoc_assets/stormpath-angular.css ================================================ ================================================ FILE: package.json ================================================ { "name": "stormpath-sdk-angularjs", "version": "2.0.1", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "Stormpath, Inc.", "license": "Apache-2.0", "homepage": "https://github.com/stormpath/stormpath-sdk-angularjs", "repository": { "type": "git", "url": "https://github.com/stormpath/stormpath-sdk-angularjs.git" }, "devDependencies": { "grunt-contrib-concat": "^0.5.1", "grunt": "^0.4.5", "load-grunt-tasks": "^3.4.0", "grunt-contrib-uglify": "^0.11.0", "grunt-contrib-jshint": "^0.11.3", "grunt-contrib-clean": "^0.7.0", "grunt-ngdocs": "^0.2.6", "grunt-contrib-watch": "^0.6.1", "connect-livereload": "^0.5.4", "open": "0.0.5", "grunt-open": "^0.2.3", "grunt-contrib-copy": "^0.8.2", "grunt-html2js": "^0.3.5", "grunt-contrib-connect": "^0.11.2", "grunt-shell": "^1.1.2", "grunt-replace": "^0.11.0" }, "dependencies": {} } ================================================ FILE: protractor.conf.js ================================================ 'use strict'; exports.config = { directConnect: true, specs: ['test/protractor/*.js'] }; ================================================ FILE: src/module.js ================================================ 'use strict'; /** * @ngdoc object * * @name stormpath.SpStateConfig:SpStateConfig * * @property {boolean} authenticate * * If `true`, the user must be authenticated in order to view this state. * If the user is not authenticated, they will * be redirected to the `login` state. After they login, they will be redirected to * the state that was originally requested. * * @property {object} authorize * * An object that defines access control rules. Currently, it supports a group-based * check. See the example below. * * @property {boolean} waitForUser * * If `true`, delay the state transition until we know * if the user is authenticated or not. This is useful for situations where * you want everyone to see this state, but the state may look different * depending on the user's authentication state. * * * @description * * The Stormpath State Config is an object that you can define on a UI Router * state. Use this configuration to define access control for your routes, as * defined by UI Router. * * You will need to be using the UI Router module, and you need * to enable the integration by calling * {@link stormpath.$stormpath#methods_uiRouter $stormpath.uiRouter()} in your * application's config block. * * If you're using Angular's built-in `$routeProvider` instead of UI Router, please * use {@link stormpath.$stormpath#methods_ngRouter $stormpath.ngRouter()} instead. * * **NOTE:** Do not define this configuration on a abstract state, it must go on * the child state. However, the controller of the abstract state will be * initialized AFTER any configuration rules of the child state have been met. * * # Support for `data.authorities` * * If you have used [JHipster](https://jhipster.github.io/) to generate your * project, you are likely using the `data.authorities` property to define * authorization for your views. This library will look for the `data.authorities` * property and apply the same logic as our own `sp.authorize` property. * * @example * *
 *
 * angular.module('myApp')
 *   .config(function ($stateProvider) {
 *
 *     // Wait until we know if the user is logged in before showing the homepage
 *     $stateProvider
 *       .state('main', {
 *         url: '/',
 *         sp: {
 *           waitForUser: true
 *         }
 *       });
 *
 *     // Require a user to be authenticated in order to see this state
 *     $stateProvider
 *       .state('secrets', {
 *         url: '/secrets',
 *         controller: 'SecretsCtrl',
 *         sp: {
 *           authenticate: true
 *         }
 *       });
 *
 *     // Require a user to be in the admins group in order to see this state
 *     $stateProvider
 *       .state('secrets', {
 *         url: '/admin',
 *         controller: 'AdminCtrl',
 *         sp: {
 *           authorize: {
 *             group: 'admins'
 *           }
 *         }
 *       });
 * });
 * 
* * If using JHipster generated code: * *
 *     // Require a user to be in the admins group in order to see this state
 *     $stateProvider
 *       .state('secrets', {
 *         url: '/admin',
 *         controller: 'AdminCtrl',
 *         data: {
 *           authorities: ['admins']
 *         }
 *       });
 *  
*/ /** * @ngdoc object * * @name stormpath.SpRouteConfig:SpRouteConfig * * @property {boolean} authenticate * * If `true`, the user must be authenticated in order to view this route. * If the user is not authenticated, they will * be redirected to the `login` route. After they login, they will be redirected to * the route that was originally requested. * * @property {object} authorize * * An object that defines access control rules. Currently, it supports a group-based * check. See the example below. * * @property {boolean} waitForUser * * If `true`, delay the route transition until we know * if the user is authenticated or not. This is useful for situations where * you want everyone to see this route, but the route may look different * depending on the user's authentication route. * * * @description * * The Stormpath Route Config is an object that you can define on a route. * Use this configuration to define access control for your routes, as * defined by the ngRoute module. * * You will need to be using the ngRoute module, and you need * to enable the integration by calling * {@link stormpath.$stormpath#methods_ngRouter $stormpath.ngRouter()} in your * application's config block. * * If you're using UI Router instead of Angular's built-in `$routeProvider`, please * use {@link stormpath.$stormpath#methods_uiRouter $stormpath.uiRouter()} instead. * * @example * *
 *
 * angular.module('myApp')
 *   .config(function ($routeProvider) {
 *     // Wait until we know if the user is logged in before showing the homepage
 *     $routeProvider
 *       .when('/main', {
*         controller: 'MainCtrl',
 *         sp: {
 *           waitForUser: true
 *         }
 *       });
 *
 *     // Require a user to be authenticated in order to see this route
 *     $routeProvider
 *       .when('/secrets', {
 *         controller: 'SecretsCtrl',
 *         sp: {
 *           authenticate: true
 *         }
 *       });
 *
 *     // Require a user to be in the admins group in order to see this route
 *     $routeProvider
 *       .when('/secrets', {
 *         controller: 'AdminCtrl',
 *         sp: {
 *           authorize: {
 *             group: 'admins'
 *           }
 *         }
 *       });
 * });
 * 
*/ angular.module('stormpath', [ 'stormpath.CONFIG', 'stormpath.utils', 'stormpath.auth', 'stormpath.userService', 'stormpath.viewModelService', 'stormpath.socialLogin', 'stormpath.oauth' ]) .factory('StormpathAgentInterceptor',['$isCurrentDomain', '$spHeaders', function($isCurrentDomain, $spHeaders){ function StormpathAgentInterceptor(){ } /** * Adds the X-Stormpath-Agent header, if the requested URL is on the same * domain as the current document. * * @param {Object} config $http config object. * @return {Object} config $http config object. */ StormpathAgentInterceptor.prototype.request = function(config){ var uriExpressions = [ '/change$', '/forgot$', '/login$', '/logout$', '/me$', '/oauth/token$', '/oauth/token$', '/register$', '/revoke$', '/verify$' ]; if (uriExpressions.some(function(expr){ return new RegExp(expr).test(config.url); })) { config.headers = angular.extend(config.headers, $spHeaders); } return config; }; return new StormpathAgentInterceptor(); }]) .config(['$httpProvider',function($httpProvider){ $httpProvider.interceptors.push('StormpathAgentInterceptor'); }]) .provider('$stormpath', [function $stormpathProvider(){ /** * @ngdoc object * * @name stormpath.$stormpath * * @description * * This service allows you to enable application-wide features of the library. * * At the moment the only feature is the UI Router integration, which is * documented below. */ this.$get = [ '$user', '$injector', 'STORMPATH_CONFIG', '$rootScope', '$location', function stormpathServiceFactory($user, $injector, STORMPATH_CONFIG, $rootScope, $location) { var $state; var $route; function StormpathService(){ var encoder = new UrlEncodedFormParser(); this.encodeUrlForm = encoder.encode.bind(encoder); if ($injector.has('$state')) { $state = $injector.get('$state'); } if ($injector.has('$route')) { $route = $injector.get('$route'); } return this; } function stateChangeUnauthenticatedEvent(toState, toParams){ /** * @ngdoc event * * @name stormpath.$stormpath#$stateChangeUnauthenticated * * @eventOf stormpath.$stormpath * * @eventType broadcast on root scope * * @param {Object} event * * Angular event object. * * @param {Object} toState The state that the user attempted to access. * * @param {Object} toParams The state params of the state that the user * attempted to access. * * @description * * This event is broadcast when a UI state change is prevented, * because the user is not logged in. * * Use this event if you want to implement your own strategy for * presenting the user with a login form. * * To receive this event, you must be using the UI Router integration. * * @example * *
         *   $rootScope.$on('$stateChangeUnauthenticated',function(e,toState,toParams){
         *     // Your custom logic for deciding how the user should login, and
         *     // if you want to redirect them to the desired state afterwards
         *   });
         * 
*/ $rootScope.$broadcast(STORMPATH_CONFIG.STATE_CHANGE_UNAUTHENTICATED,toState,toParams); } function stateChangeUnauthorizedEvent(toState,toParams){ /** * @ngdoc event * * @name stormpath.$stormpath#$stateChangeUnauthorized * * @eventOf stormpath.$stormpath * * @eventType broadcast on root scope * * @param {Object} event * * Angular event object. * * @param {Object} toState The state that the user attempted to access. * * @param {Object} toParams The state params of the state that the user * attempted to access. * * @description * * This event is broadcast when a UI state change is prevented, * because the user is not authorized by the rules defined in the * {@link stormpath.SpStateConfig:SpStateConfig Stormpath State Configuration} * for the requested state. * * Use this event if you want to implement your own strategy for telling * the user that they are forbidden from viewing that state. * * To receive this event, you must be using the UI Router integration. * * @example * *
         *   $rootScope.$on('$stateChangeUnauthorized',function(e,toState,toParams){
         *     // Your custom logic for deciding how the user should be
         *     // notified that they are forbidden from this state
         *   });
         * 
*/ $rootScope.$broadcast(STORMPATH_CONFIG.STATE_CHANGE_UNAUTHORIZED,toState,toParams); } StormpathService.prototype.stateChangeInterceptor = function stateChangeInterceptor(config) { $rootScope.$on('$stateChangeStart', function(e,toState,toParams){ var sp = toState.sp || {}; // Grab the sp config for this state var authorities = (toState.data && toState.data.authorities) ? toState.data.authorities : undefined; if((sp.authenticate || sp.authorize || (authorities && authorities.length)) && (!$user.currentUser)){ e.preventDefault(); $user.get().then(function(){ // The user is authenticated, continue to the requested state if(sp.authorize || (authorities && authorities.length)){ if(authorizeStateConfig(sp, authorities)){ $state.go(toState.name,toParams); }else{ stateChangeUnauthorizedEvent(toState,toParams); } }else{ $state.go(toState.name,toParams); } },function(){ // The user is not authenticated, emit the necessary event stateChangeUnauthenticatedEvent(toState,toParams); }); }else if(sp.waitForUser && ($user.currentUser===null)){ e.preventDefault(); $user.get().finally(function(){ $state.go(toState.name,toParams); }); } else if($user.currentUser && (sp.authorize || (authorities && authorities.length))){ if(!authorizeStateConfig(sp, authorities)){ e.preventDefault(); stateChangeUnauthorizedEvent(toState,toParams); } }else if(toState.name===config.loginState){ /* If the user is already logged in, we will redirect away from the login page and send the user to the post login state. */ if($user.currentUser!==false){ e.preventDefault(); $user.get().finally(function(){ if($user.currentUser && $user.currentUser.href){ $state.go(config.defaultPostLoginState); } else { $state.go(toState.name,toParams); } }); } } }); }; function authorizeStateConfig(spStateConfig, authorities){ var sp = spStateConfig; if(sp && sp.authorize && sp.authorize.group) { return $user.currentUser.inGroup(sp.authorize.group); }else if(authorities){ // add support for reading from JHipster's data: { authorities: ['ROLE_ADMIN'] } // https://github.com/stormpath/stormpath-sdk-angularjs/issues/190 var roles = authorities.filter(function(authority){ return $user.currentUser.inGroup(authority); }); return roles.length > 0; }else{ console.error('Unknown authorize configuration for spStateConfig',spStateConfig); return false; } } function routeChangeUnauthenticatedEvent(toRoute) { /** * @ngdoc event * * @name stormpath.$stormpath#$routeChangeUnauthenticated * * @eventOf stormpath.$stormpath * * @eventType broadcast on root scope * * @param {Object} event * * Angular event object. * * @param {Object} toRoute The route that the user attempted to access. * * @description * * This event is broadcast when a route change is prevented, * because the user is not logged in. * * Use this event if you want to implement your own strategy for * presenting the user with a login form. * * To receive this event, you must be using the ngRoute module. * * @example * *
         *   $rootScope.$on('$routeChangeUnauthenticated', function(event, toRoute) {
         *     // Your custom logic for deciding how the user should login, and
         *     // if you want to redirect them to the desired route afterwards
         *   });
         * 
*/ $rootScope.$broadcast(STORMPATH_CONFIG.ROUTE_CHANGE_UNAUTHENTICATED, toRoute); } function routeChangeUnauthorizedEvent(toRoute) { /** * @ngdoc event * * @name stormpath.$stormpath#$routeChangeUnauthorized * * @eventOf stormpath.$stormpath * * @eventType broadcast on root scope * * @param {Object} event * * Angular event object. * * @param {Object} toRoute The route that the user attempted to access. * * @description * * This event is broadcast when a route change is prevented, * because the user is not authorized by the rules defined in the * {@link stormpath.SpRouteConfig:SpRouteConfig Stormpath Route Configuration} * for the requested route. * * Use this event if you want to implement your own strategy for telling * the user that they are forbidden from viewing that route. * * To receive this event, you must be using the ngRoute module. * * @example * *
         *   $rootScope.$on('$routeChangeUnauthorized', function(event, toRoute) {
         *     // Your custom logic for deciding how the user should be
         *     // notified that they are forbidden from this route
         *   });
         * 
*/ $rootScope.$broadcast(STORMPATH_CONFIG.ROUTE_CHANGE_UNAUTHORIZED, toRoute); } StormpathService.prototype.routeChangeInterceptor = function routeChangeInterceptor(config) { function goToRoute(route) { setTimeout(function() { if (route.$$route.originalPath === $location.path()) { $route.reload(); } else { $location.path(route); } }); } $rootScope.$on('$routeChangeStart', function(event, toRoute) { if (!toRoute.$$route) { return; } var sp = toRoute.$$route.sp || {}; // Grab the sp config for this route if ((sp.authenticate || sp.authorize) && !$user.currentUser) { event.preventDefault(); $user.get().then(function() { // The user is authenticated, continue to the requested route if (sp.authorize) { if (authorizeStateConfig(sp)) { goToRoute(toRoute); } else { stateChangeUnauthorizedEvent(toRoute); } } else { goToRoute(toRoute); } }, function() { // The user is not authenticated, emit the necessary event routeChangeUnauthenticatedEvent(toRoute); }); } else if (sp.waitForUser && $user.currentUser === null) { event.preventDefault(); $user.get().finally(function() { goToRoute(toRoute); }); } else if ($user.currentUser && sp.authorize) { if (!authorizeStateConfig(sp)) { event.preventDefault(); routeChangeUnauthorizedEvent(toRoute); } } else if (toRoute.$$route.originalPath === config.loginRoute) { /* If the user is already logged in, we will redirect away from the login page and send the user to the post login route. */ if ($user.currentUser && $user.currentUser.href) { event.preventDefault(); goToRoute(config.defaultPostLoginRoute); } } }); }; function authorizeRouteConfig(spRouteConfig) { var sp = spRouteConfig; if (sp && sp.authorize && sp.authorize.group) { return $user.currentUser.inGroup(sp.authorize.group); } console.error('Unknown authorize configuration for spRouteConfig', sp); return false; } /** * @ngdoc function * * @name stormpath#uiRouter * * @methodOf stormpath.$stormpath * * @param {object} config * * * **`autoRedirect`** - Defaults to true. After the user logs in at * the state defined by `loginState`, they will be redirected back to the * state that was originally requested. * * * **`defaultPostLoginState`** - Where the user should be sent, after login, * if they have visited the login page directly. If you do not define a value, * nothing will happen at the login state. You can alternatively use the * {@link stormpath.authService.$auth#events_$authenticated $authenticated} event to know * that login is successful. * * * **`forbiddenState`** - The UI state name that we should send the user * to if they try to an access a view that they are not authorized to view. * This happens in response to an `authorize` rule in one of your * {@link stormpath.SpStateConfig:SpStateConfig Stormpath State Configurations} * * * **`loginState`** - The UI state name that we should send the user * to if they need to login. You'll probably use `login` for this value. * * @description * * Call this method to enable the integration with the UI Router module. * * When enabled, you can define {@link stormpath.SpStateConfig:SpStateConfig Stormpath State Configurations} on your UI states. * This object allows you to define access control for the state. * * You can pass config options to this integration, the options control the * default behavior around "need to login" and "forbidden" situations. * If you wish to implement your own logic for these situations, simply * omit the options and use the events (documented below) to know * what is happening in the application. * * @example * *
       * angular.module('myApp')
       *   .run(function($stormpath){
       *     $stormpath.uiRouter({
       *       forbiddenState: 'forbidden',
       *       defaultPostLoginState: 'main',
       *       loginState: 'login'
       *     });
       *   });
       * 
*/ StormpathService.prototype.uiRouter = function uiRouter(config){ var self = this; config = typeof config === 'object' ? config : {}; this.stateChangeInterceptor(config); if(config.loginState){ self.unauthenticatedWather = $rootScope.$on(STORMPATH_CONFIG.STATE_CHANGE_UNAUTHENTICATED,function(e,toState,toParams){ self.postLogin = { toState: toState, toParams: toParams }; $state.go(config.loginState); }); } $rootScope.$on(STORMPATH_CONFIG.AUTHENTICATION_SUCCESS_EVENT_NAME,function(){ if(self.postLogin && (config.autoRedirect !== false)){ $state.go(self.postLogin.toState,self.postLogin.toParams).then(function(){ self.postLogin = null; }); }else if(config.defaultPostLoginState){ $state.go(config.defaultPostLoginState); } }); if(config.forbiddenState){ self.forbiddenWatcher = $rootScope.$on(STORMPATH_CONFIG.STATE_CHANGE_UNAUTHORIZED,function(){ $state.go(config.forbiddenState); }); } }; /** * @ngdoc function * * @name stormpath#ngRouter * * @methodOf stormpath.$stormpath * * @param {object} config * * * **`autoRedirect`** - Defaults to true. After the user logs in at * the route defined by `loginRoute`, they will be redirected back to the * route that was originally requested. * * * **`defaultPostLoginRoute`** - Where the user should be sent, after login, * if they have visited the login page directly. If you do not define a value, * nothing will happen at the login route. You can alternatively use the * {@link stormpath.authService.$auth#events_$authenticated $authenticated} event to know * that login is successful. * * * **`forbiddenRoute`** - The route that we should send the user * to if they try to an access a view that they are not authorized to view. * This happens in response to an `authorize` rule in one of your * {@link stormpath.SpRouteConfig:SpRouteConfig Stormpath Route Configurations} * * * **`loginRoute`** - The route name that we should send the user * to if they need to login. You'll probably use `login` for this value. * * @description * * Call this method to enable the integration with the ngRoute module. * * When enabled, you can define {@link stormpath.SpRouteConfig:SpRouteConfig Stormpath Route Configurations} on your routes. * This object allows you to define access control for the route. * * You can pass config options to this integration, the options control the * default behavior around "need to login" and "forbidden" situations. * If you wish to implement your own logic for these situations, simply * omit the options and use the events (documented below) to know * what is happening in the application. * * @example * *
       * angular.module('myApp')
       *   .run(function($stormpath){
       *     $stormpath.ngRouter({
       *       forbiddenRoute: '/forbidden',
       *       defaultPostLoginRoute: '/home',
       *       loginRoute: '/login'
       *     });
       *   });
       * 
*/ StormpathService.prototype.ngRouter = function ngRouter(config) { var self = this; config = typeof config === 'object' ? config : {}; this.routeChangeInterceptor(config); if (config.loginRoute) { this.unauthenticatedWather = $rootScope.$on(STORMPATH_CONFIG.ROUTE_CHANGE_UNAUTHENTICATED, function(event, toRoute) { self.postLogin = { toRoute: toRoute }; $location.path(config.loginRoute); }); } $rootScope.$on(STORMPATH_CONFIG.AUTHENTICATION_SUCCESS_EVENT_NAME, function() { if (self.postLogin && config.autoRedirect !== false) { $location.path(self.postLogin.toRoute); self.postLogin = null; } else if (config.defaultPostLoginRoute) { $location.path(config.defaultPostLoginRoute); } }); if (config.forbiddenRoute) { this.forbiddenWatcher = $rootScope.$on(STORMPATH_CONFIG.ROUTE_CHANGE_UNAUTHORIZED, function() { $location.path(config.forbiddenRoute); }); } }; StormpathService.prototype.regexAttrParser = function regexAttrParser(value){ var expr; if(value instanceof RegExp){ expr = value; }else if(value && /^\/.+\/[gim]?$/.test(value)){ expr = new RegExp(value.split('/')[1],value.split('/')[2]); }else{ expr = value; } return expr; }; function UrlEncodedFormParser(){ // Copy & modify from https://github.com/hapijs/qs/blob/master/lib/stringify.js this.delimiter = '&'; this.arrayPrefixGenerators = { brackets: function (prefix) { return prefix + '[]'; }, indices: function (prefix, key) { return prefix + '[' + key + ']'; }, repeat: function (prefix) { return prefix; } }; return this; } UrlEncodedFormParser.prototype.stringify = function stringify(obj, prefix, generateArrayPrefix) { if (obj instanceof Date) { obj = obj.toISOString(); } else if (obj === null) { obj = ''; } if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean') { return [encodeURIComponent(prefix) + '=' + encodeURIComponent(obj)]; } var values = []; if (typeof obj === 'undefined') { return values; } var objKeys = Object.keys(obj); for (var i = 0, il = objKeys.length; i < il; ++i) { var key = objKeys[i]; if (Array.isArray(obj)) { values = values.concat(this.stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix)); } else { values = values.concat(this.stringify(obj[key], prefix + '[' + key + ']', generateArrayPrefix)); } } return values; }; UrlEncodedFormParser.prototype.encode = function encode(obj, options) { options = options || {}; var delimiter = typeof options.delimiter === 'undefined' ? this.delimiter : options.delimiter; var keys = []; if (typeof obj !== 'object' || obj === null) { return ''; } var arrayFormat; if (options.arrayFormat in this.arrayPrefixGenerators) { arrayFormat = options.arrayFormat; } else if ('indices' in options) { arrayFormat = options.indices ? 'indices' : 'repeat'; } else { arrayFormat = 'indices'; } var generateArrayPrefix = this.arrayPrefixGenerators[arrayFormat]; var objKeys = Object.keys(obj); for (var i = 0, il = objKeys.length; i < il; ++i) { var key = objKeys[i]; keys = keys.concat(this.stringify(obj[key], key, generateArrayPrefix)); } return keys.join(delimiter); }; return new StormpathService(); } ]; }]) .run(['$rootScope','$user','STORMPATH_CONFIG',function($rootScope,$user,STORMPATH_CONFIG){ $rootScope.user = $user.currentUser || null; $user.get().finally(function(){ $rootScope.user = $user.currentUser; }); $rootScope.$on(STORMPATH_CONFIG.GET_USER_EVENT,function(){ $rootScope.user = $user.currentUser; }); $rootScope.$on(STORMPATH_CONFIG.SESSION_END_EVENT,function(){ $rootScope.user = $user.currentUser; }); $rootScope.$on(STORMPATH_CONFIG.SESSION_END_ERROR_EVENT,function(event, error){ console.error('Logout error', error); }); $rootScope.$on(STORMPATH_CONFIG.UNAUTHENTICATED_EVENT,function(event, error){ console.error('UNAUTHENTICATED_EVENT'); }); }]) /** * @ngdoc directive * * @name stormpath.ifUser:ifUser * * @description * * Use this directive to conditionally show an element if the user is logged in. * * @example * *
 * 
*

Hello, {{user.fullName}}

*
*
*/ .directive('ifUser',['$user','$rootScope',function($user,$rootScope){ return { link: function(scope,element){ $rootScope.$watch('user',function(user){ if(user && user.href){ element.removeClass('ng-hide'); }else{ element.addClass('ng-hide'); } }); } }; }]) /** * @ngdoc directive * * @name stormpath.ifNotUser:ifNotUser * * @description * * Use this directive to conditionally show an element if the user is NOT logged in. * * @example * *
 * 
*

Hello, you need to login

*
*
*/ .directive('ifNotUser',['$user','$rootScope',function($user,$rootScope){ return { link: function(scope,element){ $rootScope.$watch('user',function(user){ if(user && user.href){ element.addClass('ng-hide'); }else{ element.removeClass('ng-hide'); } }); } }; }]) /** * @ngdoc directive * * @name stormpath.ifUserInGroup:ifUserInGroup * * @description * * Use this directive to conditionally show an element if the user is logged in * and is a member of the group that is specified by the expression. * * The attribute value MUST be one of: * * * A string expression, surrounded by quotes * * A reference to a property on the $scope. That property can be a string or * regular expression. * * **Note**: This feature depends on the data that is returned by the * {@link api/stormpath.STORMPATH_CONFIG:STORMPATH_CONFIG#properties_CURRENT_USER_URI CURRENT_USER_URI}. * Your server should expand the account's groups before returning the user. * If you are using [express-stormpath](https://github.com/stormpath/express-stormpath), simply use * [Automatic Expansion](http://docs.stormpath.com/nodejs/express/latest/user_data.html#automatic-expansion) * * # Using Regular Expressions * * If using a string expression as the attribute value, you can pass a regular * expression by wrapping it in the literal * syntax, e.g. * * `'/admins/'` would match any group which has *admins* in the name * * `'/admin$/'` would match any group were the name **ends with** *admin* * * If referencing a scope property, you should create the value as a RegExp type, * e.g.: * *
 *    $scope.matchGroup = new RegExp(/admins/);
 *  
* * All regular expressions are evaluated via * [RegExp.prototype.test](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test) * * @example * *
 *