Skip to content
This repository has been archived by the owner on Dec 4, 2017. It is now read-only.

[WIP] docs(cb-azure-ad-auth): Add new authentication cookbook #2565

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions public/docs/_examples/cb-azure-ad-auth/e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/// <reference path='../_protractor/e2e.d.ts' />
'use strict';
describe('Azure AD Auth E2E tests', function () {

let expectedMsg = 'Simple app demonstrates';

beforeEach(function () {
browser.get('');
});

it(`should display: ${expectedMsg}`, function () {
expect(element(by.css('p')).getText()).toContain(expectedMsg);
});

});
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Component } from '@angular/core';
@Component({
selector: 'app',
template: `
<a [routerLink]="['']">About</a> | <a [routerLink]="['login']">Login</a> | <a [routerLink]="['status']">Status</a> <br/>
<router-outlet></router-outlet>`
})

export class AppComponent { }
40 changes: 40 additions & 0 deletions public/docs/_examples/cb-azure-ad-auth/ts/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// #docregion
import { NgModule } from '@angular/core';
import { HttpModule } from '@angular/http';
import { BrowserModule } from '@angular/platform-browser';
import { routing } from './app.routing';
import { AppComponent } from './app.component';

import { HomeComponent } from './home/home.component';
import { LoginComponent } from './login/login.component';
import { StatusComponent } from './status/status.component';

import { AzureADServiceConstants } from './ngAuth/authenticators/AzureADServiceConstants';
import { AzureADAuthService } from './ngAuth/authenticators/AzureADAuthService';
import { AuthenticatedHttpService } from './ngAuth/AuthenticatedHttpService';

import { serviceConstants } from './authsettings.config';

let authenticator = new AzureADAuthService(serviceConstants);

@NgModule({
providers: [
AuthenticatedHttpService,
{
provide: AzureADAuthService,
useValue: authenticator
}],
imports: [
routing,
BrowserModule,
HttpModule
],
declarations: [
AppComponent,
HomeComponent,
LoginComponent,
StatusComponent
],
bootstrap: [AppComponent]
})
export class AppModule { }
13 changes: 13 additions & 0 deletions public/docs/_examples/cb-azure-ad-auth/ts/app/app.routing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// #docregion
import { ModuleWithProviders } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { LoginComponent } from './login/login.component';
import {HomeComponent} from './home/home.component';
import { StatusComponent } from './status/status.component';

export const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'login', component: LoginComponent },
{ path: 'status', component: StatusComponent },
];
export const routing: ModuleWithProviders = RouterModule.forRoot(routes);
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {AzureADServiceConstants} from './ngAuth/authenticators/AzureADServiceConstants';

export const serviceConstants: AzureADServiceConstants = new AzureADServiceConstants(
"e176a5f1-596e-44c9-91cb-46de9bb96ee1",
"angularad.onmicrosoft.com",
"http://localhost:3000/status",
"https://graph.windows.net"
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Component } from '@angular/core';
import { Inject, Injectable } from '@angular/core';

@Component({
template:`
Simple app demonstrates logging into AzureAD and running a command against the Azure AD graph. <br/>
Click the login tab to login, and status tab to view your login status.
`
})
export class HomeComponent { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// #docregion
import { Component, Inject } from '@angular/core';
import { Router } from '@angular/router';

import {AzureADAuthService} from '../ngAuth/authenticators/AzureADAuthService';

@Component({
template: `
<button (click)="logIn()">
Sign In
</button>`
})

export class LoginComponent {
constructor(
@Inject(AzureADAuthService) private _authService: AzureADAuthService,
private _router: Router) { }

logIn() {
this._authService.logIn("/");
}
}
3 changes: 3 additions & 0 deletions public/docs/_examples/cb-azure-ad-auth/ts/app/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import {AppModule} from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// #docregion
import { Injectable, Inject } from '@angular/core';
import { Http, Headers, Response } from '@angular/http';
import { AzureADAuthService } from './authenticators/AzureADAuthService';
import { Observable, Subscriber } from 'rxjs';

@Injectable()
export class AuthenticatedHttpService {
private _authenticator: AzureADAuthService;
private _http: Http;
constructor( @Inject(Http) http: Http, @Inject(AzureADAuthService) authenticator: AzureADAuthService) {
this._authenticator = authenticator;
this._http = http;
}

createAuthorizationHeader(headers: Headers) {
headers.append('Authorization', 'Bearer ' + this._authenticator.getAccessToken());
}

get(url: string) {
let headers = new Headers();
this.createAuthorizationHeader(headers);
return this._http.get(url, { headers: headers });
}

post(url: string, data: any) {
let headers = new Headers();
this.createAuthorizationHeader(headers);
return this._http.post(url, data, {
headers: headers,
});
}
}
30 changes: 30 additions & 0 deletions public/docs/_examples/cb-azure-ad-auth/ts/app/ngAuth/JwtHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Injectable } from '@angular/core';

