# Custom Sign In Page has an SPContext.Current that is always null

Found the Solution

The solution was to add a Root Site Collection (as the code was running in a sub-site, without a root site).

Important Update (based on feedback received so far)

I do not have Visual Studio installed on the SharePoint 2013 Server. All code is compiled in Visual Studio 2012 remotely using references to the required SharePoint 2013 DLLs and digitally signed so it can be deployed into the Global Assembly Cache on the SharePoint 2013 Server.

At the most basic level this statement fails at point of Page_Load();

SPContext.Current.Site.WebApplication.GetIisSettingsWithFallback(
SPContext.Current.Site.Zone
);

Because SPContext.Current is always null.

# Background

Client needs there SharePoint site to be accessible using Form Based Authentication for external users and Windows Authentication for internal users (corporate). Have gone through and have setup both Authentication Providers and now get the default sign in.

Although everything works Windows Authentication still displays the Provider selection screen when the client wants the Windows Authentication to just automatically pass through and display a Forms Based Authentication login screen if that fails.

Been struggling with this for days after looking at various blogs and articles I feel I'm no further along and that is depressing. At the moment I can't workout whether I'm missing something really fundamental.

The page is based off an amalgamation of;

2. %CommonProgramFiles%\Microsoft Shared\Web Server Extensions\15\Template\IdentityModel\Forms\default.aspx

This is just a first attempt and realise I won't need all these controls eventually, just wanted to see how things hang together.

<%@ Assembly Name="Microsoft.SharePoint.IdentityModel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="SharepointIdentity" Namespace="Microsoft.SharePoint.IdentityModel" Assembly="Microsoft.SharePoint.IdentityModel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Assembly Name="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%>
<%@ Page Language="C#" Inherits="MyCustomSignInModule.SignInForm, MyCustomSignInModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f2ae721af94bf9e9" MasterPageFile="~/_layouts/15/errorv15.master"%>
<%@ Import Namespace="Microsoft.SharePoint.WebControls" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<asp:Content ContentPlaceHolderId="PlaceHolderPageTitleInTitleArea" runat="server">
<SharePoint:EncodedLiteral runat="server"  EncodeMethod="HtmlEncode" Id="ClaimsLogonPageTitleInTitleArea" />
</asp:Content>

<asp:Content ContentPlaceHolderId="PlaceHolderPageTitle" runat="server">
<SharePoint:EncodedLiteral runat="server"  EncodeMethod="HtmlEncode" Id="ClaimsFormsPageTitle" />
</asp:Content>
<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">
<SharePoint:EncodedLiteral runat="server"  EncodeMethod="HtmlEncode" Id="ClaimsLogonPageMessage" />
<br />
<br />
<SharepointIdentity:LogonSelector ID="ClaimsLogonSelector" runat="server" />

<div id="SslWarning" style="color:red;display:none">
<SharePoint:EncodedLiteral runat="server"  EncodeMethod="HtmlEncode" Id="ClaimsFormsPageMessage" />
</div>
<script language="javascript" >
if (document.location.protocol != 'https:') {
var SslWarning = document.getElementById('SslWarning');
SslWarning.style.display = '';
}
</script>
<asp:login id="signInControl" FailureText="<%$Resources:wss,login_pageFailureText%>" runat="server" width="100%"> <layouttemplate> <asp:label id="FailureText" class="ms-error" runat="server"/> <table width="100%"> <tr> <td nowrap="nowrap"><SharePoint:EncodedLiteral runat="server" text="<%$Resources:wss,login_pageUserName%>" EncodeMethod='HtmlEncode'/></td>
<td width="100%"><asp:textbox id="UserName" autocomplete="off" runat="server" class="ms-inputuserfield" width="99%" /></td>
</tr>
<tr>
<td nowrap="nowrap"><SharePoint:EncodedLiteral runat="server" text="<%Resources:wss,login_pagePassword%>" EncodeMethod='HtmlEncode'/></td> <td width="100%"><asp:textbox id="password" TextMode="Password" autocomplete="off" runat="server" class="ms-inputuserfield" width="99%"/></td> </tr> <tr> <td colspan="2" align="right"><asp:button id="login" commandname="Login" text="<%Resources:wss,login_pagetitle%>" runat="server" /></td>
</tr>
<tr>
<td colspan="2"><asp:checkbox id="RememberMe" text="<%\$SPHtmlEncodedResources:wss,login_pageRememberMe%>" runat="server" /></td>
</tr>
</table>
</layouttemplate>
<asp:label id="DebugText" class="ms-error" runat="server"/>
</asp:Content>

