Skip to content

Commit

Permalink
Exercising the MSAL long running API (#1523)
Browse files Browse the repository at this point in the history
* execising the MSAL long running API

* Fixing a couple of things

* Updating to fixed MSAL code.
See AzureAD/microsoft-authentication-library-for-dotnet#2820
Commit: AzureAD/microsoft-authentication-library-for-dotnet@37280d5

* Updating to MSAL.NET

* Update src/Microsoft.Identity.Web/TokenAcquisitionOptions.cs

Co-authored-by: jennyf19 <jeferrie@microsoft.com>
  • Loading branch information
jmprieur and jennyf19 authored Nov 19, 2021
1 parent 9b2b59e commit a05b316
Show file tree
Hide file tree
Showing 12 changed files with 2,774 additions and 169 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Identity.Client" Version="4.37.0" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.38.0" />

<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.205">
<PrivateAssets>all</PrivateAssets>
Expand Down
2,735 changes: 2,700 additions & 35 deletions src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml

Large diffs are not rendered by default.

42 changes: 37 additions & 5 deletions src/Microsoft.Identity.Web/TokenAcquisition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -777,15 +777,47 @@ private IConfidentialClientApplication BuildConfidentialClientApplication(Merged
// In the case the token is a JWE (encrypted token), we use the decrypted token.
string? tokenUsedToCallTheWebApi = GetActualToken(validatedToken);

AcquireTokenOnBehalfOfParameterBuilder? builder = null;

// Case of web APIs: we need to do an on-behalf-of flow, with the token used to call the API
if (tokenUsedToCallTheWebApi != null)
{
var builder = application
.AcquireTokenOnBehalfOf(
scopes.Except(_scopesRequestedByMsal),
new UserAssertion(tokenUsedToCallTheWebApi))
.WithSendX5C(mergedOptions.SendX5C);

if (string.IsNullOrEmpty(tokenAcquisitionOptions?.LongRunningWebApiSessionKey))
{
builder = application
.AcquireTokenOnBehalfOf(
scopes.Except(_scopesRequestedByMsal),
new UserAssertion(tokenUsedToCallTheWebApi));
}
else
{
string? sessionKey = tokenAcquisitionOptions.LongRunningWebApiSessionKey;
if (sessionKey == TokenAcquisitionOptions.LongRunningWebApiSessionKeyAuto)
{
sessionKey = null;
}

builder = (application as ILongRunningWebApi)?
.InitiateLongRunningProcessInWebApi(
scopes.Except(_scopesRequestedByMsal),
tokenUsedToCallTheWebApi,
ref sessionKey);
tokenAcquisitionOptions.LongRunningWebApiSessionKey = sessionKey;
}
}
else if (!string.IsNullOrEmpty(tokenAcquisitionOptions?.LongRunningWebApiSessionKey))
{
string sessionKey = tokenAcquisitionOptions.LongRunningWebApiSessionKey;
builder = (application as ILongRunningWebApi)?
.AcquireTokenInLongRunningProcess(
scopes.Except(_scopesRequestedByMsal),
sessionKey);
}

if (builder != null)
{
builder.WithSendX5C(mergedOptions.SendX5C);
if (!string.IsNullOrEmpty(tenantId))
{
builder.WithTenantId(tenantId);
Expand Down
16 changes: 16 additions & 0 deletions src/Microsoft.Identity.Web/TokenAcquisitionOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,22 @@ public class TokenAcquisitionOptions
/// </summary>
public CancellationToken CancellationToken { get; set; } = CancellationToken.None;

/// <summary>
/// Key used for long running web APIs that need to call downstream web
/// APIs on behalf of the user. Can be null, if you are not developing a long
/// running web API, <see cref="LongRunningWebApiSessionKeyAuto"/> if you want
/// Microsoft.Identity.Web to allocate a session key for you, or your own string
/// if you want to associate the session with some information you have externally
/// (for instance a Microsoft Graph hook identifier).
/// </summary>
public string? LongRunningWebApiSessionKey { get; set; }

/// <summary>
/// Value that can be used for <see cref="LongRunningWebApiSessionKey"/> so that
/// MSAL.NET allocates the long running web api session key for the developer.
/// </summary>
public static string LongRunningWebApiSessionKeyAuto = "AllocateForMe";

/// <summary>
/// Clone the options (to be able to override them).
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,14 @@ public class CallbackController : Controller
{
private readonly ITokenAcquisition _tokenAcquisition;
private ILogger _logger;
ILongRunningProcessContextFactory _longRunningProcessAssertionCache;

public CallbackController(
IHttpContextAccessor contextAccessor,
ITokenAcquisition tokenAcquisition,
ILogger<CallbackController> logger,
ILongRunningProcessContextFactory longRunningProcessAssertionCache)
ILogger<CallbackController> logger)
{
_tokenAcquisition = tokenAcquisition;
_logger = logger;
_longRunningProcessAssertionCache = longRunningProcessAssertionCache;
}


Expand All @@ -43,10 +40,12 @@ public async Task GetAsync(string key)

_logger.LogWarning($"{DateTime.UtcNow}: {calledUrl}");

using (await _longRunningProcessAssertionCache.UseKey(HttpContext, key))
{
TokenAcquisitionOptions tokenAcquisitionOptions = new TokenAcquisitionOptions()
{
LongRunningWebApiSessionKey = key
};
var result = await _tokenAcquisition.GetAuthenticationResultForUserAsync(
new string[] { "user.read" })
new string[] { "user.read" }, tokenAcquisitionOptions: tokenAcquisitionOptions)
.ConfigureAwait(false); // for testing OBO

_logger.LogWarning($"OBO token acquired from {result.AuthenticationResultMetadata.TokenSource} expires {result.ExpiresOn.UtcDateTime}");
Expand All @@ -57,7 +56,6 @@ public async Task GetAsync(string key)
{
}

}
}
}
}

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,12 @@ public class TodoListController : Controller
// The Web API will only accept tokens 1) for users, and 2) having the access_as_user scope for this API
// In-memory TodoList
private static readonly Dictionary<int, Todo> TodoStore = new Dictionary<int, Todo>();
ILongRunningProcessContextFactory _longRunningProcessAssertionCache;

