summaryrefslogtreecommitdiff
path: root/backend/src/esi/validate_jwt.py
blob: 054c3674888d0118c44334d3489bb251bb34c466 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
"""
Validates a given JWT access token originating from the EVE SSO.

Prerequisites:
    * Have a Python 3 environment available (possibly using a virtual environment).
    * Run pip install -r requirements.txt with this directory as your root.

This script can be run by executing:

>>> python validate_jwt.py

and providing a JWT access token that you have retrieved from the EVE SSO.
"""

import sys
from typing import Any

import requests
from jose import jwt
from jose.exceptions import ExpiredSignatureError, JWTError

SSO_META_DATA_URL = "https://login.eveonline.com/.well-known/oauth-authorization-server"
JWK_ALGORITHM = "RS256"
JWK_ISSUERS = ("login.eveonline.com", "https://login.eveonline.com")
JWK_AUDIENCE = "EVE Online"


def validate_eve_jwt(token: str) -> dict[str, Any]:
    """
    Validates a JWT access token retrieved from the EVE SSO.

    Args:
        token: A JWT access token originating from the EVE SSO.

    Returns:
        The contents of the validated JWT access token.

    Raises:
        RuntimeError: If required data is missing from the SSO endpoints.
        jose.exceptions.JWTError: If the token is invalid or expired.
    """
    response = requests.get(SSO_META_DATA_URL)
    response.raise_for_status()
    metadata = response.json()

    jwks_uri = metadata.get("jwks_uri")
    if not jwks_uri:
        raise RuntimeError(f"Invalid data received from the SSO metadata endpoint: {metadata}")

    response = requests.get(jwks_uri)
    response.raise_for_status()
    jwks_data = response.json()

    jwk_keys = jwks_data.get("keys")
    if not jwk_keys:
        raise RuntimeError(f"Invalid data received from the JWKS endpoint: {jwks_data}")

    jwk = next((item for item in jwk_keys if item["alg"] == JWK_ALGORITHM), None)
    if not jwk:
        raise RuntimeError(f"No JWK found with algorithm {JWK_ALGORITHM}")

    # Decode and validate the token
    contents = jwt.decode(
        token=token,
        key=jwk,
        algorithms=[JWK_ALGORITHM],
        issuer=JWK_ISSUERS,
        audience=JWK_AUDIENCE,
    )
    return contents


def main() -> None:
    """
    Entry point of the script. Prompts the user for a JWT token and validates it.
    """
    token = input("Enter an access token to validate: ")

    try:
        token_contents = validate_eve_jwt(token)
    except ExpiredSignatureError:
        print("The JWT token has expired.")
        sys.exit(1)
    except JWTError as e:
        print(f"The JWT token is invalid: {e}")
        sys.exit(1)
    except RuntimeError as e:
        print(f"An error occurred: {e}")
        sys.exit(1)
    else:
        print("\nThe contents of the access token are:")
        for key, value in token_contents.items():
            print(f"{key}: {value}")


if __name__ == "__main__":
    main()