auth.py - Validation and Authentication
Provide a login page and validation endpoint to allow a student to login to the Runestone book server. This assumes that a student has registered using the old web2py registration system. So we provide validation of the user name and password.
See: FastAPI Login
Imports
These are listed in the order prescribed by PEP 8.
Standard library
Third-party imports
from fastapi import APIRouter, Depends, Request, Response # noqa F401
from fastapi_login.exceptions import InvalidCredentialsException
from fastapi.security import OAuth2PasswordRequestForm
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.templating import Jinja2Templates
from pydal.validators import CRYPT
Local application imports
Routing
See Routing for an explanation of this approach.
login
ideally we would put back the response_class parameter but its just a hint to the doc system and right now causing the docs to crash. Added to an issue for FastAPI on github. ):
This is called as the result of a login form being submitted.
If authentication is successful an access token is created and stored
in a session cookie. This session cookie is used for all protected routes.
The auth_manager is provided by session.py - Session Management which also explains how
to setup a protected route.
um = UserManagerWeb2Py()
raise InvalidCredentialsException
The password in the web2py database is formatted as follows:
alg$salt$hash
We need to grab the salt and provide that to the CRYPT function
which we import from pydal for now. Once we are completely off of
web2py then this will change. The web2py_private_key is an environment
variable that comes from the private/auth.key file.
salt = user.password.split("$")[1]
crypt = CRYPT(key=settings.web2py_private_key, salt=salt)
crypted_password = str(crypt(password)[0])
if crypted_password != user.password:
raise InvalidCredentialsException
access_token = auth_manager.create_access_token(
data={"sub": user.username}, expires=timedelta(hours=12)
)
redirect_to = f"/books/published/{user.course_name}/index.html"
rslogger.debug(f"Sending user to {redirect_to}")
response = RedirectResponse(redirect_to)
Important We need to set the cookie here for the redirect in order for the next page to validate. This will also set the cookie in the browser for future pages.
To log out, simply delete the cookie containing auth information.
Send the user to the login page after the logout.
todo: Write a second version of validate that returns the token as json this can be used by the docs/testing system.