Microsoft Office Link Pre-fetching and Single Sign-On

Photo by Masaaki Komori on Unsplash.

I updated a site to use Single Sign-On (SSO) capability, specifically with Shibboleth, and was shocked to discover that links to the site in Microsoft Office files stopped working. This turns out to be a known issue with no definitive solution. So after thorough troubleshooting I created my own workaround, and as usual I hope sharing it will benefit others.

A Normal Shibboleth Sign-On

To understand the problem, let's first look at a successful Shibboleth sign-on. When a user visits a resource protected by Shibboleth, the Service Provider (SP) examines the request. For example, on IIS the SP is installed as an ISAPI filter in the protected website. The SP checks the user's cookies to verify that they have a valid session. If the session is valid, the request continues to the resource. If the user has an invalid or no session, they are redirected to the Identity Provider (IdP), the central authentication server. The original request details (the SP's address, the requested URL, etc.) are stored in a cookie by the IdP. If the user successfully authenticates to the IdP, they are transferred back to the SP, which is sent the request URL and the tokens of a valid session. Finally the SP redirects the user to the resource they requested. Phew!

Now we'll look at Microsoft Office and its peculiar handling of links. When you click a hyperlink in an Office file (Word document, Excel spreadsheet, PowerPoint presentation, etc.), the URL is not immediately passed to your browser. First, Office will internally fetch the address. If the link returns a 3xx Redirection code, Office will request the new address and repeat. If the link returns a 4xx Client Error code, Office will abort the request and tell the user that the link is unavailable, having never opened a browser (I'm guessing that's the whole point of this "feature"). And if the link returns a 2xx Success code, the URL is finally opened in a browser. Only the final URL is passed to the browser; any redirects are masked by Office.

Microsoft Office and SSO

Microsoft Office conflicts with Shibboleth (and other SSO systems, I expect) because of cookies. When Office requests the protected resource, the SP redirects to the IdP login page. Office follows the redirect and the IdP login page returns a 200 response, waiting for the user's credentials. Office opens the IdP login page address in the user's browser, but the cookies describing the request are lost. After the user authenticates, the IdP doesn't know how to return the user to their SP and displays an error. Thus hyperlinks in Office files never load.

Client-side Solutions?

Microsoft KB #899927 (see "Hyperlinks from Office to Internet Explorer or to another Web browser") describes some client-side workarounds for this problem that will alter Office's behavior. But the registry changes could have unexpected side effects. Depending on your audience, a registry change may be inconvenient or impossible.

A Server-side Solution

Ultimately we want a server-side solution that will transparently fix the problem for the user.

To start, we need finer control over Shibboleth's redirects. By default, the SP will automatically detect invalid sessions and redirect to the IdP. We need to disable this behavior and enable "lazy sessions" instead. In the <RequestMap> section of your SP configuration you'll find a <Host> section like this.

<Host name="example.com" authType="shibboleth" requireSession="true" applicationId="xxx">
    <Path name="favicon.ico" requireSession="false"/>
</Host>

Make note of any unprotected paths, like favicon.ico in the example above.

Update your SP's configuration to set requireSession="false".

<Host name="example.com" authType="shibboleth" requireSession="false" applicationId="xxx"/>

Now the site is unprotected, with nothing to require authentication. We will shift this responsibility to a URL rewriter. On IIS I use Ionic's ISAPI Rewrite.

RewriteCond  %{HTTP_SHIB_IDENTITY_PROVIDER} ^\s*$
RewriteCond  %{REQUEST_URI}  !/Shibboleth\.sso/ [I]
RewriteCond  %{REQUEST_URI}  !/favicon\.ico [I]
RedirectRule  .*  /Shibboleth.sso/Login?target=#X%{URL}#E

This rule will detect any unauthenticated requests and force them to log in. The /Shibboleth.sso/Login path will redirect the user from the SP to the IdP, and the target parameter will contain the URL-encoded address to which the user should return after authentication. Any unprotected paths like favicon.ico should be added as exceptions to this rule.

Now that Shibboleth's protection is restored, we can add a workaround for Microsoft Office. Before the rule above, add another one.

RewriteCond %{HTTP_USER_AGENT} ;\sms-office(\)|;) [I]
RewriteRule .* /Shibboleth-MSOffice.html [L]

This rule will check the user agent for ms-office (along with some syntax to minimize false positives). When Microsoft Office fetches a link, its user agent contains this unique ms-office string. The request from Office will be rewritten to a simple HTML file, causing the link to return a 200 response code and avoid the Shibboleth redirects.

The Shibboleth-MSOffice.html file must exist, but its contents are not critical. In theory, no person will see it. Still, I recommend some additional measures in case of false positives.

<html>
	<head>
		<title>Microsoft Office application detected</title>
		<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
		<meta http-equiv="refresh" content="0" />
	</head>
	<body>
		<p>
			The web server has detected that your visit originated from a Microsoft
			Office application.
		</p>
		<p>
			To make MS Office compatible with Single Sign-On (SSO) authentication, the
			visit has been halted.
		</p>
		<p>
			<strong>
				Please press F5 to refresh the browser and continue to your page.
			</strong>
		</p>
	</body>
</html>

This message will help users who are mistakenly caught by the rule, and the redirect will help in case the response is somehow cached.

With everything in place, the Office links will work again. When Office fetches a protected resource, the server's URL rewriter will detect the request and return an immediate 200 response. Office will pass the URL, unaltered, to the user's browser. When the user visits the address again, the request is validated by the SP and finally taken to the resource.

Drew

Drew

Hi! I'm Drew, the Wimpy Programmer. I'm a software developer and formerly a Windows server administrator. I use this blog to share my mistakes and ideas.