경우 아무도 내가 (함께 오히려 추한 해결에) 발견 한 내용을이 문제에 대한 위로, 내가 여기에 제시 것이다 온다.
DotNetOpenAuth
과 Twitter 사이의 HTTP 트래픽을 조사하기 위해 Fiddler를 사용하면 인증 요청에 force_login=false
querystring 매개 변수가 포함되어있어 DNOA가 올바르게 작동하고 있음을 알 수 있습니다. 그러나 Fiddler의 스크립팅 기능을 사용하여 아웃 바운드 요청을 수정하고 force_login
매개 변수를 모두 제거하면 모든 것이 올바르게 작동하기 시작합니다. 어떤 force_login
매개 변수의 존재를 force_login=true
과 같은 것으로 처리함으로써 트위터의 구현이 잘못되었다고 생각합니다.
Twitter에서 API의 동작을 수정하는 것이 가능할 것이라고 생각하지 않기 때문에 좀 더 접근하기 쉬운 솔루션이 있는지 조사했습니다. DNOA 코드를 찾고
, 난
force_login=false
파라미터 무조건
DotNetOpenAuthWebConsumer.RequestAuthentication()
방법에 의해 HTTP 요청에 추가 된 (및 필요한 경우 후속 적으로 개질
true
)되는 것을 알 수있다.
이상적인 솔루션은 DNOA가 인증 요청 매개 변수를보다 세밀하게 제어하고 TwitterClient
이 force_login=false
매개 변수를 명시 적으로 제거하는 것입니다. 안타깝게도 현재의 DNOA 코드베이스는 이것을 직접 지원하지는 않지만 두 개의 사용자 정의 클래스를 생성하여 동일한 효과를 얻을 수 있습니다.
using System;
using System.Collections.Generic;
using System.Net;
using DotNetOpenAuth.AspNet.Clients;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth;
using DotNetOpenAuth.OAuth.ChannelElements;
using DotNetOpenAuth.OAuth.Messages;
namespace CustomDotNetOpenAuth
{
public class CustomDotNetOpenAuthWebConsumer : IOAuthWebWorker, IDisposable
{
private readonly WebConsumer _webConsumer;
public CustomDotNetOpenAuthWebConsumer(ServiceProviderDescription serviceDescription, IConsumerTokenManager tokenManager)
{
if (serviceDescription == null) throw new ArgumentNullException("serviceDescription");
if (tokenManager == null) throw new ArgumentNullException("tokenManager");
_webConsumer = new WebConsumer(serviceDescription, tokenManager);
}
public HttpWebRequest PrepareAuthorizedRequest(MessageReceivingEndpoint profileEndpoint, string accessToken)
{
return _webConsumer.PrepareAuthorizedRequest(profileEndpoint, accessToken);
}
public AuthorizedTokenResponse ProcessUserAuthorization()
{
return _webConsumer.ProcessUserAuthorization();
}
public void RequestAuthentication(Uri callback)
{
var redirectParameters = new Dictionary<string, string>();
var request = _webConsumer.PrepareRequestUserAuthorization(callback, null, redirectParameters);
_webConsumer.Channel.PrepareResponse(request).Send();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_webConsumer.Dispose();
}
}
}
}
다른 :
첫번째 빈 사전로 리디렉션 파라미터 사전 초기화 한 줄 변화 이격 원래 DotNetOpenAuthWebConsumer
클래스의 직접 카피 IOAuthWebWorker
의 맞춤 구현 요구 사항은 원래 TwitterClient
클래스를 기반으로하는 맞춤 OAuthClient
클래스입니다.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using DotNetOpenAuth.AspNet;
using DotNetOpenAuth.AspNet.Clients;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth;
using DotNetOpenAuth.OAuth.ChannelElements;
using DotNetOpenAuth.OAuth.Messages;
namespace CustomDotNetOpenAuth
{
public class CustomTwitterClient : OAuthClient
{
private static readonly string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ")" };
public static readonly ServiceProviderDescription TwitterServiceDescription = new ServiceProviderDescription
{
RequestTokenEndpoint = new MessageReceivingEndpoint("https://api.twitter.com/oauth/request_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
UserAuthorizationEndpoint = new MessageReceivingEndpoint("https://api.twitter.com/oauth/authenticate", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
AccessTokenEndpoint = new MessageReceivingEndpoint("https://api.twitter.com/oauth/access_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
};
public CustomTwitterClient(string consumerKey, string consumerSecret)
: this(consumerKey, consumerSecret, new AuthenticationOnlyCookieOAuthTokenManager())
{
}
public CustomTwitterClient(string consumerKey, string consumerSecret, IOAuthTokenManager tokenManager)
: base("twitter", new CustomDotNetOpenAuthWebConsumer(TwitterServiceDescription, new SimpleConsumerTokenManager(consumerKey, consumerSecret, tokenManager)))
{
}
protected override AuthenticationResult VerifyAuthenticationCore(AuthorizedTokenResponse response)
{
var accessToken = response.AccessToken;
var userId = response.ExtraData["user_id"];
var userName = response.ExtraData["screen_name"];
var profileRequestUrl = new Uri("https://api.twitter.com/1/users/show.xml?user_id=" + EscapeUriDataStringRfc3986(userId));
var profileEndpoint = new MessageReceivingEndpoint(profileRequestUrl, HttpDeliveryMethods.GetRequest);
var request = WebWorker.PrepareAuthorizedRequest(profileEndpoint, accessToken);
var extraData = new Dictionary<string, string> { { "accesstoken", accessToken } };
try
{
using (var profileResponse = request.GetResponse())
{
using (var responseStream = profileResponse.GetResponseStream())
{
var document = xLoadXDocumentFromStream(responseStream);
AddDataIfNotEmpty(extraData, document, "name");
AddDataIfNotEmpty(extraData, document, "location");
AddDataIfNotEmpty(extraData, document, "description");
AddDataIfNotEmpty(extraData, document, "url");
}
}
}
catch
{
// At this point, the authentication is already successful. Here we are just trying to get additional data if we can. If it fails, no problem.
}
return new AuthenticationResult(true, ProviderName, userId, userName, extraData);
}
private static XDocument xLoadXDocumentFromStream(Stream stream)
{
const int maxChars = 0x10000; // 64k
var settings = new XmlReaderSettings
{
MaxCharactersInDocument = maxChars
};
return XDocument.Load(XmlReader.Create(stream, settings));
}
private static void AddDataIfNotEmpty(Dictionary<string, string> dictionary, XDocument document, string elementName)
{
var element = document.Root.Element(elementName);
if (element != null)
{
AddItemIfNotEmpty(dictionary, elementName, element.Value);
}
}
private static void AddItemIfNotEmpty(IDictionary<string, string> dictionary, string key, string value)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
if (!string.IsNullOrEmpty(value))
{
dictionary[key] = value;
}
}
private static string EscapeUriDataStringRfc3986(string value)
{
var escaped = new StringBuilder(Uri.EscapeDataString(value));
for (var i = 0; i < UriRfc3986CharsToEscape.Length; i++)
{
escaped.Replace(UriRfc3986CharsToEscape[i], Uri.HexEscape(UriRfc3986CharsToEscape[i][0]));
}
return escaped.ToString();
}
}
}
이 두 개의 사용자 정의 클래스를 만든 데 : 그것은 또한 DNOA 기본 클래스 또는 다른 유틸리티 클래스 내부에있는 방법 중 몇 가지를 복제하는 데 필요로이 원래 TwitterClient
클래스보다 약간 더 많은 코드를 필요로합니다 구현은 단순히 MVC4 AuthConfig.cs
파일에 새로운 CustomTwitterClient
클래스의 인스턴스를 등록 수반 :
OAuthWebSecurity.RegisterClient(new CustomTwitterClient("myTwitterApiKey", "myTwitterApiSecret"));
나는이 작동 확인했습니다. 그것은 5 분이 걸렸다. Tim 감사합니다. – Matt