├── .gitignore ├── .vscode └── settings.json ├── Readme.md ├── app ├── .DS_Store ├── app.component.ts ├── app.module.ts ├── app.routing.ts ├── authSettings.config.ts ├── home │ └── home.component.ts ├── login │ └── login.component.ts ├── main.ts ├── ngAuth │ ├── .DS_Store │ ├── AuthenticatedHttpService.ts │ ├── JwtHelper.ts │ ├── authenticators │ │ ├── .DS_Store │ │ ├── AzureADAuthService.ts │ │ └── AzureADServiceConstants.ts │ └── renewToken.html └── status │ └── status.component.ts ├── bs-config.json ├── index.html ├── package.json ├── systemjs.config.js ├── tsconfig.json ├── tslint.json └── typings.json /.gitignore: -------------------------------------------------------------------------------- 1 | **/*.js.map 2 | **/*.js 3 | node_modules 4 | typings 5 | !systemjs.config.js 6 | npm-debug*.* -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "**/.git": true, 4 | "**/.svn": true, 5 | "**/.hg": true, 6 | "**/.DS_Store": true, 7 | "**/*.js":true, 8 | "**/*.js.map":true 9 | } 10 | } -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | ## Angular Azure AD Auth Lib (not the official ADAL, but similar functionality) 2 | *This is an unofficial, not written by Microsoft, but open source authentication library for Angular 2 that lets you authenticate with AzureAD, and tap into Office 365 APIs or other AzureAD protected APIs* 3 | *The code has been written so this could be easily enhanced to support other auth providers (such as ADFS3/Google etc.)* 4 | 5 | To use, 6 | 1. Register a native client app in Azure AD and enable OAuth2 implicit flow. 7 | 2. Pass in the constants in app.component.ts (intentionally sent from app since the library shouldn't have these hardcoded in them). 8 | 3. Run npm install 9 | 4. Run npm start 10 | 11 | When the app launches, it shows the standard sign in link 12 | 13 | Go ahead and sign in. 14 | 15 | At this point, you have an access token, which you can see in the local storage of the browser. 16 | Also, the library adds a hidden IFrame to renew the access token 5 mins before it expires. This is automatic, you don't have to worry about it. 17 | 18 | Note: Since this is a multi-tenant app, and it is (as of now) registered in my tenancy, just changing the tenancy will also also ask for consent to your tenancy (so you can distribute this app via appstores etc.) 19 | 20 | Now you can click on the "Get Files" button, and it will automatically add the access token in the header of your request, and the request should succeed. 21 | In order to do so, use the "AuthenticatedHttpService" which is also part of this library. 22 | 23 | If you click on Get Files without signing in, or for some reason the access token has expired (library renews it so it should never happen), it will prompt the user to sign in. 24 | 25 | And the running app makes a simple graph capp -- (yeah I know basic, and my UX sucks, but shows the concept, doesn't it?) :-) 26 | 27 | 28 | Credits: 29 | This app uses the following component: 30 | 1. Angular www.angular.io 31 | 2. Typescript typescriptlang.org 32 | 3. JWT token decoder from auth0 33 | -------------------------------------------------------------------------------- /app/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maliksahil/AngularAzureADAuthLib/c20e01a010509e84cb6c4f0a0a3306f8bdfd20b3/app/.DS_Store -------------------------------------------------------------------------------- /app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | @Component({ 3 | selector: 'app', 4 | template: ` 5 | About | Login | Status
6 | ` 7 | }) 8 | 9 | export class AppComponent { } -------------------------------------------------------------------------------- /app/app.module.ts: -------------------------------------------------------------------------------- 1 | // #docregion 2 | import { NgModule } from '@angular/core'; 3 | import { HttpModule } from '@angular/http'; 4 | import { BrowserModule } from '@angular/platform-browser'; 5 | import { routing } from './app.routing'; 6 | import { AppComponent } from './app.component'; 7 | 8 | import { HomeComponent } from './home/home.component'; 9 | import { LoginComponent } from './login/login.component'; 10 | import { StatusComponent } from './status/status.component'; 11 | 12 | import { AzureADServiceConstants } from './ngAuth/authenticators/AzureADServiceConstants'; 13 | import { AzureADAuthService } from './ngAuth/authenticators/AzureADAuthService'; 14 | import { AuthenticatedHttpService } from './ngAuth/AuthenticatedHttpService'; 15 | 16 | import { serviceConstants } from './authsettings.config'; 17 | 18 | let authenticator = new AzureADAuthService(serviceConstants); 19 | 20 | @NgModule({ 21 | providers: [ 22 | AuthenticatedHttpService, 23 | { 24 | provide: AzureADAuthService, 25 | useValue: authenticator 26 | }], 27 | imports: [ 28 | routing, 29 | BrowserModule, 30 | HttpModule 31 | ], 32 | declarations: [ 33 | AppComponent, 34 | HomeComponent, 35 | LoginComponent, 36 | StatusComponent 37 | ], 38 | bootstrap: [AppComponent] 39 | }) 40 | export class AppModule { } -------------------------------------------------------------------------------- /app/app.routing.ts: -------------------------------------------------------------------------------- 1 | // #docregion 2 | import { ModuleWithProviders } from '@angular/core'; 3 | import { Routes, RouterModule } from '@angular/router'; 4 | import { LoginComponent } from './login/login.component'; 5 | import {HomeComponent} from './home/home.component'; 6 | import { StatusComponent } from './status/status.component'; 7 | 8 | export const routes: Routes = [ 9 | { path: '', component: HomeComponent }, 10 | { path: 'login', component: LoginComponent }, 11 | { path: 'status', component: StatusComponent }, 12 | ]; 13 | export const routing: ModuleWithProviders = RouterModule.forRoot(routes); -------------------------------------------------------------------------------- /app/authSettings.config.ts: -------------------------------------------------------------------------------- 1 | import {AzureADServiceConstants} from './ngAuth/authenticators/AzureADServiceConstants'; 2 | 3 | export const serviceConstants: AzureADServiceConstants = new AzureADServiceConstants( 4 | "1c623fa4-c6c8-4903-a6aa-67c5ba9a1535", 5 | "winsmartsdev.onmicrosoft.com", 6 | "http://localhost:3000/status", 7 | "https://graph.windows.net" 8 | ); -------------------------------------------------------------------------------- /app/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Inject, Injectable } from '@angular/core'; 3 | 4 | @Component({ 5 | template:` 6 | Simple app demonstrates logging into AzureAD and running a command against the Azure AD graph.
7 | Click the login tab to login, and status tab to view your login status. 8 | ` 9 | }) 10 | export class HomeComponent { } -------------------------------------------------------------------------------- /app/login/login.component.ts: -------------------------------------------------------------------------------- 1 | // #docregion 2 | import { Component, Inject } from '@angular/core'; 3 | import { Router } from '@angular/router'; 4 | 5 | import {AzureADAuthService} from '../ngAuth/authenticators/AzureADAuthService'; 6 | 7 | @Component({ 8 | template: ` 9 | ` 12 | }) 13 | 14 | export class LoginComponent { 15 | constructor( 16 | @Inject(AzureADAuthService) private _authService: AzureADAuthService, 17 | private _router: Router) { } 18 | 19 | logIn() { 20 | this._authService.logIn("/"); 21 | } 22 | } -------------------------------------------------------------------------------- /app/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | import {AppModule} from './app.module'; 3 | platformBrowserDynamic().bootstrapModule(AppModule); -------------------------------------------------------------------------------- /app/ngAuth/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maliksahil/AngularAzureADAuthLib/c20e01a010509e84cb6c4f0a0a3306f8bdfd20b3/app/ngAuth/.DS_Store -------------------------------------------------------------------------------- /app/ngAuth/AuthenticatedHttpService.ts: -------------------------------------------------------------------------------- 1 | // #docregion 2 | import { Injectable, Inject } from '@angular/core'; 3 | import { Http, Headers, Response } from '@angular/http'; 4 | import { AzureADAuthService } from './authenticators/AzureADAuthService'; 5 | import { Observable, Subscriber } from 'rxjs'; 6 | 7 | @Injectable() 8 | export class AuthenticatedHttpService { 9 | private _authenticator: AzureADAuthService; 10 | private _http: Http; 11 | constructor( @Inject(Http) http: Http, @Inject(AzureADAuthService) authenticator: AzureADAuthService) { 12 | this._authenticator = authenticator; 13 | this._http = http; 14 | } 15 | 16 | createAuthorizationHeader(headers: Headers) { 17 | headers.append('Authorization', 'Bearer ' + this._authenticator.getAccessToken()); 18 | } 19 | 20 | get(url: string) { 21 | let headers = new Headers(); 22 | this.createAuthorizationHeader(headers); 23 | return this._http.get(url, { headers: headers }); 24 | } 25 | 26 | post(url: string, data: any) { 27 | let headers = new Headers(); 28 | this.createAuthorizationHeader(headers); 29 | return this._http.post(url, data, { 30 | headers: headers, 31 | }); 32 | } 33 | } -------------------------------------------------------------------------------- /app/ngAuth/JwtHelper.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable() 4 | export class JwtHelper { 5 | private urlBase64Decode(str: string) { 6 | var output = str.replace(/-/g, '+').replace(/_/g, '/'); 7 | switch (output.length % 4) { 8 | case 0: { break; } 9 | case 2: { output += '=='; break; } 10 | case 3: { output += '='; break; } 11 | default: { 12 | throw 'Illegal base64url string!'; 13 | } 14 | } 15 | return decodeURIComponent((window).escape(window.atob(output))); 16 | } 17 | 18 | public decodeToken(token: string = "") { 19 | if (token === null || token === "") return {"upn": ""}; 20 | var parts = token.split('.'); 21 | if (parts.length !== 3) { 22 | throw new Error('JWT must have 3 parts'); 23 | } 24 | var decoded = this.urlBase64Decode(parts[1]); 25 | if (!decoded) { 26 | throw new Error('Cannot decode the token'); 27 | } 28 | return JSON.parse(decoded); 29 | } 30 | } -------------------------------------------------------------------------------- /app/ngAuth/authenticators/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maliksahil/AngularAzureADAuthLib/c20e01a010509e84cb6c4f0a0a3306f8bdfd20b3/app/ngAuth/authenticators/.DS_Store -------------------------------------------------------------------------------- /app/ngAuth/authenticators/AzureADAuthService.ts: -------------------------------------------------------------------------------- 1 | // #docregion 2 | import { Injectable } from '@angular/core'; 3 | import { Http, Headers } from '@angular/http'; 4 | import { JwtHelper } from '../JwtHelper'; 5 | 6 | import { AzureADServiceConstants } from "./AzureADServiceConstants"; 7 | 8 | @Injectable() 9 | export class AzureADAuthService { 10 | public isUserAuthenticated(): boolean { 11 | let access_token = this.getAccessToken(); 12 | return access_token != null; 13 | } 14 | 15 | public getAccessToken(): string { 16 | return window.localStorage.getItem("access_token"); 17 | } 18 | 19 | public getUserName(): string { 20 | var jwtHelper = new JwtHelper(); 21 | var parsedToken = jwtHelper.decodeToken(this.getAccessToken()); 22 | 23 | var expiryTime = new Date(parsedToken.exp * 1000); 24 | var now = new Date(); 25 | if (now > expiryTime) this.logOut(); 26 | 27 | return parsedToken.upn; 28 | } 29 | 30 | // #docregion login 31 | public logIn(state = "/") { 32 | window.location.href = "https://login.microsoftonline.com/" + this._serviceConstants.tenantID + 33 | "/oauth2/authorize?response_type=id_token&client_id=" + this._serviceConstants.clientID + 34 | "&redirect_uri=" + encodeURIComponent(window.location.href) + 35 | "&state=" + state + "&nonce=SomeNonce"; 36 | } 37 | // #enddocregion login 38 | 39 | public logOut(state = "/") { 40 | window.localStorage.removeItem("id_token"); 41 | window.localStorage.removeItem("access_token"); 42 | window.location.href = state; 43 | } 44 | private parseQueryString = function (url: string) { 45 | var params = {}; 46 | var queryString = ""; 47 | if (url.search("#") != -1) { 48 | queryString = url.substring(url.search("#") + 1); 49 | 50 | } else { 51 | queryString = url.substring(url.indexOf("?") + 1); 52 | } 53 | var a = queryString.split('&'); 54 | for (var i = 0; i < a.length; i++) { 55 | var b = a[i].split('='); 56 | params[decodeURIComponent(b[0])] = decodeURIComponent(b[1] || ''); 57 | } 58 | return params; 59 | } 60 | 61 | private params = this.parseQueryString(location.hash); 62 | // #docregion ctor 63 | constructor(private _serviceConstants: AzureADServiceConstants) { 64 | // do we have an access token, if so add the iframe renewer 65 | if (window.localStorage.getItem("access_token")) { 66 | var iframe = document.createElement('iframe'); 67 | iframe.style.display = "none"; 68 | iframe.src = "/app/ngAuth/renewToken.html?tenantID=" + 69 | encodeURIComponent(this._serviceConstants.tenantID) + 70 | "&clientID=" + encodeURIComponent(this._serviceConstants.clientID) + 71 | "&resource=" + encodeURIComponent(this._serviceConstants.graphResource); 72 | window.onload = function () { 73 | document.body.appendChild(iframe); 74 | } 75 | } 76 | if (this.params["id_token"] != null) { 77 | window.localStorage.setItem("id_token", this.params["id_token"]); 78 | // redirect to get access token here.. 79 | window.location.href = "https://login.microsoftonline.com/" + this._serviceConstants.tenantID + 80 | "/oauth2/authorize?response_type=token&client_id=" + this._serviceConstants.clientID + 81 | "&resource=" + this._serviceConstants.graphResource + 82 | "&redirect_uri=" + encodeURIComponent(window.location.href) + 83 | "&prompt=none&state=" + this.params["state"] + "&nonce=SomeNonce"; 84 | } 85 | else if (this.params["access_token"] != null) { 86 | window.localStorage.setItem("access_token", this.params["access_token"]); 87 | // redirect to the original call URl here. 88 | window.location.href = this.params["state"]; 89 | } 90 | } 91 | // #enddocregion ctor 92 | } 93 | 94 | function error(err: any) { 95 | console.error(JSON.stringify(err, null, 4)); 96 | } 97 | 98 | // #enddocregion -------------------------------------------------------------------------------- /app/ngAuth/authenticators/AzureADServiceConstants.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable() 4 | export class AzureADServiceConstants { 5 | constructor( 6 | public clientID:string, 7 | public tenantID:string, 8 | public redirectURL:string, 9 | public backendUrl:string, 10 | public graphResource = "https://graph.windows.net", 11 | public isCordova = false, 12 | public isElectron = false) {} 13 | } -------------------------------------------------------------------------------- /app/ngAuth/renewToken.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Renew Token 5 | 9 | 10 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /app/status/status.component.ts: -------------------------------------------------------------------------------- 1 | // #docregion 2 | import { Component, Inject } from '@angular/core'; 3 | import { Router } from '@angular/router'; 4 | 5 | import { AuthenticatedHttpService } from '../ngAuth/AuthenticatedHttpService'; 6 | import { AzureADAuthService } from '../ngAuth/authenticators/AzureADAuthService'; 7 | 8 | 9 | @Component({ 10 | template: ` 11 |
12 | userName: {{this._authService.getUserName()}} 13 |
14 | 15 |
16 | 19 |
{{_userData | json}}
20 |
21 |
22 | User is not signed in. 23 |
24 | ` 25 | }) 26 | 27 | export class StatusComponent { 28 | private _userData: Object = {"intialValue":"Data will show here once you press RunCommand"}; 29 | constructor( @Inject(AzureADAuthService) private _authService: AzureADAuthService, private _authenticatedHttpService: AuthenticatedHttpService) { } 30 | 31 | logOut() { 32 | this._authService.logOut("/"); 33 | } 34 | 35 | runCommand() { 36 | this._authenticatedHttpService.get("https://graph.windows.net/me?api-version=1.6").subscribe((results => { 37 | this._userData = results.json(); 38 | })); 39 | // this._authenticatedHttpService.get("https://graph.microsoft.com/v1.0/me/drive/recent").subscribe((results => { 40 | // this._files = results.json().value; 41 | // })); 42 | } 43 | } -------------------------------------------------------------------------------- /bs-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "port": 3000, 3 | "server": { 4 | "baseDir": "." 5 | } 6 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Angular 2 AzureAD/Office365 Auth 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 21 | Loading... 22 | 23 | 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ng2adal-active-directory-auth-library", 3 | "version": "1.0.0", 4 | "description": "A library and sample app that lets you auth with AzureAD and Office365 with Angular 2", 5 | "scripts": { 6 | "start": "tsc && concurrently \"npm run tsc:w\" \"npm run lite\" ", 7 | "lite": "lite-server", 8 | "postinstall": "typings install", 9 | "tsc": "tsc", 10 | "tsc:w": "tsc -w", 11 | "typings": "typings" 12 | }, 13 | "keywords": [], 14 | "author": "sahilmalik@winsmarts.com", 15 | "license": "ISC", 16 | "dependencies": { 17 | "@angular/common": "2.0.0", 18 | "@angular/compiler": "2.0.0", 19 | "@angular/core": "2.0.0", 20 | "@angular/forms": "2.0.0", 21 | "@angular/http": "2.0.0", 22 | "@angular/platform-browser": "2.0.0", 23 | "@angular/platform-browser-dynamic": "2.0.0", 24 | "@angular/router": "3.0.0", 25 | "@angular/upgrade": "2.0.0", 26 | "angular2-in-memory-web-api": "0.0.20", 27 | "systemjs": "0.19.27", 28 | "es6-shim": "0.35.1", 29 | "core-js": "^2.4.1", 30 | "reflect-metadata": "^0.1.3", 31 | "rxjs": "5.0.0-beta.12", 32 | "zone.js": "^0.6.17" 33 | }, 34 | "devDependencies": { 35 | "concurrently": "^2.2.0", 36 | "lite-server": "^2.2.2", 37 | "typescript": "^1.8.10", 38 | "typings":"^1.3.2" 39 | }, 40 | "repository": "https://github.com/maliksahil/Angular2AzureADAuthLib" 41 | } -------------------------------------------------------------------------------- /systemjs.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * System configuration for Angular samples 3 | * Adjust as necessary for your application needs. 4 | */ 5 | (function (global) { 6 | System.config({ 7 | paths: { 8 | // paths serve as alias 9 | 'npm:': 'node_modules/' 10 | }, 11 | // map tells the System loader where to look for things 12 | map: { 13 | // our app is within the app folder 14 | app: 'app', 15 | 16 | // angular bundles 17 | '@angular/core': 'npm:@angular/core/bundles/core.umd.js', 18 | '@angular/common': 'npm:@angular/common/bundles/common.umd.js', 19 | '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js', 20 | '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js', 21 | '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js', 22 | '@angular/http': 'npm:@angular/http/bundles/http.umd.js', 23 | '@angular/router': 'npm:@angular/router/bundles/router.umd.js', 24 | '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js', 25 | '@angular/upgrade': 'npm:@angular/upgrade/bundles/upgrade.umd.js', 26 | 27 | // other libraries 28 | 'rxjs': 'npm:rxjs', 29 | 'angular-in-memory-web-api': 'npm:angular-in-memory-web-api', 30 | }, 31 | // packages tells the System loader how to load when no filename and/or no extension 32 | packages: { 33 | app: { 34 | main: './main.js', 35 | defaultExtension: 'js' 36 | }, 37 | rxjs: { 38 | defaultExtension: 'js' 39 | }, 40 | 'angular-in-memory-web-api': { 41 | main: './index.js', 42 | defaultExtension: 'js' 43 | } 44 | } 45 | }); 46 | })(this); 47 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "system", 5 | "moduleResolution": "node", 6 | "sourceMap": true, 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true, 9 | "removeComments": false, 10 | "noImplicitAny": false 11 | }, 12 | "exclude": [ 13 | "node_modules", 14 | "typings/main", 15 | "typings/main.d.ts" 16 | ] 17 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "class-name": true, 4 | "comment-format": [ 5 | true, 6 | "check-space" 7 | ], 8 | "curly": true, 9 | "eofline": true, 10 | "forin": true, 11 | "indent": [ 12 | true, 13 | "spaces" 14 | ], 15 | "label-position": true, 16 | "label-undefined": true, 17 | "max-line-length": [ 18 | true, 19 | 140 20 | ], 21 | "member-access": false, 22 | "member-ordering": [ 23 | true, 24 | "static-before-instance", 25 | "variables-before-functions" 26 | ], 27 | "no-arg": true, 28 | "no-bitwise": true, 29 | "no-console": [ 30 | true, 31 | "debug", 32 | "info", 33 | "time", 34 | "timeEnd", 35 | "trace" 36 | ], 37 | "no-construct": true, 38 | "no-debugger": true, 39 | "no-duplicate-key": true, 40 | "no-duplicate-variable": true, 41 | "no-empty": false, 42 | "no-eval": true, 43 | "no-inferrable-types": true, 44 | "no-shadowed-variable": true, 45 | "no-string-literal": false, 46 | "no-switch-case-fall-through": true, 47 | "no-trailing-whitespace": true, 48 | "no-unused-expression": true, 49 | "no-unused-variable": true, 50 | "no-unreachable": true, 51 | "no-use-before-declare": true, 52 | "no-var-keyword": true, 53 | "object-literal-sort-keys": false, 54 | "one-line": [ 55 | true, 56 | "check-open-brace", 57 | "check-catch", 58 | "check-else", 59 | "check-whitespace" 60 | ], 61 | "quotemark": [ 62 | true, 63 | "single" 64 | ], 65 | "radix": true, 66 | "semicolon": [ 67 | "always" 68 | ], 69 | "triple-equals": [ 70 | true, 71 | "allow-null-check" 72 | ], 73 | "typedef-whitespace": [ 74 | true, 75 | { 76 | "call-signature": "nospace", 77 | "index-signature": "nospace", 78 | "parameter": "nospace", 79 | "property-declaration": "nospace", 80 | "variable-declaration": "nospace" 81 | } 82 | ], 83 | "variable-name": false, 84 | "whitespace": [ 85 | true, 86 | "check-branch", 87 | "check-decl", 88 | "check-operator", 89 | "check-separator", 90 | "check-type" 91 | ] 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ambientDependencies": { 3 | "es6-shim": "github:DefinitelyTyped/DefinitelyTyped/es6-shim/es6-shim.d.ts#6697d6f7dadbf5773cb40ecda35a76027e0783b2" 4 | } 5 | } --------------------------------------------------------------------------------