Updating Web project templates to ms.id.web 0.3.0-preview (#25309)
* Updating Web project templates to ms.id.web 0.3.0-preview * version bump and quick fix to the Blasorwasm default values for the Web api so that customers have a better experience * Microsoft.Graph is brought in transitively by MS.ID.Web Co-authored-by: John Luo <johluo@microsoft.com>
This commit is contained in:
parent
f82794a6cb
commit
744e96b23d
|
|
@ -168,7 +168,6 @@ and are generated based on the last package release.
|
|||
<LatestPackageReference Include="MessagePackAnalyzer" />
|
||||
<LatestPackageReference Include="Microsoft.Data.SqlClient" />
|
||||
<LatestPackageReference Include="Microsoft.Identity.Web.UI" />
|
||||
<LatestPackageReference Include="Microsoft.Graph" />
|
||||
<LatestPackageReference Include="Mono.Cecil" />
|
||||
<LatestPackageReference Include="Moq" />
|
||||
<LatestPackageReference Include="Newtonsoft.Json.Bson" />
|
||||
|
|
|
|||
|
|
@ -260,9 +260,8 @@
|
|||
<IdentityServer4StoragePackageVersion>4.0.4</IdentityServer4StoragePackageVersion>
|
||||
<IdentityServer4EntityFrameworkStoragePackageVersion>4.0.4</IdentityServer4EntityFrameworkStoragePackageVersion>
|
||||
<MessagePackPackageVersion>2.1.90</MessagePackPackageVersion>
|
||||
<MicrosoftIdentityWebPackageVersion>0.2.3-preview</MicrosoftIdentityWebPackageVersion>
|
||||
<MicrosoftIdentityWebUIPackageVersion>0.2.3-preview</MicrosoftIdentityWebUIPackageVersion>
|
||||
<MicrosoftGraphPackageVersion>3.8.0</MicrosoftGraphPackageVersion>
|
||||
<MicrosoftIdentityWebPackageVersion>0.3.1-preview</MicrosoftIdentityWebPackageVersion>
|
||||
<MicrosoftIdentityWebUIPackageVersion>0.3.1-preview</MicrosoftIdentityWebUIPackageVersion>
|
||||
<MessagePackAnalyzerPackageVersion>$(MessagePackPackageVersion)</MessagePackAnalyzerPackageVersion>
|
||||
<MoqPackageVersion>4.10.0</MoqPackageVersion>
|
||||
<MonoCecilPackageVersion>0.11.2</MonoCecilPackageVersion>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@
|
|||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="${MicrosoftEntityFrameworkCoreToolsPackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' " />
|
||||
<PackageReference Include="Microsoft.Identity.Web" Version="${MicrosoftIdentityWebPackageVersion}" Condition=" '$(IndividualB2CAuth)' == 'True' OR '$(OrganizationalAuth)' == 'True'" />
|
||||
<PackageReference Include="Microsoft.Identity.Web.UI" Version="${MicrosoftIdentityWebUIPackageVersion}" Condition=" '$(IndividualB2CAuth)' == 'True' OR '$(OrganizationalAuth)' == 'True'" />
|
||||
<PackageReference Include="Microsoft.Graph" Version="${MicrosoftGraphPackageVersion}" Condition=" '$(GenerateGraph)' == 'True' "/>
|
||||
</ItemGroup>
|
||||
|
||||
<!--#endif -->
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Identity.Web" Version="${MicrosoftIdentityWebPackageVersion}" />
|
||||
<PackageReference Include="Microsoft.Identity.Web.UI" Version="${MicrosoftIdentityWebUIPackageVersion}" />
|
||||
<PackageReference Include="Microsoft.Graph" Version="${MicrosoftGraphPackageVersion}" Condition=" '$(GenerateGraph)' == 'True' "/>
|
||||
</ItemGroup>
|
||||
<!--#endif -->
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@
|
|||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="${MicrosoftEntityFrameworkCoreToolsPackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' " />
|
||||
<PackageReference Include="Microsoft.Identity.Web" Version="${MicrosoftIdentityWebPackageVersion}" Condition=" '$(IndividualB2CAuth)' == 'True' OR '$(OrganizationalAuth)' == 'True'" />
|
||||
<PackageReference Include="Microsoft.Identity.Web.UI" Version="${MicrosoftIdentityWebUIPackageVersion}" Condition=" '$(IndividualB2CAuth)' == 'True' OR '$(OrganizationalAuth)' == 'True'" />
|
||||
<PackageReference Include="Microsoft.Graph" Version="${MicrosoftGraphPackageVersion}" Condition=" '$(GenerateGraph)' == 'True' "/>
|
||||
</ItemGroup>
|
||||
<!--#endif -->
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@
|
|||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="${MicrosoftEntityFrameworkCoreToolsPackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' " />
|
||||
<PackageReference Include="Microsoft.Identity.Web" Version="${MicrosoftIdentityWebPackageVersion}" Condition=" '$(IndividualB2CAuth)' == 'True' OR '$(OrganizationalAuth)' == 'True'" />
|
||||
<PackageReference Include="Microsoft.Identity.Web.UI" Version="${MicrosoftIdentityWebUIPackageVersion}" Condition=" '$(IndividualB2CAuth)' == 'True' OR '$(OrganizationalAuth)' == 'True'" />
|
||||
<PackageReference Include="Microsoft.Graph" Version="${MicrosoftGraphPackageVersion}" Condition=" '$(GenerateGraph)' == 'True' "/>
|
||||
</ItemGroup>
|
||||
<!--#endif -->
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Identity.Web" Version="${MicrosoftIdentityWebPackageVersion}" Condition="'$(OrganizationalAuth)' == 'True' OR '$(IndividualB2CAuth)' == 'True'"/>
|
||||
<PackageReference Include="Microsoft.Graph" Version="${MicrosoftGraphPackageVersion}" Condition="'$(GenerateGraph)' == 'True'"/>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="${SwashbuckleAspNetCorePackageVersion}" Condition="'$(EnableOpenAPI)' == 'True'" />
|
||||
</ItemGroup>
|
||||
<!--#endif -->
|
||||
|
|
|
|||
|
|
@ -138,8 +138,6 @@
|
|||
{
|
||||
"condition": "(!GenerateGraph)",
|
||||
"exclude": [
|
||||
"Services/MicrosoftGraphServiceExtensions.cs",
|
||||
"Services/TokenAcquisitionCredentialProvider.cs",
|
||||
"Shared/NavMenu.CallsMicrosoftGraph.razor",
|
||||
"Pages/ShowProfile.razor"
|
||||
]
|
||||
|
|
|
|||
|
|
@ -21,13 +21,25 @@ else
|
|||
}
|
||||
|
||||
@code {
|
||||
private HttpResponseMessage response;
|
||||
private string apiResult;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
apiResult = await downstreamAPI.CallWebApiAsync("me");
|
||||
response = await downstreamAPI.CallWebApiForUserAsync(
|
||||
"DownstreamApi",
|
||||
options => options.RelativePath = "");
|
||||
|
||||
if (response.StatusCode == System.Net.HttpStatusCode.OK)
|
||||
{
|
||||
apiResult = await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
apiResult = "Failed to call the web API";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Identity.Web;
|
||||
|
||||
namespace BlazorServerWeb_CSharp
|
||||
{
|
||||
public interface IDownstreamWebApi
|
||||
{
|
||||
Task<string> CallWebApiAsync(string relativeEndpoint = "", string[] requiredScopes = null);
|
||||
}
|
||||
|
||||
public static class DownstreamWebApiExtensions
|
||||
{
|
||||
public static void AddDownstreamWebApiService(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
// https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests
|
||||
services.AddHttpClient<IDownstreamWebApi, DownstreamWebApi>();
|
||||
}
|
||||
}
|
||||
|
||||
public class DownstreamWebApi : IDownstreamWebApi
|
||||
{
|
||||
private readonly ITokenAcquisition _tokenAcquisition;
|
||||
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public DownstreamWebApi(
|
||||
ITokenAcquisition tokenAcquisition,
|
||||
IConfiguration configuration,
|
||||
HttpClient httpClient)
|
||||
{
|
||||
_tokenAcquisition = tokenAcquisition;
|
||||
_configuration = configuration;
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls the Web API with the required scopes
|
||||
/// </summary>
|
||||
/// <param name="requireScopes">[Optional] Scopes required to call the Web API. If
|
||||
/// not specified, uses scopes from the configuration</param>
|
||||
/// <param name="relativeEndpoint">Endpoint relative to the CalledApiUrl configuration</param>
|
||||
/// <returns>A JSON string representing the result of calling the Web API</returns>
|
||||
public async Task<string> CallWebApiAsync(string relativeEndpoint = "", string[] requiredScopes = null)
|
||||
{
|
||||
string[] scopes = requiredScopes ?? _configuration["CalledApi:CalledApiScopes"]?.Split(' ');
|
||||
string apiUrl = (_configuration["CalledApi:CalledApiUrl"] as string)?.TrimEnd('/') + $"/{relativeEndpoint}";
|
||||
|
||||
string accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(scopes);
|
||||
HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, apiUrl);
|
||||
httpRequestMessage.Headers.Add("Authorization", $"bearer {accessToken}");
|
||||
|
||||
string apiResult;
|
||||
var response = await _httpClient.SendAsync(httpRequestMessage);
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
apiResult = await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
apiResult = $"Error calling the API '{apiUrl}'";
|
||||
}
|
||||
|
||||
return apiResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Graph;
|
||||
using Microsoft.Identity.Web;
|
||||
|
||||
namespace BlazorServerWeb_CSharp
|
||||
{
|
||||
public static class MicrosoftGraphServiceExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the Microsoft Graph client as a singleton.
|
||||
/// </summary>
|
||||
/// <param name="services">Service collection.</param>
|
||||
/// <param name="initialScopes">Initial scopes.</param>
|
||||
/// <param name="graphBaseUrl">Base URL for Microsoft graph. This can be
|
||||
/// changed for instance for applications running in national clouds</param>
|
||||
public static IServiceCollection AddMicrosoftGraph(this IServiceCollection services,
|
||||
IEnumerable<string> initialScopes,
|
||||
string graphBaseUrl = "https://graph.microsoft.com/v1.0")
|
||||
{
|
||||
services.AddTokenAcquisition(true);
|
||||
services.AddSingleton<GraphServiceClient, GraphServiceClient>(serviceProvider =>
|
||||
{
|
||||
var tokenAquisitionService = serviceProvider.GetService<ITokenAcquisition>();
|
||||
GraphServiceClient client = string.IsNullOrWhiteSpace(graphBaseUrl) ?
|
||||
new GraphServiceClient(new TokenAcquisitionCredentialProvider(tokenAquisitionService, initialScopes)) :
|
||||
new GraphServiceClient(graphBaseUrl, new TokenAcquisitionCredentialProvider(tokenAquisitionService, initialScopes));
|
||||
return client;
|
||||
});
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
using Microsoft.Graph;
|
||||
using Microsoft.Identity.Web;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BlazorServerWeb_CSharp
|
||||
{
|
||||
internal class TokenAcquisitionCredentialProvider : IAuthenticationProvider
|
||||
{
|
||||
public TokenAcquisitionCredentialProvider(ITokenAcquisition tokenAcquisition, IEnumerable<string> initialScopes)
|
||||
{
|
||||
_tokenAcquisition = tokenAcquisition;
|
||||
_initialScopes = initialScopes;
|
||||
}
|
||||
|
||||
ITokenAcquisition _tokenAcquisition;
|
||||
IEnumerable<string> _initialScopes;
|
||||
|
||||
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
|
||||
{
|
||||
request.Headers.Add("Authorization",
|
||||
$"Bearer {await _tokenAcquisition.GetAccessTokenForUserAsync(_initialScopes)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,9 +4,9 @@ using System.Linq;
|
|||
using System.Threading.Tasks;
|
||||
#if (OrganizationalAuth || IndividualB2CAuth)
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
||||
using Microsoft.Identity.Web;
|
||||
using Microsoft.Identity.Web.UI;
|
||||
using Microsoft.Identity.Web.TokenCacheProviders.InMemory;
|
||||
#endif
|
||||
#if (OrganizationalAuth)
|
||||
#if (MultiOrgAuth)
|
||||
|
|
@ -73,33 +73,36 @@ namespace BlazorServerWeb_CSharp
|
|||
.AddEntityFrameworkStores<ApplicationDbContext>();
|
||||
#elif (OrganizationalAuth)
|
||||
#if (GenerateApiOrGraph)
|
||||
string[] scopes = Configuration.GetValue<string>("CalledApi:CalledApiScopes")?.Split(' ');
|
||||
var initialScopes = Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');
|
||||
|
||||
#endif
|
||||
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
|
||||
#if (GenerateApiOrGraph)
|
||||
services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd")
|
||||
.AddMicrosoftWebAppCallsWebApi(Configuration, scopes, "AzureAd")
|
||||
.AddInMemoryTokenCaches();
|
||||
#else
|
||||
services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd");
|
||||
#endif
|
||||
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))
|
||||
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
|
||||
#if (GenerateApi)
|
||||
services.AddDownstreamWebApiService(Configuration);
|
||||
.AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi"))
|
||||
#endif
|
||||
#if (GenerateGraph)
|
||||
services.AddMicrosoftGraph(scopes, Configuration.GetValue<string>("CalledApi:CalledApiUrl"));
|
||||
.AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
|
||||
#endif
|
||||
.AddInMemoryTokenCaches();
|
||||
#else
|
||||
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"));
|
||||
#endif
|
||||
#elif (IndividualB2CAuth)
|
||||
#if (GenerateApi)
|
||||
string[] scopes = Configuration.GetValue<string>("CalledApi:CalledApiScopes")?.Split(' ');
|
||||
#endif
|
||||
#if (GenerateApi)
|
||||
services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C")
|
||||
.AddMicrosoftWebAppCallsWebApi(Configuration, scopes, "AzureAdB2C")
|
||||
.AddInMemoryTokenCaches();
|
||||
var initialScopes = Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');
|
||||
|
||||
services.AddDownstreamWebApiService(Configuration);
|
||||
#endif
|
||||
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
|
||||
#if (GenerateApi)
|
||||
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C"))
|
||||
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
|
||||
.AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi"))
|
||||
.AddInMemoryTokenCaches();
|
||||
#else
|
||||
services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C");
|
||||
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C"));
|
||||
#endif
|
||||
#endif
|
||||
#if (OrganizationalAuth || IndividualB2CAuth)
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@
|
|||
// },
|
||||
////#elseif (OrganizationalAuth)
|
||||
// "AzureAd": {
|
||||
//#if (MultiOrgAuth)
|
||||
// "Instance": "https:////login.microsoftonline.com/common",
|
||||
//#elseif (SingleOrgAuth)
|
||||
// "Instance": "https:////login.microsoftonline.com/",
|
||||
//#if (MultiOrgAuth)
|
||||
// "TenantId": "common",
|
||||
//#elseif (SingleOrgAuth)
|
||||
// "Domain": "qualified.domain.name",
|
||||
// "TenantId": "22222222-2222-2222-2222-222222222222",
|
||||
//#endif
|
||||
|
|
@ -34,16 +34,16 @@
|
|||
// },
|
||||
////#endif
|
||||
////#if (GenerateApiOrGraph)
|
||||
// "CalledApi": {
|
||||
// "DownstreamApi": {
|
||||
// /*
|
||||
// 'CalledApiScopes' contains space separated scopes of the Web API you want to call. This can be:
|
||||
// 'Scopes' contains space separated scopes of the Web API you want to call. This can be:
|
||||
// - a scope for a V2 application (for instance api://b3682cc7-8b30-4bd2-aaba-080c6bf0fd31/access_as_user)
|
||||
// - a scope corresponding to a V1 application (for instance <App ID URI>/.default, where <App ID URI> is the
|
||||
// App ID URI of a legacy v1 Web application
|
||||
// Applications are registered in the https://portal.azure.com portal.
|
||||
// */
|
||||
// "CalledApiScopes": "user.read",
|
||||
// "CalledApiUrl": "[WebApiUrl]"
|
||||
// "BaseUrl": "[WebApiUrl]",
|
||||
// "Scopes": "user.read"
|
||||
// },
|
||||
////#endif
|
||||
////#if (IndividualLocalAuth)
|
||||
|
|
|
|||
|
|
@ -211,19 +211,6 @@
|
|||
"Server/Controllers/OidcConfigurationController.cs",
|
||||
"Server/Models/ApplicationUser.cs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"condition": "(Hosted && !GenerateApi)",
|
||||
"exclude": [
|
||||
"Server/Services/DownstreamWebApi.cs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"condition": "(Hosted &&!GenerateGraph)",
|
||||
"exclude": [
|
||||
"Server/Services/MicrosoftGraphServiceExtensions.cs",
|
||||
"Server/Services/TokenAcquisitionCredentialProvider.cs"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -301,7 +288,7 @@
|
|||
"SignUpSignInPolicyId": {
|
||||
"type": "parameter",
|
||||
"datatype": "string",
|
||||
"defaultValue": "",
|
||||
"defaultValue": "b2c_1_susi",
|
||||
"replaces": "MySignUpSignInPolicyId",
|
||||
"description": "The sign-in and sign-up policy ID for this project (use with IndividualB2C auth)."
|
||||
},
|
||||
|
|
@ -340,7 +327,7 @@
|
|||
"type": "parameter",
|
||||
"datatype": "string",
|
||||
"replaces": "api-scope",
|
||||
"defaultValue": "user_impersonation",
|
||||
"defaultValue": "access_as_user",
|
||||
"description": "The API scope the client needs to request to provision an access token. (use with IndividualB2C, SingleOrg)."
|
||||
},
|
||||
"TenantId": {
|
||||
|
|
@ -470,7 +457,7 @@
|
|||
"type": "parameter",
|
||||
"datatype": "string",
|
||||
"replaces": "[WebApiUrl]",
|
||||
"defaultValue" : "https://graph.microsoft.com/v1.0/me",
|
||||
"defaultValue": "https://graph.microsoft.com/v1.0",
|
||||
"description": "URL of the API to call from the web app. This option only applies if --auth SingleOrg, --auth MultiOrg or --auth IndividualB2C without and ASP.NET Core host is specified."
|
||||
},
|
||||
"CallsMicrosoftGraph": {
|
||||
|
|
@ -487,7 +474,7 @@
|
|||
},
|
||||
"GenerateApi": {
|
||||
"type": "computed",
|
||||
"value": "(( (IndividualB2CAuth && !Hosted) || OrganizationalAuth) && (CalledApiUrl != \"https://graph.microsoft.com/v1.0/me\" || CalledApiScopes != \"user.read\"))"
|
||||
"value": "(( (IndividualB2CAuth && !Hosted) || OrganizationalAuth) && (CalledApiUrl != \"https://graph.microsoft.com/v1.0\" || CalledApiScopes != \"user.read\"))"
|
||||
},
|
||||
"GenerateGraph": {
|
||||
"type": "computed",
|
||||
|
|
|
|||
|
|
@ -15,10 +15,10 @@ using System.Net.Http;
|
|||
using Microsoft.Graph;
|
||||
#endif
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
#if (OrganizationalAuth || IndividualB2CAuth)
|
||||
using Microsoft.Identity.Web.Resource;
|
||||
#endif
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ComponentsWebAssembly_CSharp.Shared;
|
||||
|
||||
namespace ComponentsWebAssembly_CSharp.Server.Controllers
|
||||
|
|
@ -37,7 +37,7 @@ namespace ComponentsWebAssembly_CSharp.Server.Controllers
|
|||
|
||||
private readonly ILogger<WeatherForecastController> _logger;
|
||||
|
||||
// The Web API will only accept tokens 1) for users, and 2) having the api-scope scope for this API
|
||||
// The Web API will only accept tokens 1) for users, and 2) having the "api-scope" scope for this API
|
||||
static readonly string[] scopeRequiredByApi = new string[] { "api-scope" };
|
||||
|
||||
#if (GenerateApi)
|
||||
|
|
@ -55,7 +55,17 @@ namespace ComponentsWebAssembly_CSharp.Server.Controllers
|
|||
{
|
||||
HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);
|
||||
|
||||
string downstreamApiResult = await _downstreamWebApi.CallWebApiAsync();
|
||||
using var response = await _downstreamWebApi.CallWebApiForUserAsync("DownstreamApi").ConfigureAwait(false);
|
||||
if (response.StatusCode == System.Net.HttpStatusCode.OK)
|
||||
{
|
||||
var apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
// Do something
|
||||
}
|
||||
else
|
||||
{
|
||||
var error = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
throw new HttpRequestException($"Invalid status code in the HttpResponseMessage: {response.StatusCode}: {error}");
|
||||
}
|
||||
|
||||
var rng = new Random();
|
||||
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
|
||||
|
|
@ -75,7 +85,7 @@ namespace ComponentsWebAssembly_CSharp.Server.Controllers
|
|||
{
|
||||
_logger = logger;
|
||||
_graphServiceClient = graphServiceClient;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IEnumerable<WeatherForecast>> Get()
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Identity.Web;
|
||||
|
||||
namespace ComponentsWebAssembly_CSharp.Server
|
||||
{
|
||||
public interface IDownstreamWebApi
|
||||
{
|
||||
Task<string> CallWebApiAsync(string relativeEndpoint = "", string[] requiredScopes = null);
|
||||
}
|
||||
|
||||
public static class DownstreamWebApiExtensions
|
||||
{
|
||||
public static void AddDownstreamWebApiService(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
// https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests
|
||||
services.AddHttpClient<IDownstreamWebApi, DownstreamWebApi>();
|
||||
}
|
||||
}
|
||||
|
||||
public class DownstreamWebApi : IDownstreamWebApi
|
||||
{
|
||||
private readonly ITokenAcquisition _tokenAcquisition;
|
||||
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public DownstreamWebApi(
|
||||
ITokenAcquisition tokenAcquisition,
|
||||
IConfiguration configuration,
|
||||
HttpClient httpClient)
|
||||
{
|
||||
_tokenAcquisition = tokenAcquisition;
|
||||
_configuration = configuration;
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls the Web API with the required scopes
|
||||
/// </summary>
|
||||
/// <param name="requireScopes">[Optional] Scopes required to call the Web API. If
|
||||
/// not specified, uses scopes from the configuration</param>
|
||||
/// <param name="relativeEndpoint">Endpoint relative to the CalledApiUrl configuration</param>
|
||||
/// <returns>A JSON string representing the result of calling the Web API</returns>
|
||||
public async Task<string> CallWebApiAsync(string relativeEndpoint = "", string[] requiredScopes = null)
|
||||
{
|
||||
string[] scopes = requiredScopes ?? _configuration["CalledApi:CalledApiScopes"]?.Split(' ');
|
||||
string apiUrl = (_configuration["CalledApi:CalledApiUrl"] as string)?.TrimEnd('/') + $"/{relativeEndpoint}";
|
||||
|
||||
string accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(scopes);
|
||||
HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, apiUrl);
|
||||
httpRequestMessage.Headers.Add("Authorization", $"bearer {accessToken}");
|
||||
|
||||
string apiResult;
|
||||
var response = await _httpClient.SendAsync(httpRequestMessage);
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
apiResult = await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
apiResult = $"Error calling the API '{apiUrl}'";
|
||||
}
|
||||
|
||||
return apiResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Graph;
|
||||
using Microsoft.Identity.Web;
|
||||
|
||||
namespace ComponentsWebAssembly_CSharp.Server
|
||||
{
|
||||
public static class MicrosoftGraphServiceExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the Microsoft Graph client as a singleton.
|
||||
/// </summary>
|
||||
/// <param name="services">Service collection.</param>
|
||||
/// <param name="initialScopes">Initial scopes.</param>
|
||||
/// <param name="graphBaseUrl">Base URL for Microsoft graph. This can be
|
||||
/// changed for instance for applications running in national clouds</param>
|
||||
public static IServiceCollection AddMicrosoftGraph(this IServiceCollection services,
|
||||
IEnumerable<string> initialScopes,
|
||||
string graphBaseUrl = "https://graph.microsoft.com/v1.0")
|
||||
{
|
||||
services.AddTokenAcquisition(true);
|
||||
services.AddSingleton<GraphServiceClient, GraphServiceClient>(serviceProvider =>
|
||||
{
|
||||
var tokenAquisitionService = serviceProvider.GetService<ITokenAcquisition>();
|
||||
GraphServiceClient client = string.IsNullOrWhiteSpace(graphBaseUrl) ?
|
||||
new GraphServiceClient(new TokenAcquisitionCredentialProvider(tokenAquisitionService, initialScopes)) :
|
||||
new GraphServiceClient(graphBaseUrl, new TokenAcquisitionCredentialProvider(tokenAquisitionService, initialScopes));
|
||||
return client;
|
||||
});
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
using Microsoft.Graph;
|
||||
using Microsoft.Identity.Web;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ComponentsWebAssembly_CSharp.Server
|
||||
{
|
||||
internal class TokenAcquisitionCredentialProvider : IAuthenticationProvider
|
||||
{
|
||||
public TokenAcquisitionCredentialProvider(ITokenAcquisition tokenAcquisition, IEnumerable<string> initialScopes)
|
||||
{
|
||||
_tokenAcquisition = tokenAcquisition;
|
||||
_initialScopes = initialScopes;
|
||||
}
|
||||
|
||||
ITokenAcquisition _tokenAcquisition;
|
||||
IEnumerable<string> _initialScopes;
|
||||
|
||||
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
|
||||
{
|
||||
request.Headers.Add("Authorization",
|
||||
$"Bearer {await _tokenAcquisition.GetAccessTokenForUserAsync(_initialScopes)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,8 +3,8 @@ using Microsoft.AspNetCore.Authentication;
|
|||
#endif
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
#if (OrganizationalAuth || IndividualB2CAuth)
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.Identity.Web;
|
||||
using Microsoft.Identity.Web.TokenCacheProviders.InMemory;
|
||||
#endif
|
||||
#if (RequiresHttps)
|
||||
using Microsoft.AspNetCore.HttpsPolicy;
|
||||
|
|
@ -63,31 +63,29 @@ namespace ComponentsWebAssembly_CSharp.Server
|
|||
.AddIdentityServerJwt();
|
||||
#endif
|
||||
#if (OrganizationalAuth)
|
||||
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
#if (GenerateApiOrGraph)
|
||||
// Adds Microsoft Identity platform (AAD v2.0) support to protect this Api
|
||||
services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAd")
|
||||
.AddMicrosoftWebApiCallsWebApi(Configuration, "AzureAd")
|
||||
.AddInMemoryTokenCaches();
|
||||
#else
|
||||
// Adds Microsoft Identity platform (AAD v2.0) support to protect this Api
|
||||
services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAd");
|
||||
#endif
|
||||
.AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd"))
|
||||
.EnableTokenAcquisitionToCallDownstreamApi()
|
||||
#if (GenerateApi)
|
||||
services.AddDownstreamWebApiService(Configuration);
|
||||
.AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi"))
|
||||
#endif
|
||||
#if (GenerateGraph)
|
||||
services.AddMicrosoftGraph(Configuration.GetValue<string>("CalledApi:CalledApiScopes")?.Split(' '),
|
||||
Configuration.GetValue<string>("CalledApi:CalledApiUrl"));
|
||||
.AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
|
||||
#endif
|
||||
.AddInMemoryTokenCaches();
|
||||
#else
|
||||
.AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd"));
|
||||
#endif
|
||||
#elif (IndividualB2CAuth)
|
||||
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
#if (GenerateApi)
|
||||
services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAdB2C")
|
||||
.AddMicrosoftWebApiCallsWebApi(Configuration, "AzureAdB2C")
|
||||
.AddInMemoryTokenCaches();
|
||||
|
||||
services.AddDownstreamWebApiService(Configuration);
|
||||
.AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAdB2C"))
|
||||
.EnableTokenAcquisitionToCallDownstreamApi()
|
||||
.AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi"))
|
||||
.AddInMemoryTokenCaches();
|
||||
#else
|
||||
services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAdB2C");
|
||||
.AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAdB2C"));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -21,10 +21,10 @@
|
|||
// },
|
||||
////#elseif (OrganizationalAuth)
|
||||
// "AzureAd": {
|
||||
//#if (!SingleOrgAuth)
|
||||
// "Instance": "https:////login.microsoftonline.com/common",
|
||||
//#else
|
||||
// "Instance": "https:////login.microsoftonline.com/",
|
||||
//#if (!SingleOrgAuth)
|
||||
// "TenantId": "common",
|
||||
//#else
|
||||
// "Domain": "qualified.domain.name",
|
||||
// "TenantId": "22222222-2222-2222-2222-222222222222",
|
||||
//#endif
|
||||
|
|
@ -38,16 +38,16 @@
|
|||
// },
|
||||
////#endif
|
||||
////#if (GenerateApiOrGraph)
|
||||
// "CalledApi": {
|
||||
// "DownstreamAPI": {
|
||||
// /*
|
||||
// 'CalledApiScopes' contains space separated scopes of the Web API you want to call. This can be:
|
||||
// 'Scopes' contains space separated scopes of the Web API you want to call. This can be:
|
||||
// - a scope for a V2 application (for instance api://b3682cc7-8b30-4bd2-aaba-080c6bf0fd31/access_as_user)
|
||||
// - a scope corresponding to a V1 application (for instance <App ID URI>/.default, where <App ID URI> is the
|
||||
// App ID URI of a legacy v1 Web application
|
||||
// Applications are registered in the https://portal.azure.com portal.
|
||||
// */
|
||||
// "CalledApiScopes": "user.read",
|
||||
// "CalledApiUrl": "[WebApiUrl]"
|
||||
// "BaseUrl": "[WebApiUrl]",
|
||||
// "Scopes": "user.read"
|
||||
// },
|
||||
////#endif
|
||||
"Logging": {
|
||||
|
|
|
|||
|
|
@ -94,19 +94,6 @@
|
|||
"exclude": [
|
||||
"Data/SqlServer/**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"condition": "(!GenerateApi)",
|
||||
"exclude": [
|
||||
"Services/DownstreamWebApi.cs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"condition": "(!GenerateGraph)",
|
||||
"exclude": [
|
||||
"Services/MicrosoftGraphServiceExtensions.cs",
|
||||
"Services/TokenAcquisitionCredentialProvider.cs"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ using Microsoft.Extensions.Logging;
|
|||
namespace Company.WebApplication1.Pages
|
||||
{
|
||||
#if (GenerateApiOrGraph)
|
||||
[AuthorizeForScopes(ScopeKeySection = "CalledApi:CalledApiScopes")]
|
||||
[AuthorizeForScopes(ScopeKeySection = "DownstreamApi:Scopes")]
|
||||
#endif
|
||||
public class IndexModel : PageModel
|
||||
{
|
||||
|
|
@ -36,7 +36,17 @@ namespace Company.WebApplication1.Pages
|
|||
|
||||
public async Task OnGet()
|
||||
{
|
||||
ViewData["ApiResult"] = await _downstreamWebApi.CallWebApiAsync();
|
||||
using var response = await _downstreamWebApi.CallWebApiForUserAsync("DownstreamApi").ConfigureAwait(false);
|
||||
if (response.StatusCode == System.Net.HttpStatusCode.OK)
|
||||
{
|
||||
var apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
ViewData["ApiResult"] = apiResult;
|
||||
}
|
||||
else
|
||||
{
|
||||
var error = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
throw new HttpRequestException($"Invalid status code in the HttpResponseMessage: {response.StatusCode}: {error}");
|
||||
}
|
||||
}
|
||||
#elseif (GenerateGraph)
|
||||
private readonly GraphServiceClient _graphServiceClient;
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Identity.Web;
|
||||
|
||||
namespace Company.WebApplication1
|
||||
{
|
||||
public interface IDownstreamWebApi
|
||||
{
|
||||
Task<string> CallWebApiAsync(string relativeEndpoint = "", string[] requiredScopes = null);
|
||||
}
|
||||
|
||||
public static class DownstreamWebApiExtensions
|
||||
{
|
||||
public static void AddDownstreamWebApiService(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
// https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests
|
||||
services.AddHttpClient<IDownstreamWebApi, DownstreamWebApi>();
|
||||
}
|
||||
}
|
||||
|
||||
public class DownstreamWebApi : IDownstreamWebApi
|
||||
{
|
||||
private readonly ITokenAcquisition _tokenAcquisition;
|
||||
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public DownstreamWebApi(
|
||||
ITokenAcquisition tokenAcquisition,
|
||||
IConfiguration configuration,
|
||||
HttpClient httpClient)
|
||||
{
|
||||
_tokenAcquisition = tokenAcquisition;
|
||||
_configuration = configuration;
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls the Web API with the required scopes
|
||||
/// </summary>
|
||||
/// <param name="requireScopes">[Optional] Scopes required to call the Web API. If
|
||||
/// not specified, uses scopes from the configuration</param>
|
||||
/// <param name="relativeEndpoint">Endpoint relative to the CalledApiUrl configuration</param>
|
||||
/// <returns>A JSON string representing the result of calling the Web API</returns>
|
||||
public async Task<string> CallWebApiAsync(string relativeEndpoint = "", string[] requiredScopes = null)
|
||||
{
|
||||
string[] scopes = requiredScopes ?? _configuration["CalledApi:CalledApiScopes"]?.Split(' ');
|
||||
string apiUrl = (_configuration["CalledApi:CalledApiUrl"] as string)?.TrimEnd('/') + $"/{relativeEndpoint}";
|
||||
|
||||
string accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(scopes);
|
||||
HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, apiUrl);
|
||||
httpRequestMessage.Headers.Add("Authorization", $"bearer {accessToken}");
|
||||
|
||||
string apiResult;
|
||||
var response = await _httpClient.SendAsync(httpRequestMessage);
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
apiResult = await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
apiResult = $"Error calling the API '{apiUrl}'";
|
||||
}
|
||||
|
||||
return apiResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Graph;
|
||||
using Microsoft.Identity.Web;
|
||||
|
||||
namespace Company.WebApplication1
|
||||
{
|
||||
public static class MicrosoftGraphServiceExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the Microsoft Graph client as a singleton.
|
||||
/// </summary>
|
||||
/// <param name="services">Service collection.</param>
|
||||
/// <param name="initialScopes">Initial scopes.</param>
|
||||
/// <param name="graphBaseUrl">Base URL for Microsoft graph. This can be
|
||||
/// changed for instance for applications running in national clouds</param>
|
||||
public static IServiceCollection AddMicrosoftGraph(this IServiceCollection services,
|
||||
IEnumerable<string> initialScopes,
|
||||
string graphBaseUrl = "https://graph.microsoft.com/v1.0")
|
||||
{
|
||||
services.AddTokenAcquisition(true);
|
||||
services.AddSingleton<GraphServiceClient, GraphServiceClient>(serviceProvider =>
|
||||
{
|
||||
var tokenAquisitionService = serviceProvider.GetService<ITokenAcquisition>();
|
||||
GraphServiceClient client = string.IsNullOrWhiteSpace(graphBaseUrl) ?
|
||||
new GraphServiceClient(new TokenAcquisitionCredentialProvider(tokenAquisitionService, initialScopes)) :
|
||||
new GraphServiceClient(graphBaseUrl, new TokenAcquisitionCredentialProvider(tokenAquisitionService, initialScopes));
|
||||
return client;
|
||||
});
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
using Microsoft.Graph;
|
||||
using Microsoft.Identity.Web;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Company.WebApplication1
|
||||
{
|
||||
internal class TokenAcquisitionCredentialProvider : IAuthenticationProvider
|
||||
{
|
||||
public TokenAcquisitionCredentialProvider(ITokenAcquisition tokenAcquisition, IEnumerable<string> initialScopes)
|
||||
{
|
||||
_tokenAcquisition = tokenAcquisition;
|
||||
_initialScopes = initialScopes;
|
||||
}
|
||||
|
||||
ITokenAcquisition _tokenAcquisition;
|
||||
IEnumerable<string> _initialScopes;
|
||||
|
||||
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
|
||||
{
|
||||
request.Headers.Add("Authorization",
|
||||
$"Bearer {await _tokenAcquisition.GetAccessTokenForUserAsync(_initialScopes)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,21 +4,11 @@ using System.Linq;
|
|||
using System.Threading.Tasks;
|
||||
#if (OrganizationalAuth || IndividualB2CAuth)
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
#endif
|
||||
#if (OrganizationalAuth)
|
||||
using Microsoft.Identity.Web;
|
||||
using Microsoft.Identity.Web.UI;
|
||||
using Microsoft.Identity.Web.TokenCacheProviders.InMemory;
|
||||
#if (MultiOrgAuth)
|
||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
||||
#endif
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
#endif
|
||||
#if (IndividualB2CAuth)
|
||||
using Microsoft.Identity.Web;
|
||||
using Microsoft.Identity.Web.UI;
|
||||
using Microsoft.Identity.Web.TokenCacheProviders.InMemory;
|
||||
#endif
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
#if (IndividualLocalAuth)
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
|
@ -73,28 +63,36 @@ namespace Company.WebApplication1
|
|||
.AddEntityFrameworkStores<ApplicationDbContext>();
|
||||
#elif (OrganizationalAuth)
|
||||
#if (GenerateApiOrGraph)
|
||||
services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd")
|
||||
.AddMicrosoftWebAppCallsWebApi(Configuration, "AzureAd")
|
||||
.AddInMemoryTokenCaches();
|
||||
#else
|
||||
services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd");
|
||||
var initialScopes = Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');
|
||||
|
||||
#endif
|
||||
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
|
||||
#if (GenerateApiOrGraph)
|
||||
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))
|
||||
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
|
||||
#if (GenerateApi)
|
||||
services.AddDownstreamWebApiService(Configuration);
|
||||
.AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi"))
|
||||
#endif
|
||||
#if (GenerateGraph)
|
||||
services.AddMicrosoftGraph(Configuration.GetValue<string>("CalledApi:CalledApiScopes")?.Split(' '),
|
||||
Configuration.GetValue<string>("CalledApi:CalledApiUrl"));
|
||||
.AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
|
||||
#endif
|
||||
.AddInMemoryTokenCaches();
|
||||
#else
|
||||
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"));
|
||||
#endif
|
||||
#elif (IndividualB2CAuth)
|
||||
#if (GenerateApi)
|
||||
services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C")
|
||||
.AddMicrosoftWebAppCallsWebApi(Configuration, "AzureAdB2C")
|
||||
.AddInMemoryTokenCaches();
|
||||
var initialScopes = Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');
|
||||
|
||||
services.AddDownstreamWebApiService(Configuration);
|
||||
#endif
|
||||
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
|
||||
#if (GenerateApi)
|
||||
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C"))
|
||||
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
|
||||
.AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi"))
|
||||
.AddInMemoryTokenCaches();
|
||||
#else
|
||||
services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C");
|
||||
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C"));
|
||||
#endif
|
||||
#endif
|
||||
#if (OrganizationalAuth)
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@
|
|||
// },
|
||||
////#elseif (OrganizationalAuth)
|
||||
// "AzureAd": {
|
||||
//#if (MultiOrgAuth)
|
||||
// "Instance": "https:////login.microsoftonline.com/common",
|
||||
//#elseif (SingleOrgAuth)
|
||||
// "Instance": "https:////login.microsoftonline.com/",
|
||||
//#if (MultiOrgAuth)
|
||||
// "TenantId": "common",
|
||||
//#elseif (SingleOrgAuth)
|
||||
// "Domain": "qualified.domain.name",
|
||||
// "TenantId": "22222222-2222-2222-2222-222222222222",
|
||||
//#endif
|
||||
|
|
@ -34,16 +34,16 @@
|
|||
// },
|
||||
////#endif
|
||||
////#if (GenerateApiOrGraph)
|
||||
// "CalledApi": {
|
||||
// "DownstreamApi": {
|
||||
// /*
|
||||
// 'CalledApiScopes' contains space separated scopes of the Web API you want to call. This can be:
|
||||
// 'Scopes' contains space separated scopes of the Web API you want to call. This can be:
|
||||
// - a scope for a V2 application (for instance api://b3682cc7-8b30-4bd2-aaba-080c6bf0fd31/access_as_user)
|
||||
// - a scope corresponding to a V1 application (for instance <App ID URI>/.default, where <App ID URI> is the
|
||||
// App ID URI of a legacy v1 Web application
|
||||
// Applications are registered in the https://portal.azure.com portal.
|
||||
// */
|
||||
// "CalledApiScopes": "user.read",
|
||||
// "CalledApiUrl": "[WebApiUrl]"
|
||||
// "BaseUrl": "[WebApiUrl]",
|
||||
// "Scopes": "user.read"
|
||||
// },
|
||||
////#endif
|
||||
////#if (IndividualLocalAuth)
|
||||
|
|
|
|||
|
|
@ -90,19 +90,6 @@
|
|||
"exclude": [
|
||||
"Data/SqlServer/**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"condition": "(!GenerateApi)",
|
||||
"exclude": [
|
||||
"Services/DownstreamWebApi.cs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"condition": "(!GenerateGraph)",
|
||||
"exclude": [
|
||||
"Services/MicrosoftGraphServiceExtensions.cs",
|
||||
"Services/TokenAcquisitionCredentialProvider.cs"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,11 +38,20 @@ namespace Company.WebApplication1.Controllers
|
|||
_downstreamWebApi = downstreamWebApi;
|
||||
}
|
||||
|
||||
[AuthorizeForScopes(ScopeKeySection = "CalledApi:CalledApiScopes")]
|
||||
[AuthorizeForScopes(ScopeKeySection = "DownstreamApi:Scopes")]
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
ViewData["ApiResult"] = await _downstreamWebApi.CallWebApiAsync();
|
||||
|
||||
using var response = await _downstreamWebApi.CallWebApiForUserAsync("DownstreamApi").ConfigureAwait(false);
|
||||
if (response.StatusCode == System.Net.HttpStatusCode.OK)
|
||||
{
|
||||
var apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
ViewData["ApiResult"] = apiResult;
|
||||
}
|
||||
else
|
||||
{
|
||||
var error = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
throw new HttpRequestException($"Invalid status code in the HttpResponseMessage: {response.StatusCode}: {error}");
|
||||
}
|
||||
return View();
|
||||
}
|
||||
#elseif (GenerateGraph)
|
||||
|
|
@ -55,7 +64,7 @@ namespace Company.WebApplication1.Controllers
|
|||
_graphServiceClient = graphServiceClient;
|
||||
}
|
||||
|
||||
[AuthorizeForScopes(ScopeKeySection = "CalledApi:CalledApiScopes")]
|
||||
[AuthorizeForScopes(ScopeKeySection = "DownstreamApi:Scopes")]
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var user = await _graphServiceClient.Me.Request().GetAsync();
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Identity.Web;
|
||||
|
||||
namespace Company.WebApplication1
|
||||
{
|
||||
public interface IDownstreamWebApi
|
||||
{
|
||||
Task<string> CallWebApiAsync(string relativeEndpoint = "", string[] requiredScopes = null);
|
||||
}
|
||||
|
||||
public static class DownstreamWebApiExtensions
|
||||
{
|
||||
public static void AddDownstreamWebApiService(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
// https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests
|
||||
services.AddHttpClient<IDownstreamWebApi, DownstreamWebApi>();
|
||||
}
|
||||
}
|
||||
|
||||
public class DownstreamWebApi : IDownstreamWebApi
|
||||
{
|
||||
private readonly ITokenAcquisition _tokenAcquisition;
|
||||
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public DownstreamWebApi(
|
||||
ITokenAcquisition tokenAcquisition,
|
||||
IConfiguration configuration,
|
||||
HttpClient httpClient)
|
||||
{
|
||||
_tokenAcquisition = tokenAcquisition;
|
||||
_configuration = configuration;
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls the Web API with the required scopes
|
||||
/// </summary>
|
||||
/// <param name="requireScopes">[Optional] Scopes required to call the Web API. If
|
||||
/// not specified, uses scopes from the configuration</param>
|
||||
/// <param name="relativeEndpoint">Endpoint relative to the CalledApiUrl configuration</param>
|
||||
/// <returns>A JSON string representing the result of calling the Web API</returns>
|
||||
public async Task<string> CallWebApiAsync(string relativeEndpoint = "", string[] requiredScopes = null)
|
||||
{
|
||||
string[] scopes = requiredScopes ?? _configuration["CalledApi:CalledApiScopes"]?.Split(' ');
|
||||
string apiUrl = (_configuration["CalledApi:CalledApiUrl"] as string)?.TrimEnd('/') + $"/{relativeEndpoint}";
|
||||
|
||||
string accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(scopes);
|
||||
HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, apiUrl);
|
||||
httpRequestMessage.Headers.Add("Authorization", $"bearer {accessToken}");
|
||||
|
||||
string apiResult;
|
||||
var response = await _httpClient.SendAsync(httpRequestMessage);
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
apiResult = await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
apiResult = $"Error calling the API '{apiUrl}'";
|
||||
}
|
||||
|
||||
return apiResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Graph;
|
||||
using Microsoft.Identity.Web;
|
||||
|
||||
namespace Company.WebApplication1
|
||||
{
|
||||
public static class MicrosoftGraphServiceExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the Microsoft Graph client as a singleton.
|
||||
/// </summary>
|
||||
/// <param name="services">Service collection.</param>
|
||||
/// <param name="initialScopes">Initial scopes.</param>
|
||||
/// <param name="graphBaseUrl">Base URL for Microsoft graph. This can be
|
||||
/// changed for instance for applications running in national clouds</param>
|
||||
public static IServiceCollection AddMicrosoftGraph(this IServiceCollection services,
|
||||
IEnumerable<string> initialScopes,
|
||||
string graphBaseUrl = "https://graph.microsoft.com/v1.0")
|
||||
{
|
||||
services.AddTokenAcquisition(true);
|
||||
services.AddSingleton<GraphServiceClient, GraphServiceClient>(serviceProvider =>
|
||||
{
|
||||
var tokenAquisitionService = serviceProvider.GetService<ITokenAcquisition>();
|
||||
GraphServiceClient client = string.IsNullOrWhiteSpace(graphBaseUrl) ?
|
||||
new GraphServiceClient(new TokenAcquisitionCredentialProvider(tokenAquisitionService, initialScopes)) :
|
||||
new GraphServiceClient(graphBaseUrl, new TokenAcquisitionCredentialProvider(tokenAquisitionService, initialScopes));
|
||||
return client;
|
||||
});
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
using Microsoft.Graph;
|
||||
using Microsoft.Identity.Web;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Company.WebApplication1
|
||||
{
|
||||
internal class TokenAcquisitionCredentialProvider : IAuthenticationProvider
|
||||
{
|
||||
public TokenAcquisitionCredentialProvider(ITokenAcquisition tokenAcquisition, IEnumerable<string> initialScopes)
|
||||
{
|
||||
_tokenAcquisition = tokenAcquisition;
|
||||
_initialScopes = initialScopes;
|
||||
}
|
||||
|
||||
ITokenAcquisition _tokenAcquisition;
|
||||
IEnumerable<string> _initialScopes;
|
||||
|
||||
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
|
||||
{
|
||||
request.Headers.Add("Authorization",
|
||||
$"Bearer {await _tokenAcquisition.GetAccessTokenForUserAsync(_initialScopes)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,20 +4,10 @@ using System.Linq;
|
|||
using System.Threading.Tasks;
|
||||
#if (OrganizationalAuth || IndividualB2CAuth)
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
#endif
|
||||
#if (OrganizationalAuth)
|
||||
using Microsoft.Identity.Web;
|
||||
using Microsoft.Identity.Web.UI;
|
||||
using Microsoft.Identity.Web.TokenCacheProviders.InMemory;
|
||||
#if (MultiOrgAuth)
|
||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
||||
#endif
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
#endif
|
||||
#if (IndividualB2CAuth)
|
||||
using Microsoft.Identity.Web;
|
||||
using Microsoft.Identity.Web.UI;
|
||||
using Microsoft.Identity.Web.TokenCacheProviders.InMemory;
|
||||
#endif
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
#if (IndividualLocalAuth)
|
||||
|
|
@ -74,28 +64,36 @@ namespace Company.WebApplication1
|
|||
.AddEntityFrameworkStores<ApplicationDbContext>();
|
||||
#elif (OrganizationalAuth)
|
||||
#if (GenerateApiOrGraph)
|
||||
services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd")
|
||||
.AddMicrosoftWebAppCallsWebApi(Configuration, "AzureAd")
|
||||
.AddInMemoryTokenCaches();
|
||||
#else
|
||||
services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd");
|
||||
var initialScopes = Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');
|
||||
|
||||
#endif
|
||||
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
|
||||
#if (GenerateApiOrGraph)
|
||||
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))
|
||||
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
|
||||
#if (GenerateApi)
|
||||
services.AddDownstreamWebApiService(Configuration);
|
||||
.AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi"))
|
||||
#endif
|
||||
#if (GenerateGraph)
|
||||
services.AddMicrosoftGraph(Configuration.GetValue<string>("CalledApi:CalledApiScopes")?.Split(' '),
|
||||
Configuration.GetValue<string>("CalledApi:CalledApiUrl"));
|
||||
.AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
|
||||
#endif
|
||||
.AddInMemoryTokenCaches();
|
||||
#else
|
||||
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"));
|
||||
#endif
|
||||
#elif (IndividualB2CAuth)
|
||||
#if (GenerateApi)
|
||||
services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C")
|
||||
.AddMicrosoftWebAppCallsWebApi(Configuration, "AzureAdB2C")
|
||||
.AddInMemoryTokenCaches();
|
||||
var initialScopes = Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');
|
||||
|
||||
services.AddDownstreamWebApiService(Configuration);
|
||||
#endif
|
||||
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
|
||||
#if (GenerateApi)
|
||||
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C"))
|
||||
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
|
||||
.AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi"))
|
||||
.AddInMemoryTokenCaches();
|
||||
#else
|
||||
services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C");
|
||||
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C"));
|
||||
#endif
|
||||
#endif
|
||||
#if (OrganizationalAuth)
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@
|
|||
// },
|
||||
////#elseif (OrganizationalAuth)
|
||||
// "AzureAd": {
|
||||
//#if (MultiOrgAuth)
|
||||
// "Instance": "https:////login.microsoftonline.com/common",
|
||||
//#elseif (SingleOrgAuth)
|
||||
// "Instance": "https:////login.microsoftonline.com/",
|
||||
//#if (MultiOrgAuth)
|
||||
// "TenantId": "common",
|
||||
//#elseif (SingleOrgAuth)
|
||||
// "Domain": "qualified.domain.name",
|
||||
// "TenantId": "22222222-2222-2222-2222-222222222222",
|
||||
//#endif
|
||||
|
|
@ -34,16 +34,16 @@
|
|||
// },
|
||||
////#endif
|
||||
////#if (GenerateApiOrGraph)
|
||||
// "CalledApi": {
|
||||
// "DownstreamApi": {
|
||||
// /*
|
||||
// 'CalledApiScopes' contains space separated scopes of the Web API you want to call. This can be:
|
||||
// 'Scopes' contains space separated scopes of the Web API you want to call. This can be:
|
||||
// - a scope for a V2 application (for instance api://b3682cc7-8b30-4bd2-aaba-080c6bf0fd31/access_as_user)
|
||||
// - a scope corresponding to a V1 application (for instance <App ID URI>/.default, where <App ID URI> is the
|
||||
// App ID URI of a legacy v1 Web application
|
||||
// Applications are registered in the https://portal.azure.com portal.
|
||||
// */
|
||||
// "CalledApiScopes": "user.read",
|
||||
// "CalledApiUrl": "[WebApiUrl]"
|
||||
// "BaseUrl": "[WebApiUrl]",
|
||||
// "Scopes": "user.read"
|
||||
// },
|
||||
////#endif
|
||||
////#if (IndividualLocalAuth)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,10 @@
|
|||
"longName": "tenant-id",
|
||||
"shortName": ""
|
||||
},
|
||||
"DefaultScope": {
|
||||
"longName": "default-scope",
|
||||
"shortName": ""
|
||||
},
|
||||
"Framework": {
|
||||
"longName": "framework"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -36,19 +36,6 @@
|
|||
"exclude": [
|
||||
"Properties/launchSettings.json"
|
||||
]
|
||||
},
|
||||
{
|
||||
"condition": "(!GenerateApi)",
|
||||
"exclude": [
|
||||
"Services/DownstreamWebApi.cs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"condition": "(!GenerateGraph)",
|
||||
"exclude": [
|
||||
"Services/MicrosoftGraphServiceExtensions.cs",
|
||||
"Services/TokenAcquisitionCredentialProvider.cs"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -111,6 +98,13 @@
|
|||
"replaces": "qualified.domain.name",
|
||||
"description": "The domain for the directory tenant (use with SingleOrg or IndividualB2C auth)."
|
||||
},
|
||||
"DefaultScope": {
|
||||
"type": "parameter",
|
||||
"datatype": "string",
|
||||
"replaces": "api-scope",
|
||||
"defaultValue": "access_as_user",
|
||||
"description": "The API scope the client needs to request to provision an access token. (use with IndividualB2C, SingleOrg)."
|
||||
},
|
||||
"TenantId": {
|
||||
"type": "parameter",
|
||||
"datatype": "string",
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ namespace Company.WebApplication1.Controllers
|
|||
|
||||
private readonly ILogger<WeatherForecastController> _logger;
|
||||
|
||||
// The Web API will only accept tokens 1) for users, and 2) having the access_as_user scope for this API
|
||||
static readonly string[] scopeRequiredByApi = new string[] { "access_as_user" };
|
||||
// The Web API will only accept tokens 1) for users, and 2) having the "api-scope" scope for this API
|
||||
static readonly string[] scopeRequiredByApi = new string[] { "api-scope" };
|
||||
|
||||
#if (GenerateApi)
|
||||
private readonly IDownstreamWebApi _downstreamWebApi;
|
||||
|
|
@ -54,7 +54,17 @@ namespace Company.WebApplication1.Controllers
|
|||
{
|
||||
HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);
|
||||
|
||||
string downstreamApiResult = await _downstreamWebApi.CallWebApiAsync();
|
||||
using var response = await _downstreamWebApi.CallWebApiForUserAsync("DownstreamApi").ConfigureAwait(false);
|
||||
if (response.StatusCode == System.Net.HttpStatusCode.OK)
|
||||
{
|
||||
var apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
// Do something
|
||||
}
|
||||
else
|
||||
{
|
||||
var error = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
throw new HttpRequestException($"Invalid status code in the HttpResponseMessage: {response.StatusCode}: {error}");
|
||||
}
|
||||
|
||||
var rng = new Random();
|
||||
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Identity.Web;
|
||||
|
||||
namespace Company.WebApplication1
|
||||
{
|
||||
public interface IDownstreamWebApi
|
||||
{
|
||||
Task<string> CallWebApiAsync(string relativeEndpoint = "", string[] requiredScopes = null);
|
||||
}
|
||||
|
||||
public static class DownstreamWebApiExtensions
|
||||
{
|
||||
public static void AddDownstreamWebApiService(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
// https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests
|
||||
services.AddHttpClient<IDownstreamWebApi, DownstreamWebApi>();
|
||||
}
|
||||
}
|
||||
|
||||
public class DownstreamWebApi : IDownstreamWebApi
|
||||
{
|
||||
private readonly ITokenAcquisition _tokenAcquisition;
|
||||
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public DownstreamWebApi(
|
||||
ITokenAcquisition tokenAcquisition,
|
||||
IConfiguration configuration,
|
||||
HttpClient httpClient)
|
||||
{
|
||||
_tokenAcquisition = tokenAcquisition;
|
||||
_configuration = configuration;
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls the Web API with the required scopes
|
||||
/// </summary>
|
||||
/// <param name="requireScopes">[Optional] Scopes required to call the Web API. If
|
||||
/// not specified, uses scopes from the configuration</param>
|
||||
/// <param name="relativeEndpoint">Endpoint relative to the CalledApiUrl configuration</param>
|
||||
/// <returns>A JSON string representing the result of calling the Web API</returns>
|
||||
public async Task<string> CallWebApiAsync(string relativeEndpoint = "", string[] requiredScopes = null)
|
||||
{
|
||||
string[] scopes = requiredScopes ?? _configuration["CalledApi:CalledApiScopes"]?.Split(' ');
|
||||
string apiUrl = (_configuration["CalledApi:CalledApiUrl"] as string)?.TrimEnd('/') + $"/{relativeEndpoint}";
|
||||
|
||||
string accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(scopes);
|
||||
HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, apiUrl);
|
||||
httpRequestMessage.Headers.Add("Authorization", $"bearer {accessToken}");
|
||||
|
||||
string apiResult;
|
||||
var response = await _httpClient.SendAsync(httpRequestMessage);
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
apiResult = await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
apiResult = $"Error calling the API '{apiUrl}'";
|
||||
}
|
||||
|
||||
return apiResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Graph;
|
||||
using Microsoft.Identity.Web;
|
||||
|
||||
namespace Company.WebApplication1
|
||||
{
|
||||
public static class MicrosoftGraphServiceExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the Microsoft Graph client as a singleton.
|
||||
/// </summary>
|
||||
/// <param name="services">Service collection.</param>
|
||||
/// <param name="initialScopes">Initial scopes.</param>
|
||||
/// <param name="graphBaseUrl">Base URL for Microsoft graph. This can be
|
||||
/// changed for instance for applications running in national clouds</param>
|
||||
public static IServiceCollection AddMicrosoftGraph(this IServiceCollection services,
|
||||
IEnumerable<string> initialScopes,
|
||||
string graphBaseUrl = "https://graph.microsoft.com/v1.0")
|
||||
{
|
||||
services.AddTokenAcquisition(true);
|
||||
services.AddSingleton<GraphServiceClient, GraphServiceClient>(serviceProvider =>
|
||||
{
|
||||
var tokenAquisitionService = serviceProvider.GetService<ITokenAcquisition>();
|
||||
GraphServiceClient client = string.IsNullOrWhiteSpace(graphBaseUrl) ?
|
||||
new GraphServiceClient(new TokenAcquisitionCredentialProvider(tokenAquisitionService, initialScopes)) :
|
||||
new GraphServiceClient(graphBaseUrl, new TokenAcquisitionCredentialProvider(tokenAquisitionService, initialScopes));
|
||||
return client;
|
||||
});
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
using Microsoft.Graph;
|
||||
using Microsoft.Identity.Web;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Company.WebApplication1
|
||||
{
|
||||
internal class TokenAcquisitionCredentialProvider : IAuthenticationProvider
|
||||
{
|
||||
public TokenAcquisitionCredentialProvider(ITokenAcquisition tokenAcquisition, IEnumerable<string> initialScopes)
|
||||
{
|
||||
_tokenAcquisition = tokenAcquisition;
|
||||
_initialScopes = initialScopes;
|
||||
}
|
||||
|
||||
ITokenAcquisition _tokenAcquisition;
|
||||
IEnumerable<string> _initialScopes;
|
||||
|
||||
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
|
||||
{
|
||||
request.Headers.Add("Authorization",
|
||||
$"Bearer {await _tokenAcquisition.GetAccessTokenForUserAsync(_initialScopes)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,14 +10,8 @@ using Microsoft.AspNetCore.HttpsPolicy;
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
#if (OrganizationalAuth || IndividualB2CAuth)
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
#endif
|
||||
#if (OrganizationalAuth)
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.Identity.Web;
|
||||
using Microsoft.Identity.Web.TokenCacheProviders.InMemory;
|
||||
#endif
|
||||
#if (IndividualB2CAuth)
|
||||
using Microsoft.Identity.Web;
|
||||
using Microsoft.Identity.Web.TokenCacheProviders.InMemory;
|
||||
#endif
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
|
@ -45,32 +39,29 @@ namespace Company.WebApplication1
|
|||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
#if (OrganizationalAuth)
|
||||
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
#if (GenerateApiOrGraph)
|
||||
// Adds Microsoft Identity platform (AAD v2.0) support to protect this Api
|
||||
services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAd")
|
||||
.AddMicrosoftWebApiCallsWebApi(Configuration, "AzureAd")
|
||||
.AddInMemoryTokenCaches();
|
||||
|
||||
#else
|
||||
// Adds Microsoft Identity platform (AAD v2.0) support to protect this Api
|
||||
services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAd");
|
||||
#endif
|
||||
.AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd"))
|
||||
.EnableTokenAcquisitionToCallDownstreamApi()
|
||||
#if (GenerateApi)
|
||||
services.AddDownstreamWebApiService(Configuration);
|
||||
.AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi"))
|
||||
#endif
|
||||
#if (GenerateGraph)
|
||||
services.AddMicrosoftGraph(Configuration.GetValue<string>("CalledApi:CalledApiScopes")?.Split(' '),
|
||||
Configuration.GetValue<string>("CalledApi:CalledApiUrl"));
|
||||
.AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
|
||||
#endif
|
||||
.AddInMemoryTokenCaches();
|
||||
#else
|
||||
.AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd"));
|
||||
#endif
|
||||
#elif (IndividualB2CAuth)
|
||||
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
#if (GenerateApi)
|
||||
services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAdB2C")
|
||||
.AddMicrosoftWebApiCallsWebApi(Configuration, "AzureAdB2C")
|
||||
.AddInMemoryTokenCaches();
|
||||
|
||||
services.AddDownstreamWebApiService(Configuration);
|
||||
.AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAdB2C"))
|
||||
.EnableTokenAcquisitionToCallDownstreamApi()
|
||||
.AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi"))
|
||||
.AddInMemoryTokenCaches();
|
||||
#else
|
||||
services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAdB2C");
|
||||
.AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAdB2C"));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
|
@ -98,6 +89,7 @@ namespace Company.WebApplication1
|
|||
|
||||
app.UseHttpsRedirection();
|
||||
#endif
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
#if (OrganizationalAuth || IndividualAuth)
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@
|
|||
// },
|
||||
////#elseif (OrganizationalAuth)
|
||||
// "AzureAd": {
|
||||
//#if (!SingleOrgAuth)
|
||||
// "Instance": "https:////login.microsoftonline.com/common",
|
||||
//#else
|
||||
// "Instance": "https:////login.microsoftonline.com/",
|
||||
//#if (!SingleOrgAuth)
|
||||
// "TenantId": "common",
|
||||
//#else
|
||||
// "Domain": "qualified.domain.name",
|
||||
// "TenantId": "22222222-2222-2222-2222-222222222222",
|
||||
//#endif
|
||||
|
|
@ -31,16 +31,16 @@
|
|||
// },
|
||||
////#endif
|
||||
////#if (GenerateApiOrGraph)
|
||||
// "CalledApi": {
|
||||
// "DownstreamAPI": {
|
||||
// /*
|
||||
// 'CalledApiScopes' contains space separated scopes of the Web API you want to call. This can be:
|
||||
// 'Scopes' contains space separated scopes of the Web API you want to call. This can be:
|
||||
// - a scope for a V2 application (for instance api://b3682cc7-8b30-4bd2-aaba-080c6bf0fd31/access_as_user)
|
||||
// - a scope corresponding to a V1 application (for instance <App ID URI>/.default, where <App ID URI> is the
|
||||
// App ID URI of a legacy v1 Web application
|
||||
// Applications are registered in the https://portal.azure.com portal.
|
||||
// */
|
||||
// "CalledApiScopes": "user.read",
|
||||
// "CalledApiUrl": "[WebApiUrl]"
|
||||
// "BaseUrl": "[WebApiUrl]",
|
||||
// "Scopes": "user.read"
|
||||
// },
|
||||
////#endif
|
||||
"Logging": {
|
||||
|
|
|
|||
Loading…
Reference in New Issue