Getting a callback from Twitter

I’m trying to set up a 3-legged Oauth with Twitter so users can store credentials in my app. The issue I’m having is with the callback URL. Anvil uses the # to specify parameters, but Twitter returns a URL with query parameters.

I’m trying to get two query parameters from the URL Twitter is passing back. The user is directed to Twitter to login and give permissions, then sent back to my app with some oauth credentials in the URL. I’m using a Startup form to do the URL checks before routing the user to the appropriate form.

I’ve tried a few different things and seem to be hitting a wall:

Method 1: Use root domain as callback URL

Specify the root URL as the callback and capture query parameters with get_url_hash. Unfortunately, the get_url_hash method only grabs things after the #, which standard URL query parameters don’t have. So this doesn’t work.

Method 2: Manually add in Anvil style hash

I thought I would try to hack my way into this by adding an Anvil style hash to my callback. Something like:

https://anvilurl.anvil.app/#?source=twittercallback

Unfortunately, Twitter grabs the hash and throws it to the end (which is how hashes work in URLs right?) returning this:

https://anvilurl.anvil.app/?oauth_token=1234556&oauth_verifier=1234566#?source=twittercallback

So this doesn’t work.

Method 3: Add hash to callback

Really same as method 1, but I added a hash at the end of the callback to see what would happen.

https://anvilurl.anvil.app/#

And again it just moves the hash to the end.

API, you are my only hope…or maybe JS?

So what I’m thinking is that my callback will have to be an API call, as those support query strings. But I wouldn’t be able to redirect a user to a form via an API route right?

The other way is to open a popup and have the user copy/paste an auth code from Twitter. But this is really not ideal.

2 Likes

What I’d suggest is using an API endpoint, which can pick up the query parameters and then HTTP redirect you to a URL with a hash on. You can either capture the OAuth token and update the session in the HTTP endpoint (if you specify cross_site_session=True, that HTTP endpoint will run in the same session as the rest of your app), or redirect to https://your.anvil.app/#?params=etc where you can pick them out with get_url_hash().

3 Likes

Thanks so much! I think I needed more coffee today.

And nice tip on keeping it the same session. Will be good for the logs.

For completeness and to help others here is the API function I made to handle the Twitter callback. It’s a bit verbose but it does what it’s supposed to.

It grabs the params from the callback, grabs the data to generate the user access_token and access_token_secret, generates it with the Tweepy package (which is already conveniently available), and stores it in the data table.

I then redirect the user using the Anvil URL hash. I use the oauth_token in the client side to match up the user row to finish up the registration and fill in the user’s details.

@anvil.server.http_endpoint("/oauth_callback")
def get_callback(**params):
  # Get the params I want from the query string.
  oauth_token = params['oauth_token']
  oauth_verifier = params['oauth_verifier']
  
  # Get my secrets....precious...
  consumer_key = anvil.secrets.get_secret('consumer_key')
  consumer_secret = anvil.secrets.get_secret('consumer_secret')
  
  # Try to get access tokens
  auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
  auth.request_token = {'oauth_token': oauth_token, 'oauth_token_secret': oauth_verifier}
  auth.get_access_token(oauth_verifier)
  
  # Store credentials in the data table
  app_tables.users.add_row(access_token=auth.access_token, 
                           access_token_secret=auth.access_token_secret, 
                           oauth_token=oauth_token, 
                           oauth_verifier=oauth_verifier)
  
  # Make the redirect url. Normally this would be static but I'm testing with a dev branch.
  app_url = anvil.server.get_app_origin()
  redirect_url = app_url + f"#?oauth_token={oauth_token}&oauth_verifier={oauth_verifier}"
  
  # Build the HTTP response
  response = anvil.server.HttpResponse(302, "Redirecting back to feedforward registration.")
  response.headers['Location'] = redirect_url
  return response
4 Likes