# MyCustomSignInModule Assembly

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

using Microsoft.IdentityModel.Web;
using Microsoft.SharePoint;
using Microsoft.SharePoint.IdentityModel;
using Microsoft.SharePoint.IdentityModel.Pages;
using Microsoft.SharePoint.ApplicationRuntime;
using Microsoft.SharePoint.Diagnostics;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebControls;
using System.IdentityModel.Tokens;
using System.Web.Configuration;

namespace MyCustomSignInModule
{
public class SignInForm : FormsSignInPage
{
protected Label DebugText;

private static SPIisSettings _iisSettings;

internal static SPIisSettings iisSettings
{
get
{
if (_iisSettings == null)
{
SPSecurity.RunWithElevatedPrivileges(delegate()
{
_iisSettings = SPContext.Current.Site.WebApplication.GetIisSettingsWithFallback(SPContext.Current.Site.Zone);
});
}
return _iisSettings;
}
}

protected void Page_Load(object sender, EventArgs e)
{
HttpRequest request = HttpContext.Current.Request;

bool isWindowsAuth = false;

// If no username is provided, it'll probably be Windows Authentication (NTLMv2 protocol)
{
isWindowsAuth = true;
}

try
{

//SPIisSettings iisSettings = SPContext.Current.Site.WebApplication.IisSettings[SPUrlZone.Default];

if (isWindowsAuth)
{
// Windows Authentication it is

if (iisSettings.UseWindowsClaimsAuthenticationProvider)
{
SPAuthenticationProvider provider = iisSettings.WindowsClaimsAuthenticationProvider;
}
}
else
{
// FBA authentication it is.

SPFormsAuthenticationProvider formsClaimsAuthenticationProvider = iisSettings.FormsClaimsAuthenticationProvider;

if (null != token)
{
EstablishSessionWithToken(token);
base.RedirectToSuccessUrl();
}
}
}
catch (Exception ex)
{
DebugText.Text = ex.ToString();
}

}

// Microsoft.SharePoint.IdentityModel.LogonSelector
{
string components = HttpContext.Current.Request.Url.GetComponents(UriComponents.Query, UriFormat.SafeUnescaped);
string url = provider.AuthenticationRedirectionUrl.ToString();
if (provider is SPWindowsAuthenticationProvider)
{
components = EnsureUrlSkipsFormsAuthModuleRedirection(components, true);
}
SPUtility.Redirect(url, SPRedirectFlags.Default, this.Context, components);
}

// Microsoft.SharePoint.Utilities.SPUtility
private string EnsureUrlSkipsFormsAuthModuleRedirection
(string url, bool urlIsQueryStringOnly)
{
if (!url.Contains("ReturnUrl="))
{
if (urlIsQueryStringOnly)
{
url = url + (string.IsNullOrEmpty(url) ? "" : "&");
}
else
{
url = url + ((url.IndexOf('?') == -1) ? "?" : "&");
}
url = url + "ReturnUrl=";
}
return url;
}

// Microsoft.SharePoint.IdentityModel.Pages.IdentityModelSignInPageBase
private void EstablishSessionWithToken(SecurityToken securityToken)
{
if (securityToken == null)
{
throw new ArgumentNullException("securityToken");
}
SPFederationAuthenticationModule fam = SPFederationAuthenticationModule.Current;
if (fam == null)
{
throw new InvalidOperationException();
}

fam.SetPrincipalAndWriteSessionToken(securityToken);
}

private SPAuthenticationProvider
GetAuthenticationProvider(string providerName)
{
SPIisSettings iisSettings =
SPContext.Current.Site.WebApplication.IisSettings[SPUrlZone.Default];

foreach (SPAuthenticationProvider currentProvider in
iisSettings.ClaimsAuthenticationProviders)
{
if (currentProvider.DisplayName.ToLower() ==
providerName.ToLower())
{
return currentProvider;
}
}

return null;
}
}
}

# The Process

I have a test Sharepoint instance running on the VM but no Visual Studio installed as all the development is done remotely. The current process I follow;

1. Build C# Class Library (this will be MyCustomSignInModule.Dll)
2. Sign MyCustomSignInModule.Dll so it can be placed in the VM GAC.
3. Register MyCustomSignInModule.Dll in the GAC on the VM instance.
4. Save SignInForm.aspx into the %CommonProgramFiles%\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\ which is the equivalent of <SharePointSiteRoot>/_layouts/ virtual directory in IIS.
6. Reset IIS using iisreset.
7. Test accessing SharePoint site (this is where it falls over).

## The Problem

If it's not that clear the error is;

