Configuring OneDrive sync app to connect SharePoint on-premises server using OIDC authentication
Disclaimer: What works for me may not necessarily work for you (or work in exactly the same way), and I accept no responsibility for the information provided here.
Some organizations still use on-premises SharePoint servers, and some of them also offer OneDrive for Business to their users. SharePoint Server SE (unlike Skype for Business Server SE and Exchange Server SE) has indeed brought some new features to these organizations, one of which is that SharePoint Server is finally capable of OIDC authentication with both Entra ID and AD FS.
This is a very exciting development because it means that hybrid modern authentication is not only available for Exchange or SfB, but can also be fully utilized in SharePoint with all its benefits (CA policies, access packages, and all other great Entra ID features are now available)
Microsoft has made two Group Policy settings available for the OneDrive sync client, which allow system administrators to use this new authentication method for synchronization as well. However, these are not officially documented, and Microsoft does not provide any official documentation detailing how to properly configure the environment for this to work correctly.
If you want to read more about this, you can do so here, but the two settings are as follows:
- SharePointOnPremOIDC: if enabled, OneDrive uses OIDC for on-premises SharePoint.
- SharePointOnPremApplicationIdUri: this setting's value should be either Entra Resource Identifier or named URI of the Entra app used for the OIDC flow between the OneDrive client and the local SharePoint server.
I thought the settings were pretty straightforward, so I configured them and... of course, they didn't work. The OneDrive client understood the settings and tried to authenticate accordingly, but Entra ID didn't like this setup. Let me show you the error message (and some parameters) that I got:
Error message: "AADSTS650057: Invalid resource. The client has requested access to a resource which is not listed in the requested permissions in the client's application registration. Client app ID: {appId}({appName}). Resource value from request: {resource}. Resource app ID: {resourceAppId}. List of valid resources from app registration: {regList}."
Resource Display Name: SharePoint on-premises (this will probably be different for you)
App DisplayName: OneDrive SyncEngine
App ID: ab9b8c07-8f02-4f72-87fa-80105867a763
Okay, so the OneDrive client uses the OneDrive SyncEngine first-party application for authentication, and the resource is the application I created (based on Microsoft's guide) for SharePoint on-premises authentication. I haven't really made any "connection" between the two, but there hasn't been any documentation that instructed me to do so. How about we take a look at what happens if we want to authenticate to SharePoint Online using OneDrive client? That could serve as a good reference for what we need. Let's take a look at an extract of example such an authentication:
Resource Display Name: OfficeServicesManager
Resource ID: 9e4a5442-a5c9-4f6f-b03f-5b9fcaaf24b1
App DisplayName: OneDrive SyncEngine
App ID: ab9b8c07-8f02-4f72-87fa-80105867a763
Right, so we are talking about two first-party applications here. One of them is still OneDrive SyncEngine, but the one that is more interesting to us is OfficeServicesManager. We need to see what the relationship is between the two first-party applications, and there is a site (save it, I think it will be very useful later) where you can check this. Let's take a look at it.
Perfect, now we know what we need. We need to grant OneDrive SyncEngine the "user_impersonation" permission for the SharePoint on-premise application we created (hereinafter referred to as: 3rd party SP app).
From the application registrations, find the 3rd party SP app and edit the Microsoft Graph App Manifest for the application. Add the following to the "oauth2PermissionScopes":
{
"adminConsentDescription": "user_impersonation",
"adminConsentDisplayName": "user_impersonation",
"id": "acf82d26-07cd-436e-8206-5fcee42df38d",
"isEnabled": true,
"type": "Admin",
"userConsentDescription": null,
"userConsentDisplayName": null,
"value": "user_impersonation"
}
Please note, that you probably want to generate a new GUID/UUID for the "id" parameter. After saving the new manifest, the question is how we will authorize OneDrive SyncEngine (which is a first-party application, so our hands are somewhat tied) for this new scope.
I had to do a lot of research before I found that Microsoft provides a script for Power Apps that serves exactly this purpose, called ManagePermissionGrant.ps1.
To make things a little more complicated, we need to modify two things in the script: first, the value of the "$HttpWithAADAppAppId" variable, which will be the OneDrive SyncEngine app ID, and second, we need to add our 3rd party SP app to the Get-FirstPartyAppList function. Here are the two modifications:
function Get-FirstPartyAppList{
@(
[App]@{IsCommonlyUsedApp=$true;ApplicationName='SharePoint on-premises';AppId='e73466c1-0485-469f-b772-99facabc14ef'}
) | Sort-Object -Property ApplicationName
}
(...)
$HttpWithAADAppAppId = 'ab9b8c07-8f02-4f72-87fa-80105867a763'
Remember, you don't need to delete all other applications from the Get-FirstPartyAppList function. Just add your own application, and when you run the script, you should select that.
Once you have made these changes, run the script and answer yes to basically every question.
If you've done everything correctly, the OneDrive client will no longer stop at authentication, but after successful authentication, it will ask you for the synchronization location (where you want it to create the synchronization folder on your computer) and start the setup. After 1 minute, the loading screen starts to worry you; after 10 minutes, you feel that something is wrong; and after 1 hour, you know that something is still not right. But what? What's more, the client doesn't display any error message, so it's not an easy task.
Let's check the client log... Sure, as if it were that straightforward. Microsoft doesn't make it too easy for us, but if you're interested in how I solved it, check out this GitHub project. It did a great job of converting the logs exported from the client into a readable format.
Let's take a look at the log excerpt ("(...)" indicates where I have removed information for security reasons):
BaseOneAuthMSALUserAuthenticationConverged.cpp,OneAuth::BaseOneAuthMSALUserAuthenticationConverged::DecodeAndHandleOneAuthError,"['OneAuthApiResult', 'Failure', 'DRX_E_AUTH2_ONEAUTH_INCORRECTCONFIGURATION', '(...)', 'Auth', '4usqa', '(...)', '500011', 'secondary']"
OneAuthMSALWrapper.cpp,OneAuthMSALWrapper::Initialize::<lambda_1>::operator (),"[MSAL:0005] INFO LogTelemetryData:443 Key: wam_telemetry, Value: {""x_ms_clitelem"":""1,500011,0,1531.7727,"",""ui_visible"":false,""server_error_code"":500011,""scope"":""https://sharepoint-my.(...)//.default offline_access openid profile"",""redirect_uri"":""ms-appx-web://Microsoft.AAD.BrokerPlugin/ab9b8c07-8f02-4f72-87fa-80105867a763"",""provider_id"":""https://login.windows.net"",""oauth_error_code"":""invalid_resource"",""http_status"":400,""http_event_count"":1,""http_content_type"":""application/json; charset=utf-8"",""http_content_size"":777,""device_join"":""not_joined"",""correlation_id"":""(...)"",""client_id"":""ab9b8c07-8f02-4f72-87fa-80105867a763"",""ccs_failover_v2"":""1.P"",""cache_event_count"":0,""broker_version"":""10.0.26100.4202"",""authority"":""https://login.microsoftonline.com/(...)"",""api_error_code"":-895352821,""account_join_on_start"":""secondary"",""account_join_on_end"":""secondary"",""silent_code"":0,""silent_bi_sub_code"":0,""silent_message"":"""",""silent_status"":0,""is_cached"":0}"
