Python SDK

Official OAuth42 SDK for Python. Works with Django, Flask, FastAPI, and any Python web framework.

Installation

Install the OAuth42 SDK using pip:

pip install oauth42

Requirements

Python 3.8 or higher. The SDK uses modern Python features including type hints for better IDE support.

Quick Start

1. Initialize the Client

from oauth42 import OAuth42Client

client = OAuth42Client(
    client_id="your_client_id",
    client_secret="your_client_secret",  # Optional for server-side
    redirect_uri="https://yourapp.com/callback",
    issuer="https://oauth42.com",  # Optional
)

2. Start Authorization Flow

# Generate authorization URL with PKCE
auth_data = client.authorize(
    scope=["openid", "profile", "email"],
    response_type="code"
)

# Store code_verifier and state in session
session['code_verifier'] = auth_data['code_verifier']
session['state'] = auth_data['state']

# Redirect user to authorization URL
return redirect(auth_data['url'])

3. Handle Callback

# Parse callback parameters
code = request.args.get('code')
state = request.args.get('state')

# Verify state
if state != session.get('state'):
    raise ValueError("Invalid state parameter")

# Exchange code for tokens
tokens = client.exchange_code_for_tokens(
    code=code,
    code_verifier=session.get('code_verifier')
)

# Access tokens
print(f"Access token: {tokens['access_token']}")
print(f"Refresh token: {tokens['refresh_token']}")
print(f"ID token: {tokens['id_token']}")

Configuration

The OAuth42Client class accepts the following parameters:

client_idrequired

Your application's client ID from the OAuth42 dashboard.

redirect_urirequired

The redirect URI registered in your OAuth42 application settings.

client_secretoptional

Your application's client secret. Required for confidential clients.

issueroptional

The OAuth42 issuer URL. Defaults to https://oauth42.com

timeoutoptional

Request timeout in seconds. Defaults to 30

Authentication Flows

Authorization Code Flow with PKCE

from oauth42 import OAuth42Client

client = OAuth42Client(
    client_id=os.getenv("OAUTH42_CLIENT_ID"),
    client_secret=os.getenv("OAUTH42_CLIENT_SECRET"),
    redirect_uri="http://localhost:5000/callback"
)

# Start authorization
auth_data = client.authorize(
    scope=["openid", "profile", "email"],
    response_type="code"
)

# Store in session
session['code_verifier'] = auth_data['code_verifier']
session['state'] = auth_data['state']

# Redirect
return redirect(auth_data['url'])

# --- After callback ---

# Exchange code for tokens
tokens = client.exchange_code_for_tokens(
    code=request.args.get('code'),
    code_verifier=session.get('code_verifier')
)

Client Credentials Flow

# Server-to-server authentication
tokens = client.client_credentials(
    scope=["api:read", "api:write"]
)

access_token = tokens['access_token']

Refresh Token Flow

# Refresh expired access token
new_tokens = client.refresh_tokens(
    refresh_token=stored_refresh_token
)

print(f"New access token: {new_tokens['access_token']}")
print(f"New refresh token: {new_tokens['refresh_token']}")

User Management

Get User Info

user_info = client.get_user_info(access_token)

print(f"User ID: {user_info['sub']}")
print(f"Name: {user_info['name']}")
print(f"Email: {user_info['email']}")
print(f"Email verified: {user_info['email_verified']}")

Update User Profile

client.update_user(
    access_token,
    name="John Doe",
    picture="https://example.com/avatar.jpg"
)

Change Password

client.change_password(
    access_token,
    current_password="old_password",
    new_password="new_secure_password"
)

Token Management

Validate Token

is_valid = client.validate_token(access_token)

if is_valid:
    print("Token is valid")
else:
    print("Token is expired or invalid")

Introspect Token

token_info = client.introspect_token(access_token)

print(f"Active: {token_info['active']}")
print(f"Expires at: {token_info['exp']}")
print(f"Scopes: {token_info['scope']}")
print(f"Client ID: {token_info['client_id']}")

Revoke Token

# Revoke access token or refresh token
client.revoke_token(token)

print("Token revoked successfully")

Multi-Factor Authentication

Setup MFA

mfa_setup = client.setup_mfa(access_token)

print(f"Secret: {mfa_setup['secret']}")
print(f"QR Code: {mfa_setup['qr_code']}")
print(f"Backup codes: {mfa_setup['backup_codes']}")

# User scans QR code with authenticator app
# Then verify with first code
client.verify_mfa_setup(access_token, code="123456")

Verify MFA Code

# During login, after receiving MFA challenge
tokens = client.verify_mfa(
    mfa_token=mfa_token,
    code="123456"
)

print(f"Access token: {tokens['access_token']}")

Disable MFA

client.disable_mfa(
    access_token,
    password="user_password"
)

Error Handling

The SDK raises typed exceptions that you can catch and handle:

from oauth42 import OAuth42Client, OAuth42Error

try:
    tokens = client.exchange_code_for_tokens(
        code=auth_code,
        code_verifier=verifier
    )
