我正在尝试让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_uri
QueryAccessToken
OAuthWebSecurity
DotNetOpenAuth.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
不仅会被忽略,而且会被禁止(至少在本质上是动态的)。所以,看起来我不能使用一个可以在所有其他平台上运行并遵循标准的库——我必须创建自己的。
叹。