Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Request] CallsMicrosoftGraph in Startup.cs enables usage of the Graph SDK in controllers / Razor / Blazor pages #427

Closed
9 tasks
jmprieur opened this issue Aug 10, 2020 · 1 comment · Fixed by #434
Assignees
Labels
enhancement New feature or request fixed P1
Milestone

Comments

@jmprieur
Copy link
Collaborator

jmprieur commented Aug 10, 2020

Issue

Today the template introduce files to easy calling MIcrosoft Graph that would benefit from being productized in the library

Spec

See #431

We'd want to be able to set which Microsoft graph endpoint to use (v1.0, Beta, but also national clouds) from the configuration, or programmatically. For instance in the appsettings.json

{
    "AzureAd": {
        "Instance": "https://login.microsoftonline.com/",
        "Domain": "qualified.domain.name",
        "TenantId": "22222222-2222-2222-2222-222222222222",
        "ClientId": "11111111-1111-1111-11111111111111111",
        "ClientSecret": "[Copy the client secret added to the app from the Azure portal]",
        "CallbackPath": "/signin-oidc"
    },

    "GraphBeta": {
        "CalledApiUrl": "https://graph.microsoft.com/beta",
        "CalledApiScopes": "user.read"
    },
}

And be able to reference them in a CallsMicrosoftGraph method that would be called under CallsWebApi() or under AddMicrosoftIdentityWebApp or AddMicrosoftIdentityWebApi (to be decided)

  services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
               .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))
                 .CallsWebApi()
                   .CallsMicrosoftGraph(Configuration.GetSection("GraphBeta"))
                   .AddInMemoryTokenCaches(); 

We'll also provide a simple override that use the default (https://graph.microsoft.com/v1.0 and "user.read")

 services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
              .AddMicrosoftIdentityPlatformWebApp(Configuration.GetSection("AzureAd"))
                 .CallsWebApi(initialScopes:new string[] {"user.read" })
                    .CallsMicrosoftGraph()
                    .AddInMemoryTokenCaches(); 

And an override that offers a delegate:

  services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
              .AddMicrosoftIdentityPlatformWebApp(Configuration.GetSection("AzureAd"))
                .CallsWebApi(initialScopes:new string[] {"user.read" })
                .CallsMicrosoftGraph("GraphBeta", 
                                                   options => options.CalledApiBaseUrl = "https://graph.microsoft.com/beta")
                .AddInMemoryTokenCaches(); 

In the controller or a Razor page, or a Blazor page, the GraphServiceClient will be injected by dependency injection and simply used.

using Microsoft.Identity.Web;

namespace WebAppCallsMicrosoftGraph.Pages
{
    [AuthorizeForScopes(Scopes = new[] { "user.read" })]
    public class IndexModel : PageModel
    {
        private readonly GraphServiceClient _graphServiceClient;

        public IndexModel(ILogger<IndexModel> logger, GraphServiceClient graphServiceClient)
        {
            _graphServiceClient = graphServiceClient;
        }

        public async Task OnGet()
        {
            var user = await _graphServiceClient.Me.Request().GetAsync();
         
            try
            {
                using (var photoStream = await _graphServiceClient.Me.Photo.Content.Request().GetAsync())
                {
                    byte[] photoByte = ((MemoryStream)photoStream).ToArray();
                    ViewData["photo"] = Convert.ToBase64String(photoByte);
                }
                ViewData["name"] = user.DisplayName;
            }
            catch (Exception)
            {
                ViewData["photo"] = null;
            }
        }
    }
}

