diff --git a/.gitignore b/.gitignore index 8616315..ffe984e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ __pycache__/ instance/ -website/avatars/ \ No newline at end of file +website/avatars/ + +.env \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 83544d3..80d7a7a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,7 @@ Flask-SQLAlchemy Authlib requests dnspython -requests-doh \ No newline at end of file +requests-doh +python-dotenv +web3 +eth-account \ No newline at end of file diff --git a/website/app.py b/website/app.py index ded960c..9f7300b 100644 --- a/website/app.py +++ b/website/app.py @@ -3,7 +3,10 @@ from flask import Flask from .models import db from .oauth2 import config_oauth from .routes import bp +from datetime import timedelta +import dotenv +dotenv.load_dotenv() def create_app(config=None): app = Flask(__name__) @@ -15,18 +18,26 @@ def create_app(config=None): if 'WEBSITE_CONF' in os.environ: app.config.from_envvar('WEBSITE_CONF') + # Set the secret key to a key from the ENV + app.secret_key = os.getenv("FLASK_SECRET_KEY", os.urandom(24).hex()) + + # Set the session to be permanent + app.config["SESSION_PERMANENT"] = True + + # Set it to 6 months + app.config["PERMANENT_SESSION_LIFETIME"] = timedelta(days=180) + # 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): +def setup_app(app: Flask): db.init_app(app) # Create tables if they do not exist already diff --git a/website/routes.py b/website/routes.py index d8ed4e0..58915ce 100644 --- a/website/routes.py +++ b/website/routes.py @@ -13,9 +13,15 @@ import dns.message import dns.query import dns.rdatatype from requests_doh import DNSOverHTTPSSession, add_dns_provider +from datetime import timedelta +from eth_account.messages import encode_defunct +from eth_account import Account + bp = Blueprint("home", __name__) +openSeaAPIKey = os.getenv("OPENSEA_API_KEY") + if not os.path.exists("website/avatars"): os.makedirs("website/avatars") @@ -45,6 +51,8 @@ def home(): db.session.add(user) db.session.commit() session["id"] = user.id + # Make sure the session is permanent + session.permanent = True # 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) @@ -57,8 +65,68 @@ def home(): else: clients = [] - return render_template("home.html", user=user, clients=clients) + # Check if the user has signed in with HNS ID + hnsid='' + address='' + if "address" in session: + address = session["address"] + openseaInfo = requests.get("https://api.opensea.io/api/v2/chain/optimism/account/{address}/nfts?collection=handshake-slds", + headers={"Accept": "application/json", + "x-api-key":openSeaAPIKey}) + if openseaInfo.status_code == 200: + hnsid = openseaInfo.json() + + + return render_template("home.html", user=user, clients=clients, address=address, hnsid=hnsid) + +@bp.route("/hnsid", methods=["POST"]) +def hnsid(): + # Get address and signature from the request + address = request.json.get("address") + signature = request.json.get("signature") + message = request.json.get("message") + # Verify the signature + msg = encode_defunct(text=message) + signer = Account.recover_message(msg, signature=signature).lower() + if signer != address: + print("Signature verification failed") + print(signer, address) + return jsonify({"success": False}) + + # Save the address in the session + session["address"] = address + session.permanent = True + + return jsonify({"success": True}) + +@bp.route("/hnsid/") +def hnsid_domain(domain): + # Get the address from the session + address = session.get("address") + if not address: + return jsonify({"error": "No address found in session"}) + + # Get domain info from Opensea + openseaInfo = requests.get(f"https://api.opensea.io/api/v2/chain/optimism/account/{address}/nfts?collection=handshake-slds", + headers={"Accept": "application/json", + "x-api-key":openSeaAPIKey}) + if openseaInfo.status_code != 200: + return jsonify({"error": "Failed to get domain info from Opensea"}) + hnsid = openseaInfo.json() + for nft in hnsid["nfts"]: + if nft["name"] == domain: + # Add domain to the session + user = User.query.filter_by(username=domain).first() + if not user: + user = User(username=domain) + db.session.add(user) + db.session.commit() + session["id"] = user.id + session.permanent = True + return redirect("/") + + return jsonify({"success": False, "error": "Domain not found"}) @bp.route("/logout") def logout(): diff --git a/website/templates/home.html b/website/templates/home.html index 2191fa0..59a0c1f 100644 --- a/website/templates/home.html +++ b/website/templates/home.html @@ -1,80 +1,104 @@ + HNS Login - + 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%; + margin-top: 20px; + transform: translateX(-50%); + } + + .login-option { + margin-top: 20px; + } + select { + padding: 10px 20px; + font-size: 16px; + background-color: #333; + color: #fff; + border: none; + cursor: pointer; + margin-right: 25px; + } + + + -

HNS Login

+

HNS Login

-{% if user %} + {% if user %} -

You are currently logged in as {{ user }}/

+

You are currently logged in as {{ user }}/

-Log Out + Log Out -{% for client in clients %} -
+  {% for client in clients %}
+  
 Client Info
   {%- for key in client.client_info %}
   {{ key }}: {{ client.client_info[key] }}
@@ -84,36 +108,133 @@
   {{ key }}: {{ client.client_metadata[key] }}
   {%- endfor %}
 
-
-{% endfor %} +
+ {% endfor %} -

-

Want to implement OAuth?
-Contact Nathan.Woodburn/ on any social media platform

+

+

Want to implement OAuth?
+ Contact Nathan.Woodburn/ on any social media platform

-{% else %} -

Login with your Handshake domain

-

If you have already used Varo Auth, you can just select the domain you want to login with.

+ {% else %} +

Login with your Handshake domain

- - - - -{% endif %} +});'>Login with Varo + + + + + {% endif %} + + + +
+ Powered by Varo Auth and Nathan.Woodburn/ +
+ \ No newline at end of file