feat: Push initial version
This commit is contained in:
parent
924d9639a5
commit
6f195c86a1
28
.gitea/workflows/build.yml
Normal file
28
.gitea/workflows/build.yml
Normal file
@ -0,0 +1,28 @@
|
||||
name: Build Docker
|
||||
run-name: Build Docker Image
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
Build Docker:
|
||||
runs-on: [ubuntu-latest, amd]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Install Docker
|
||||
run : |
|
||||
apt-get install ca-certificates curl gnupg
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt-get update
|
||||
apt-get install docker-ce-cli -y
|
||||
- name: Build Docker image
|
||||
run : |
|
||||
echo "${{ secrets.DOCKERGIT_TOKEN }}" | docker login git.woodburn.au -u nathanwoodburn --password-stdin
|
||||
tag_num=$(git rev-parse --short HEAD)
|
||||
docker build -t hns-login:$tag_num .
|
||||
docker tag hns-login:$tag_num git.woodburn.au/nathanwoodburn/hns-login:$tag_num
|
||||
docker push git.woodburn.au/nathanwoodburn/hns-login:$tag_num
|
||||
docker tag hns-login:$tag_num git.woodburn.au/nathanwoodburn/hns-login:latest
|
||||
docker push git.woodburn.au/nathanwoodburn/hns-login:latest
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
__pycache__/
|
||||
|
||||
instance/
|
7
Dockerfile
Normal file
7
Dockerfile
Normal file
@ -0,0 +1,7 @@
|
||||
FROM python:3.10-bullseye
|
||||
COPY requirements.txt /app/
|
||||
WORKDIR /app
|
||||
RUN pip install -r requirements.txt
|
||||
COPY . .
|
||||
VOLUME /app/instance
|
||||
CMD ["flask", "run", "-p", "9090", "-h", "0.0.0.0"]
|
@ -1 +1,10 @@
|
||||
# varo-openid
|
||||
|
||||
## Add a client
|
||||
Set the following parameters:
|
||||
|
||||
Allowed Scope: `profile`
|
||||
Allowed Grant Types: `authorization_code`
|
||||
Allowed Response Types: `code`
|
||||
Token Endpoint Authentication Method: `client_secret_post`
|
||||
|
||||
|
9
app.py
Normal file
9
app.py
Normal file
@ -0,0 +1,9 @@
|
||||
from website.app import create_app
|
||||
|
||||
|
||||
app = create_app({
|
||||
'SECRET_KEY': 'secret',
|
||||
'OAUTH2_REFRESH_TOKEN_GENERATOR': True,
|
||||
'SQLALCHEMY_TRACK_MODIFICATIONS': False,
|
||||
'SQLALCHEMY_DATABASE_URI': 'sqlite:///db.sqlite',
|
||||
})
|
4
requrements.txt
Normal file
4
requrements.txt
Normal file
@ -0,0 +1,4 @@
|
||||
Flask
|
||||
Flask-SQLAlchemy
|
||||
Authlib
|
||||
requests
|
36
website/app.py
Normal file
36
website/app.py
Normal file
@ -0,0 +1,36 @@
|
||||
import os
|
||||
from flask import Flask
|
||||
from .models import db
|
||||
from .oauth2 import config_oauth
|
||||
from .routes import bp
|
||||
|
||||
|
||||
def create_app(config=None):
|
||||
app = Flask(__name__)
|
||||
|
||||
# load default configuration
|
||||
app.config.from_object('website.settings')
|
||||
|
||||
# load environment configuration
|
||||
if 'WEBSITE_CONF' in os.environ:
|
||||
app.config.from_envvar('WEBSITE_CONF')
|
||||
|
||||
# load app specified configuration
|
||||
if config is not None:
|
||||
if isinstance(config, dict):
|
||||
app.config.update(config)
|
||||
elif config.endswith('.py'):
|
||||
app.config.from_pyfile(config)
|
||||
|
||||
setup_app(app)
|
||||
return app
|
||||
|
||||
|
||||
def setup_app(app):
|
||||
|
||||
db.init_app(app)
|
||||
# Create tables if they do not exist already
|
||||
with app.app_context():
|
||||
db.create_all()
|
||||
config_oauth(app)
|
||||
app.register_blueprint(bp, url_prefix='')
|
56
website/models.py
Normal file
56
website/models.py
Normal file
@ -0,0 +1,56 @@
|
||||
import time
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from authlib.integrations.sqla_oauth2 import (
|
||||
OAuth2ClientMixin,
|
||||
OAuth2AuthorizationCodeMixin,
|
||||
OAuth2TokenMixin,
|
||||
)
|
||||
|
||||
db = SQLAlchemy()
|
||||
|
||||
|
||||
class User(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(db.String(40), unique=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.username
|
||||
|
||||
def get_user_id(self):
|
||||
return self.id
|
||||
|
||||
def check_password(self, password):
|
||||
return password == 'valid'
|
||||
|
||||
|
||||
class OAuth2Client(db.Model, OAuth2ClientMixin):
|
||||
__tablename__ = 'oauth2_client'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
user_id = db.Column(
|
||||
db.Integer, db.ForeignKey('user.id', ondelete='CASCADE'))
|
||||
user = db.relationship('User')
|
||||
|
||||
|
||||
class OAuth2AuthorizationCode(db.Model, OAuth2AuthorizationCodeMixin):
|
||||
__tablename__ = 'oauth2_code'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
user_id = db.Column(
|
||||
db.Integer, db.ForeignKey('user.id', ondelete='CASCADE'))
|
||||
user = db.relationship('User')
|
||||
|
||||
|
||||
class OAuth2Token(db.Model, OAuth2TokenMixin):
|
||||
__tablename__ = 'oauth2_token'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
user_id = db.Column(
|
||||
db.Integer, db.ForeignKey('user.id', ondelete='CASCADE'))
|
||||
user = db.relationship('User')
|
||||
|
||||
def is_refresh_token_active(self):
|
||||
if self.revoked:
|
||||
return False
|
||||
expires_at = self.issued_at + self.expires_in * 2
|
||||
return expires_at >= time.time()
|
101
website/oauth2.py
Normal file
101
website/oauth2.py
Normal file
@ -0,0 +1,101 @@
|
||||
from authlib.integrations.flask_oauth2 import (
|
||||
AuthorizationServer,
|
||||
ResourceProtector,
|
||||
)
|
||||
from authlib.integrations.sqla_oauth2 import (
|
||||
create_query_client_func,
|
||||
create_save_token_func,
|
||||
create_revocation_endpoint,
|
||||
create_bearer_token_validator,
|
||||
)
|
||||
from authlib.oauth2.rfc6749 import grants
|
||||
from authlib.oauth2.rfc7636 import CodeChallenge
|
||||
from .models import db, User
|
||||
from .models import OAuth2Client, OAuth2AuthorizationCode, OAuth2Token
|
||||
|
||||
|
||||
class AuthorizationCodeGrant(grants.AuthorizationCodeGrant):
|
||||
TOKEN_ENDPOINT_AUTH_METHODS = [
|
||||
'client_secret_basic',
|
||||
'client_secret_post',
|
||||
'none',
|
||||
]
|
||||
|
||||
def save_authorization_code(self, code, request):
|
||||
code_challenge = request.data.get('code_challenge')
|
||||
code_challenge_method = request.data.get('code_challenge_method')
|
||||
auth_code = OAuth2AuthorizationCode(
|
||||
code=code,
|
||||
client_id=request.client.client_id,
|
||||
redirect_uri=request.redirect_uri,
|
||||
scope=request.scope,
|
||||
user_id=request.user.id,
|
||||
code_challenge=code_challenge,
|
||||
code_challenge_method=code_challenge_method,
|
||||
)
|
||||
db.session.add(auth_code)
|
||||
db.session.commit()
|
||||
return auth_code
|
||||
|
||||
def query_authorization_code(self, code, client):
|
||||
auth_code = OAuth2AuthorizationCode.query.filter_by(
|
||||
code=code, client_id=client.client_id).first()
|
||||
if auth_code and not auth_code.is_expired():
|
||||
return auth_code
|
||||
|
||||
def delete_authorization_code(self, authorization_code):
|
||||
db.session.delete(authorization_code)
|
||||
db.session.commit()
|
||||
|
||||
def authenticate_user(self, authorization_code):
|
||||
return User.query.get(authorization_code.user_id)
|
||||
|
||||
|
||||
class PasswordGrant(grants.ResourceOwnerPasswordCredentialsGrant):
|
||||
def authenticate_user(self, username, password):
|
||||
user = User.query.filter_by(username=username).first()
|
||||
if user is not None and user.check_password(password):
|
||||
return user
|
||||
|
||||
|
||||
class RefreshTokenGrant(grants.RefreshTokenGrant):
|
||||
def authenticate_refresh_token(self, refresh_token):
|
||||
token = OAuth2Token.query.filter_by(refresh_token=refresh_token).first()
|
||||
if token and token.is_refresh_token_active():
|
||||
return token
|
||||
|
||||
def authenticate_user(self, credential):
|
||||
return User.query.get(credential.user_id)
|
||||
|
||||
def revoke_old_credential(self, credential):
|
||||
credential.revoked = True
|
||||
db.session.add(credential)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
query_client = create_query_client_func(db.session, OAuth2Client)
|
||||
save_token = create_save_token_func(db.session, OAuth2Token)
|
||||
authorization = AuthorizationServer(
|
||||
query_client=query_client,
|
||||
save_token=save_token,
|
||||
)
|
||||
require_oauth = ResourceProtector()
|
||||
|
||||
|
||||
def config_oauth(app):
|
||||
authorization.init_app(app)
|
||||
|
||||
# support all grants
|
||||
authorization.register_grant(grants.ImplicitGrant)
|
||||
authorization.register_grant(grants.ClientCredentialsGrant)
|
||||
authorization.register_grant(AuthorizationCodeGrant, [CodeChallenge(required=True)])
|
||||
authorization.register_grant(PasswordGrant)
|
||||
authorization.register_grant(RefreshTokenGrant)
|
||||
|
||||
# support revocation
|
||||
revocation_cls = create_revocation_endpoint(db.session, OAuth2Token)
|
||||
authorization.register_endpoint(revocation_cls)
|
||||
|
||||
# protect resource
|
||||
bearer_cls = create_bearer_token_validator(db.session, OAuth2Token)
|
||||
require_oauth.register_token_validator(bearer_cls())
|
144
website/routes.py
Normal file
144
website/routes.py
Normal file
@ -0,0 +1,144 @@
|
||||
import time
|
||||
from .varo_auth import flask_login as varo_auth_flask_login
|
||||
from flask import Blueprint, request, session, url_for
|
||||
from flask import render_template, redirect, jsonify, send_from_directory
|
||||
from werkzeug.security import gen_salt
|
||||
from authlib.integrations.flask_oauth2 import current_token
|
||||
from authlib.oauth2 import OAuth2Error
|
||||
from .models import db, User, OAuth2Client
|
||||
from .oauth2 import authorization, require_oauth
|
||||
|
||||
|
||||
bp = Blueprint('home', __name__)
|
||||
|
||||
|
||||
def current_user():
|
||||
if 'id' in session:
|
||||
uid = session['id']
|
||||
return User.query.get(uid)
|
||||
return None
|
||||
|
||||
|
||||
def split_by_crlf(s):
|
||||
return [v for v in s.splitlines() if v]
|
||||
|
||||
|
||||
@bp.route('/', methods=('GET', 'POST'))
|
||||
def home():
|
||||
next_page = request.args.get('next')
|
||||
if request.method == 'POST':
|
||||
auth = varo_auth_flask_login(request)
|
||||
if auth == False:
|
||||
return redirect('/?error=login_failed')
|
||||
print(auth)
|
||||
user = User.query.filter_by(username=auth).first()
|
||||
if not user:
|
||||
user = User(username=auth)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
session['id'] = user.id
|
||||
# if user is not just to log in, but need to head back to the auth page, then go for it
|
||||
if next_page:
|
||||
return redirect(next_page)
|
||||
return redirect('/')
|
||||
user = current_user()
|
||||
if user:
|
||||
clients = OAuth2Client.query.filter_by(user_id=user.id).all()
|
||||
if next_page:
|
||||
return redirect(next_page)
|
||||
else:
|
||||
clients = []
|
||||
|
||||
return render_template('home.html', user=user, clients=clients)
|
||||
|
||||
|
||||
@bp.route('/logout')
|
||||
def logout():
|
||||
del session['id']
|
||||
next = request.args.get('next')
|
||||
if next:
|
||||
return redirect(url_for('home.home', next=next))
|
||||
|
||||
return redirect('/')
|
||||
|
||||
|
||||
@bp.route('/create_client', methods=('GET', 'POST'))
|
||||
def create_client():
|
||||
user = current_user()
|
||||
if not user:
|
||||
return redirect('/')
|
||||
if request.method == 'GET':
|
||||
return render_template('create_client.html')
|
||||
|
||||
client_id = gen_salt(24)
|
||||
client_id_issued_at = int(time.time())
|
||||
client = OAuth2Client(
|
||||
client_id=client_id,
|
||||
client_id_issued_at=client_id_issued_at,
|
||||
user_id=user.id,
|
||||
)
|
||||
|
||||
form = request.form
|
||||
client_metadata = {
|
||||
"client_name": form["client_name"],
|
||||
"client_uri": form["client_uri"],
|
||||
"grant_types": split_by_crlf(form["grant_type"]),
|
||||
"redirect_uris": split_by_crlf(form["redirect_uri"]),
|
||||
"response_types": split_by_crlf(form["response_type"]),
|
||||
"scope": form["scope"],
|
||||
"token_endpoint_auth_method": form["token_endpoint_auth_method"]
|
||||
}
|
||||
client.set_client_metadata(client_metadata)
|
||||
|
||||
if form['token_endpoint_auth_method'] == 'none':
|
||||
client.client_secret = ''
|
||||
else:
|
||||
client.client_secret = gen_salt(48)
|
||||
|
||||
db.session.add(client)
|
||||
db.session.commit()
|
||||
return redirect('/')
|
||||
|
||||
|
||||
@bp.route('/oauth/authorize', methods=['GET', 'POST'])
|
||||
def authorize():
|
||||
user = current_user()
|
||||
# if user log status is not true (Auth server), then to log it in
|
||||
if not user:
|
||||
return redirect(url_for('home.home', next=request.url))
|
||||
if request.method == 'GET':
|
||||
try:
|
||||
grant = authorization.get_consent_grant(end_user=user)
|
||||
except OAuth2Error as error:
|
||||
return error.error
|
||||
return render_template('authorize.html', user=user, grant=grant)
|
||||
|
||||
grant_user = user
|
||||
|
||||
return authorization.create_authorization_response(grant_user=grant_user)
|
||||
|
||||
|
||||
@bp.route('/oauth/token', methods=['POST'])
|
||||
def issue_token():
|
||||
return authorization.create_token_response()
|
||||
|
||||
|
||||
@bp.route('/oauth/revoke', methods=['POST'])
|
||||
def revoke_token():
|
||||
return authorization.create_endpoint_response('revocation')
|
||||
|
||||
|
||||
@bp.route('/api/me')
|
||||
@require_oauth('profile')
|
||||
def api_me():
|
||||
user = current_token.user
|
||||
print(user.id, user.username)
|
||||
return jsonify(id=user.id, username=user.username,
|
||||
email="auth+" + user.username + "@hnshosting.au",
|
||||
displayName=user.username+"/")
|
||||
|
||||
|
||||
|
||||
@bp.route('/favicon.png')
|
||||
def favicon():
|
||||
return send_from_directory('templates', 'favicon.png', mimetype='image/png')
|
0
website/settings.py
Normal file
0
website/settings.py
Normal file
62
website/templates/authorize.html
Normal file
62
website/templates/authorize.html
Normal file
@ -0,0 +1,62 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>HNS Login</title>
|
||||
<link rel="icon" href="favicon.png" type="image/png">
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
/* Dark theme*/
|
||||
background-color: #222;
|
||||
color: #fff;
|
||||
}
|
||||
h1 {
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
p {
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
form {
|
||||
text-align: center;
|
||||
}
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
font-size: 16px;
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Authorize Access</h1>
|
||||
<p><strong>{{grant.client.client_name}}</strong> is requesting to access your account.</p>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You are currently logged in as <strong>{{ user.username }}/</strong> (<a href="{{ url_for('.logout', next=request.url) }}">Change Account</a>)
|
||||
</p>
|
||||
|
||||
<form action="" method="post">
|
||||
<br>
|
||||
<button>Continue</button>
|
||||
</form>
|
||||
|
||||
<div style="position: fixed; bottom: 0; width: 100%; text-align: center; background-color: #333; padding: 10px;">
|
||||
Powered by <a href="https://auth.varo.domains/implement" target="_blank">Varo Auth</a> and <a href="nathan.woodburn.au" target="_blank">Nathan.Woodburn/</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
101
website/templates/create_client.html
Normal file
101
website/templates/create_client.html
Normal file
@ -0,0 +1,101 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>HNS Login</title>
|
||||
<link rel="icon" href="favicon.png" type="image/png">
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
/* Dark theme*/
|
||||
background-color: #222;
|
||||
color: #fff;
|
||||
}
|
||||
h1 {
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
h2 {
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
p {
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
form {
|
||||
text-align: center;
|
||||
}
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
font-size: 16px;
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
label, label > span { display: block; }
|
||||
label { margin: 15px 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/"><h1>Home</h1></a>
|
||||
<h2>Create OAuth Client</h2>
|
||||
|
||||
<form action="" method="post">
|
||||
<label>
|
||||
<span>Client Name</span>
|
||||
<input type="text" name="client_name">
|
||||
</label>
|
||||
<label>
|
||||
<span>Client URI</span>
|
||||
<input type="url" name="client_uri">
|
||||
</label>
|
||||
<label>
|
||||
<span>Allowed Scope</span>
|
||||
<input type="text" name="scope">
|
||||
</label>
|
||||
<label>
|
||||
<span>Redirect URIs</span>
|
||||
<textarea name="redirect_uri" cols="30" rows="10"></textarea>
|
||||
</label>
|
||||
<label>
|
||||
<span>Allowed Grant Types</span>
|
||||
<textarea name="grant_type" cols="30" rows="10"></textarea>
|
||||
</label>
|
||||
<label>
|
||||
<span>Allowed Response Types</span>
|
||||
<textarea name="response_type" cols="30" rows="10"></textarea>
|
||||
</label>
|
||||
<label>
|
||||
<span>Token Endpoint Auth Method</span>
|
||||
<select name="token_endpoint_auth_method">
|
||||
<option value="client_secret_basic">client_secret_basic</option>
|
||||
<option value="client_secret_post">client_secret_post</option>
|
||||
<option value="none">none</option>
|
||||
</select>
|
||||
</label>
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
|
||||
<div style="height: 5em;"></div>
|
||||
<div style="bottom: 0; text-align: center; background-color: #333; padding: 10px;">
|
||||
Powered by <a href="https://auth.varo.domains/implement" target="_blank">Varo Auth</a> and <a href="nathan.woodburn.au" target="_blank">Nathan.Woodburn/</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
BIN
website/templates/favicon.png
Normal file
BIN
website/templates/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
119
website/templates/home.html
Normal file
119
website/templates/home.html
Normal file
@ -0,0 +1,119 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>HNS Login</title>
|
||||
<link rel="icon" href="favicon.png" type="image/png">
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
/* Dark theme*/
|
||||
background-color: #222;
|
||||
color: #fff;
|
||||
}
|
||||
h1 {
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
h2 {
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
p {
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
form {
|
||||
text-align: center;
|
||||
}
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
font-size: 16px;
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
a.button {
|
||||
display: block;
|
||||
width: 200px;
|
||||
margin: 20px auto;
|
||||
padding: 10px 20px;
|
||||
font-size: 16px;
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
}
|
||||
button.loginbutton {
|
||||
/* Put in the centre of the screen */
|
||||
|
||||
margin-left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>HNS Login</h1>
|
||||
|
||||
{% if user %}
|
||||
|
||||
<h2>You are currently logged in as <strong>{{ user }}/</strong></h2>
|
||||
|
||||
|
||||
<a href="{{ url_for('.logout') }}" class="button">Log Out</a>
|
||||
|
||||
{% for client in clients %}
|
||||
<pre>
|
||||
<strong>Client Info</strong>
|
||||
{%- for key in client.client_info %}
|
||||
<strong>{{ key }}: </strong>{{ client.client_info[key] }}
|
||||
{%- endfor %}
|
||||
<strong>Client Metadata</strong>
|
||||
{%- for key in client.client_metadata %}
|
||||
<strong>{{ key }}: </strong>{{ client.client_metadata[key] }}
|
||||
{%- endfor %}
|
||||
</pre>
|
||||
<hr>
|
||||
{% endfor %}
|
||||
|
||||
<br><br>
|
||||
<p>Want to implement OAuth?<br>
|
||||
Contact Nathan.Woodburn/ on any social media platform</p>
|
||||
|
||||
{% else %}
|
||||
<h2>Login with your Handshake domain</h2>
|
||||
<p>If you have already used Varo Auth, you can just select the domain you want to login with.</p>
|
||||
|
||||
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
|
||||
<script type="text/javascript" src="https://auth.varo.domains/v1"></script>
|
||||
<script>var varo = new Varo();</script>
|
||||
<button class="loginbutton" onclick='varo.auth().then(auth => {
|
||||
if (auth.success) {
|
||||
// handle success by calling your api to update the users session
|
||||
$.post("/", JSON.stringify(auth.data), (response) => {
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
});'>Login</button>
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
<div style="position: fixed; bottom: 0; width: 100%; text-align: center; background-color: #333; padding: 10px;">
|
||||
Powered by <a href="https://auth.varo.domains/implement" target="_blank">Varo Auth</a> and <a href="nathan.woodburn.au" target="_blank">Nathan.Woodburn/</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
22
website/varo_auth.py
Normal file
22
website/varo_auth.py
Normal file
@ -0,0 +1,22 @@
|
||||
import requests
|
||||
import json
|
||||
|
||||
def flask_login(request):
|
||||
dict = request.form.to_dict()
|
||||
keys = dict.keys()
|
||||
keys = list(keys)[0]
|
||||
keys = json.loads(keys)
|
||||
auth_request = keys['request']
|
||||
return login(auth_request)
|
||||
|
||||
def login(request):
|
||||
r = requests.get(f'https://auth.varo.domains/verify/{request}')
|
||||
r = r.json()
|
||||
if r['success'] == False:
|
||||
return False
|
||||
|
||||
if 'data' in r:
|
||||
data = r['data']
|
||||
if 'name' in data:
|
||||
return data['name']
|
||||
return False
|
Loading…
Reference in New Issue
Block a user