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

Added support for long-running On Behalf Of flow #651

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
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
91 changes: 91 additions & 0 deletions src/lib/PnP.Framework/AuthenticationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ public class AuthenticationManager : IDisposable
private readonly SecureString accessToken;
private readonly IAuthenticationProvider authenticationProvider;
private readonly PnPContext pnpContext;
private readonly string userToken;
private string sessionKey;

public CookieContainer CookieContainer { get; set; }

Expand Down Expand Up @@ -255,6 +257,20 @@ public static AuthenticationManager CreateWithOnBehalfOf(string clientId, string
return new AuthenticationManager(clientId, clientSecret, userAssertion, tenantId, azureEnvironment, tokenCacheCallback);
}

/// <summary>
/// Creates a new instance of the Authentication Manager to acquire authenticated ClientContext.
/// </summary>
/// <param name="clientId">The client id of the Azure AD application to use for authentication.</param>
/// <param name="clientSecret">The client secret of the Azure AD application to use for authentication.</param>
/// <param name="tenantId">Optional tenant id or tenant url</param>
/// <param name="azureEnvironment">The azure environment to use. Defaults to AzureEnvironment.Production</param>
/// <param name="userToken">The user token of the user on whose behalf to acquire the context</param>
/// <param name="tokenCacheCallback">If present, after setting up the base flow for authentication this callback will be called register a custom tokencache. See https://aka.ms/msal-net-token-cache-serialization.</param>
public static AuthenticationManager CreateWithLongRunningOnBehalfOf(string clientId, string clientSecret, string userToken, string tenantId = null, AzureEnvironment azureEnvironment = AzureEnvironment.Production, Action<ITokenCache> tokenCacheCallback = null)
{
return new AuthenticationManager(clientId, clientSecret, userToken, tenantId, azureEnvironment, tokenCacheCallback);
}

/// <summary>
/// Creates a new instance of the Authentication Manager to acquire an authenticated ClientContext.
/// </summary>
Expand Down Expand Up @@ -608,6 +624,45 @@ public AuthenticationManager(string clientId, string clientSecret, UserAssertion
authenticationType = ClientContextType.AzureOnBehalfOf;
}

/// <summary>
/// Creates a new instance of the Authentication Manager to acquire authenticated ClientContext.
/// </summary>
/// <param name="clientId">The client id of the Azure AD application to use for authentication.</param>
/// <param name="clientSecret">The client secret of the Azure AD application to use for authentication.</param>
/// <param name="tenantId">Optional tenant id or tenant url</param>
/// <param name="azureEnvironment">The azure environment to use. Defaults to AzureEnvironment.Production</param>
/// <param name="userToken">The user token of the user on whose behalf to acquire the context</param>
/// <param name="tokenCacheCallback"></param>
public AuthenticationManager(string clientId, string clientSecret, string userToken, string tenantId = null, AzureEnvironment azureEnvironment = AzureEnvironment.Production, Action<ITokenCache> tokenCacheCallback = null) : this()
{
this.azureEnvironment = azureEnvironment;
var azureADEndPoint = GetAzureADLoginEndPoint(azureEnvironment);

ConfidentialClientApplicationBuilder builder;
if (azureEnvironment != AzureEnvironment.Production)
{
if (tenantId == null)
{
throw new ArgumentException("tenantId is required", nameof(tenantId));
}
builder = ConfidentialClientApplicationBuilder.Create(clientId).WithClientSecret(clientSecret).WithAuthority(azureADEndPoint, tenantId, true).WithHttpClientFactory(HttpClientFactory);
}
else
{
builder = ConfidentialClientApplicationBuilder.Create(clientId).WithClientSecret(clientSecret).WithAuthority($"{azureADEndPoint}/organizations/").WithHttpClientFactory(HttpClientFactory);
if (!string.IsNullOrEmpty(tenantId))
{
builder = builder.WithTenantId(tenantId);
}
}
this.userToken = userToken;
confidentialClientApplication = builder.Build();

// register tokencache if callback provided
tokenCacheCallback?.Invoke(confidentialClientApplication.UserTokenCache);
authenticationType = ClientContextType.AzureLongRunningOnBehalfOf;
}

