🔍 Code Extractor

class RemarkableAuth

Maturity: 48

Handles the complete authentication flow for reMarkable cloud services, managing device tokens, user tokens, and authenticated HTTP sessions.

File:
/tf/active/vicechatdev/e-ink-llm/cloudtest/auth.py
Lines:
36 - 112
Complexity:
moderate

Purpose

This class manages the two-stage authentication process required to interact with reMarkable's cloud API. It loads a device token from local files, exchanges it for a user token via the reMarkable API, and maintains an authenticated requests.Session object for subsequent API calls. The class handles token file discovery across multiple locations, provides detailed logging of the authentication process, and manages session headers automatically.

Source Code

class RemarkableAuth:
    """Handles reMarkable authentication flow"""
    
    def __init__(self, config: RemarkableConfig = None):
        self.config = config or RemarkableConfig()
        self.session = requests.Session()
        self.session.headers.update({
            'User-Agent': 'reMarkable-desktop-win/3.11.1.1951'
        })
    
    def load_device_token(self) -> Optional[str]:
        """Load device token from file - checks current directory first, then fallback location"""
        # Try current directory first
        if self.config.DEVICE_TOKEN_FILE.exists():
            token_file = self.config.DEVICE_TOKEN_FILE
        elif self.config.FALLBACK_DEVICE_TOKEN_FILE.exists():
            token_file = self.config.FALLBACK_DEVICE_TOKEN_FILE
        else:
            print(f"❌ Device token file not found in:")
            print(f"   Primary: {self.config.DEVICE_TOKEN_FILE}")
            print(f"   Fallback: {self.config.FALLBACK_DEVICE_TOKEN_FILE}")
            print("💡 You need to create remarkable_device_token.txt with your device token")
            return None
        
        try:
            with open(token_file, 'r') as f:
                token = f.read().strip()
            print(f"✅ Loaded device token from {token_file} ({len(token)} chars)")
            return token
        except Exception as e:
            print(f"❌ Error loading device token: {e}")
            return None
    
    def get_user_token(self, device_token: str) -> Optional[str]:
        """Get user token using device token"""
        headers = {"Authorization": f"Bearer {device_token}"}
        
        try:
            print(f"🔑 Requesting user token from: {self.config.USER_TOKEN_URL}")
            response = self.session.post(self.config.USER_TOKEN_URL, headers=headers, timeout=30)
            
            if response.status_code == 200:
                user_token = response.text.strip()
                print(f"✅ User token obtained ({len(user_token)} chars)")
                return user_token
            else:
                print(f"❌ User token request failed: {response.status_code}")
                print(f"   Response: {response.text}")
                return None
                
        except Exception as e:
            print(f"❌ User token error: {e}")
            return None
    
    def authenticate(self) -> Optional[str]:
        """Complete authentication flow"""
        print("🔑 Starting reMarkable authentication...")
        
        device_token = self.load_device_token()
        if not device_token:
            return None
        
        user_token = self.get_user_token(device_token)
        if user_token:
            # Update session headers for future API calls
            self.session.headers.update({"Authorization": f"Bearer {user_token}"})
            print("✅ Authentication complete")
            return user_token
        
        return None
    
    def get_authenticated_session(self) -> Optional[requests.Session]:
        """Get an authenticated session ready for API calls"""
        user_token = self.authenticate()
        if user_token:
            return self.session
        return None

Parameters

Name Type Default Kind
bases - -

Parameter Details

config: Optional RemarkableConfig instance containing configuration settings like token file paths and API URLs. If not provided, a default RemarkableConfig instance is created. This allows customization of file locations and endpoints while providing sensible defaults.

Return Value

Instantiation returns a RemarkableAuth object. Key method returns: load_device_token() returns Optional[str] (device token or None), get_user_token() returns Optional[str] (user token or None), authenticate() returns Optional[str] (user token or None), get_authenticated_session() returns Optional[requests.Session] (authenticated session or None if authentication fails).

Class Interface

Methods

__init__(self, config: RemarkableConfig = None)

Purpose: Initializes the authentication handler with configuration and sets up an HTTP session with appropriate headers

Parameters:

  • config: Optional RemarkableConfig instance for customizing file paths and API endpoints. Defaults to new RemarkableConfig() if not provided

Returns: None (constructor)

load_device_token(self) -> Optional[str]

Purpose: Loads the device token from a local file, checking the primary location first, then the fallback location

Returns: The device token as a string if found and successfully loaded, None if file not found or error occurs. Prints detailed status messages during execution