System.NullReferenceException: Object reference not set to an instance of an object. at
MyCustomSignInModule.SignInForm.b__0() at
Microsoft.SharePoint.SPSecurity.<>c__DisplayClass5.b__3() at
Microsoft.SharePoint.Utilities.SecurityContext.RunAsProcess(CodeToRunElevated secureCode) at
Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(WaitCallback secureCode, Object param) at
Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(CodeToRunElevated secureCode) at MyCustomSignInModule.SignInForm.get_iisSettings() at

## SPContext.Current is null why?

The NullReferenceException is being caused by SPContext.Current being null, but I can't workout why it's null and no amount of articles I've looked at have so far led to helping me solve this. I'm hoping by posting my whole process here someone might be able to point me to what I'm missing or doing wrong.

• @Lankymart I took the time to test your code on my environment and it's working just fine. There must be something related to how you are deploying the solution (I don't understand how you can even develop on a machine without SP2013 installed) – MdMazzotti Apr 16 '14 at 11:51
• @Lankymart I deployed directly from Visual Studio. Tomorrow if I have some spare time I will try to deploy with your method and let you know – MdMazzotti Apr 16 '14 at 21:39
• @Lankymart here's a screenshot proving that SPContext.Current is not null (when deploying the solution from Visual Studio): s4.postimg.org/86ae7jwkt/loginpage.jpg – MdMazzotti Apr 20 '14 at 9:43
• @Lankymart I just tested your deployment method (I built the DLL on a different machine whit no sharepoint installed, compiled and manually deployed). It is working fine. So I have no idea why it doesn't work on your environment. The code is correct – MdMazzotti Apr 20 '14 at 10:23
• @Lankymart here's a screenshot proving that the custom signin page got called (I just commented out the RedirectToLoginPage(provider); line and added this DebugText.Text = "You are about to be redirected using WindowsClaims auth provider";) postimg.org/image/ttp2cwcgj – MdMazzotti Apr 20 '14 at 10:53

Finally got to the bottom of what was causing the issue, firstly would like to thank both @MdMazzotti and @Hugh Wood for their persistent help.

In the end I stumbled on the fix which turned out to be partly because the way the site was built. In my particular case the site was built as a sub-site in SharePoint, for whatever reason this meant there was no Site Collection at the root of the SharePoint website which was the cause of the Null SPContext in my code.

After adding a Root Site Collection my code began to work as expected.

• I'm glad to see you finally found out what the problem was – MdMazzotti May 21 '14 at 16:30
• @MdMazzotti Can't believe how simple it was, just sorry I didn't provide the site structure. Maybe then would have gotten to the bottom of the issue far quicker. Really appreciate your help though, was invaluable. – Lankymart May 21 '14 at 16:32

I'm just guessing here, but here are some things that I think could help.

SP.SOD.executeFunc('SP.js', 'SP.ClientContext');

You could also add this JavaScript to your page to test if it works or not, using the console:

var clientContext = new  SP.ClientContext.get_current();
console.log(clientContext);

If it returns something, then we have progress! If not, then I'll have to continue my search, since I find your question interesting.

EDIT

Instead of using this server-side code:

if (_iisSettings == null)
{
SPSecurity.RunWithElevatedPrivileges(delegate()
{
_iisSettings = SPContext.Current.Site.WebApplication.GetIisSettingsWithFallback(SPContext.Current.Site.Zone);
});
}
return _iisSettings;

if (_iisSettings == null)
{
SPSecurity.RunWithElevatedPrivileges(delegate()
{
_iisSettings = Microsoft.SharePoint.SPContext.Current.Site.WebApplication.GetIisSettingsWithFallback(Microsoft.SharePoint.SPContext.Current.Site.Zone);
});
}
return _iisSettings;

I searched around and noticed that in some of the code examples I encountered, the _iisSettings was different from yours. Perhaps that is why your SPContext.Current is null?

• Isn't the code above server side code, not javascript? – Robert Lindgren Apr 16 '14 at 8:59
• Yeah it's c#, I didn't know if this question was a good fit for this site. Do you mean add the javascript in the ASPX page above the if (document.location.protocol != 'https:') { line? – Lankymart Apr 16 '14 at 9:01
• Ah, it is indeed. Let my edit my answer. – Magali Apr 16 '14 at 9:01
• Yes I did mean to add it in the ASPX page above that if-statement, sorry if I wasn't too clear. I sometimes forget to mention things. :) – Magali Apr 16 '14 at 9:07
• @Magali the poster is developing on a remote machine and manually deploying to the server. I don't see how your suggestion applies in his case... – MdMazzotti Apr 16 '14 at 12:54