Skip to content

Commit

Permalink
feat: add support for GCC and other sovereign clouds (#1928)
Browse files Browse the repository at this point in the history
* feat: add support for configuring the base URL in the graph client

* feat: add base-url attribute to Msal2Provider

base-url specifies a base url for the graph client config. It is optional and defaults to https://graph.microsoft.com.

With this attribute, you can pass in any of the GCC and national clouds urls

* refactor: use GraphEndpoint type instead of string

* refactor: move validating base URL to utility function

* fix: use new URL().origin to validate the base URL

* feat: set baseURL when you initialize with clientID

* feat: update the types on the declared variable

* fix: cast string to GraphEndpoint type in validation

* feat: update MsalProvider to use base-url property

* feat: update the ElectronProvider to set a baseURL

You can set a baseURL when calling the electron provider.

Additional refactors to fix tsc errors

* feat: add base url to Sharepoint provider

* feat: add base-url attribute to TeamsMsal2Provider

Since it uses Msal2Provider, all we do is pass the base URL where Msal2Provider is called

* feat: add base-url attribute to TeamsMsalProvider

Since it uses MsalProvider, all we do is pass the base URL where MsalProvider is called

* feat: add base-url attribute to TeamsFxProvider

The baseUrl is passed during initialization. The default is already set to MICROSOFT_GRAPH_DEFAULT_ENDPOINT

* feat: change to validating the baseUrl before initializing the client

* refactor: set the baseURL default value in constractor

* refactor: set the baseURL default value in constructor

* feat: add base-url to the baseProvider

* refactor: remove re-declaration of property in base class

* fix: validate base URL string in the IProvider

* fix: do the validation when setting the baseURL

* refactor/docs: use one check to get all accounts and document the exported types

* fix: remove duplicate import

* fix: set licence text on all files

* fix: updaet the memberof value of the base provider

Co-authored-by: Gavin Barron <[email protected]>

Co-authored-by: Gavin Barron <[email protected]>
  • Loading branch information
2 people authored and Mnickii committed Dec 9, 2022
1 parent 5bbabb6 commit 7742373
Show file tree
Hide file tree
Showing 23 changed files with 259 additions and 48 deletions.
7 changes: 7 additions & 0 deletions packages/mgt-components/src/utils/FluentComponents.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/**
* -------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License.
* See License in the project root for license information.
* -------------------------------------------------------------------------------------------
*/

import { provideFluentDesignSystem } from '@fluentui/web-components';

const designSystem = provideFluentDesignSystem();
Expand Down
6 changes: 4 additions & 2 deletions packages/mgt-element/src/Graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
TelemetryHandler
} from '@microsoft/microsoft-graph-client';

import { IGraph } from './IGraph';
import { IGraph, MICROSOFT_GRAPH_DEFAULT_ENDPOINT } from './IGraph';
import { IProvider } from './providers/IProvider';
import { Batch } from './utils/Batch';
import { ComponentMiddlewareOptions } from './utils/ComponentMiddlewareOptions';
Expand Down Expand Up @@ -155,8 +155,10 @@ export function createFromProvider(provider: IProvider, version?: string, compon
new HTTPMessageHandler()
];

let baseURL = provider.baseURL ? provider.baseURL : MICROSOFT_GRAPH_DEFAULT_ENDPOINT;
const client = Client.initWithMiddleware({
middleware: chainMiddleware(...middleware)
middleware: chainMiddleware(...middleware),
baseUrl: baseURL
});

const graph = new Graph(client, version);
Expand Down
32 changes: 32 additions & 0 deletions packages/mgt-element/src/IGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,35 @@ export interface IGraph {
*/
createBatch(): IBatch;
}

/**
* GraphEndpoint is a valid URL that is used to access the Graph.
*/
export type GraphEndpoint =
| 'https://graph.microsoft.com'
| 'https://graph.microsoft.us'
| 'https://dod-graph.microsoft.us'
| 'https://graph.microsoft.de'
| 'https://microsoftgraph.chinacloudapi.cn';

