Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
// Licensed under the MIT License.

using System.Net.Mime;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Microsoft.Identity.Abstractions;
using Microsoft.Identity.Client;
using Microsoft.Identity.Web.Sidecar.Logging;
using Microsoft.Identity.Web.Sidecar.Models;

Expand Down Expand Up @@ -81,6 +81,13 @@ private static async Task<Results<Ok<AuthorizationHeaderResult>, ProblemHttpResu
detail: ex.InnerException?.Message ?? ex.Message,
statusCode: StatusCodes.Status401Unauthorized);
}
catch(MsalServiceException ex)
{
logger.AuthorizationHeaderAsyncError(ex);
return TypedResults.Problem(
detail: ex.Message,
statusCode: StatusCodes.Status401Unauthorized);
}
catch (Exception ex)
{
logger.AuthorizationHeaderAsyncError(ex);
Expand Down
3 changes: 1 addition & 2 deletions src/Microsoft.Identity.Web.Sidecar/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ public static void Main(string[] args)

builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))

.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"), subscribeToJwtBearerMiddlewareDiagnosticsEvents: true)
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();

Expand Down
37 changes: 18 additions & 19 deletions src/Microsoft.Identity.Web.Sidecar/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,30 @@ before the project can be successfully executed.
For more info see https://aka.ms/dotnet-template-ms-identity-platform
*/
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "f645ad92-e38d-4d1a-b510-d1b09a74a8ca",
"ClientId": "556d438d-2f4b-4add-9713-ede4e5f5d7da", //"712ae8d7-548a-4306-95b6-ee9117ee86f0", JWE clientID
"Instance": "", // https://login.microsoftonline.com/
"TenantId": "", // f645ad92-e38d-4d1a-b510-d1b09a74a8ca
"ClientId": "", //"556d438d-2f4b-4add-9713-ede4e5f5d7da"

"Scopes": "access_as_user",
// "Scopes": "access_as_user" // access_as_user

"ClientCredentials": [
{
"SourceType": "StoreWithDistinguishedName",
"CertificateStorePath": "LocalMachine/My",
"CertificateDistinguishedName": "CN=LabAuth.MSIDLab.com"
}
],
// "ClientCredentials": [
// {
// "SourceType": "StoreWithDistinguishedName",
// "CertificateStorePath": "LocalMachine/My",
// "CertificateDistinguishedName": "CN=LabAuth.MSIDLab.com"
// }
// ],

"EnablePiiLogging": false,
"AllowWebApiToBeAuthorizedByACL": true
},

"DownstreamApis": {
"me": {
"BaseUrl": "https://graph.microsoft.com/v1.0/",
"RelativePath": "me",
"Scopes": [ "User.Read" ]
}
},
//"DownstreamApis": {
// "me": {
// "BaseUrl": "https://graph.microsoft.com/v1.0/",
// "RelativePath": "me",
// "Scopes": [ "User.Read" ]
// }
//},

"Logging": {
"LogLevel": {
Expand Down
105 changes: 93 additions & 12 deletions tests/E2E Tests/Sidecar.Tests/EndpointE2ETests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,42 @@
using System.Net.Http.Headers;
using System.Net.Http.Json;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Identity.Abstractions;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.Sidecar;
using Microsoft.Identity.Web.Sidecar.Models;
using Microsoft.Identity.Web.TokenCacheProviders.InMemory;
using Xunit;

namespace Sidecar.Tests;

public class SidecarApiFactory : WebApplicationFactory<Program>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
readonly Action<IConfigurationBuilder> _configureOptions;

public SidecarApiFactory() : this(null)
{
builder.ConfigureAppConfiguration(builder =>
}

internal SidecarApiFactory(Action<IConfigurationBuilder>? configureOptions)
{
_configureOptions = configureOptions ?? (builder =>
{
builder.AddJsonFile(
path: Path.Combine(Directory.GetCurrentDirectory().ToString(), "appsettings.agentids.json"),
optional: false,
reloadOnChange: true);
path: Path.Combine(Directory.GetCurrentDirectory().ToString(), "appsettings.agentids.json"),
optional: false,
reloadOnChange: true);
});
}


protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureAppConfiguration(_configureOptions);
builder.ConfigureServices(services =>
{
// Given we add the Json file after the initial configuration, and that
Expand All @@ -46,8 +60,11 @@ public class EndpointsE2ETests : IClassFixture<SidecarApiFactory>
private readonly SidecarApiFactory _factory;

public EndpointsE2ETests(SidecarApiFactory factory) => _factory = factory;
string agentIdentity = "d84da24a-2ea2-42b8-b5ab-8637ec208024"; // Replace with the actual agent identity
string userUpn = "[email protected]"; // Replace with the actual user upn.

const string TenantId = "31a58c3b-ae9c-4448-9e8f-e9e143e800df"; // Replace with your tenant ID
const string AgentApplication = "d15884b6-a447-4dd5-a5a5-a668c49f6300"; // Replace with the actual agent application client ID
const string AgentIdentity = "d84da24a-2ea2-42b8-b5ab-8637ec208024"; // Replace with the actual agent identity
const string UserUpn = "[email protected]"; // Replace with the actual user upn.

[Fact]
public async Task Validate_WhenBadTokenAsync()
Expand Down Expand Up @@ -80,20 +97,31 @@ public async Task Validate_WhenGoodTokenAsync()
[Fact]
public async Task GetAuthorizationHeaderForAgentUserIdentityAuthenticated()
{
string agentIdentity = "d84da24a-2ea2-42b8-b5ab-8637ec208024"; // Replace with the actual agent identity
string userUpn = "[email protected]"; // Replace with the actual user upn.

// Getting a token to call the API.
string authorizationHeader = await GetAuthorizationHeaderToCallTheSideCarAsync();

// Calling the API
var client = _factory.CreateClient();

client.DefaultRequestHeaders.Authorization = AuthenticationHeaderValue.Parse(authorizationHeader);
var response = await client.PostAsync($"/AuthorizationHeader/MsGraph?agentidentity={agentIdentity}&agentUsername={userUpn}", null);
var response = await client.PostAsync($"/AuthorizationHeader/MsGraph?agentidentity={AgentIdentity}&agentUsername={UserUpn}", null);
var content = await response.Content.ReadAsStringAsync();
Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode);
}

[Fact]
public async Task DownstreamApiForAgentUserIdentityAuthenticated()
{
// Getting a token to call the API.
string authorizationHeader = await GetAuthorizationHeaderToCallTheSideCarAsync();

// Calling the API
var client = _factory.CreateClient();

client.DefaultRequestHeaders.Authorization = AuthenticationHeaderValue.Parse(authorizationHeader);
var response = await client.PostAsync($"/DownstreamApi/MsGraph?agentidentity={AgentIdentity}&agentUsername={UserUpn}", null);
var content = await response.Content.ReadAsStringAsync();
Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode);
}

[Fact]
Expand All @@ -102,10 +130,63 @@ public async Task GetAuthorizationHeaderForAgentUserIdentityUnauthenticated()
// Calling the API
var client = _factory.CreateClient();

var response = await client.PostAsync($"/AuthorizationHeader/MsGraph?agentidentity={agentIdentity}&agentUsername={userUpn}", null);
var response = await client.PostAsync($"/AuthorizationHeader/MsGraph?agentidentity={AgentIdentity}&agentUsername={UserUpn}", null);
var content = await response.Content.ReadAsStringAsync();
Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode);
}

[Fact]
public async Task GetAuthorizationHeaderForAgentUserIdentityUnauthenticated_WithOptionsOverride()
{
var client = _factory.CreateClient();

var result = await client.PostAsync(
$"/AuthorizationHeader/AgentUserIdentityCallsGraph?AgentIdentity={AgentIdentity}&AgentUsername={UserUpn}",
JsonContent.Create(new DownstreamApiOptions()
{
AcquireTokenOptions = new()
{
Tenant = TenantId,
},
Scopes = ["user.read"]
}));

Assert.True(result.IsSuccessStatusCode);

var response = await result.Content.ReadFromJsonAsync<AuthorizationHeaderResult>();

Assert.NotNull(response?.AuthorizationHeader);
Assert.StartsWith("Bearer ey", response.AuthorizationHeader, StringComparison.Ordinal);
}

[Fact]
public async Task GetDownstreamApiForAgentUserIdentityUnauthenticated()
{
// Calling the API
var client = _factory.CreateClient();
var response = await client.PostAsync($"/DownstreamApi/MsGraph?agentidentity={AgentIdentity}&agentUsername={UserUpn}", null);
Assert.Equal(System.Net.HttpStatusCode.Unauthorized, response.StatusCode);
}

[Fact]
public async Task TestAgentIdentityConfiguration_InvalidTenant()
{
var client = _factory.CreateClient();

var result = await client.PostAsync(
$"/AuthorizationHeader/AgentUserIdentityCallsGraph?AgentIdentity={AgentIdentity}&AgentUsername={UserUpn}",
JsonContent.Create(new DownstreamApiOptions()
{
AcquireTokenOptions = new()
{
Tenant = "invalid-tenant-id"
},
Scopes = ["user.read"]
}));

var response = await result.Content.ReadFromJsonAsync<ProblemDetails>();

Assert.Equal(401, response?.Status);
}


Expand Down