Skip to content

Evrmore Authentication OAuth 2.0 Implementation Guide

Overview

This guide provides comprehensive documentation for the OAuth 2.0 implementation in Evrmore Authentication. It includes setup instructions, client registration, troubleshooting, and best practices based on real-world experience deploying the system.

Table of Contents

Setup and Configuration

Database Configuration

The OAuth system requires a properly configured database. By default, it uses SQLite located at ./evrmore_authentication/data/evrmore_auth.db.

  1. Set the database path in your .env file:
SQLITE_DB_PATH=./evrmore_authentication/data/evrmore_auth.db
  1. Initialize the database:
python3 -m scripts.db_manage init

This command creates the necessary tables for the OAuth system, including: - oauth_clients - Stores registered OAuth client applications - oauth_authorization_codes - Stores temporary authorization codes - oauth_tokens - Stores access and refresh tokens

Running the Authentication Server

Start the authentication server with:

python3 -m scripts.run_api_server --port 8001

The server provides all the OAuth endpoints needed for the authorization flow.

Client Registration

Before using OAuth, you must register a client application. The system provides a dedicated script for this purpose.

Using the Client Registration Script

The scripts/register_oauth_client.py script simplifies OAuth client registration:

python3 scripts/register_oauth_client.py register \
    --name "Your Application Name" \
    --redirects "http://your-app.com/callback" \
    --uri "http://your-app.com" \
    --scopes "profile,email" \
    --response-types "code"

Parameters: - --name: Your application's name (displayed to users) - --redirects: Comma-separated list of allowed redirect URIs - --uri: Your application's main URI (optional) - --scopes: Comma-separated list of scopes your app needs - --response-types: Response types your app supports (usually "code")

Managing Clients

You can list registered clients:

python3 scripts/register_oauth_client.py list

Or delete a client:

python3 scripts/register_oauth_client.py delete --client-id YOUR_CLIENT_ID

Storing Client Credentials

The script will output client credentials that should be stored securely. For example:

Successfully registered new OAuth client:
  ID:             92fae2b2-f231-43a4-945c-8c9f1f737627
  Client ID:      d5fdcc0c-7a88-4cfc-a1ff-6af04b92e9b0
  Client Secret:  XBBMQM9kppmbCyT5opG5FCZ0g89osYeKkrlNrmbIfKk
  Name:           FastAPI OAuth Example
  Redirect URIs:  http://localhost:8000/callback
  Scopes:         profile
  Response Types: code

To update the OAuth client in your .env file or environment:
OAUTH_CLIENT_ID=d5fdcc0c-7a88-4cfc-a1ff-6af04b92e9b0
OAUTH_CLIENT_SECRET=XBBMQM9kppmbCyT5opG5FCZ0g89osYeKkrlNrmbIfKk

We recommend creating a dedicated .env.oauth file to store these credentials:

# OAuth Client Credentials
OAUTH_CLIENT_ID=d5fdcc0c-7a88-4cfc-a1ff-6af04b92e9b0
OAUTH_CLIENT_SECRET=XBBMQM9kppmbCyT5opG5FCZ0g89osYeKkrlNrmbIfKk
OAUTH_REDIRECT_URI=http://localhost:8000/callback

# Authorization Server Settings
AUTH_SERVER_URL=http://localhost:8001

OAuth 2.0 Flow Implementation

The OAuth 2.0 implementation follows the standard Authorization Code flow:

Step 1: Redirect to Authorization Endpoint

Redirect the user to the authorization endpoint:

import uuid
from urllib.parse import urlencode

# Generate state parameter to prevent CSRF
state = str(uuid.uuid4())
session['oauth_state'] = state

# Build the authorization URL
params = {
    "client_id": CLIENT_ID,
    "redirect_uri": REDIRECT_URI,
    "response_type": "code",
    "scope": "profile email",
    "state": state
}

auth_url = f"{AUTH_SERVER}/oauth/authorize?{urlencode(params)}"
return redirect(auth_url)

Step 2: User Authentication

The user will be presented with an authentication page where they: 1. Enter their Evrmore wallet address 2. Receive a challenge to sign 3. Sign the challenge with their wallet 4. Submit the signature for verification

Step 3: Handle the Callback

When authentication succeeds, the user is redirected to your redirect URI with a code:

def oauth_callback(code, state):
    # Verify state parameter
    if state != session.get('oauth_state'):
        return "Invalid state parameter", 400

    # Exchange code for tokens
    token_response = requests.post(
        f"{AUTH_SERVER}/oauth/token",
        data={
            "grant_type": "authorization_code",
            "code": code,
            "redirect_uri": REDIRECT_URI,
            "client_id": CLIENT_ID,
            "client_secret": CLIENT_SECRET
        },
        headers={
            "Content-Type": "application/x-www-form-urlencoded"
        }
    )

    token_data = token_response.json()

    # Store tokens securely
    session['access_token'] = token_data["access_token"]
    session['refresh_token'] = token_data["refresh_token"]

    return redirect('/profile')

