Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
June 17, 2022 08:42 pm GMT

Handling Refresh Tokens in the OAuth 2.0 Authorization Code Flow with PKCE with Flask

We recently released OAuth 2.0 Authorization Code Flow with PKCE for use with most of our v2 endpoints. As a backend-focused Python developer, I struggled with the flow since much of the code I like to write focuses on automation tasks.

This tutorial will walk you through critical concepts related to refresh tokens using v2 of the Twitter API. If you dont already have access to the Twitter API, you can sign up for an account. You will also need to have OAuth 2.0 turned on in your Apps settings.

Tokens are only valid for two hours

I was used to working with OAuth 1.0a, where keys and tokens are available for use until they are revoked. This is not the case with OAuth 2.0 Authorization Code Flow with PKCE. Access Tokens generated using this flow are only valid for 2 hours.

Refresh tokens

Inside the token object generated from the consent flow is a refresh token. A refresh token allows an application to obtain a new access token without prompting the user to log in again.

To generate a refresh token, you must set a scope for offline access. If I was using the manage Tweets endpoint and I wanted to Tweet on behalf of a user every six months, Id use the following line of Python code:

scopes = ["tweet.read", "users.read", "tweet.write", "offline.access"]

How long are refresh tokens valid for?

Refresh tokens stay valid for six months, so you will want to refresh them at least that often or more regularly.

Generating tokens

To generate a token using OAuth 2.0 Authorization Code Flow with PKCE you can use a method similar to this example that allows you to look up an authenticated users bookmarks. Since the authenticated user will need to log in directly, and you will need to parse the response, I found creating a Flask application allowed for a more automated process.

The following code will generate a token and save the token into a dictionary.

You will be working with the following libraries:

  • requests for making HTTP requests

  • requests_oauthlib for working with OAuth 2.0

  • os for parsing environment variables and creating random strings

  • re, base64 and hashlib to create the code challenge and code verifier

  • flask for creating a web framework

If you dont already have flask, requests_oauthlib and requests installed you will need to install these libraries.

You will first need to import the following libraries:

import base64import hashlibimport osimport reimport requestsfrom requests_oauthlib import OAuth2Sessionfrom flask import Flask, request, redirect, session, url_for

First, you will need to set a variable for your app, which is the start of every Flask app. Additionally, you will need to set a secret key for your app to be a random string.

app = Flask(__name__)app.secret_key = os.urandom(50)

The two main credentials you will need to authenticate with OAuth 2.0 Authorization Code Flow with PKCE are Client ID and Client Secret. You can set these as environment variables to ensure security.

client_id = os.environ.get("CLIENT_ID")client_secret = os.environ.get("CLIENT_SECRET")

You will also need to set a variable for your redirect URL, this should be the same value as your callback URL in your Apps settings in the Developer Portal.

redirect_uri = os.environ.get("REDIRECT_URI")auth_url = "https://twitter.com/i/oauth2/authorize"token_url = "https://api.twitter.com/2/oauth2/token"

To define the permissions of your App, you will need to set the scopes of your application. Check out the authentication mapping guide to determine what scopes you will need based on the endpoints you are using. The scope offline.access allows you to generate refresh tokens. The scopes for this demo are tweet.read and users.read, which gives you access to read Tweets and obtain information about users.

scopes = ["tweet.read", "users.read", "offline.access"]

You will need to set a code verifier which is a secure random string. The code verifier is used to create the code challenge.

code_verifier = base64.urlsafe_b64encode(os.urandom(30)).decode("utf-8")code_verifier = re.sub("[^a-zA-Z0-9]+", "", code_verifier

With PKCE the code challenge is a base64 encoded string of the SHA256 hash of the code verifier.

code_challenge = hashlib.sha256(code_verifier.encode("utf-8")).digest()code_challenge = base64.urlsafe_b64encode(code_challenge).decode("utf-8")code_challenge = code_challenge.replace("=", "")

Now you can start with creating a login page that will be the first page you will visit when you run your application. @app.route("/") indicates its the first landing page. This page will be the page that an authenticated user logs into.

@app.route("/")def demo():    global twitter    twitter = OAuth2Session(client_id, redirect_uri=redirect_uri, scope=scopes)    authorization_url, state = twitter.authorization_url(        auth_url, code_challenge=code_challenge, code_challenge_method="S256"    )    session["oauth_state"] = state    return redirect(authorization_url)

After the user logs in, they will be directed to the callback. In the callback you will generate an object called token that contains a series of tokens that includes an access token and a refresh token.

@app.route("/oauth/callback", methods=["GET"])def callback():    code = request.args.get("code")    token = twitter.fetch_token(        token_url=token_url,        client_secret=client_secret,        code_verifier=code_verifier,        code=code,    )

Now that youve generated a token object, you can now make a request to the authenticated user lookup, to obtain a user ID that can be used to access many of our v2 Users endpoints such as manage Tweets or manage blocks.
The access_token variable inside of the token object is the bearer token youd use to connect to any of the endpoints that support OAuth 2.0 Authorization Code Flow with PKCE.

    user_me = requests.request(        "GET",        "https://api.twitter.com/2/users/me",        headers={"Authorization": "Bearer {}".format(token["access_token"])},    ).json()    print(user_me)    user_id = user_me["data"]["id"]

Now that youve created a token and saved into a dictionary you can now access the token dictionary and create a refreshed token through this process. This code should be added to your callback method. This refreshed_token will contain a new access token as well.

tokens = {"new_token": token}    t = tokens["new_token"]    refreshed_token = twitter.refresh_token(          client_id=client_id,          client_secret=client_secret,          token_url=token_url,          refresh_token=t["refresh_token"],        )    tokens.update({"new_token": refreshed_token})    return "You should now have a refreshed token"if __name__ == "__main__":    app.run()

Full code

Here is the full code that you can save as app.py

import base64import hashlibimport osimport reimport requestsfrom requests_oauthlib import OAuth2Sessionfrom flask import Flask, request, redirect, session, url_forapp = Flask(__name__)app.secret_key = os.urandom(50)client_id = os.environ.get("CLIENT_ID")client_secret = os.environ.get("CLIENT_SECRET")redirect_uri = os.environ.get("REDIRECT_URI")auth_url = "https://twitter.com/i/oauth2/authorize"token_url = "https://api.twitter.com/2/oauth2/token"scopes = ["tweet.read", "users.read", "offline.access"]code_verifier = base64.urlsafe_b64encode(os.urandom(30)).decode("utf-8")code_verifier = re.sub("[^a-zA-Z0-9]+", "", code_verifier)code_challenge = hashlib.sha256(code_verifier.encode("utf-8")).digest()code_challenge = base64.urlsafe_b64encode(code_challenge).decode("utf-8")code_challenge = code_challenge.replace("=", "")@app.route("/")def demo():    global twitter    twitter = OAuth2Session(client_id, redirect_uri=redirect_uri, scope=scopes)    authorization_url, state = twitter.authorization_url(        auth_url, code_challenge=code_challenge, code_challenge_method="S256"    )    session["oauth_state"] = state    return redirect(authorization_url)@app.route("/oauth/callback", methods=["GET"])def callback():    code = request.args.get("code")    token = twitter.fetch_token(        token_url=token_url,        client_secret=client_secret,        code_verifier=code_verifier,        code=code,    )    user_me = requests.request(        "GET",        "https://api.twitter.com/2/users/me",        headers={"Authorization": "Bearer {}".format(token["access_token"])},    ).json()    print(user_me)    user_id = user_me["data"]["id"]    tokens = {"new_token": token}    t = tokens["new_token"]    refreshed_token = twitter.refresh_token(          client_id=client_id,          client_secret=client_secret,          token_url=token_url,          refresh_token=t["refresh_token"],        )    tokens.update({"new_token": refreshed_token})    return "You should now have a refreshed token"if __name__ == "__main__":    app.run()

To run the file locally you can run the following line:

python app.py

Next steps

Hopefully, this can be a starting point for you to get started with generating refresh tokens. As a next step, if you are using Flask you may want to consider using a schedular to update your refresh tokens regularly in an automated fashion. Additionally you may want to consider saving your tokens to a database in a secure fashion. This code sample can also be extended to allow you to connect to any of the endpoints that support v2 and can be deployed to a server as part of a more complete application.

Be sure to let us know on the forums if you run into any troubles along the way, or Tweet us at @TwitterDev if this tutorial inspires you to create anything.


Original Link: https://dev.to/twitterdev/handling-refresh-tokens-in-the-oauth-20-authorization-code-flow-with-pkce-with-flask-481p

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To