定制的OAuth客户端MVC4 / DotNetOpenAuth - 缺少访问令牌密钥 [英] Custom OAuth client in MVC4 / DotNetOpenAuth - missing access token secret

查看:250
本文介绍了定制的OAuth客户端MVC4 / DotNetOpenAuth - 缺少访问令牌密钥的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在实施一个Dropbox的客户端的OAuth我的应用程序。这是一个相当痛苦的过程,直到我打到最后。一旦我的授权,当我试图访问用户数据,我收到了来自401回的Dropbox对令牌是无效的。我问在Dropbox的论坛,它看起来像我的要求是缺少Dropbox的返回回access_token_secret。我能够使用Fiddler挖出秘密并将其添加到我的请求的URL和它工作得很好,所以这肯定是问题。那么,为什么当它返回访问令牌不DotNetOpenAuth返回回访问令牌秘密?

有关参考,我的code:

公共类DropboxClient:OAuthClient
{
    公共静态只读ServiceProviderDescription DropboxServiceDescription =新ServiceProviderDescription
    {
        RequestTokenEndpoint =新MessageReceivingEndpoint(https://api.dropbox.com/1/oauth/request_token,HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest)
        UserAuthorizationEndpoint =新MessageReceivingEndpoint(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 =新ITamperProtectionChannelBindingElement [] {新PlaintextSigningBindingElement()}
    };    公共DropboxClient(字符串consumerKey,串consumerSecret):
        这个(consumerKey,consumerSecret,新AuthenticationOnlyCookieOAuthTokenManager())
    {
    }    公共DropboxClient(字符串consumerKey,串consumerSecret,IOAuthTokenManager tokenManager):
        基地(Dropbox的,DropboxServiceDescription,新SimpleConsumerTokenManager(consumerKey,consumerSecret,tokenManager))
    {
    }    保护覆盖DotNetOpenAuth.AspNet.AuthenticationResult VerifyAuthenticationCore(DotNetOpenAuth.OAuth.Messages.AuthorizedTokenResponse响应)
    {
        VAR profileEndpoint =新MessageReceivingEndpoint(https://api.dropbox.com/1/account/info,HttpDeliveryMethods.GetRequest);
        HttpWebRequest的要求= this.WebWorker prepareAuthorizedRequest(profileEndpoint,response.AccessToken)。        尝试
        {
            使用(WebResponse类profileResponse = request.GetResponse())
            {
                使用(流profileResponseStream = profileResponse.GetResponseStream())
                {
                    使用(StreamReader的读者=新的StreamReader(profileResponseStream))
                    {
                        字符串jsonText = reader.ReadToEnd();
                        JSS的JavaScriptSerializer =新的JavaScriptSerializer();
                        动态jsonData = jss.DeserializeObject(jsonText);
                        字典<字符串,字符串> extraData =新词典<字符串,字符串>();
                        extraData.Add(显示名,jsonData.display_name ??未知);
                        extraData.Add(用户id,jsonData.uid ??未知);
                        返回新DotNetOpenAuth.AspNet.AuthenticationResult(真,的ProviderName,extraData [用户id],extraData [显示名],extraData);
                    }
                }
            }
        }
        赶上(引发WebException前)
        {
            使用(流S = ex.Response.GetResponseStream())
            {
                使用(StreamReader的SR =新的StreamReader(S))
                {
                    串体= sr.ReadToEnd();
                    返回新DotNetOpenAuth.AspNet.AuthenticationResult(新的异常(机身,EX));
                }
            }
        }
    }
}


解决方案

我发现你的问题,当我正在寻找解决类似的问题。我通过2个新的类别,你可以在此 $ C $了解crwall帖子解决了这个问题。

我也将在这里复制和粘贴完整的信息:


DotNetOpenAuth.AspNet 401未授权错误和持久访问令牌密钥修复

在设计QuietThyme,我们的云电子书经理,我们知道每个人都讨厌创建新帐户,就像我们一样。我们开始寻找,我们可以充分利用,允许社会登录的OAuth和OpenID库。我们结束了使用用户身份验证 DotNetOpenAuth.AspNet 库,因为它支持微软,微博,Facebook,LinkedIn和雅虎,以及许多其他的权利了弓。虽然我们有一些问题,设置了这一切,最终我们只需要做一些小的定制得到大部分在的 previous coderwall帖子)。我们注意到,不同于所有其它的,LinkedIn的客户端将不进行身份验证,返回从DotNetOpenAuth 401未经授权错误。它很快因签名问题变得很明显,这是和查看源后,我们能够确定检索到的accessToken秘密不被与验证的个人资料信息请求中使用。

这实际上可以是有道理的,之所以说OAuthClient类不包括检索访问令牌的秘密在于,它通常不需要进行身份验证,这是ASP.NET的OAuth库的主要目的。

我们需要做出对API认证的请求,得到了用户登录之后,取回一些标准配置文件信息,包括电子邮件地址和全名。我们可以通过使用InMemoryOAuthTokenManager暂时解决了这个问题。

 公共类LinkedInCustomClient:OAuthClient
{
    私有静态的XDocument LoadXDocumentFromStream(流流)
    {
        VAR设置=新XmlReaderSettings
        {
            MaxCharactersInDocument = 65536L
        };
        返回XDocument.Load(XmlReader.Create(流设置));
    }    ///描述了LinkedIn的OAuth的服务提供者端点。
    私人静态只读ServiceProviderDescription LinkedInServiceDescription =
            新ServiceProviderDescription
            {
                AccessTokenEndpoint =
                        新MessageReceivingEndpoint(https://api.linkedin.com/uas/oauth/accessToken
                        HttpDeliveryMethods.PostRequest)
                RequestTokenEndpoint =
                        新MessageReceivingEndpoint(\"https://api.linkedin.com/uas/oauth/requestToken?scope=r_basicprofile+r_emailaddress\",
                        HttpDeliveryMethods.PostRequest)
                UserAuthorizationEndpoint =
                        新MessageReceivingEndpoint(https://www.linkedin.com/uas/oauth/authorize
                        HttpDeliveryMethods.PostRequest)
                TamperProtectionElements =
                        新ITamperProtectionChannelBindingElement [] {新HmacSha1SigningBindingElement()},
                // ProtocolVersion = ProtocolVersion.V10a
            };    私人字符串ConsumerKey {搞定;组; }
    私人字符串ConsumerSecret {搞定;组; }    公共LinkedInCustomClient(字符串consumerKey,串consumerSecret)
        :这个(consumerKey,consumerSecret,新AuthenticationOnlyCookieOAuthTokenManager()){}    公共LinkedInCustomClient(字符串consumerKey,串consumerSecret,IOAuthTokenManager tokenManager)
        :基地(LinkedIn,LinkedInServiceDescription,新SimpleConsumerTokenManager(consumerKey,consumerSecret,tokenManager))
    {
        ConsumerKey = consumerKey;
        ConsumerSecret = consumerSecret;
    }    //公共LinkedInCustomClient(字符串consumerKey,串consumerSecret):
    //基地(LinkedIn,LinkedInServiceDescription,consumerKey,consumerSecret){}    ///检查用户重定向从服务提供商回来后验证成功。
    ///从服务供应商认证结果返回的响应令牌。
    【超级pressMessage(Microsoft.Design,CA1031:DoNotCatchGeneralExceptionTypes
        理由=我们不如果请求失败无所谓了。)
    保护覆盖AuthenticationResult VerifyAuthenticationCore(AuthorizedTokenResponse响应)
    {
        //在这里看到现场选择器API http://developer.linkedin.com/docs/DOC-1014
        常量字符串profileRequestUrl =
            \"https://api.linkedin.com/v1/people/~:(id,first-name,last-name,headline,industry,summary,email-address)\";        字符串的accessToken = response.AccessToken;        VAR profileEndpoint =
            新MessageReceivingEndpoint(profileRequestUrl,HttpDeliveryMethods.GetRequest);        尝试
        {
            InMemoryOAuthTokenManager imoatm =新InMemoryOAuthTokenManager(ConsumerKey,ConsumerSecret);
            imoatm.ExpireRequestTokenAndStoreNewAccessToken(的String.Empty,的String.Empty,的accessToken,(响应,ITokenSecretContainingMessage).TokenSecret);
            WebConsumer W =新WebConsumer(LinkedInServiceDescription,imoatm);            HttpWebRequest的要求= W prepareAuthorizedRequest(profileEndpoint,的accessToken)。            使用(WebResponse类profileResponse = request.GetResponse())
            {
                使用(流responseStream = profileResponse.GetResponseStream())
                {
                    的XDocument文档= LoadXDocumentFromStream(responseStream);
                    字符串userid = document.Root.Element(ID)值。                    。字符串的firstName = document.Root.Element(直呼其名)值;
                    字符串的lastName = document.Root.Element(最后的名字)值。
                    字符串username =名字++ lastName的;                    字符串email =的String.Empty;
                    尝试
                    {
                        电子邮件= document.Root.Element(电子邮件地址)值。
                    }
                    赶上(例外)
                    {
                    }                    VAR extraData =新词典<字符串,字符串>();
                    extraData.Add(的accessToken的accessToken);
                    extraData.Add(名,用户名);
                    extraData.AddDataIfNotEmpty(文件,标题);
                    extraData.AddDataIfNotEmpty(文件摘要);
                    extraData.AddDataIfNotEmpty(文件,业内人士);                    如果(!String.IsNullOrEmpty(电子邮件))
                    {
                        extraData.Add(邮件,邮件);
                    }                    返回新AuthenticationResult(
                        isSuccessful:真实,供应商:this.ProviderName,providerUserId:用户id,用户名:用​​户名,extraData:extraData);
                }
            }
        }
        赶上(例外的例外)
        {
            返回新AuthenticationResult(例外);
        }
    }
}

下面是一个已经从微软写的基地LinkedIn客户端更改了部分。

  InMemoryOAuthTokenManager imoatm =新InMemoryOAuthTokenManager(ConsumerKey,ConsumerSecret);
imoatm.ExpireRequestTokenAndStoreNewAccessToken(的String.Empty,的String.Empty,的accessToken,(响应,ITokenSecretContainingMessage).TokenSecret);
WebConsumer W =新WebConsumer(LinkedInServiceDescription,imoatm);HttpWebRequest的要求= W prepareAuthorizedRequest(profileEndpoint,的accessToken)。

不幸的是, IOAuthTOkenManger.ReplaceRequestTokenWithAccessToken(..)方法没有得到执行后才 VerifyAuthentication()方法返回,所以我们反而要创建一个新的TokenManager和并创建一个 WebConsumer 的HttpWebRequest 使用的accessToken凭据我们只是检索。

这解决了我们简单的401未授权的问题。

现在会发生什么,如果你想坚持的认证过程后的accessToken凭据?这可能是一个升降梭箱客户,例如,在那里要文件asyncronously同步到用户的升降梭箱是有用的。这个问题可以追溯到ASPNET文库是书面的,它被认为DotNetOpenAuth将只用于用户authethentication,不作为进一步的OAuth的API调用的基础的方式。值得庆幸的是修复相当简单,所有我所要做的就是修改基 AuthetnicationOnlyCookieOAuthTokenManger ,使 ReplaceRequestTokenWithAccessToken(..)方法存储新的accessToken键和秘密。

  ///<总结>
///商店OAuth凭证在当前请求的cookie
///< /总结>
公共类PersistentCookieOAuthTokenManagerCustom:AuthenticationOnlyCookieOAuthTokenManager
{
    ///<总结>
    ///用于标记饼干的关键
    ///< /总结>
    私人常量字符串TokenCookieKey =OAuthTokenSecret;    ///<总结>
    ///主请求上下文。
    ///< /总结>
    私人只读HttpContextBase primaryContext;    ///<总结>
    ///初始化℃的新的实例;参见CREF =AuthenticationOnlyCookieOAuthTokenManager/>类。
    ///< /总结>
    公共PersistentCookieOAuthTokenManagerCustom():基地()
    {
    }    ///<总结>
    ///初始化℃的新的实例;参见CREF =AuthenticationOnlyCookieOAuthTokenManager/>类。
    ///< /总结>
    ///< PARAM NAME =背景方式>当前请求上下文< /参数>
    公共PersistentCookieOAuthTokenManagerCustom(HttpContextBase上下文):基地(上下文)
    {
        this.primaryContext =背景;
    }    ///<总结>
    ///获取有效的HttpContext对象来使用。
    ///< /总结>
    私人HttpContextBase上下文
    {
        得到
        {
            返回this.primaryContext?新HttpContextWrapper(HttpContext.Current);
        }
    }
    ///<总结>
    ///替换为访问令牌请求令牌。
    ///< /总结>
    ///< PARAM NAME =requestToken方式>请求令牌LT; /参数>
    ///< PARAM NAME =的accessToken方式>访问令牌LT; /参数>
    ///< PARAM NAME =accessTokenSecret方式>访问令牌秘密< /参数>
    新公共无效ReplaceRequestTokenWithAccessToken(字符串requestToken,字符串的accessToken,串accessTokenSecret)
    {
        //删除旧requestToken饼干
        // VAR饼干=新的HttpCookie(TokenCookieKey)
        // {
        //值=的String.Empty,
        //过期= DateTime.UtcNow.AddDays(-5)
        //};
        //this.Context.Response.Cookies.Set(cookie);        //添加新的accessToken +饼干的秘密
        StoreRequestToken(的accessToken,accessTokenSecret);    }}

然后使用该 PersistentCookieOAuthTokenManager 所有你需要做的就是修改DropboxClient构造函数或其他任何客户端,你想坚持的秘密的accessToken

 公共DropBoxCustomClient(字符串consumerKey,串consumerSecret)
        :这个(consumerKey,consumerSecret,新PersistentCookieOAuthTokenManager()){}    公共DropBoxCustomClient(字符串consumerKey,串consumerSecret,IOAuthTokenManager tokenManager)
        :基地(Dropbox的,DropBoxServiceDescription,新SimpleConsumerTokenManager(consumerKey,consumerSecret,tokenManager))
    {}

I'm currently working on implementing a Dropbox OAuth client for my application. It's been a fairly painless process until I hit the end. Once I've authorized, when I attempt to access user data I get a 401 back from Dropbox about the token being invalid. I asked on the Dropbox forums and it looks like my request is missing the access_token_secret that Dropbox returns back. I was able to use Fiddler to dig out the secret and add it to my request url and it worked fine, so that's definitely the issue. So why doesn't DotNetOpenAuth return back the access token secret when it returns the access token?

For reference, my code:

public class DropboxClient : OAuthClient
{
    public static readonly ServiceProviderDescription DropboxServiceDescription = new ServiceProviderDescription
    {
        RequestTokenEndpoint = new MessageReceivingEndpoint("https://api.dropbox.com/1/oauth/request_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
        UserAuthorizationEndpoint = new MessageReceivingEndpoint("https://www.dropbox.com/1/oauth/authorize", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
        AccessTokenEndpoint = new MessageReceivingEndpoint("https://api.dropbox.com/1/oauth/access_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
        TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new PlaintextSigningBindingElement() }
    };

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

    public DropboxClient(string consumerKey, string consumerSecret, IOAuthTokenManager tokenManager) : 
        base("dropbox", DropboxServiceDescription, new SimpleConsumerTokenManager(consumerKey, consumerSecret, tokenManager))
    {
    }

    protected override DotNetOpenAuth.AspNet.AuthenticationResult VerifyAuthenticationCore(DotNetOpenAuth.OAuth.Messages.AuthorizedTokenResponse response)
    {            
        var profileEndpoint = new MessageReceivingEndpoint("https://api.dropbox.com/1/account/info", HttpDeliveryMethods.GetRequest);
        HttpWebRequest request = this.WebWorker.PrepareAuthorizedRequest(profileEndpoint, response.AccessToken);

        try
        {
            using (WebResponse profileResponse = request.GetResponse())
            {
                using (Stream profileResponseStream = profileResponse.GetResponseStream())
                {
                    using (StreamReader reader = new StreamReader(profileResponseStream))
                    {
                        string jsonText = reader.ReadToEnd();
                        JavaScriptSerializer jss = new JavaScriptSerializer();
                        dynamic jsonData = jss.DeserializeObject(jsonText);
                        Dictionary<string, string> extraData = new Dictionary<string, string>();
                        extraData.Add("displayName", jsonData.display_name ?? "Unknown");
                        extraData.Add("userId", jsonData.uid ?? "Unknown");
                        return new DotNetOpenAuth.AspNet.AuthenticationResult(true, ProviderName, extraData["userId"], extraData["displayName"], extraData);
                    }
                }
            }
        }
        catch (WebException ex)
        {
            using (Stream s = ex.Response.GetResponseStream())
            {
                using (StreamReader sr = new StreamReader(s))
                {
                    string body = sr.ReadToEnd();
                    return new DotNetOpenAuth.AspNet.AuthenticationResult(new Exception(body, ex));
                }
            }
        }
    }
}

解决方案

I found your question when I was searching for solution to a similar problem. I solved it by making 2 new classes, which you can read about in this coderwall post.

I'll also copy and paste the full post here:


DotNetOpenAuth.AspNet 401 Unauthorized Error and Persistent Access Token Secret Fix

When designing QuietThyme, our Cloud Ebook Manager, we knew that everyone hates creating new accounts just as much as we do. We started looking for OAuth and OpenId libraries that we could leverage to allow for social login. We ended up using the DotNetOpenAuth.AspNet library for user authentication, because it supports Microsoft, Twitter, Facebook, LinkedIn and Yahoo, and many others right out of the bow. While we had some issues setting it all up, in the end we only needed to do a few small customizations to get most of it working (described in a previous coderwall post). We noticed that, unlike all the others, the LinkedIn client would not authenticate, returning a 401 Unauthorized Error from DotNetOpenAuth. It quickly became apparent that this was due to a signature issue, and after looking at the source we were able to determine that the retrieved AccessToken secret is not being used with the authenticated profile info request.

It acutally makes sense, the reason that OAuthClient class doesn't include the retrieved access token secret is that it's normally not needed for authentication purposes, which is the primary purpose of the ASP.NET OAuth library.

We needed to make authenticated requests against the api, after the user has logged in, to retrieve some standard profile information, including email address and full name. We were able to solve this issue by making use of an InMemoryOAuthTokenManager temporarily.

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);
        }
    }
}

Here's the section that has changed from the base LinkedIn client written by Microsoft.

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);

Unfortunately, the IOAuthTOkenManger.ReplaceRequestTokenWithAccessToken(..) method does not get executed until after the VerifyAuthentication() method returns, so we instead have to create a new TokenManager and and create a WebConsumer and HttpWebRequest using the AccessToken credentials we just retrieved.

This solves our simple 401 Unauthorized issue.

Now what happens if you would like to persist the AccessToken credentials after the authentication process? This could be useful for a DropBox client for instance, where you would like to sync files to a user's DropBox asyncronously. The issue goes back to the way the AspNet library was written, it was assumed that DotNetOpenAuth would only be used for user authethentication, not as a basis for futher OAuth api calls. Thankfully the fix was fairly simple, all I had to do was modify the base AuthetnicationOnlyCookieOAuthTokenManger so that the ReplaceRequestTokenWithAccessToken(..) method stored the new AccessToken key and secrets.

/// <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);

    }

}

Then to use this PersistentCookieOAuthTokenManager all you need to do is modify your DropboxClient constructor, or any other client where you would like to persist the AccessToken Secret

    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))
    {}

这篇关于定制的OAuth客户端MVC4 / DotNetOpenAuth - 缺少访问令牌密钥的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