First published on TECHNET on Oct 31, 2018
This post is a contribution from Mustaq Patel, an engineer with the SharePoint Developer Support team
Requirement: You have low trust Provider Hosted add-in that does on the fly OAuth (request permissions at runtime). You try to use this Provider Hosted add-in on a GCC High or DoD tenant. You see that the add-in does not work and you see an error message which seems to indicate that the add-in is looking for Client Id/Client Secret in wrong tenant.
Issue: when the add-in does on the fly OAuth the TokenHelper class that the add-in uses to get the OAuth process going and retrieve Access Token by validating Client Id and Client Secret in the Azure Active Directory, goes to a wrong AAD (i.e. public AAD) and will come back without any record matching with that Client Id and so the add-in fails. It is due to below hard coded string in the TokenHelper.cs file which is not valid for on the fly OAuth on GCC High/DOD tenants.
To fix this modify this error line as below.
private static string AcsHostUrl = "login.microsoftonline.us"; // Also modify method GetAcsGlobalEndpointUrl as below. private static string GetAcsGlobalEndpointUrl() { return String.Format(CultureInfo.InvariantCulture, "https://{0}/", AcsHostUrl); }
These 2 changes will fix the Provider Hosted add-in by causing the OAuth requests to go to correct authentication url for GCCH AAD.
If you are using same Provider Hosted add-in for both GCCH and public cloud or want to maintain same code base for both types of cloud, than we have to do some extra work. The code logic would be to find out the top domain of the sharepoint site and if it is .us than go to login.microsoftonline.us and if it is .com go to accesscontrol.windows.net.
Below is the complete code change.
1. Declare 2 static variables as below (highlighted).
private static string GlobalEndPointPrefix = "accounts"; private static string AcsHostUrl = "accesscontrol.windows.net"; private static string GCCHAcsHostUrl = "login.microsoftonline.us"; private static string SPTargetUrl = "";
2. Modify GetAuthorizationUrl to set SPTargetUrl as below
public static string GetAuthorizationUrl(string contextUrl, string scope, string redirectUri) { SPTargetUrl = contextUrl; return string.Format( "{0}{1}?IsDlg=1&client_id={2}&scope={3}&response_type=code&redirect_uri={4}", EnsureTrailingSlash(contextUrl), AuthorizationPage, ClientId, scope, redirectUri); }
3. Add below method, to get the top domain of the sharepoint url.
private static string GetTopDomainType(Uri uri) { if (!uri.HostNameType.Equals(UriHostNameType.Dns) || uri.IsLoopback) return string.Empty; // or throw an exception return String.Format(CultureInfo.InvariantCulture, "{0}", uri.Host.Split('.').Last()); }
4. Modify GetAcsGlobalEndpointUrl as below, to find if SharePoint is in GCC High by checking top domain ( .us )
private static string GetAcsGlobalEndpointUrl() { string topDomain = GetTopDomainType(new Uri(SPTargetUrl)); if (string.Equals(topDomain, "us", StringComparison.OrdinalIgnoreCase)) { AcsHostUrl = GCCHAcsHostUrl; return String.Format(CultureInfo.InvariantCulture, "https://{0}/", AcsHostUrl); } else { return String.Format(CultureInfo.InvariantCulture, "https://{0}.{1}/", GlobalEndPointPrefix, AcsHostUrl); } }
5. Deploy the Provider Hosted add-in with this code change and test the add-in.
Please note that from the Provider Hosted add-in, you have to call GetAuthorizationUrl method to construct the redirecturl for add-in consent.
For more details please refer below article
On the fly oauth - https://docs.microsoft.com/en-us/sharepoint/dev/sp-add-ins/authorization-code-oauth-flow-for-sharepoint-add-ins .
GCC high and DoD - https://docs.microsoft.com/en-us/office365/servicedescriptions/office-365-platform-service-description/office-365-us-government/gcc-high-and-dod .
Welcome to the SharePoint Blog! Learn best practices, news, and trends directly from the SharePoint team.