Skip to content

Commit 55d7355

Browse files
committed
adding graph sdk to sample 2-1
1 parent 9359475 commit 55d7355

File tree

15 files changed

+437
-38
lines changed

15 files changed

+437
-38
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,7 @@ obj/
111111
.vs
112112

113113
# VS Code cache
114-
.vscode
114+
.vscode
115+
116+
# NPM cache
117+
package-lock.json

1-Authentication/1-sign-in/SPA/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
"codelyzer": "^6.0.0",
3838
"jasmine-core": "~3.6.0",
3939
"jasmine-spec-reporter": "~5.0.0",
40-
"karma": "~5.1.0",
40+
"karma": "^6.3.2",
4141
"karma-chrome-launcher": "~3.1.0",
4242
"karma-coverage": "~2.0.3",
4343
"karma-jasmine": "~4.0.0",

1-Authentication/2-sign-in-b2c/SPA/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
"codelyzer": "^6.0.0",
3838
"jasmine-core": "~3.6.0",
3939
"jasmine-spec-reporter": "~5.0.0",
40-
"karma": "~5.1.0",
40+
"karma": "^6.3.2",
4141
"karma-chrome-launcher": "~3.1.0",
4242
"karma-coverage": "~2.0.3",
4343
"karma-jasmine": "~4.0.0",

2-Authorization-I/1-call-graph/README.md

Lines changed: 135 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
## Overview
1717