except OAuth42Error as error:
    print(f"OAuth42 Error: {error.message}")
    print(f"Error code: {error.code}")
    print(f"Status: {error.status}")

    if error.code == "invalid_grant":
        # Authorization code expired or invalid
        print("Authorization code is invalid")
    elif error.code == "invalid_client":
        # Client credentials are wrong
        print("Client authentication failed")
    elif error.code == "access_denied":
        # User denied authorization
        print("User denied access")
    else:
        print(f"Unknown error: {error.code}")
except Exception as error:
    # Network or other error
    print(f"Unexpected error: {error}")

Common Error Codes

invalid_grant

Authorization code or refresh token is invalid or expired

invalid_client

Client authentication failed (wrong credentials)

invalid_request

Request is missing required parameters or malformed

unauthorized_client

Client is not authorized for this grant type

Framework Integration

Flask

from flask import Flask, redirect, request, session, url_for
from oauth42 import OAuth42Client
import os

app = Flask(__name__)
app.secret_key = os.getenv("SECRET_KEY")

client = OAuth42Client(
    client_id=os.getenv("OAUTH42_CLIENT_ID"),
    client_secret=os.getenv("OAUTH42_CLIENT_SECRET"),
    redirect_uri="http://localhost:5000/callback"
)

@app.route("/login")
def login():
    auth_data = client.authorize(
        scope=["openid", "profile", "email"]
    )
    session['code_verifier'] = auth_data['code_verifier']
    session['state'] = auth_data['state']
    return redirect(auth_data['url'])

@app.route("/callback")
def callback():
    code = request.args.get('code')
    state = request.args.get('state')

    if state != session.get('state'):
        return "Invalid state", 400

    tokens = client.exchange_code_for_tokens(
        code=code,
        code_verifier=session.get('code_verifier')
    )

    session['access_token'] = tokens['access_token']
    return redirect(url_for('dashboard'))

@app.route("/dashboard")
def dashboard():
    if 'access_token' not in session:
        return redirect(url_for('login'))

    user = client.get_user_info(session['access_token'])
    return f"Welcome, {user['name']}!"

if __name__ == "__main__":
    app.run(debug=True)

FastAPI

from fastapi import FastAPI, Request, Depends
from fastapi.responses import RedirectResponse
from oauth42 import OAuth42Client
import os

app = FastAPI()

client = OAuth42Client(
    client_id=os.getenv("OAUTH42_CLIENT_ID"),
    client_secret=os.getenv("OAUTH42_CLIENT_SECRET"),
    redirect_uri="http://localhost:8000/callback"
)

@app.get("/login")
async def login(request: Request):
    auth_data = client.authorize(
        scope=["openid", "profile", "email"]
    )
    request.session['code_verifier'] = auth_data['code_verifier']
    request.session['state'] = auth_data['state']
    return RedirectResponse(auth_data['url'])

@app.get("/callback")
async def callback(request: Request, code: str, state: str):
    if state != request.session.get('state'):
        return {"error": "Invalid state"}

    tokens = client.exchange_code_for_tokens(
        code=code,
        code_verifier=request.session.get('code_verifier')
    )

    request.session['access_token'] = tokens['access_token']
    return RedirectResponse("/dashboard")

@app.get("/dashboard")
async def dashboard(request: Request):
    access_token = request.session.get('access_token')
    if not access_token:
        return RedirectResponse("/login")

    user = client.get_user_info(access_token)
    return {"user": user}

Django

# views.py
from django.shortcuts import redirect
from django.http import JsonResponse
from oauth42 import OAuth42Client
import os

client = OAuth42Client(
    client_id=os.getenv("OAUTH42_CLIENT_ID"),
    client_secret=os.getenv("OAUTH42_CLIENT_SECRET"),
    redirect_uri="http://localhost:8000/callback"
)

def login(request):
    auth_data = client.authorize(
        scope=["openid", "profile", "email"]
    )
    request.session['code_verifier'] = auth_data['code_verifier']
    request.session['state'] = auth_data['state']
    return redirect(auth_data['url'])

def callback(request):
    code = request.GET.get('code')
    state = request.GET.get('state')

    if state != request.session.get('state'):
        return JsonResponse({"error": "Invalid state"}, status=400)

    tokens = client.exchange_code_for_tokens(
        code=code,
        code_verifier=request.session.get('code_verifier')
    )

    request.session['access_token'] = tokens['access_token']
    return redirect('/dashboard')

def dashboard(request):
    access_token = request.session.get('access_token')
    if not access_token:
        return redirect('/login')

    user = client.get_user_info(access_token)
    return JsonResponse({"user": user})

Best Practices

Use Environment Variables

Store sensitive credentials in environment variables, never hardcode them in source code.

Always Use PKCE

PKCE is enabled by default and provides additional security against authorization code interception attacks.

Implement Token Refresh

Automatically refresh access tokens before they expire to maintain seamless user sessions.

Validate State Parameter

Always verify the state parameter matches to prevent CSRF attacks during the OAuth flow.

Handle Errors Gracefully

Catch OAuth42Error exceptions and provide meaningful feedback to users while logging details for debugging.

Use Type Hints

The SDK includes full type hints. Use them with mypy or Pyright for better type safety and IDE support.

Next Steps