Skip to content

Bug: Nous Portal OAuth device code flow broken; _nous_api_key() rejects valid API keys #47950

Description

@natehale

Bug: Nous Portal OAuth device code flow — approval never propagates to token endpoint; _nous_api_key() rejects valid API keys

Summary

The OAuth device code flow for Nous Portal is broken: the verification URL shows a subscription plan page with no device approval UI, so the token endpoint never receives the approval signal. Additionally, _nous_api_key() in agent/auxiliary_client.py strictly validates tokens as JWTs, rejecting plain sk- API keys that the Portal's own API docs describe as the standard authentication method.

This blocks all Nous auxiliary tasks (compression, vision, skills_hub, etc.) for users who authenticate via API key.

Environment

  • Hermes Agent v0.16.0 (editable install)
  • Linux 6.8.0-110-generic
  • Nous Portal account: Plus plan ($20/mo, paid-up)

Steps to Reproduce

  1. Run hermes auth add nous --no-browser --timeout 300
  2. CLI prints: https://portal.nousresearch.com/manage-subscription?user_code=XXXX-XXXX
  3. Open the URL in a browser — it shows a subscription plan selection page, not a device approval page
  4. Sign in to the Portal (Google/GitHub/email)
  5. No "Approve Device" or similar UI appears
  6. CLI continues polling: Waiting for approval (polling every 1s)...
  7. Poll the token endpoint directly — it returns authorization_pending indefinitely

Verified with direct curl testing:

  • Device code POST to /api/oauth/device/code → 200, returns device_code + user_code ✓
  • Token poll POST to /api/oauth/token with device_code → authorization_pending for 150+ seconds, even after the user has signed into the Portal ✓
  • No API endpoint exists to programmatically approve the device code

What Works

  • The sk- API key from the Portal works perfectly for direct inference:
    curl -s https://inference-api.nousresearch.com/v1/chat/completions \
      -H "Authorization: Bearer $NOUS_API_KEY" \
      -d '{"model":"stepfun/step-3.7-flash:free","messages":[...]}'
    → 200, valid response
    
  • The API docs at https://portal.nousresearch.com/api-docs confirm API keys are the standard auth method (Option 1: "Using API keys & account credits")

Root Cause

Two issues compound:

  1. Portal-side: The device code verification URL (/manage-subscription?user_code=...) does not present a device approval UI. The approval never reaches the token endpoint. (Related: [Bug]: Nous Portal OAuth device code flow fails on servers/headless environments (Vercel error 249) #12275 — device code flow also fails from servers due to Vercel WAF)

  2. Hermes-side: _nous_api_key() in agent/auxiliary_client.py (line ~1278) only accepts JWT tokens via _nous_invoke_jwt_is_usable(). Plain sk- API keys fail with access_token_not_jwt, even though:

Proposed Fix

In agent/auxiliary_client.py, update _nous_api_key() to accept sk- prefixed API keys without JWT validation:

def _nous_api_key(provider: dict) -> str:
    """Extract a usable Nous inference credential from stored auth state.

    Accepts both JWT tokens (minted by the Portal OAuth flow) and plain
    API keys (``sk-`` prefixed, generated on the Portal).  The inference
    API treats both as valid bearer tokens.
    """
    from hermes_cli.auth import _nous_invoke_jwt_is_usable

    for token_key, expiry_key in (
        ("agent_key", "agent_key_expires_at"),
        ("access_token", "expires_at"),
    ):
        token = provider.get(token_key)
        if not isinstance(token, str) or not token.strip():
            continue
        # Plain API keys (sk-...) are valid bearer tokens per the Portal
        # API docs — accept them without JWT validation.
        if token.startswith("sk-"):
            return token
        if _nous_invoke_jwt_is_usable(
            token,
            scope=provider.get("scope"),
            expires_at=provider.get(expiry_key),
        ):
            return token
    return ""

This is a minimal, backwards-compatible change — JWT tokens continue to work as before, and sk- API keys are additionally accepted.

Alternative / Longer-term

  • Fix the Portal's device code approval UI — the verification URL should show an "Approve Device" button, not a subscription page
  • Re-add nous-api as a provider (reverting 013cc4d2f) — gives users a proper setup wizard path for API key auth
  • At minimum, document that hermes auth add nous --type api-key is the recommended path when OAuth is unavailable

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium — degraded but workaround existsarea/authAuthentication, OAuth, credential poolscomp/agentCore agent runtime: loop, agent_init, prompt builder, context-compression, responses endpointcomp/portalNous portal / Hermes Pro / hosted-Hermes pathprovider/nousNous Research API (OAuth)type/bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions