Getting Secure Store Credentials in SharePoint 2013

Last week I reviewed some code that contains memory leaks and I noticed that they fetched some credentials from the Secure Store Service in a way that just didn't seem right to me. If you look at this code sample on MSDN you should be able to see what I mean.

They created a new SPServiceContext by first fetching a SPSite object of the Central Admin site. While this might (don't know) be required for the code sample, this doesn't make sense if we are inside a SharePoint context. I do know however why they thought they needed to go with this approach. They were creating a custom claims provider and the code for this custom claims provider does not run within a site context, but inside the Secure Token Service. They could however use a much easier approach to fetch the default secure store provider. Below you'll find the three classes I create for them that solved the memory issue.

class SecureStoreCredentials {
    public string UserName { get; set; }
    public string Password { get; set; }
    public string Pin { get; set; }
}

static class SecureStringUtility {
    public static string ConvertToString(this SecureString secureString) {
        string retVal = null;

        if (secureString != null) {
            IntPtr pPlainText = IntPtr.Zero;
            try {
                pPlainText = Marshal.SecureStringToBSTR(secureString);
                retVal = Marshal.PtrToStringBSTR(pPlainText);
            } finally {
                if (pPlainText != IntPtr.Zero) {
                    Marshal.FreeBSTR(pPlainText);
                }
            }
        }

        return retVal;
    }
}

class SecureStoreUtility {
    public static SecureStoreCredentials GetCredentials(string applicationId) {
        if (string.IsNullOrEmpty(applicationId)) {
            throw new ArgumentNullException("applicationId");
        }

        var retVal = new SecureStoreCredentials();

        SPServiceContext context = SPServiceContext.GetContext(SPServiceApplicationProxyGroup.Default, SPSiteSubscriptionIdentifier.Default);
        SecureStoreServiceProxy ssp = new SecureStoreServiceProxy();
        ISecureStore iss = ssp.GetSecureStore(context);
            
        SecureStoreCredentialCollection credentials = null;

        try {
            credentials = iss.GetCredentials(applicationId);
            if (credentials != null) {
                foreach (SecureStoreCredential credential in credentials) {
                    if (credential == null) {
                        continue;
                    }

                    switch (credential.CredentialType) {
                        case SecureStoreCredentialType.WindowsUserName:
                        case SecureStoreCredentialType.UserName:
                            retVal.UserName = credential.Credential.ConvertToString();
                            break;

                        case SecureStoreCredentialType.WindowsPassword:
                        case SecureStoreCredentialType.Password:
                            retVal.Password = credential.Credential.ConvertToString();
                            break;

                        case SecureStoreCredentialType.Pin:
                            retVal.Pin = credential.Credential.ConvertToString();
                            break;
                    }
                }
            }
        } catch (Exception ex) {
            Debug.WriteLine(ex.ToString());                
        } finally {
            if (credentials != null) {
                credentials.Dispose();
            }
        }

        return retVal;
    }
}

The important line here is:

SPServiceContext context = SPServiceContext.GetContext(SPServiceApplicationProxyGroup.Default, SPSiteSubscriptionIdentifier.Default);

Which fetches a SPServiceContext without the need of a SPSite object.

Have fun with it!

Wesley

No Comments