Skip to content

Commit 4f8e386

Browse files
committed
edits
1 parent 9688004 commit 4f8e386

File tree

1 file changed

+108
-98
lines changed

1 file changed

+108
-98
lines changed

articles/app-service/tutorial-connect-app-app-graph-javascript.md

Lines changed: 108 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ In this tutorial, you learn how to:
2626

2727
Complete the previous tutorial, [Access Microsoft Graph from a secured JavaScript app as the user](tutorial-auth-aad.md), before starting this tutorial but don't remove the resources at the end of the tutorial. This tutorial assumes you have the two App services and their corresponding authentication apps.
2828

29-
The previous tutorial used the Azure Cloud Shell as the shell for the Azure CLI. This tutorial continues that usage. In order to add the code to the backend app service to exchange the token, this tutorial uses a version of the _code_ editor. Other editors are available such as nano, vim, and emacs.
29+
The previous tutorial used the Azure Cloud Shell as the shell for the Azure CLI. This tutorial continues that usage.
3030

3131
## Architecture
3232

@@ -41,9 +41,13 @@ The tutorial shows how to pass the user credential provided by the frontend app
4141
1. Sign in user to a frontend App service configured to use Active Directory as the identity provider.
4242
1. The frontend App service passes user's token to backend App service.
4343
1. The backend App is secured to allow the frontend to make an API request. The user's access token has an audience for the backend API and scope of `user_impersonation`.
44+
1. The backend app registration already has the Microsoft Graph with the scope `User.Read`. This is added by default to all app registrations.
4445

4546
This tutorial extends the architecture:
4647

48+
* Grant admin consent to bypass the user consent screen for the back-end app.
49+
* Change the application code to convert the access token sent from the front-end app to an access token with the required permission for Microsoft Graph.
50+
4751
1. Provide code to have backend app **exchange token** for new token with scope of downstream Azure service such as Microsoft Graph.
4852
1. Provide code to have backend app **use new token** to access downstream service as the current authenticate user.
4953
1. **Redeploy** backend app with `az webapp up`.
@@ -71,8 +75,6 @@ In this tutorial, in order to read user profile from Microsoft Graph, the back-e
7175

7276
:::image type="content" source="./media/tutorial-connect-app-app-graph-javascript/azure-portal-authentication-app-api-permission-admin-consent-granted.png" alt-text="Screenshot of Azure portal authentication app with admin consent granted in status column.":::
7377

74-
75-
7678
## 2. Install npm packages
7779

