Understanding OAuth

I should definitely finish the other parts of the NN posts, but here is a brief detour into OAuth (Open Authorization). Note that I am still a novice and so there might be explanations that are missing information or lacking depth. But this is a way for me to synthesize what I know, so please message me if there are any mistakes!

So what is the point of OAuth? As I’ve seen it, it’s a way for web services or apps to exchange information without compromising the people using apps. For example, one might use Spotify for the first time and notice that it lets you do something like connect your Spotify account with the Spotify account of your Facebook friends. This usually means Spotify also gets access to certain information from your Facebook account that they consider helpful (e.g your friend list). Now, how does Facebook give Spotify certain data without Spotify having access to your username and password? This is where OAuth comes in. OAuth is an authorization protocol – or in other words, a set of rules – that allows a third-party website or application to access a user’s data without the user needing to share login credentials. (from Technopedia)

Note that there is OAuth1 and OAuth2. OAuth2 is a bit simplified and allows flexibility (for reasons I’m not too sure of yet). This is what I learned so for now on we will focus on OAuth2. This requires three ‘players’ involved

  • The Client: The (third party) application that wants access to the user’s data. It needs to get permission from the user first. (e.g. Spotify wants access to FB data)
  • The User: The person that wants to utilize an app and give it permission to access their data from another app or web service (e.g. The person that wants to connect Spotify and Facebook)
  • The Server: The website/app that has the user’s data (e.g. Facebook)

Note that for this to even be possible, the Client would have to approach the Server and essentially tell it, “Hey, people will want to use me and connect me to you later. Can you give me personal identification info so that you know it’s me when I come to you in the future on behalf of the people using me?”. Then the client gets something called an Client Key and Client Secret. It will use this in the authencation process. How does this work? We have the following steps

  1. User tells Client (Spotify) to connect to Server (FB)
    • This usually looks like clicking on some sort of button that says ‘import contacts’ or something Spotify asking if I want to give it my FB data for ease
  2. Client (Spotify) directs User to Server (FB) Spotify directs me to FB so I can potentially give access
    • Note that the Client also sends a URL that the server sends us back to after we’re done talking to the Server. This is called a redirect uri.
  3. User grants Client (Spotify) access
    • This might look like a box popping up or that says “Allow xyz app access to your friends list” or “Please log into your account to allow access” and you can click the allow button or log in. Me maybe giving Facebook confirmation that it can give Spotify my FB friend list
  4. The Server (Facebook) sends the User back to the Client (Spotify) along with a authorization code
  5. Client (Spotify) exchanges authorization code and identification info for an access token
    • This means the client makes another request to the server using the recently received authorization code along with something called a secret key. This is so that the server really knows that the Client is the real deal. The Client is then given an access token.
  6. Client (Spotify) fetches data from the Server (Facebook) using the access token
  7. ???
  8. Profit

I hope that helps! Jk, what good is this explanation without a bit of code?

The following is something I did to get data from EventBrites API. Some of the data requires authencation. I applied as a client and received a client key and client secret. You can find the entire project here. You definitely don’t want the client key and secret available, so you typically import it from another file. You’ll also need to search through the app’s documents to get the base form of their authorization url (the link the Client sends the User to so they can tell the Server to give the Client access) and their token url (what the Client visits behind the scenes in order to the the access token)

Well, the first thing to do is check and see if the result from the API has already been saved, i.e have we cached the data?

def check_if_cached(fname):
    try:
        with open(fname, 'r') as cache_file:
            cache_json = cache_file.read()
            CACHE_DICTION = json.loads(cache_json)
    except:
        CACHE_DICTION = {}
    return CACHE_DICTION

You might also have the token saved but haven’t cached the data yet. So we will cache the token using the following

def get_saved_token():
    with open('token.json', 'r') as f:
        token_json = f.read()
        token_dict = json.loads(token_json)
        return token_dict
def save_token(token_dict):
    with open('token.json', 'w') as f:
        token_json = json.dumps(token_dict)
        f.write(token_json)

There might be times that you already have the data cached, but you want to force a download. Or, you may need to get the data for the first time. This necessitates the following

# import webbrowser
# import json
# import requests
# import requests_oauthlib
def get_eventbrite_cache(search_params, CACHE_FNAME, force_download=False):
    CACHE_DICTION = check_if_cached(CACHE_FNAME)
    # if we need to get an oauth2 session started
    if CACHE_DICTION == {} or force_download:
        # see if we have the token
        try:
            token = get_saved_token()
        except FileNotFoundError:
            token = None
        if token:
            print('Token already saved, just retrieved it')
            # start the OAuth2 session using the saved token
            oauth2inst = requests_oauthlib.OAuth2Session(CLIENT_ID, token=token)
        else:
            print('Getting token the long way')
            # Create an instance of an OAuth2Session
            oauth2inst = requests_oauthlib.OAuth2Session(CLIENT_ID, redirect_uri=REDIRECT_URI) 
            # get the authorization url to send the user to
            authorization_url, state = oauth2inst.authorization_url(AUTHORIZATION_URL)
            # Opening auth URL for you to sign in to the EventBrite service
            webbrowser.open(authorization_url) 
            authorization_response = input('Authenticate and then enter the full callback URL: ').strip() # Need to get the full URL in order to parse the response
            # The OAuth2Session instance has a method that extracts what we need from the url, and helps do some other back and forth with EB
            token = oauth2inst.fetch_token(TOKEN_URL, authorization_response=authorization_response, client_secret=CLIENT_SECRET)
            save_token(token)
        
        print('Token saved. Getting search results')
        r = oauth2inst.get('https://www.eventbriteapi.com/v3/events/search/', params=search_params)
        # the result is now a dictionary
        response_diction = json.loads(r.text)
        with open(CACHE_FNAME, 'w') as cache_file:
            print('caching result as:', CACHE_FNAME)
            # this required me to look at what the json result looks like. I save the each event with the ID as the key, and the event information as the value
            for event in response_diction['events']:
                CACHE_DICTION[event['id']] = event
            cache_json = json.dumps(CACHE_DICTION, indent=2)
            cache_file.write(cache_json)
    else:
        print("{} already saved as cache, will return it".format(CACHE_FNAME))
    
    return CACHE_DICTION

I hope this code isn’t too ugly :) I’m definitely still working on making my code efficient and nice looking.