This project has moved and is read-only. For the latest updates, please go here.

Question about StoreNewRequestToken method

Oct 26, 2009 at 12:09 PM

Hi folks,

I've got my Linq to Twitter code working fine. I'm trying to refactor some code smell and I'm stuck with the StoreNewRequestToken method. I've refactored my database tables to the following :-

Database Diagram

So each user can have zero to many OAuth Access Tokens + Access Secrets.

Now, when I want to save this, I would normally do this in the StoreNewRequestToken method

public void StoreNewRequestToken(UnauthorizedTokenRequest request, ITokenSecretContainingMessage response)

    // Save this token request to the repository.
    _oAuthTokenRepository.SaveOAuthToken(new OAuthToken
                                                 // UserId = ???? // This is the busted bit.
                                                 TokenKey = response.Token,
                                                 TokenSecret = response.TokenSecret

    // Now save it, int the in-memory dictionary.
    _tokensAndSecrets[response.Token] = response.TokenSecret;

Notice how I need the UserId to save an OAuthToken? I was wondering how I could pass this in? I have no idea at all :( I'm toying with the idea that this method only stores the info into the memory (aka. _tokensAndSecrests dictionary) and i handle saving this to the repository elsewhere (basically, in the Controller code when i've received the Access Token after I've returned back from Twitter)

But I'm not a fan of that idea :(

Anyone have any suggestions? Do I need to make my own UnauthorizedTokenRequest class or something? Or could we get this method signature updated ot changed or something? In my scenario, I might want an integer, but others might be wanting another type or even more than one type, when they persist this data.

Oct 27, 2009 at 2:18 PM

First some background:

You have to store two types of tokens:  unauthorized request tokens and access tokens.  Unauthorized request tokens must never be associated with a user account.  It usually opens up security attacks to associate request tokens with user accounts.  Access tokens should be associated with user accounts, obviously, but only after you have received them (don't earmark the request token for a particular user account for once the access token arrives).

With that in mind, StoreNewRequestToken is storing the first type of token, so you shouldn't be trying to do what you're doing! (ha ha, I try to make the API "difficult" to misuse).

Now, you're going to get to implementing the ExpireRequestTokenAndStoreNewAccessToken method and ask the same question, so let me get to that right now. Access tokens as I said should be associated with users.  You have a couple of options here.  

The method I mildly prefer is just to store the access token itself in the table from the ExpireRequestTokenAndStoreNewAccessToken method, not yet associated with a user account.  Now this method gets called as a result of your call to (Web/Desktop)Consumer.ProcessUserAuthorization(...), which itself returns the access token. When this method returns, you're back on your web page, and the web page knows who the user is, so it can then make the association in the database between user and access token.

Your other option is that since you actually also have the user context available from your ExpireRequestTokenAndStoreNewAccessToken implementation you can actually make the user association there.  Just use HttpContext.Current.User to find out who's logged into your web site and associate the access token. 

Hope this helps.

Oct 30, 2009 at 2:31 PM
Edited Oct 30, 2009 at 2:59 PM

Kewl. that does make some sense :) I didn't know about the security conserns, so taking that into account, i've quickly remodelled the tables...

Twitter Database Scheme

So the OAuthToken table now has no relationship to any users. This will get populated with a single key/secret when we first BeginAuthorization() . Once the user accepts on Twitter, we are redirected back and the AccessToken is now returned via the CompleteAuthorize() method. We know which user this is ... and we now have a token, so we can save that to the User table.

Sounds about right?

EDIT: (sorry, the rest forgot to add/save)

One more thing I forgot to mention. The OAuthToken table will originally hold the unauthorised key/value data ... and after we have received an access token the CompleteAuthorized() method, then we overwrite that key/value with the authorised key/value data (or delete the previous row and insert a new row, whatever floats your boat, etc).

Now .. if that IS the case ... is the new key which we now store, is that the same data value which goes into the user table? if so, we now have a direct relationship with user/oauth table, which defeats the security considerations noted earlier? OR .. is it that this data is an authorised key/secret data .. so that's fine. it's only when we have the unauthorised key/secret, this data should NOT be associated to any user?

Oct 31, 2009 at 11:40 PM

I think you've got it about right.  

There's nothing wrong with having a relationship between an [authorized] access token and a user account.  That in fact is necessary to offer anything meaningful to your users.  The security risk is that until you get the access token back, you don't associate the request token with the user.  

But you also need to be able to distinguish between a request token and an access token (in practice not knowing may or may not bite you, so I like to play it safe).  So I'd add a bit column to your tokens table like "IsAccessToken" or something like that.  But you may not actually need to do this.  Oh, and it may be useful to know how old a token is, so a datetime column indicating when the token was obtained would allow you to weed out very old request tokens that never matured into access tokens (abandoned sessions).

Nov 1, 2009 at 7:37 AM

Ahh - cheers :) those are some great ideas!

I've also modified the schema to have a Nullable OAuthTokenId in the User table, instead of the TwitterAccessToken (string). Same thing, just different implimentation.

Cheers Andrew :) very kewl!