Skip to content

Commit 1612045

Browse files
authored
Merge pull request Azure-Samples#9 from Azure-Samples/add-graph-sdk
adding graph sdk to sample 2-1
2 parents 9359475 + 9ba3131 commit 1612045

File tree

17 files changed

+397
-40
lines changed

17 files changed

+397
-40
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: 120 additions & 7 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 use [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,118 @@ 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) { }
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),
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+
constructor(providerOptions: ProviderOptions, private authService: MsalService) {
339+
this.account = providerOptions.account;
340+
this.scopes = providerOptions.scopes;
341+
this.interactionType = providerOptions.interactionType;
342+
}
343+
344+
/**
345+
* This method will get called before every request to the ms graph server
346+
* This should return a Promise that resolves to an accessToken (in case of success) or rejects with error (in case of failure)
347+
* Basically this method will contain the implementation for getting and refreshing accessTokens
348+
*/
349+
getAccessToken(): Promise<string> {
350+
return new Promise(async (resolve, reject) => {
351+
let response: AuthenticationResult;
352+
353+
try {
354+
response = await this.authService.instance.acquireTokenSilent({
355+
account: this.account,
356+
scopes: this.scopes
357+
});
358+
359+
if (response.accessToken) {
360+
resolve(response.accessToken);
361+
} else {
362+
reject(Error('Failed to acquire an access token'));
363+
}
364+
} catch (error) {
365+
// in case if silent token acquisition fails, fallback to an interactive method
366+
if (error instanceof InteractionRequiredAuthError) {
367+
switch (this.interactionType) {
368+
case InteractionType.Popup:
369+
response = await this.authService.instance.acquireTokenPopup({
370+
scopes: this.scopes
371+
});
372+
373+
if (response.accessToken) {
374+
resolve(response.accessToken);
375+
} else {
376+
reject(Error('Failed to acquire an access token'));
377+
}
378+
break;
379+
380+
case InteractionType.Redirect:
381+
this.authService.instance.acquireTokenRedirect({
382+
scopes: this.scopes
383+
});
384+
break;
385+
386+
default:
387+
break;
388+
}
389+
}
390+
}
391+
});
392+
}
393+
}
394+
```
395+
396+
See [graph.service.ts](./SPA/src/app/graph.service.ts). The Graph client then can be used in your components as shown below:
397+
398+
```typescript
399+
const providerOptions: ProviderOptions = {
400+
account: this.authService.instance.getActiveAccount()!,
401+
scopes: protectedResources.graphMe.scopes,
402+
interactionType: InteractionType.Popup
403+
};
404+
405+
this.graphService.getGraphClient(providerOptions)
406+
.api('/me').get()
407+
.then((profileResponse: ProfileType) => {
408+
// do something with response
409+
})
410+
.catch((error) => {
411+
// handle errors
412+
});
413+
```
414+
302415
## More information
303416

304417
- [Microsoft identity platform (Azure Active Directory for developers)](https://docs.microsoft.com/azure/active-directory/develop/)
@@ -331,4 +444,4 @@ To provide feedback on or suggest features for Azure Active Directory, visit [Us
331444

332445
If you'd like to contribute to this sample, see [CONTRIBUTING.MD](/CONTRIBUTING.md).
333446

334-
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information, see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
447+
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information, see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

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

Lines changed: 120 additions & 7 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 use [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,118 @@ 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) { }
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+
constructor(providerOptions: ProviderOptions, private authService: MsalService) {
307+
this.account = providerOptions.account;
308+
this.scopes = providerOptions.scopes;
309+
this.interactionType = providerOptions.interactionType;
310+
}
311+
312+
/**
313+
* This method will get called before every request to the ms graph server
314+
* This should return a Promise that resolves to an accessToken (in case of success) or rejects with error (in case of failure)
315+
* Basically this method will contain the implementation for getting and refreshing accessTokens
316+
*/
317+
getAccessToken(): Promise<string> {
318+
return new Promise(async (resolve, reject) => {
319+
let response: AuthenticationResult;
320+
321+
try {
322+
response = await this.authService.instance.acquireTokenSilent({
323+
account: this.account,
324+
scopes: this.scopes
325+
});
326+
327+
if (response.accessToken) {
328+
resolve(response.accessToken);
329+
} else {
330+
reject(Error('Failed to acquire an access token'));
331+
}
332+
} catch (error) {
333+
// in case if silent token acquisition fails, fallback to an interactive method
334+
if (error instanceof InteractionRequiredAuthError) {
335+
switch (this.interactionType) {
336+
case InteractionType.Popup:
337+
response = await this.authService.instance.acquireTokenPopup({
338+
scopes: this.scopes
339+
});
340+
341+
if (response.accessToken) {
342+
resolve(response.accessToken);
343+
} else {
344+
reject(Error('Failed to acquire an access token'));
345+
}
346+
break;
347+
348+
case InteractionType.Redirect:
349+
this.authService.instance.acquireTokenRedirect({
350+
scopes: this.scopes
351+
});
352+
break;
353+
354+
default:
355+
break;
356+
}
357+
}
358+
}
359+
});
360+
}
361+
}
362+
```
363+
364+
See [graph.service.ts](./SPA/src/app/graph.service.ts). The Graph client then can be used in your components as shown below:
365+
366+
```typescript
367+
const providerOptions: ProviderOptions = {
368+
account: this.authService.instance.getActiveAccount()!,
369+
scopes: protectedResources.graphMe.scopes,
370+
interactionType: InteractionType.Popup
371+
};
372+
373+
this.graphService.getGraphClient(providerOptions)
374+
.api('/me').get()
375+
.then((profileResponse: ProfileType) => {
376+
// do something with response
377+
})
378+
.catch((error) => {
379+
// handle errors
380+
});
381+
```
382+
270383
## Next Tutorial
271384

272385
Continue with the next tutorial: [Protect and call a web API](../../3-Authorization-II/1-call-api/README-incremental.md).
@@ -303,4 +416,4 @@ To provide feedback on or suggest features for Azure Active Directory, visit [Us
303416

304417
If you'd like to contribute to this sample, see [CONTRIBUTING.MD](/CONTRIBUTING.md).
305418

306-
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information, see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
419+
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information, see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

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)