How to detect and recover from bad or missing access tokens?



We’re starting to look at integrating more Twitter functionality into our app, which will require users to be logged in, rather than just relying on guest authentication, and I’m trying to understand what tokens TwitterKit is actually using, and how to detect when things change.

I have an iOS simulator with a system Twitter account. Fabric is configured with the keys for our Twitter app, which is currently read-only. I did what I thought was a simple test:

  1. Launch the app, which then loads a timeline in a TWTRTimelineViewController. This works, and presumably acquires a guest token behind the scenes, but the TWTRSessionStore doesn’t show any sign of an existing session once it’s done.
  2. Show a TWTRComposer, which appears to acquire a token for my Twitter account from the system. This isn’t tied to our Twitter app, so posting the tweet works, event though our app is read-only.
  3. But it also seems to acquire a token from our app, which shows up in my account settings on (And this happens when I display the composer, even if I don’t actually attempt to post the tweet.)
  4. If I then revoke the token for our app, the timeline no longer loads.
  • The framework logs the error to the console (a 401, with a clear “expired or invalid” message), but does not appear to make any effort to acquire a new token.
  • If I pass the session and the error to -[TWTRSessionStore isExpiredSession:error:], it returns NO, which is clearly wrong.
  1. If I use the composer to post another tweet, that works just fine, since it’s not using our Twitter app at all. There is no apparent attempt to (re)acquire access to our Twitter app.

What should our app be doing to detect this situation and recover from it?


And, for the sake of completeness, if I block access to the system Twitter accounts, and log in through the web UI in step 2, revoking that token has the same effect in step 4 (it fails, but doesn’t think my session has expired).

Remarkably, step 5 still works! The tweet’s source says iOS, which sort of explains why it didn’t care about the settings for our Twitter app… but if I’ve disallowed access to my system Twitter accounts, how does the app manage this?


… and, sure enough, if I remove the system Twitter account, then I get an alert telling me I have no Twitter accounts, and the sheet goes away. (Despite the fact that TwitterKit thinks I’m logged in.)

So, is TWTRComposer just a thin candy shell around the iOS SLComposeViewController? Does logging the user in through TwitterKit actually have any bearing whatsoever on whether that will work properly or not?


@sfko You are correct in assuming that TWTRComposer is just a thin wrapper around SLComposeViewController. This composer uses the apple tokens and the system accounts behind the scenes. The TWTRComposerViewController is not a wrapper around the system frameworks and uses your app’s tokens and the sessions stored in the TWTRSessionStore. However, this composer is not fully featured and currently only supports attaching text and does not support attaching media.

When you load the timelines in the TWTRTimelineViewController you need to provide a TWTRTimelineDataSource which contains a TWTRAPIClient. The manner in which you create this API client determines which level of auth is used when loading Tweets for your timeline. If you provide a user id which maps to a session in the session store then that user session will be used for all network requests. If you don’t provide a user id then guest authentication will be used; this is what you are seeing in step 1.

For step 4: you really shouldn’t need to revoke guest tokens as they are short lived and are not tied to a user. We do not have a good way of handling a revoked guest token as it is not really a common use case, the token will eventually expire and the system will request a new one, this usually takes a few hours. The method -[TWTRSessionStore isExpiredSession:error:] is not used to check if the actual session is expired on its own, we have no way of checking this, but it is used in conjunction with an error returned from the API to check if the error indicates that the token is expired. The logic is essentially, are you a guest session and does the error code indicate expired. This is how we internally automatically manage the guest tokens for you - when a error comes back from the API we call this method to figure out how to proceed. So when you call this method it returns NO which is correct because the error does not contain an expired error code.

Hope that clears things up a bit.


For step 4: you really shouldn’t need to revoke guest tokens as they are short lived and are not tied to a user.

Is that what I’m doing? Is the token that I’m seeing appear under my account settings in step 3 a guest token? (Is there any way to tell that from the UI?) It does not appear to be a guest token inside the app: it reports my user ID, which the documentation leads me to believe is not the case with guest tokens.

it is used in conjunction with an error returned from the API to check if the error indicates that the token is expired

Right, and like I said: I’m passing it the error that comes from the timeline’s failed attempt to load content, that outright says it’s because of an invalid or expired token, and the method is returning NO. Now, if it only works on guest sessions, and this is not a guest session that might explain the discrepancy. But then, how can you ever tell whether your non-guest sessions have expired, or been revoked?

(Also: the documentation doesn’t specify that those methods only apply to guest sessions.)