Support for OpenID Connect authentication flow

master
dsc 4 years ago
parent a386a8e0c9
commit d9bf22c5ff

@ -7,6 +7,7 @@ from sqlalchemy.orm import relationship, backref
import sqlalchemy as sa
from sqlalchemy.orm import scoped_session, sessionmaker, relationship
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.types import Float
from sqlalchemy_json import MutableJson
@ -26,11 +27,14 @@ class User(db.Model):
admin = db.Column(db.Boolean, default=False)
proposals = relationship('Proposal', back_populates="user")
comments = relationship("Comment", back_populates="user")
uuid = db.Column(UUID(as_uuid=True), unique=True)
def __init__(self, username, password, email):
def __init__(self, username, password, email, uuid=None):
from funding.factory import bcrypt
self.username = username
self.password = bcrypt.generate_password_hash(password).decode('utf8')
if password:
self.password = bcrypt.generate_password_hash(password).decode('utf8')
self.uuid = uuid
self.email = email
self.registered_on = datetime.utcnow()
@ -57,16 +61,17 @@ class User(db.Model):
return self.username
@classmethod
def add(cls, username, password, email):
def add(cls, username, password=None, email=None, uuid=None):
from funding.factory import db
from funding.validation import val_username, val_email
try:
# validate incoming username/email
val_username(username)
val_email(email)
if email:
val_email(email)
user = User(username, password, email)
user = User(username=username, password=password, email=email, uuid=uuid)
db.session.add(user)
db.session.commit()
db.session.flush()

@ -1,7 +1,8 @@
import uuid
from datetime import datetime
import requests
from flask import request, redirect, render_template, url_for, flash, make_response, send_from_directory, jsonify
from flask import request, redirect, render_template, url_for, flash, make_response, send_from_directory, jsonify, session
from flask_login import login_user , logout_user , current_user
from dateutil.parser import parse as dateutil_parse
from flask_yoloapi import endpoint, parameter
@ -301,12 +302,93 @@ def register():
return make_response(render_template('register.html'))
if settings.OPENID_ENABLED:
@app.route("/wow-auth/")
def wow_auth():
assert "state" in request.args
assert "session_state" in request.args
assert "code" in request.args
# verify state
if not session.get('auth_state'):
return "session error", 500
if request.args['state'] != session['auth_state']:
return "attack detected :)", 500
# with this authorization code we can fetch an access token
url = f"{settings.OPENID_URL}/token"
data = {
"grant_type": "authorization_code",
"code": request.args["code"],
"redirect_uri": settings.OPENID_REDIRECT_URI,
"client_id": settings.OPENID_CLIENT_ID,
"client_secret": settings.OPENID_CLIENT_SECRET,
"state": request.args['state']
}
try:
resp = requests.post(url, data=data)
resp.raise_for_status()
except:
return "something went wrong :( #1", 500
data = resp.json()
assert "access_token" in data
assert data.get("token_type") == "bearer"
access_token = data['access_token']
# fetch user information with the access token
url = f"{settings.OPENID_URL}/userinfo"
try:
resp = requests.post(url, headers={"Authorization": f"Bearer {access_token}"})
resp.raise_for_status()
user_profile = resp.json()
except:
return "something went wrong :( #2", 500
username = user_profile.get("preferred_username")
sub = user_profile.get("sub")
if not username:
return "something went wrong :( #3", 500
sub_uuid = uuid.UUID(sub)
user = User.query.filter_by(username=username).first()
if user:
if not user.uuid:
user.uuid = sub_uuid
db.session.commit()
db.session.flush()
else:
user = User.add(username=username,
password=None, email=None, uuid=sub_uuid)
login_user(user)
response = redirect(request.args.get('next') or url_for('index'))
response.headers['X-Set-Cookie'] = True
return response
@app.route('/login', methods=['GET', 'POST'])
@endpoint.api(
parameter('username', type=str, location='form'),
parameter('password', type=str, location='form')
parameter('username', type=str, location='form', required=False),
parameter('password', type=str, location='form', required=False)
)
def login(username, password):
if settings.OPENID_ENABLED:
state = uuid.uuid4().hex
session['auth_state'] = state
url = f"{settings.OPENID_URL}/auth?" \
f"client_id={settings.OPENID_CLIENT_ID}&" \
f"redirect_uri={settings.OPENID_REDIRECT_URI}&" \
f"response_type=code&" \
f"state={state}"
return redirect(url)
if not username or not password:
flash('Enter username/password pl0x')
return make_response(render_template('login.html'))
if request.method == 'GET':
return make_response(render_template('login.html'))
@ -327,7 +409,6 @@ def logout():
logout_user()
response = redirect(request.args.get('next') or url_for('login'))
response.headers['X-Set-Cookie'] = True
flash('Logout successfully')
return response

@ -14,6 +14,13 @@ PSQL_DB = ''
PSQL_USER = 'postgres'
PSQL_PASS = ''
OPENID_ENABLED = False
OPENID_REALM = "master"
OPENID_URL = f"https://login.wownero.com/auth/realms/{OPENID_REALM}/protocol/openid-connect"
OPENID_CLIENT_ID = ""
OPENID_CLIENT_SECRET = ""
OPENID_REDIRECT_URI = "http://0.0.0.0:5004/wow-auth/"
SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DATABASE_URI', 'postgresql://{user}:{pw}@localhost/{db}').format(user=PSQL_USER, pw=PSQL_PASS, db=PSQL_DB)
SESSION_COOKIE_NAME = os.environ.get('{coincode}_SESSION_COOKIE_NAME', '{coincode}_id').format(coincode=COINCODE.upper())

Loading…
Cancel
Save