Implemented oidc with hydra
This commit is contained in:
parent
26ffb28a41
commit
2160f634d1
9 changed files with 87 additions and 16 deletions
3
app.py
3
app.py
|
@ -13,10 +13,12 @@ from areas import auth
|
|||
from helpers import (
|
||||
BadRequest,
|
||||
KratosError,
|
||||
HydraError,
|
||||
bad_request_error,
|
||||
validation_error,
|
||||
kratos_error,
|
||||
global_error,
|
||||
hydra_error,
|
||||
)
|
||||
from config import *
|
||||
|
||||
|
@ -30,6 +32,7 @@ app.register_error_handler(Exception, global_error)
|
|||
app.register_error_handler(BadRequest, bad_request_error)
|
||||
app.register_error_handler(ValidationError, validation_error)
|
||||
app.register_error_handler(KratosError, kratos_error)
|
||||
app.register_error_handler(HydraError, hydra_error)
|
||||
|
||||
jwt = JWTManager(app)
|
||||
|
||||
|
|
|
@ -1,21 +1,26 @@
|
|||
from flask import request, jsonify
|
||||
from flask import jsonify
|
||||
from flask_jwt_extended import create_access_token
|
||||
from flask_cors import cross_origin
|
||||
from datetime import timedelta
|
||||
|
||||
from areas import api_v1
|
||||
|
||||
USERNAME = 'admin'
|
||||
PASSWORD = 'admin'
|
||||
from config import *
|
||||
from helpers import HydraOauth
|
||||
|
||||
|
||||
@api_v1.route('/login', methods=['POST'])
|
||||
@api_v1.route("/login", methods=["POST"])
|
||||
@cross_origin()
|
||||
def login():
|
||||
username = request.json.get('username')
|
||||
password = request.json.get('password')
|
||||
authorization_url = HydraOauth.authorize()
|
||||
return jsonify({"authorizationUrl": authorization_url})
|
||||
|
||||
if username != USERNAME or password != PASSWORD:
|
||||
return jsonify({'errorMessage': 'Invalid username or password'}), 401
|
||||
|
||||
access_token = create_access_token(identity=username)
|
||||
return jsonify({'username': USERNAME, 'access_token': access_token})
|
||||
@api_v1.route("/hydra/callback")
|
||||
@cross_origin()
|
||||
def hydra_callback():
|
||||
token = HydraOauth.get_token()
|
||||
access_token = create_access_token(
|
||||
identity=token, expires_delta=timedelta(days=365)
|
||||
)
|
||||
|
||||
return jsonify({"access_token": access_token})
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
import os
|
||||
|
||||
SECRET_KEY = os.environ.get('SECRET_KEY')
|
||||
KRATOS_URL = os.environ.get('KRATOS_URL')
|
||||
SECRET_KEY = os.environ.get("SECRET_KEY")
|
||||
KRATOS_URL = os.environ.get("KRATOS_URL")
|
||||
HYDRA_CLIENT_ID = os.environ.get("HYDRA_CLIENT_ID")
|
||||
HYDRA_CLIENT_SECRET = os.environ.get("HYDRA_CLIENT_SECRET")
|
||||
HYDRA_AUTHORIZATION_BASE_URL = os.environ.get("HYDRA_AUTHORIZATION_BASE_URL")
|
||||
TOKEN_URL = os.environ.get("TOKEN_URL")
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
from .kratos_api import *
|
||||
from .error_handler import *
|
||||
from .hydra_oauth import *
|
||||
|
|
|
@ -6,6 +6,10 @@ class KratosError(Exception):
|
|||
pass
|
||||
|
||||
|
||||
class HydraError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class BadRequest(Exception):
|
||||
pass
|
||||
|
||||
|
@ -24,11 +28,17 @@ def validation_error(e):
|
|||
|
||||
|
||||
def kratos_error(e):
|
||||
message = e.args[0] if e.args else "Failed to contact Kratos."
|
||||
message = "[KratosError] " + e.args[0] if e.args else "Failed to contact Kratos."
|
||||
status_code = e.args[1] if e.args else 500
|
||||
return jsonify({"errorMessage": message}), status_code
|
||||
|
||||
|
||||
def hydra_error(e):
|
||||
message = "[HydraError] " + e.args[0] if e.args else "Failed to contact Hydra."
|
||||
status_code = e.args[1] if e.args else 500
|
||||
return jsonify({"errorMessage": message}), status_code
|
||||
|
||||
|
||||
def global_error(e):
|
||||
message = str(e)
|
||||
return jsonify({"errorMessage": message})
|
||||
return jsonify({"errorMessage": message}), 500
|
||||
|
|
41
helpers/hydra_oauth.py
Normal file
41
helpers/hydra_oauth.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
from flask import request, session
|
||||
from requests_oauthlib import OAuth2Session
|
||||
|
||||
from config import *
|
||||
from helpers import HydraError
|
||||
|
||||
|
||||
class HydraOauth:
|
||||
SESSION_KEY = "oauth_state"
|
||||
|
||||
@staticmethod
|
||||
def authorize():
|
||||
try:
|
||||
hydra = OAuth2Session(HYDRA_CLIENT_ID)
|
||||
authorization_url, state = hydra.authorization_url(
|
||||
HYDRA_AUTHORIZATION_BASE_URL
|
||||
)
|
||||
|
||||
# State is used to prevent CSRF, keep this for later.
|
||||
session[HydraOauth.SESSION_KEY] = state
|
||||
|
||||
return authorization_url
|
||||
except Exception as err:
|
||||
raise HydraError(str(err), 500)
|
||||
|
||||
@staticmethod
|
||||
def get_token():
|
||||
try:
|
||||
hydra = OAuth2Session(
|
||||
HYDRA_CLIENT_ID, state=session[HydraOauth.SESSION_KEY]
|
||||
)
|
||||
token = hydra.fetch_token(
|
||||
TOKEN_URL,
|
||||
client_secret=HYDRA_CLIENT_SECRET,
|
||||
authorization_response=request.url,
|
||||
)
|
||||
|
||||
session["hydra_token"] = token
|
||||
return token
|
||||
except Exception as err:
|
||||
raise HydraError(str(err), 500)
|
|
@ -18,6 +18,8 @@ class KratosApi:
|
|||
res = requests.get("{}{}".format(KRATOS_URL, url))
|
||||
KratosApi.__handleError(res)
|
||||
return res
|
||||
except KratosError as err:
|
||||
raise err
|
||||
except:
|
||||
raise KratosError()
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ jsonschema==4.3.2
|
|||
Jinja2==3.0.3
|
||||
MarkupSafe==2.0.1
|
||||
mypy-extensions==0.4.3
|
||||
oauthlib==3.1.1
|
||||
pathspec==0.9.0
|
||||
platformdirs==2.4.0
|
||||
pycparser==2.21
|
||||
|
@ -24,6 +25,7 @@ PyJWT==2.3.0
|
|||
pyrsistent==0.18.0
|
||||
regex==2021.11.10
|
||||
requests==2.26.0
|
||||
requests-oauthlib==1.3.0
|
||||
six==1.16.0
|
||||
tomli==1.2.3
|
||||
typing-extensions==4.0.1
|
||||
|
|
|
@ -22,5 +22,8 @@ export FLASK_APP=app.py
|
|||
export FLASK_ENV=development
|
||||
export SECRET_KEY="e38hq!@0n64g@qe6)5csk41t=ljo2vllog(%k7njnm4b@kh42c"
|
||||
export KRATOS_URL="http://127.0.0.1:8000"
|
||||
|
||||
export HYDRA_CLIENT_ID="dashboard"
|
||||
export HYDRA_CLIENT_SECRET="BrYRtKygtrcwGHviUSqybvFTgfnaZgPh"
|
||||
export HYDRA_AUTHORIZATION_BASE_URL="https://sso.init.stackspin.net/oauth2/auth"
|
||||
export TOKEN_URL="https://sso.init.stackspin.net/oauth2/token"
|
||||
flask run
|
||||
|
|
Loading…
Reference in a new issue