/// <summary> /// Gets an access token from a <see cref="ClientContext"/> instance. Only works when using an add-in or app-only authentication flow. /// </summary> /// <param name="clientContext"><see cref="ClientContext"/> instance to obtain an access token for</param> /// <returns>Access token for the given <see cref="ClientContext"/> instance</returns> public static string GetAccessToken(this ClientRuntimeContext clientContext) { string accessToken = null; if (PnPProvisioningContext.Current != null) { accessToken = PnPProvisioningContext.Current.AcquireToken(new Uri(clientContext.Url).Authority, null); } else { if (clientContext.GetContextSettings().AuthenticationManager != null) { var contextSettings = clientContext.GetContextSettings(); if (contextSettings.Type == ClientContextType.SharePointACSAppOnly) { } else { accessToken = contextSettings.AuthenticationManager.GetAccessTokenAsync(clientContext.Url).GetAwaiter().GetResult(); } } else { EventHandler <WebRequestEventArgs> handler = (s, e) => { string authorization = e.WebRequestExecutor.RequestHeaders["Authorization"]; if (!string.IsNullOrEmpty(authorization)) { accessToken = authorization.Replace("Bearer ", string.Empty); } }; // Issue a dummy request to get it from the Authorization header clientContext.ExecutingWebRequest += handler; clientContext.ExecuteQuery(); clientContext.ExecutingWebRequest -= handler; } } return(accessToken); }
/// <summary> /// Clones a ClientContext object while "taking over" the security context of the existing ClientContext instance /// </summary> /// <param name="clientContext">ClientContext to be cloned</param> /// <param name="siteUrl">Site URL to be used for cloned ClientContext</param> /// <param name="accessTokens">Dictionary of access tokens for sites URLs</param> /// <returns>A ClientContext object created for the passed site URL</returns> public static ClientContext Clone(this ClientRuntimeContext clientContext, Uri siteUrl, Dictionary <String, String> accessTokens = null) { if (siteUrl == null) { throw new ArgumentException(CoreResources.ClientContextExtensions_Clone_Url_of_the_site_is_required_, nameof(siteUrl)); } ClientContext clonedClientContext = new ClientContext(siteUrl); clonedClientContext.AuthenticationMode = clientContext.AuthenticationMode; clonedClientContext.ClientTag = clientContext.ClientTag; #if !ONPREMISES || SP2016 || SP2019 clonedClientContext.DisableReturnValueCache = clientContext.DisableReturnValueCache; #endif // In case of using networkcredentials in on premises or SharePointOnlineCredentials in Office 365 if (clientContext.Credentials != null) { clonedClientContext.Credentials = clientContext.Credentials; } else { // Check if we do have context settings var contextSettings = clientContext.GetContextSettings(); if (contextSettings != null) // We do have more information about this client context, so let's use it to do a more intelligent clone { string newSiteUrl = siteUrl.ToString(); // A diffent host = different audience ==> new access token is needed if (contextSettings.UsesDifferentAudience(newSiteUrl)) { // We need to create a new context using a new authentication manager as the token expiration is handled in there OfficeDevPnP.Core.AuthenticationManager authManager = new OfficeDevPnP.Core.AuthenticationManager(); ClientContext newClientContext = null; if (contextSettings.Type == ClientContextType.SharePointACSAppOnly) { newClientContext = authManager.GetAppOnlyAuthenticatedContext(newSiteUrl, TokenHelper.GetRealmFromTargetUrl(new Uri(newSiteUrl)), contextSettings.ClientId, contextSettings.ClientSecret, contextSettings.AcsHostUrl, contextSettings.GlobalEndPointPrefix); } #if !ONPREMISES && !NETSTANDARD2_0 else if (contextSettings.Type == ClientContextType.AzureADCredentials) { newClientContext = authManager.GetAzureADCredentialsContext(newSiteUrl, contextSettings.UserName, contextSettings.Password); } else if (contextSettings.Type == ClientContextType.AzureADCertificate) { newClientContext = authManager.GetAzureADAppOnlyAuthenticatedContext(newSiteUrl, contextSettings.ClientId, contextSettings.Tenant, contextSettings.Certificate, contextSettings.Environment); } #endif if (newClientContext != null) { //Take over the form digest handling setting newClientContext.FormDigestHandlingEnabled = (clientContext as ClientContext).FormDigestHandlingEnabled; newClientContext.ClientTag = clientContext.ClientTag; #if !ONPREMISES || SP2016 || SP2019 newClientContext.DisableReturnValueCache = clientContext.DisableReturnValueCache; #endif return(newClientContext); } else { throw new Exception($"Cloning for context setting type {contextSettings.Type} was not yet implemented"); } } else { // Take over the context settings, this is needed if we later on want to clone this context to a different audience contextSettings.SiteUrl = newSiteUrl; clonedClientContext.AddContextSettings(contextSettings); clonedClientContext.ExecutingWebRequest += delegate(object oSender, WebRequestEventArgs webRequestEventArgs) { // Call the ExecutingWebRequest delegate method from the original ClientContext object, but pass along the webRequestEventArgs of // the new delegate method MethodInfo methodInfo = clientContext.GetType().GetMethod("OnExecutingWebRequest", BindingFlags.Instance | BindingFlags.NonPublic); object[] parametersArray = new object[] { webRequestEventArgs }; methodInfo.Invoke(clientContext, parametersArray); }; } } else // Fallback the default cloning logic if there were not context settings available { //Take over the form digest handling setting clonedClientContext.FormDigestHandlingEnabled = (clientContext as ClientContext).FormDigestHandlingEnabled; var originalUri = new Uri(clientContext.Url); // If the cloned host is not the same as the original one // and if there is an active PnPProvisioningContext if (originalUri.Host != siteUrl.Host && PnPProvisioningContext.Current != null) { // Let's apply that specific Access Token clonedClientContext.ExecutingWebRequest += (sender, args) => { // We get a fresh new Access Token for every request, to avoid using an expired one var accessToken = PnPProvisioningContext.Current.AcquireToken(siteUrl.Authority, null); args.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + accessToken; }; } // Else if the cloned host is not the same as the original one // and if there is a custom Access Token for it in the input arguments else if (originalUri.Host != siteUrl.Host && accessTokens != null && accessTokens.Count > 0 && accessTokens.ContainsKey(siteUrl.Authority)) { // Let's apply that specific Access Token clonedClientContext.ExecutingWebRequest += (sender, args) => { args.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + accessTokens[siteUrl.Authority]; }; } // Else if the cloned host is not the same as the original one // and if the client context is a PnPClientContext with custom access tokens in its property bag else if (originalUri.Host != siteUrl.Host && accessTokens == null && clientContext is PnPClientContext && ((PnPClientContext)clientContext).PropertyBag.ContainsKey("AccessTokens") && ((Dictionary <string, string>)((PnPClientContext)clientContext).PropertyBag["AccessTokens"]).ContainsKey(siteUrl.Authority)) { // Let's apply that specific Access Token clonedClientContext.ExecutingWebRequest += (sender, args) => { args.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + ((Dictionary <string, string>)((PnPClientContext)clientContext).PropertyBag["AccessTokens"])[siteUrl.Authority]; }; } else { // In case of app only or SAML clonedClientContext.ExecutingWebRequest += (sender, webRequestEventArgs) => { // Call the ExecutingWebRequest delegate method from the original ClientContext object, but pass along the webRequestEventArgs of // the new delegate method MethodInfo methodInfo = clientContext.GetType().GetMethod("OnExecutingWebRequest", BindingFlags.Instance | BindingFlags.NonPublic); object[] parametersArray = new object[] { webRequestEventArgs }; methodInfo.Invoke(clientContext, parametersArray); }; } } } return(clonedClientContext); }
/// <summary> /// Clones a ClientContext object while "taking over" the security context of the existing ClientContext instance /// </summary> /// <param name="clientContext">ClientContext to be cloned</param> /// <param name="targetContext">CientContext stub to be used for cloning</param> /// <param name="siteUrl">Site URL to be used for cloned ClientContext</param> /// <param name="accessTokens">Dictionary of access tokens for sites URLs</param> /// <returns>A ClientContext object created for the passed site URL</returns> internal static ClientContext Clone(this ClientRuntimeContext clientContext, ClientContext targetContext, Uri siteUrl, Dictionary <string, string> accessTokens = null) { if (siteUrl == null) { throw new ArgumentException(CoreResources.ClientContextExtensions_Clone_Url_of_the_site_is_required_, nameof(siteUrl)); } ClientContext clonedClientContext = targetContext; clonedClientContext.ClientTag = clientContext.ClientTag; clonedClientContext.DisableReturnValueCache = clientContext.DisableReturnValueCache; // Check if we do have context settings var contextSettings = clientContext.GetContextSettings(); if (contextSettings != null) // We do have more information about this client context, so let's use it to do a more intelligent clone { string newSiteUrl = siteUrl.ToString(); // A diffent host = different audience ==> new access token is needed if (contextSettings.UsesDifferentAudience(newSiteUrl)) { var authManager = contextSettings.AuthenticationManager; ClientContext newClientContext = null; if (contextSettings.Type == ClientContextType.SharePointACSAppOnly) { newClientContext = authManager.GetACSAppOnlyContext(newSiteUrl, contextSettings.ClientId, contextSettings.ClientSecret, contextSettings.Environment); } else { newClientContext = authManager.GetContextAsync(newSiteUrl).GetAwaiter().GetResult(); } if (newClientContext != null) { //Take over the form digest handling setting newClientContext.ClientTag = clientContext.ClientTag; newClientContext.DisableReturnValueCache = clientContext.DisableReturnValueCache; return(newClientContext); } else { throw new Exception($"Cloning for context setting type {contextSettings.Type} was not yet implemented"); } } else { // Take over the context settings, this is needed if we later on want to clone this context to a different audience contextSettings.SiteUrl = newSiteUrl; clonedClientContext.AddContextSettings(contextSettings); clonedClientContext.ExecutingWebRequest += delegate(object oSender, WebRequestEventArgs webRequestEventArgs) { // Call the ExecutingWebRequest delegate method from the original ClientContext object, but pass along the webRequestEventArgs of // the new delegate method MethodInfo methodInfo = clientContext.GetType().GetMethod("OnExecutingWebRequest", BindingFlags.Instance | BindingFlags.NonPublic); object[] parametersArray = new object[] { webRequestEventArgs }; methodInfo.Invoke(clientContext, parametersArray); }; } } else // Fallback the default cloning logic if there were not context settings available { //Take over the form digest handling setting var originalUri = new Uri(clientContext.Url); // If the cloned host is not the same as the original one // and if there is an active PnPProvisioningContext if (originalUri.Host != siteUrl.Host && PnPProvisioningContext.Current != null) { // Let's apply that specific Access Token clonedClientContext.ExecutingWebRequest += (sender, args) => { // We get a fresh new Access Token for every request, to avoid using an expired one var accessToken = PnPProvisioningContext.Current.AcquireToken(siteUrl.Authority, null); args.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + accessToken; }; } // Else if the cloned host is not the same as the original one // and if there is a custom Access Token for it in the input arguments else if (originalUri.Host != siteUrl.Host && accessTokens != null && accessTokens.Count > 0 && accessTokens.ContainsKey(siteUrl.Authority)) { // Let's apply that specific Access Token clonedClientContext.ExecutingWebRequest += (sender, args) => { args.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + accessTokens[siteUrl.Authority]; }; } // Else if the cloned host is not the same as the original one // and if the client context is a PnPClientContext with custom access tokens in its property bag else if (originalUri.Host != siteUrl.Host && accessTokens == null && clientContext is PnPClientContext && ((PnPClientContext)clientContext).PropertyBag.ContainsKey("AccessTokens") && ((Dictionary <string, string>)((PnPClientContext)clientContext).PropertyBag["AccessTokens"]).ContainsKey(siteUrl.Authority)) { // Let's apply that specific Access Token clonedClientContext.ExecutingWebRequest += (sender, args) => { args.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + ((Dictionary <string, string>)((PnPClientContext)clientContext).PropertyBag["AccessTokens"])[siteUrl.Authority]; }; } else { // In case of app only or SAML clonedClientContext.ExecutingWebRequest += (sender, webRequestEventArgs) => { // Call the ExecutingWebRequest delegate method from the original ClientContext object, but pass along the webRequestEventArgs of // the new delegate method MethodInfo methodInfo = clientContext.GetType().GetMethod("OnExecutingWebRequest", BindingFlags.Instance | BindingFlags.NonPublic); object[] parametersArray = new object[] { webRequestEventArgs }; methodInfo.Invoke(clientContext, parametersArray); }; } } return(clonedClientContext); }