get_user_token(self, device_token: str) -> Optional[str]

Purpose: Exchanges a device token for a user token by making an authenticated request to the reMarkable API

Parameters:

  • device_token: Valid device token string obtained from load_device_token() or stored elsewhere

Returns: User token as a string if the API request succeeds (HTTP 200), None if request fails or encounters an error. Prints detailed status and error messages

authenticate(self) -> Optional[str]

Purpose: Performs the complete authentication flow: loads device token, exchanges it for user token, and updates session headers

Returns: User token as a string if authentication succeeds, None if any step fails. Also updates self.session.headers with the Authorization header for subsequent API calls

get_authenticated_session(self) -> Optional[requests.Session]

Purpose: Convenience method that performs authentication and returns a ready-to-use authenticated session object

Returns: Authenticated requests.Session object if authentication succeeds, None if authentication fails. The returned session has Authorization headers set and is ready for API calls

Attributes

Name Type Description Scope
config RemarkableConfig Configuration object containing file paths (DEVICE_TOKEN_FILE, FALLBACK_DEVICE_TOKEN_FILE) and API URLs (USER_TOKEN_URL) used throughout the authentication process instance
session requests.Session HTTP session object used for all API requests. Initialized with User-Agent header and updated with Authorization header after successful authentication. Maintains connection pooling and cookies across requests instance

Dependencies

  • requests
  • pathlib

Required Imports

import requests
from pathlib import Path
from typing import Optional

Usage Example

# Basic usage
from remarkable_auth import RemarkableAuth, RemarkableConfig

# Create auth instance with default config
auth = RemarkableAuth()

# Option 1: Get authenticated session directly
session = auth.get_authenticated_session()
if session:
    # Use session for API calls
    response = session.get('https://api.remarkable.com/some-endpoint')
    print(response.json())

# Option 2: Step-by-step authentication
device_token = auth.load_device_token()
if device_token:
    user_token = auth.get_user_token(device_token)
    if user_token:
        # Session is now authenticated
        response = auth.session.get('https://api.remarkable.com/some-endpoint')

# Option 3: Just get the user token
user_token = auth.authenticate()
if user_token:
    print(f'Authenticated with token: {user_token[:10]}...')
    # auth.session is now ready for API calls

# With custom config
custom_config = RemarkableConfig()
auth = RemarkableAuth(config=custom_config)
session = auth.get_authenticated_session()

Best Practices

  • Always check return values for None before using tokens or sessions, as authentication can fail at multiple stages
  • Use get_authenticated_session() for the simplest workflow - it handles the complete authentication flow
  • The session object is reused and maintains state - create one RemarkableAuth instance per application lifecycle
  • Device tokens are long-lived but user tokens may expire - consider re-authenticating if API calls start failing
  • Store device tokens securely in the remarkable_device_token.txt file with appropriate file permissions
  • The class prints detailed status messages to stdout - redirect or capture these if running in a non-interactive environment
  • Session headers are automatically updated after successful authentication - no manual header management needed
  • Methods can be called independently (load_device_token, get_user_token) or use the convenience methods (authenticate, get_authenticated_session)
  • The class checks multiple file locations for the device token (current directory first, then fallback) - place token file in the primary location for best performance

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function create_remarkable_session 80.8% similar

    Creates and returns an authenticated requests.Session object configured for interacting with the reMarkable Cloud API.

    From: /tf/active/vicechatdev/e-ink-llm/mixed_cloud_processor.py
  • function authenticate_remarkable 74.9% similar

    A convenience wrapper function that creates a RemarkableAuth instance and performs authentication to obtain a user token for the reMarkable cloud service.

    From: /tf/active/vicechatdev/e-ink-llm/cloudtest/auth.py
  • function test_remarkable_auth 73.9% similar

    Asynchronous function that tests authentication and API connectivity with the reMarkable Cloud service, verifying credentials and basic API access.

    From: /tf/active/vicechatdev/e-ink-llm/test_mixed_mode.py
  • class RemarkableConfig 73.7% similar

    A dataclass that stores configuration constants for interacting with the reMarkable cloud API, including API endpoints and local file paths for device tokens.

    From: /tf/active/vicechatdev/e-ink-llm/cloudtest/auth.py
  • function test_remarkable_authentication 73.2% similar

    Asynchronous test function that validates reMarkable Cloud authentication and verifies access to the root folder by listing its contents.

    From: /tf/active/vicechatdev/e-ink-llm/test_remarkable.py
← Back to Browse