Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 8779791
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 13, 20262026-06-13T19:53:12+00:00 2026-06-13T19:53:12+00:00

I’m currently working on implementing a Dropbox OAuth client for my application. It’s been

  • 0

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));
                }
            }
        }
    }
}
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-13T19:53:14+00:00Added an answer on June 13, 2026 at 7:53 pm

    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))
        {}
    
    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I have a string like this: La Torre Eiffel paragonata all&#8217;Everest What PHP function
link Im having trouble converting the html entites into html characters, (&# 8217;) i
I want use html5's new tag to play a wav file (currently only supported
I'm parsing an RSS feed that has an &#8217; in it. SimpleXML turns this
I am currently running into a problem where an element is coming back from
I have thousands of HTML files to process using Groovy/Java and I need to
I'm working with an upstream system that sometimes sends me text destined for HTML/XML
Let's say I'm outputting a post title and in our database, it's Hello Y&#8217;all
I have a .ini file as follows: [playlist] numberofentries=2 File1=http://87.230.82.17:80 Title1=(#1 - 365/1400) Example
That's pretty much it. I'm using Nokogiri to scrape a web page what has

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.