public TodoListController(
IHttpContextAccessor contextAccessor,
ITokenAcquisition tokenAcquisition,
ILongRunningProcessContextFactory longRunningProcessAssertionCache)
ITokenAcquisition tokenAcquisition)
{
_tokenAcquisition = tokenAcquisition;
_longRunningProcessAssertionCache = longRunningProcessAssertionCache;

// Pre-populate with sample data
if (TodoStore.Count == 0)
Expand All @@ -55,6 +52,7 @@ public TodoListController(
public async Task<IEnumerable<Todo>> GetAsync()
{
string owner = User.GetDisplayName();

// Below is for testing multi-tenants
var result = await _tokenAcquisition.GetAccessTokenForUserAsync(new string[] { "user.read" }).ConfigureAwait(false); // for testing OBO

Expand All @@ -77,7 +75,13 @@ public async Task<IEnumerable<Todo>> GetAsync()
private async Task RegisterPeriodicCallbackForLongProcessing(string keyHint)
{
// Get the token incoming to the web API - we could do better here.
string key = await _longRunningProcessAssertionCache.CreateKey(HttpContext, keyHint);
TokenAcquisitionOptions tokenAcquisitionOptions = new TokenAcquisitionOptions()
{
LongRunningWebApiSessionKey = keyHint ?? TokenAcquisitionOptions.LongRunningWebApiSessionKeyAuto
};

_= await _tokenAcquisition.GetAuthenticationResultForUserAsync(new string[] { "user.read" }, tokenAcquisitionOptions: tokenAcquisitionOptions);
string key = tokenAcquisitionOptions.LongRunningWebApiSessionKey;

// Build the URL to the callback controller, based on the request.
var request = HttpContext.Request;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "https://localhost:44351",
"sslPort": 44351
"applicationUrl": "http://localhost:58713/",
"sslPort": 44361
}
},
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
Expand All @@ -26,4 +26,4 @@
"applicationUrl": "https://localhost:44351/"
}
}
}
}
2 changes: 0 additions & 2 deletions tests/WebAppCallsWebApiCallsGraph/TodoListService/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,6 @@ public void ConfigureServices(IServiceCollection services)
#endif

services.AddControllers();
services.AddSingleton<ILongRunningProcessContextFactory, LongRunningProcessContextFactory>();

#region
/*
// 1) With the requirement, applies to all endpoints which have [Authorize(Policy = "foo")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Identity.Client" Version="4.37.0" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.38.0" />
</ItemGroup>

<ItemGroup>
Expand Down

0 comments on commit a05b316

Please sign in to comment.