Skip to content

Commit 564dec3

Browse files
authored
2-4-Sovereign-Call-MSGraph updated to latest template (#417)
* updating front end packages * updating views * updating views * updating example to match templates * updating apis * add settings * updating the readme * updating packages * fix merge
1 parent 1910095 commit 564dec3

File tree

72 files changed

+40198
-24384
lines changed

Some content is hidden

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

72 files changed

+40198
-24384
lines changed
Lines changed: 71 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,71 @@
1-
using System.Diagnostics;
2-
using System.Threading.Tasks;
3-
using Microsoft.AspNetCore.Authorization;
4-
using Microsoft.AspNetCore.Mvc;
5-
using Microsoft.Identity.Web;
6-
using WebApp_OpenIDConnect_DotNet.Models;
7-
using WebApp_OpenIDConnect_DotNet.Services.GraphOperations;
8-
using Constants = WebApp_OpenIDConnect_DotNet.Infrastructure.Constants;
9-
10-
namespace WebApp_OpenIDConnect_DotNet.Controllers
11-
{
12-
[Authorize]
13-
public class HomeController : Controller
14-
{
15-
readonly ITokenAcquisition tokenAcquisition;
16-
private readonly IGraphApiOperations graphApiOperations;
17-
18-
public HomeController(ITokenAcquisition tokenAcquisition,
19-
IGraphApiOperations graphApiOperations)
20-
{
21-
this.tokenAcquisition = tokenAcquisition;
22-
this.graphApiOperations = graphApiOperations;
23-
}
24-
25-
public IActionResult Index()
26-
{
27-
return View();
28-
}
29-
30-
[AuthorizeForScopes(Scopes = new[] { Constants.ScopeUserRead})]
31-
public async Task<IActionResult> Profile()
32-
{
33-
var accessToken =
34-
await tokenAcquisition.GetAccessTokenForUserAsync(new[] {Constants.ScopeUserRead});
35-
36-
var me = await graphApiOperations.GetUserInformation(accessToken);
37-
var photo = await graphApiOperations.GetPhotoAsBase64Async(accessToken);
38-
39-
ViewData["Me"] = me;
40-
ViewData["Photo"] = photo;
41-
42-
return View();
43-
}
44-
45-
[AllowAnonymous]
46-
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
47-
public IActionResult Error()
48-
{
49-
return View(new ErrorViewModel {RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier});
50-
}
51-
}
52-
}
1+
using System;
2+
using System.Diagnostics;
3+
using System.Threading.Tasks;
4+
using Microsoft.AspNetCore.Authorization;
5+
using Microsoft.Identity.Web;
6+
using Microsoft.Graph;
7+
using Microsoft.AspNetCore.Mvc;
8+
using Microsoft.Extensions.Logging;
9+
using WebApp_OpenIDConnect_DotNet.Models;
10+
using System.IO;
11+
12+
namespace WebApp_OpenIDConnect_DotNet.Controllers
13+
{
14+
[Authorize]
15+
public class HomeController : Controller
16+
{
17+
private readonly ILogger<HomeController> _logger;
18+
19+
private readonly GraphServiceClient _graphServiceClient;
20+
21+
public HomeController(ILogger<HomeController> logger,
22+
GraphServiceClient graphServiceClient)
23+
{
24+
_logger = logger;
25+
_graphServiceClient = graphServiceClient;
26+
}
27+
28+
[AuthorizeForScopes(ScopeKeySection = "DownstreamApi:Scopes")]
29+
public async Task<IActionResult> Index()
30+
{
31+
var user = await _graphServiceClient.Me.Request().GetAsync();
32+
ViewData["ApiResult"] = user.DisplayName;
33+
34+
return View();
35+
}
36+
37+
[AuthorizeForScopes(ScopeKeySection = "DownstreamApi:Scopes")]
38+
public async Task<IActionResult> Profile()
39+
{
40+
var me = await _graphServiceClient.Me.Request().GetAsync();
41+
ViewData["Me"] = me;
42+
43+
try
44+
{
45+
// Get user photo
46+
using (var photoStream = await _graphServiceClient.Me.Photo.Content.Request().GetAsync())
47+
{
48+
byte[] photoByte = ((MemoryStream)photoStream).ToArray();
49+
ViewData["Photo"] = Convert.ToBase64String(photoByte);
50+
}
51+
}
52+
catch (System.Exception)
53+
{
54+
ViewData["Photo"] = null;
55+
}
56+
57+
return View();
58+
}
59+
public IActionResult Privacy()
60+
{
61+
return View();
62+
}
63+
64+
[AllowAnonymous]
65+
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
66+
public IActionResult Error()
67+
{
68+
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
69+
}
70+
}
71+
}

2-WebApp-graph-user/2-4-Sovereign-Call-MSGraph/Infrastructure/Constants.cs

Lines changed: 0 additions & 8 deletions
This file was deleted.

2-WebApp-graph-user/2-4-Sovereign-Call-MSGraph/README.md

Lines changed: 81 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,13 @@ Go to the `"2-WebApp-graph-user\2-4-Sovereign-Call-MSGraph"` folder
6464
- the `TenantId` by `organizations`, as here you chose to sign-in users with their work or school account. In case you want to sign-in different audiences, refer back to the first phase of the tutorial
6565
- and the `ClientSecret` by the client secret you generated in Step 1.
6666

67-
- The `GraphApiUrl` for US Government cloud is
67+
- The `DownstreamApi:BaseUrl` for US Government cloud is
6868

69-
```JSon
70-
"GraphApiUrl": "https://graph.microsoft.us"
71-
```
69+
```JSon
70+
"DownstreamApi": {
71+
"BaseUrl": "https://graph.microsoft.us/beta"
72+
}
73+
```
7274
In case you want to deploy your app in other sovereign or national clouds, go to [Microsoft Graph service root endpoints](https://docs.microsoft.com/en-us/graph/deployments#microsoft-graph-and-graph-explorer-service-root-endpoints).
7375

7476
### Step 3: Run the sample
@@ -92,42 +94,35 @@ Starting from the [previous phase of the tutorial](../../1-WebApp-OIDC), the cod
9294
After the following lines in the ConfigureServices(IServiceCollection services) method, replace `services.AddAzureAdV2Authentication(Configuration);`, by the following lines:
9395

9496
```CSharp
95-
public void ConfigureServices(IServiceCollection services)
97+
public void ConfigureServices(IServiceCollection services)
9698
{
97-
. . .
98-
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
99-
.AddMicrosoftIdentityWebApp(Configuration)
100-
101-
// Token acquisition service based on MSAL.NET
102-
// and chosen token cache implementation
103-
.EnableTokenAcquisitionToCallDownstreamApi(new string[] { Constants.ScopeUserRead })
104-
.AddInMemoryTokenCaches();
99+
services.AddOptions();
100+
101+
string[] initialScopes = Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');
102+
103+
// Token acquisition service based on MSAL.NET
104+
// and chosen token cache implementation
105+
106+
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
107+
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))
108+
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
109+
.AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
110+
.AddInMemoryTokenCaches();
105111
```
106112

107113
The two new lines of code:
108114

109115
- enable MSAL.NET to hook-up to the OpenID Connect events and redeem the authorization code obtained by the ASP.NET Core middleware and after obtaining a token, saves it into the token cache, for use by the Controllers.
110116
- Decide which token cache implementation to use. In this part of the phase, we'll use a simple in memory token cache, but next steps will show you other implementations you can benefit from, including distributed token caches based on a SQL database, or a Redis cache.
111117

112-
### Add additional files to call Microsoft Graph
113-
114-
Add the `Services\Microsoft-Graph-Rest\*.cs` files. This is an implementation of a custom service, which encapsulates the call to the Microsoft Graph /me endpoint. Given an access token for Microsoft Graph, it's capable of getting the user information and the photo of the user.
115-
116-
```CSharp
117-
public interface IGraphApiOperations
118-
{
119-
Task<dynamic> GetUserInformation(string accessToken);
120-
Task<string> GetPhotoAsBase64Async(string accessToken);
121-
}
122-
```
123118

124119
### Update the `Startup.cs` file to enable the Microsoft Graph custom service
125120

126121
Still in the `Startup.cs` file, add the following lines just after the following. This line ensures that the GraphAPIService benefits from the optimized `HttpClient` management by ASP.NET Core.
127122

128123
```CSharp
129124
// Add Graph
130-
services.AddGraphService(Configuration);
125+
.AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
131126
```
132127

133128
### Change the controller code to acquire a token and call Microsoft Graph
@@ -136,26 +131,28 @@ In the `Controllers\HomeController.cs`file:
136131

137132
1. Add a constructor to HomeController, making the ITokenAcquisition service available (used by the ASP.NET dependency injection mechanism)
138133

139-
```CSharp
140-
public HomeController(ITokenAcquisition tokenAcquisition, IGraphApiOperations graphApiOperations)
141-
{
142-
this.tokenAcquisition = tokenAcquisition;
143-
this.graphApiOperations = graphApiOperations;
134+
```CSharp
135+
private readonly ILogger<HomeController> _logger;
136+
137+
private readonly GraphServiceClient _graphServiceClient;
144138

145-
}
146-
private ITokenAcquisition tokenAcquisition;
147-
private readonly IGraphApiOperations graphApiOperations;
148-
```
139+
public HomeController(ILogger<HomeController> logger,
140+
GraphServiceClient graphServiceClient)
141+
{
142+
_logger = logger;
143+
_graphServiceClient = graphServiceClient;
144+
}
145+
```
149146

150147
1. Add a `Profile()` action so that it calls the Microsoft Graph *me* endpoint. In case a token cannot be acquired, a challenge is attempted to re-sign-in the user, and have them consent to the requested scopes. This is expressed declaratively by the `AuthorizeForScopes`attribute. This attribute is part of the `Microsoft.Identity.Web` project and automatically manages incremental consent.
151148

152-
```CSharp
153-
[AuthorizeForScopes(Scopes = new[] {Constants.ScopeUserRead})]
154-
public async Task<IActionResult> Profile()
155-
{
149+
```CSharp
150+
[AuthorizeForScopes(Scopes = new[] {Constants.ScopeUserRead})]
151+
public async Task<IActionResult> Profile()
152+
{
156153
var accessToken =
157154
await tokenAcquisition.GetAccessTokenOnBehalfOfUser(HttpContext,
158-
new[] {Constants.ScopeUserRead});
155+
new[] {Constants.ScopeUserRead});
159156

160157
var me = await graphApiOperations.GetUserInformation(accessToken);
161158
var photo = await graphApiOperations.GetPhotoAsBase64Async(accessToken);
@@ -164,8 +161,8 @@ In the `Controllers\HomeController.cs`file:
164161
ViewData["Photo"] = photo;
165162

166163
return View();
167-
}
168-
```
164+
}
165+
```
169166