7880
In the previous tutorial, the backend app didn't need any npm packages for authentication because the only authentication was provided by configuring the identity provider in the Azure portal. In this tutorial, the signed-in user's access token for the back-end API must be exchanged for an access token with Microsoft Graph in its scope. This exchange is completed with two libraries because this exchange doesn't use App Service authentication anymore, but Azure Active Directory and MSAL.js directly.
@@ -134,125 +136,133 @@ The source code to complete this step is provided for you. Use the following ste
134136
```azurecli-interactive
135137
az webapp up --resource-group myAuthResourceGroup --name <back-end-app-name>
136138
137-
## 4. Exchange backend API token for the Microsoft Graph token
139+
## 4. Review backend code to exchange backend API token for the Microsoft Graph token
138140
139141
In order to change the backend API audience token for a Microsoft Graph token, the backend app needs to find the Tenant ID and use that as part of the MSAL.js configuration object. Because the backend app with configured with Microsoft as the identity provider, the Tenant ID and several other required values are already in the App service app settings.
140142
141143
The following code is already provided for you in the sample app. You need to understand why it's there and how it works so that you can apply this work to other apps you build that need this same functionality.
142144
143145
### Get the Tenant ID
144146
145-
Get the current tenant ID from the `WEBSITE_AUTH_OPENID_ISSUER` environment variable. The ID just needs to be parsed out of the variable with a regular expression.
147+
1. Open the `./backend/src/with-graph/auth.js` file.
146148
147-
```javascript
148-
export function getTenantId() {
149+
2. Review the `getTenantId()` function.
149150
150-
const openIdIssuer = process.env.WEBSITE_AUTH_OPENID_ISSUER;
151-
const backendAppTenantId = openIdIssuer.replace(/https:\/\/sts\.windows\.net\/(.{1,36})\/v2\.0/gm, '$1');
151+
```javascript
152+
export function getTenantId() {
153+
154+
const openIdIssuer = process.env.WEBSITE_AUTH_OPENID_ISSUER;
155+
const backendAppTenantId = openIdIssuer.replace(/https:\/\/sts\.windows\.net\/(.{1,36})\/v2\.0/gm, '$1');
156+
157+
return backendAppTenantId;
158+
}
159+
```
152160
153-
return backendAppTenantId;
154-
}
155-
```
161+
3. This function gets the current tenant ID from the `WEBSITE_AUTH_OPENID_ISSUER` environment variable. The ID is parsed out of the variable with a regular expression.
156162
157163
### Configure MSAL.js
158164
159-
Build the MSAL.js configuration object.
160-
161-
```javascript
162-
// ./backend/src/auth.js
163-
// Exchange current bearerToken for Graph API token
164-
// Env vars were set by App Service
165-
export async function getGraphToken(backEndAccessToken) {
166-
167-
const config = {
168-
// MSAL configuration
169-
auth: {
170-
// the backend's authentication CLIENT ID
171-
clientId: process.env.WEBSITE_AUTH_CLIENT_ID,
172-
// the backend's authentication CLIENT SECRET
173-
clientSecret: process.env.MICROSOFT_PROVIDER_AUTHENTICATION_SECRET,
174-
// OAuth 2.0 authorization endpoint (v2)
175-
// should be: https://login.microsoftonline.com/BACKEND-TENANT-ID
176-
authority: `https://login.microsoftonline.com/${getTenantId()}`
177-
},
178-
// used for debugging
179-
system: {
180-
loggerOptions: {
181-
loggerCallback(loglevel, message, containsPii) {
182-
console.log(message);
183-
},
184-
piiLoggingEnabled: true,
185-
logLevel: MSAL.LogLevel.Verbose,
165+
1. Still in the `./backend/src/with-graph/auth.js` file, review the `getGraphToken()` function.
166+
1. Build the MSAL.js configuration object, use the MSAL configuration to create the clientCredentialAuthority. Configure the on-behalf-off request. Then use the acquireTokenOnBehalfOf to exchange the backend API access token for a Graph access token.
167+
168+
```javascript
169+
// ./backend/src/auth.js
170+
// Exchange current bearerToken for Graph API token
171+
// Env vars were set by App Service
172+
export async function getGraphToken(backEndAccessToken) {
173+
174+
const config = {
175+
// MSAL configuration
176+
auth: {
177+
// the backend's authentication CLIENT ID
178+
clientId: process.env.WEBSITE_AUTH_CLIENT_ID,
179+
// the backend's authentication CLIENT SECRET
180+
clientSecret: process.env.MICROSOFT_PROVIDER_AUTHENTICATION_SECRET,
181+
// OAuth 2.0 authorization endpoint (v2)
182+
// should be: https://login.microsoftonline.com/BACKEND-TENANT-ID
183+
authority: `https://login.microsoftonline.com/${getTenantId()}`
184+
},
185+
// used for debugging
186+
system: {
187+
loggerOptions: {
188+
loggerCallback(loglevel, message, containsPii) {
189+
console.log(message);
190+
},
191+
piiLoggingEnabled: true,
192+
logLevel: MSAL.LogLevel.Verbose,
193+
}
186194
}
195+
};
196+
197+
const clientCredentialAuthority = new MSAL.ConfidentialClientApplication(config);
198+
199+
const oboRequest = {
200+
oboAssertion: backEndAccessToken,
201+
// this scope must already exist on the backend authentication app registration
202+
// and visible in resources.azure.com backend app auth config
203+
scopes: ["https://graph.microsoft.com/.default"]
204+
}
205+
206+
// This example has App service validate token in runtime
207+
// from headers that can't be set externally
208+
209+
// If you aren't using App service's authentication,
210+
// you must validate your access token yourself
211+
// before calling this code
212+
try {
213+
const { accessToken } = await clientCredentialAuthority.acquireTokenOnBehalfOf(oboRequest);
214+
return accessToken;
215+
} catch (error) {
216+
console.log(`getGraphToken:error.type = ${error.type} ${error.message}`);
187217
}
188-
};
189-
190-
const clientCredentialAuthority = new MSAL.ConfidentialClientApplication(config);
191-
192-
const oboRequest = {
193-
oboAssertion: backEndAccessToken,
194-
// this scope must already exist on the backend authentication app registration
195-
// and visible in resources.azure.com backend app auth config
196-
scopes: ["https://graph.microsoft.com/.default"]
197218
}
219+
```
198220
199-
// This example has App service validate token in runtime
200-
// from headers that can't be set externally
201-
202-
// If you aren't using App service's authentication,
203-
// you must validate your access token yourself
204-
// before calling this code
205-
try {
206-
const { accessToken } = await clientCredentialAuthority.acquireTokenOnBehalfOf(oboRequest);
207-
return accessToken;
208-
} catch (error) {
209-
console.log(`getGraphToken:error.type = ${error.type} ${error.message}`);
210-
}
211-
}
212-
```
221+
## 5. Review backend code to access Microsoft Graph with the new token
213222
214-
## 5. Get the user's profile from Microsoft Graph
223+
To access Microsoft Graph as a user signed in to the frontend application, the changes include:
215224
216-
Before the token exchange, remember that the steps included:
217-
* Configuration of the Active Directory app registration with an API permission to Microsoft Graph with the scope `User.Read`.
225+
* Configuration of the Active Directory app registration with an API permission to the downstream service, Microsoft Graph, with the necessary scope of `User.Read`.
218226
* Grant admin consent to bypass the user consent screen for the back-end app.
219-
* Change the application code to convert the access token sent from the front-end app to an access token with the required permission for Microsoft Graph.
227+
* Change the application code to convert the access token sent from the front-end app to an access token with the required permission for the downstream service, Microsoft Graph.
220228
221229
Now that the code has the correct token for Microsoft Graph, use it to create a client to Microsoft Graph then get the user's profile.
222230
223-
The following code is already provided for you in the sample app. You need to understand why it's there and how it works so that you can apply this work to other apps you build that need this same functionality.
224-
225-
```javascript
226-
// ./backend/src/graph.js
227-
import graph from "@microsoft/microsoft-graph-client";
228-
import { getGraphToken } from "./auth.js";
229-
230-
// Create client from token with Graph API scope
231-
export function getAuthenticatedClient(accessToken) {
232-
const client = graph.Client.init({
233-
authProvider: (done) => {
234-
done(null, accessToken);
235-
}
236-
});
237-
238-
return client;
239-
}
240-
export async function getGraphProfile(accessToken) {
241-
// exchange current backend token for token with
242-
// graph api scope
243-
const graphToken = await getGraphToken(accessToken);
231+
1. Open the `./backend/src/graph.js`
244232
245-
// use graph token to get Graph client
246-
const graphClient = getAuthenticatedClient(graphToken);
233+
2. In the `getGraphProfile()` function, get the token, then the authenticated client from the token then get the profile.
247234
248-
// get profile of user
249-
const profile = await graphClient
250-
.api('/me')
251-
.get();
252-
253-
return profile;
254-
}
255-
```
235+
```javascript
236+
//
237+
import graph from "@microsoft/microsoft-graph-client";
238+
import { getGraphToken } from "./auth.js";
239+
240+
// Create client from token with Graph API scope
241+
export function getAuthenticatedClient(accessToken) {
242+
const client = graph.Client.init({
243+
authProvider: (done) => {
244+
done(null, accessToken);
245+
}
246+
});
247+
248+
return client;
249+
}
250+
export async function getGraphProfile(accessToken) {
251+
// exchange current backend token for token with
252+
// graph api scope
253+
const graphToken = await getGraphToken(accessToken);
254+
255+
// use graph token to get Graph client
256+
const graphClient = getAuthenticatedClient(graphToken);
257+
258+
// get profile of user
259+
const profile = await graphClient
260+
.api('/me')
261+
.get();
262+
263+
return profile;
264+
}
265+
```
256266
257267
## 6. Test your changes
258268
@@ -271,7 +281,7 @@ export async function getGraphProfile(accessToken) {
271281
272282
#### I got an error `80049217`, what does it mean?
273283
274-
This error, `CompactToken parsing failed with error code: 80049217`, means the backend App service isn't authorized to return the Microsoft Graph token.
284+
This error, `CompactToken parsing failed with error code: 80049217`, means the backend App service isn't authorized to return the Microsoft Graph token. This error is caused because the app registration is missing the `User.Read` permission.
275285
276286
#### I got an error `AADSTS65001`, what does it mean?
277287

0 commit comments

Comments
 (0)