Sample Application
Sample application using OAuth 2.0
Overview
This simple application, built with Python and Flask, illustrates how to use OAuth 2.0 and its authorization token workflow. Note that this is a demonstration app and does not follow standard token security practices.
Setting up the environment
First, we'll use python's venv
to create a lightweight virtual environment for our project:
python3 -m venv .venv
After python sets up the environment, activate it:
source .venv/bin/activate
Then install the flask
and requests
libraries:
pip install flask requests
Creating the Application
Create a file named app.py
:
vi app.py
Create a flask application.
from flask import Flask
app = Flask(__name__)
app.secret_key = "<SECRET>"
@app.get("/")
def index():
return "Hello!"
if __name__ == "__main__":
app.run()
Run the current application.
python3 app.py
Your application should now be running on port 5000.
* Serving Flask app 'app' (lazy loading)
* Environment: development
* Debug mode: on
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
Navigate to http://localhost:5000/
and confirm you get the "Hello!"
response.
Authorizing the Application using Carta's OAuth
Next, we'll confirm that the app can authenticate using Carta's OAuth process. Update app.py with the code below, and change the CLIENT_ID
and CLIENT_SECRET
variables to the client_id
and client_secret
strings from your playground app.
from hashlib import sha256
from os import urandom
from urllib.parse import urlencode
from flask import Flask, redirect, session, url_for
import requests
app = Flask(__name__)
app.secret_key = "<SECRET>"
CLIENT_ID = "<REPLACE_THIS_WITH_YOUR_CARTA_OAUTH_APP_CLIENT_ID>"
CLIENT_SECRET = "<REPLACE_THIS_WITH_YOUR_CARTA_OAUTH_APP_CLIENT_SECRET>"
BASE_AUTH_URL = "https://login.playground.carta.team"
BASE_API_URL = "https://api.playground.carta.team"
SCOPES_REQUESTS = ["read_issuer_info", "read_issuer_securities"]
@app.get("/")
def index():
return "Hello!"
@app.get("/carta/connect")
def connect():
state = sha256(urandom(1024)).hexdigest()
session["state"] = state
params = {
"response_type": "code",
"client_id": CLIENT_ID,
"scope": " ".join(SCOPES_REQUESTS),
"redirect_uri": url_for("callback", _external=True),
"state": state,
}
url = f"{BASE_AUTH_URL}/o/authorize?{urlencode(params)}"
return redirect(url)
@app.get("/carta/callback")
def callback():
return "Callback"
if __name__ == "__main__":
app.run()
We added two new endpoints to our application:
/carta/connect
- This endpoint builds the authorization url and redirects the user to Carta's authorization page./carta/callback
- After the user grants access, Carta redirects the user back to this endpoint.
Note
Make sure that the application runs and that you successfully get redirected to Carta authorization page when navigating to
http://localhost:5000/carta/connect
.
Generating an access token
Once the user has authorized the application, Carta will redirect the user's browser to the specified redirect_uri
with the authorization code (specified as code
) and the state
parameter.
from hashlib import sha256
from os import urandom
from urllib.parse import urlencode
from flask import Flask, redirect, session, url_for, request
import requests
from requests.auth import HTTPBasicAuth
app = Flask(__name__)
app.secret_key = "<SECRET>"
CLIENT_ID = "<REPLACE_THIS_WITH_YOUR_CARTA_OAUTH_APP_CLIENT_ID>"
CLIENT_SECRET = "<REPLACE_THIS_WITH_YOUR_CARTA_OAUTH_APP_CLIENT_SECRET>"
BASE_AUTH_URL = "https://login.playground.carta.team"
BASE_API_URL = "https://api.playground.carta.team"
SCOPES_REQUESTS = ["read_issuer_info", "read_issuer_securities"]
@app.get("/")
def index():
return "Hello!"
@app.get("/carta/connect")
def connect():
state = sha256(urandom(1024)).hexdigest()
session["state"] = state
params = {
"response_type": "code",
"client_id": CLIENT_ID,
"scope": " ".join(SCOPES_REQUESTS),
"redirect_uri": url_for("callback", _external=True),
"state": state,
}
url = f"{BASE_AUTH_URL}/o/authorize?{urlencode(params)}"
return redirect(url)
@app.get("/carta/callback")
def callback():
code = request.args.get("code")
state = request.args.get("state")
if code and state and state == session["state"]:
token_url = f"{BASE_AUTH_URL}/o/access_token/"
auth = HTTPBasicAuth(CLIENT_ID, CLIENT_SECRET)
data = {
"grant_type": "authorization_code",
"code": code,
"redirect_uri": url_for("callback", _external=True),
"client_id": CLIENT_ID,
}
resp = requests.post(token_url, data=data, auth=auth)
if resp.ok:
token_resp = resp.json()
session["access_token"] = token_resp["access_token"]
return redirect(url_for("index"))
return resp.json()
return f"Failed: {request.args.to_dict()}"
if __name__ == "__main__":
app.run()
We have updated the callback endpoint to perform the following steps:
- Verify that
state
parameter matches thestate
we saved in our session store when theconnect
request was initiated. - Exchange the
code
for anaccess_token
using Carta's/o/access_token
endpoint. - If the exchange is successful, save the
access_token
in the current session and redirect the user to the index page (http://localhost:5000
).
Do not save tokens within a user-agent
When implementing your actual integration, never store the access token or refresh token within a user-agent. Our simple and insecure example here is for demonstration purposes only.
Using the API
Finally, we will update the index endpoint to use the access_token
we generated in the previous step to connect with Carta's ListIssuers API:
from hashlib import sha256
from os import urandom
from urllib.parse import urlencode
from flask import Flask, redirect, session, url_for, request
import requests
from requests.auth import HTTPBasicAuth
app = Flask(__name__)
app.secret_key = "<SECRET>"
CLIENT_ID = "<REPLACE_THIS_WITH_YOUR_CARTA_OAUTH_APP_CLIENT_ID>"
CLIENT_SECRET = "<REPLACE_THIS_WITH_YOUR_CARTA_OAUTH_APP_CLIENT_SECRET>"
BASE_AUTH_URL = "https://login.playground.carta.team"
BASE_API_URL = "https://api.playground.carta.team"
SCOPES_REQUESTS = ["read_issuer_info", "read_issuer_securities"]
@app.get("/")
def index():
access_token = session.get("access_token")
if access_token:
url = f"{BASE_API_URL}/v1alpha1/issuers"
resp = requests.get(url, headers={"authorization": f"Bearer {access_token}"})
if resp.ok:
return resp.json()
del session["access_token"]
return redirect(url_for("connect"))
@app.get("/carta/connect")
def connect():
state = sha256(urandom(1024)).hexdigest()
session["state"] = state
params = {
"response_type": "code",
"client_id": CLIENT_ID,
"scope": " ".join(SCOPES_REQUESTS),
"redirect_uri": url_for("callback", _external=True),
"state": state,
}
url = f"{BASE_AUTH_URL}/o/authorize?{urlencode(params)}"
return redirect(url)
@app.get("/carta/callback")
def callback():
code = request.args.get("code")
state = request.args.get("state")
if code and state and state == session["state"]:
token_url = f"{BASE_AUTH_URL}/o/access_token/"
auth = HTTPBasicAuth(CLIENT_ID, CLIENT_SECRET)
data = {
"grant_type": "authorization_code",
"code": code,
"redirect_uri": url_for("callback", _external=True),
"client_id": CLIENT_ID,
}
resp = requests.post(token_url, data=data, auth=auth)
if resp.ok:
token_resp = resp.json()
session["access_token"] = token_resp["access_token"]
return redirect(url_for("index"))
return resp.json()
return f"Failed: {request.args.to_dict()}"
if __name__ == "__main__":
app.run()
In our final version, we have modified the index
endpoint to first check if an access_token
is available in the session storage. If so, the app submits a request to Carta's API using that token. If the request fails or no access_token
is available, the user is redirected to the connect
endpoint to start Carta's authorization process.
Updated 9 days ago