170167
### Add a Profile view to display the *me* object
171168

@@ -181,37 +178,50 @@ HTML table displaying the properties of the *me* object as returned by Microsoft
181178
<h3>@ViewData["Message"]</h3>
182179

183180
<table class="table table-striped table-condensed" style="font-family: monospace">
184-
<tr>
185-
<th>Property</th>
186-
<th>Value</th>
187-
</tr>
188-
<tr>
189-
<td>photo</td>
190-
<td>
191-
@{
192-
if (ViewData["photo"] != null)
193-
{
194-
<img style="margin: 5px 0; width: 150px" src="data:image/jpeg;base64, @ViewData["photo"]" />
195-
}
196-
else
197-
{
198-
<h3>NO PHOTO</h3>
199-
<p>Check user profile in Azure Active Directory to add a photo.</p>
200-
}
201-
}
202-
</td>
203-
</tr>
204-
@{
205-
var me = ViewData["me"] as JObject;
206-
var children = me.Properties();
207-
foreach (var child in children)
208-
{
209-
<tr>
210-
<td>@child.Name</td>
211-
<td>@child.Value</td>
212-
</tr>
181+
<tr>
182+
<th>Property</th>
183+
<th>Value</th>
184+
</tr>
185+
<tr>
186+
<td>photo</td>
187+
<td>
188+
@{
189+
if (ViewData["photo"] != null)
190+
{
191+
<img style="margin: 5px 0; width: 150px" src="data:image/jpeg;base64, @ViewData["photo"]" />
192+
}
193+
else
194+
{
195+
<h3>NO PHOTO</h3>
196+
<p>Check user profile in Azure Active Directory to add a photo.</p>
197+
}
198+
}
199+
</td>
200+
</tr>
201+
@{
202+
var me = ViewData["me"] as Microsoft.Graph.User;
203+
var properties = me.GetType().GetProperties();
204+
foreach (var child in properties)
205+
{
206+
object value = child.GetValue(me);
207+
string stringRepresentation;
208+
if (!(value is string) && value is IEnumerable<string>)
209+
{
210+
stringRepresentation = "["
211+
+ string.Join(", ", (value as IEnumerable<string>).OfType<object>().Select(c => c.ToString()))
212+
+ "]";
213+
}
214+
else
215+
{
216+
stringRepresentation = value?.ToString();
217+
}
218+
219+
<tr>
220+
<td> @child.Name </td>
221+
<td> @stringRepresentation </td>
222+
</tr>
223+
}
213224
}
214-
}
215225
</table>
216226
```
217227

2-WebApp-graph-user/2-4-Sovereign-Call-MSGraph/Services/MicrosoftGraph-Rest/Bootstrapper.cs

Lines changed: 0 additions & 15 deletions
This file was deleted.

0 commit comments

Comments
 (0)