Reservations and Authorization in OCPP

IoTEV ChargingOCPPSecurity
Share on LinkedIn

A driver reserves a charger for 6 PM, arrives at 6:05, and finds another vehicle charging on "their" connector. The reservation expired at 6:00 because the CSMS set a 30-minute window and the driver's meeting ran long. Reservations and authorization are the access control layer of OCPP—who can charge, on which connector, and for how long. Get authorization wrong and you bill the wrong account. Get reservations wrong and drivers stop trusting your network.

Authorization flow

User presents ID tag (RFID, app, credit card)
  → Charger sends Authorize(idTag) to CSMS
  → CSMS responds: Accepted / Blocked / Expired / Invalid
  → If Accepted: StartTransaction
  → If not: reject, display reason

Authorize request/response:

// Request
{ "idTag": "RFID-0042-A" }

// Response
{
  "idTagInfo": {
    "status": "Accepted",
    "expiryDate": "2026-06-01T00:00:00Z",
    "parentIdTag": "FLEET-ACME-CORP"
  }
}
Status Meaning User message
Accepted Authorized to charge "Starting session"
Blocked Explicitly denied "Card blocked—contact support"
Expired Tag validity ended "Card expired"
Invalid Unknown tag "Card not recognized"
ConcurrentTx Already charging elsewhere "Active session on another charger"

Local authorization list

For offline operation and reduced latency, cache authorized tags:

{
  "listVersion": 15,
  "localAuthorizationList": [
    {
      "idTagInfo": {
        "status": "Accepted",
        "expiryDate": "2026-01-01T00:00:00Z",
        "parentIdTag": "FLEET-ACME"
      },
      "idTag": "RFID-0042-A"
    }
  ]
}

CSMS updates via SendLocalList:

{
  "listVersion": 16,
  "updateType": "Differential",
  "localAuthorizationList": [
    { "idTag": "RFID-0099-B", "idTagInfo": { "status": "Accepted" } }
  ]
}

Differential adds/updates entries. Full replaces the entire list. Increment listVersion on every update.

Reservations

CSMS reserves a connector:

{
  "connectorId": 2,
  "expiryDate": "2025-11-05T18:30:00Z",
  "idTag": "RFID-0042-A",
  "reservationId": 101
}

Charger response:

{ "status": "Accepted" }
// or: "Faulted", "Occupied", "Rejected", "Unavailable"

While reserved:

Cancel reservation:

{ "reservationId": 101 }

Parent ID tag billing

def process_authorization(id_tag: str, auth_response: dict) -> BillingAccount:
    parent = auth_response.get("parentIdTag")
    billing_tag = parent if parent else id_tag

    account = db.get_account(billing_tag)
    if not account:
        raise BillingError(f"No account for {billing_tag}")

    if account.balance <= 0 and account.payment_method == "prepaid":
        return reject("Insufficient balance")

    return account

Fleet scenario: 50 drivers, each with RFID-XXXX child tags under FLEET-ACME parent. All sessions bill to the fleet account.

Concurrent transaction handling

Prevent one user from occupying multiple connectors:

def check_concurrent(id_tag: str, csms) -> bool:
    active = csms.get_active_transactions(id_tag)
    if active:
        return False  # ConcurrentTx
    return True

Configure per account: fleet accounts may allow 2 concurrent sessions; individual users get 1.

Authorization cache on charger

OCPP 1.6 supports LocalAuthListEnabled and AuthorizationCacheEnabled:

Authorize(idTag) → check cache (TTL: LocalAuthorizeOfflineParam)
                 → check local list
                 → send to CSMS (if online)
                 → cache response

Cache TTL of 300 seconds reduces CSMS load. Clear cache entry when CSMS sends ClearCache.

Reservation UX considerations

OCPP 2.0.1 authorization improvements

OCPP 2.0.1 replaces flat authorization with structured IdToken types:

{
  "idToken": {
    "idToken": "RFID-ABC123",
    "type": "ISO14443"
  },
  "certificate": "base64-ocsp-response"
}

Supported IdToken types: Central, eMAID (ISO 15118 Plug & Charge), ISO14443, ISO15693, KeyCode, Local, NoAuthorization. Each type has different validation rules — eMAID requires certificate chain validation against V2G root CA.

Plug & Charge (ISO 15118) authorization flow:

EV presents eMAID → charger validates certificate → CSMS Authorize(eMAID)
→ contract certificate verified → charging starts without RFID tap

Offline authorization fallback

When CSMS is unreachable, chargers must decide locally:

Authorize(idTag):
  1. Check local auth list (pre-synced whitelist)
  2. Check authorization cache (recent successful auths)
  3. If AllowOfflineTxForUnknownIdEnabled → accept unknown tags
  4. Else → reject with Blocked status

Configure LocalAuthListMaxLength and sync frequency based on expected offline duration. Fleet depots with reliable connectivity can use shorter cache TTL; highway stations need longer offline tolerance.

Reservation conflict resolution

Multiple reservation scenarios need explicit handling:

Scenario Resolution
User arrives early Allow charging if connector free; reservation still valid
User arrives late (within grace) Honor reservation; shorten session if needed
Connector occupied at arrival Queue or redirect to alternate connector
CSMS offline during reservation window Local list must include reservation idTags
Double reservation same connector CSMS rejects second ReserveNow

Log all reservation state transitions — disputes about no-shows require audit trail.

Failure modes

Production checklist

Common production mistakes

Teams get ocpp reservation authorization wrong in predictable ways:

Production implementations of ocpp reservation authorization fail when staging mirrors production topology poorly, rollback is untested, and on-call runbooks describe the happy path only.

Resources

Frequently asked questions

How does an OCPP reservation work?

The CSMS sends ReserveNow with an ID tag, connector ID, and expiry time. The charger holds that connector for the tagged user until the reservation expires or the user starts charging. Other users cannot start a session on the reserved connector.

What happens when an unauthorized ID tag is presented?

The charger sends Authorize to the CSMS (if online) or checks the local auth list (if offline). The response is Accepted, Blocked, Expired, or Invalid. Only Accepted tags can start a transaction. Blocked tags are explicitly denied; Invalid means the tag is unknown.

What is a parent ID tag?

A parent ID tag groups multiple child tags under one billing account. When a child tag authorizes, the CSMS returns the parent ID tag in the Authorize response. The transaction is billed to the parent account. Useful for fleet cards where each driver has an individual RFID but one company pays.

Hiring a senior Android / Flutter engineer?

I architect and ship production mobile software — Kotlin, Jetpack Compose, Flutter — for robotics, EV infrastructure, fintech, and real-time systems. Open to remote roles in Europe and the US.

Get in touch →