3

我正在尝试让ASP.NET MVC站点接受 Salesforce 作为身份验证提供程序,但我没有任何运气。我将从IAuthenticationClient到目前为止的内容开始:

public class SalesForceOAuth2Client : OAuth2Client
{
    private readonly String consumerKey;
    private readonly String consumerSecret;

    #if DEBUG
        private const String BaseEndpoint = @"https://test.salesforce.com";
    #else
        private const String BaseEndpoint = @"https://login.salesforce.com";
    #endif

    private const String AuthorizeEndpoint = BaseEndpoint + @"/services/oauth2/authorize";
    private const String TokenEndpoint = BaseEndpoint + @"/services/oauth2/token";
    private const String RevokeEndpoint = BaseEndpoint + @"/services/oauth2/revoke";

    public SalesForceOAuth2Client(String consumerKey, String consumerSecret)
        : base("SalesForce")
    {
        if (String.IsNullOrWhiteSpace(consumerKey))
        {
            throw new ArgumentNullException("consumerKey");
        }
        if (String.IsNullOrWhiteSpace(consumerSecret))
        {
            throw new ArgumentNullException("consumerSecret");
        }
        this.consumerKey = consumerKey;
        this.consumerSecret = consumerSecret;
    }

    protected override Uri GetServiceLoginUrl(Uri returnUrl)
    {
        String redirect_url = returnUrl.AbsoluteUri;

        // Hack to work-around the __provider__ & __sid__ query parameters,
        // but it is ultimately useless.
        /*String state = String.Empty;
        Int32 q = redirect_url.IndexOf('?');
        if (q != -1)
        {
            state = redirect_url.Substring(q + 1);
            redirect_url = redirect_url.Substring(0, q);
        }*/

        var builder = new UriBuilder(AuthorizeEndpoint);
        builder.Query = "response_type=code"
                      + "&client_id=" + HttpUtility.UrlEncode(this.consumerKey)
                      + "&scope=full"
                      + "&redirect_uri=" + HttpUtility.UrlEncode(redirect_url)
                      // Part of the above hack (tried to use `state` parameter)
                      /*+ (!String.IsNullOrWhiteSpace(state) ? "&state=" + HttpUtility.UrlEncode(state) : String.Empty)*/;
        return builder.Uri;
    }

    protected override IDictionary<String, String> GetUserData(String accessToken)
    {
        // I am not sure how to get this yet as everything concrete I've
        // seen uses the service's getUserInfo call (but this service relies
        // heavily on a username, password, token combination. The whole point
        // of using oatuh is to avoid asking the user for his/her credentials)
        // more information about the original call:
        // http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_getuserinfo.htm

        // Return static information for now
        //TODO: Get information dynamically
        return new Dictionary<String, String>
        {
            { "username", "BradChristie" },
            { "name", "Brad Christie" }
        };
    }

    protected override String QueryAccessToken(Uri returnUrl, String authorizationCode)
    {
        HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(TokenEndpoint);
        webRequest.ContentType = "application/x-www-form-urlencoded";
        webRequest.Method = "POST";
        using (StreamWriter streamWriter = new StreamWriter(webRequest.GetRequestStream()))
        {
            streamWriter.Write("grant_type=authorization_code");
            streamWriter.Write("&client_id=" + HttpUtility.UrlEncode(this.consumerKey));
            streamWriter.Write("&client_secret=" + HttpUtility.UrlEncode(this.consumerSecret));
            streamWriter.Write("&redirect_uri=" + HttpUtility.UrlEncode(returnUrl.AbsoluteUri));
            streamWriter.Write("&code=" + HttpUtility.UrlEncode(authorizationCode));
            streamWriter.Flush();
        }

        HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
        if (webResponse.StatusCode == HttpStatusCode.OK)
        {
            using (StreamReader streamReader = new StreamReader(webResponse.GetResponseStream()))
            {
                String response = streamReader.ReadToEnd();
                var queryString = HttpUtility.ParseQueryString(response);
                return queryString["access_token"];
            }
        }
        return String.Empty;
    }
}

主要问题是redirect_uri!= Callback Url

Salesforce 强制您在应用程序配置中提供的回调URL与 中提供的值完全匹配。不幸的是,依赖于,并且该库附加了两个查询参数:和. 如果我尝试删除这些(参见 hack 中的 hack ),显然登录失败,因为回手不知道如何在不知道要使用哪个提供程序的情况下继续请求。redirect_uriQueryAccessTokenOAuthWebSecurityDotNetOpenAuth.AspNet__provider____sid__GetServiceLoginUrl

为了解决这个问题,我确实注意到请求调用接受一个可选state参数,该参数(基本上)用于在请求/回调之间来回传递东西。但是,依赖__provider____sid__成为自己的密钥data=__provider__%3DSalesForce%26__sid__%3D1234567890是没有用的。

是否有一种解决方法,而不必分叉/重新编译Microsoft.Web.WebPages.OAuth库并修改OAuthWebSecurity.VerifyAuthenticationCore(HttpContextBase, String)方法以data首先查看,然后继续OpenAuthSecurityMananer.GetProviderName

此外,如果注册很重要(AuthConfig.cs):

OAuthWebSecurity.RegisterClient(
  new SalesForceOAuth2Client(/*consumerKey*/, /*consumerSecret*/),
  "SalesForce",
  new Dictionary<String, Object>()
);

更新 (11.01.2013)

我刚收到 Salesforce 的回复。看起来他们不知道如何实现RFC 的3.1.2,这意味着您发送的任何查询参数return_uri不仅会被忽略,而且会被禁止(至少在本质上是动态的)。所以,看起来我不能使用一个可以在所有其他平台上运行并遵循标准的库——我必须创建自己的。

叹。

4

0 回答 0