summaryrefslogtreecommitdiff
path: root/backend/src/esi/shared_flow.py
blob: 1d89fb137bdca1e6a7cd5b737695cc7e6021b057 (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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
"""
Contains shared OAuth 2.0 flow functions for EVE Online examples.

This module contains shared functions used between the two different OAuth 2.0
flows recommended for web-based and mobile/desktop applications. The functions
found here are used by the OAuth 2.0 examples contained in this project.
"""

import requests

from .validate_jwt import validate_eve_jwt


def print_auth_url(client_id: str, code_challenge: str = "") -> None:
    """
    Prints the URL to redirect users to for OAuth authorization.

    Args:
        client_id: The client ID of an EVE SSO application.
        code_challenge: A PKCE code challenge (optional).
    """
    base_auth_url = "https://login.eveonline.com/v2/oauth/authorize/"
    params = {
        "response_type": "code",
        "redirect_uri": "https://localhost/callback/",
        "client_id": client_id,
        "state": "unique-state",
    }

    # Define the required scopes
    scopes = [
        "esi-assets.read_assets.v1",
        "esi-location.read_location.v1",
        "esi-location.read_online.v1",
        "esi-ui.open_window.v1",
        "esi-ui.write_waypoint.v1",
    ]
    scope_param = "%20".join(scopes)
    params["scope"] = scope_param

    if code_challenge:
        params["code_challenge"] = code_challenge
        params["code_challenge_method"] = "S256"

    # Construct the full authorization URL
    request = requests.Request('GET', base_auth_url, params=params)
    prepared_request = request.prepare()
    full_auth_url = prepared_request.url

    print(
        "\nOpen the following link in your browser:\n\n"
        f"{full_auth_url}\n\n"
        "Once you have logged in as a character, you will be redirected to "
        "https://localhost/callback/."
    )


def send_token_request(form_values: dict[str, str], add_headers: dict[str, str] = {}) -> requests.Response:
    """
    Sends a request for an authorization token to the EVE SSO.

    Args:
        form_values: A dictionary containing the form-encoded values to send with the request.
        add_headers: A dictionary containing additional headers to send (optional).

    Returns:
        A requests.Response object containing the server's response.
    """
    headers = {
        "Content-Type": "application/x-www-form-urlencoded",
        "Host": "login.eveonline.com",
    }
    headers.update(add_headers)

    response = requests.post(
        "https://login.eveonline.com/v2/oauth/token",
        data=form_values,
        headers=headers,
    )

    print(
        f"Request sent to URL {response.url} with headers {headers} and form values: {form_values}\n"
    )
    response.raise_for_status()

    return response


def handle_sso_token_response(sso_response: requests.Response) -> None:
    """
    Handles the authorization code response from the EVE SSO.

    Args:
        sso_response: A requests.Response object from the EVE SSO /v2/oauth/token endpoint.
    """
    if sso_response.status_code == 200:
        data = sso_response.json()
        access_token = data.get("access_token")

        print("\nVerifying access token JWT...")

        jwt = validate_eve_jwt(access_token)
        character_id = jwt["sub"].split(":")[2]
        character_name = jwt["name"]
        blueprint_url = f"https://esi.evetech.net/latest/characters/{character_id}/blueprints/"

        print(
            f"\nSuccess! Here is the payload received from the EVE SSO: {data}\n"
            "You can use the access token to make an authenticated request to "
            f"{blueprint_url}"
        )

        input("\nPress Enter to have this program make the request for you:")

        headers = {
            "Authorization": f"Bearer {access_token}"
        }

        res = requests.get(blueprint_url, headers=headers)
        print(
            f"\nMade request to {blueprint_url} with headers: {res.request.headers}"
        )
        res.raise_for_status()

        blueprints = res.json()
        print(f"\n{character_name} has {len(blueprints)} blueprints.")
    else:
        print(
            "\nSomething went wrong! Please re-read the comments at the top of this "
            "file and ensure you completed all the prerequisites, then try again. "
            "Here's some debug info to help you out:"
        )
        request_info = sso_response.request
        print(
            f"\nSent request with URL: {request_info.url}\n"
            f"Body: {request_info.body}\n"
            f"Headers: {request_info.headers}"
        )
        print(f"\nSSO response code is: {sso_response.status_code}")
        try:
            error_data = sso_response.json()
            print(f"\nSSO response JSON is: {error_data}")
        except ValueError:
            print("\nNo JSON response received from the SSO.")