/// <summary>
/// Creates an AuthenticationManager for the given PnP Core SDK <see cref="IAuthenticationProvider"/>.
/// </summary>
Expand Down Expand Up @@ -791,6 +846,18 @@ public async Task<string> GetAccessTokenAsync(string[] scopes, CancellationToken
}
break;
}
case ClientContextType.AzureLongRunningOnBehalfOf:
{
try
{
authResult = await ((ILongRunningWebApi)confidentialClientApplication).AcquireTokenInLongRunningProcess(scopes, sessionKey).ExecuteAsync(cancellationToken);
}
catch
{
authResult = await ((ILongRunningWebApi)confidentialClientApplication).InitiateLongRunningProcessInWebApi(scopes, userToken, ref sessionKey).ExecuteAsync(cancellationToken);
}
break;
}
case ClientContextType.SharePointACSAppOnly:
{
if (acsTokenGenerator == null)
Expand Down Expand Up @@ -940,6 +1007,18 @@ public async Task<ClientContext> GetContextAsync(string siteUrl, CancellationTok
}
break;
}
case ClientContextType.AzureLongRunningOnBehalfOf:
{
try
{
authResult = await ((ILongRunningWebApi)confidentialClientApplication).AcquireTokenInLongRunningProcess(scopes, sessionKey).ExecuteAsync(cancellationToken);
}
catch
{
authResult = await ((ILongRunningWebApi)confidentialClientApplication).InitiateLongRunningProcessInWebApi(scopes, userToken, ref sessionKey).ExecuteAsync(cancellationToken);
}
break;
}
case ClientContextType.DeviceLogin:
{
var accounts = await publicClientApplication.GetAccountsAsync();
Expand Down Expand Up @@ -1106,6 +1185,18 @@ private ClientContext BuildClientContext(IClientApplicationBase application, str
ar = ((IConfidentialClientApplication)application).AcquireTokenOnBehalfOf(scopes, assertion).ExecuteAsync().GetAwaiter().GetResult();
break;
}
case ClientContextType.AzureLongRunningOnBehalfOf:
{
if (sessionKey != null)
{
ar = ((ILongRunningWebApi)application).AcquireTokenInLongRunningProcess(scopes, sessionKey).ExecuteAsync().GetAwaiter().GetResult();
}
else
{
ar = ((ILongRunningWebApi)application).InitiateLongRunningProcessInWebApi(scopes, userToken, ref sessionKey).ExecuteAsync().GetAwaiter().GetResult();
}
break;
}
case ClientContextType.DeviceLogin:
{
ar = ((IPublicClientApplication)application).AcquireTokenWithDeviceCode(scopes, deviceCodeCallback).ExecuteAsync().GetAwaiter().GetResult();
Expand Down
4 changes: 2 additions & 2 deletions src/lib/PnP.Framework/PnP.Framework.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@

<ItemGroup>
<!-- Required -->
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="2.18.4" />
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="2.20.1" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-*" PrivateAssets="All" />
<PackageReference Include="AngleSharp" Version="0.14.0" />
<PackageReference Include="AngleSharp.Css" Version="0.14.2" />
Expand All @@ -240,7 +240,7 @@
<PackageReference Include="Microsoft.Data.OData" Version="5.8.4" />
<PackageReference Include="Microsoft.Graph" Version="3.33.0" />
<PackageReference Include="Microsoft.Graph.Core" Version="1.25.1" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.36.1" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.43.2" />
<PackageReference Include="Microsoft.SharePointOnline.CSOM" Version="16.1.*" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="PnP.Core" Version="1.6.*-*" Condition="'$(PnPCoreSdkPath)' == ''" />
Expand Down
3 changes: 2 additions & 1 deletion src/lib/PnP.Framework/Utilities/Context/ClientContextType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public enum ClientContextType
DeviceLogin = 6,
OnPremises = 7,
AccessToken = 8,
PnPCoreSdk = 9
PnPCoreSdk = 9,
AzureLongRunningOnBehalfOf = 10
}
}