Skip to main content

luduvo.py

A Python library for interacting with the Luduvo API.

It provides a simple interface for Luduvo API requests, OAuth 2.0 authentication, PKCE, OAuth application management, token exchange, and authenticated user information.

Features

  • Luduvo API client
  • Bearer token authentication
  • OAuth application creation
  • OAuth application listing
  • OAuth application deletion
  • OAuth 2.0 authorization
  • PKCE authentication
  • Authorization code exchange
  • Authenticated user information
  • Simple Python API
  • Automatic JSON request handling

Installation

Install the package from PyPI:

python -m pip install luduvo.py

Upgrade to the latest version:

python -m pip install --upgrade luduvo.py

Import the package:

from luduvo import Luduvo

Requirements

  • Python 3.9+
  • requests

requests is installed automatically when installing the package.

Basic Usage

Create a client without authentication:

from luduvo import Luduvo

client = Luduvo()

Or provide a Luduvo API token:

from luduvo import Luduvo

client = Luduvo("YOUR_LUDUVO_TOKEN")

The API base URL is:

https://api.luduvo.com

When a token is provided, authenticated requests automatically use:

Authorization: Bearer YOUR_LUDUVO_TOKEN

OAuth

luduvo.py provides helpers for the Luduvo OAuth 2.0 authorization flow with PKCE.

The OAuth flow is:

Create OAuth Application
         │
         ▼
Generate PKCE verifier
         │
         ▼
Generate PKCE challenge
         │
         ▼
Create authorization URL
         │
         ▼
User signs into Luduvo
         │
         ▼
User authorizes application
         │
         ▼
Luduvo redirects to callback
         │
         ▼
Receive authorization code
         │
         ▼
Exchange code + verifier
         │
         ▼
Receive access token
         │
         ▼
Request /oauth/userinfo

OAuth Application Management

Creating an OAuth Application

OAuth applications can be created through the Luduvo API.

from luduvo import Luduvo

client = Luduvo("YOUR_LUDUVO_TOKEN")

app = client.oauth.create(
    name="My Luduvo App",
    description="An application using Luduvo OAuth.",
    redirect_uris=[
        "http://localhost:3000/callback"
    ],
    is_confidential=True
)

print(app)

A response will contain information similar to:

{
    "id": 5,
    "client_id": "ldv_b748212bcc097120d2bfac589a6977dd",
    "name": "My Luduvo App",
    "description": "An application using Luduvo OAuth.",
    "redirect_uris": [
        "http://localhost:3000/callback"
    ],
    "is_confidential": true,
    "created_at": 1790751617,
    "updated_at": 1790751617
}

create_oauth()

create_oauth() is an alias for create().

from luduvo import Luduvo

client = Luduvo("YOUR_LUDUVO_TOKEN")

app = client.oauth.create_oauth(
    name="My Luduvo App",
    description="An application using Luduvo OAuth.",
    redirect_uris=[
        "http://localhost:3000/callback"
    ],
    is_confidential=True
)

Listing OAuth Applications

List all OAuth applications belonging to the authenticated account.

from luduvo import Luduvo

client = Luduvo("YOUR_LUDUVO_TOKEN")

apps = client.oauth.list()

for app in apps:
    print("Name:", app["name"])
    print("Client ID:", app["client_id"])
    print()

Deleting an OAuth Application

Delete an OAuth application using its client_id.

from luduvo import Luduvo

client = Luduvo("YOUR_LUDUVO_TOKEN")

client.oauth.delete(
    "ldv_b748212bcc097120d2bfac589a6977dd"
)

The numeric application ID should not be used for deletion.

Use:

client_id

instead of:

id

PKCE

PKCE values can be generated with:

from luduvo import Luduvo

client = Luduvo()

pkce = client.oauth.create_pkce()

print("Code verifier:", pkce["code_verifier"])
print("Code challenge:", pkce["code_challenge"])

The returned object contains:

{
    "code_verifier": "...",
    "code_challenge": "..."
}

The library generates the challenge using SHA-256 and Base64 URL encoding.

The authorization request uses:

code_challenge_method=S256

The code_verifier must be kept until the authorization code is exchanged for an access token.

Authorization

Creating an Authorization URL

Generate a PKCE pair:

pkce = client.oauth.create_pkce()

Then create the authorization URL:

auth_url, state = client.oauth.authorize_url(
    client_id="ldv_b748212bcc097120d2bfac589a6977dd",
    redirect_uri="http://localhost:3000/callback",
    code_challenge=pkce["code_challenge"]
)

print(auth_url)
print(state)

The generated authorization URL contains parameters such as:

response_type=code
client_id=...
redirect_uri=...
scope=identify
code_challenge=...
code_challenge_method=S256
state=...

The state value should be stored and verified when the OAuth callback is received.

Authorization URL Parameters

authorize_url() accepts:

