├── .gitignore ├── .jshintrc ├── .travis.yml ├── .vscode └── settings.json ├── Gruntfile.js ├── LICENSE.txt ├── README.md ├── RELEASES.md ├── bower.json ├── changelog.txt ├── contributing.md ├── dist ├── adal-angular.min.js └── adal.min.js ├── doc ├── AuthenticationContext.html ├── RequestInfo.html ├── User.html ├── adal.js.html ├── config.html ├── global.html ├── index.html ├── scripts │ ├── linenumber.js │ └── prettify │ │ ├── Apache-License-2.0.txt │ │ ├── lang-css.js │ │ └── prettify.js └── styles │ ├── jsdoc-default.css │ ├── prettify-jsdoc.css │ └── prettify-tomorrow.css ├── karma.conf.js ├── lib ├── adal-angular.js └── adal.js ├── package.json └── tests ├── angularModuleSpec.js ├── browser.manual.runner.html ├── stateApp.js ├── testApp.js └── unit └── spec └── AdalSpec.js /.gitignore: -------------------------------------------------------------------------------- 1 | #OS junk files 2 | [Tt]humbs.db 3 | *.DS_Store 4 | 5 | #node 6 | *.iml 7 | *.ipr 8 | *.iws 9 | *.tmproj 10 | .project 11 | .settings 12 | .externalToolBuilders 13 | *.swp 14 | node_modules 15 | *~ 16 | /.c9revisions/ 17 | build/ 18 | samples/ 19 | 20 | #Visual Studio files 21 | *.[Oo]bj 22 | *.user 23 | *.aps 24 | *.pch 25 | *.vspscc 26 | *.vssscc 27 | *_i.c 28 | *_p.c 29 | *.ncb 30 | *.suo 31 | *.tlb 32 | *.tlh 33 | *.bak 34 | *.[Cc]ache 35 | *.ilk 36 | *.log 37 | *.lib 38 | *.sbr 39 | *.sdf 40 | *.opensdf 41 | *.unsuccessfulbuild 42 | ipch/ 43 | [Oo]bj/ 44 | [Bb]in 45 | [Dd]ebug*/ 46 | [Rr]elease*/ 47 | Ankh.NoLoad 48 | 49 | #MonoDevelop 50 | *.pidb 51 | *.userprefs 52 | 53 | #Tooling 54 | _ReSharper*/ 55 | *.resharper 56 | [Tt]est[Rr]esult* 57 | *.sass-cache 58 | 59 | #Project files 60 | [Bb]uild/ 61 | 62 | #Subversion files 63 | .svn 64 | 65 | # Office Temp Files 66 | ~$* 67 | 68 | # vim Temp Files 69 | *~ 70 | 71 | #NuGet 72 | packages/ 73 | *.nupkg 74 | 75 | #bower 76 | bower_components 77 | 78 | #ncrunch 79 | *ncrunch* 80 | *crunch*.local.xml 81 | 82 | # visual studio database projects 83 | *.dbmdl 84 | 85 | #Test files 86 | *.testsettings 87 | 88 | #copied adal 89 | samples/owin/OwinSample/Scripts/adal.js 90 | 91 | #webstorm 92 | .idea 93 | 94 | #codecoverage 95 | coverage -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": true, 3 | "camelcase": true, 4 | "curly": true, 5 | "eqeqeq": true, 6 | "forin": true, 7 | "freeze": true, 8 | "immed": true, 9 | "indent": 2, 10 | "latedef": true, 11 | "maxparams": false, 12 | "maxdepth": false, 13 | "maxstatements": false, 14 | "maxcomplexity": false, 15 | "multistr" : true, 16 | "newcap": true, 17 | "noarg": true, 18 | "node": true, 19 | "noempty": true, 20 | "nonew": true, 21 | "plusplus": false, 22 | "quotmark": "single", 23 | "regexp": true, 24 | "sub": true, 25 | "strict": true, 26 | "trailing": true, 27 | "undef": true, 28 | "unused": true 29 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.11" 4 | - "0.10" 5 | - "4.7.3" 6 | - "5.12.0" 7 | - "6.1.0" 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "github-pr.targetBranch": "dev" 3 | } -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | 3 | // Project configuration. 4 | grunt.initConfig({ 5 | pkg: grunt.file.readJSON('package.json'), 6 | clean: ['build/'], 7 | jsdoc: { 8 | dist: { 9 | src: ['lib/*.js'], 10 | options: { 11 | destination: 'doc' 12 | } 13 | } 14 | }, 15 | jshint: { 16 | src: { 17 | options: { 18 | jshintrc: '.jshintrc' 19 | }, 20 | src: ['lib/*.js'] 21 | } 22 | }, 23 | jasmine_node: { 24 | options: { 25 | forceExit: true, 26 | match: '.', 27 | matchall: false, 28 | extensions: 'js', 29 | specNameMatcher: 'spec', 30 | jUnit: { 31 | report: true, 32 | savePath: "./build/reports/jasmine/", 33 | useDotNotation: true, 34 | consolidate: true 35 | } 36 | }, 37 | all: ['tests/unit/spec/'] 38 | }, 39 | uglify: { 40 | options: { 41 | banner: '/*! <%= pkg.name %> v<%= pkg.version %> <%= grunt.template.today("yyyy-mm-dd") %> */\n' 42 | }, 43 | static_mappings: { 44 | // Because these src-dest file mappings are manually specified, every 45 | // time a new file is added or removed, the Gruntfile has to be updated. 46 | files: [ 47 | { src: 'lib/adal.js', dest: 'build/adal.min.js' }, 48 | { src: 'lib/adal-angular.js', dest: 'build/adal-angular.min.js' }, 49 | ], 50 | } 51 | }, 52 | }); 53 | 54 | // Load the plugin that provides the "uglify" task. 55 | grunt.loadNpmTasks('grunt-jsdoc'); 56 | grunt.loadNpmTasks('grunt-contrib-jshint'); 57 | grunt.loadNpmTasks('grunt-contrib-uglify'); 58 | grunt.loadNpmTasks('grunt-jasmine-node'); 59 | // uglify task is producing invalid js file 60 | 61 | // jasmine node directly js api 62 | grunt.registerTask('default', ['jshint', 'jasmine_node']); 63 | grunt.registerTask('doc', ['jsdoc']); 64 | grunt.registerTask('minify', ['uglify']); 65 | 66 | }; -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | This library, ADAL for JavaScript, will no longer receive new feature improvements, and this repository has been archived. Instead, use the new library 4 | [MSAL.js](https://github.com/AzureAD/microsoft-authentication-library-for-js) (the code for ADAL.js and ADAL Angular has also been moved to the MSAL.js repository). 5 | 6 | * If you are starting a new project, you can get started with the 7 | [MSAL.js docs](https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki) 8 | for details about the scenarios, usage, and relevant concepts. 9 | * If your application is using the previous ADAL JavaScript library, you can follow this 10 | [migration guide](https://docs.microsoft.com/azure/active-directory/develop/msal-compare-msal-js-and-adal-js) 11 | to update to MSAL.js. 12 | * Existing applications relying on ADAL JavaScript will continue to work. 13 | 14 | --- 15 | 16 | Active Directory Authentication Library (ADAL) for JavaScript 17 | ==================================== 18 | |[Getting Started](https://github.com/Azure-Samples/active-directory-javascript-singlepageapp-dotnet-webapi)| [Docs](https://aka.ms/aaddev)| [Samples](https://github.com/azure-samples?query=active-directory)| [Support](README.md#community-help-and-support) | [Feedback](https://forms.office.com/r/cnmB9FbgX9) 19 | | --- | --- | --- | --- | --- | 20 | 21 | Active Directory Authentication Library for JavaScript (ADAL JS) helps you to use Azure AD for handling authentication in your single page applications. 22 | This library works with both plain JS as well as AngularJS applications. 23 | 24 | [![Build Status](https://travis-ci.org/AzureAD/azure-activedirectory-library-for-js.svg?branch=master)](https://travis-ci.org/AzureAD/azure-activedirectory-library-for-js)[![npm](https://img.shields.io/npm/v/adal-angular.svg)](https://www.npmjs.com/package/adal-angular)[![npm](https://img.shields.io/npm/dm/adal-angular.svg)](https://www.npmjs.com/package/adal-angular) 25 | 26 | ## Installation 27 | 28 | You have multiple ways of getting ADAL JS: 29 | 30 | Via NPM: 31 | 32 | npm install adal-angular 33 | 34 | *Note:* Currently there is one NPM package providing both the plain JS library (adal.js) and the AngularJS wrapper (adal-angular.js). 35 | 36 | Via CDN: 37 | 38 | 39 | 40 | 41 | 42 | CDN will be updated to latest version 1.0.18. 43 | 44 | Via Bower: 45 | 46 | $ bower install adal-angular 47 | 48 | The adal.js source is [here](https://github.com/AzureAD/azure-activedirectory-library-for-js/tree/master/lib/adal.js). 49 | The adal-angular.js source is [here](https://github.com/AzureAD/azure-activedirectory-library-for-js/tree/master/lib/adal-angular.js). 50 | 51 | ## Usage 52 | 53 | **In JavaScript** 54 | 55 | You can use ADAL JS as follows in a plain JavaScript application without any frameworks. 56 | 57 | 1- Include a reference to adal.js in your main app page before your application scripts. 58 | 59 | ```html 60 | 61 | 62 | ``` 63 | 64 | 2- Initialize ADAL with the AAD app coordinates at app config time. The minimum required config to initialize ADAL is: 65 | ```js 66 | window.config = { 67 | clientId: 'g075edef-0efa-453b-997b-de1337c29185' 68 | }; 69 | var authContext = new AuthenticationContext(config); 70 | ``` 71 | 72 | 3- You can trigger the login and logout using the authContext 73 | ```js 74 | $signInButton.click(function () { 75 | authContext.login(); 76 | }); 77 | 78 | $signOutButton.click(function () { 79 | authContext.logOut(); 80 | }); 81 | ``` 82 | 83 | Refer this [sample](https://github.com/Azure-Samples/active-directory-javascript-singlepageapp-dotnet-webapi) for a full implementation example. 84 | 85 | **In AngularJS** 86 | 87 | ADAL also provides an AngularJS wrapper as adal-angular.js. Below you can find a quick reference for the most common operations you need to perform in AngularJS applications to use ADAL JS. 88 | 89 | 1- Include references to angular.js libraries, adal.js, adal-angular.js in your main app page. The ADAL should be included after Angular, but before your application scripts as shown below. 90 | ```html 91 | 92 | 93 | 94 | 95 | 96 | ``` 97 | 98 | 2- Include a reference to the ADAL module in your app module. 99 | ```js 100 | var app = angular.module('demoApp', ['ngRoute', 'AdalAngular']); 101 | ``` 102 | 103 | 3- ***When HTML5 mode is configured***, ensure the $locationProvider hashPrefix is set 104 | 105 | ```js 106 | // using '!' as the hashPrefix but can be a character of your choosing 107 | app.config(['$locationProvider', function($locationProvider) { 108 | $locationProvider.html5Mode(true).hashPrefix('!'); 109 | }]); 110 | ``` 111 | 112 | Without the hashPrefix set, the AAD login will loop indefinitely as the callback URL from AAD (in the form of, {yourBaseUrl}/#{AADTokenAndState}) will be rewritten to remove the '#' causing the token parsing to fail and login sequence to occur again. 113 | 114 | 4- Initialize ADAL with the AAD app coordinates at app config time. The minimum required config to initialize ADAL is: 115 | ```js 116 | adalAuthenticationServiceProvider.init({ 117 | // clientId is the identifier assigned to your app by Azure Active Directory. 118 | clientId: "e9a5a8b6-8af7-4719-9821-0deef255f68e" 119 | }, 120 | $httpProvider // pass http provider to inject request interceptor to attach tokens 121 | ); 122 | ``` 123 | 124 | 5- Define which routes you want to secure via ADAL - by adding `requireADLogin: true` to their definition 125 | ```js 126 | $routeProvider. 127 | when("/todoList", { 128 | controller: "todoListController", 129 | templateUrl: "/App/Views/todoList.html", 130 | requireADLogin: true 131 | }); 132 | 133 | ``` 134 | Any service invocation code you might have will remain unchanged. ADAL's interceptor will automatically add tokens for every outgoing call. 135 | 136 | Anonymous endpoints, introduced in version 1.0.10, is an array of values that will be ignored by the ADAL route/state change handlers. ADAL will not attach a token to outgoing requests that have these keywords or URI. Routes that *do not* specify the ```requireADLogin=true``` property are added to the ```anonymousEndpoints``` array automatically. 137 | 138 | ***Optional*** 139 | 140 | If you so choose, in addition (or substitution) to route level protection you can add explicit login/logout UX elements. Furthermore, you can access properties of the currently signed in user directly form JavaScript (via userInfo and userInfo.profile). 141 | The userInfo.profile property provides access to the claims in the ID token received from AAD. The claims can be used by the application for validation, to identify the subject's directory tenant, and so on. The complete list of claims with a brief description of each value is here, [Claims in Azure AD Security Tokens](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-authentication-scenarios): 142 | ```html 143 | 144 | 145 | 146 | Angular Adal Sample 147 | 148 | 149 | Home 150 | ToDo List 151 | 152 | 153 | 154 |
155 | Welcome {{userInfo.userName}} 156 | 157 | 158 | 159 |
160 | {{userInfo.loginError}} 161 |
162 |
163 | {{testMessage}} 164 |
165 |
166 |
167 | Your view will appear here. 168 |
169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | ``` 182 | 6- You have full control on how to trigger sign in, sign out and how to deal with errors: 183 | 184 | ```js 185 | 'use strict'; 186 | app.controller('homeController', ['$scope', '$location', 'adalAuthenticationService', function ($scope, $location, adalAuthenticationService) { 187 | // this is referencing adal module to do login 188 | 189 | //userInfo is defined at the $rootscope with adalAngular module 190 | $scope.testMessage = ""; 191 | $scope.init = function () { 192 | $scope.testMessage = ""; 193 | }; 194 | 195 | $scope.logout = function () { 196 | adalAuthenticationService.logOut(); 197 | }; 198 | 199 | $scope.login = function () { 200 | adalAuthenticationService.login(); 201 | }; 202 | 203 | // optional 204 | $scope.$on("adal:loginSuccess", function () { 205 | $scope.testMessage = "loginSuccess"; 206 | }); 207 | 208 | // optional 209 | $scope.$on("adal:loginFailure", function () { 210 | $scope.testMessage = "loginFailure"; 211 | $location.path("/login"); 212 | }); 213 | 214 | // optional 215 | $scope.$on("adal:notAuthorized", function (event, rejection, forResource) { 216 | $scope.testMessage = "It is not Authorized for resource:" + forResource; 217 | }); 218 | 219 | }]); 220 | ``` 221 | 222 | ### Multi-Tenant 223 | 224 | By default, you have multi-tenant support. ADAL will set tenant to 'common', if it is not specified in the config. This allows any Microsoft account to authenticate to your application. If you are not interested in multi-tenant behavior, you will need to set the ```tenant``` property as shown below. 225 | 226 | ```js 227 | window.config = { 228 | tenant: "52d4b072-9470-49fb-8721-bc3a1c9912a1", // Optional by default, it sends common 229 | clientId: 'g075edef-0efa-453b-997b-de1337c29185' 230 | }; 231 | ``` 232 | If you allow multi-tenant authentication, and you do not wish to allow all Microsoft account users to use your application, you must provide your own method of filtering the token issuers to only those tenants who are allowed to login. 233 | 234 | ### Cache Location 235 | Default storage location is sessionStorage. You can specify localStorage in the config as well. 236 | 237 | ```js 238 | window.config = { 239 | clientId: 'g075edef-0efa-453b-997b-de1337c29185', 240 | cacheLocation: 'localStorage' // optional cache location. Default is sessionStorage 241 | }; 242 | ``` 243 | 244 | ### Logging 245 | 246 | Log levels are mapped as: 247 | 248 | 0: Error 249 | 1: Warning 250 | 2: Info 251 | 3: Verbose 252 | 253 | You can add the code below to app.js to turn on logging. Implement the `log` method depending on how you want to redirect logs. 254 | 255 | Logging = { 256 | level: 3, 257 | log: function (message) { 258 | console.log(message); 259 | } 260 | }; 261 | 262 | ### Security 263 | Tokens are accessible from JavaScript since ADAL.JS is using HTML5 storage. Default storage option is sessionStorage, which keeps the tokens per session. You should prompt users to login again for important operations on your app. 264 | You should protect your site for XSS. Please check the article here: [https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet](https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet) 265 | 266 | 267 | ### CORS API usage and IE 268 | ADAL will get access token using Iframe for the given CORS API endpoints in the config. The Iframe needs to access cookies for the same domain that you did the initial sign in. Since IE does not allow to access cookies in an IFrame for localhost, your URL needs to be a fully qualified domain i.e http://yoursite.azurewebsites.com. Chrome does not have this restriction. 269 | 270 | To make CORS API call, you need to specify endpoints in the config for your CORS API as shown here. 271 | 272 | ```js 273 | // endpoint to resource mapping(optional) 274 | var endpoints = { 275 | "https://yourhost/api": "b6a68585-5287-45b2-ba82-383ba1f60932", 276 | }; 277 | adalAuthenticationServiceProvider.init( 278 | { 279 | tenant: "52d4b072-9470-49fb-8721-bc3a1c9912a1", // Optional by default, it sends common 280 | clientId: "e9a5a8b6-8af7-4719-9821-0deef255f68e", // Required 281 | endpoints: endpoints // If you need to send CORS API requests. 282 | }, 283 | $httpProvider // pass http provider to inject request interceptor to attach tokens 284 | ); 285 | ``` 286 | 287 | Your service will be as shown below to make the call from JS. In your API project, you need to enable CORS API requests to receive pre-flight requests. You can check this [sample](https://github.com/AzureADSamples/SinglePageApp-WebAPI-AngularJS-DotNet) for CORS API. 288 | 289 | ```js 290 | 'use strict'; 291 | app.factory('contactService', ['$http', function ($http) { 292 | var serviceFactory = {}; 293 | 294 | var _getItems = function () { 295 | $http.defaults.useXDomain = true; 296 | delete $http.defaults.headers.common['X-Requested-With']; 297 | return $http.get('http://adaljscors.azurewebsites.net/api/contacts'); 298 | }; 299 | 300 | serviceFactory.getItems = _getItems; 301 | 302 | return serviceFactory; 303 | }]); 304 | ``` 305 | 306 | You can read extended blogs about CORS API below. 307 | 308 | Andrew's blog related to CORS and Office365 usage 309 | 310 | http://www.andrewconnell.com/blog/adal-js-cors-with-o365-apis-files-sharepoint 311 | 312 | Vittorio's blog 313 | 314 | http://www.cloudidentity.com/blog/2015/02/19/introducing-adal-js-v1/ 315 | http://www.cloudidentity.com/blog/2014/10/28/adal-javascript-and-angularjs-deep-dive/ 316 | 317 | ### Trusted Site settings in IE 318 | If you put your site in the trusted site list, cookies are not accessible for iFrame requests. You need to remove protected mode for Internet zone or add the authority URL for the login to the trusted sites as well. 319 | 320 | ### Known issues on Edge 321 | Certain issues have been reported when using ADAL.js with the Microsoft Edge version 40.15063.0.0. Please take a look at [this page](https://github.com/AzureAD/azure-activedirectory-library-for-js/wiki/Known-issues-on-Edge) for details and workarounds before filing a new issue experienced with Edge. 322 | 323 | ## Build and run tests 324 | 325 | **Run tests** 326 | 327 | npm install 328 | bower install 329 | npm test 330 | 331 | // angular tests 332 | karma start 333 | 334 | To use Karma as test runner, you need to install the karma command line. 335 | 336 | npm install -g karma 337 | npm install -g karma-cli 338 | 339 | **Reference doc generation** 340 | 341 | Install grunt and run the command 342 | 343 | grunt doc 344 | 345 | ## Contribution 346 | 347 | We encourage and welcome contributions to the library. Please read the [contributing guide](./contributing.md) before starting. 348 | 349 | ## Versions 350 | This is a GA released version. Current version - **1.0.18** 351 | Minimum recommended version - 1.0.18 352 | You can find the changes for each version in the [change log](https://github.com/AzureAD/azure-activedirectory-library-for-js/blob/master/changelog.txt). 353 | 354 | ## Samples and Documentation 355 | 356 | [We provide a full suite of sample applications and documentation on GitHub](https://github.com/azure-samples?query=active-directory) to help you get started with learning the Azure Identity system. This includes tutorials for native clients such as Windows, Windows Phone, iOS, OSX, Android, and Linux; and a detailed guide to registering your app with Azure Active Directory. We also provide full walkthroughs for authentication flows such as OAuth2, OpenID Connect, Graph API, and other awesome features. 357 | 358 | ## Community Help and Support 359 | 360 | We leverage [Stack Overflow](http://stackoverflow.com/) to work with the community on supporting Azure Active Directory and its SDKs, including this one! We highly recommend you ask your questions on Stack Overflow (we're all on there!) Also browser existing issues to see if someone has had your question before. 361 | 362 | We recommend you use the "adal" tag so we can see it! Here is the latest Q&A on Stack Overflow for ADAL: [http://stackoverflow.com/questions/tagged/adal](http://stackoverflow.com/questions/tagged/adal) 363 | 364 | ## Submit Feedback 365 | We'd like your thoughts on this library. Please complete [this short survey.](https://forms.office.com/r/cnmB9FbgX9) 366 | 367 | ## Security Reporting 368 | 369 | If you find a security issue with our libraries or services [please report it to the Microsoft Security Response Center (MSRC)](https://aka.ms/report-security-issue) with as much detail as possible. Your submission may be eligible for a bounty through the [Microsoft Bounty](http://aka.ms/bugbounty) program. Please do not post security issues to GitHub Issues or any other public site. We will contact you shortly upon receiving the information. We encourage you to get notifications of when security incidents occur by visiting [this page](https://www.microsoft.com/msrc/technical-security-notifications) and subscribing to Security Advisory Alerts. 370 | 371 | ## License 372 | Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); 373 | 374 | ## We value and adhere to the Microsoft Open Source Code of Conduct 375 | 376 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 377 | -------------------------------------------------------------------------------- /RELEASES.md: -------------------------------------------------------------------------------- 1 | # Microsoft Identity SDK Versioning and Servicing FAQ 2 | 3 | We have adopted the semantic versioning flow that is industry standard for OSS projects. It gives the maximum amount of control on what risk you take with what versions. If you know how semantic versioning works with node.js, java, and ruby none of this will be new. 4 | 5 | ##Semantic Versioning and API stability promises 6 | 7 | Microsoft Identity libraries are independent open source libraries that are used by partners both internal and external to Microsoft. As with the rest of Microsoft, we have moved to a rapid iteration model where bugs are fixed daily and new versions are produced as required. To communicate these frequent changes to external partners and customers, we use semantic versioning for all our public Microsoft Identity SDK libraries. This follows the practices of other open source libraries on the internet. This allows us to support our downstream partners which will lock on certain versions for stability purposes, as well as providing for the distribution over NuGet, CocoaPods, and Maven. 8 | 9 | The semantics are: MAJOR.MINOR.PATCH (example 1.1.5) 10 | 11 | We will update our code distributions to use the latest PATCH semantic version number in order to make sure our customers and partners get the latest bug fixes. Downstream partner needs to pull the latest PATCH version. Most partners should try lock on the latest MINOR version number in their builds and accept any updates in the PATCH number. 12 | 13 | Examples: 14 | Using Cocapods, the following in the podfile will take the latest ADALiOS build that is > 1.1 but not 1.2. 15 | ``` 16 | pod 'ADALiOS', '~> 1.1' 17 | ``` 18 | 19 | Using NuGet, this ensures all 1.1.0 to 1.1.x updates are included when building your code, but not 1.2. 20 | 21 | ``` 22 | 26 | ``` 27 | 28 | | Version | Description | Example | 29 | |:-------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:---------------------------------------------------------------------------------------------------------:| 30 | | x.x.x | PATCH version number. Incrementing these numbers is for bug fixes and updates but do not introduce new features. This is used for close partners who build on our platform release (ex. Azure AD Fabric, Office, etc.),In addition, Cocoapods, NuGet, and Maven use this number to deliver the latest release to customers.,This will update frequently (sometimes within the same day),There is no new features, and no regressions or API surface changes. Code will continue to work unless affected by a particular code fix. | ADAL for iOS 1.0.10,(this was a fix for the Storyboard display that was fixed for a specific Office team) | 31 | | x.x | MINOR version numbers. Incrementing these second numbers are for new feature additions that do not impact existing features or introduce regressions. They are purely additive, but may require testing to ensure nothing is impacted.,All x.x.x bug fixes will also roll up in to this number.,There is no regressions or API surface changes. Code will continue to work unless affected by a particular code fix or needs this new feature. | ADAL for iOS 1.1.0,(this added WPJ capability to ADAL, and rolled all the updates from 1.0.0 to 1.0.12) | 32 | | x | MAJOR version numbers. This should be considered a new, supported version of Microsoft Identity SDK and begins the Azure two year support cycle anew. Major new features are introduced and API changes can occur.,This should only be used after a large amount of testing and used only if those features are needed.,We will continue to service MAJOR version numbers with bug fixes up to the two year support cycle. | ADAL for iOS 1.0,(our first official release of ADAL) | 33 | 34 | 35 | 36 | ## Serviceability 37 | 38 | When we release a new MINOR version, the previous MINOR version is abandoned. 39 | 40 | When we release a new MAJOR version, we will continue to apply bug fixes to the existing features in the previous MAJOR version for up to the 2 year support cycle for Azure. 41 | Example: We release ADALiOS 2.0 in the future which supports unified Auth for AAD and MSA. Later, we then have a fix in Conditional Access for ADALiOS. Since that feature exists both in ADALiOS 1.1 and ADALiOS 2.0, we will fix both. It will roll up in a PATCH number for each. Customers that are still locked down on ADALiOS 1.1 will receive the benefit of this fix. 42 | 43 | ## Microsoft Identity SDKs and Azure Active Directory 44 | 45 | Microsoft Identity SDKs major versions will maintain backwards compatibility with Azure Active Directory web services through the support period. This means that the API surface area defined in a MAJOR version will continue to work for 2 years after release. 46 | 47 | We will respond to bugs quickly from our partners and customers submitted through GitHub and through our private alias (tellaad@microsoft.com) for security issues and update the PATCH version number. We will also submit a change summary for each PATCH number. 48 | Occasionally, there will be security bugs or breaking bugs from our partners that will require an immediate fix and a publish of an update to all partners and customers. When this occurs, we will do an emergency roll up to a PATCH version number and update all our distribution methods to the latest. 49 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "adal-angular", 3 | "version": "1.0.18", 4 | "homepage": "https://github.com/AzureAD/azure-activedirectory-library-for-js", 5 | "authors": [ 6 | "MSOpentech" 7 | ], 8 | "description": "Azure Active Directory Client Library for js", 9 | "main": [ 10 | "./lib/adal.js", 11 | "./lib/adal-angular.js" 12 | ], 13 | "moduleType": [ 14 | "node" 15 | ], 16 | "keywords": [ 17 | "adal", 18 | "oauth", 19 | "active", 20 | "directory", 21 | "azure" 22 | ], 23 | "licenses": [ 24 | { 25 | "type": "Apache 2.0", 26 | "url": "http://www.apache.org/licenses/LICENSE-2.0" 27 | } 28 | ], 29 | "ignore": [ 30 | "**/.*", 31 | "node_modules", 32 | "bower_components", 33 | "test", 34 | "tests" 35 | ], 36 | "dependencies": { 37 | "angular": "*" 38 | }, 39 | "devDependencies": { 40 | "angular-resource": "~1.2.26", 41 | "angular-mocks": "^1.3.0", 42 | "jasmine": "2.0.0", 43 | "angular-route": "~1.2.26", 44 | "angular-ui-router": "^0.3.1" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /changelog.txt: -------------------------------------------------------------------------------- 1 | Version 1.0.18 2 | ========================= 3 | * Properly reject requests with mismatched nonce. 4 | 5 | Version 1.0.17 6 | ========================= 7 | * Added sid support. When the session id parameter is provided in the id_token, use that instead of the upn 8 | * Add GDPR compliant logging. You can now set a flag piiLoggingEnabled by calling Logging.piiLoggingEnabled = true which when set, Adal JS will not log messages that contain the user's personal identifier. 9 | * Removed const keyword to support ES5 compatibility. 10 | * Other bug fixes and updates 11 | 12 | Version 1.0.16 13 | ========================= 14 | * Added ability to login in multiple tabs simultaneously when using localStorage where data is shared across tabs. Please see this: https://github.com/AzureAD/azure-activedirectory-library-for-js/issues/639. 15 | * Added ablity to pass prompt parameter specifically to login method of adal which was not possible in previous versions. You can set extraQueryParameter:'prompt=select_account' in the adal config and this 16 | value will only get appended to login url. Please see this: https://github.com/AzureAD/azure-activedirectory-library-for-js/issues/636. 17 | * Added support for ui-router versions > 1.0 in which all state change events were deprecated and replaced with transition events. Please see this : https://github.com/AzureAD/azure-activedirectory-library-for-js/issues/574. 18 | * Added ability to make loadFrameTimeout property of adal configurable. This is the time adal waits to receive a response for the token renewal request in case of acquireToken. 19 | The default value is 6 seconds but there were several instances where increasing the timeout to 10 seconds helped reduce network latency errors. 20 | * Added error condition to cover the case when user opens the popup window for login and then closes it without entering their credentials. The same error is thrown when popup window handle is destroyed during cross zone 21 | navigation in IE/Edge. The error message ('Popup Window closed by UI action/ Popup Window handle destroyed due to cross zone navigation in IE/Edge') is broadcasted as an event in case adal-angular is used and it is 22 | passed to the callback function in the config in case plain adalJs is used. 23 | * In the case where user already has an existing session with AAD and just wants to acquire a token for a resource for the same user using Adal, he can set extraQueryParameter:'login_hint=xxx' in the adal config and 24 | directly call acquireToken without calling login first. In this scenario, adal will send the request with responseType = 'id_token token' in a hidden iframe to receive both an id_token to estabilish user context 25 | as well as an access_token for the specified resource. 26 | This feature allows silent login as opposed to showing a UI in the case where the user has an existing session with AAD. 27 | * Other bug fixes and updates 28 | 29 | Version 1.0.15 30 | ========================= 31 | * Added acquireTokenRedirect and acquireTokenPopup api's to acquire a token for resource interactively (MFA) and support conditional access. 32 | * Fixed a bug in acquireToken method that was happening when Adaljs was consumed as a module using loaders such as webpack. 33 | * Added custom event polyfill to support IE 34 | * Other bug fixes and updates 35 | 36 | Version 1.0.14 37 | ========================= 38 | * Adding ability to specify anonymous sub-urls for a configured endpoint 39 | * Adding api documentation 40 | * Adding ability to turn off default navigation to start page after login. Set navigateToLoginRequestUrl:false. 41 | * Fixing http interceptor to look into templateCache first 42 | * Fixing infinite loop issues when using popUp for angular versions 1.5.8 or above 43 | * Fixing silent exception error in case of user defined callback function 44 | * Fixing double error pipeline in angular acquireToken method 45 | * Updating the default token expiry offset from 2 minutes to 5 minutes 46 | * Other bug fixes and updates 47 | 48 | Version 1.0.13 49 | ========================= 50 | * Pass 'error' parameter to the callback besides 'error_description': #424 51 | * Adding API documentation of adal.js 52 | * Adding 'acquireTokenSuccess' and 'acquireTokenFailure' events in adal-angular.js 53 | * Other bug fixes and updates 54 | 55 | Version 1.0.12 56 | ========================== 57 | * Adding support for Login using a pop-up instead of a full redirect. Please see this: https://github.com/AzureAD/azure-activedirectory-library-for-js/issues/60 58 | * Updating anonymousEndpoints feature to handle nested states in ui-router. Please see this: https://github.com/AzureAD/azure-activedirectory-library-for-js/issues/345 59 | * Fix bug in anonymousEndpoints allowing templateUrl property of the route/state to be declared as a function. Thanks @dhodgin for the Pull Request. Please see this: https://github.com/AzureAD/azure-activedirectory-library-for-js/issues/368 60 | * Using window.crypto.getRandomValues API to generate version 4 UUID as per RFC 4122. Please see this: https://github.com/AzureAD/azure-activedirectory-library-for-js/issues/88 61 | * Fix bug in handleWindowCallback to call the callback defined on config after Login operation is completed. Please see this: https://github.com/AzureAD/azure-activedirectory-library-for-js/issues/324 62 | * Other bug fixes and updates. 63 | 64 | Version 1.0.11 65 | ========================== 66 | * Adding support for using a special html for iFrames. This prevents app reloading in the iframe. Please see this: https://github.com/AzureAD/azure-activedirectory-library-for-js/wiki/FAQs#q1-my-app-is-re-loading-every-time-adal-renews-a-token 67 | * Fixing multiple root causes for infinte loops at the time of login or token renewal. 68 | * Fixing url paramters getting dropped after login. 69 | * Adding timeout to token renewal requests. Thanks @dmxfee for the Pull Request. 70 | * Use module pattern in adal.js 71 | 72 | Version 1.0.10 73 | ========================== 74 | Fixing infinite loop when refreshing tokens, adding extensibility for specifying anonymous endpoints 75 | 76 | Version 1.0.9 77 | ========================== 78 | Adding events for state mismatch and error responses. Fix for token renewal for app's backend. Making library available on NPM 79 | 80 | Version 1.0.8 81 | ========================== 82 | Fix for persisting route parameters of protected state 83 | 84 | Version 1.0.7 85 | ========================== 86 | Fix for checking if interceptor call comes from app backend 87 | 88 | Version 1.0.6 89 | ========================== 90 | Fix issue#159, support base64 decoding in ie9 91 | Remove UTF-8 BOM headers from files 92 | Ignore inceptor call if the url is not app backend or not listed in the endpoints list 93 | Fix issue #153, proivde logging level, and user needs to specify logging way 94 | 95 | Version 1.0.5 96 | ========================== 97 | check domain_hint query param existence before adding 98 | 99 | Version 1.0.4 100 | ========================== 101 | Fix infinite loop when specifying login resource 102 | Fix #139: Queue callbacks for renewals 103 | Fix constant definitions and use global in unit tests 104 | 105 | Version 1.0.3 106 | ========================== 107 | Revert the change from prevous release "Move the check for the boolean value isEndPoint in AdalAngular interceptor (issue #127)" 108 | update minified version of adal and adal_angular. 109 | 110 | Version 1.0.2 111 | ========================== 112 | Move the check for the boolean value isEndPoint in AdalAngular interceptor (issue #127) 113 | 114 | Version 0.0.7 115 | ========================== 116 | idtoken fix 117 | 118 | Version 0.0.6 119 | ========================== 120 | Fix to refresh token for external endpoints 121 | Ui router 122 | 123 | 124 | Version 0.0.5 125 | ========================== 126 | Storage option for localStorage and sessionStorage. 127 | Simple js sample 128 | 129 | Version 0.0.4 130 | ========================== 131 | Split adal.js to adal.js and adal-angular.js 132 | Fix interceptor reject message 133 | Use location.path for route params 134 | Add grunt minify task 135 | 136 | Version 0.0.3 137 | ========================== 138 | Apply changes for DI strict, 139 | Token interceptor fixes. 140 | 141 | Version 0.0.2 142 | ========================== 143 | Fix DI strict mode issue for latest version of Angular 144 | Fix unknown header errors from token interceptor 145 | 146 | Version 0.0.1 147 | ========================== 148 | Preview Release 149 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTING 2 | 3 | Azure Active Directory SDK projects welcomes new contributors. This document will guide you 4 | through the process. 5 | 6 | ### CONTRIBUTOR LICENSE AGREEMENT 7 | 8 | Please visit [https://cla.microsoft.com/](https://cla.microsoft.com/) and sign the Contributor License 9 | Agreement. You only need to do that once. We can not look at your code until you've submitted this request. 10 | 11 | 12 | ### FORK 13 | 14 | Fork the project [on GitHub][] and check out 15 | your copy. 16 | 17 | Example for JS: 18 | 19 | ``` 20 | $ git clone git@github.com:username/azure-activedirectory-library-for-js.git 21 | $ cd azure-activedirectory-library-for-js 22 | $ git remote add upstream git@github.com:MSOpenTech/azure-activedirectory-library-for-js.git 23 | ``` 24 | 25 | Now decide if you want your feature or bug fix to go into the dev branch 26 | or the master branch. **All bug fixes and new features should go into the dev branch.** 27 | 28 | The master branch is effectively frozen; patches that change the SDKs 29 | protocols or API surface area or affect the run-time behavior of the SDK will be rejected. 30 | 31 | Some of our SDKs have bundled dependencies that are not part of the project proper. Any changes to files in those directories or its subdirectories should be sent to their respective 32 | projects. Do not send your patch to us, we cannot accept it. 33 | 34 | In case of doubt, open an issue in the [issue tracker][]. 35 | 36 | Especially do so if you plan to work on a major change in functionality. Nothing is more 37 | frustrating than seeing your hard work go to waste because your vision 38 | does not align with our goals for the SDK. 39 | 40 | 41 | ### BRANCH 42 | 43 | Okay, so you have decided on the proper branch. Create a feature branch 44 | and start hacking: 45 | 46 | ``` 47 | $ git checkout -b my-feature-branch 48 | ``` 49 | 50 | ### COMMIT 51 | 52 | Make sure git knows your name and email address: 53 | 54 | ``` 55 | $ git config --global user.name "J. Random User" 56 | $ git config --global user.email "j.random.user@example.com" 57 | ``` 58 | 59 | Writing good commit logs is important. A commit log should describe what 60 | changed and why. Follow these guidelines when writing one: 61 | 62 | 1. The first line should be 50 characters or less and contain a short 63 | description of the change prefixed with the name of the changed 64 | subsystem (e.g. "net: add localAddress and localPort to Socket"). 65 | 2. Keep the second line blank. 66 | 3. Wrap all other lines at 72 columns. 67 | 68 | A good commit log looks like this: 69 | 70 | ``` 71 | fix: explaining the commit in one line 72 | 73 | Body of commit message is a few lines of text, explaining things 74 | in more detail, possibly giving some background about the issue 75 | being fixed, etc etc. 76 | 77 | The body of the commit message can be several paragraphs, and 78 | please do proper word-wrap and keep columns shorter than about 79 | 72 characters or so. That way `git log` will show things 80 | nicely even when it is indented. 81 | ``` 82 | 83 | The header line should be meaningful; it is what other people see when they 84 | run `git shortlog` or `git log --oneline`. 85 | 86 | Check the output of `git log --oneline files_that_you_changed` to find out 87 | what directories your changes touch. 88 | 89 | 90 | ### REBASE 91 | 92 | Use `git rebase` (not `git merge`) to sync your work from time to time. 93 | 94 | ``` 95 | $ git fetch upstream 96 | $ git rebase upstream/v0.1 # or upstream/master 97 | ``` 98 | 99 | 100 | ### TEST 101 | 102 | Bug fixes and features should come with tests. Add your tests in the 103 | test directory. This varies by repository but often follows the same convention of /src/test. Look at other tests to see how they should be 104 | structured (license boilerplate, common includes, etc.). 105 | 106 | 107 | Make sure that all tests pass. 108 | 109 | 110 | ### PUSH 111 | 112 | ``` 113 | $ git push origin my-feature-branch 114 | ``` 115 | 116 | Go to https://github.com/username/azure-activedirectory-library-for-***.git and select your feature branch. Click 117 | the 'Pull Request' button and fill out the form. 118 | 119 | Pull requests are usually reviewed within a few days. If there are comments 120 | to address, apply your changes in a separate commit and push that to your 121 | feature branch. Post a comment in the pull request afterwards; GitHub does 122 | not send out notifications when you add commits. 123 | 124 | 125 | [on GitHub]: https://github.com/MSOpenTech/azure-activedirectory-library-for-js 126 | [issue tracker]: https://github.com/MSOpenTech/azure-activedirectory-library-for-js/issues 127 | -------------------------------------------------------------------------------- /dist/adal-angular.min.js: -------------------------------------------------------------------------------- 1 | /*! adal-angular v1.0.17 2018-02-27 */ 2 | !function(){"use strict";if("undefined"!=typeof module&&module.exports&&(module.exports.inject=function(a){return new AuthenticationContext(a)}),angular){var a=angular.module("AdalAngular",[]);a.provider("adalAuthenticationService",function(){var a=null,b={isAuthenticated:!1,userName:"",loginError:"",profile:""},c=function(c){var d=a.getCachedToken(c);b.isAuthenticated=null!==d&&d.length>0;var e=a.getCachedUser()||{userName:""};b.userName=e.userName,b.profile=e.profile,b.loginError=a.getLoginError()};this.init=function(b,d){if(!b)throw new Error("You must set configOptions, when calling init");b.isAngular=!0,d&&d.interceptors&&d.interceptors.push("ProtectedResourceInterceptor"),a=new AuthenticationContext(b),c(a.config.loginResource)},this.$get=["$rootScope","$window","$q","$location","$timeout","$injector",function(d,e,f,g,h,i){function j(a,b){return b.requireADLogin?!1!==a.requireADLogin:!!a.requireADLogin}function k(b){if(a.config&&a.config.anonymousEndpoints)for(var c=0;c-1)return!0;return!1}function l(a){var b=null,c=[];if(a.hasOwnProperty("parent"))for(b=a;b;)c.unshift(b),b=i.get("$state").get(b.parent);else for(var d=a.name.split("."),e=0,f=d[0];e0&&a._openedWindows[a._openedWindows.length-1].opener&&a._openedWindows[a._openedWindows.length-1].opener._adalInstance?(a=a._openedWindows[a._openedWindows.length-1].opener._adalInstance,k=!0):e.parent&&e.parent._adalInstance&&(a=e.parent._adalInstance),a.verbose("Processing the hash: "+f);var l=a.getRequestInfo(f);a.saveTokenFromHash(l);var m=l.parameters.access_token||l.parameters.id_token,n=l.parameters.error,o=l.parameters.error_description,p=null,q=a._callBackMappedToRenewStates[l.stateResponse]||a.callback;if(l.stateMatch){if(l.requestType===a.REQUEST_TYPE.RENEW_TOKEN?(p=a.CONSTANTS.ACCESS_TOKEN,a._renewActive=!1,e.parent!==e||a._callBackMappedToRenewStates[l.stateResponse]||(m?d.$broadcast("adal:acquireTokenSuccess",m):n&&o&&d.$broadcast("adal:acquireTokenFailure",o,n))):l.requestType===a.REQUEST_TYPE.LOGIN&&(p=a.CONSTANTS.ID_TOKEN,c(a.config.loginResource),b.userName?(h(function(){c(a.config.loginResource),d.userInfo=b},1),d.$broadcast("adal:loginSuccess",m)):d.$broadcast("adal:loginFailure",o,n)),q&&"function"==typeof q&&q(o,m,n,p),window.parent!==window)return void(j&&j.preventDefault&&j.preventDefault());if(e.parent===window&&!k)if(a.config.navigateToLoginRequestUrl){var r=a._getItem(a.CONSTANTS.STORAGE.LOGIN_REQUEST);void 0!==r&&r&&0!==r.length&&(a.verbose("Redirecting to start page: "+r),!g.$$html5&&r.indexOf("#")>-1&&g.url(r.substring(r.indexOf("#")+1)),e.location.href=r)}else g.$$html5?g.hash(""):g.path("")}else d.$broadcast("adal:stateMismatch",o,n)}else if(c(a.config.loginResource),!b.isAuthenticated&&b.userName&&!a._renewActive){var s=i.get("adalAuthenticationService");s.acquireToken(a.config.loginResource).then(function(a){a&&(b.isAuthenticated=!0)},function(a){var b=a.split("|");d.$broadcast("adal:loginFailure",b[0],b[1])})}},q=function(b){b&&a._saveItem(a.CONSTANTS.STORAGE.ANGULAR_LOGIN_REQUEST,b),a.config&&a.config.localLoginUrl?(a.info("Login event for:"+a.config.localLoginUrl),g.path(a.config.localLoginUrl)):(a.info("Start login at:"+e.location.href),d.$broadcast("adal:loginRedirect"),a.login())},r=function(c,d){if(d&&d.$$route)if(j(d.$$route,a.config))b.isAuthenticated||a._renewActive||a.loginInProgress()||(a.info("Route change event for:"+g.$$url),q());else{var e;e="function"==typeof d.$$route.templateUrl?d.$$route.templateUrl(d.params):d.$$route.templateUrl,e&&!k(e)&&a.config.anonymousEndpoints.push(e)}},s=function(c,d,e,f,h){if(d)for(var m=l(d),n=null,o=0;o-1},AuthenticationContext.prototype.getCachedToken=function(a){if(!this._hasResource(a))return null;var b=this._getItem(this.CONSTANTS.STORAGE.ACCESS_TOKEN_KEY+a),c=this._getItem(this.CONSTANTS.STORAGE.EXPIRATION_KEY+a),d=this.config.expireOffsetSeconds||300;return c&&c>this._now()+d?b:(this._saveItem(this.CONSTANTS.STORAGE.ACCESS_TOKEN_KEY+a,""),this._saveItem(this.CONSTANTS.STORAGE.EXPIRATION_KEY+a,0),null)},AuthenticationContext.prototype.getCachedUser=function(){if(this._user)return this._user;var a=this._getItem(this.CONSTANTS.STORAGE.IDTOKEN);return this._user=this._createUser(a),this._user},AuthenticationContext.prototype.registerCallback=function(a,b,c){this._activeRenewals[b]=a,this._callBacksMappedToRenewStates[a]||(this._callBacksMappedToRenewStates[a]=[]);var d=this;this._callBacksMappedToRenewStates[a].push(c),this._callBackMappedToRenewStates[a]||(this._callBackMappedToRenewStates[a]=function(c,e,f,g){d._activeRenewals[b]=null;for(var h=0;h-1)){var b=this._user.profile.upn.split("@");a+="&domain_hint="+encodeURIComponent(b[b.length-1])}return a},AuthenticationContext.prototype._createUser=function(a){var b=null,c=this._extractIdToken(a);return c&&c.hasOwnProperty("aud")&&(c.aud.toLowerCase()===this.config.clientId.toLowerCase()?(b={userName:"",profile:c},c.hasOwnProperty("upn")?b.userName=c.upn:c.hasOwnProperty("email")&&(b.userName=c.email)):this.warn("IdToken has invalid aud field")),b},AuthenticationContext.prototype._getHash=function(a){return a.indexOf("#/")>-1?a=a.substring(a.indexOf("#/")+2):a.indexOf("#")>-1&&(a=a.substring(1)),a},AuthenticationContext.prototype.isCallback=function(a){a=this._getHash(a);var b=this._deserialize(a);return b.hasOwnProperty(this.CONSTANTS.ERROR_DESCRIPTION)||b.hasOwnProperty(this.CONSTANTS.ACCESS_TOKEN)||b.hasOwnProperty(this.CONSTANTS.ID_TOKEN)},AuthenticationContext.prototype.getLoginError=function(){return this._getItem(this.CONSTANTS.STORAGE.LOGIN_ERROR)},AuthenticationContext.prototype.getRequestInfo=function(a){a=this._getHash(a);var b=this._deserialize(a),c={valid:!1,parameters:{},stateMatch:!1,stateResponse:"",requestType:this.REQUEST_TYPE.UNKNOWN};if(b&&(c.parameters=b,b.hasOwnProperty(this.CONSTANTS.ERROR_DESCRIPTION)||b.hasOwnProperty(this.CONSTANTS.ACCESS_TOKEN)||b.hasOwnProperty(this.CONSTANTS.ID_TOKEN))){c.valid=!0;var d="";if(!b.hasOwnProperty("state"))return this.warn("No state returned"),c;if(this.verbose("State: "+b.state),d=b.state,c.stateResponse=d,this._matchState(c))return c;if(!c.stateMatch&&window.parent){c.requestType=this._requestType;for(var e=this._renewStates,f=0;f-1&&b+1-1)return null;if(this.config&&this.config.endpoints)for(var c in this.config.endpoints)if(a.indexOf(c)>-1)return this.config.endpoints[c];return a.indexOf("http://")>-1||a.indexOf("https://")>-1?this._getHostFromUri(a)===this._getHostFromUri(this.config.redirectUri)?this.config.loginResource:null:this.config.loginResource},AuthenticationContext.prototype._getHostFromUri=function(a){var b=String(a).replace(/^(https?:)\/\//,"");return b=b.split("/")[0]},AuthenticationContext.prototype.handleWindowCallback=function(a){if(null==a&&(a=window.location.hash),this.isCallback(a)){var b=null,c=!1;this._openedWindows.length>0&&this._openedWindows[this._openedWindows.length-1].opener&&this._openedWindows[this._openedWindows.length-1].opener._adalInstance?(b=this._openedWindows[this._openedWindows.length-1].opener._adalInstance,c=!0):window.parent&&window.parent._adalInstance&&(b=window.parent._adalInstance);var d,e,f=b.getRequestInfo(a),g=null;e=c||window.parent!==window?b._callBackMappedToRenewStates[f.stateResponse]:b.callback,b.info("Returned from redirect url"),b.saveTokenFromHash(f),f.requestType===this.REQUEST_TYPE.RENEW_TOKEN&&window.parent?(window.parent!==window?b.verbose("Window is in iframe, acquiring token silently"):b.verbose("acquiring token interactive in progress"),d=f.parameters[b.CONSTANTS.ACCESS_TOKEN]||f.parameters[b.CONSTANTS.ID_TOKEN],g=b.CONSTANTS.ACCESS_TOKEN):f.requestType===this.REQUEST_TYPE.LOGIN&&(d=f.parameters[b.CONSTANTS.ID_TOKEN],g=b.CONSTANTS.ID_TOKEN);var h=f.parameters[b.CONSTANTS.ERROR_DESCRIPTION],i=f.parameters[b.CONSTANTS.ERROR];try{e&&e(h,d,i,g)}catch(a){b.error("Error occurred in user defined callback function: "+a)}window.parent!==window||c||(b.config.navigateToLoginRequestUrl?window.location.href=b._getItem(b.CONSTANTS.STORAGE.LOGIN_REQUEST):window.location.hash="")}},AuthenticationContext.prototype._getNavigateUrl=function(a,b){var c="common";this.config.tenant&&(c=this.config.tenant);var d=this.instance+c+"/oauth2/authorize"+this._serialize(a,this.config,b)+this._addLibMetadata();return this.info("Navigate url:"+d),d},AuthenticationContext.prototype._extractIdToken=function(a){var b=this._decodeJwt(a);if(!b)return null;try{var c=b.JWSPayload,d=this._base64DecodeStringUrlSafe(c);return d?JSON.parse(d):(this.info("The returned id_token could not be base64 url safe decoded."),null)}catch(a){this.error("The returned id_token could not be decoded",a)}return null},AuthenticationContext.prototype._base64DecodeStringUrlSafe=function(a){return a=a.replace(/-/g,"+").replace(/_/g,"/"),window.atob?decodeURIComponent(escape(window.atob(a))):decodeURIComponent(escape(this._decode(a)))},AuthenticationContext.prototype._decode=function(a){var b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";a=String(a).replace(/=+$/,"");var c=a.length;if(c%4==1)throw new Error("The token to be decoded is not correctly encoded.");for(var d,e,f,g,h,i,j,k,l="",m=0;m>16&255,j=h>>8&255,l+=String.fromCharCode(i,j);break}if(m+1===c-1){h=d<<18|e<<12,i=h>>16&255,l+=String.fromCharCode(i);break}h=d<<18|e<<12|f<<6|g,i=h>>16&255,j=h>>8&255,k=255&h,l+=String.fromCharCode(i,j,k)}return l},AuthenticationContext.prototype._decodeJwt=function(a){if(this._isEmpty(a))return null;var b=/^([^\.\s]*)\.([^\.\s]+)\.([^\.\s]*)$/,c=b.exec(a);return!c||c.length<4?(this.warn("The returned id_token is not parseable."),null):{header:c[1],JWSPayload:c[2],JWSSig:c[3]}},AuthenticationContext.prototype._convertUrlSafeToRegularBase64EncodedString=function(a){return a.replace("-","+").replace("_","/")},AuthenticationContext.prototype._serialize=function(a,b,c){var d=[];if(null!==b){d.push("?response_type="+a),d.push("client_id="+encodeURIComponent(b.clientId)),c&&d.push("resource="+encodeURIComponent(c)),d.push("redirect_uri="+encodeURIComponent(b.redirectUri)),d.push("state="+encodeURIComponent(b.state)),b.hasOwnProperty("slice")&&d.push("slice="+encodeURIComponent(b.slice)),b.hasOwnProperty("extraQueryParameter")&&d.push(b.extraQueryParameter);var e=b.correlationId?b.correlationId:this._guid();d.push("client-request-id="+encodeURIComponent(e))}return d.join("&")},AuthenticationContext.prototype._deserialize=function(a){var b,c=/\+/g,d=/([^&=]+)=([^&]*)/g,e=function(a){return decodeURIComponent(a.replace(c," "))},f={};for(b=d.exec(a);b;)f[e(b[1])]=e(b[2]),b=d.exec(a);return f},AuthenticationContext.prototype._decimalToHex=function(a){for(var b=a.toString(16);b.length<2;)b="0"+b;return b},AuthenticationContext.prototype._guid=function(){var a=window.crypto||window.msCrypto;if(a&&a.getRandomValues){var b=new Uint8Array(16);return a.getRandomValues(b),b[6]|=64,b[6]&=79,b[8]|=128,b[8]&=191,this._decimalToHex(b[0])+this._decimalToHex(b[1])+this._decimalToHex(b[2])+this._decimalToHex(b[3])+"-"+this._decimalToHex(b[4])+this._decimalToHex(b[5])+"-"+this._decimalToHex(b[6])+this._decimalToHex(b[7])+"-"+this._decimalToHex(b[8])+this._decimalToHex(b[9])+"-"+this._decimalToHex(b[10])+this._decimalToHex(b[11])+this._decimalToHex(b[12])+this._decimalToHex(b[13])+this._decimalToHex(b[14])+this._decimalToHex(b[15])}for(var c="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx",d="0123456789abcdef",e=0,f="",g=0;g<36;g++)"-"!==c[g]&&"4"!==c[g]&&(e=16*Math.random()|0),"x"===c[g]?f+=d[e]:"y"===c[g]?(e&=3,e|=8,f+=d[e]):f+=c[g];return f},AuthenticationContext.prototype._expiresIn=function(a){return a||(a=3599),this._now()+parseInt(a,10)},AuthenticationContext.prototype._now=function(){return Math.round((new Date).getTime()/1e3)},AuthenticationContext.prototype._addAdalFrame=function(a){if(void 0!==a){this.info("Add adal frame to document:"+a);var b=document.getElementById(a);if(!b){if(document.createElement&&document.documentElement&&(window.opera||-1===window.navigator.userAgent.indexOf("MSIE 5.0"))){var c=document.createElement("iframe");c.setAttribute("id",a),c.setAttribute("aria-hidden","true"),c.style.visibility="hidden",c.style.position="absolute",c.style.width=c.style.height=c.borderWidth="0px",b=document.getElementsByTagName("body")[0].appendChild(c)}else document.body&&document.body.insertAdjacentHTML&&document.body.insertAdjacentHTML("beforeEnd",'');window.frames&&window.frames[a]&&(b=window.frames[a])}return b}},AuthenticationContext.prototype._saveItem=function(a,b,c){if(this.config&&this.config.cacheLocation&&"localStorage"===this.config.cacheLocation){if(!this._supportsLocalStorage())return this.info("Local storage is not supported"),!1;if(c){var d=this._getItem(a)||"";localStorage.setItem(a,d+b+this.CONSTANTS.CACHE_DELIMETER)}else localStorage.setItem(a,b);return!0}return this._supportsSessionStorage()?(sessionStorage.setItem(a,b),!0):(this.info("Session storage is not supported"),!1)},AuthenticationContext.prototype._getItem=function(a){return this.config&&this.config.cacheLocation&&"localStorage"===this.config.cacheLocation?this._supportsLocalStorage()?localStorage.getItem(a):(this.info("Local storage is not supported"),null):this._supportsSessionStorage()?sessionStorage.getItem(a):(this.info("Session storage is not supported"),null)},AuthenticationContext.prototype._supportsLocalStorage=function(){try{return!!window.localStorage&&(window.localStorage.setItem("storageTest","A"),"A"==window.localStorage.getItem("storageTest")&&(window.localStorage.removeItem("storageTest"),!window.localStorage.getItem("storageTest")))}catch(a){return!1}},AuthenticationContext.prototype._supportsSessionStorage=function(){try{return!!window.sessionStorage&&(window.sessionStorage.setItem("storageTest","A"),"A"==window.sessionStorage.getItem("storageTest")&&(window.sessionStorage.removeItem("storageTest"),!window.sessionStorage.getItem("storageTest")))}catch(a){return!1}},AuthenticationContext.prototype._cloneConfig=function(a){if(null===a||"object"!=typeof a)return a;var b={};for(var c in a)a.hasOwnProperty(c)&&(b[c]=a[c]);return b},AuthenticationContext.prototype._addLibMetadata=function(){return"&x-client-SKU=Js&x-client-Ver="+this._libVersion()},AuthenticationContext.prototype.log=function(a,b,c,d){if(a<=Logging.level){if(!Logging.piiLoggingEnabled&&d)return;var e=(new Date).toUTCString(),f="";f=this.config.correlationId?e+":"+this.config.correlationId+"-"+this._libVersion()+"-"+this.CONSTANTS.LEVEL_STRING_MAP[a]+" "+b:e+":"+this._libVersion()+"-"+this.CONSTANTS.LEVEL_STRING_MAP[a]+" "+b,c&&(f+="\nstack:\n"+c.stack),Logging.log(f)}},AuthenticationContext.prototype.error=function(a,b){this.log(this.CONSTANTS.LOGGING_LEVEL.ERROR,a,b)},AuthenticationContext.prototype.warn=function(a){this.log(this.CONSTANTS.LOGGING_LEVEL.WARN,a,null)},AuthenticationContext.prototype.info=function(a){ 3 | this.log(this.CONSTANTS.LOGGING_LEVEL.INFO,a,null)},AuthenticationContext.prototype.verbose=function(a){this.log(this.CONSTANTS.LOGGING_LEVEL.VERBOSE,a,null)},AuthenticationContext.prototype.errorPii=function(a,b){this.log(this.CONSTANTS.LOGGING_LEVEL.ERROR,a,b,!0)},AuthenticationContext.prototype.warnPii=function(a){this.log(this.CONSTANTS.LOGGING_LEVEL.WARN,a,null,!0)},AuthenticationContext.prototype.infoPii=function(a){this.log(this.CONSTANTS.LOGGING_LEVEL.INFO,a,null,!0)},AuthenticationContext.prototype.verbosePii=function(a){this.log(this.CONSTANTS.LOGGING_LEVEL.VERBOSE,a,null,!0)},AuthenticationContext.prototype._libVersion=function(){return"1.0.17"},"undefined"!=typeof module&&module.exports&&(module.exports=AuthenticationContext,module.exports.inject=function(a){return new AuthenticationContext(a)}),AuthenticationContext}(); -------------------------------------------------------------------------------- /doc/RequestInfo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Class: RequestInfo 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Class: RequestInfo

21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 |
29 |

30 | RequestInfo 31 |

32 | 33 |
34 | 35 |
36 |
37 | 38 | 39 | 40 | 41 |
42 |

new RequestInfo()

43 | 44 | 45 |
46 |
47 | 48 | 49 |
50 | Request info object created from the response received from AAD. 51 |
52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
62 | 63 | 64 |
Properties:
65 | 66 |
67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 |
NameTypeDescription
parameters 94 | 95 | 96 | object 97 | 98 | 99 | 100 | object comprising of fields such as id_token/error, session_state, state, e.t.c.
requestType 117 | 118 | 119 | REQUEST_TYPE 120 | 121 | 122 | 123 | either LOGIN, RENEW_TOKEN or UNKNOWN.
stateMatch 140 | 141 | 142 | boolean 143 | 144 | 145 | 146 | true if state is valid, false otherwise.
stateResponse 163 | 164 | 165 | string 166 | 167 | 168 | 169 | unique guid used to match the response with the request.
valid 186 | 187 | 188 | boolean 189 | 190 | 191 | 192 | true if requestType contains id_token, access_token or error, false otherwise.
204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 |
Source:
225 |
228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 |
236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 |
250 | 251 | 252 |
253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 |
272 | 273 |
274 | 275 | 276 | 277 | 278 |
279 | 280 | 283 | 284 |
285 | 286 |
287 | Documentation generated by JSDoc 3.2.2 on Fri Oct 20 2017 15:19:15 GMT-0700 (PDT) 288 |
289 | 290 | 291 | 292 | 293 | -------------------------------------------------------------------------------- /doc/User.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Class: User 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Class: User

21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 |
29 |

30 | User 31 |

32 | 33 |
34 | 35 |
36 |
37 | 38 | 39 | 40 | 41 |
42 |

new User()

43 | 44 | 45 |
46 |
47 | 48 | 49 |
50 | User information from idtoken. 51 |
52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
62 | 63 | 64 |
Properties:
65 | 66 |
67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 |
NameTypeDescription
userName 94 | 95 | 96 | string 97 | 98 | 99 | 100 | username assigned from upn or email.
profile 117 | 118 | 119 | object 120 | 121 | 122 | 123 | properties parsed from idtoken.
135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 |
Source:
156 |
159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 |
167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 |
181 | 182 | 183 |
184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 |
203 | 204 |
205 | 206 | 207 | 208 | 209 |
210 | 211 | 214 | 215 |
216 | 217 |
218 | Documentation generated by JSDoc 3.2.2 on Fri Oct 20 2017 15:19:15 GMT-0700 (PDT) 219 |
220 | 221 | 222 | 223 | 224 | -------------------------------------------------------------------------------- /doc/config.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Class: config 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Class: config

21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 |
29 |

30 | config 31 |

32 | 33 |
34 | 35 |
36 |
37 | 38 | 39 | 40 | 41 |
42 |

new config()

43 | 44 | 45 |
46 |
47 | 48 | 49 |
50 | Configuration options for Authentication Context. 51 |
52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
62 | 63 | 64 |
Properties:
65 | 66 |
67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 |
NameTypeDescription
tenant 94 | 95 | 96 | string 97 | 98 | 99 | 100 | Your target tenant.
clientId 117 | 118 | 119 | string 120 | 121 | 122 | 123 | Client ID assigned to your app by Azure Active Directory.
redirectUri 140 | 141 | 142 | string 143 | 144 | 145 | 146 | Endpoint at which you expect to receive tokens.Defaults to `window.location.href`.
instance 163 | 164 | 165 | string 166 | 167 | 168 | 169 | Azure Active Directory Instance.Defaults to `https://login.microsoftonline.com/`.
endpoints 186 | 187 | 188 | Array 189 | 190 | 191 | 192 | Collection of {Endpoint-ResourceId} used for automatically attaching tokens in webApi calls.
popUp 209 | 210 | 211 | Boolean 212 | 213 | 214 | 215 | Set this to true to enable login in a popup winodow instead of a full redirect.Defaults to `false`.
localLoginUrl 232 | 233 | 234 | string 235 | 236 | 237 | 238 | Set this to redirect the user to a custom login page.
displayCall 255 | 256 | 257 | function 258 | 259 | 260 | 261 | User defined function of handling the navigation to Azure AD authorization endpoint in case of login. Defaults to 'null'.
postLogoutRedirectUri 278 | 279 | 280 | string 281 | 282 | 283 | 284 | Redirects the user to postLogoutRedirectUri after logout. Defaults is 'redirectUri'.
cacheLocation 301 | 302 | 303 | string 304 | 305 | 306 | 307 | Sets browser storage to either 'localStorage' or sessionStorage'. Defaults to 'sessionStorage'.
anonymousEndpoints 324 | 325 | 326 | Array.<string> 327 | 328 | 329 | 330 | Array of keywords or URI's. Adal will not attach a token to outgoing requests that have these keywords or uri. Defaults to 'null'.
expireOffsetSeconds 347 | 348 | 349 | number 350 | 351 | 352 | 353 | If the cached token is about to be expired in the expireOffsetSeconds (in seconds), Adal will renew the token instead of using the cached token. Defaults to 300 seconds.
correlationId 370 | 371 | 372 | string 373 | 374 | 375 | 376 | Unique identifier used to map the request with the response. Defaults to RFC4122 version 4 guid (128 bits).
loadFrameTimeout 393 | 394 | 395 | number 396 | 397 | 398 | 399 | The number of milliseconds of inactivity before a token renewal response from AAD should be considered timed out.
411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 |
Source:
432 |
435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 |
443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 |
457 | 458 | 459 |
460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 |
479 | 480 |
481 | 482 | 483 | 484 | 485 |
486 | 487 | 490 | 491 |
492 | 493 |
494 | Documentation generated by JSDoc 3.2.2 on Fri Oct 20 2017 15:19:15 GMT-0700 (PDT) 495 |
496 | 497 | 498 | 499 | 500 | -------------------------------------------------------------------------------- /doc/global.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Global 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Global

21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 |
29 |

30 | 31 |

32 | 33 |
34 | 35 |
36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 |
44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 |
70 | 71 | 72 | 73 | 74 |
75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 |

Type Definitions

92 | 93 |
94 | 95 |
96 |

tokenCallback(error_description, token, error)

97 | 98 | 99 |
100 |
101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 |
Parameters:
110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 |
NameTypeDescription
error_description 138 | 139 | 140 | string 141 | 142 | 143 | 144 | error description returned from AAD if token request fails.
token 161 | 162 | 163 | string 164 | 165 | 166 | 167 | token returned from AAD if token request is successful.
error 184 | 185 | 186 | string 187 | 188 | 189 | 190 | error message returned from AAD if token request fails.
202 | 203 | 204 | 205 |
206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 |
Source:
226 |
229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 |
237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 |
251 | 252 | 253 | 254 |
255 |

userCallback(error, user)

256 | 257 | 258 |
259 |
260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 |
Parameters:
269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 |
NameTypeDescription
error 297 | 298 | 299 | string 300 | 301 | 302 | 303 | error message if user info is not available.
user 320 | 321 | 322 | User 323 | 324 | 325 | 326 | user object retrieved from the cache.
338 | 339 | 340 | 341 |
342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 |
Source:
362 |
365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 |
373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 |
387 | 388 |
389 | 390 | 391 | 392 |
393 | 394 |
395 | 396 | 397 | 398 | 399 |
400 | 401 | 404 | 405 |
406 | 407 |
408 | Documentation generated by JSDoc 3.2.2 on Fri Oct 20 2017 15:19:15 GMT-0700 (PDT) 409 |
410 | 411 | 412 | 413 | 414 | -------------------------------------------------------------------------------- /doc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Index 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Index

21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |

29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
49 | 50 | 53 | 54 |
55 | 56 |
57 | Documentation generated by JSDoc 3.2.2 on Fri Oct 20 2017 15:19:15 GMT-0700 (PDT) 58 |
59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /doc/scripts/linenumber.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var counter = 0; 3 | var numbered; 4 | var source = document.getElementsByClassName('prettyprint source'); 5 | 6 | if (source && source[0]) { 7 | source = source[0].getElementsByTagName('code')[0]; 8 | 9 | numbered = source.innerHTML.split('\n'); 10 | numbered = numbered.map(function(item) { 11 | counter++; 12 | return '' + item; 13 | }); 14 | 15 | source.innerHTML = numbered.join('\n'); 16 | } 17 | })(); 18 | -------------------------------------------------------------------------------- /doc/scripts/prettify/Apache-License-2.0.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /doc/scripts/prettify/lang-css.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", 2 | /^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); 3 | -------------------------------------------------------------------------------- /doc/scripts/prettify/prettify.js: -------------------------------------------------------------------------------- 1 | var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; 2 | (function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= 3 | [],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), 9 | l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, 10 | q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, 11 | q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, 12 | "");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), 13 | a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} 14 | for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], 18 | "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], 19 | H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], 20 | J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ 21 | I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), 22 | ["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", 23 | /^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), 24 | ["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", 25 | hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= 26 | !k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p 0; 42 | var user = _adal.getCachedUser() || { userName: '' }; 43 | _oauthData.userName = user.userName; 44 | _oauthData.profile = user.profile; 45 | _oauthData.loginError = _adal.getLoginError(); 46 | }; 47 | 48 | this.init = function (configOptions, httpProvider) { 49 | if (configOptions) { 50 | configOptions.isAngular = true; 51 | 52 | if (httpProvider && httpProvider.interceptors) { 53 | httpProvider.interceptors.push('ProtectedResourceInterceptor'); 54 | } 55 | 56 | // create instance with given config 57 | _adal = new AuthenticationContext(configOptions); 58 | } else { 59 | throw new Error('You must set configOptions, when calling init'); 60 | } 61 | 62 | // loginResource is used to set authenticated status 63 | updateDataFromCache(_adal.config.loginResource); 64 | }; 65 | 66 | // special function that exposes methods in Angular controller 67 | // $rootScope, $window, $q, $location, $timeout are injected by Angular 68 | this.$get = ['$rootScope', '$window', '$q', '$location', '$timeout', '$injector', function ($rootScope, $window, $q, $location, $timeout, $injector) { 69 | 70 | var locationChangeHandler = function (event, newUrl, oldUrl) { 71 | _adal.verbose('Location change event from ' + oldUrl + ' to ' + newUrl); 72 | if ($location.$$html5) { 73 | var hash = $location.hash(); 74 | } 75 | else { 76 | var hash = '#' + $location.path(); 77 | } 78 | processHash(hash, event); 79 | 80 | $timeout(function () { 81 | updateDataFromCache(_adal.config.loginResource); 82 | $rootScope.userInfo = _oauthData; 83 | }, 1); 84 | }; 85 | 86 | var processHash = function (hash, event) { 87 | 88 | if (_adal.isCallback(hash)) { 89 | var isPopup = false; 90 | 91 | if (_adal._openedWindows.length > 0 && _adal._openedWindows[_adal._openedWindows.length - 1].opener 92 | && _adal._openedWindows[_adal._openedWindows.length - 1].opener._adalInstance) { 93 | _adal = _adal._openedWindows[_adal._openedWindows.length - 1].opener._adalInstance; 94 | isPopup = true; 95 | } 96 | else if ($window.parent && $window.parent._adalInstance) { 97 | _adal = $window.parent._adalInstance; 98 | } 99 | 100 | // callback can come from login or iframe request 101 | _adal.verbose('Processing the hash: ' + hash); 102 | var requestInfo = _adal.getRequestInfo(hash); 103 | _adal.saveTokenFromHash(requestInfo); 104 | // Return to callback if it is sent from iframe 105 | var token = requestInfo.parameters['access_token'] || requestInfo.parameters['id_token']; 106 | var error = requestInfo.parameters['error']; 107 | var errorDescription = requestInfo.parameters['error_description']; 108 | var tokenType = null; 109 | var callback = _adal._callBackMappedToRenewStates[requestInfo.stateResponse] || _adal.callback; 110 | 111 | if (requestInfo.stateMatch) { 112 | if (requestInfo.requestType === _adal.REQUEST_TYPE.RENEW_TOKEN) { 113 | tokenType = _adal.CONSTANTS.ACCESS_TOKEN; 114 | _adal._renewActive = false; 115 | 116 | // Call within the same context without full page redirect keeps the callback 117 | // id_token or access_token can be renewed 118 | if ($window.parent === $window && !_adal._callBackMappedToRenewStates[requestInfo.stateResponse]) { 119 | if (token) { 120 | $rootScope.$broadcast('adal:acquireTokenSuccess', token); 121 | } 122 | else if (error && errorDescription) { 123 | $rootScope.$broadcast('adal:acquireTokenFailure', errorDescription, error); 124 | } 125 | } 126 | 127 | } else if (requestInfo.requestType === _adal.REQUEST_TYPE.LOGIN) { 128 | tokenType = _adal.CONSTANTS.ID_TOKEN; 129 | updateDataFromCache(_adal.config.loginResource); 130 | 131 | if (_oauthData.userName) { 132 | $timeout(function () { 133 | // id_token is added as token for the app 134 | updateDataFromCache(_adal.config.loginResource); 135 | $rootScope.userInfo = _oauthData; 136 | }, 1); 137 | 138 | $rootScope.$broadcast('adal:loginSuccess', token); 139 | } else { 140 | $rootScope.$broadcast('adal:loginFailure', errorDescription, error); 141 | } 142 | 143 | } 144 | 145 | if (callback && typeof callback === 'function') { 146 | callback(errorDescription, token, error, tokenType); 147 | } 148 | 149 | // since this is a token renewal request in iFrame, we don't need to proceed with the location change. 150 | if (window.parent !== window) {//in iframe 151 | if (event && event.preventDefault) { 152 | event.preventDefault(); 153 | } 154 | return; 155 | } 156 | 157 | // redirect to login start page 158 | if ($window.parent === window && !isPopup) { 159 | if (_adal.config.navigateToLoginRequestUrl) { 160 | var loginStartPage = _adal._getItem(_adal.CONSTANTS.STORAGE.LOGIN_REQUEST); 161 | if (typeof loginStartPage !== 'undefined' && loginStartPage && loginStartPage.length !== 0) { 162 | // prevent the current location change and redirect the user back to the login start page 163 | _adal.verbose('Redirecting to start page: ' + loginStartPage); 164 | if (!$location.$$html5 && loginStartPage.indexOf('#') > -1) { 165 | $location.url(loginStartPage.substring(loginStartPage.indexOf('#') + 1)); 166 | } 167 | 168 | $window.location.href = loginStartPage; 169 | } 170 | } 171 | else { 172 | // resetting the hash to null 173 | if ($location.$$html5) { 174 | $location.hash(''); 175 | } 176 | else { 177 | $location.path(''); 178 | } 179 | } 180 | } 181 | } 182 | else { 183 | // state did not match, broadcast an error 184 | $rootScope.$broadcast('adal:stateMismatch', errorDescription, error); 185 | } 186 | } else { 187 | // No callback. App resumes after closing or moving to new page. 188 | // Check token and username 189 | updateDataFromCache(_adal.config.loginResource); 190 | if (!_oauthData.isAuthenticated && _oauthData.userName && !_adal._renewActive) { 191 | // id_token is expired or not present 192 | var self = $injector.get('adalAuthenticationService'); 193 | self.acquireToken(_adal.config.loginResource).then(function (token) { 194 | if (token) { 195 | _oauthData.isAuthenticated = true; 196 | } 197 | }, function (error) { 198 | var errorParts = error.split('|'); 199 | $rootScope.$broadcast('adal:loginFailure', errorParts[0], errorParts[1]); 200 | }); 201 | } 202 | } 203 | 204 | }; 205 | 206 | var loginHandler = function (loginStartPage) { 207 | if (loginStartPage) { 208 | _adal._saveItem(_adal.CONSTANTS.STORAGE.ANGULAR_LOGIN_REQUEST, loginStartPage); 209 | } 210 | 211 | if (_adal.config && _adal.config.localLoginUrl) { 212 | _adal.info('Login event for:' + _adal.config.localLoginUrl); 213 | $location.path(_adal.config.localLoginUrl); 214 | } 215 | else { 216 | // directly start login flow 217 | _adal.info('Start login at:' + $window.location.href); 218 | $rootScope.$broadcast('adal:loginRedirect'); 219 | _adal.login(); 220 | } 221 | }; 222 | 223 | function isADLoginRequired(route, global) { 224 | return global.requireADLogin ? route.requireADLogin !== false : !!route.requireADLogin; 225 | } 226 | 227 | function isAnonymousEndpoint(url) { 228 | if (_adal.config && _adal.config.anonymousEndpoints) { 229 | for (var i = 0; i < _adal.config.anonymousEndpoints.length; i++) { 230 | if (url.indexOf(_adal.config.anonymousEndpoints[i]) > -1) { 231 | return true; 232 | } 233 | } 234 | } 235 | return false; 236 | } 237 | 238 | function getStates(toState) { 239 | var state = null; 240 | var states = []; 241 | if (toState.hasOwnProperty('parent')) { 242 | state = toState; 243 | while (state) { 244 | states.unshift(state); 245 | state = $injector.get('$state').get(state.parent); 246 | } 247 | } 248 | else { 249 | var stateNames = toState.name.split('.'); 250 | for (var i = 0, stateName = stateNames[0]; i < stateNames.length; i++) { 251 | state = $injector.get('$state').get(stateName); 252 | if (state) { 253 | states.push(state); 254 | } 255 | stateName += '.' + stateNames[i + 1]; 256 | } 257 | } 258 | return states; 259 | } 260 | 261 | var routeChangeHandler = function (e, nextRoute) { 262 | if (nextRoute && nextRoute.$$route) { 263 | if (isADLoginRequired(nextRoute.$$route, _adal.config)) { 264 | if (!_oauthData.isAuthenticated) { 265 | if (!_adal._renewActive && !_adal.loginInProgress()) { 266 | _adal.info('Route change event for:' + $location.$$url); 267 | loginHandler(); 268 | } 269 | } 270 | } 271 | else { 272 | var nextRouteUrl; 273 | if (typeof nextRoute.$$route.templateUrl === "function") { 274 | nextRouteUrl = nextRoute.$$route.templateUrl(nextRoute.params); 275 | } else { 276 | nextRouteUrl = nextRoute.$$route.templateUrl; 277 | } 278 | if (nextRouteUrl && !isAnonymousEndpoint(nextRouteUrl)) { 279 | _adal.config.anonymousEndpoints.push(nextRouteUrl); 280 | } 281 | } 282 | } 283 | }; 284 | 285 | var stateChangeHandler = function (e, toState, toParams, fromState, fromParams) { 286 | if (toState) { 287 | var states = getStates(toState); 288 | var state = null; 289 | for (var i = 0; i < states.length; i++) { 290 | state = states[i]; 291 | if (isADLoginRequired(state, _adal.config)) { 292 | if (!_oauthData.isAuthenticated) { 293 | if (!_adal._renewActive && !_adal.loginInProgress()) { 294 | _adal.info('State change event for:' + $location.$$url); 295 | var $state = $injector.get('$state'); 296 | var loginStartPage = $state.href(toState, toParams, { absolute: true }) 297 | loginHandler(loginStartPage); 298 | } 299 | } 300 | } 301 | else if (state.templateUrl) { 302 | var nextStateUrl; 303 | if (typeof state.templateUrl === 'function') { 304 | nextStateUrl = state.templateUrl(toParams); 305 | } 306 | else { 307 | nextStateUrl = state.templateUrl; 308 | } 309 | if (nextStateUrl && !isAnonymousEndpoint(nextStateUrl)) { 310 | _adal.config.anonymousEndpoints.push(nextStateUrl); 311 | } 312 | } 313 | } 314 | } 315 | }; 316 | 317 | var stateChangeErrorHandler = function (event, toState, toParams, fromState, fromParams, error) { 318 | _adal.verbose("State change error occured. Error: " + typeof (error) === 'string' ? error : JSON.stringify(error)); 319 | // adal interceptor sets the error on config.data property. If it is set, it means state change is rejected by adal, 320 | // in which case set the defaultPrevented to true to avoid url update as that sometimesleads to infinte loop. 321 | if (error && error.data) { 322 | _adal.info("Setting defaultPrevented to true if state change error occured because adal rejected a request. Error: " + error.data); 323 | if (event) 324 | event.preventDefault(); 325 | } 326 | }; 327 | 328 | if ($injector.has('$transitions')) { 329 | var $transitions = $injector.get('$transitions'); 330 | 331 | function onStartStateChangeHandler(transition) { 332 | stateChangeHandler(null, transition.to(), transition.params('to'), transition.from(), transition.params('from')); 333 | } 334 | 335 | function onErrorStateChangeHandler(transition) { 336 | stateChangeErrorHandler(null, transition.to(), transition.params('to'), transition.from(), transition.params('from'), transition.error()); 337 | } 338 | 339 | $transitions.onStart({}, onStartStateChangeHandler); 340 | $transitions.onError({}, onErrorStateChangeHandler); 341 | } 342 | 343 | // Route change event tracking to receive fragment and also auto renew tokens 344 | $rootScope.$on('$routeChangeStart', routeChangeHandler); 345 | 346 | $rootScope.$on('$stateChangeStart', stateChangeHandler); 347 | 348 | $rootScope.$on('$locationChangeStart', locationChangeHandler); 349 | 350 | $rootScope.$on('$stateChangeError', stateChangeErrorHandler); 351 | 352 | //Event to track hash change of 353 | $window.addEventListener('adal:popUpHashChanged', function (e) { 354 | processHash(e.detail); 355 | }); 356 | 357 | $window.addEventListener('adal:popUpClosed', function (e) { 358 | var errorParts = e.detail.split('|'); 359 | 360 | if (_adal._loginInProgress) { 361 | $rootScope.$broadcast('adal:loginFailure', errorParts[0], errorParts[1]); 362 | _adal._loginInProgress = false; 363 | } 364 | else if (_adal._acquireTokenInProgress) { 365 | $rootScope.$broadcast('adal:acquireTokenFailure', errorParts[0], errorParts[1]); 366 | _adal._acquireTokenInProgress = false; 367 | } 368 | }); 369 | 370 | updateDataFromCache(_adal.config.loginResource); 371 | $rootScope.userInfo = _oauthData; 372 | 373 | return { 374 | // public methods will be here that are accessible from Controller 375 | config: _adal.config, 376 | login: function () { 377 | _adal.login(); 378 | }, 379 | loginInProgress: function () { 380 | return _adal.loginInProgress(); 381 | }, 382 | logOut: function () { 383 | _adal.logOut(); 384 | //call signout related method 385 | }, 386 | getCachedToken: function (resource) { 387 | return _adal.getCachedToken(resource); 388 | }, 389 | userInfo: _oauthData, 390 | acquireToken: function (resource) { 391 | // automated token request call 392 | var deferred = $q.defer(); 393 | _adal._renewActive = true; 394 | _adal.acquireToken(resource, function (errorDesc, tokenOut, error) { 395 | _adal._renewActive = false; 396 | if (error) { 397 | $rootScope.$broadcast('adal:acquireTokenFailure', errorDesc, error); 398 | _adal.error('Error when acquiring token for resource: ' + resource, error); 399 | deferred.reject(errorDesc + "|" + error); 400 | } else { 401 | $rootScope.$broadcast('adal:acquireTokenSuccess', tokenOut); 402 | deferred.resolve(tokenOut); 403 | } 404 | }); 405 | 406 | return deferred.promise; 407 | }, 408 | 409 | acquireTokenPopup: function (resource, extraQueryParameters, claims) { 410 | var deferred = $q.defer(); 411 | _adal.acquireTokenPopup(resource, extraQueryParameters, claims, function (errorDesc, tokenOut, error) { 412 | if (error) { 413 | $rootScope.$broadcast('adal:acquireTokenFailure', errorDesc, error); 414 | _adal.error('Error when acquiring token for resource: ' + resource, error); 415 | deferred.reject(errorDesc + "|" + error); 416 | } else { 417 | $rootScope.$broadcast('adal:acquireTokenSuccess', tokenOut); 418 | deferred.resolve(tokenOut); 419 | } 420 | }); 421 | 422 | return deferred.promise; 423 | }, 424 | 425 | acquireTokenRedirect: function (resource, extraQueryParameters, claims) { 426 | _adal.acquireTokenRedirect(resource, extraQueryParameters, claims); 427 | }, 428 | 429 | getUser: function () { 430 | var deferred = $q.defer(); 431 | _adal.getUser(function (error, user) { 432 | if (error) { 433 | _adal.error('Error when getting user', error); 434 | deferred.reject(error); 435 | } else { 436 | deferred.resolve(user); 437 | } 438 | }); 439 | 440 | return deferred.promise; 441 | }, 442 | getResourceForEndpoint: function (endpoint) { 443 | return _adal.getResourceForEndpoint(endpoint); 444 | }, 445 | clearCache: function () { 446 | _adal.clearCache(); 447 | }, 448 | clearCacheForResource: function (resource) { 449 | _adal.clearCacheForResource(resource); 450 | }, 451 | info: function (message) { 452 | _adal.info(message); 453 | }, 454 | verbose: function (message) { 455 | _adal.verbose(message); 456 | } 457 | }; 458 | }]; 459 | }); 460 | 461 | // Interceptor for http if needed 462 | AdalModule.factory('ProtectedResourceInterceptor', ['adalAuthenticationService', '$q', '$rootScope', '$templateCache', function (authService, $q, $rootScope, $templateCache) { 463 | 464 | return { 465 | request: function (config) { 466 | if (config) { 467 | 468 | config.headers = config.headers || {}; 469 | 470 | // if the request can be served via templateCache, no need to token 471 | if ($templateCache.get(config.url)) return config; 472 | 473 | var resource = authService.getResourceForEndpoint(config.url); 474 | authService.verbose('Url: ' + config.url + ' maps to resource: ' + resource); 475 | if (resource === null) { 476 | return config; 477 | } 478 | var tokenStored = authService.getCachedToken(resource); 479 | if (tokenStored) { 480 | authService.info('Token is available for this url ' + config.url); 481 | // check endpoint mapping if provided 482 | config.headers.Authorization = 'Bearer ' + tokenStored; 483 | return config; 484 | } 485 | else { 486 | // Cancel request if login is starting 487 | if (authService.loginInProgress()) { 488 | if (authService.config.popUp) { 489 | authService.info('Url: ' + config.url + ' will be loaded after login is successful'); 490 | var delayedRequest = $q.defer(); 491 | $rootScope.$on('adal:loginSuccess', function (event, token) { 492 | if (token) { 493 | authService.info('Login completed, sending request for ' + config.url); 494 | config.headers.Authorization = 'Bearer ' + tokenStored; 495 | delayedRequest.resolve(config); 496 | } 497 | }); 498 | $rootScope.$on('adal:loginFailure', function (event, error) { 499 | if (error) { 500 | config.data = error; 501 | delayedRequest.reject(config); 502 | } 503 | }); 504 | return delayedRequest.promise; 505 | } 506 | else { 507 | authService.info('login is in progress.'); 508 | config.data = 'login in progress, cancelling the request for ' + config.url; 509 | return $q.reject(config); 510 | } 511 | } 512 | else { 513 | // delayed request to return after iframe completes 514 | var delayedRequest = $q.defer(); 515 | authService.acquireToken(resource).then(function (token) { 516 | authService.verbose('Token is available'); 517 | config.headers.Authorization = 'Bearer ' + token; 518 | delayedRequest.resolve(config); 519 | }, function (error) { 520 | config.data = error; 521 | delayedRequest.reject(config); 522 | }); 523 | 524 | return delayedRequest.promise; 525 | } 526 | } 527 | } 528 | }, 529 | responseError: function (rejection) { 530 | authService.info('Getting error in the response: ' + JSON.stringify(rejection)); 531 | if (rejection) { 532 | if (rejection.status === 401) { 533 | var resource = authService.getResourceForEndpoint(rejection.config.url); 534 | authService.clearCacheForResource(resource); 535 | $rootScope.$broadcast('adal:notAuthorized', rejection, resource); 536 | } 537 | else { 538 | $rootScope.$broadcast('adal:errorResponse', rejection); 539 | } 540 | return $q.reject(rejection); 541 | } 542 | } 543 | }; 544 | }]); 545 | } else { 546 | console.error('Angular.JS is not included'); 547 | } 548 | }()); 549 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "adal-angular", 3 | "author": { 4 | "name": "Microsoft", 5 | "email": "nugetaad@microsoft.com", 6 | "url": "https://www.microsoft.com" 7 | }, 8 | "license": "Apache-2.0", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/AzureAD/azure-activedirectory-library-for-js.git" 12 | }, 13 | "version": "1.0.18", 14 | "description": "Windows Azure Active Directory Client Library for js", 15 | "keywords": [ 16 | "implicit", 17 | "js", 18 | "AAD", 19 | "adal", 20 | "oauth" 21 | ], 22 | "main": "./lib/adal.js", 23 | "engines": { 24 | "node": ">=0.8.0" 25 | }, 26 | "dependencies": {}, 27 | "devDependencies": { 28 | "jasmine-node": "~1.14.5", 29 | "jshint": "*", 30 | "grunt": "^0.4.5", 31 | "grunt-cli": "^0.1.13", 32 | "grunt-jsdoc": "~0.5.7", 33 | "grunt-contrib-clean": "~0.5.0", 34 | "grunt-contrib-concat": "^0.3.0", 35 | "grunt-contrib-connect": "^0.7.1", 36 | "grunt-contrib-copy": "~0.4.1", 37 | "grunt-contrib-jshint": "^0.10.0", 38 | "grunt-contrib-uglify": "~0.6.0", 39 | "grunt-contrib-watch": "~0.2.0", 40 | "grunt-karma": "^0.9.x", 41 | "atob": "~1.1.2", 42 | "karma-chrome-launcher": "^0.1.5", 43 | "karma": "^0.12.24", 44 | "karma-jasmine": "^0.1.5", 45 | "bower": "^1.3.3", 46 | "grunt-jasmine-node": "~0.2.1" 47 | }, 48 | "directories": { 49 | "test": "tests" 50 | }, 51 | "scripts": { 52 | "test": "jasmine-node tests/unit/spec" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tests/angularModuleSpec.js: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------- 2 | // Copyright (c) Microsoft Open Technologies, Inc. 3 | // All Rights Reserved 4 | // Apache License 2.0 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | //---------------------------------------------------------------------- 18 | 'use strict' 19 | /* Directive tells jshint that it, describe are globals defined by jasmine */ 20 | /* global it */ 21 | /* global describe */ 22 | 23 | 'use strict'; 24 | 25 | describe('TaskCtl', function () { 26 | var scope, $httpBackend, adalServiceProvider, rootScope, controller, q, window, route, location; 27 | 28 | //mock Application to allow us to inject our own dependencies 29 | beforeEach(angular.mock.module('TestApplication')); 30 | 31 | //mock the controller for the same reason and include $scope and $controller 32 | beforeEach(angular.mock.inject(function (_adalAuthenticationService_, _$rootScope_, _$controller_, _$httpBackend_, _$q_, _$window_, _$route_, _$location_) { 33 | adalServiceProvider = _adalAuthenticationService_; 34 | rootScope = _$rootScope_; 35 | controller = _$controller_; 36 | $httpBackend = _$httpBackend_; 37 | q = _$q_; 38 | window = _$window_; 39 | route = _$route_; 40 | location = _$location_; 41 | 42 | //create an empty scope 43 | scope = rootScope.$new(); 44 | 45 | adalServiceProvider.getCachedToken = function (resource) { 46 | console.log('Requesting token for resource:' + resource); 47 | if (resource === 'resource1') { 48 | return 'Token3434'; 49 | } 50 | 51 | if (resource === 'resource2') { 52 | return 'Token123'; 53 | } 54 | 55 | if (resource === adalServiceProvider.config.loginResource) { 56 | return 'Token456'; 57 | } 58 | 59 | return ''; 60 | }; 61 | 62 | adalServiceProvider.acquireToken = function (resource) { 63 | console.log('acquire token for resource:' + resource); 64 | var token = ''; 65 | if (resource === 'resource1') { 66 | token = 'RenewToken3434'; 67 | } 68 | 69 | if (resource === 'resource2') { 70 | token = 'RenewToken123'; 71 | } 72 | 73 | if (resource === adalServiceProvider.config.loginResource) { 74 | token = 'RenewToken456'; 75 | } 76 | return q.when(token); 77 | }; 78 | 79 | window.parent.AuthenticationContext = window.AuthenticationContext; 80 | window.location.hash = ''; 81 | 82 | // to prevent full page reload error in karma 83 | window.onbeforeunload = function () { return }; 84 | controller('TaskCtl', { $scope: scope, adalAuthenticationService: adalServiceProvider }); 85 | 86 | location.$$html5 = true; 87 | window.event = { 88 | preventDefault: function () { 89 | return; 90 | } 91 | }; 92 | 93 | })); 94 | 95 | it('assigns user', function () { 96 | expect(scope.user.userName).toBe(''); 97 | expect(scope.user.isAuthenticated).toBe(false); 98 | }); 99 | 100 | it('send tokens for webapi call in endpoints list', function () { 101 | $httpBackend.expectGET('/api/Todo/5', function (headers) { 102 | return headers.Authorization === 'Bearer Token3434'; 103 | }).respond(200, { id: 5, name: 'TODOItem1' }); 104 | scope.taskCall(); 105 | $httpBackend.flush(); 106 | 107 | var task = scope.task; 108 | expect(task.name).toBe('TODOItem1'); 109 | }); 110 | 111 | it('send tokens for webapi call in endpoints list', function () { 112 | $httpBackend.expectGET('/anotherApi/Item/13', function (headers) { 113 | console.log('headers test' + headers.Authorization); 114 | return headers.Authorization === 'Bearer Token123'; 115 | }).respond(200, { id: 5, itemName: 'ItemWithoutAuth' }); 116 | scope.itemCall(); 117 | $httpBackend.flush(); 118 | 119 | var task = scope.item; 120 | expect(task.itemName).toBe('ItemWithoutAuth'); 121 | }); 122 | 123 | it('send tokens for webapi call in endpoints list', function () { 124 | $httpBackend.expectGET('https://testapi.com/', function (headers) { 125 | return headers.Authorization === 'Bearer Token3434'; 126 | }).respond(200); 127 | scope.taskCall3(); 128 | $httpBackend.flush(); 129 | }); 130 | 131 | it('does not send tokens for webapi(https) call not in endpoints list', function () { 132 | $httpBackend.expectGET('https://test.com/', function (headers) { 133 | return headers.hasOwnProperty('Authorization') === false; 134 | }).respond(200); 135 | scope.taskCall2(); 136 | $httpBackend.flush(); 137 | }); 138 | 139 | it('does not send tokens for webapi(http) call not in endpoint list', function () { 140 | $httpBackend.expectGET('http://testwebapi.com/', function (headers) { 141 | return headers.hasOwnProperty('Authorization') === false; 142 | }).respond(200); 143 | scope.taskCall6(); 144 | $httpBackend.flush(); 145 | }); 146 | 147 | it('send tokens for app backend call not in endpoints list', function () { 148 | $httpBackend.expectGET('/someapi/item', function (headers) { 149 | return headers.Authorization === 'Bearer Token456' 150 | }).respond(200); 151 | scope.taskCall4(); 152 | $httpBackend.flush(); 153 | }); 154 | 155 | it('send tokens for app backend call', function () { 156 | $httpBackend.expectGET('https://myapp.com/someapi/item', function (headers) { 157 | return headers.Authorization === 'Bearer Token456' 158 | }).respond(200); 159 | scope.taskCall5(); 160 | $httpBackend.flush(); 161 | }); 162 | 163 | it('renews tokens for app backend', function () { 164 | // This makes adal to try renewing the token since no token is returned from cache 165 | adalServiceProvider.getCachedToken = function () { 166 | return ''; 167 | }; 168 | $httpBackend.expectGET('https://myapp.com/someapi/item', function (headers) { 169 | return headers.Authorization === 'Bearer RenewToken456'; 170 | }).respond(200, { id: 5, name: 'TODOItem2' }); 171 | scope.taskCall5(); 172 | $httpBackend.flush(); 173 | 174 | var task = scope.task; 175 | expect(task.name).toBe('TODOItem2'); 176 | }); 177 | 178 | it('renews tokens for webapi in endpoint list', function () { 179 | adalServiceProvider.getCachedToken = function () { 180 | return ''; 181 | }; 182 | $httpBackend.expectGET('/anotherApi/Item/13', function (headers) { 183 | console.log('headers test' + headers.Authorization); 184 | return headers.Authorization === 'Bearer RenewToken123'; 185 | }).respond(200, { id: 5, itemName: 'ItemWithoutAuth' }); 186 | scope.itemCall(); 187 | $httpBackend.flush(); 188 | 189 | var task = scope.item; 190 | expect(task.itemName).toBe('ItemWithoutAuth'); 191 | }); 192 | 193 | it('renews tokens for webapi in endpoint list', function () { 194 | adalServiceProvider.getCachedToken = function () { 195 | return ''; 196 | }; 197 | $httpBackend.expectGET('https://testapi.com/', function (headers) { 198 | return headers.Authorization === 'Bearer RenewToken3434'; 199 | }).respond(200); 200 | scope.taskCall3(); 201 | $httpBackend.flush(); 202 | }); 203 | 204 | it('tests errorResponse broadcast when login is in progress', function () { 205 | adalServiceProvider.getCachedToken = function () { 206 | return ''; 207 | }; 208 | adalServiceProvider.loginInProgress = function () { 209 | return true; 210 | }; 211 | spyOn(rootScope, '$broadcast').andCallThrough(); 212 | $httpBackend.expectGET('https://myapp.com/someapi/item', function (headers) { 213 | return headers.Authorization === 'Bearer Token456' 214 | }).respond(200); 215 | 216 | var eventName = '', msg = ''; 217 | scope.$on('adal:errorResponse', function (event, message) { 218 | eventName = event.name; 219 | msg = message; 220 | }); 221 | scope.taskCall5(); 222 | scope.$apply(); 223 | expect(rootScope.$broadcast).toHaveBeenCalled(); 224 | expect(eventName).toBe('adal:errorResponse'); 225 | expect(msg.data).toBe('login in progress, cancelling the request for https://myapp.com/someapi/item'); 226 | 227 | }); 228 | 229 | it('tests stateMismatch broadcast when state does not match', function () { 230 | console.log(adalServiceProvider); 231 | 232 | location.hash('#id_token=sample&state=4343'); 233 | spyOn(rootScope, '$broadcast').andCallThrough(); 234 | 235 | var eventName = '', msg = ''; 236 | scope.$on('adal:stateMismatch', function (event, message) { 237 | eventName = event.name; 238 | msg = message; 239 | }); 240 | 241 | scope.$apply(); 242 | expect(rootScope.$broadcast).toHaveBeenCalled(); 243 | expect(eventName).toBe('adal:stateMismatch'); 244 | expect(msg).toBe('Invalid_state. state: 4343'); 245 | }); 246 | 247 | it('tests callback is called when response contains error', function () { 248 | var error = '', errorDesc = ''; 249 | var callback = function (valErrorDesc, valToken, valError) { 250 | error = valError; 251 | errorDesc = valErrorDesc; 252 | }; 253 | var adalInstance = window.AuthenticationContext(); 254 | adalInstance._renewStates = ['4343']; 255 | adalInstance._requestType = 'RENEW_TOKEN', 256 | adalInstance._callBackMappedToRenewStates = { "4343": callback } 257 | location.hash('#error=sample&error_description=renewfailed&state=4343'); 258 | scope.$apply(); 259 | expect(error).toBe('sample'); 260 | expect(errorDesc).toBe('renewfailed'); 261 | }); 262 | 263 | it('tests callback is called when response contains access token', function () { 264 | var error = null, errorDesc = null, token = ''; 265 | var callback = function (valErrorDesc, valToken, valError) { 266 | error = valError; 267 | errorDesc = valErrorDesc; 268 | token = valToken; 269 | }; 270 | var adalInstance = window.AuthenticationContext(); 271 | adalInstance._renewStates = ['4343']; 272 | adalInstance._requestType = 'RENEW_TOKEN', 273 | adalInstance._callBackMappedToRenewStates = { "4343": callback } 274 | location.hash('#access_token=newAccessToken123&state=4343'); 275 | scope.$apply(); 276 | expect(error).toBeUndefined(); 277 | expect(errorDesc).toBeUndefined(); 278 | expect(token).toBe('newAccessToken123'); 279 | }); 280 | 281 | it('tests callback is called when response contains id token', function () { 282 | var error = '', errorDesc = '', token = ''; 283 | var callback = function (valErrorDesc, valToken, valError) { 284 | error = valError; 285 | errorDesc = valErrorDesc; 286 | token = valToken; 287 | }; 288 | var adalInstance = window.AuthenticationContext(); 289 | adalInstance._renewStates = ['4343']; 290 | adalInstance._requestType = 'RENEW_TOKEN', 291 | adalInstance._callBackMappedToRenewStates = { "4343": callback } 292 | var createUser = adalInstance._createUser; 293 | adalInstance._createUser = function (idtoken) { 294 | return { 295 | profile: {} 296 | } 297 | } 298 | location.hash('#id_token=newIdToken123&state=4343'); 299 | scope.$apply(); 300 | expect(errorDesc).toBeUndefined(); 301 | expect(error).toBeUndefined(); 302 | expect(token).toBe('newIdToken123'); 303 | adalInstance._createUser = createUser; 304 | }); 305 | 306 | 307 | it('tests login failure after users logs in', function () { 308 | var mockInvalidClientIdToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJjbGllbnQxMjMiLCJuYW1lIjoiSm9obiBEb2UiLCJ1cG4iOiJqb2huQGVtYWlsLmNvbSJ9.zNX4vfLzlbFeKHZ9BMN3sYLtEEE-0o1RoL4NUhXz-l8'; 309 | location.hash('#'+ 'id_token=' + mockInvalidClientIdToken + '&state=1234'); 310 | window.sessionStorage.setItem('adal.state.login', '1234'); 311 | spyOn(rootScope, '$broadcast').andCallThrough(); 312 | var eventName = '', error = '', errorDesc = '', token = ''; 313 | scope.$on('adal:loginFailure', function (event, valErrorDesc, valError) { 314 | eventName = event.name; 315 | errorDesc = valErrorDesc; 316 | error = valError; 317 | }); 318 | window.parent = window; 319 | scope.$apply(); 320 | expect(rootScope.$broadcast).toHaveBeenCalled(); 321 | expect(eventName).toBe('adal:loginFailure'); 322 | expect(errorDesc).toBe('Invalid id_token. id_token: ' + mockInvalidClientIdToken); 323 | expect(error).toBe('invalid id_token'); 324 | window.sessionStorage.setItem('adal.state.login', ''); 325 | }); 326 | 327 | it('tests login success after users logs in', function () { 328 | var mockIdToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJjbGllbnRpZDEyMyIsIm5hbWUiOiJKb2huIERvZSIsInVwbiI6ImpvaG5AZW1haWwuY29tIiwibm9uY2UiOiIxMjM0In0.bpIBG3n1w7Cv3i_JHRGji6Zuc9F5H8jbDV5q3oj0gcw'; 329 | location.hash('#' + 'id_token=' + mockIdToken + '&state=1234'); 330 | window.sessionStorage.setItem('adal.nonce.idtoken', '1234'); 331 | window.sessionStorage.setItem('adal.state.login', '1234'); 332 | spyOn(rootScope, '$broadcast').andCallThrough(); 333 | var eventName = '', token = ''; 334 | scope.$on('adal:loginSuccess', function (event, valToken) { 335 | eventName = event.name; 336 | token = valToken; 337 | }); 338 | scope.$apply(); 339 | expect(rootScope.$broadcast).toHaveBeenCalled(); 340 | expect(eventName).toBe('adal:loginSuccess'); 341 | expect(adalServiceProvider.userInfo.userName).toBe('john@email.com'); 342 | expect(adalServiceProvider.userInfo.profile.upn).toBe('john@email.com'); 343 | expect(adalServiceProvider.userInfo.profile.aud).toBe('clientid123'); 344 | expect(token).toBe(mockIdToken); 345 | adalServiceProvider.logOut(); 346 | }); 347 | 348 | it('tests route change handler', function () { 349 | var homeRoute = route.routes['/home']; 350 | var aboutRoute = route.routes['/about']; 351 | 352 | location.url('/home'); 353 | scope.$apply(); 354 | expect(route.current.controller).toBe(homeRoute.controller); 355 | expect(route.current.template).toBe(homeRoute.template); 356 | 357 | $httpBackend.expectGET('about.html').respond(200); 358 | location.url('/about'); 359 | scope.$apply(); 360 | expect(route.current.controller).toBe(aboutRoute.controller); 361 | expect(route.current.templateUrl).toBe(aboutRoute.templateUrl); 362 | expect(adalServiceProvider.config.anonymousEndpoints).toContain(aboutRoute.templateUrl); 363 | $httpBackend.flush(); 364 | }); 365 | 366 | it('checks if Logging is defined', function () { 367 | Logging.level = 2; 368 | Logging.log = function (message) { 369 | window.logMessage = message; 370 | } 371 | adalServiceProvider.info("test message"); 372 | expect(window.logMessage).toContain("test message"); 373 | expect(Logging.level).toEqual(2); 374 | }); 375 | }); 376 | 377 | describe('StateCtrl', function () { 378 | var $httpBackend, adalServiceProvider, rootScope, $state, location, $templateCache, $stateParams; 379 | 380 | //mock Application to allow us to inject our own dependencies 381 | beforeEach(angular.mock.module('StateApplication')); 382 | 383 | //mock the controller for the same reason and include $scope and $controller 384 | beforeEach(angular.mock.inject(function (_adalAuthenticationService_, _$rootScope_, _$httpBackend_, _$state_, _$location_, _$templateCache_, _$stateParams_) { 385 | adalServiceProvider = _adalAuthenticationService_; 386 | rootScope = _$rootScope_; 387 | $httpBackend = _$httpBackend_; 388 | $state = _$state_; 389 | location = _$location_; 390 | $templateCache = _$templateCache_; 391 | $stateParams = _$stateParams_; 392 | $httpBackend.expectGET('settings.html').respond(200); 393 | $httpBackend.expectGET('profile.html').respond(200); 394 | $httpBackend.expectGET('name.html').respond(200); 395 | $httpBackend.expectGET('account.html').respond(200); 396 | $templateCache.put('profile.html', ''); 397 | $templateCache.put('settings.html', ''); 398 | $templateCache.put('account.html', ''); 399 | $templateCache.put('name.html', ''); 400 | adalServiceProvider.config.anonymousEndpoints = []; 401 | })); 402 | 403 | it('checks if anonymous endpoints are populated on statechange event if states are nested and separated by .', function () { 404 | var state; 405 | rootScope.$on('$stateChangeSuccess', function (event, toState) { 406 | state = toState; 407 | }); 408 | var urlNavigate = 'settings/profile/name'; 409 | location.url(urlNavigate); 410 | rootScope.$digest(); 411 | expect(state.name).toEqual('settings.profile.name'); 412 | var states = urlNavigate.split('/'); 413 | for (var i = 0; i < states.length; i++) { 414 | expect(adalServiceProvider.config.anonymousEndpoints[i]).toEqual(states[i] + '.html'); 415 | } 416 | }); 417 | 418 | it('checks if state is resolved when templateUrl is a function which depends on stateParams and states have parent property', function () { 419 | var state; 420 | rootScope.$on('$stateChangeSuccess', function (event, toState) { 421 | state = toState; 422 | }); 423 | var urlNavigate = 'settings/account/Id/testId/name/Name/testName'; 424 | location.url(urlNavigate); 425 | rootScope.$digest(); 426 | expect($stateParams.accountId).toEqual('testId'); 427 | expect($stateParams.accountName).toEqual('testName'); 428 | expect(state.name).toEqual('settings.account.name'); 429 | var states = state.name.split('.'); 430 | for (var i = 0; i < states.length ; i++) { 431 | expect(adalServiceProvider.config.anonymousEndpoints[i]).toEqual(states[i] + '.html'); 432 | } 433 | }); 434 | }); 435 | 436 | describe('AcquireTokenCtl', function () { 437 | var scope, adalServiceProvider, rootScope, controller, window, $httpBackend, route, location; 438 | var store = {}; 439 | //mock Application to allow us to inject our own dependencies 440 | beforeEach(angular.mock.module('TestApplication')); 441 | 442 | //mock the controller for the same reason and include $scope and $controller 443 | beforeEach(angular.mock.inject(function (_adalAuthenticationService_, _$rootScope_, _$controller_, _$window_, _$httpBackend_, _$route_, _$location_) { 444 | adalServiceProvider = _adalAuthenticationService_; 445 | rootScope = _$rootScope_; 446 | controller = _$controller_; 447 | window = _$window_; 448 | $httpBackend = _$httpBackend_; 449 | route = _$route_; 450 | location = _$location_; 451 | //create an empty scope 452 | scope = rootScope.$new(); 453 | 454 | spyOn(sessionStorage, 'getItem').andCallFake(function (key) { 455 | return store[key]; 456 | }); 457 | spyOn(sessionStorage, 'setItem').andCallFake(function (key, value) { 458 | store[key] = value; 459 | }); 460 | spyOn(sessionStorage, 'removeItem').andCallFake(function (key) { 461 | delete store[key]; 462 | }); 463 | spyOn(window, 'Date').andCallFake(function () { 464 | return { 465 | getTime: function () { 466 | return 1000; 467 | }, 468 | toUTCString: function () { 469 | return ""; 470 | } 471 | }; 472 | }); 473 | })); 474 | 475 | afterEach(function () { 476 | store = {}; 477 | }); 478 | 479 | it('checks if acquireTokenSuccess/acquireTokenFailure events are broadcasted in case of acquireToken', function () { 480 | var error = '', errorDesc = ''; 481 | var tokenOut = ''; 482 | var token = 'token123'; 483 | spyOn(rootScope, '$broadcast').andCallThrough(); 484 | scope.$on('adal:acquireTokenFailure', function (event, valErrorDesc, valError) { 485 | errorDesc = valErrorDesc; 486 | error = valError; 487 | }); 488 | adalServiceProvider.acquireToken(adalServiceProvider.config.loginResource); 489 | expect(errorDesc).toBe('User login is required'); 490 | expect(error).toBe('login required'); 491 | store = { 492 | 'adal.token.keys': adalServiceProvider.config.loginResource + '|', 493 | 'adal.access.token.keyloginResource123': token, 494 | 'adal.expiration.keyloginResource123': 302 495 | }; 496 | scope.$on('adal:acquireTokenSuccess', function (event, message) { 497 | tokenOut = message; 498 | }); 499 | adalServiceProvider.acquireToken(adalServiceProvider.config.loginResource); 500 | expect(tokenOut).toBe(token); 501 | }); 502 | 503 | 504 | it('checks if user is redirected to the custom Login Page when localLoginUrl is specified', function () { 505 | spyOn(rootScope, '$broadcast').andCallThrough(); 506 | 507 | adalServiceProvider.config.localLoginUrl = '/login'; 508 | $httpBackend.expectGET('login.html').respond(200); 509 | var loginRoute = route.routes['/login']; 510 | location.url('/todoList'); 511 | scope.$apply(); 512 | expect(route.current.controller).toBe(loginRoute.controller); 513 | expect(route.current.templateUrl).toBe(loginRoute.templateUrl); 514 | expect(adalServiceProvider.loginInProgress()).toBe(false); 515 | adalServiceProvider.config.localLoginUrl = null; 516 | }); 517 | 518 | it('checks if loginRedirect event is fired when localLoginUrl is not specified', function () { 519 | spyOn(rootScope, '$broadcast').andCallThrough(); 520 | 521 | adalServiceProvider.config.localLoginUrl = null; 522 | location.url('/todoList'); 523 | var eventName = ''; 524 | scope.$on('adal:loginRedirect', function (event) { 525 | eventName = event.name; 526 | }); 527 | scope.$apply(); 528 | expect(adalServiceProvider.loginInProgress()).toBe(true); 529 | expect(rootScope.$broadcast).toHaveBeenCalled(); 530 | expect(eventName).toBe('adal:loginRedirect'); 531 | }); 532 | 533 | it('tests auto id token renew when id token expires', function () { 534 | spyOn(rootScope, '$broadcast').andCallThrough(); 535 | 536 | var loginResourceOldValue = adalServiceProvider.config.loginResource; 537 | adalServiceProvider.config.loginResource = null; 538 | window.location.hash = 'hash'; 539 | var eventName = '', error = '', errorDesc = '', token = ''; 540 | scope.$on('adal:loginFailure', function (event, valErrorDesc, valError) { 541 | eventName = event.name; 542 | errorDesc = valErrorDesc; 543 | error = valError; 544 | }); 545 | 546 | store = { 547 | 'adal.idtoken': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJjbGllbnRpZDEyMyIsIm5hbWUiOiJKb2huIERvZSIsInVwbiI6ImpvaG5AZW1haWwuY29tIiwibm9uY2UiOm51bGx9.DLCO6yIWhnNBYfHH8qFPswcH4M2Alpjn6AZy7K6HENY' 548 | } 549 | scope.$apply(); 550 | 551 | adalServiceProvider.config.loginResource = loginResourceOldValue; 552 | expect(rootScope.$broadcast).toHaveBeenCalled(); 553 | expect(eventName).toBe('adal:loginFailure'); 554 | expect(errorDesc).toBe('resource is required'); 555 | expect(error).toBe('resource is required'); 556 | adalServiceProvider.logOut(); 557 | }); 558 | }); 559 | -------------------------------------------------------------------------------- /tests/browser.manual.runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Test Suite 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 30 | 31 | -------------------------------------------------------------------------------- /tests/stateApp.js: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------- 2 | // @preserve Copyright (c) Microsoft Open Technologies, Inc. 3 | // All Rights Reserved 4 | // Apache License 2.0 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | //---------------------------------------------------------------------- 18 | 19 | 'use strict'; 20 | // Test app Ui-Router 21 | var stateApp = angular.module("StateApplication", ['ui.router', 'AdalAngular']) 22 | .config(['$stateProvider', '$urlRouterProvider', '$httpProvider', 'adalAuthenticationServiceProvider', function ($stateProvider, $urlRouterProvider, $httpProvider, adalProvider) { 23 | $stateProvider 24 | .state('settings', { 25 | url: '/settings', 26 | templateUrl: 'settings.html' 27 | }) 28 | .state('settings.profile', { 29 | url: '/profile', 30 | templateUrl: 'profile.html', 31 | }) 32 | .state('settings.profile.name', { 33 | url: '/name', 34 | templateUrl: 'name.html', 35 | }) 36 | .state('settings.profile.email', { 37 | url: '/email', 38 | templateUrl: 'email.html', 39 | }) 40 | .state('settings.account', { 41 | parent: 'settings', 42 | url: '/account/Id/:accountId', 43 | templateUrl: function (stateParams) { 44 | if (stateParams.accountId == 'testId') 45 | return 'account.html'; 46 | }, 47 | }) 48 | .state('settings.account.name', { 49 | parent: 'settings.account', 50 | url: '/name/Name/:accountName', 51 | templateUrl: function (stateParams) { 52 | if (stateParams.accountName == 'testName') 53 | return 'name.html'; 54 | } 55 | }) 56 | .state('settings.account.email', { 57 | url: '/email', 58 | templateUrl: 'email.html', 59 | }); 60 | 61 | $urlRouterProvider.otherwise('/settings'); 62 | var endpoints = {}; 63 | 64 | adalProvider.init( 65 | { 66 | tenant: 'tenantid123', 67 | clientId: 'clientid123', 68 | loginResource: 'loginResource123', 69 | redirectUri: 'https://myapp.com/page', 70 | endpoints: endpoints // optional 71 | }, 72 | $httpProvider // pass http provider to inject request interceptor to attach tokens 73 | ); 74 | }]); 75 | 76 | app.controller('StateCtrl', ['$scope', '$location', 'adalAuthenticationService', function ($scope, $location, adalAuthenticationService) { }]); 77 | -------------------------------------------------------------------------------- /tests/testApp.js: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------- 2 | // @preserve Copyright (c) Microsoft Open Technologies, Inc. 3 | // All Rights Reserved 4 | // Apache License 2.0 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | //---------------------------------------------------------------------- 18 | 19 | 'use strict'; 20 | // Test app 21 | var app = angular.module('TestApplication', ['ngResource', 'ngRoute', 'AdalAngular']); 22 | 23 | app.config(['$httpProvider', '$routeProvider', 'adalAuthenticationServiceProvider', function ($httpProvider, $routeProvider, adalAuthenticationServiceProvider) { 24 | 25 | $routeProvider. 26 | when('/home', { 27 | controller: 'homeController', 28 | template: '
home
' 29 | }). 30 | when('/about', { 31 | controller: 'aboutController', 32 | templateUrl: 'about.html' 33 | }). 34 | when('/todoList', { 35 | controller: 'todoListController', 36 | template: '
todoList
', 37 | requireADLogin: true 38 | }). 39 | when('/login', { 40 | controller: 'loginController', 41 | templateUrl: 'login.html', 42 | }). 43 | otherwise({ redirectTo: '/home' }); 44 | 45 | var endpoints = { 46 | '/api/Todo/': 'resource1', 47 | '/anotherApi/Item/': 'resource2', 48 | 'https://testapi.com/': 'resource1' 49 | }; 50 | 51 | adalAuthenticationServiceProvider.init( 52 | { 53 | tenant: 'tenantid123', 54 | clientId: 'clientid123', 55 | loginResource: 'loginResource123', 56 | redirectUri: 'https://myapp.com/page', 57 | endpoints: endpoints, // optional 58 | }, 59 | $httpProvider // pass http provider to inject request interceptor to attach tokens 60 | ); 61 | }]); 62 | 63 | app.factory('ItemFactory', ['$http', function ($http) { 64 | var serviceFactory = {}; 65 | var _getItem = function (id) { 66 | return $http.get('/anotherApi/Item/' + id); 67 | }; 68 | 69 | serviceFactory.getItem = _getItem; 70 | return serviceFactory; 71 | }]); 72 | 73 | app.factory('TaskFactory', ['$http', function ($http) { 74 | var serviceFactory = {}; 75 | var _getItem = function (id) { 76 | return $http.get('/api/Todo/' + id); 77 | }; 78 | 79 | var _getItem2 = function (url) { 80 | return $http.get(url); 81 | }; 82 | serviceFactory.getItem = _getItem; 83 | serviceFactory.getItem2 = _getItem2; 84 | return serviceFactory; 85 | }]); 86 | 87 | app.controller('TaskCtl', ['$scope', '$location', 'adalAuthenticationService', 'TaskFactory', 'ItemFactory', function ($scope, $location, adalAuthenticationService, TaskFactory, ItemFactory) { 88 | 89 | $scope.taskCall = function () { 90 | TaskFactory.getItem(5).then(function (response) { 91 | $scope.task = response.data; 92 | }, function (err) { 93 | $scope.error = err; 94 | $scope.loadingMsg = ""; 95 | }); 96 | } 97 | 98 | $scope.itemCall = function () { 99 | ItemFactory.getItem(13).then(function (response) { 100 | $scope.item = response.data; 101 | }, function (err) { 102 | $scope.error = err; 103 | $scope.loadingMsg = ""; 104 | }); 105 | } 106 | 107 | $scope.taskCall2 = function () { 108 | TaskFactory.getItem2('https://test.com/').then(function (response) { 109 | $scope.task = response.data; 110 | }, function (err) { 111 | $scope.error = err; 112 | $scope.loadingMsg = ""; 113 | }); 114 | } 115 | 116 | $scope.taskCall3 = function () { 117 | TaskFactory.getItem2('https://testapi.com/').then(function (response) { 118 | $scope.task = response.data; 119 | }, function (err) { 120 | $scope.error = err; 121 | $scope.loadingMsg = ""; 122 | }); 123 | } 124 | 125 | $scope.taskCall4 = function () { 126 | TaskFactory.getItem2('/someapi/item').then(function (response) { 127 | $scope.task = response.data; 128 | }, function (err) { 129 | $scope.error = err; 130 | $scope.loadingMsg = ""; 131 | }); 132 | } 133 | 134 | $scope.taskCall5 = function () { 135 | TaskFactory.getItem2('https://myapp.com/someapi/item').then(function (response) { 136 | $scope.task = response.data; 137 | }, function (err) { 138 | $scope.error = err; 139 | $scope.loadingMsg = ""; 140 | }); 141 | } 142 | 143 | $scope.taskCall6 = function () { 144 | TaskFactory.getItem2('http://testwebapi.com/').then(function (response) { 145 | $scope.task = response.data; 146 | }, function (err) { 147 | $scope.error = err; 148 | $scope.loaingMsg = ""; 149 | }); 150 | } 151 | 152 | $scope.user = adalAuthenticationService.userInfo; 153 | }]); 154 | --------------------------------------------------------------------------------