/**
* MICROSOFT_GRAPH_ENDPOINTS is a set of all the valid Graph URL endpoints.
*/
export const MICROSOFT_GRAPH_ENDPOINTS: Set<GraphEndpoint> = new Set<GraphEndpoint>();

/**
* MICROSOFT_GRAPH_DEFAULT_ENDPOINT is the default Graph endpoint that is silently set on
* the providers as the baseURL.
*/
export const MICROSOFT_GRAPH_DEFAULT_ENDPOINT: GraphEndpoint = 'https://graph.microsoft.com';

(() => {
const endpoints: GraphEndpoint[] = [
MICROSOFT_GRAPH_DEFAULT_ENDPOINT,
'https://graph.microsoft.us',
'https://dod-graph.microsoft.us',
'https://graph.microsoft.de',
'https://microsoftgraph.chinacloudapi.cn'
];
endpoints.forEach(endpoint => MICROSOFT_GRAPH_ENDPOINTS.add(endpoint));
})();
12 changes: 12 additions & 0 deletions packages/mgt-element/src/components/baseProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import { property } from 'lit/decorators.js';
import { MgtBaseComponent } from './baseComponent';
import { IProvider } from '../providers/IProvider';
import { GraphEndpoint } from '../IGraph';

/**
* Abstract implementation for provider component
Expand Down Expand Up @@ -65,6 +66,17 @@ export abstract class MgtBaseProvider extends MgtBaseComponent {
})
public dependsOn: MgtBaseProvider;

/**
* The base URL that should be used in the graph client config.
*
* @memberof MgtBaseProvider
*/
@property({
attribute: 'base-url',
type: String
})
public baseUrl: GraphEndpoint;

private _provider: IProvider;

/**
Expand Down
20 changes: 19 additions & 1 deletion packages/mgt-element/src/providers/IProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
*/

import { AuthenticationProvider, AuthenticationProviderOptions } from '@microsoft/microsoft-graph-client';
import { IGraph } from '../IGraph';
import { validateBaseURL } from '../utils/GraphHelpers';
import { GraphEndpoint, IGraph, MICROSOFT_GRAPH_DEFAULT_ENDPOINT } from '../IGraph';
import { EventDispatcher, EventHandler } from '../utils/EventDispatcher';

/**
Expand Down Expand Up @@ -48,6 +49,23 @@ export abstract class IProvider implements AuthenticationProvider {
private _state: ProviderState;
private _loginChangedDispatcher = new EventDispatcher<LoginChangedEvent>();
private _activeAccountChangedDispatcher = new EventDispatcher<ActiveAccountChanged>();
private _baseURL: GraphEndpoint = MICROSOFT_GRAPH_DEFAULT_ENDPOINT;

/**
* The base URL to be used in the graph client config.
*/
public set baseURL(url: GraphEndpoint) {
if (validateBaseURL(url)) {
this._baseURL = url;
return;
} else {
throw new Error(`${url} is not a valid Graph URL endpoint.`);
}
}

public get baseURL(): GraphEndpoint {
return this._baseURL;
}

/**
* Enable/Disable incremental consent
Expand Down
19 changes: 18 additions & 1 deletion packages/mgt-element/src/utils/GraphHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import { AuthenticationHandlerOptions, Middleware } from '@microsoft/microsoft-graph-client';
import { Providers } from '..';
import { GraphEndpoint, MICROSOFT_GRAPH_ENDPOINTS, Providers } from '..';

/**
* creates an AuthenticationHandlerOptions from scopes array that
Expand Down Expand Up @@ -46,3 +46,20 @@ export function chainMiddleware(...middleware: Middleware[]): Middleware {
}
return rootMiddleware;
}

/**
* Helper method to validate a base URL string
* @param url a URL string
* @returns GraphEndpoint
*/
export function validateBaseURL(url: string): GraphEndpoint {
try {
const urlObj = new URL(url);
const originAsEndpoint = urlObj.origin as GraphEndpoint;
if (MICROSOFT_GRAPH_ENDPOINTS.has(originAsEndpoint)) {
return originAsEndpoint;
}
} catch (error) {
return;
}
}
7 changes: 7 additions & 0 deletions packages/mgt-spfx/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/**
* -------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License.
* See License in the project root for license information.
* -------------------------------------------------------------------------------------------
*/

