EV

How to Integrate App Authentication with an OCPP Central System

As EV charging infrastructure grows, user-friendly mobile apps and dashboards are essential. But how do you securely authenticate users in your app and link their actions to OCPP-connected chargers?

In this guide, we’ll show you how to build an app authentication system that works hand-in-hand with your OCPP central system (CSMS).


🧩 Architecture Overview

Components:

  • Mobile App / Dashboard – Where users sign in and control charging
  • Central System API (Flask) – REST API layer for users and admin actions
  • OCPP WebSocket Server – Communicates with EVSEs via OCPP 1.6
  • MongoDB – Stores users and charge point data

📊 Sequence Diagram: Start Charging via App

sequenceDiagram
    participant User as Mobile App
    participant API as Flask API Server
    participant CSMS as OCPP Central System
    participant EVSE as Charger (EVSE)

    User->>API: POST /api/login (username, password)
    API-->>User: JWT token + idTag

    User->>API: POST /api/start_charging (evse_id, connector_id, token)
    API->>CSMS: RemoteStartTransaction(idTag, connectorId)
    CSMS->>EVSE: RemoteStartTransaction.req
    EVSE-->>CSMS: RemoteStartTransaction.conf
    CSMS-->>API: status = Accepted
    API-->>User: {"status": "Accepted"}

This flow ensures only authenticated users can trigger OCPP commands like RemoteStartTransaction.


🔑 1. User Authentication via JWT

We use Flask-JWT-Extended to handle token-based auth.

Setup

Install:

pip install flask flask-jwt-extended pymongo werkzeug

Flask routes for signup and login:

@flask_app.route("/api/signup", methods=["POST"])
def signup():
    ...
    token = create_access_token(identity=user["username"])
    return jsonify(access_token=token, idTag=user["idTag"])

@flask_app.route("/api/login", methods=["POST"])
def login():
    ...
    token = create_access_token(identity=user["username"])
    return jsonify(access_token=token, idTag=user["idTag"])

⚡ 2. Secure OCPP Action (Remote Start)

@flask_app.route("/api/start_charging", methods=["POST"])
@jwt_required()
def start_charging():
    cp = connected_charge_points.get(evse_id)
    payload = call.RemoteStartTransactionPayload(id_tag=id_tag, connector_id=connector_id)
    future = asyncio.run_coroutine_threadsafe(cp.call(payload), main_loop)
    result = future.result(timeout=10)
    return jsonify({"status": result.status})

🧠 Best Practices

  • Hash passwords with werkzeug.security
  • Use JWTs and set expiration
  • Use idTag consistently across auth + OCPP
  • Restrict access by role (user/admin/root)
  • Link idTag to vehicles or accounts

🔧 Tools Used

Tool Purpose
Flask REST API backend
Flask-JWT Auth token generation
PyMongo MongoDB access
asyncio Concurrency + WebSocket calls
OCPP v1.6 Communication with EVSEs

🚀 Going Further

  • Add QR-based pairing: ocpp://CP001?connector=1
  • Add firmware update + diagnostics features
  • Build admin dashboard with user role control

Want the full source code or Postman test collection? Just ask!