client.oauth.authorize_url(
    client_id,
    redirect_uri,
    code_challenge,
    state=None,
    scope="identify",
    prompt=None
)

client_id

The OAuth application's client ID.

Example:

client_id="ldv_b748212bcc097120d2bfac589a6977dd"

redirect_uri

The callback URL registered with the OAuth application.

Example:

redirect_uri="http://localhost:3000/callback"

The URI must match one of the application's registered redirect URIs.

code_challenge

The PKCE challenge generated by:

pkce = client.oauth.create_pkce()

pkce["code_challenge"]

state

An optional state value.

If omitted, the library automatically generates a secure random state.

Example:

auth_url, state = client.oauth.authorize_url(
    client_id="ldv_b748212bcc097120d2bfac589a6977dd",
    redirect_uri="http://localhost:3000/callback",
    code_challenge=pkce["code_challenge"]
)

scope

The OAuth scope.

The default scope is:

identify

Example:

auth_url, state = client.oauth.authorize_url(
    client_id="ldv_b748212bcc097120d2bfac589a6977dd",
    redirect_uri="http://localhost:3000/callback",
    code_challenge=pkce["code_challenge"],
    scope="identify"
)

prompt

An optional prompt value can be passed to the authorization endpoint.

auth_url, state = client.oauth.authorize_url(
    client_id="ldv_b748212bcc097120d2bfac589a6977dd",
    redirect_uri="http://localhost:3000/callback",
    code_challenge=pkce["code_challenge"],
    prompt="login"
)

Token Exchange

After the user authorizes the application, Luduvo redirects to the registered callback URL.

The callback contains an authorization code.

Exchange the code for an access token:

token = client.oauth.token(
    client_id="ldv_b748212bcc097120d2bfac589a6977dd",
    client_secret="YOUR_CLIENT_SECRET",
    redirect_uri="http://localhost:3000/callback",
    code="AUTHORIZATION_CODE",
    code_verifier=pkce["code_verifier"]
)

print(token)

The token request sends:

{
    "grant_type": "authorization_code",
    "client_id": "...",
    "client_secret": "...",
    "redirect_uri": "...",
    "code": "...",
    "code_verifier": "..."
}

The authorization code can only be used according to the OAuth server's code expiration and reuse rules.

User Information

Once an OAuth access token has been received, user information can be requested using:

user = client.oauth.userinfo(
    access_token=token["access_token"]
)

print(user)

A userinfo response looks similar to:

{
    "avatar_url": "https://assets.luduvo.com/headshots/5/0197c88cd526d6a5f3d7ead056d1f12fba6df6a1b2dd9454fe022a978ab88970.png",
    "created_at": 1774378965,
    "display_name": "dargy",
    "id": 5,
    "sub": "5",
    "username": "dargs"
}

The available fields include:

Field Description
id Luduvo user ID
sub OAuth subject identifier
username Luduvo username
display_name User display name
avatar_url User avatar URL
created_at Account creation timestamp

Using the Access Token

The access token can be passed directly to userinfo():

user = client.oauth.userinfo(
    access_token=token["access_token"]
)

Alternatively, assign the access token to the client:

client.token = token["access_token"]

Then:

user = client.oauth.userinfo()

This allows userinfo() to use the client's stored token.

Complete OAuth Example

The following example demonstrates the complete OAuth flow.

from luduvo import Luduvo

client = Luduvo("YOUR_LUDUVO_TOKEN")

# Generate PKCE values
pkce = client.oauth.create_pkce()

# Create the authorization URL
auth_url, state = client.oauth.authorize_url(
    client_id="YOUR_CLIENT_ID",
    redirect_uri="http://localhost:3000/callback",
    code_challenge=pkce["code_challenge"]
)

print("Open this URL in your browser:")
print(auth_url)

# After the user authorizes the application,
# your callback receives the authorization code.

code = input("Authorization code: ")

# Exchange the authorization code for an access token
token = client.oauth.token(
    client_id="YOUR_CLIENT_ID",
    client_secret="YOUR_CLIENT_SECRET",
    redirect_uri="http://localhost:3000/callback",
    code=code,
    code_verifier=pkce["code_verifier"]
)

print("Access token received.")

# Request user information
user = client.oauth.userinfo(
    access_token=token["access_token"]
)

print("User ID:", user["id"])
print("Username:", user["username"])
print("Display Name:", user["display_name"])

Flask Callback Example

A minimal Flask application can be used to handle the OAuth callback.

from flask import Flask, redirect, request

from luduvo import Luduvo

app = Flask(__name__)

client = Luduvo("YOUR_LUDUVO_TOKEN")

pkce = client.oauth.create_pkce()

CLIENT_ID = "YOUR_CLIENT_ID"
CLIENT_SECRET = "YOUR_CLIENT_SECRET"
REDIRECT_URI = "http://127.0.0.1:3000/callback"

auth_url, state = client.oauth.authorize_url(
    client_id=CLIENT_ID,
    redirect_uri=REDIRECT_URI,
    code_challenge=pkce["code_challenge"],
    state=state
)