Step 4: Access Protected Resources

Use the access token to fetch user info or access protected resources:

def get_user_profile():
    access_token = session.get('access_token')

    response = requests.get(
        f"{AUTH_SERVER}/oauth/userinfo",
        headers={
            "Authorization": f"Bearer {access_token}"
        }
    )

    user_data = response.json()
    return render_template('profile.html', user=user_data)

Step 5: Refresh Tokens

When the access token expires, use the refresh token to get a new one:

def refresh_access_token():
    refresh_token = session.get('refresh_token')

    response = requests.post(
        f"{AUTH_SERVER}/oauth/token",
        data={
            "grant_type": "refresh_token",
            "refresh_token": refresh_token,
            "client_id": CLIENT_ID,
            "client_secret": CLIENT_SECRET
        },
        headers={
            "Content-Type": "application/x-www-form-urlencoded"
        }
    )

    token_data = response.json()

    # Update tokens
    session['access_token'] = token_data["access_token"]
    session['refresh_token'] = token_data["refresh_token"]

    return token_data

Step 6: Logout (Token Revocation)

When the user logs out, revoke their tokens:

def logout():
    access_token = session.get('access_token')

    requests.post(
        f"{AUTH_SERVER}/oauth/revoke",
        json={
            "token": access_token,
            "client_id": CLIENT_ID,
            "client_secret": CLIENT_SECRET
        }
    )

    # Clear session
    session.clear()
    return redirect('/')

Troubleshooting

Based on our experience, here are solutions to common OAuth implementation issues:

"Invalid client_id" Error

If you encounter an "Invalid client_id" error:

  1. Verify that the client ID is registered in the database:

    python3 scripts/register_oauth_client.py list
    

  2. Ensure the database path is consistent between your server and client:

    SQLITE_DB_PATH=./evrmore_authentication/data/evrmore_auth.db
    

  3. Re-register the client if necessary:

    python3 scripts/register_oauth_client.py register \
        --name "Your App" --redirects "http://your-app.com/callback"
    

"Missing or expired OAuth session" Error

This error occurs when the OAuth session cookie is missing or invalid:

  1. Ensure cookies are properly stored and have appropriate security settings
  2. Check that the session duration is sufficient
  3. Verify the state parameter is properly managed

Authorization Code Exchange Fails (422 Error)

If token exchange fails with a 422 error:

  1. Ensure the content type is correctly set for the token request:

    headers={"Content-Type": "application/x-www-form-urlencoded"}
    

  2. Verify that you're sending form data rather than JSON:

    # Correct:
    requests.post(url, data={"grant_type": "authorization_code", ...})
    
    # Incorrect:
    requests.post(url, json={"grant_type": "authorization_code", ...})
    

  3. Check that all required parameters are included:

  4. grant_type
  5. code
  6. redirect_uri
  7. client_id
  8. client_secret

"Failed to verify signature" Error

If signature verification fails:

  1. Ensure the user is signing the exact challenge text provided
  2. Check that the signature and Evrmore address match
  3. Make sure you're using the correct verification order in any custom code

Debugging Tools

The repository includes two useful debugging tools:

  1. monitor_logs.py - Real-time log monitoring:

    python3 monitor_logs.py
    

  2. check_auth_codes.py - Monitor OAuth authorization codes:

    python3 check_auth_codes.py
    

Best Practices

Security

  1. Use HTTPS in production environments
  2. Set secure cookie attributes:
  3. httpOnly - Prevents JavaScript access
  4. secure - Ensures cookies are sent only over HTTPS
  5. sameSite=lax - Protects against CSRF

  6. Validate state parameter to prevent CSRF attacks

  7. Store client secrets securely and never expose them client-side
  8. Implement proper error handling for authentication failures
  9. Use refresh tokens for longer sessions rather than extending access token lifetimes
  10. Revoke tokens when users log out

Implementation

  1. Separate OAuth configuration into a dedicated file (like .env.oauth)
  2. Implement token refresh logic to handle expired tokens gracefully
  3. Add proper logging for debugging
  4. Handle errors gracefully with user-friendly messages
  5. Validate all inputs before sending requests

Example Applications

The repository includes example applications:

  1. fastapi_oauth_example.py - A complete FastAPI web application demonstrating the OAuth flow

Run the FastAPI example:

python3 evrmore_authentication/examples/fastapi_oauth_example.py

Then visit http://localhost:8000 in your browser.

API Reference

OAuth 2.0 Endpoints

  • POST /oauth/clients - Register a new OAuth client
  • GET/POST /oauth/authorize - Start the authorization process
  • POST /oauth/token - Exchange authorization codes or refresh tokens
  • GET /oauth/userinfo - Get the authenticated user's profile
  • POST /oauth/revoke - Revoke tokens
  • GET /auth - HTML authentication page for the OAuth flow

For detailed API documentation, see the FastAPI automatic docs at /docs when the server is running.

Support and Contact

If you encounter issues or have questions, please contact: - Email: dev@manticore.technology - GitHub: https://github.com/manticoretechnologies