@Injectable()
export class JwtHelper {
private urlBase64Decode(str: string) {
var output = str.replace(/-/g, '+').replace(/_/g, '/');
switch (output.length % 4) {
case 0: { break; }
case 2: { output += '=='; break; }
case 3: { output += '='; break; }
default: {
throw 'Illegal base64url string!';
}
}
return decodeURIComponent((<any>window).escape(window.atob(output)));
}

public decodeToken(token: string = "") {
if (token === null || token === "") return {"upn": ""};
var parts = token.split('.');
if (parts.length !== 3) {
throw new Error('JWT must have 3 parts');
}
var decoded = this.urlBase64Decode(parts[1]);
if (!decoded) {
throw new Error('Cannot decode the token');
}
return JSON.parse(decoded);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// #docregion
import { Injectable } from '@angular/core';
import { Http, Headers } from '@angular/http';
import { JwtHelper } from '../JwtHelper';

import { AzureADServiceConstants } from "./AzureADServiceConstants";

@Injectable()
export class AzureADAuthService {
public isUserAuthenticated(): boolean {
let access_token = this.getAccessToken();
return access_token != null;
}

public getAccessToken(): string {
return window.localStorage.getItem("access_token");
}

public getUserName(): string {
var jwtHelper = new JwtHelper();
var parsedToken = jwtHelper.decodeToken(this.getAccessToken());

var expiryTime = new Date(parsedToken.exp * 1000);
var now = new Date();
if (now > expiryTime) this.logOut();

return parsedToken.upn;
}

// #docregion login
public logIn(state = "/") {
window.location.href = "https://login.microsoftonline.com/" + this._serviceConstants.tenantID +
"/oauth2/authorize?response_type=id_token&client_id=" + this._serviceConstants.clientID +
"&redirect_uri=" + encodeURIComponent(window.location.href) +
"&state=" + state + "&nonce=SomeNonce";
}
// #enddocregion login

public logOut(state = "/") {
window.localStorage.removeItem("id_token");
window.localStorage.removeItem("access_token");
window.location.href = state;
}
private parseQueryString = function (url: string) {
var params = {};
var queryString = "";
if (url.search("#") != -1) {
queryString = url.substring(url.search("#") + 1);

} else {
queryString = url.substring(url.indexOf("?") + 1);
}
var a = queryString.split('&');
for (var i = 0; i < a.length; i++) {
var b = a[i].split('=');
params[decodeURIComponent(b[0])] = decodeURIComponent(b[1] || '');
}
return params;
}

private params = this.parseQueryString(location.hash);
// #docregion ctor
constructor(private _serviceConstants: AzureADServiceConstants) {
// do we have an access token, if so add the iframe renewer
if (window.localStorage.getItem("access_token")) {
var iframe = document.createElement('iframe');
iframe.style.display = "none";
iframe.src = "/app/ngAuth/renewToken.html?tenantID=" +
encodeURIComponent(this._serviceConstants.tenantID) +
"&clientID=" + encodeURIComponent(this._serviceConstants.clientID) +
"&resource=" + encodeURIComponent(this._serviceConstants.graphResource);
window.onload = function () {
document.body.appendChild(iframe);
}
}
if (this.params["id_token"] != null) {
window.localStorage.setItem("id_token", this.params["id_token"]);
// redirect to get access token here..
window.location.href = "https://login.microsoftonline.com/" + this._serviceConstants.tenantID +
"/oauth2/authorize?response_type=token&client_id=" + this._serviceConstants.clientID +
"&resource=" + this._serviceConstants.graphResource +
"&redirect_uri=" + encodeURIComponent(window.location.href) +
"&prompt=none&state=" + this.params["state"] + "&nonce=SomeNonce";
}
else if (this.params["access_token"] != null) {
window.localStorage.setItem("access_token", this.params["access_token"]);
// redirect to the original call URl here.
window.location.href = this.params["state"];
}
}
// #enddocregion ctor
}

function error(err: any) {
console.error(JSON.stringify(err, null, 4));
}

// #enddocregion
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Injectable } from '@angular/core';

@Injectable()
export class AzureADServiceConstants {
constructor(
public clientID:string,
public tenantID:string,
public redirectURL:string,
public backendUrl:string,
public graphResource = "https://graph.windows.net",
public isCordova = false,
public isElectron = false) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<html>

<head>
<title>Renew Token</title>
<script>
// jwt_decode from https://raw.githubusercontent.com/auth0/jwt-decode/master/build/jwt-decode.min.js
!function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b,c){function d(a){this.message=a}function e(a){var b=String(a).replace(/=+$/,"");if(b.length%4==1)throw new d("'atob' failed: The string to be decoded is not correctly encoded.");for(var c,e,g=0,h=0,i="";e=b.charAt(h++);~e&&(c=g%4?64*c+e:e,g++%4)?i+=String.fromCharCode(255&c>>(-2*g&6)):0)e=f.indexOf(e);return i}var f="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";d.prototype=new Error,d.prototype.name="InvalidCharacterError",b.exports="undefined"!=typeof window&&window.atob||e},{}],2:[function(a,b,c){function d(a){return decodeURIComponent(e(a).replace(/(.)/g,function(a,b){var c=b.charCodeAt(0).toString(16).toUpperCase();return c.length<2&&(c="0"+c),"%"+c}))}var e=a("./atob");b.exports=function(a){var b=a.replace(/-/g,"+").replace(/_/g,"/");switch(b.length%4){case 0:break;case 2:b+="==";break;case 3:b+="=";break;default:throw"Illegal base64url string!"}try{return d(b)}catch(c){return e(b)}}},{"./atob":1}],3:[function(a,b,c){"use strict";var d=a("./base64_url_decode");b.exports=function(a){if(!a)throw new Error("Invalid token specified");return JSON.parse(d(a.split(".")[1]))}},{"./base64_url_decode":2}],4:[function(a,b,c){var d="undefined"!=typeof self?self:"undefined"!=typeof window?window:{},e=a("./lib/index");"function"==typeof d.window.define&&d.window.define.amd?d.window.define("jwt_decode",function(){return e}):d.window&&(d.window.jwt_decode=e)},{"./lib/index":3}]},{},[4]);
</script>

