Skip to content

Instantly share code, notes, and snippets.

@devashish2024
Created February 2, 2025 21:45
Show Gist options
  • Save devashish2024/edcdc89330d020822b3e523d2fb4369e to your computer and use it in GitHub Desktop.
Save devashish2024/edcdc89330d020822b3e523d2fb4369e to your computer and use it in GitHub Desktop.
Chess.com OAuth JWT Implementation (Python)
import base64
import hashlib
import secrets
import string
import requests
import urllib.parse
import json
from typing import Optional, Dict, Any
class ChessComOAuthHandler:
def __init__(
self,
client_id: str,
client_secret: Optional[str] = None,
scope: str = "openid profile",
redirect_uri: str = "http://localhost:3000/api/callback/chess"
):
self.client_id = client_id
self.client_secret = client_secret
self.scope = scope
self.redirect_uri = redirect_uri
self.token_endpoint = "https://oauth.chess.com/token"
self.code_verifier = self._generate_code_verifier()
self.code_challenge = self._generate_code_challenge()
def _parse_jwt(self, jwt_token):
parts = jwt_token.split('.')
base64_payload = parts[1]
base64_payload += '=' * ((4 - len(base64_payload) % 4) % 4)
decoded_payload = base64.urlsafe_b64decode(base64_payload).decode('utf-8')
parsed_payload = json.loads(decoded_payload)
return parsed_payload
def _generate_code_verifier(self, length: int = 64) -> str:
allowed_chars = string.ascii_letters + string.digits + "-._~"
return ''.join(secrets.choice(allowed_chars) for _ in range(length))
def _generate_code_challenge(self) -> str:
sha256_hash = hashlib.sha256(self.code_verifier.encode('utf-8')).digest()
code_challenge = base64.urlsafe_b64encode(sha256_hash).decode('utf-8')
return code_challenge.replace('=', '')
def get_auth_url(self, state: Optional[str] = None) -> str:
params = {
'client_id': self.client_id,
'redirect_uri': self.redirect_uri,
'response_type': 'code',
'scope': self.scope,
'code_challenge': self.code_challenge,
'code_challenge_method': 'S256'
}
if state:
params['state'] = state
query = urllib.parse.urlencode(params)
return f"https://oauth.chess.com/authorize?{query}"
def exchange_code_for_tokens(self, authorization_code: str) -> Dict[str, Any]:
data = {
'grant_type': 'authorization_code',
'client_id': self.client_id,
'redirect_uri': self.redirect_uri,
'code': authorization_code,
'code_verifier': self.code_verifier
}
if self.client_secret:
data['client_secret'] = self.client_secret
response = requests.post(self.token_endpoint, data=data)
if response.status_code != 200:
raise Exception(f"Token exchange failed: {response.text}")
return response.json()
def main():
handler = ChessComOAuthHandler(
client_id="" # your token
)
auth_url = handler.get_auth_url(state="random_state_string")
print(f"1. Visit this URL in your browser to authorize:\n{auth_url}\n")
print("2. After authorization, you'll be redirected to your callback URL.")
print("3. Copy the 'code' parameter from the URL and paste it here:")
authorization_code = input("Enter the code: ")
try:
tokens = handler.exchange_code_for_tokens(authorization_code)
jwt = tokens.get('id_token')
parsedJwt = handler._parse_jwt(jwt)
print("\nSuccess!")
print(json.dumps(parsedJwt, indent=4))
except Exception as e:
print(f"\nError: {str(e)}")
if __name__ == "__main__":
main()
@devashish2024
Copy link
Author

devashish2024 commented Feb 2, 2025

This was generated with ChatGPT but seems to work like damn. What I get upon a successful response (this is what is logged:)

{
    "iss": "https://oauth.chess.com",
    "aud": "7380c3a8-dfec-11ef-a0b5-236e523e4849",
    "sub": "45e781e6-0ba0-11ef-a2d1-85f891c9659e",
    "iat": 1738532600.257108,
    "exp": 1738619000.249765,
    "preferred_username": "DevVortex",
    "profile": "https://www.chess.com/member/devvortex",
    "picture": "https://www.chess.com/bundles/web/images/noavatar_l.84a92436.gif",
    "zoneinfo": "Asia/Kolkata",
    "locale": "en_US",
    "user_id": "361502413",
    "country": "India",
    "country_code": "IN",
    "membership": "basic"
}

This is just an example of Chess.com OAuth API because I struggled hard to get to this. Maybe because I didn't though how easy would it be. But for those who need, this is it. (just modify a bit)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment