Example
Example¶
Lets see a little example of how to use the middleware, and Integrate it with the application.
We could Create Our Project based on Starlette, instead of FastAPI.
Create Crud Instance¶
Using SQLAlchemy
, we can create a Crud instance, and we can use it to create a
Database and show multiple crud functionalities.
import typing
from sqlalchemy import JSON, Column, MetaData, String, Table, create_engine
from sqlalchemy.ext.automap import automap_base
from sqlalchemy.orm import create_session
from starlette.config import Config
config = Config(".env.sample")
class DB:
def __init__(self) -> None:
self._engine = create_engine(config("DATABASE_URL"))
_metatable = Table(
"users",
MetaData(bind=self._engine),
*[
Column("id", String, primary_key=True, nullable=False),
Column("token", JSON),
],
)
_metatable.create(checkfirst=True)
Base = automap_base(metadata=_metatable.metadata)
Base.prepare()
self._session = create_session(bind=self._engine)
self._User = Base.classes.users
- Here we are using the
create_engine
function to create a database engine, and we are using theconfig
function to get the database url from the.env.sample
file. - We are using the
Table
function to create a table, and we are using theColumn
function to create the columns of the table. - We are using the
create_session
function to create a session, and we are using theBase
function to create the Base class. - We are using the
prepare
function to prepare the Base class.
def put(self, user_id: str, token: typing.Dict[str, typing.Any]) -> None:
self._session.merge(self._User(id=user_id, token=dict(token)))
self._session.flush()
- Here we are using the
merge
function to merge the user, and we are using theflush
function to flush the session. - We are using the
id
andtoken
attributes to create the user. - We are using the
put
function to put the user in the database.
def get(self, user_id: str) -> typing.Optional[typing.Dict[str, typing.Any]]:
result = (
self._session.query(self._User).filter(self._User.id == user_id).first()
)
if result is not None:
return result.token
- Here we are using the
query
function to query the database, and we are using thefilter
function to filter the query, and we are using thefirst
function to get the first result of the query. - We are using the
id
attribute to filter the query, and we are using thetoken
attribute to get the token of the user. - We are using the
get
function to get the token of the user.
def delete(self, user_id: str) -> None:
self._session.query(self._User).filter(self._User.id == user_id).delete()
self._session.flush()
- Here we are using the
query
function to query the database, and we are using thefilter
function to filter the query, and we are using thedelete
function to delete the user. - We are using the
id
attribute to filter the query, and we are using theflush
function to flush the session. - We are using the
delete
function to delete the user.
Create Middleware & App¶
Now After initializing the database, we can create the middleware and the app.
Let's Call our Imports and Initial the Environment Variables using the Config
function in starlette.config
.
from starlette.applications import Starlette
from starlette.config import Config
from starlette.datastructures import Secret
from starlette.middleware.sessions import SessionMiddleware
from starlette.responses import JSONResponse
from authx import MiddlewareOauth2
config = Config(".env.sample")
- Here we are using the
Config
function to get the environment variables from the.env.sample
file.
config.SECRET_KEY = config("SECRET_KEY", cast=Secret)
config.SERVER_METADATA_URL = config("SERVER_METADATA_URL", cast=str)
config.CLIENT_ID = config("CLIENT_ID", cast=str)
config.CLIENT_SECRET = config("CLIENT_SECRET", cast=Secret)
- Here we are using the
SECRET_KEY
andSERVER_METADATA_URL
environment variables to set theSECRET_KEY
andSERVER_METADATA_URL
attributes of theconfig
function. - We are using the
CLIENT_ID
andCLIENT_SECRET
environment variables to set theCLIENT_ID
andCLIENT_SECRET
attributes of theconfig
function.
app = Starlette()
db = DB()
class AuthenticateMiddleware(MiddlewareOauth2):
PUBLIC_PATHS = {"/public"}
app.add_middleware(
AuthenticateMiddleware,
db=db,
server_metadata_url=config.SERVER_METADATA_URL,
client_id=config.CLIENT_ID,
client_secret=config.CLIENT_SECRET,
force_https_redirect=False,
)
app.add_middleware(SessionMiddleware, secret_key=config.SECRET_KEY)
- We Instance the application using the
Starlette
function, and we are using thedb
andserver_metadata_url
attributes of theconfig
function to initialize thedb
andserver_metadata_url
attributes of theAuthenticateMiddleware
class. - We are using the
client_id
andclient_secret
attributes of theconfig
function to initialize theclient_id
andclient_secret
attributes of theAuthenticateMiddleware
class. - We are using the
force_https_redirect
attribute of theconfig
function to initialize theforce_https_redirect
attribute of theAuthenticateMiddleware
class. - We are using the
add_middleware
function to add theSessionMiddleware
class to the application.
@app.route("/other")
async def homepage(request):
user = request.session.get("user")
return JSONResponse(user)
- Here we are using the
route
function to create a route, and we are using theget
function to get the user from the session. - We are using the
JSONResponse
function to return the user as a JSON response.
@app.route("/public")
async def homepage(request):
user = request.session.get("user")
payload = {"message": "User not authenticated"} if user is None else user
return JSONResponse(payload)
- Here we are using the
route
function to create a route, and we are using theget
function to get the user from the session. - We are using the
payload
variable to create the payload of the response. - We are using the
JSONResponse
function to return the payload as a JSON response.
To ensure that we can run the app we will use uvicorn
and httpx
to run the
app.
if __name__ == "__main__":
import logging
import sys
import uvicorn
logger = logging.getLogger("httpx")
logger.setLevel(logging.DEBUG)
logger.addHandler(logging.StreamHandler(sys.stdout))
uvicorn.run(app, host="localhost", port=5001)
- Here we are using the
if
statement to check if the__name__
is equal to__main__
. - We import some libraries like
logging
andsys
, and at the end we are using theuvicorn
function to run the app. - We are using the
getLogger
function to get the logger of thehttpx
library, and we are using thesetLevel
function to set the level of the logger toDEBUG
. - We are using the
addHandler
function to add theStreamHandler
to the logger. - We are using the
run
function to run the app.
Result¶
At the end of the process, we can run the app using the following command:
python -m app
We can see that the app is running on the following URL:
- When you visit
http://localhost:5001/public
, you will see that you are not authenticated. - When you visit
http://localhost:5001/other
, you will be redirected to your tenant, to authenticate. - Once authenticated, you will be redirected back to
http://localhost:5001/other
, and your email will appear.