Proposed design / work to be done

  • Add a folder named DownstreamApiSupport
  • Add a CalledApiOptions class, which will also be used for Downstream APIs (hence the HttpMethod which does not seem to be used here)
       /// <summary>
       /// Options passed-in to call web APIs (Microsoft Graph or another API).
       /// </summary>
       public class CalledApiOptions
       {
         /// <summary>
         /// Base URL for the called Web API. For instance <c>"https://graph.microsoft.com/v1.0/".</c>.
         /// </summary>
         public string CalledApiBaseUrl { get; set; } = "https://graph.microsoft.com/v1.0/";

         /// <summary>
         /// Space separated scopes used to call the Web API.
         /// For instance "user.read mail.read".
         /// </summary>
         public string? CalledApiScopes { get; set; } = null;

         /// <summary>
         /// Http method used to call this API (by default Get).
         /// </summary>
         public HttpMethod HttpMethod { get; set; } = HttpMethod.Get;
       }
  • Add the TokenAcquisitionCredentialProvider class which is currently shipped in the templates.
  • Reference the Microsoft.Graph NuGet package from the Microsoft.Identity.Web project
  • Remove references to the Microsoft.Graph NuGet package from all the test apps that use it
  • Add a MicrosoftGraphServiceExtensions inspired by the one shipped in the templates, but applied to
    /// <summary>
    /// Extensions methods on a MicrososoftAppCallingWebApiAuthenticationBuilder builder
    /// to add support to call Microsoft Graph.
    /// </summary>
    public static class MicrosoftGraphServiceExtensions
    {
        /// <summary>
        /// Add support to calls Microsoft graph. From a named option and a configuration section
        /// </summary>
        /// <param name="builder">Builder.</param>
        /// <param name="configurationSection">Configuraiton section.</param>
        /// <returns>The builder to chain.</returns>
        public static MicrososoftAppCallingWebApiAuthenticationBuilder CallsMicrosoftGraph(
            this MicrososoftAppCallingWebApiAuthenticationBuilder builder,
            IConfigurationSection configurationSection)
       {
          // Todo chain with the one with delegates
       }

        /// <summary>
        /// Add support to calls Microsoft graph. From a base graph Url and a default scope.
        /// </summary>
        /// <param name="builder">Builder.</param>
        /// <param name="graphBaseUrl">Named instance of option.</param>
        /// <param name="defaultScopes">Configuration section.</param>
        /// <returns>The builder to chain.</returns>
        public static MicrososoftAppCallingWebApiAuthenticationBuilder CallsMicrosoftGraph(
            this MicrososoftAppCallingWebApiAuthenticationBuilder builder,
            string graphBaseUrl = "https://graph.microsoft.com/v1.0",
            string defaultScopes = "user.read")
        {
          // Todo chain with the one with delegates
        }

        /// <summary>
        /// Add support to calls Microsoft graph. From a named options and a configuraiton method.
        /// </summary>
        /// <param name="builder">Builder.</param>
        /// <param name="configureOptions">Method to configure the options.</param>
        /// <returns>The builder to chain.</returns>
        public static MicrososoftAppCallingWebApiAuthenticationBuilder CallsMicrosoftGraph(
            this MicrososoftAppCallingWebApiAuthenticationBuilder builder,
            Action<CalledApiOptions> configureOptions);
  • The override with delegates will need to add the following services:

    • Options (builder.Services.AddOptions<CalledApiOptions>().Configure(configureOptions))
    • TokenAcquisition (as a singleton) (builder.Services.AddTokenAcquisition(true))
    • GraphServiceClient (as a singleton) (builder.Services.AddSingleton<GraphServiceClient, GraphServiceClient>(serviceProvider => {`
  • Update the test apps to use this new way of adding Graph (in the Startup.cs / appsettings.json)

  • Update the project templates to use this new way of adding Graph (and remove the references to the Microsoft.Graph NuGet package)

@jmprieur jmprieur added the enhancement New feature or request label Aug 10, 2020
@jmprieur jmprieur added the P1 label Aug 10, 2020
@jmprieur jmprieur changed the title [Feature Request] CallsMicrosoftGraph exposes the Graph SDK [Feature Request] CallsMicrosoftGraph in Startup.cs enables usage of the Graph SDK in controllers / Razor / Blazor pages Aug 10, 2020
@jennyf19 jennyf19 assigned jennyf19 and jmprieur and unassigned jennyf19 Aug 10, 2020
jmprieur added a commit that referenced this issue Aug 13, 2020
Fixes #427

Add the `MicrosoftGraphOptions`, `MicrosoftGraphServiceExtensions`, `TokenAcquisitionCredentialProvider` (coming from the Blazor sample)

Update the test apps that where using graph
- WebAppCallsMicrosoftGraph
- BlazorServerCallsGraph

Updating the samples requires the following:
- remove the package reference to the Microsoft Graph SDK, as it is no longer needed in the sample (brought by Microsoft.Identity.Web)
- update the appsettings.Json to use the new names for the settings (`BaseUrl` and `Scopes`)
- remove the implementation which was so far in the sample for the MicrosoftGraphService and it's token provider (this was the implementation brought by the project templates)
- in startup.cs, remove the calls to `services.AddMicrosoftGraph(scopes, Configuration.GetValue<string>("CalledApi:CalledApiUrl"));` and replace it by what we agreed on with Damien:

```CSharp
 .EnableTokenAcquisitionToCallDownstreamApi()
                            .AddMicrosoftGraphServiceClient(Configuration.GetSection("GraphBeta"))
```

Co-authored-by: pmaytak <34331512+pmaytak@users.noreply.github.com>
Co-authored-by: Jenny Ferries <jeferrie@microsoft.com>
@jennyf19 jennyf19 added this to the 0.3.0-preview milestone Aug 14, 2020
@jennyf19
Copy link
Collaborator

Included in 0.3.0-preview release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request fixed P1
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants