Skip to content

Support anonymous/unauthenticated VTN connections #87

@dcj

Description

@dcj

Context

Some OpenADR 3 VTNs operate without authentication — for example, public price servers in Phase 1 deployments, or testing/development VTNs. The current VirtualEndNodeHttpClientFactory.create_http_ven_client() requires client_id, client_secret, and token_url parameters, with no way to opt out of OAuth.

Problem

When connecting to an unauthenticated VTN, the BearerAuthenticatedSession tries to fetch an OAuth token before every request. If the VTN's /auth/token endpoint returns 501 (not implemented) or doesn't exist, the client fails with oauthlib.oauth2.rfc6749.errors.MissingTokenError before any API call is made.

Reproduction

from openadr3_client.ven.http_factory import VirtualEndNodeHttpClientFactory
from openadr3_client.version import OADRVersion

ven = VirtualEndNodeHttpClientFactory.create_http_ven_client(
    vtn_base_url="https://price.grid-coordination.energy/openadr3/3.1.0",
    client_id="anonymous",
    client_secret="anonymous",
    token_url="https://price.grid-coordination.energy/openadr3/3.1.0/auth/token",
    version=OADRVersion.OADR_310,
)
programs = ven.programs.get_programs(target=None, pagination=None)
# → MissingTokenError: Missing access token parameter.

The VTN at https://price.grid-coordination.energy/openadr3/3.1.0 is a public California electricity price server that serves 492 programs with hourly marginal prices. It has no authentication (Phase 1).

Workaround

Replacing the authenticated session with a plain requests.Session() after client creation works — the entire API (programs, events, intervals) parses correctly:

import requests
ven.programs.session = requests.Session()
ven.events.session = requests.Session()
programs = ven.programs.get_programs(target=None, pagination=None)
# → 50 programs, all fields correctly parsed

Suggested fix

Allow client_id, client_secret, and token_url to be None (or add a separate factory method like create_unauthenticated_ven_client). When all three are None, use a plain requests.Session instead of BearerAuthenticatedSession.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions