Skip to content

Instantly share code, notes, and snippets.

@zplume
Last active April 26, 2018 08:17
Show Gist options
  • Save zplume/6a6e780a2cf0d8be40d4d3ba2d97b1a0 to your computer and use it in GitHub Desktop.
Save zplume/6a6e780a2cf0d8be40d4d3ba2d97b1a0 to your computer and use it in GitHub Desktop.

SPO cookie auth for Azure Functions

There is some great guidance on using Azure Functions with SharePoint Online cookie authentication here: https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/guidance/connect-to-api-secured-with-aad

Unfortunately the guidance doesn't cover the situation where the cookie has expired and the authentication fails (for example if the browser is left open for a long time after initial sign-in to SharePoint).

Here are some (hopefully) helpful classes for implementing Azure Functions with SPO cookie authentication that also allow handling the expired SPO cookie and attaching CORS headers in C#.

public static class AuthCheck
{
// This Function will return OK if the user is authenticated
[FunctionName("AuthCheck")]
public static HttpResponseMessage Run([HttpTrigger(AuthorizationLevel.Anonymous, "get")]HttpRequestMessage request, TraceWriter log)
{
var response = request.CreateResponse(HttpStatusCode.OK);
Cors.Enable(request, response);
return response;
}
}
(function() {
// NOTE: this example uses jQuery.ajax, but this could be swapped out for another XHR method if required
var endpoint = "https://[your-function-app].azurewebsites.net";
function authCheckSucceeded() {
console.info("Auth flow completed!");
// Logic to render page / enable controls that rely on
// Azure Functions should be triggered here
}
function authCheckFailed() {
// SPO cookie has expired, redirect user to Login function
// - user will be prompted to sign-in
// - browser will redirect back to this page after sign-in
console.error("Auth check failed, logging in...");
// timeout here is to help show the process, but isn't needed in production
setTimeout(function() {
window.location.href = endpoint + "/api/Login?redirect_uri=" + window.location.href;
}, 2000);
}
function onFunctionIframeLoad() {
// Azure Function iframe has loaded
// check if silent auth has succeeded by calling AuthCheck function
// which should return OK if silent authentication was successful
$.ajax({
url: endpoint + "/api/AuthCheck",
method: "GET",
xhrFields: {
withCredentials: true
},
crossDomain: true
}).then(authCheckSucceeded).fail(authCheckFailed);
}
// Load iframe into Azure Function root to attempt silent authentication using SPO cookie
var iframe = document.createElement("iframe");
iframe.style = "display: none;";
iframe.onload = onFunctionIframeLoad.bind(this);
iframe.src = endpoint;
document.body.appendChild(iframe);
})();
public static class Cors
{
// Attach CORS headers to the response
// Only allows the SharePoint domain specified in Azure App Settings as the Origin
public static void Enable(HttpRequestMessage request, HttpResponseMessage response)
{
if (request.Headers.Contains("Origin"))
{
response.Headers.Add("Access-Control-Allow-Credentials", "true");
response.Headers.Add("Access-Control-Allow-Origin", EnvironmentVariables.SharePointRootSiteCollection);
response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, OPTIONS, HEAD");
}
}
}
public static class EnvironmentVariables
{
public static string SharePointRootSiteCollection
{
get
{
return Get("SharePointRootSiteCollection").TrimEnd('/');
}
}
private static string Get(string key)
{
return Environment.GetEnvironmentVariable(key);
}
}
public static class Login
{
// This Function can be used to handle the OAuth sign-in flow with a redirect back to the originating page
[FunctionName("Login")]
public static HttpResponseMessage Run([HttpTrigger(AuthorizationLevel.Anonymous, "get")]HttpRequestMessage request, TraceWriter log)
{
// parse query parameter
string redirectUri = request.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Equals(q.Key, "redirect_uri", System.StringComparison.CurrentCultureIgnoreCase))
.Value;
var response = request.CreateResponse(HttpStatusCode.Redirect);
response.Headers.Add("Location", redirectUri);
return response;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment