SharepointPro

Loading

Replacing SharePoint Add-In Authentication Before the April 2026 Retirement

Replace SharePoint Add-In authentication before the April 2, 2026 retirement deadline to keep provider-hosted solutions working. This guide explains how to move from ACS authentication to Microsoft Entra ID, delegated SharePoint access tokens, a custom SharePointContextFilter, and bearer token authentication for CSOM with minimal controller changes.

This article shows how to replace the old authentication model using:
– Microsoft Entra ID authentication
– Delegated SharePoint access tokens
– A custom [SharePointContextFilter]
– Bearer token authentication for CSOM

The goal is to allow existing controllers to continue working with minimal changes.

Many teams now need to replace SharePoint Add-In authentication without rewriting their entire MVC application.

Many organizations are now planning how to replace SharePoint Add-In authentication before the April 2, 2026 retirement deadline.

The Problem With the Old Add-In Model

Provider-hosted add-ins relied on Azure ACS authentication and helper libraries to generate SharePoint access tokens. Typical controller code looked like this:

replace SharePoint Add-In authentication

[SharePointContextFilter]
public ActionResult Index()
{
    var ctx = SharePointContextProvider.Current.GetSharePointContext(HttpContext);
    using (var clientContext = ctx.CreateUserClientContextForSPHost())
    {
        // SharePoint CSOM operations
    }
}

New Architecture

The replacement approach uses Microsoft Entra ID authentication and issues a delegated SharePoint access token which is stored in the user’s authentication cookie.

The goal of this architecture is to replace SharePoint Add-In authentication while keeping existing MVC controllers functional with minimal code changes.

User visits MVC page
        ↓
[SharePointContextFilter]
        ↓
User redirected to Entra ID login
        ↓
Authorization code returned
        ↓
Code exchanged for SharePoint access token
        ↓
Token stored in authentication cookie
        ↓
ClientContext created with Bearer token

Custom SharePointContextFilter

This filter validates authentication and injects a ClientContext into the request pipeline.

public sealed class SharePointContextFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext ctx)
{
var auth = ctx.HttpContext.GetOwinContext().Authentication;
var ticket = auth.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationType).Result;

if (ticket?.Identity?.IsAuthenticated != true)
{
ctx.Result = new RedirectResult(“/redirect/login?returnUrl=” +
HttpUtility.UrlEncode(ctx.HttpContext.Request.RawUrl));
return;
}

var token = ticket.Identity.FindFirst(“spo_access_token”)?.Value;
var expiresTicks = ticket.Identity.FindFirst(“spo_expires”)?.Value;

if (token == null || expiresTicks == null ||
new DateTime(long.Parse(expiresTicks)) <= DateTime.UtcNow)
{
auth.SignOut(CookieAuthenticationDefaults.AuthenticationType);
ctx.Result = new RedirectResult(“/redirect/login”);
return;
}

var siteUrl = ConfigurationManager.AppSettings[“SharePoint:SiteUrl”];
var spCtx = SharePointDelegatedContext.Create(siteUrl, token);

ctx.HttpContext.Items[“SPO_CTX”] = spCtx;
}

public override void OnActionExecuted(ActionExecutedContext ctx)
{
(ctx.HttpContext.Items[“SPO_CTX”] as ClientContext)?.Dispose();
}
}

Creating a ClientContext Using a Bearer Token

SharePoint CSOM supports OAuth tokens through the Authorization header.

public static class SharePointDelegatedContext
{
    public static ClientContext Create(string siteUrl, string accessToken)
    {
        var ctx = new ClientContext(siteUrl);

        ctx.ExecutingWebRequest += (s, e) =>
        {
            e.WebRequestExecutor.RequestHeaders[“Authorization”] =
                “Bearer ” + accessToken;
        };

        return ctx;
    }
}

Handling Entra ID Login

Step 1 – Redirect the user to Microsoft Entra ID for authentication.

[HttpGet]
public ActionResult Login(string returnUrl = “/”)
{
    var tenantId = ConfigurationManager.AppSettings[“AzureAd:TenantId”];
    var clientId = ConfigurationManager.AppSettings[“AzureAd:ClientId”];

    var redirectUri = “https://yourapp.com/redirect”;

    var state = Guid.NewGuid().ToString(“N”);

    var authorizeUrl =
        $”https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/authorize” +
        $”?client_id={clientId}” +
        $”&response_type=code” +
        $”&redirect_uri={Uri.EscapeDataString(redirectUri)}” +
        $”&response_mode=form_post” +
        $”&scope=openid profile email” +
        $”&state={state}”;

    return Redirect(authorizeUrl);
}

Step 2 – Exchange the authorization code for a SharePoint access token.

var app = ConfidentialClientApplicationBuilder
    .Create(clientId)
    .WithClientSecret(clientSecret)
    .WithAuthority($”https://login.microsoftonline.com/{tenantId}/v2.0″)
    .WithRedirectUri(redirectUri)
    .Build();

var result = await app.AcquireTokenByAuthorizationCode(
    new[] { $”{siteHost}/.default” }, code)
    .ExecuteAsync();

Storing the SharePoint Token

var claims = new JwtSecurityTokenHandler()
    .ReadJwtToken(result.AccessToken)
    .Claims
    .ToList();

claims.Add(new Claim(“spo_access_token”, result.AccessToken));
claims.Add(new Claim(“spo_expires”, result.ExpiresOn.UtcTicks.ToString()));

Cookie Authentication Setup

private void ConfigureAuth(IAppBuilder app)
{
    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
        CookieName = “Sppro.Auth”,
        SlidingExpiration = true,
        ExpireTimeSpan = TimeSpan.FromHours(8)
    });
}

Conclusion

With the retirement of the SharePoint Add-In model approaching, applications relying on ACS authentication must move to a modern authentication approach. By implementing delegated SharePoint tokens and a custom SharePointContextFilter, existing provider-hosted solutions can continue working with minimal architectural changes.

In a future post I will show how to create custom actions on the SharePoint ECB (Edit Control Block) without using the add-in model.

Microsoft has officially announced the SharePoint Add-In model retirement scheduled for April 2, 2026.

Before migrating authentication models, it is recommended to perform a Microsoft 365 governance assessment to uncover SharePoint permission risks, outdated configurations, and tenant security issues that may impact the new Entra ID authentication approach.

By implementing Entra ID authentication and delegated SharePoint tokens, teams can successfully replace SharePoint Add-In authentication and keep their applications operational after the 2026 retirement.

FAQ

Provider-hosted add-ins using ACS authentication will stop working after April 2, 2026.

 Microsoft Entra ID with delegated SharePoint access tokens is the modern replacement.

Yes. A custom SharePointContextFilter can preserve most of the existing controller pattern.

Yes. You can attach the OAuth bearer token through the Authorization header in ClientContext requests.

Logo
Need a SharePoint Consultant?

Speak with our Brisbane SharePoint consultants about governance, risk control and Microsoft 365 stability.