export { MgtLibrary } from './libraries/mgt/MgtLibrary';
export * from '@microsoft/mgt-sharepoint-provider';
export * from '@microsoft/mgt-components';
Expand Down
7 changes: 7 additions & 0 deletions packages/mgt-spfx/src/libraries/mgt/MgtLibrary.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/**
* -------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License.
* See License in the project root for license information.
* -------------------------------------------------------------------------------------------
*/

export class MgtLibrary {
public name(): string {
return 'MgtLibrary';
Expand Down
7 changes: 7 additions & 0 deletions packages/mgt-spfx/src/libraries/mgt/loc/en-us.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/**
* -------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License.
* See License in the project root for license information.
* -------------------------------------------------------------------------------------------
*/

define([], function() {
return {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
PublicClientApplication
} from '@azure/msal-node';
import { AuthenticationProviderOptions } from '@microsoft/microsoft-graph-client';
import { GraphEndpoint } from '@microsoft/mgt-element';
import { BrowserWindow, ipcMain } from 'electron';
import { CustomFileProtocolListener } from './CustomFileProtocol';
import { REDIRECT_URI, COMMON_AUTHORITY_URL } from './Constants';
Expand Down Expand Up @@ -65,6 +66,10 @@ export interface MsalElectronConfig {
* @memberof MsalElectronConfig
*/
cachePlugin?: ICachePlugin;
/**
* The base URL for the graph client
*/
baseURL?: GraphEndpoint;
}

/**
Expand All @@ -86,6 +91,11 @@ enum AuthState {
LOGGED_OUT = 'logged_out'
}

/**
* AccountDetails defines the available AccountInfo or undefined.
*/
type AccountDetails = AccountInfo | undefined;

/**
* ElectronAuthenticator class to be instantiated in the main process.
* Responsible for MSAL authentication flow and token acqusition.
Expand Down Expand Up @@ -133,10 +143,10 @@ export class ElectronAuthenticator {
* Logged in account
*
* @private
* @type {AccountInfo}
* @type {AccountDetails}
* @memberof ElectronAuthenticator
*/
private account: AccountInfo;
private account: AccountDetails;

/**
* Params to generate the URL for MSAL auth
Expand Down Expand Up @@ -182,7 +192,7 @@ export class ElectronAuthenticator {
*/
private constructor(config: MsalElectronConfig) {
this.setConfig(config);
this.account = null;
this.account = undefined;
this.mainWindow = config.mainWindow;
this.setRequestObjects(config.scopes);
this.setupProvider();
Expand Down Expand Up @@ -224,7 +234,7 @@ export class ElectronAuthenticator {
clientId: config.clientId,
authority: config.authority ? config.authority : COMMON_AUTHORITY_URL
},
cache: config.cachePlugin ? { cachePlugin: config.cachePlugin } : null,
cache: config.cachePlugin ? { cachePlugin: config.cachePlugin } : undefined,
system: {
loggerOptions: {
loggerCallback(loglevel, message, containsPii) {},
Expand Down Expand Up @@ -255,7 +265,7 @@ export class ElectronAuthenticator {
this.authCodeRequest = {
scopes: requestScopes,
redirectUri,
code: null
code: ''
};
}

Expand Down Expand Up @@ -311,7 +321,7 @@ export class ElectronAuthenticator {
* @return {*} {Promise<string>}
* @memberof ElectronAuthenticator
*/
protected async getAccessToken(options?: AuthenticationProviderOptions): Promise<string> {
protected async getAccessToken(options?: AuthenticationProviderOptions): Promise<string | undefined> {
let authResponse;
const scopes = options && options.scopes ? options.scopes : this.authCodeUrlParams.scopes;
const account = this.account || (await this.getAccount());
Expand All @@ -326,6 +336,7 @@ export class ElectronAuthenticator {
if (authResponse) {
return authResponse.accessToken;
}
return undefined;
}

/**
Expand All @@ -337,7 +348,7 @@ export class ElectronAuthenticator {
* @return {*} {Promise<AuthenticationResult>}
* @memberof ElectronAuthenticator
*/
protected async getTokenSilent(tokenRequest, scopes?): Promise<AuthenticationResult> {
protected async getTokenSilent(tokenRequest, scopes?): Promise<AuthenticationResult | null> {
try {
return await this.clientApplication.acquireTokenSilent(tokenRequest);
} catch (error) {
Expand Down Expand Up @@ -366,7 +377,7 @@ export class ElectronAuthenticator {
protected async logout(): Promise<void> {
if (this.account) {
await this.clientApplication.getTokenCache().removeAccount(this.account);
this.account = null;
this.account = undefined;
}
}

Expand All @@ -379,8 +390,8 @@ export class ElectronAuthenticator {
* @memberof ElectronAuthenticator
*/
private async setAccountFromResponse(response: AuthenticationResult) {
if (response !== null) {
this.account = response.account;
if (response) {
this.account = response?.account || undefined;
} else {
this.account = await this.getAccount();
}
Expand Down Expand Up @@ -412,7 +423,7 @@ export class ElectronAuthenticator {
.acquireTokenByCode({
...this.authCodeRequest,
scopes: requestScopes,
code: authCode
code: authCode || ''
})
.catch((e: AuthError) => {
throw e;
Expand All @@ -429,7 +440,7 @@ export class ElectronAuthenticator {
* @return {*} {Promise<string>}
* @memberof ElectronAuthenticator
*/
private async listenForAuthCode(navigateUrl: string, prompt_type: promptType): Promise<string> {
private async listenForAuthCode(navigateUrl: string, prompt_type: promptType): Promise<string | null> {
this.setAuthWindow(true);
await this.authWindow.loadURL(navigateUrl);
return new Promise((resolve, reject) => {
Expand Down Expand Up @@ -474,20 +485,12 @@ export class ElectronAuthenticator {
* @return {*} {Promise<AccountInfo>}
* @memberof ElectronAuthenticator
*/
private async getAccount(): Promise<AccountInfo> {
private async getAccount(): Promise<AccountDetails> {
const cache = this.clientApplication.getTokenCache();
const currentAccounts = await cache.getAllAccounts();

if (currentAccounts === null) {
return null;
}

if (currentAccounts.length > 1) {
if (currentAccounts?.length >= 1) {
return currentAccounts[0];
} else if (currentAccounts.length === 1) {
return currentAccounts[0];
} else {
return null;
}
return undefined;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@
* -------------------------------------------------------------------------------------------
*/

import { IProvider, Providers, ProviderState, createFromProvider } from '@microsoft/mgt-element';
import {
IProvider,
Providers,
ProviderState,
createFromProvider,
GraphEndpoint,
MICROSOFT_GRAPH_DEFAULT_ENDPOINT
} from '@microsoft/mgt-element';
import { AuthenticationProviderOptions } from '@microsoft/microsoft-graph-client';
import { ipcRenderer } from 'electron';

Expand All @@ -28,8 +35,9 @@ export class ElectronProvider extends IProvider {
return 'MgtElectronProvider';
}

constructor() {
constructor(baseUrl: GraphEndpoint = MICROSOFT_GRAPH_DEFAULT_ENDPOINT) {
super();
this.baseURL = baseUrl;
this.graph = createFromProvider(this);
this.setupProvider();
}
Expand Down
Loading

0 comments on commit 7742373

Please sign in to comment.