wire up metamask authentication
parent
73800d2a69
commit
2a9b1995ce
@ -0,0 +1,13 @@
|
|||||||
|
from eth_account.messages import encode_defunct
|
||||||
|
|
||||||
|
from suchwowx.factory import w3
|
||||||
|
|
||||||
|
|
||||||
|
def verify_signature(message, signature, public_address):
|
||||||
|
msg = encode_defunct(text=message)
|
||||||
|
recovered = w3.eth.account.recover_message(msg, signature=signature)
|
||||||
|
print(f'found recovered: {recovered}')
|
||||||
|
if recovered.lower() == public_address.lower():
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
@ -0,0 +1,95 @@
|
|||||||
|
from secrets import token_urlsafe
|
||||||
|
|
||||||
|
from flask import Blueprint, request, jsonify, url_for
|
||||||
|
from flask_login import current_user
|
||||||
|
|
||||||
|
from suchwowx.factory import db
|
||||||
|
from suchwowx.helpers import verify_signature
|
||||||
|
from suchwowx.models import User
|
||||||
|
|
||||||
|
|
||||||
|
bp = Blueprint('api', 'api', url_prefix='/api/v1')
|
||||||
|
|
||||||
|
@bp.route('/user_exists')
|
||||||
|
def user_exists():
|
||||||
|
"""
|
||||||
|
Check to see if a given user exists (handle or wallet address).
|
||||||
|
This logic will help the login/connect MetaMask flow.
|
||||||
|
"""
|
||||||
|
if 'public_address' in request.args:
|
||||||
|
query_str = 'public_address'
|
||||||
|
query_field = User.public_address
|
||||||
|
elif 'handle' in request.args:
|
||||||
|
query_str = 'handle'
|
||||||
|
query_field = User.handle
|
||||||
|
else:
|
||||||
|
return jsonify({'success': False})
|
||||||
|
|
||||||
|
u = User.query.filter(
|
||||||
|
query_field == request.args[query_str].lower()
|
||||||
|
).first()
|
||||||
|
if u:
|
||||||
|
nonce = u.nonce
|
||||||
|
else:
|
||||||
|
nonce = User().generate_nonce()
|
||||||
|
return jsonify({
|
||||||
|
'user_exists': u is not None,
|
||||||
|
'nonce': nonce,
|
||||||
|
'query': query_str,
|
||||||
|
'success': True
|
||||||
|
})
|
||||||
|
|
||||||
|
@bp.route('/authenticate/metamask', methods=['POST'])
|
||||||
|
def authenticate_metamask():
|
||||||
|
"""
|
||||||
|
This is the login/authenticate route for this dApp.
|
||||||
|
Users POST a `signedData` blob, a message signed by the user with MetaMask
|
||||||
|
(`personal_sign` method).
|
||||||
|
|
||||||
|
This route will verify the signed data against the user's public ETH
|
||||||
|
address. If no user exists, they get an entry in the database with a
|
||||||
|
default handle assigned. If user does exist, they get logged in.
|
||||||
|
"""
|
||||||
|
data = request.get_json()
|
||||||
|
if current_user.is_authenticated:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'message': 'Already registered and authenticated.'
|
||||||
|
})
|
||||||
|
|
||||||
|
_u = User.query.filter_by(
|
||||||
|
public_address=data['public_address'].lower()
|
||||||
|
).first()
|
||||||
|
|
||||||
|
if _u:
|
||||||
|
if data['message'].endswith(_u.nonce):
|
||||||
|
if verify_signature(data['message'], data['signed_data'], data['public_address']):
|
||||||
|
_u.login()
|
||||||
|
return jsonify({
|
||||||
|
'success': True,
|
||||||
|
'message': 'Logged in'
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'message': 'Invalid signature'
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'message': 'Invalid nonce in signed message'
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
rand_str = token_urlsafe(6)
|
||||||
|
user = User(
|
||||||
|
public_address=data['public_address'].lower()
|
||||||
|
)
|
||||||
|
db.session.add(user)
|
||||||
|
db.session.commit()
|
||||||
|
user.handle = f'anon{user.id}-{rand_str}'
|
||||||
|
db.session.commit()
|
||||||
|
user.login()
|
||||||
|
return jsonify({
|
||||||
|
'success': True,
|
||||||
|
'message': 'Registered'
|
||||||
|
})
|
@ -0,0 +1,24 @@
|
|||||||
|
from flask import Blueprint, render_template, send_from_directory
|
||||||
|
from flask import redirect, url_for
|
||||||
|
from flask_login import logout_user
|
||||||
|
|
||||||
|
from suchwowx import config
|
||||||
|
|
||||||
|
|
||||||
|
bp = Blueprint('meta', 'meta')
|
||||||
|
|
||||||
|
@bp.route('/uploads/<path:filename>')
|
||||||
|
def uploaded_file(filename):
|
||||||
|
"""
|
||||||
|
Retrieve an uploaded file from uploads directory.
|
||||||
|
"""
|
||||||
|
return send_from_directory(f'{config.DATA_FOLDER}/uploads', filename)
|
||||||
|
|
||||||
|
@bp.route('/about')
|
||||||
|
def about():
|
||||||
|
return render_template('about.html')
|
||||||
|
|
||||||
|
@bp.route('/disconnect')
|
||||||
|
def disconnect():
|
||||||
|
logout_user()
|
||||||
|
return redirect(url_for('meme.index'))
|
Loading…
Reference in New Issue