Skip to content
This repository has been archived by the owner on Apr 12, 2023. It is now read-only.

Commit

Permalink
Merge pull request #63 from microsoftgraph/netcore3.1
Browse files Browse the repository at this point in the history
Migrating to ASP.NET Core 3.1
  • Loading branch information
jasonjoh authored Jul 13, 2020
2 parents bc4abf0 + 6d0bba1 commit d61f68b
Show file tree
Hide file tree
Showing 12 changed files with 67 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,22 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MicrosoftGraphAspNetCoreConnectSample.Helpers;
using Microsoft.Extensions.Configuration;
using Microsoft.Graph;
using Microsoft.AspNetCore.Hosting;
using System.Security.Claims;
using MicrosoftGraphAspNetCoreConnectSample.Services;

namespace MicrosoftGraphAspNetCoreConnectSample.Controllers
{
public class HomeController : Controller
{
private readonly IConfiguration _configuration;
private readonly IHostingEnvironment _env;
private readonly IGraphSdkHelper _graphSdkHelper;
private readonly IWebHostEnvironment _env;
private readonly IGraphServiceClientFactory _graphServiceClientFactory;

public HomeController(IConfiguration configuration, IHostingEnvironment hostingEnvironment, IGraphSdkHelper graphSdkHelper)
public HomeController(IWebHostEnvironment hostingEnvironment, IGraphServiceClientFactory graphServiceClientFactory)
{
_configuration = configuration;
_env = hostingEnvironment;
_graphSdkHelper = graphSdkHelper;
_graphServiceClientFactory = graphServiceClientFactory;
}

[AllowAnonymous]
Expand All @@ -34,11 +31,11 @@ public async Task<IActionResult> Index(string email)
if (User.Identity.IsAuthenticated)
{
// Get users's email.
email = email ?? User.FindFirst("preferred_username")?.Value;
email ??= User.FindFirst("preferred_username")?.Value;
ViewData["Email"] = email;

// Initialize the GraphServiceClient.
var graphClient = _graphSdkHelper.GetAuthenticatedClient((ClaimsIdentity)User.Identity);
var graphClient = _graphServiceClientFactory.GetAuthenticatedGraphClient((ClaimsIdentity)User.Identity);

ViewData["Response"] = await GraphService.GetUserJson(graphClient, email, HttpContext);

Expand All @@ -62,7 +59,7 @@ public async Task<IActionResult> SendEmail(string recipients)
try
{
// Initialize the GraphServiceClient.
var graphClient = _graphSdkHelper.GetAuthenticatedClient((ClaimsIdentity)User.Identity);
var graphClient = _graphServiceClientFactory.GetAuthenticatedGraphClient((ClaimsIdentity)User.Identity);

// Send the email.
await GraphService.SendEmail(graphClient, _env, recipients, HttpContext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using MicrosoftGraphAspNetCoreConnectSample.Helpers;
using MicrosoftGraphAspNetCoreConnectSample.Services;

namespace MicrosoftGraphAspNetCoreConnectSample.Extensions
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<UserSecretsId>aspnet-MicrosoftGraphAspNetCoreConnectSample-ec1d62b9-d84d-45c8-8b3e-dc4e8b2ed850</UserSecretsId>
<WebProject_DirectoryAccessLevelKey>0</WebProject_DirectoryAccessLevelKey>
<Version>2.0.0</Version>
<TargetFramework>netcoreapp3.1</TargetFramework>
<Version>3.0.0</Version>
<Authors>Mark Szabo</Authors>
<Company>Microsoft</Company>
<Product>Microsoft Graph</Product>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.Graph" Version="1.16.0" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.2.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
</ItemGroup>

<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="3.1.5" />
<PackageReference Include="Microsoft.Graph" Version="3.8.0" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.16.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>

</Project>
6 changes: 6 additions & 0 deletions MicrosoftGraphAspNetCoreConnectSample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;

namespace MicrosoftGraphAspNetCoreConnectSample
{
Expand All @@ -17,6 +18,11 @@ public static void Main(string[] args)

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConsole();
logging.AddDebug();
})
.UseStartup<Startup>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
using Microsoft.Graph;
using MicrosoftGraphAspNetCoreConnectSample.Extensions;

namespace MicrosoftGraphAspNetCoreConnectSample.Helpers
namespace MicrosoftGraphAspNetCoreConnectSample.Services
{
public class GraphAuthProvider : IGraphAuthProvider
{
private IConfidentialClientApplication _app;
private readonly IConfidentialClientApplication _app;
private readonly string[] _scopes;

public GraphAuthProvider(IConfiguration configuration)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@
using System.Linq;
using System.Threading.Tasks;

namespace MicrosoftGraphAspNetCoreConnectSample.Helpers
namespace MicrosoftGraphAspNetCoreConnectSample.Services
{
public static class GraphService
{
private const string PlaceholderImage = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz4NCjxzdmcgd2lkdGg9IjQwMXB4IiBoZWlnaHQ9IjQwMXB4IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDMxMi44MDkgMCA0MDEgNDAxIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjMxMi44MDkgMCA0MDEgNDAxIiB4bWw6c3BhY2U9InByZXNlcnZlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPg0KPGcgdHJhbnNmb3JtPSJtYXRyaXgoMS4yMjMgMCAwIDEuMjIzIC00NjcuNSAtODQzLjQ0KSI+DQoJPHJlY3QgeD0iNjAxLjQ1IiB5PSI2NTMuMDciIHdpZHRoPSI0MDEiIGhlaWdodD0iNDAxIiBmaWxsPSIjRTRFNkU3Ii8+DQoJPHBhdGggZD0ibTgwMi4zOCA5MDguMDhjLTg0LjUxNSAwLTE1My41MiA0OC4xODUtMTU3LjM4IDEwOC42MmgzMTQuNzljLTMuODctNjAuNDQtNzIuOS0xMDguNjItMTU3LjQxLTEwOC42MnoiIGZpbGw9IiNBRUI0QjciLz4NCgk8cGF0aCBkPSJtODgxLjM3IDgxOC44NmMwIDQ2Ljc0Ni0zNS4xMDYgODQuNjQxLTc4LjQxIDg0LjY0MXMtNzguNDEtMzcuODk1LTc4LjQxLTg0LjY0MSAzNS4xMDYtODQuNjQxIDc4LjQxLTg0LjY0MWM0My4zMSAwIDc4LjQxIDM3LjkgNzguNDEgODQuNjR6IiBmaWxsPSIjQUVCNEI3Ii8+DQo8L2c+DQo8L3N2Zz4NCg==";

// Load user's profile in formatted JSON.
public static async Task<string> GetUserJson(GraphServiceClient graphClient, string email, HttpContext httpContext)
{
Expand Down Expand Up @@ -58,6 +60,8 @@ public static async Task<string> GetPictureBase64(GraphServiceClient graphClient
// Load user's profile picture.
var pictureStream = await GetPictureStream(graphClient, email, httpContext);

if (pictureStream == null) return PlaceholderImage;

// Copy stream to MemoryStream object so that it can be converted to byte array.
var pictureMemoryStream = new MemoryStream();
await pictureStream.CopyToAsync(pictureMemoryStream);
Expand All @@ -72,16 +76,12 @@ public static async Task<string> GetPictureBase64(GraphServiceClient graphClient
}
catch (Exception e)
{
switch (e.Message)
return e.Message switch
{
case "ResourceNotFound":
// If picture not found, return the default image.
return "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz4NCjxzdmcgd2lkdGg9IjQwMXB4IiBoZWlnaHQ9IjQwMXB4IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDMxMi44MDkgMCA0MDEgNDAxIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjMxMi44MDkgMCA0MDEgNDAxIiB4bWw6c3BhY2U9InByZXNlcnZlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPg0KPGcgdHJhbnNmb3JtPSJtYXRyaXgoMS4yMjMgMCAwIDEuMjIzIC00NjcuNSAtODQzLjQ0KSI+DQoJPHJlY3QgeD0iNjAxLjQ1IiB5PSI2NTMuMDciIHdpZHRoPSI0MDEiIGhlaWdodD0iNDAxIiBmaWxsPSIjRTRFNkU3Ii8+DQoJPHBhdGggZD0ibTgwMi4zOCA5MDguMDhjLTg0LjUxNSAwLTE1My41MiA0OC4xODUtMTU3LjM4IDEwOC42MmgzMTQuNzljLTMuODctNjAuNDQtNzIuOS0xMDguNjItMTU3LjQxLTEwOC42MnoiIGZpbGw9IiNBRUI0QjciLz4NCgk8cGF0aCBkPSJtODgxLjM3IDgxOC44NmMwIDQ2Ljc0Ni0zNS4xMDYgODQuNjQxLTc4LjQxIDg0LjY0MXMtNzguNDEtMzcuODk1LTc4LjQxLTg0LjY0MSAzNS4xMDYtODQuNjQxIDc4LjQxLTg0LjY0MWM0My4zMSAwIDc4LjQxIDM3LjkgNzguNDEgODQuNjR6IiBmaWxsPSIjQUVCNEI3Ii8+DQo8L2c+DQo8L3N2Zz4NCg==";
case "EmailIsNull":
return JsonConvert.SerializeObject(new { Message = "Email address cannot be null." }, Formatting.Indented);
default:
return null;
}
"ResourceNotFound" => PlaceholderImage, // If picture is not found, return the placeholder image.
"EmailIsNull" => JsonConvert.SerializeObject(new { Message = "Email address cannot be null." }, Formatting.Indented),
_ => null,
};
}
}

Expand Down Expand Up @@ -183,7 +183,7 @@ public static async Task<Stream> GetMyPictureStream(GraphServiceClient graphClie
}

// Send an email message from the current user.
public static async Task SendEmail(GraphServiceClient graphClient, IHostingEnvironment hostingEnvironment, string recipients, HttpContext httpContext)
public static async Task SendEmail(GraphServiceClient graphClient, IWebHostEnvironment hostingEnvironment, string recipients, HttpContext httpContext)
{
if (recipients == null) return;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,23 @@
* See LICENSE in the source repository root for complete license information.
*/

using System.Collections.Generic;
using Microsoft.Graph;
using System.Net.Http.Headers;
using System.Security.Claims;
using Microsoft.Graph;

namespace MicrosoftGraphAspNetCoreConnectSample.Helpers
namespace MicrosoftGraphAspNetCoreConnectSample.Services
{
public class GraphSdkHelper : IGraphSdkHelper
public class GraphServiceClientFactory : IGraphServiceClientFactory
{
private readonly IGraphAuthProvider _authProvider;
private GraphServiceClient _graphClient;

public GraphSdkHelper(IGraphAuthProvider authProvider)
public GraphServiceClientFactory(IGraphAuthProvider authProvider)
{
_authProvider = authProvider;
}

// Get an authenticated Microsoft Graph Service client.
public GraphServiceClient GetAuthenticatedClient(ClaimsIdentity userIdentity)
{
_graphClient = new GraphServiceClient(new DelegateAuthenticationProvider(
public GraphServiceClient GetAuthenticatedGraphClient(ClaimsIdentity userIdentity) =>
new GraphServiceClient(new DelegateAuthenticationProvider(
async requestMessage =>
{
// Get user's id for token cache.
Expand All @@ -38,12 +34,10 @@ public GraphServiceClient GetAuthenticatedClient(ClaimsIdentity userIdentity)
// This header identifies the sample in the Microsoft Graph service. If extracting this code for your project please remove.
requestMessage.Headers.Add("SampleID", "aspnetcore-connect-sample");
}));

return _graphClient;
}
}
public interface IGraphSdkHelper

public interface IGraphServiceClientFactory
{
GraphServiceClient GetAuthenticatedClient(ClaimsIdentity userIdentity);
GraphServiceClient GetAuthenticatedGraphClient(ClaimsIdentity userIdentity);
}
}
33 changes: 13 additions & 20 deletions MicrosoftGraphAspNetCoreConnectSample/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,11 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Hosting;
using MicrosoftGraphAspNetCoreConnectSample.Extensions;
using MicrosoftGraphAspNetCoreConnectSample.Helpers;


using MicrosoftGraphAspNetCoreConnectSample.Services;

namespace MicrosoftGraphAspNetCoreConnectSample
{
Expand Down Expand Up @@ -51,28 +48,25 @@ public void ConfigureServices(IServiceCollection services)
.AddAzureAd(options => Configuration.Bind("AzureAd", options))
.AddCookie();

services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

services.AddSession();
services.AddMvc();
services.AddControllers();

// Add application services.
//services.AddSingleton<IConfiguration>(Configuration);
services.AddSingleton<IGraphAuthProvider, GraphAuthProvider>();
services.AddTransient<IGraphSdkHelper, GraphSdkHelper>();
services.AddSingleton<IGraphServiceClientFactory, GraphServiceClientFactory>();

services.Configure<HstsOptions>(options =>
{
options.IncludeSubDomains = true;
options.MaxAge = TimeSpan.FromDays(365);
});

services.AddHealthChecks();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();

if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
Expand All @@ -85,14 +79,13 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseSession();
app.UseRouting();
app.UseAuthentication();

app.UseMvc(routes =>
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapHealthChecks("/healthcheck");
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
ViewData["Title"] = "Home Page";
}

<h2>Microsoft Graph ASP.NET Core 2.1 Connect Sample</h2>
<h2>Microsoft Graph ASP.NET Core 3.1 Connect Sample</h2>

@if (!User.Identity.IsAuthenticated)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
@RenderBody()
<hr />
<footer>
<p>&copy; 2018 - Microsoft</p>
<p>&copy; 2020 - Microsoft</p>
</footer>
</div>

Expand Down
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
page_type: sample
description: "Use ASP.NET Core 2.1 MVC to connect to Microsoft Graph using the delegated permissions flow."
description: "Use ASP.NET Core 3.1 MVC to connect to Microsoft Graph using the delegated permissions flow."
products:
- ms-graph
languages:
Expand All @@ -15,16 +15,16 @@ extensions:
- Microsoft identity platform
createdDate: 8/6/2017 5:17:58 AM
---
# Microsoft Graph Connect Sample for ASP.NET Core 2.1
# Microsoft Graph Connect Sample for ASP.NET Core 3.1

![Microsoft Graph Connect Sample for ASP.NET Core 2.1 screenshot](readme-images/Page1.PNG)
![Screenshot](readme-images/Page1.PNG)

**Scenario**: Use ASP.NET Core 2.1 MVC to connect to Microsoft Graph using the delegated permissions flow to retrieve a user's profile, their photo from Azure AD (v2.0) endpoint and then send an email that contains the photo as attachment.
**Scenario**: Use ASP.NET Core 3.1 MVC to connect to Microsoft Graph using the delegated permissions flow to retrieve a user's profile, their photo from Azure AD (v2.0) endpoint and then send an email that contains the photo as attachment.

The sample uses OpenID Connect for sign in, [Microsoft Authentication Library (MSAL) for .NET](https://github.com/AzureAD/microsoft-authentication-library-for-dotnet) to obtain an access token, and the [Microsoft Graph Client Library for .NET](https://github.com/microsoftgraph/msgraph-sdk-dotnet) (SDK) to interact with Microsoft Graph. The MSAL SDK provides features for working with the [Azure AD v2.0 endpoint](https://azure.microsoft.com/en-us/documentation/articles/active-directory-appmodel-v2-overview), which enables developers to write a single code flow that handles authentication for both work or school (Azure Active Directory) and personal (Microsoft) accounts.
The sample uses only delegate permissions, therefore it does not require admin consent.

> Previous version of this sample that uses ASP.NET Core 1.1 version is [here](https://github.com/microsoftgraph/aspnetcore-connect-sample/tree/netcore1.1) and the ASP.NET Core 2.0 version [here](https://github.com/microsoftgraph/aspnetcore-connect-sample/tree/netcore2.0).
> Previous version of this sample that uses ASP.NET Core 1.1 version is [here](https://github.com/microsoftgraph/aspnetcore-connect-sample/tree/netcore1.1) and the ASP.NET Core 2.1 version [here](https://github.com/microsoftgraph/aspnetcore-connect-sample/tree/netcore2.1).
## Table of contents

Expand All @@ -42,9 +42,9 @@ ADAL (Azure AD v1.0) and MSAL (Azure AD v2.0) are both authentication libraries

## Prerequisites

To use the Microsoft Graph Connect Sample for ASP.NET Core 2.1, you need the following:
To use the Microsoft Graph Connect Sample for ASP.NET Core 3.1, you need the following:

- Visual Studio 2017 [with .NET Core 2.1 SDK](https://www.microsoft.com/net/download/core) installed on your development computer.
- Visual Studio 2019 [with .NET Core 3.1 SDK](https://www.microsoft.com/net/download/core) installed on your development computer.
- Either a [personal Microsoft account](https://signup.live.com) or a [work or school account](https://dev.office.com/devprogram). (You don't need to be an administrator of the tenant.)
- The application ID and key from the application that you [register on the App Registration Portal](#register-the-app).

Expand Down Expand Up @@ -109,7 +109,7 @@ To use the Microsoft Graph Connect Sample for ASP.NET Core 2.1, you need the fol

1. Download or clone the Microsoft Graph Connect Sample for ASP.NET Core.

2. Open the **MicrosoftGraphAspNetCoreConnectSample.sln** sample file in Visual Studio 2017.
2. Open the **MicrosoftGraphAspNetCoreConnectSample.sln** sample file in Visual Studio 2019.

3. In Solution Explorer, open the **appsettings.json** file in the root directory of the project.

Expand Down Expand Up @@ -177,4 +177,4 @@ You can suggest changes for Microsoft Graph on [UserVoice](https://officespdev.u

## Copyright

Copyright (c) 2019 Microsoft. All rights reserved.
Copyright (c) 2020 Microsoft. All rights reserved.
Binary file modified readme-images/Page1.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit d61f68b

Please sign in to comment.