~jae/dn0magik-mc

8a7ba90b0ac753a9308f08fbe2da62b7b531ab64 — Jae Lo Presti (DN0) 1 year, 8 months ago 26ac553
API: Working login / register

Fixes:
 - Patch bug in the password hash storage that would store the function
   instead of the the actual hash

Additions:
 - Login endpoint (/authenticate)
3 files changed, 125 insertions(+), 9 deletions(-)

M src/api/userapi.py
M src/utils/dbutils.py
A src/utils/secretsutil.py
M src/api/userapi.py => src/api/userapi.py +71 -2
@@ 1,7 1,14 @@
from flask import Blueprint, jsonify, request
from uuid import uuid4

from utils.dbutils import get_uuid_from_username, register_user
from flask import Blueprint, jsonify, request

from utils.dbutils import (
    get_uuid_from_username,
    register_user,
    verify_login,
    get_user_remoteid,
)
from utils.secretsutil import encode_auth_jwt

user_api = Blueprint("user_api", __name__)



@@ 42,3 49,65 @@ def api_v1_register():
        )

    return jsonify({"status": "ok", "uuid": uuid})


@user_api.post("/authenticate")
def userapi_auth():
    data = request.json

    if not data:
        res = {
            "error": "ForbiddenOperationException",
            "errorMessage": "No data found in request",
        }
        return jsonify(res), 400

    # PARSE DATA
    # WE WILL IGNORE THE "AGENT" PART BECAUSE WHO CARES ABOUT "SCROLLS"
    username = data.get("username")
    password = data.get("password")
    client_token = data.get("clientToken")  # OPTIONAL

    if not username or not password:
        res = {
            "error": "ForbiddenOperationException",
            "errorMessage": "Missing username or password",
        }
        return jsonify(res), 400

    print(data)

    # VERIFY LOGIN
    if not verify_login(username, password):
        res = {
            "error": "ForbiddenOperationException",
            "errorMessage": "Incorrect login or password",
        }
        return jsonify(res), 400

    # CLIENT TOKEN & RES GENERATION LOGIC
    if not client_token:
        client_token = str(uuid4())

    auth_jwt = encode_auth_jwt(username, client_token)
    user_uuid = get_uuid_from_username(username)
    remoteid = get_user_remoteid(username)

    res = {
        "user": {
            "username": username,
            "properties": [
                {"name": "preferredLanguage", "value": "en-us"},
                {"name": "registrationCountry", "value": "SAV"},
            ],
            "id": 1,
        },
        "clientToken": client_token,
        "accessToken": auth_jwt,
        "availableProfiles": [{"name": username, "id": user_uuid}],
        "selectedProfile": {"name": username, "id": user_uuid},
    }

    print(res)

    return jsonify(res), 200

M src/utils/dbutils.py => src/utils/dbutils.py +25 -7
@@ 1,7 1,8 @@
from datetime import datetime, timedelta
from uuid import uuid4
from base64 import b64encode

from bcrypt import gensalt, hashpw
from bcrypt import gensalt, hashpw, checkpw

from utils.redisutil import cache_val, get_val
from utils.db import get_object, Users, UsernameHistory


@@ 52,16 53,16 @@ def get_all_users_count():


def hash_password(password: str):
    salt = gensalt()
    hashed = hashpw(str.encode(password), salt)
    salt = gensalt(10)
    hashed = hashpw(password.encode("utf-8"), salt)

    return hashed.decode()
    return str(hashed.decode())


def register_user(username: str, password: str):
    # USER CHECK
    existing_user = Users.select().where(Users.username == username).count()
    if existing_user > 0:
    existing_user = get_object(Users, username=username)
    if existing_user:
        return None

    # DO REGISTRATION


@@ 71,7 72,7 @@ def register_user(username: str, password: str):

    user = Users.create(
        username=username,
        password=hash_password,
        password=hashed_pass,
        uuid=uuid,
        registered_on=register_date,
    )


@@ 84,3 85,20 @@ def register_user(username: str, password: str):
    uh.save()

    return uuid


def verify_login(username: str, password: str):
    user_exists = get_object(Users, username=username)
    if not user_exists:
        return False

    check = hashpw(str.encode(password), str.encode(user_exists.password))
    return check


def get_user_remoteid(username: str):
    user_exists = get_object(Users, username=username)
    if not user_exists:
        return None

    return str(b64encode(str(user_exists.id).encode("ascii")))

A src/utils/secretsutil.py => src/utils/secretsutil.py +29 -0
@@ 0,0 1,29 @@
from os import environ
from base64 import b64encode
from time import time

from jwt import encode, decode

from utils.redisutil import cache_val, get_val

# ENV
JWT_SECRET_KEY = environ.get("JWT_SECRET_KEY")


def encode_auth_jwt(username: str, client_token: str):
    # IF THERE IS A GOD, HE WEEPS
    user_base = str(b64encode(username.encode("ascii")))
    token_base = str(b64encode(client_token.encode("ascii")))

    payload = {
        "sub": user_base,
        "yggt": token_base,
        "spr": user_base,
        "iss": "dn0magik",
        "exp": int(time()) + 86400 * 180,
        "iat": int(time()),
    }

    encoded_jwt = encode(payload, JWT_SECRET_KEY, algorithm="HS256")

    return encoded_jwt