Asp.net mvc 4 MVC4/DotNetOpenAuth中的自定义OAuth客户端-缺少访问令牌机密

Asp.net mvc 4 MVC4/DotNetOpenAuth中的自定义OAuth客户端-缺少访问令牌机密,asp.net-mvc-4,dotnetopenauth,Asp.net Mvc 4,Dotnetopenauth,我目前正在为我的应用程序实现Dropbox OAuth客户端。在我到达终点之前,这是一个相当无痛的过程。一旦我授权,当我尝试访问用户数据时,我从Dropbox中得到一个401返回,关于令牌无效。我在Dropbox论坛上问过,看起来我的请求缺少Dropbox返回的访问令牌秘密。我能够使用Fiddler挖掘出秘密并将其添加到我的请求url中,它工作得很好,所以这肯定是问题所在。那么,为什么DotNetOpenAuth在返回访问令牌时不返回访问令牌密钥呢 作为参考,我的代码: 公共类DropboxCl

我目前正在为我的应用程序实现Dropbox OAuth客户端。在我到达终点之前,这是一个相当无痛的过程。一旦我授权,当我尝试访问用户数据时,我从Dropbox中得到一个401返回,关于令牌无效。我在Dropbox论坛上问过,看起来我的请求缺少Dropbox返回的访问令牌秘密。我能够使用Fiddler挖掘出秘密并将其添加到我的请求url中,它工作得很好,所以这肯定是问题所在。那么,为什么DotNetOpenAuth在返回访问令牌时不返回访问令牌密钥呢

作为参考,我的代码:

