From 18ea15fb9ef4680185de8f829b7a727eecb06cb2 Mon Sep 17 00:00:00 2001 From: BeMor81 <35695724+BeMor81@users.noreply.github.com> Date: Thu, 15 Mar 2018 14:09:38 +0000 Subject: [PATCH] Option to use Broker and configurable redirect URL --- src/adal.android.ts | 111 ++++++++++++++++++++++++++++++++++++++++---- src/adal.ios.ts | 72 +++++++++++++++++++++++++--- src/index.d.ts | 25 ++++++++-- 3 files changed, 188 insertions(+), 20 deletions(-) diff --git a/src/adal.android.ts b/src/adal.android.ts index 44c5850..4cf1850 100644 --- a/src/adal.android.ts +++ b/src/adal.android.ts @@ -2,6 +2,16 @@ import * as application from 'tns-core-modules/application'; import * as utils from 'tns-core-modules/utils/utils'; +import { isAndroid } from 'tns-core-modules/ui/frame/frame'; +import { getString, setString } from "tns-core-modules/application-settings"; + +export interface MultiPlatformRedirectUri { + ios: string; + android: string; +} + +const USERID_KEY = "USER_ID"; +const LOGINHINT_KEY = "LOGIN_HINT"; export class AdalContext { @@ -9,20 +19,59 @@ export class AdalContext { private authority: string; private clientId: string; private context: com.microsoft.aad.adal.AuthenticationContext; - private loginHint: string = ''; + private _loginHint: string; + private get loginHint(): string { + if(this.useBroker) { + return ''; + } + if(this._loginHint == null) { + this._loginHint = getString(LOGINHINT_KEY); + if(this._loginHint == null) { + this.loginHint = ''; + } + } + return this._loginHint; + } + private set loginHint(value: string) { + this._loginHint = value; + setString(LOGINHINT_KEY, this._loginHint); + } private redirectUri: string = 'urn:ietf:wg:oauth:2.0:oob'; + private useBroker: boolean; private resourceId: string; - private userId: string; - + private _userId: string; + private get userId(): string { + if(this._userId == null) { + this._userId = getString(USERID_KEY); + } + return this._userId; + } + private set userId(value: string) { + this._userId = value; + setString(USERID_KEY, this._userId); + } // Authority is in the form of https://login.microsoftonline.com/yourtenant.onmicrosoft.com - constructor(authority: string, clientId: string, resourceId: string) { + constructor(authority: string, clientId: string, resourceId: string, redirectUri?: MultiPlatformRedirectUri | string, useBroker?: boolean) { this.authority = authority; this.clientId = clientId; this.resourceId = resourceId; + if(redirectUri != null) { + if (typeof redirectUri === "string") { + this.redirectUri = redirectUri; + } else { + this.redirectUri = redirectUri.android; + } + } + this.useBroker = ((useBroker == null) ? false : useBroker); + }; + public initContext() { + if(this.context != null) { + console.log("Context already initialised"); + return; + } this.activity = application.android.foregroundActivity || application.android.startActivity; - this.context = new com.microsoft.aad.adal.AuthenticationContext(utils.ad.getApplicationContext(), this.authority, true); - + com.microsoft.aad.adal.AuthenticationSettings.INSTANCE.setUseBroker(this.useBroker); application.android.on('activityResult', (args) => { let intent: android.content.Intent = args.activity.getIntent(); if (this.context) { @@ -30,8 +79,11 @@ export class AdalContext { } }); } - public login(): Promise { + if(!this.isContextInit()) { + this.initContext(); + } + var that = this; return new Promise((resolve: any, reject: any) => { this.context.acquireToken( this.activity, @@ -41,7 +93,8 @@ export class AdalContext { this.loginHint, new com.microsoft.aad.adal.AuthenticationCallback({ onSuccess(result: com.microsoft.aad.adal.AuthenticationResult): void { - this.userId = result.getUserInfo().getUserId(); + that.userId = result.getUserInfo().getUserId(); + that.loginHint = result.getUserInfo().getDisplayableId(); resolve(result.getAccessToken()); }, onError(error: javalangException): void { @@ -53,20 +106,60 @@ export class AdalContext { } public getToken(): Promise { + if(!this.isContextInit()) { + this.initContext(); + } + if(this.userId == null) { + return this.login(); + } + var that = this; return new Promise((resolve: any, reject) => { this.context.acquireTokenSilentAsync( - this.clientId, + this.resourceId, this.clientId, this.userId, new com.microsoft.aad.adal.AuthenticationCallback({ onSuccess(result: com.microsoft.aad.adal.AuthenticationResult): void { + console.log("expires on = " + result.getExpiresOn().toGMTString()); + that.userId = result.getUserInfo().getUserId(); + that.loginHint = result.getUserInfo().getDisplayableId(); resolve(result.getAccessToken()); }, onError(error: javalangException): void { + if (error instanceof com.microsoft.aad.adal.AuthenticationException) { + if(error.getCode() == com.microsoft.aad.adal.ADALError.AUTH_REFRESH_FAILED_PROMPT_NOT_ALLOWED) { + that.context.acquireToken( + that.activity, + that.resourceId, + that.clientId, + that.redirectUri, + that.loginHint, + new com.microsoft.aad.adal.AuthenticationCallback({ + onSuccess(result: com.microsoft.aad.adal.AuthenticationResult): void { + that.userId = result.getUserInfo().getUserId(); + that.loginHint = result.getUserInfo().getDisplayableId(); + resolve(result.getAccessToken()); + }, + onError(error: javalangException): void { + reject(error); + } + })); + return; + } + } reject(error); } }) ); }); } + + private isContextInit(): boolean { + if(this.context == null) { + console.log("Context not initialised"); + return false; + } + console.log("Context initialised"); + return true; + } } \ No newline at end of file diff --git a/src/adal.ios.ts b/src/adal.ios.ts index 3b80635..6a7cef3 100644 --- a/src/adal.ios.ts +++ b/src/adal.ios.ts @@ -1,8 +1,15 @@ -/// +/// import { isIOS } from "tns-core-modules/ui/frame/frame"; + +import { getString, setString } from "tns-core-modules/application-settings"; declare var interop: any; declare var NSURL: any; +export interface MultiPlatformRedirectUri { + ios: string; + android: string; +} + export class AdalContext { private authError: any; @@ -11,20 +18,43 @@ export class AdalContext { private clientId: string; private context: ADAuthenticationContext; private redirectUri: string = 'urn:ietf:wg:oauth:2.0:oob'; + private useBroker: boolean; private resourceId: string; private userId: string; // Authority is in the form of https://login.microsoftonline.com/yourtenant.onmicrosoft.com - constructor(authority: string, clientId: string, resourceId: string) { + constructor(authority: string, clientId: string, resourceId: string, redirectUri?: MultiPlatformRedirectUri | string, useBroker?: boolean) { this.authError = new interop.Reference(); this.authority = authority; this.clientId = clientId; this.resourceId = resourceId; + if(redirectUri != null) { + if (typeof redirectUri === "string") { + this.redirectUri = redirectUri; + } else { + this.redirectUri = redirectUri.ios; + } + } + this.useBroker = ((useBroker == null) ? false : useBroker); + + } + public initContext() { + if(this.context != null) { + console.log("Context already initialised"); + return; + } ADAuthenticationSettings.sharedInstance().setDefaultKeychainGroup(null); this.context = ADAuthenticationContext.authenticationContextWithAuthorityError(this.authority, this.authError); + if(this.useBroker) { + this.context.credentialsType = ADCredentialsType.D_CREDENTIALS_AUTO; + } else { + this.context.credentialsType = ADCredentialsType.D_CREDENTIALS_EMBEDDED; + } } - public login(): Promise { + if(!this.isContextInit()) { + this.initContext(); + } this.authError = new interop.Reference(); return new Promise((resolve, reject) => { this.context.acquireTokenWithResourceClientIdRedirectUriCompletionBlock( @@ -44,15 +74,45 @@ export class AdalContext { } public getToken(): Promise { - return new Promise((resolve) => { + if(!this.isContextInit()) { + this.initContext(); + } + return new Promise((resolve, reject) => { this.context.acquireTokenSilentWithResourceClientIdRedirectUriCompletionBlock( - this.clientId, + this.resourceId, this.clientId, NSURL.URLWithString(this.redirectUri), (result: ADAuthenticationResult) => { - resolve(result.accessToken); + if(result.accessToken == null) { + Promise.resolve().then(() => { + this.context.acquireTokenWithResourceClientIdRedirectUriCompletionBlock( + this.resourceId, + this.clientId, + NSURL.URLWithString(this.redirectUri), + (result: ADAuthenticationResult) => { + this.authResult = result; + if (result.error) { + reject(result.error); + } else { + this.userId = result.tokenCacheItem.userInformation.userObjectId; + resolve(result.accessToken); + } + }); + }); + } else { + resolve(result.accessToken); + } } ); }); } + + private isContextInit(): boolean { + if(this.context == null) { + console.log("Context not initialised"); + return false; + } + console.log("Context initialised"); + return true; + } } \ No newline at end of file diff --git a/src/index.d.ts b/src/index.d.ts index 7721ffd..5fff3d2 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -1,5 +1,20 @@ -/** - * iOS and Android apis should match. - * It doesn't matter if you export `.ios` or `.android`, either one but only one. - */ -export * from './adal.ios'; \ No newline at end of file +export interface MultiPlatformRedirectUri { + ios: string; + android: string; +} +export declare class AdalContext { + private authError; + private authResult; + private authority; + private clientId; + private context; + private redirectUri; + private useBroker; + private resourceId; + private userId; + constructor(authority: string, clientId: string, resourceId: string, redirectUri?: MultiPlatformRedirectUri | string, useBroker?: boolean); + initContext(): void; + login(): Promise; + getToken(): Promise; + private isContextInit(); +}