<script>
'use strict';
var parsedToken = jwt_decode(window.localStorage.getItem("access_token"));
var timeoutMiliSeconds = (parsedToken.exp*1000 - 300000) - (new Date().getTime());
// parse QueryString
var params = {};
var queryString = "" ;
if (window.location.href.search("#") != -1) {
queryString = window.location.href.substring(window.location.href.search("#") + 1);

} else {
queryString = window.location.search.substring(1);
}
var a = queryString.split('&');
for (var i = 0; i < a.length; i++) {
var b = a[i].split('=');
params[decodeURIComponent(b[0])] = decodeURIComponent(b[1] || '');
}

if (params["id_token"] != null) {
window.localStorage.setItem("id_token", this.params["id_token"]);
// redirect to get access token here..
window.location.href = "https://login.microsoftonline.com/" + params["tenantID"] +
"/oauth2/authorize?response_type=token&client_id=" + params["clientID"] +
"&resource=" + params["resource"] +
"&redirect_uri=" + encodeURIComponent(window.location.href) +
"&prompt=none&state=" + params["state"] + "&nonce=SomeNonce";
}
else if (params["access_token"] != null) {
window.localStorage.setItem("access_token", this.params["access_token"]);
window.location.href = params["state"];
} else {
window.setTimeout(function() {
var idToken = window.localStorage.getItem("id_token");
var urlWithoutQueryString = window.location.href.substring(0, window.location.href.length - window.location.search.length)
window.location.href = "https://login.microsoftonline.com/" + params["tenantID"] +
"/oauth2/authorize?response_type=token&client_id=" + params["clientID"] +
"&resource=" + params["resource"] +
"&redirect_uri=" + encodeURIComponent(window.location.href) +
"&prompt=none&state=" + encodeURIComponent(window.location.href) + "&nonce=SomeNonce";
}, timeoutMiliSeconds);
}
</script>
</head>

<body>
</body>

</html>
Loading