~jae/dn0magik-mc

d67cba5f774f41702f26f3109d418458a7c679bf — Jae Lo Presti (DN0) 1 year, 7 months ago 6063499
API: fully functioning login & refresh sequence

Added:
 - Refresh endpoint so launchers can refresh the session (/refresh)

Fixed:
 - Moved the main response generators to their own files (playerutil.py)

Minor:
 - Added a test cape URI in the profile response
3 files changed, 117 insertions(+), 44 deletions(-)

M src/api/userapi.py
A src/utils/playerutil.py
M src/utils/secretsutil.py
M src/api/userapi.py => src/api/userapi.py +42 -37
@@ 9,6 9,7 @@ from utils.dbutils import (
    get_user_remoteid,
)
from utils.secretsutil import encode_auth_jwt, check_auth_jwt
from utils.playerutil import generate_login_dict, generate_user_profile

user_api = Blueprint("user_api", __name__)



@@ 75,8 76,6 @@ def userapi_auth():
        }
        return jsonify(res), 400

    print(data)

    # VERIFY LOGIN
    if not verify_login(username, password):
        res = {


@@ 90,23 89,8 @@ def userapi_auth():
        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": remoteid,
        },
        "clientToken": client_token,
        "accessToken": auth_jwt,
        "availableProfiles": [{"name": username, "id": user_uuid}],
        "selectedProfile": {"name": username, "id": user_uuid},
    }

    res = generate_login_dict(username, auth_jwt, client_token)

    return jsonify(res), 200



@@ 124,7 108,7 @@ def userapi_minecraft_profile():

    token = data.replace("Bearer ", "")

    jwt_check = check_auth_jwt(token)
    jwt_check = check_auth_jwt(token, None)

    if not jwt_check:
        res = {


@@ 134,23 118,44 @@ def userapi_minecraft_profile():
        return jsonify(res), 400

    username = jwt_check["sub"]
    uuid = get_uuid_from_username(username)

    # TODO: GET SKINS

    res = {
        "id": uuid,
        "name": username,
        "skins": [
            {
                "id": "8c94945e-d0b4-4df8-97d1-d8d397624f93",
                "state": "ACTIVE",
                # TEMPORARY
                "url": "https://bm.jae.fi/default.png",
                "variant": "SLIM",
            }
        ],
        "CAPES": [],
    }
    res = generate_user_profile(username)

    return jsonify(res), 200


@user_api.post("/refresh")
def userapi_refresh():
    data = request.json
    if not data:
        res = {
            "error": "ForbiddenOperationException",
            "errorMessage": "No data found in request",
        }
        return jsonify(res), 400

    token = data.get("accessToken")
    client_token = data.get("clientToken")

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

    jwt_auth_ok = check_auth_jwt(token, client_token)

    print(jwt_auth_ok)

    if not jwt_auth_ok:
        res = {
            "error": "ForbiddenOperationException",
            "errorMessage": "Invalid token",
        }
        return jsonify(res), 400

    auth_jwt = encode_auth_jwt(jwt_auth_ok, client_token)
    res = generate_login_dict(jwt_auth_ok, auth_jwt, client_token)

    return jsonify(res), 200

A src/utils/playerutil.py => src/utils/playerutil.py +52 -0
@@ 0,0 1,52 @@
from utils.dbutils import get_uuid_from_username, get_user_remoteid


def generate_login_dict(username: str, token: str, client_identifer: str):
    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": remoteid,
        },
        "clientToken": client_identifer,
        "accessToken": token,
        "availableProfiles": [{"name": username, "id": user_uuid}],
        "selectedProfile": {"name": username, "id": user_uuid},
    }

    return res


def generate_user_profile(username: str):
    user_uuid = get_uuid_from_username(username)

    # TODO: SKINHANDLER

    res = {
        "id": user_uuid,
        "name": username,
        "skins": [
            {
                "id": "8c94945e-d0b4-4df8-97d1-d8d397624f93",
                "state": "ACTIVE",
                # TEMPORARY
                "url": "https://bm.jae.fi/default.png",
                "variant": "SLIM",
            }
        ],
        "CAPES": [
            {
                "id": "8c94945e-d0b4-4df8-97d1-d8d397624f93",
                "state": "ACTIVE",
                "url": "https://bm.jae.fi/defaultcape.png",
            }
        ],
    }

    return res

M src/utils/secretsutil.py => src/utils/secretsutil.py +23 -7
@@ 20,7 20,7 @@ def encode_auth_jwt(username: str, client_token: str):
        "yggt": client_token,
        "spr": username,
        "iss": "dn0magik",
        "exp": int(time()) + 86400 * 180,
        "exp": int(time()) + 86400,
        "iat": int(time()),
    }



@@ 29,10 29,26 @@ def encode_auth_jwt(username: str, client_token: str):
    return encoded_jwt


def check_auth_jwt(token: str):
    try:
        data = decode(token, JWT_SECRET_KEY, algorithms=["HS256"])
    except:
        return None
def check_auth_jwt(token: str, client_identifer: str):
    if not client_identifer:
        try:
            data = decode(token, JWT_SECRET_KEY, algorithms=["HS256"])
        except:
            return None

    return data
        return data
    else:
        try:
            data = decode(token, JWT_SECRET_KEY, algorithms=["HS256"])

            ctoken = data["yggt"]
            cuser = data["sub"]

            print(f"{client_identifer} - {ctoken}")

            if client_identifer == ctoken:
                return cuser
            else:
                return False
        except:
            return None