better separation between api and frontend login

This commit is contained in:
2019-04-02 10:47:53 +02:00
parent ed57dc2720
commit 024f063bea
7 changed files with 80 additions and 14 deletions

View File

@@ -5,12 +5,17 @@ Backend base module
from flask import Flask
from flask_httpauth import HTTPTokenAuth, HTTPBasicAuth, MultiAuth
from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config.from_object('backend.config.Config')
db = SQLAlchemy(app)
login_manager = LoginManager()
login_manager.init_app(app)
jwt_auth = HTTPTokenAuth()
basic_auth = HTTPBasicAuth()
multi_auth = MultiAuth(basic_auth, jwt_auth)

View File

@@ -2,7 +2,10 @@
"""
This module provides functions related to authentication through the API.
For example: listing of available auth providers or registration of users.
Login through API does not start a new session, but instead returns JWT.
"""
import flask
from datetime import datetime, timedelta
import jwt
from flask import request, jsonify, current_app, url_for
@@ -12,9 +15,9 @@ from random import randint
from flask_login import logout_user, login_user
from werkzeug.routing import BuildError
from backend import db
from backend import db, app
from backend.api import auth_api_bp
from backend.auth import AUTH_PROVIDERS
from backend.auth import AUTH_PROVIDERS, oidc_auth
from backend.models.user_model import User
@@ -62,11 +65,39 @@ def login():
return jsonify({'message': 'Invalid credentials', 'authenticated': False}), 401
token = create_jwt(user)
#login_user(user)
return jsonify({'token': token.decode('UTF-8')})
@auth_api_bp.route('/logout', methods=('GET', ))
def logout():
return jsonify({'message': 'Not yet implemented!', 'authenticated': False}), 401
#logout_user()
def create_or_retrieve_user_from_userinfo(userinfo):
try:
email = userinfo["email"]
except KeyError:
return None
user = User.get_by_identifier(email)
if user is not None:
app.logger.info("user found")
return user
user = User(email=email, first_name=userinfo.get("given_name", ""),
last_name=userinfo.get("family_name", ""))
app.logger.info("creating new user")
db.session.add(user)
db.session.commit()
return user
@auth_api_bp.route('/oidc', methods=['GET'])
@oidc_auth.oidc_auth()
def oidc():
user = create_or_retrieve_user_from_userinfo(flask.session['userinfo'])
if user is None:
return "Could not authenticate: could not find or create user.", 401
if current_app.config.get("AUTH_RETURN_EXTERNAL_JWT", False):
token = jwt.encode(flask.session['id_token'], current_app.config['SECRET_KEY'])
else:
token = create_jwt(user)
return token

View File

@@ -5,13 +5,16 @@ Base module for auth aspects.
Also this module contains mainly code for login through HTML pages served by the backend.
If frontend pages are build by frontend code (JS, etc.) authentication should consider using api functions.
(For more info, see api.auth_api.py.)
This code uses login_user and logout user (to start and end sessions) ... API code returns JWTs.
"""
from flask import Blueprint
from flask import Blueprint, jsonify
from flask_login import logout_user, LoginManager
from werkzeug.routing import BuildError
auth_bp = Blueprint('auth', __name__, url_prefix='/auth', template_folder='templates')
from backend.auth.config import AUTH_PROVIDERS, DEFAULT_PROVIDER
from backend.auth.config import AUTH_PROVIDERS, DEFAULT_FRONTEND_PROVIDER
from backend.auth.oidc_config import OIDC_PROVIDERS
from backend.auth.oidc import oidc_auth
@@ -26,7 +29,7 @@ def auth_decorator(): # custom decorator
@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
try:
prov = AUTH_PROVIDERS[DEFAULT_PROVIDER]
prov = AUTH_PROVIDERS[DEFAULT_FRONTEND_PROVIDER]
except KeyError:
return "No known default provider specified!"
url = prov["url"]
@@ -41,3 +44,8 @@ def login():
@auth_bp.route('/login_select', methods=['GET'])
def login_select():
return render_template('login_select.html', providers=AUTH_PROVIDERS)
@auth_bp.route('/logout', methods=('GET', ))
def logout():
logout_user()

View File

@@ -1,5 +1,7 @@
# Route for handling the login page logic
from flask import request, redirect, render_template, url_for
from flask_login import login_user
from backend.auth import auth_bp
@@ -10,5 +12,9 @@ def base_login():
if request.form['username'] != 'admin' or request.form['password'] != 'admin':
error = 'Invalid Credentials. Please try again.'
else:
login_user()
return redirect("/")
return render_template('login.html', error=error)

View File

@@ -11,7 +11,19 @@ AUTH_PROVIDERS: Dict[str, Dict[str, str]] = {
"type": "login_form",
"url": "auth.base_login"
},
"KIT OIDC (API)":
{
"type": "api_oidc",
"url": "auth_api_bp.oidc"
},
"User-Password (API)":
{
"type": "api_login_form",
"url": "auth_api_bp.base_login"
},
}
DEFAULT_PROVIDER: str = "Base Login"
#DEFAULT_PROVIDER: str = "KIT OIDC"
#DEFAULT_PROVIDER: str = "Base Login"
DEFAULT_PROVIDER: str = "KIT OIDC (API)"
DEFAULT_FRONTEND_PROVIDER: str = "Base Login"

View File

@@ -5,6 +5,7 @@ OIDC login auth module
import flask
from flask import jsonify
from flask_login import login_user
from flask_pyoidc.flask_pyoidc import OIDCAuthentication
from flask_pyoidc.user_session import UserSession
@@ -50,8 +51,9 @@ def create_or_retrieve_user_from_userinfo(userinfo):
@oidc_auth.oidc_auth()
def oidc():
user_session = UserSession(flask.session)
create_or_retrieve_user_from_userinfo(user_session.userinfo)
#login_user(user)
app.logger.info(user_session.userinfo)
user = create_or_retrieve_user_from_userinfo(user_session.userinfo)
login_user(user)
return jsonify(id_token=user_session.id_token,
access_token=flask.session['access_token'],
userinfo=user_session.userinfo)

View File

@@ -68,6 +68,8 @@ class Config():
JWT_ALGORITHM = "HS256"
JWT_EXP_DELTA_SECONDS = 5 * 60
AUTH_RETURN_EXTERNAL_JWT = False
INDEX_TEMPLATE = "index.html"