4

I created a provider hosted app, which I want to deploy in a azure website. The problem that I'm currently facing is that I don't have a SharePointContext after postback. I'm using the default way to create the sharepoint context like this.

var spContext = SharePointContextProvider.Current.GetSharePointContext(Context);
var clientContext = spContext.CreateUserClientContextForSPHost();

The spContext variable is null after a postback

I looked into the two default TokenHelper and SharePointContext classes to see why this variable is null. The first time, a SharePointContext is created and saved in the session like this (SaveSharePointContext method).

httpContext.Session[SPContextKey] = spAcsContext;

After a postback, the class is trying to load the context from the session like this (LoadSharePointContext method)

return httpContext.Session[SPContextKey] as SharePointAcsContext;

This will return because inproc session is not available in my azure website. This makes sense because after a postback, my request might be handled by a different server.

I thought to work around this issue by not using inproc session management but using the azure redis cache for session management (http://azure.microsoft.com/en-us/documentation/articles/cache-dotnet-how-to-use-azure-redis-cache/)

The problem in this case is that objects stored in the session should be serializable. I can mark the SharePointContext object as serializable, but it contains a contextTokenObj variable which inherits from JsonWebSecurityToken. JsoWebSecurityToken is defined in a Microsoft dll which I can't mark serializable.

How can I store my SharePoint context so that I can reuse it after a postback? I don't want to recreate the context on every postback because it will have a negative effect on the performance.

4

It's not enough to mark the SharePointContext, SharePointAcsContext and SharePointContextToken serializable.

You must implement ISeralizable interface. In there are three method declaration. One constructor for the deserialization, and two for the serialization.

The following link refers to a msdn article, that's about how to implement it correctly. In that article, the second example explains the proper way on derrived classes.

http://msdn.microsoft.com/en-us/library/ms182342.aspx

So, here is my code snippets:

SharePointContext:

protected SharePointContext(SerializationInfo info, StreamingContext context)
    {
        if (info == null)
            throw new ArgumentNullException("info");

        try
        {
            var tmp = info.GetString("spHostUrl");
            this.spHostUrl = new Uri(tmp);
        }
        catch (SerializationException) { }
        try
        {
            var tmp = info.GetString("spAppWebUrl");
            this.spAppWebUrl = new Uri(tmp);
        }
        catch (SerializationException) { }
        try
        {
            var tmp = info.GetString("spLanguage");
            this.spLanguage = tmp;
        }
        catch (SerializationException) { }
        try
        {
            var tmp = info.GetString("spClientTag");
            this.spClientTag = tmp;
        }
        catch (SerializationException) { }

        try
        {
            var tmp = info.GetString("spProductNumber");
            this.spProductNumber = tmp;
        }
        catch (SerializationException) { }
    }



    [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
    protected virtual void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        if (spHostUrl != null) info.AddValue("spHostUrl", spHostUrl, spHostUrl.GetType());
        if (spAppWebUrl != null) info.AddValue("spAppWebUrl", spAppWebUrl, spAppWebUrl.GetType());
        if (spLanguage != null) info.AddValue("spLanguage", spLanguage, spLanguage.GetType());
        if (spClientTag != null) info.AddValue("spClientTag", spClientTag, spClientTag.GetType());
        if (spProductNumber != null) info.AddValue("spProductNumber", spProductNumber, spProductNumber.GetType());
    }

    [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
    {
        if (info == null)
            throw new ArgumentNullException("info");

        GetObjectData(info, context);
    }    

SharePointAcsContext:

protected SharePointAcsContext(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {
        if (info == null)
            throw new ArgumentNullException("info");

        this.contextToken = info.GetString("contextToken");
        this.contextTokenObj = (SharePointContextToken)info.GetValue("contextTokenObj", typeof(SharePointContextToken));
    }

    [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
    protected override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        base.GetObjectData(info, context);

        info.AddValue("contextToken", contextToken);
        info.AddValue("contextTokenObj", contextTokenObj);
    }

    [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
    {
        if (info == null)
            throw new ArgumentNullException("info");

        GetObjectData(info, context);
    }    

SharePointContextToken:

protected SharePointContextToken(SerializationInfo info, StreamingContext context)
        : base(info.GetString("Issuer")
            , info.GetString("Audience")
            , info.GetDateTime("ValidFrom")
            , info.GetDateTime("ValidTo")
            , JsonConvert.DeserializeObject<IEnumerable<JsonWebTokenClaim>>(info.GetString("Claims")))
    {
        if (info == null)
            throw new ArgumentNullException("info");
    }

    [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
    protected virtual void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        if (Issuer != null) info.AddValue("Issuer", Issuer, Issuer.GetType());
        if (Audience != null) info.AddValue("Audience", Audience, Audience.GetType());
        info.AddValue("ValidFrom", ValidFrom, ValidFrom.GetType());
        info.AddValue("ValidTo", ValidTo, ValidTo.GetType());
        info.AddValue("Claims", JsonConvert.SerializeObject(Claims));
    }

    [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
    {
        if (info == null)
            throw new ArgumentNullException("info");

        GetObjectData(info, context);
    }    

Hope it helps!

Here are the two zipped class: http://1drv.ms/1wy3E7B

ps: I am not fully satisfied with the SharePointContext constructor. If somebody know a better way, just share it.

| improve this answer | |
-1
protected override bool ValidateSharePointContext(SharePointContext spContext, HttpContextBase httpContext)
{
    SharePointHighTrustContext spHighTrustContext = spContext as SharePointHighTrustContext;

    if (spHighTrustContext != null)
    {
        Uri spHostUrl = SharePointContext.GetSPHostUrl(httpContext.Request);
        WindowsIdentity logonUserIdentity = httpContext.Request.LogonUserIdentity;

        return spHostUrl == spHighTrustContext.SPHostUrl &&
            logonUserIdentity != null &&
            logonUserIdentity.IsAuthenticated &&
            !logonUserIdentity.IsGuest &&
            logonUserIdentity.User == spHighTrustContext.LogonUserIdentity.User;
    }

    return false;
}

spHighTrustContext.LogonUserIdentity.User this get's null on second request in HighTrust On Prime

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.