18-
This sample demonstrates an Angular single-page application (SPA) that lets users sign-in with Azure Active Directory (Azure AD) using the [Microsoft Authentication Library for Angular](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-angular) (MSAL Angular).
18+
This sample demonstrates an Angular single-page application (SPA) that lets users sign-in with Azure Active Directory (Azure AD) using the [Microsoft Authentication Library for Angular](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-angular) (MSAL Angular). In addition, this sample also demonstrates how to user [Microsoft Graph JavaScript SDK](https://github.com/microsoftgraph/msgraph-sdk-javascript) client with MSAL as authentication provider to call the Graph API.
1919

2020
Here you'll learn about [Access Tokens](https://docs.microsoft.com/azure/active-directory/develop/access-tokens), [acquiring a token](https://docs.microsoft.com/azure/active-directory/develop/scenario-spa-acquire-token), [calling a protected web API](https://docs.microsoft.com/azure/active-directory/develop/scenario-spa-call-api), as well as [Dynamic Scopes and Incremental Consent](https://docs.microsoft.com/azure/active-directory/develop/v2-permissions-and-consent), **silent token acquisition**, **working with multiple resources** and more.
2121

@@ -28,11 +28,12 @@ Here you'll learn about [Access Tokens](https://docs.microsoft.com/azure/active-
2828

2929
## Contents
3030

31-
| File/folder | Description |
32-
|---------------------------------|-----------------------------------------------------------|
33-
| `src/app/auth-config.ts` | Authentication parameters reside here. |
34-
| `src/app/app.module.ts` | MSAL-Angular configuration parameters reside here. |
35-
| `src/app/app-routing.module.ts` | Configure your MSAL-Guard here. |
31+
| File/folder | Description |
32+
|---------------------------------|-----------------------------------------------------------------------|
33+
| `src/app/auth-config.ts` | Authentication parameters reside here. |
34+
| `src/app/app.module.ts` | MSAL-Angular configuration parameters reside here. |
35+
| `src/app/app-routing.module.ts` | Configure your MSAL-Guard here. |
36+
| `src/app/graph.service.ts` | Instantiates Graph SDK client using a custom authentication provider. |
3637

3738
## Prerequisites
3839

@@ -299,6 +300,134 @@ In the code snippet above, the user will be prompted for consent once they authe
299300

300301
Clients should treat access tokens as opaque strings, as the contents of the token are intended for the **resource only** (such as a web API or Microsoft Graph). For validation and debugging purposes, developers can decode **JWT**s (*JSON Web Tokens*) using a site like [jwt.ms](https://jwt.ms).
301302

303+
### Calling the Microsoft Graph API
304+
305+
[Microsoft Graph JavaScript SDK](https://github.com/microsoftgraph/msgraph-sdk-javascript) provides various utility methods to query the Graph API. While the SDK has a default authentication provider that can be used in basic scenarios, it can also be extended to use with a custom authentication provider such as MSAL. To do so, we will initialize the Graph SDK client with [clientOptions](https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/docs/CreatingClientInstance.md) method, which contains an `authProvider` object of class **MyAuthenticationProvider** that handles the token acquisition process for the client. We offer this as a service to other components as shown below:
306+
307+
```typescript
308+
export class GraphService {
309+
310+
constructor(private authService: MsalService, private msalBroadcastService: MsalBroadcastService) { }
311+
312+
getGraphClient = (providerOptions: ProviderOptions) => {
313+
314+
/**
315+
* Pass the instance as authProvider in ClientOptions to instantiate the Client which will create and set the default middleware chain.
316+
* For more information, visit: https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/docs/CreatingClientInstance.md
317+
*/
318+
let clientOptions = {
319+
authProvider: new MyAuthenticationProvider(providerOptions, this.authService, this.msalBroadcastService),
320+
};
321+
322+
const graphClient = Client.initWithMiddleware(clientOptions);
323+
324+
return graphClient;
325+
}
326+
}
327+
```
328+
329+
**MyAuthenticationProvider** class needs to implement the [IAuthenticationProvider](https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/src/IAuthenticationProvider.ts) interface, which can be done as shown below:
330+
331+
```typescript
332+
class MyAuthenticationProvider implements AuthenticationProvider {
333+
334+
account;
335+
scopes;
336+
interactionType;
337+
338+
private readonly _destroying$ = new Subject<void>();
339+
340+
constructor(providerOptions: ProviderOptions, private authService: MsalService, private msalBroadcastService: MsalBroadcastService) {
341+
this.account = providerOptions.account;
342+
this.scopes = providerOptions.scopes;
343+
this.interactionType = providerOptions.interactionType;
344+
}
345+
346+
/**
347+
* This method will get called before every request to the ms graph server
348+
* This should return a Promise that resolves to an accessToken (in case of success) or rejects with error (in case of failure)
349+
* Basically this method will contain the implementation for getting and refreshing accessTokens
350+
*/
351+
getAccessToken(): Promise<string> {
352+
return new Promise(async (resolve, reject) => {
353+
let response: AuthenticationResult;
354+
355+
try {
356+
response = await this.authService.instance.acquireTokenSilent({
357+
account: this.account,
358+
scopes: this.scopes
359+
});
360+
361+
if (response.accessToken) {
362+
resolve(response.accessToken);
363+
} else {
364+
reject(Error('Failed to acquire an access token'));
365+
}
366+
} catch (error) {
367+
// in case if silent token acquisition fails, fallback to an interactive method
368+
if (error instanceof InteractionRequiredAuthError) {
369+
switch (this.interactionType) {
370+
case InteractionType.Popup:
371+
response = await this.authService.instance.acquireTokenPopup({
372+
scopes: this.scopes
373+
});
374+
375+
if (response.accessToken) {
376+
resolve(response.accessToken);
377+
} else {
378+
reject(Error('Failed to acquire an access token'));
379+
}
380+
381+
break;
382+
383+
case InteractionType.Redirect:
384+
this.authService.instance.acquireTokenRedirect({
385+
scopes: this.scopes
386+
});
387+
388+
this.msalBroadcastService.msalSubject$.pipe(
389+
filter((msg: EventMessage) => msg.eventType === EventType.ACQUIRE_TOKEN_SUCCESS),
390+
takeUntil(this._destroying$)
391+
).subscribe((result: EventMessage) => {
392+
response = result.payload as AuthenticationResult;
393+
394+
if (response.accessToken) {
395+
resolve(response.accessToken);
396+
} else {
397+
reject(Error('Failed to acquire an access token'));
398+
}
399+
});
400+
break;
401+
402+
default:
403+
break;
404+
}
405+
}
406+
}
407+
});
408+
}
409+
}
410+
```
411+
412+
See [graph.service.ts](./SPA/src/app/graph.service.ts). The Graph client then can be used in your components as shown below:
413+
414+
```typescript
415+
const providerOptions: ProviderOptions = {
416+
account: this.authService.instance.getActiveAccount()!,
417+
scopes: protectedResources.graphMe.scopes,
418+
interactionType: InteractionType.Popup
419+
};
420+
421+
this.graphService.getGraphClient(providerOptions)
422+
.api('/me').get()
423+
.then((profileResponse: ProfileType) => {
424+
// do something with response
425+
})
426+
.catch((error) => {
427+
// handle errors
428+
});
429+
```
430+
302431
## More information
303432

304433
- [Microsoft identity platform (Azure Active Directory for developers)](https://docs.microsoft.com/azure/active-directory/develop/)

2-Authorization-I/1-call-graph/Readme-incremental.md

Lines changed: 135 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
## Overview
1616

17-
This sample demonstrates an Angular single-page application (SPA) that lets users sign-in with Azure Active Directory (Azure AD) using the [Microsoft Authentication Library for Angular](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-angular) (MSAL Angular).
17+
This sample demonstrates an Angular single-page application (SPA) that lets users sign-in with Azure Active Directory (Azure AD) using the [Microsoft Authentication Library for Angular](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-angular) (MSAL Angular). In addition, this sample also demonstrates how to user [Microsoft Graph JavaScript SDK](https://github.com/microsoftgraph/msgraph-sdk-javascript) client with a MSAL as authentication provider to call the Graph API.
1818

1919
Here you'll learn about [Access Tokens](https://docs.microsoft.com/azure/active-directory/develop/access-tokens), [acquiring a token](https://docs.microsoft.com/azure/active-directory/develop/scenario-spa-acquire-token), [calling a protected web API](https://docs.microsoft.com/azure/active-directory/develop/scenario-spa-call-api), as well as [Dynamic Scopes and Incremental Consent](https://docs.microsoft.com/azure/active-directory/develop/v2-permissions-and-consent), **silent token acquisition**, **working with multiple resources** and more.
2020

@@ -27,11 +27,12 @@ Here you'll learn about [Access Tokens](https://docs.microsoft.com/azure/active-
2727

2828
## Contents
2929

30-
| File/folder | Description |
31-
|---------------------------------|-----------------------------------------------------------|
32-
| `src/app/auth-config.ts` | Authentication parameters reside here. |
33-
| `src/app/app.module.ts` | MSAL-Angular configuration parameters reside here. |
34-
| `src/app/app-routing.module.ts` | Configure your MSAL-Guard here. |
30+
| File/folder | Description |
31+
|---------------------------------|-----------------------------------------------------------------------|
32+
| `src/app/auth-config.ts` | Authentication parameters reside here. |
33+
| `src/app/app.module.ts` | MSAL-Angular configuration parameters reside here. |
34+
| `src/app/app-routing.module.ts` | Configure your MSAL-Guard here. |
35+
| `src/app/graph.service.ts` | Instantiates Graph SDK client using a custom authentication provider. |
3536

3637
## Setup
3738

@@ -267,6 +268,134 @@ In the code snippet above, the user will be prompted for consent once they authe
267268

268269
Clients should treat access tokens as opaque strings, as the contents of the token are intended for the **resource only** (such as a web API or Microsoft Graph). For validation and debugging purposes, developers can decode **JWT**s (*JSON Web Tokens*) using a site like [jwt.ms](https://jwt.ms).
269270

271+
### Calling the Microsoft Graph API
272+
273+
[Microsoft Graph JavaScript SDK](https://github.com/microsoftgraph/msgraph-sdk-javascript) provides various utility methods to query the Graph API. While the SDK has a default authentication provider that can be used in basic scenarios, it can also be extended to use with a custom authentication provider such as MSAL. To do so, we will initialize the Graph SDK client with [clientOptions](https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/docs/CreatingClientInstance.md) method, which contains an `authProvider` object of class **MyAuthenticationProvider** that handles the token acquisition process for the client. We offer this as a service to other components as shown below:
274+
275+
```typescript
276+
export class GraphService {
277+
278+
constructor(private authService: MsalService, private msalBroadcastService: MsalBroadcastService) { }
279+
280+
getGraphClient = (providerOptions: ProviderOptions) => {
281+
282+
/**
283+
* Pass the instance as authProvider in ClientOptions to instantiate the Client which will create and set the default middleware chain.
284+
* For more information, visit: https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/docs/CreatingClientInstance.md
285+
*/
286+
let clientOptions = {
287+
authProvider: new MyAuthenticationProvider(providerOptions, this.authService, this.msalBroadcastService),
288+
};
289+
290+
const graphClient = Client.initWithMiddleware(clientOptions);
291+
292+
return graphClient;
293+
}
294+
}
295+
```
296+
297+
**MyAuthenticationProvider** class needs to implement the [IAuthenticationProvider](https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/src/IAuthenticationProvider.ts) interface, which can be done as shown below:
298+
299+
```typescript
300+
class MyAuthenticationProvider implements AuthenticationProvider {
301+
302+
account;
303+
scopes;
304+
interactionType;
305+
306+
private readonly _destroying$ = new Subject<void>();
307+
308+
constructor(providerOptions: ProviderOptions, private authService: MsalService, private msalBroadcastService: MsalBroadcastService) {
309+
this.account = providerOptions.account;
310+
this.scopes = providerOptions.scopes;
311+
this.interactionType = providerOptions.interactionType;
312+
}
313+
314+
/**
315+
* This method will get called before every request to the ms graph server
316+
* This should return a Promise that resolves to an accessToken (in case of success) or rejects with error (in case of failure)
317+
* Basically this method will contain the implementation for getting and refreshing accessTokens
318+
*/
319+
getAccessToken(): Promise<string> {
320+
return new Promise(async (resolve, reject) => {
321+
let response: AuthenticationResult;
322+
323+
try {
324+
response = await this.authService.instance.acquireTokenSilent({
325+
account: this.account,
326+
scopes: this.scopes
327+
});
328+
329+
if (response.accessToken) {
330+
resolve(response.accessToken);
331+
} else {
332+
reject(Error('Failed to acquire an access token'));
333+
}
334+
} catch (error) {
335+
// in case if silent token acquisition fails, fallback to an interactive method
336+
if (error instanceof InteractionRequiredAuthError) {
337+
switch (this.interactionType) {
338+
case InteractionType.Popup:
339+
response = await this.authService.instance.acquireTokenPopup({
340+
scopes: this.scopes
341+
});
342+
343+
if (response.accessToken) {
344+
resolve(response.accessToken);
345+
} else {
346+
reject(Error('Failed to acquire an access token'));
347+
}
348+
349+
break;
350+
351+
case InteractionType.Redirect:
352+
this.authService.instance.acquireTokenRedirect({
353+
scopes: this.scopes
354+
});
355+
356+
this.msalBroadcastService.msalSubject$.pipe(
357+
filter((msg: EventMessage) => msg.eventType === EventType.ACQUIRE_TOKEN_SUCCESS),
358+
takeUntil(this._destroying$)
359+
).subscribe((result: EventMessage) => {
360+
response = result.payload as AuthenticationResult;
361+
362+
if (response.accessToken) {
363+
resolve(response.accessToken);
364+
} else {
365+
reject(Error('Failed to acquire an access token'));
366+
}
367+
});
368+
break;
369+
370+
default:
371+
break;
372+
}
373+
}
374+
}
375+
});
376+
}
377+
}
378+
```
379+
380+
See [graph.service.ts](./SPA/src/app/graph.service.ts). The Graph client then can be used in your components as shown below:
381+
382+
```typescript
383+
const providerOptions: ProviderOptions = {
384+
account: this.authService.instance.getActiveAccount()!,
385+
scopes: protectedResources.graphMe.scopes,
386+
interactionType: InteractionType.Popup
387+
};
388+
389+
this.graphService.getGraphClient(providerOptions)
390+
.api('/me').get()
391+
.then((profileResponse: ProfileType) => {
392+
// do something with response
393+
})
394+
.catch((error) => {
395+
// handle errors
396+
});
397+
```
398+
270399
## Next Tutorial
271400

272401
Continue with the next tutorial: [Protect and call a web API](../../3-Authorization-II/1-call-api/README-incremental.md).

2-Authorization-I/1-call-graph/SPA/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"@angular/router": "~11.0.0",
2323
"@azure/msal-angular": "^2.0.0",
2424
"@azure/msal-browser": "^2.14.2",
25+
"@microsoft/microsoft-graph-client": "^2.2.1",
2526
"core-js": "^3.8.0",
2627
"rxjs": "~6.6.0",
2728
"tslib": "^2.0.0",
@@ -36,7 +37,7 @@
3637
"codelyzer": "^6.0.0",
3738
"jasmine-core": "~3.6.0",
3839
"jasmine-spec-reporter": "~5.0.0",
39-
"karma": "~5.1.0",
40+
"karma": "^6.3.2",
4041
"karma-chrome-launcher": "~3.1.0",
4142
"karma-coverage": "~2.0.3",
4243
"karma-jasmine": "~4.0.0",

0 commit comments

Comments
 (0)