Source code for psnawp_api.core.authenticator

from __future__ import annotations

import time
from typing import Any
from urllib.parse import urlparse, parse_qs

import requests

from psnawp_api.core import psnawp_exceptions
from psnawp_api.utils.endpoints import BASE_PATH, API_PATH
from psnawp_api.utils.misc import create_logger


[docs]class Authenticator: """Provides an interface for PSN Authentication.""" __PARAMS = { "CLIENT_ID": "ac8d161a-d966-4728-b0ea-ffec22f69edc", "SCOPE": "psn:clientapp psn:mobile.v1", "REDIRECT_URI": "com.playstation.PlayStationApp://redirect", } __AUTH_HEADER = {"Authorization": "Basic YWM4ZDE2MWEtZDk2Ni00NzI4LWIwZWEtZmZlYzIyZjY5ZWRjOkRFaXhFcVhYQ2RYZHdqMHY="}
[docs] def __init__(self, npsso_cookie: str): """Represents a single authentication to PSN API. :param npsso_cookie: npsso cookie obtained from PSN website. :raises: ``PSNAWPAuthenticationError`` If npsso code is expired or is incorrect. """ self._npsso_token = npsso_cookie self._auth_properties: dict[str, Any] = {} self._authenticate() self._authenticator_logger = create_logger(__file__)
[docs] def obtain_fresh_access_token(self) -> Any: """Gets a new access token from refresh token. :returns: access token """ if self._auth_properties["access_token_expires_at"] > time.time(): return self._auth_properties["access_token"] data = { "refresh_token": self._auth_properties["refresh_token"], "grant_type": "refresh_token", "scope": Authenticator.__PARAMS["SCOPE"], "token_format": "jwt", } response = requests.post( f"{BASE_PATH['base_uri']}{API_PATH['access_token']}", headers=Authenticator.__AUTH_HEADER, data=data, ) self._auth_properties = response.json() self._auth_properties["access_token_expires_at"] = self._auth_properties["expires_in"] + time.time() if self._auth_properties["refresh_token_expires_in"] <= 60 * 60 * 24 * 3: self._authenticator_logger.warning("Warning: Your refresh token is going to expire in less than 3 days. Please renew you npsso token!") return self._auth_properties["access_token"]
[docs] def oauth_token(self, code: str) -> None: """Obtain the access token using oauth code for the first time, after this the access token is obtained via refresh token. :param code: Code obtained using npsso code. """ data = { "code": code, "grant_type": "authorization_code", "redirect_uri": Authenticator.__PARAMS["REDIRECT_URI"], "scope": Authenticator.__PARAMS["SCOPE"], "token_format": "jwt", } response = requests.post( f"{BASE_PATH['base_uri']}{API_PATH['access_token']}", headers=Authenticator.__AUTH_HEADER, data=data, ) self._auth_properties = response.json() self._auth_properties["access_token_expires_at"] = self._auth_properties["expires_in"] + time.time() if self._auth_properties["refresh_token_expires_in"] <= 60 * 60 * 24 * 3: self._authenticator_logger.warning("Warning: Your refresh token is going to expire in less than 3 days. Please renew you npsso token!")
def _authenticate(self) -> None: """Authenticate using the npsso code provided in the constructor. Obtains the access code and the refresh code. Access code lasts about 1 hour. While the refresh code lasts about 2 months. After 2 months a new npsso code is needed. :raises: ``PSNAWPAuthenticationError`` If authentication is not successful. """ cookies = {"Cookie": f"npsso={self._npsso_token}"} params = { "access_type": "offline", "client_id": Authenticator.__PARAMS["CLIENT_ID"], "scope": Authenticator.__PARAMS["SCOPE"], "redirect_uri": Authenticator.__PARAMS["REDIRECT_URI"], "response_type": "code", } response = requests.get( f"{BASE_PATH['base_uri']}{API_PATH['oauth_code']}", headers=cookies, params=params, allow_redirects=False, ) response.raise_for_status() location_url = response.headers["location"] parsed_url = urlparse(location_url) parsed_query = parse_qs(parsed_url.query) if "error" in parsed_query.keys(): if "4165" in parsed_query["error_code"]: raise psnawp_exceptions.PSNAWPAuthenticationError("Your npsso code has expired or is incorrect. Please generate a new code!") else: raise psnawp_exceptions.PSNAWPAuthenticationError("Something went wrong while authenticating") self.oauth_token(parsed_query["code"][0])