lenticular_cloud2/lenticular_cloud/views/oauth2.py

105 lines
3.2 KiB
Python
Raw Normal View History

from authlib.integrations.flask_client import OAuth
from authlib.integrations.base_client.errors import MismatchingStateError
2022-06-17 11:38:49 +00:00
from flask import Flask, Blueprint, Response, session, request, redirect, url_for
from flask_login import login_user, logout_user, current_user
from flask.typing import ResponseReturnValue
from flask_login import LoginManager
from typing import Optional
2022-07-15 08:53:06 +00:00
from werkzeug.wrappers.response import Response as WerkzeugResponse
2022-06-17 11:38:49 +00:00
import logging
from ..model import User, SecurityUser
2022-06-17 11:38:49 +00:00
logger = logging.getLogger(__name__)
def fetch_token(name: str) -> Optional[dict]:
2022-04-08 19:29:23 +00:00
token = session.get('token', None)
if isinstance(token, dict):
return token
return None
oauth2 = OAuth(fetch_token=fetch_token)
oauth2_views = Blueprint('oauth2', __name__, url_prefix='/oauth')
login_manager = LoginManager()
def redirect_login() -> ResponseReturnValue:
logout_user()
session['next_url'] = request.path
redirect_uri = url_for('oauth2.authorized', _external=True)
2022-06-17 11:38:49 +00:00
response = oauth2.custom.authorize_redirect(redirect_uri)
2022-07-15 08:53:06 +00:00
if not isinstance(response, WerkzeugResponse):
2022-06-18 17:35:05 +00:00
raise RuntimeError("invalid redirect")
2022-06-17 11:38:49 +00:00
return response
@oauth2_views.route('/authorized')
def authorized() -> ResponseReturnValue:
try:
token = oauth2.custom.authorize_access_token()
except MismatchingStateError:
2022-06-17 11:38:49 +00:00
logger.warning("MismatchingStateError redirect user")
return redirect(url_for('oauth2.login'))
if token is None:
return 'bad request', 400
session['token'] = token
userinfo = oauth2.custom.get('/userinfo').json()
2022-07-15 08:53:06 +00:00
user = User.query.get(str(userinfo["sub"])) # type: Optional[User]
2022-06-17 11:38:49 +00:00
if user is None:
return "user not found", 404
2022-06-18 11:05:18 +00:00
logger.info(f"user `{user.username}` successfully logged in")
2022-06-17 11:38:49 +00:00
login_user(SecurityUser(user.username))
next_url = request.args.get('next_url')
if next_url is None:
next_url = '/'
return redirect(next_url)
2022-06-17 11:38:49 +00:00
@oauth2_views.route('login')
def login() -> ResponseReturnValue:
redirect_uri = url_for('.authorized', _external=True)
2022-06-17 11:38:49 +00:00
response = oauth2.custom.authorize_redirect(redirect_uri)
2022-07-15 08:53:06 +00:00
if not isinstance(response, WerkzeugResponse):
raise RuntimeError("invalid redirect")
2022-06-17 11:38:49 +00:00
return response
@login_manager.user_loader
def user_loader(username) -> Optional[User]:
2022-07-15 08:53:06 +00:00
user = User.query.filter_by(username=username).first() # type: Optional[User]
if isinstance(user, User):
return user
else:
return None
@login_manager.request_loader
def request_loader(_request):
pass
@login_manager.unauthorized_handler
2022-06-17 11:38:49 +00:00
def unauthorized() -> Optional[User]:
pass
2022-06-17 11:38:49 +00:00
def init_login_manager(app: Flask) -> None:
base_url = app.config['HYDRA_PUBLIC_URL']
2022-06-17 11:38:49 +00:00
if not isinstance(base_url, str):
raise RuntimeError("HYDRA_PUBLIC_URL not set")
oauth2.register(
name="custom",
client_id=app.config['OAUTH_ID'],
client_secret=app.config['OAUTH_SECRET'],
access_token_url=f"{base_url}/oauth2/token",
authorize_url=f"{base_url}/oauth2/auth",
api_base_url=base_url,
client_kwargs={'scope': ' '.join(['openid', 'profile', 'manage'])}
)
oauth2.init_app(app)
login_manager.init_app(app)