Skip to content

Commit 62bed08

Browse files
authored
Merge branch 'MicrosoftDocs:main' into main
2 parents f941e04 + b9b0fa9 commit 62bed08

File tree

69 files changed

+2039
-1197
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+2039
-1197
lines changed

.openpublishing.publish.config.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,12 @@
884884
"branch": "main",
885885
"branch_mapping": {}
886886
},
887+
{
888+
"path_to_root": "ms-identity-python-webapp-tutorial",
889+
"url": "https://github.com/Azure-Samples/ms-identity-python-webapp",
890+
"branch": "0.5.0",
891+
"branch_mapping": {}
892+
},
887893
{
888894
"path_to_root": "ms-identity-node",
889895
"url": "https://github.com/Azure-Samples/ms-identity-node",

articles/active-directory/develop/scenario-web-app-call-api-acquire-token.md

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -182,26 +182,34 @@ public ModelAndView getUserFromGraph(HttpServletRequest httpRequest, HttpServlet
182182

183183
# [Python](#tab/python)
184184

185-
In the Python sample, the code that calls Microsoft Graph is in [app.py#L53-L62](https://github.com/Azure-Samples/ms-identity-python-webapp/blob/48637475ed7d7733795ebeac55c5d58663714c60/app.py#L53-L62).
186-
187-
The code attempts to get a token from the token cache. Then, after setting the authorization header, it calls the web API. If it can't get a token, it signs the user in again.
188-
189-
```python
190-
@app.route("/graphcall")
191-
def graphcall():
192-
token = _get_token_from_cache(app_config.SCOPE)
193-
if not token:
194-
return redirect(url_for("login"))
195-
graph_data = requests.get( # Use token to call downstream service.
196-
app_config.ENDPOINT,
197-
headers={'Authorization': 'Bearer ' + token['access_token']},
198-
).json()
199-
return render_template('display.html', result=graph_data)
200-
```
185+
In the Python sample, the code that calls the API is in [app.py#L60-71](https://github.com/Azure-Samples/ms-identity-python-webapp/blob/0.5.0/app.py#L60-71).
186+
187+
The code attempts to get a token from the token cache. If it can't get a token, it redirects the user to the sign-in route. Otherwise, it can proceed to call the API.
188+
189+
:::code language="python" source="~/ms-identity-python-webapp-tutorial/app.py" range="60-71":::
201190

202191
---
203192

204193
## Next steps
205194

195+
# [ASP.NET Core](#tab/aspnetcore)
196+
197+
Move on to the next article in this scenario,
198+
[Call a web API](scenario-web-app-call-api-call-api.md?tabs=aspnetcore).
199+
200+
# [ASP.NET](#tab/aspnet)
201+
206202
Move on to the next article in this scenario,
207-
[Call a web API](scenario-web-app-call-api-call-api.md).
203+
[Call a web API](scenario-web-app-call-api-call-api.md?tabs=aspnet).
204+
205+
# [Java](#tab/java)
206+
207+
Move on to the next article in this scenario,
208+
[Call a web API](scenario-web-app-call-api-call-api.md?tabs=java).
209+
210+
# [Python](#tab/python)
211+
212+
Move on to the next article in this scenario,
213+
[Call a web API](scenario-web-app-call-api-call-api.md?tabs=python).
214+
215+
---

articles/active-directory/develop/scenario-web-app-call-api-app-configuration.md

Lines changed: 45 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ Instead of a client secret, you can provide a client certificate. The following
9292

9393
## Startup.cs
9494

95-
Your web app will need to acquire a token for the downstream API. You specify it by adding the `.EnableTokenAcquisitionToCallDownstreamApi()` line after `.AddMicrosoftIdentityWebApp(Configuration)`. This line exposes the `ITokenAcquisition` service that you can use in your controller and page actions. However, as you'll see in the following two options, it can be done more simply. You'll also need to choose a token cache implementation, for example `.AddInMemoryTokenCaches()`, in *Startup.cs*:
95+
Your web app needs to acquire a token for the downstream API. You specify it by adding the `.EnableTokenAcquisitionToCallDownstreamApi()` line after `.AddMicrosoftIdentityWebApp(Configuration)`. This line exposes the `ITokenAcquisition` service that you can use in your controller and page actions. However, as you'll see in the following two options, it can be done more simply. You'll also need to choose a token cache implementation, for example `.AddInMemoryTokenCaches()`, in *Startup.cs*:
9696

9797
```csharp
9898
using Microsoft.Identity.Web;
@@ -113,7 +113,7 @@ Your web app will need to acquire a token for the downstream API. You specify it
113113
}
114114
```
115115

116-
The scopes passed to `EnableTokenAcquisitionToCallDownstreamApi` are optional, and enable your web app to request the scopes and the user's consent to those scopes when they log in. If you don't specify the scopes, *Microsoft.Identity.Web* will enable an incremental consent experience.
116+
The scopes passed to `EnableTokenAcquisitionToCallDownstreamApi` are optional, and enable your web app to request the scopes and the user's consent to those scopes when they sign in. If you don't specify the scopes, *Microsoft.Identity.Web* enables an incremental consent experience.
117117

118118
If you don't want to acquire the token yourself, *Microsoft.Identity.Web* provides two mechanisms for calling a web API from a web app. The option you choose depends on whether you want to call Microsoft Graph or another API.
119119

@@ -172,7 +172,7 @@ To call a web API other than Microsoft Graph, *Microsoft.Identity.Web* provides
172172

173173
As with web APIs, you can choose various token cache implementations. For details, see [Microsoft.Identity.Web - Token cache serialization](https://aka.ms/ms-id-web/token-cache-serialization) on GitHub.
174174

175-
The following image shows the various possibilities of *Microsoft.Identity.Web* and their impact on the *Startup.cs* file:
175+
The following image shows the various possibilities of *Microsoft.Identity.Web* and their effect on the *Startup.cs* file:
176176

177177
:::image type="content" source="media/scenarios/microsoft-identity-web-startup-cs.svg" alt-text="Block diagram showing service configuration options in startup dot C S for calling a web API and specifying a token cache implementation":::
178178

@@ -187,7 +187,7 @@ For ASP.NET, you'll subscribe to middleware OIDC events:
187187

188188
- You'll let ASP.NET Core request an authorization code by means of the Open ID Connect middleware. ASP.NET or ASP.NET Core will let the user sign in and consent.
189189
- You'll subscribe the web app to receive the authorization code. This subscription is done by using a C# delegate.
190-
- When the authorization code is received, you'll use MSAL libraries to redeem it. The resulting access tokens and refresh tokens are stored in the token cache. The cache can be used in other parts of the application, such as controllers, to acquire other tokens silently.
190+
- When the authorization code is received, the code uses MSAL libraries to redeem it. The resulting access tokens and refresh tokens are stored in the token cache. The cache can be used in other parts of the application, such as controllers, to acquire other tokens silently.
191191

192192
Code examples in this article and the following one are extracted from the [ASP.NET Web app sample](https://github.com/Azure-Samples/ms-identity-aspnet-webapp-openidconnect). You might want to refer to that sample for full implementation details.
193193

@@ -198,8 +198,9 @@ The sample currently lets MSAL for Java produce the authorization-code URL and h
198198

199199
# [Python](#tab/python)
200200

201-
Code examples in this article and the following one are extracted from the [Python web application calling Microsoft Graph](https://github.com/Azure-Samples/ms-identity-python-webapp), a web-app sample that uses MSAL.Python.
202-
The sample currently lets MSAL.Python produce the authorization-code URL and handles the navigation to the authorization endpoint for the Microsoft identity platform. You might want to refer to the sample for full implementation details.
201+
Code snippets in this article and the following are extracted from the [Python web application calling Microsoft graph](https://github.com/Azure-Samples/ms-identity-python-webapp) sample using the [identity package](https://pypi.org/project/identity/) (a wrapper around MSAL Python).
202+
203+
The sample uses the identity package to produce the authorization-code URL and handles the navigation to the authorization endpoint for the Microsoft identity platform. You might want to refer to the sample for full implementation details.
203204

204205
---
205206

@@ -211,7 +212,7 @@ Microsoft.Identity.Web simplifies your code by setting the correct OpenID Connec
211212

212213
# [ASP.NET](#tab/aspnet)
213214

214-
ASP.NET handles things similarly to ASP.NET Core, except that the configuration of OpenID Connect and the subscription to the `OnAuthorizationCodeReceived` event happen in the [App_Start\Startup.Auth.cs](https://github.com/Azure-Samples/ms-identity-aspnet-webapp-openidconnect/blob/a2da310539aa613b77da1f9e1c17585311ab22b7/WebApp/App_Start/Startup.Auth.cs) file. The concepts are also similar to those in ASP.NET Core, except that in ASP.NET you must specify the `RedirectUri` in [Web.config#L15](https://github.com/Azure-Samples/ms-identity-aspnet-webapp-openidconnect/blob/master/WebApp/Web.config#L15). This configuration is a bit less robust than the one in ASP.NET Core, because you'll need to change it when you deploy your application.
215+
ASP.NET handles things similarly to ASP.NET Core, except that the configuration of OpenID Connect and the subscription to the `OnAuthorizationCodeReceived` event happen in the [App_Start\Startup.Auth.cs](https://github.com/Azure-Samples/ms-identity-aspnet-webapp-openidconnect/blob/a2da310539aa613b77da1f9e1c17585311ab22b7/WebApp/App_Start/Startup.Auth.cs) file. The concepts are also similar to those in ASP.NET Core, except that in ASP.NET you must specify the `RedirectUri` in [Web.config#L15](https://github.com/Azure-Samples/ms-identity-aspnet-webapp-openidconnect/blob/master/WebApp/Web.config#L15). This configuration is a bit less robust than the one in ASP.NET Core, because you need to change it when you deploy your application.
215216

216217
Here's the code for Startup.Auth.cs:
217218

@@ -351,29 +352,17 @@ The `getAuthResultByAuthCode` method is defined in [AuthHelper.java#L176](https:
351352

352353
# [Python](#tab/python)
353354

354-
The authorization code flow is requested as shown in [Web app that signs in users: Code configuration](scenario-web-app-sign-user-app-configuration.md?tabs=python#initialization-code). The code is then received on the `authorized` function, which Flask routes from the `/getAToken` URL. See [app.py#L30-L44](https://github.com/Azure-Samples/ms-identity-python-webapp/blob/e03be352914bfbd58be0d4170eba1fb7a4951d84/app.py#L30-L44) for the full context of this code:
355-
356-
```python
357-
@app.route("/getAToken") # Its absolute URL must match your app's redirect_uri set in AAD.
358-
def authorized():
359-
if request.args['state'] != session.get("state"):
360-
return redirect(url_for("login"))
361-
cache = _load_cache()
362-
result = _build_msal_app(cache).acquire_token_by_authorization_code(
363-
request.args['code'],
364-
scopes=app_config.SCOPE, # Misspelled scope would cause an HTTP 400 error here.
365-
redirect_uri=url_for("authorized", _external=True))
366-
if "error" in result:
367-
return "Login failure: %s, %s" % (
368-
result["error"], result.get("error_description"))
369-
session["user"] = result.get("id_token_claims")
370-
_save_cache(cache)
371-
return redirect(url_for("index"))
372-
```
355+
See [Web app that signs in users: Code configuration](scenario-web-app-sign-user-app-configuration.md?tabs=python#initialization-code) to understand how the Python sample gets the authorization code.
356+
357+
The Microsoft sign-in screen sends the authorization code to the `/getAToken` URL that was specified in the app registration. The `auth_response` route handles that URL, calling `auth.complete_login` to process the authorization code, and then either returning an error or redirecting to the home page.
358+
359+
:::code language="python" source="~/ms-identity-python-webapp-tutorial/app.py" range="36-41":::
360+
361+
See [app.py](https://github.com/Azure-Samples/ms-identity-python-webapp/blob/0.5.0/app.py#L36-41) for the full context of that code.
373362

374363
---
375364

376-
Instead of a client secret, the confidential client application can also prove its identity by using a client certificate, or a client assertion.
365+
Instead of a client secret, the confidential client application can also prove its identity by using a client certificate or a client assertion.
377366
The use of client assertions is an advanced scenario, detailed in [Client assertions](msal-net-client-assertions.md).
378367

379368
## Token cache
@@ -384,7 +373,7 @@ The use of client assertions is an advanced scenario, detailed in [Client assert
384373
385374
# [ASP.NET Core](#tab/aspnetcore)
386375

387-
The ASP.NET core tutorial uses dependency injection to let you decide the token cache implementation in the Startup.cs file for your application. Microsoft.Identity.Web comes with pre-built token-cache serializers described in [Token cache serialization](msal-net-token-cache-serialization.md). An interesting possibility is to choose ASP.NET Core [distributed memory caches](/aspnet/core/performance/caching/distributed#distributed-memory-cache):
376+
The ASP.NET core tutorial uses dependency injection to let you decide the token cache implementation in the Startup.cs file for your application. Microsoft.Identity.Web comes with prebuilt token-cache serializers described in [Token cache serialization](msal-net-token-cache-serialization.md). An interesting possibility is to choose ASP.NET Core [distributed memory caches](/aspnet/core/performance/caching/distributed#distributed-memory-cache):
388377

389378
```csharp
390379
// Use a distributed token cache by adding:
@@ -423,7 +412,7 @@ The web-app implementation can use the ASP.NET session or the server memory. For
423412

424413

425414
First, to use these implementations:
426-
- add the Microsoft.Identity.Web NuGet package. These token cache serializers are not brought in MSAL.NET directly to avoid unwanted dependencies. In addition to a higher level for ASP.NET Core, Microsoft.Identity.Web brings classes that are helpers for MSAL.NET,
415+
- add the Microsoft.Identity.Web NuGet package. These token cache serializers aren't brought in MSAL.NET directly to avoid unwanted dependencies. In addition to a higher level for ASP.NET Core, Microsoft.Identity.Web brings classes that are helpers for MSAL.NET,
427416
- In your code, use the Microsoft.Identity.Web namespace:
428417

429418
```csharp
@@ -453,7 +442,7 @@ public static class MsalAppBuilder
453442
}
454443
```
455444

456-
Instead of `clientapp.AddInMemoryTokenCache()`, you can also use more advanced cache serialization implementations like Redis, SQL, CosmosDB, or distributed memory. Here's an example for Redis:
445+
Instead of `clientapp.AddInMemoryTokenCache()`, you can also use more advanced cache serialization implementations like Redis, SQL, Cosmos DB, or distributed memory. Here's an example for Redis:
457446

458447
```csharp
459448
clientapp.AddDistributedTokenCache(services =>
@@ -503,41 +492,40 @@ The detail of the `SessionManagementHelper` class is provided in the [MSAL sampl
503492

504493
# [Python](#tab/python)
505494

506-
In the Python sample, one cache per account is ensured by recreating a confidential client application for each request and then serializing it in the Flask session cache:
507-
508-
```python
509-
from flask import Flask, render_template, session, request, redirect, url_for
510-
from flask_session import Session # https://pythonhosted.org/Flask-Session
511-
import msal
512-
import app_config
495+
In the Python sample, the identity package takes care of the token cache, using the global `session` object for storage.
513496

497+
Flask has built-in support for sessions stored in a cookie, but due to the length of the identity cookies, the sample uses the [Flask-session](https://flask-session.readthedocs.io/) package instead. Everything is initialized in *app.py*:
514498

515-
app = Flask(__name__)
516-
app.config.from_object(app_config)
517-
Session(app)
499+
:::code language="python" source="~/ms-identity-python-webapp-tutorial/app.py" range="1-11,19-25" highlight="5,11":::
518500

519-
# Code omitted here for simplicity
501+
Due to the `SESSION_TYPE="filesystem"` setting in `app_config.py`, the Flask-session package stores sessions using the local file system.
520502

521-
def _load_cache():
522-
cache = msal.SerializableTokenCache()
523-
if session.get("token_cache"):
524-
cache.deserialize(session["token_cache"])
525-
return cache
526-
527-
def _save_cache(cache):
528-
if cache.has_state_changed:
529-
session["token_cache"] = cache.serialize()
530-
531-
def _build_msal_app(cache=None):
532-
return msal.ConfidentialClientApplication(
533-
app_config.CLIENT_ID, authority=app_config.AUTHORITY,
534-
client_credential=app_config.CLIENT_SECRET, token_cache=cache)
535-
```
503+
For production, you should use [a setting](https://flask-session.readthedocs.io/en/latest/#configuration) that persists across multiple instances and deploys of your app, such as "sqlachemy" or "redis".
536504

537505
---
538506

539507
## Next steps
540508

541509
At this point, when the user signs in, a token is stored in the token cache. Let's see how it's then used in other parts of the web app.
542510

543-
[Remove accounts from the cache on global sign-out](scenario-web-app-call-api-sign-in.md)
511+
# [ASP.NET Core](#tab/aspnetcore)
512+
513+
Move on to the next article in this scenario,
514+
[Remove accounts from the cache on global sign out](scenario-web-app-call-api-sign-in.md?tabs=aspnetcore).
515+
516+
# [ASP.NET](#tab/aspnet)
517+
518+
Move on to the next article in this scenario,
519+
[Remove accounts from the cache on global sign out](scenario-web-app-call-api-sign-in.md?tabs=aspnet).
520+
521+
# [Java](#tab/java)
522+
523+
Move on to the next article in this scenario,
524+
[Remove accounts from the cache on global sign out](scenario-web-app-call-api-sign-in.md?tabs=java).
525+
526+
# [Python](#tab/python)
527+
528+
Move on to the next article in this scenario,
529+
[Remove accounts from the cache on global sign out](scenario-web-app-call-api-sign-in.md?tabs=python).
530+
531+
---

articles/active-directory/develop/scenario-web-app-call-api-call-api.md

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -179,18 +179,10 @@ private String getUserInfoFromGraph(String accessToken) throws Exception {
179179

180180
# [Python](#tab/python)
181181

182-
```python
183-
@app.route("/graphcall")
184-
def graphcall():
185-
token = _get_token_from_cache(app_config.SCOPE)
186-
if not token:
187-
return redirect(url_for("login"))
188-
graph_data = requests.get( # Use token to call downstream service.
189-
app_config.ENDPOINT,
190-
headers={'Authorization': 'Bearer ' + token['access_token']},
191-
).json()
192-
return render_template('display.html', result=graph_data)
193-
```
182+
After successfully retrieving a token, the code uses the requests package to query the API endpoint and retrieve a JSON result.
183+
184+
:::code language="python" source="~/ms-identity-python-webapp-tutorial/app.py" range="60-71" highlight="7-11":::
185+
194186

195187
---
196188

0 commit comments

Comments
 (0)