From 2dcc5dd49aaa7ed6c6d32d0c12abe8304a09f013 Mon Sep 17 00:00:00 2001 From: Niklas Hildebrandt Date: Thu, 11 Jun 2026 17:06:09 +0200 Subject: [PATCH] fix(push,cdr): honor Receiver.raw_token and add CdrLocation.evse_uid Two OCPI compliance/interop fixes needed for CDR push to eMSPs that do not base64-decode the auth token (e.g. DepotCharge): - push_object now sends the raw auth token when Receiver.raw_token=True, instead of always base64-encoding for 2.2.x/2.3.x. Some eMSPs compare the literal Token header value, so a base64-encoded token is rejected with 401 "Unknown token". - CdrLocation (2.2.1 and 2.3.0) now includes the spec-required evse_uid field. It was missing, so the lib silently dropped evse_uid from the serialized CDR, causing receivers to reject the push with 400. Co-Authored-By: Claude Opus 4.8 (1M context) --- ocpi/core/push.py | 2 +- ocpi/core/schemas.py | 1 + ocpi/modules/cdrs/v_2_2_1/schemas.py | 1 + ocpi/modules/cdrs/v_2_3_0/schemas.py | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ocpi/core/push.py b/ocpi/core/push.py index f00dc19..f1c7836 100644 --- a/ocpi/core/push.py +++ b/ocpi/core/push.py @@ -123,7 +123,7 @@ async def push_object( receiver_responses = [] for receiver in push.receivers: # get client endpoints - if version.value.startswith("2.1") or version.value.startswith("2.0"): + if receiver.raw_token or version.value.startswith("2.1") or version.value.startswith("2.0"): token = receiver.auth_token else: # 2.2.x and 2.3.x use base64-encoded tokens token = encode_string_base64(receiver.auth_token) diff --git a/ocpi/core/schemas.py b/ocpi/core/schemas.py index 93772d6..78a1999 100644 --- a/ocpi/core/schemas.py +++ b/ocpi/core/schemas.py @@ -24,6 +24,7 @@ class OCPIResponse(BaseModel): class Receiver(BaseModel): endpoints_url: URL auth_token: str + raw_token: bool = False class Push(BaseModel): diff --git a/ocpi/modules/cdrs/v_2_2_1/schemas.py b/ocpi/modules/cdrs/v_2_2_1/schemas.py index 7a798a0..d6430f8 100644 --- a/ocpi/modules/cdrs/v_2_2_1/schemas.py +++ b/ocpi/modules/cdrs/v_2_2_1/schemas.py @@ -79,6 +79,7 @@ class CdrLocation(BaseModel): state: String(20) | None # type: ignore country: String(3) # type: ignore coordinates: GeoLocation + evse_uid: CiString(36) # type: ignore evse_id: CiString(48) # type: ignore connector_id: CiString(36) # type: ignore connector_standard: ConnectorType diff --git a/ocpi/modules/cdrs/v_2_3_0/schemas.py b/ocpi/modules/cdrs/v_2_3_0/schemas.py index 64a3a5b..4be0a50 100644 --- a/ocpi/modules/cdrs/v_2_3_0/schemas.py +++ b/ocpi/modules/cdrs/v_2_3_0/schemas.py @@ -79,6 +79,7 @@ class CdrLocation(BaseModel): state: String(20) | None # type: ignore country: String(3) # type: ignore coordinates: GeoLocation + evse_uid: CiString(36) # type: ignore evse_id: CiString(48) # type: ignore connector_id: CiString(36) # type: ignore connector_standard: ConnectorType