@app.route("/")
def index():
    return f'<a href="{auth_url}">Login with Luduvo</a>'

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

    if not code:
        return "Missing authorization code.", 400

    if returned_state != state:
        return "Invalid state.", 400

    token = client.oauth.token(
        client_id=CLIENT_ID,
        client_secret=CLIENT_SECRET,
        redirect_uri=REDIRECT_URI,
        code=code,
        code_verifier=pkce["code_verifier"]
    )

    user = client.oauth.userinfo(
        access_token=token["access_token"]
    )

    return {
        "user": user,
        "token": token
    }

if __name__ == "__main__":
    app.run(
        host="127.0.0.1",
        port=3000,
        debug=True
    )

For a real application, store the PKCE verifier and OAuth state in the user's server-side session rather than using global variables.

API Endpoints

The library currently uses these Luduvo OAuth endpoints:

Method Endpoint Purpose
POST /oauth/apps Create OAuth application
GET /oauth/apps List OAuth applications
DELETE /oauth/apps/{client_id} Delete OAuth application
GET /oauth/authorize Authorize OAuth application
POST /oauth/token Exchange authorization code
GET /oauth/userinfo Get authenticated user

The complete base URL is:

https://api.luduvo.com

Authentication

Application-management requests use a Luduvo API token:

Authorization: Bearer YOUR_LUDUVO_TOKEN

OAuth userinfo requests use an OAuth access token:

Authorization: Bearer YOUR_ACCESS_TOKEN

The library automatically sets the appropriate headers for requests made through the client.

Error Handling

API errors raise a RuntimeError.

Example:

from luduvo import Luduvo

client = Luduvo("YOUR_LUDUVO_TOKEN")

try:
    apps = client.oauth.list()
except RuntimeError as error:
    print(error)

An error contains the HTTP status code and the response returned by the API.

For example:

Luduvo API returned 401: {'error': 'unauthorized'}

Security

Never expose your Luduvo API token or OAuth client secret in frontend JavaScript, public repositories, or client-side applications.

Store secrets in environment variables or another secure secret store.

For example:

import os

from luduvo import Luduvo

client = Luduvo(
    os.getenv("LUDUVO_TOKEN")
)

For OAuth applications, keep the following values private:

  • API tokens
  • OAuth client secrets
  • OAuth access tokens
  • PKCE code verifiers

The authorization state value should also be verified before accepting an OAuth callback.

Environment Variables

A common setup is:

LUDUVO_TOKEN=your_token_here

Python:

import os

from luduvo import Luduvo

client = Luduvo(
    os.getenv("LUDUVO_TOKEN")
)

On Windows PowerShell:

$env:LUDUVO_TOKEN="YOUR_TOKEN_HERE"

Then run:

python main.py

Package Structure

The project structure is:

luduvo.py/
├── luduvo/
│   ├── __init__.py
│   └── client.py
├── demo/
│   └── test.py
├── pyproject.toml
└── README.md

The package is imported as:

import luduvo

or:

from luduvo import Luduvo

API Client

The main client class is:

from luduvo import Luduvo

client = Luduvo()

The client exposes OAuth functionality through:

client.oauth

Available OAuth methods:

client.oauth.create(...)
client.oauth.create_oauth(...)
client.oauth.delete(...)
client.oauth.list(...)
client.oauth.create_pkce(...)
client.oauth.authorize_url(...)
client.oauth.token(...)
client.oauth.userinfo(...)

Metadata

Release files for luduvo.py 0.1.13

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for luduvo.py 0.1.13
File Size Uploaded
luduvo_py-0.1.13.tar.gz 6.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for luduvo.py 0.1.13
File Interpreter ABI Platform
luduvo_py-0.1.13-py3-none-any.whl Python 3 none any Details

Total release size: 13.4 kB

Release files / luduvo_py-0.1.13.tar.gz

Download URL luduvo_py-0.1.13.tar.gz
Size 6.7 kB
Tags Source
SHA-256 checksum
How to use checksums
4dd4532be2cdb902eb7cb5947fec34578098e4b29ca71deb26b8bae2965bda0d
BLAKE2b-256 checksum
How to use checksums
3b8cd7553a4b8590f423d48371160115e47349b549c3ede2e8488005c74f4150
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / luduvo_py-0.1.13-py3-none-any.whl

Download URL luduvo_py-0.1.13-py3-none-any.whl
Size 6.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
254064b0d67baaeef3cd66232144659d64c490865047308f73c9a07b6a93f0e4
BLAKE2b-256 checksum
How to use checksums
57391af1eee7d144602dfc6d3638c04945a10292186907f1047cb6137e392656
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

This release

0.1.13 This release

2 release files

0.1.11

2 release files

0.1.10

2 release files

0.1.9

2 release files

0.1.8

2 release files

0.1.7

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page