I've been banging my head on the wall for several days, but I finally have something that works. It would be interesting to know if this is a valid solution!
First create a new OAuthClient:
public class TwitterClient : OAuthClient { /// <summary> /// The description of Twitter OAuth protocol URIs for use with their "Sign in with Twitter" feature. /// </summary> 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 TwitterClient(string consumerKey, string consumerSecret) : base("twitter", TwitterServiceDescription, consumerKey, consumerSecret) { } /// Check if authentication succeeded after user is redirected back from the service provider. /// The response token returned from service provider authentication result. protected override AuthenticationResult VerifyAuthenticationCore(AuthorizedTokenResponse response) { string accessToken = response.AccessToken; string accessSecret = (response as ITokenSecretContainingMessage).TokenSecret; string userId = response.ExtraData["user_id"]; string userName = response.ExtraData["screen_name"]; var extraData = new Dictionary<string, string>() { {"accesstoken", accessToken}, {"accesssecret", accessSecret} }; return new AuthenticationResult( isSuccessful: true, provider: ProviderName, providerUserId: userId, userName: userName, extraData: extraData); } }
The important part is the response to the ITokenSecretContainingMessage message. It seems like TokenSecret has the answer all the time, but it is only on the internal property. By choosing it, you get access to public property. I cannot say that I am a fan of this, but then I also do not understand why the DotNetOpenAuth Asp.Net team hid the property in the first place. There must be a good reason.
Then you register this client in AuthConfig:
OAuthWebSecurity.RegisterClient( new TwitterClient( consumerKey: "", consumerSecret: ""), "Twitter", null);
Now, in the ExternalLoginCallback method on the AccountController, accessSecret is available in the ExtraData dictionary.