Skip to content

core.py

Main class for OSM login.

Source code in osm_login_python/core.py
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def __init__(self, osm_url, client_id, client_secret, secret_key, login_redirect_uri, scope):
    """Set object params and get OAuth2 session."""
    # Strip trailing slash so our URL forming works
    if osm_url.endswith("/"):
        osm_url = osm_url.rstrip("/")

    self.osm_url = osm_url
    self.client_secret = client_secret
    self.secret_key = secret_key
    self.oauth = OAuth2Session(
        client_id,
        redirect_uri=login_redirect_uri,
        scope=scope,
    )

login

login()

Generate login URL from OSM session.

Provides a login URL using the session created by osm client id and redirect uri supplied.

Returns:

Name Type Description
dict dict

{'login_url': 'URL'}

Source code in osm_login_python/core.py
35
36
37
38
39
40
41
42
43
44
45
46
47
48
def login(
    self,
) -> dict:
    """Generate login URL from OSM session.

    Provides a login URL using the session created by osm
    client id and redirect uri supplied.

    Returns:
        dict: {'login_url': 'URL'}
    """
    authorize_url = f"{self.osm_url}/oauth2/authorize/"
    login_url, _ = self.oauth.authorization_url(authorize_url)
    return json.loads(Login(login_url=login_url).model_dump_json())

callback

callback(callback_url)

Performs token exchange between OSM and the callback website.

The returned data will be individually serialized and encoded, so it can only be used from within the same module.

The returned dictionary / JSON will contain: - user_data, containing OSM user details. - oauth_token, containing the OSM OAuth token for API calls.

To use these values, we must run them through the deserialize_data function to deserialize and decode the data using the secret_key variable set.

NOTE 'oauth_token' should not be stored in a frontend and can be discarded if not required. It could, however, be stored in a secure httpOnly cookie in the frontend if required, for subsequent API calls.

Parameters:

Name Type Description Default
callback_url(str)

Absolute URL should be passed which is returned from login_redirect_uri.

required

Returns:

Name Type Description
dict dict

The encoded user details and encoded OSM access token.

Source code in osm_login_python/core.py
 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
def callback(self, callback_url: str) -> dict:
    """Performs token exchange between OSM and the callback website.

    The returned data will be individually serialized and encoded, so it can
    only be used from within the same module.

    The returned dictionary / JSON will contain:
    - `user_data`, containing OSM user details.
    - `oauth_token`, containing the OSM OAuth token for API calls.

    To use these values, we must run them through the `deserialize_data`
    function to deserialize and decode the data using the `secret_key`
    variable set.

    NOTE 'oauth_token' should not be stored in a frontend and can be discarded
    if not required. It could, however, be stored in a secure httpOnly cookie
    in the frontend if required, for subsequent API calls.

    Args:
        callback_url(str): Absolute URL should be passed which
            is returned from login_redirect_uri.

    Returns:
        dict: The encoded user details and encoded OSM access token.
    """
    token_url = f"{self.osm_url}/oauth2/token"
    token = self.oauth.fetch_token(
        token_url,
        authorization_response=callback_url,
        client_secret=self.client_secret,
    )
    # NOTE this is the actual token for the OSM API
    osm_access_token = token.get("access_token")

    user_api_url = f"{self.osm_url}/api/0.6/user/details.json"
    # NOTE the osm token is included automatically in requests from self.oauth
    resp = self.oauth.get(user_api_url)
    if resp.status_code != 200:
        raise ValueError("Invalid response from OSM")
    data = resp.json().get("user")
    user_data = {
        "id": data.get("id"),
        "username": data.get("display_name"),
        "img_url": data.get("img").get("href") if data.get("img") else None,
    }

    encoded_user_data = self._serialize_encode_data(user_data)
    encoded_osm_token = self._serialize_encode_data(osm_access_token)

    # The actual response from this endpoint {"user_data": xxx, "oauth_token": xxx}
    token = Token(user_data=encoded_user_data, oauth_token=encoded_osm_token)
    return token.model_dump()

deserialize_data

deserialize_data(data)

Returns the userdata as JSON from access token.

Can be used for login required decorator or to check the access token provided.

Parameters:

Name Type Description Default
data(str)

The user_data or oauth_token from Auth.callback()

required

Returns:

Name Type Description
deserialized_data dict

A deserialized JSON data.

Source code in osm_login_python/core.py
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
def deserialize_data(self, data: str) -> dict:
    """Returns the userdata as JSON from access token.

    Can be used for login required decorator or to check
    the access token provided.

    Args:
        data(str): The user_data or oauth_token from Auth.callback()

    Returns:
        deserialized_data(dict): A deserialized JSON data.
    """
    deserializer = URLSafeSerializer(self.secret_key)

    try:
        decoded_data = base64.b64decode(data)
    except Exception as e:
        log.error(e)
        log.error(f"Could not decode token: {data}")
        raise ValueError("Could not decode token") from e

    try:
        deserialized_data = deserializer.loads(decoded_data)
    except (SignatureExpired, BadSignature) as e:
        log.error(e)
        raise ValueError("Auth token is invalid or expired") from e

    return deserialized_data

options: show_source: false heading_level: 3