Skip to content

bug: FAILED_AUTHORIZATION only fires on Invalid, missing Blocked, Expired, and ConcurrentTx #156

Description

@sepehr-safari

Describe the Bug

FAILED_AUTHORIZATION fires only when an Authorize response carries
idTagInfo.status === 'Invalid':

const status = getAuthorizeStatus(event);
if (status === 'Invalid') { ... }

The OCPP 1.6 AuthorizationStatus enumeration (edition 2, section 7.2) has five
values, and only one of them permits charging:

Value Meaning in the spec
Accepted Identifier is allowed for charging
Blocked Identifier has been blocked, not allowed for charging
Expired Identifier has expired, not allowed for charging
Invalid Identifier is unknown, not allowed for charging
ConcurrentTx Identifier is already involved in another transaction

So four of the five are refusals, and the rule detects one of them. A driver
whose token is blocked or expired hits exactly the failure the rule exists to
surface, the trace shows the same shape (an Authorize answered with a refusing
status, then no transaction), and the report says nothing. Silence from a
detector reads as "this is not the problem", which is worse here than no rule at
all.

ConcurrentTx is the one with a caveat: section 7.2 marks it "Only relevant for
a StartTransaction.req", so seeing it in an Authorize.conf is itself
irregular. It is still a refusal, and treating it as one costs nothing.

Steps to Reproduce

  1. Build the toolkit.
  2. Evaluate an Authorize call answered with Blocked:
const events = [
  { message: [2, 'a1', 'Authorize', { idTag: 'SYNTHETIC-TAG-001' }] },
  { message: [3, 'a1', { idTagInfo: { status: 'Blocked' } }] },
];
const trace = parseTrace(JSON.stringify({ events }));
detectFailures(trace.events, buildSessionTimeline(trace.events)).map((f) => f.code);
  1. Repeat with Expired and ConcurrentTx.

Expected Behavior

Each of the four non-Accepted statuses is reported as a failed authorization,
naming which status it was.

Actual Behavior

Only Invalid is reported. Blocked, Expired, and ConcurrentTx produce
nothing.

Environment

  • OCPP DebugKit version: 0.4.4
  • Node.js version: 26.x
  • OS: macOS

Proposed Fix

The open question is whether these are one code or several. Proposal: keep a
single FAILED_AUTHORIZATION code covering all four refusals, with the status
named in the description (Authorization rejected: idTag returned "Blocked" status), and severity unchanged at warning.

Reasoning for one code rather than four: the operator-facing question is the same
in every case (the identity layer refused, so no energy will flow), the
suggested steps already cover all four causes, and the specific status is on the
failure object for anyone who wants to branch on it. Four codes would grow the
FailureCode union, and every code is part of the published surface and of the
contract-v1 corpus shared with Studio, so they are worth adding only when a
consumer would act differently. If we later want ConcurrentTx separated out
(it does point at CSMS state rather than at the token), that can be split then.

Out of scope, worth its own issue: the rule only inspects Authorize responses.
StartTransaction.conf and StopTransaction.conf also carry idTagInfo, and
section 4.8 is explicit that the Central System verifies the identifier again on
StartTransaction, so a session can start and then be deauthorized. That path is
currently invisible to this rule.

Note for whoever picks this up: this one widens what the engine detects, so it is
the change most likely to move contract-v1 goldens. None of the current
scenarios carry a non-Accepted, non-Invalid idTagInfo, so the corpus should
come out unchanged, but check rather than assume, and Studio's Zig rule
(src/ocpp/detection.zig) needs the same widening in lockstep.

Metadata

Metadata

Assignees

No one assigned

    Labels

    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