公共类DropboxClient:OAuthClient
{
公共静态只读ServiceProviderDescription DropboxServiceDescription=新ServiceProviderDescription
{
RequestTokenEndpoint=新消息ReceivingEndpoint(“https://api.dropbox.com/1/oauth/request_token,HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
UserAuthorizationEndpoint=新消息ReceivingEndpoint(“https://www.dropbox.com/1/oauth/authorize,HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
AccessTokenEndpoint=新的MessageReceivingEndpoint(“https://api.dropbox.com/1/oauth/access_token,HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
TamperProtectionElements=new-ITamperProtectionChannelBindingElement[]{new-PlaintextSigningBindingElement()}
};
public DropboxClient(字符串consumerKey,字符串ConsumerCret):
这(consumerKey、ConsumerCret、new AuthenticationOnlyCookieAuthTokenManager())
{
}
public DropboxClient(字符串consumerKey、字符串ConsumerCret、IOAuthTokenManager tokenManager):
基本(“dropbox”、DropboxServiceDescription、新SimpleConsumerTokenManager(consumerKey、ConsumerCret、tokenManager))
{
}
受保护的覆盖DotNetOpenAuth.AspNet.AuthenticationResult VerifyAuthenticationCore(DotNetOpenAuth.OAuth.Messages.AuthorizedTokenResponse响应)
{            
var profileEndpoint=new messagereceivingendendpoint(“https://api.dropbox.com/1/account/info,HttpDeliveryMethods.GetRequest);
HttpWebRequest请求=this.WebWorker.PrepareAuthorizedRequest(profileEndpoint,response.AccessToken);
尝试
{
使用(WebResponse profileResponse=request.GetResponse())
{
使用(Stream profileResponseStream=profileResponse.GetResponseStream())
{
使用(StreamReader=新StreamReader(profileResponseStream))
{
字符串jsonText=reader.ReadToEnd();
JavaScriptSerializer jss=新的JavaScriptSerializer();
动态jsonData=jss.DeserializeObject(jsonText);
Dictionary extraData=新字典();
添加(“displayName”,jsonData.display_name??“未知”);
Add(“userId”,jsonData.uid??“未知”);
返回新的DotNetOpenAuth.AspNet.AuthenticationResult(true、ProviderName、extraData[“userId”]、extraData[“displayName”]、extraData);
}
}
}
}
捕获(WebException ex)
{
使用(流s=ex.Response.GetResponseStream())
{
使用(StreamReader sr=新StreamReader)
{
字符串体=sr.ReadToEnd();
返回新的DotNetOpenAuth.AspNet.AuthenticationResult(新异常(body,ex));
}
}
}
}
}

OAuthClient类不包含访问令牌机密的原因是,通常不需要它来进行身份验证,而身份验证是ASP.NET OAuth库的主要目的


也就是说,如果您想在您的案例中检索访问令牌机密,可以重写VerifyAuthentication()方法,而不是像上面那样重写VerifyAuthenticationCore()。在VerifyAuthentication()内部,您可以调用WebWorker.ProcessUserAuthorization()来验证登录,并且从返回的AuthorizedTokenResponse对象,您可以访问令牌密码。

在进行一些挖掘之后,我可以通过如下更改构造函数逻辑来解决此问题:

公共DropboxClient(字符串consumerKey,字符串ConsumerCret):
这(consumerKey、ConsumerCret、new AuthenticationOnlyCookieAuthTokenManager())
{
}
public DropboxClient(字符串consumerKey、字符串ConsumerCret、IOAuthTokenManager tokenManager):
基本(“dropbox”、DropboxServiceDescription、新SimpleConsumerTokenManager(consumerKey、ConsumerCret、tokenManager))
{
}
变成

公共DropboxClient(字符串consumerKey,字符串ConsumerCret):
基本(“dropbox”,DropboxServiceDescription,consumerKey,ConsumerCret)
{
}
深入研究DNOA源代码可以发现,如果只使用使用者密钥和密码构造OAuthClient(我的基类),它将使用InMemoryAuthTokenManager而不是SimpleConsumerTokenManager。我不知道为什么,但现在我的访问令牌密码被正确地附加到授权请求中的签名中,并且一切正常。希望这能帮助其他人。同时,我可能会在一篇博客文章中对此进行清理,因为网上(我可以找到)没有任何指导

编辑:我将撤销我的回答,因为正如一位同事指出的那样,这将处理一个请求,但现在我正在使用内存管理器,一旦我完全返回浏览器,它将刷新(我假设)。因此,我认为这里的根本问题是,我需要获取访问令牌机密,但我仍然没有看到如何做。

我发现
public class LinkedInCustomClient : OAuthClient
{
    private static XDocument LoadXDocumentFromStream(Stream stream)
    {
        var settings = new XmlReaderSettings
        {
            MaxCharactersInDocument = 65536L
        };
        return XDocument.Load(XmlReader.Create(stream, settings));
    }

    /// Describes the OAuth service provider endpoints for LinkedIn.
    private static readonly ServiceProviderDescription LinkedInServiceDescription =
            new ServiceProviderDescription
            {
                AccessTokenEndpoint =
                        new MessageReceivingEndpoint("https://api.linkedin.com/uas/oauth/accessToken",
                        HttpDeliveryMethods.PostRequest),
                RequestTokenEndpoint =
                        new MessageReceivingEndpoint("https://api.linkedin.com/uas/oauth/requestToken?scope=r_basicprofile+r_emailaddress",
                        HttpDeliveryMethods.PostRequest),
                UserAuthorizationEndpoint =
                        new MessageReceivingEndpoint("https://www.linkedin.com/uas/oauth/authorize",
                        HttpDeliveryMethods.PostRequest),
                TamperProtectionElements =
                        new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
                //ProtocolVersion = ProtocolVersion.V10a
            };

    private string ConsumerKey { get; set; }
    private string ConsumerSecret { get; set; }

    public LinkedInCustomClient(string consumerKey, string consumerSecret)
        : this(consumerKey, consumerSecret, new AuthenticationOnlyCookieOAuthTokenManager()) { }

    public LinkedInCustomClient(string consumerKey, string consumerSecret, IOAuthTokenManager tokenManager)
        : base("linkedIn", LinkedInServiceDescription, new SimpleConsumerTokenManager(consumerKey, consumerSecret, tokenManager))
    {
        ConsumerKey = consumerKey;
        ConsumerSecret = consumerSecret;
    }

    //public LinkedInCustomClient(string consumerKey, string consumerSecret) :
    //    base("linkedIn", LinkedInServiceDescription, consumerKey, consumerSecret) { }

    /// Check if authentication succeeded after user is redirected back from the service provider.
    /// The response token returned from service provider authentication result. 
    [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
        Justification = "We don't care if the request fails.")]
    protected override AuthenticationResult VerifyAuthenticationCore(AuthorizedTokenResponse response)
    {
        // See here for Field Selectors API http://developer.linkedin.com/docs/DOC-1014
        const string profileRequestUrl =
            "https://api.linkedin.com/v1/people/~:(id,first-name,last-name,headline,industry,summary,email-address)";

        string accessToken = response.AccessToken;

        var profileEndpoint =
            new MessageReceivingEndpoint(profileRequestUrl, HttpDeliveryMethods.GetRequest);

        try
        {
            InMemoryOAuthTokenManager imoatm = new InMemoryOAuthTokenManager(ConsumerKey, ConsumerSecret);
            imoatm.ExpireRequestTokenAndStoreNewAccessToken(String.Empty, String.Empty, accessToken, (response as ITokenSecretContainingMessage).TokenSecret);
            WebConsumer w = new WebConsumer(LinkedInServiceDescription, imoatm);

            HttpWebRequest request = w.PrepareAuthorizedRequest(profileEndpoint, accessToken);

            using (WebResponse profileResponse = request.GetResponse())
            {
                using (Stream responseStream = profileResponse.GetResponseStream())
                {
                    XDocument document = LoadXDocumentFromStream(responseStream);
                    string userId = document.Root.Element("id").Value;

                    string firstName = document.Root.Element("first-name").Value;
                    string lastName = document.Root.Element("last-name").Value;
                    string userName = firstName + " " + lastName;

                    string email = String.Empty;
                    try
                    {
                        email = document.Root.Element("email-address").Value;
                    }
                    catch(Exception)
                    {
                    }

                    var extraData = new Dictionary<string, string>();
                    extraData.Add("accesstoken", accessToken);
                    extraData.Add("name", userName);
                    extraData.AddDataIfNotEmpty(document, "headline");
                    extraData.AddDataIfNotEmpty(document, "summary");
                    extraData.AddDataIfNotEmpty(document, "industry");

                    if(!String.IsNullOrEmpty(email))
                    {
                        extraData.Add("email",email);
                    }

                    return new AuthenticationResult(
                        isSuccessful: true, provider: this.ProviderName, providerUserId: userId, userName: userName, extraData: extraData);
                }
            }
        }
        catch (Exception exception)
        {
            return new AuthenticationResult(exception);
        }
    }
}
InMemoryOAuthTokenManager imoatm = new InMemoryOAuthTokenManager(ConsumerKey, ConsumerSecret);
imoatm.ExpireRequestTokenAndStoreNewAccessToken(String.Empty, String.Empty, accessToken, (response as ITokenSecretContainingMessage).TokenSecret);
WebConsumer w = new WebConsumer(LinkedInServiceDescription, imoatm);

HttpWebRequest request = w.PrepareAuthorizedRequest(profileEndpoint, accessToken);
/// <summary>
/// Stores OAuth tokens in the current request's cookie
/// </summary>
public class PersistentCookieOAuthTokenManagerCustom : AuthenticationOnlyCookieOAuthTokenManager
{
    /// <summary>
    /// Key used for token cookie
    /// </summary>
    private const string TokenCookieKey = "OAuthTokenSecret";

    /// <summary>
    /// Primary request context.
    /// </summary>
    private readonly HttpContextBase primaryContext;

    /// <summary>
    /// Initializes a new instance of the <see cref="AuthenticationOnlyCookieOAuthTokenManager"/> class.
    /// </summary>
    public PersistentCookieOAuthTokenManagerCustom() : base()
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="AuthenticationOnlyCookieOAuthTokenManager"/> class.
    /// </summary>
    /// <param name="context">The current request context.</param>
    public PersistentCookieOAuthTokenManagerCustom(HttpContextBase context) : base(context)
    {
        this.primaryContext = context;
    }

    /// <summary>
    /// Gets the effective HttpContext object to use.
    /// </summary>
    private HttpContextBase Context
    {
        get
        {
            return this.primaryContext ?? new HttpContextWrapper(HttpContext.Current);
        }
    }


    /// <summary>
    /// Replaces the request token with access token.
    /// </summary>
    /// <param name="requestToken">The request token.</param>
    /// <param name="accessToken">The access token.</param>
    /// <param name="accessTokenSecret">The access token secret.</param>
    public new void ReplaceRequestTokenWithAccessToken(string requestToken, string accessToken, string accessTokenSecret)
    {
        //remove old requestToken Cookie
        //var cookie = new HttpCookie(TokenCookieKey)
        //{
        //    Value = string.Empty,
        //    Expires = DateTime.UtcNow.AddDays(-5)
        //};
        //this.Context.Response.Cookies.Set(cookie);

        //Add new AccessToken + secret Cookie
        StoreRequestToken(accessToken, accessTokenSecret);

    }

}
    public DropBoxCustomClient(string consumerKey, string consumerSecret)
        : this(consumerKey, consumerSecret, new PersistentCookieOAuthTokenManager()) { }

    public DropBoxCustomClient(string consumerKey, string consumerSecret, IOAuthTokenManager tokenManager)
        : base("dropBox", DropBoxServiceDescription, new SimpleConsumerTokenManager(consumerKey, consumerSecret, tokenManager))
    {}
  string token = response.AccessToken; ;
  string secret = (response as ITokenSecretContainingMessage).TokenSecret;