From aa6aae57f59e8d89bb4f50d15ba0445162b616f6 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Tue, 31 Mar 2026 22:32:02 +0200 Subject: [PATCH] fix(auth): trigger post-authorize by default when DisablePostAuthorize not configured Change shouldTriggerPostAuthorize from === false to !== true so that non-Accepted local/cached tokens correctly trigger remote re-auth when disablePostAuthorize is undefined (not configured). Previously, undefined === false evaluated to false, silently skipping re-auth. Also flatten redundant double try/catch in OCPP20AuthAdapter. --- .../ocpp/auth/adapters/OCPP20AuthAdapter.ts | 102 +++++++----------- .../ocpp/auth/strategies/LocalAuthStrategy.ts | 2 +- ...lAuthStrategy-DisablePostAuthorize.test.ts | 7 +- 3 files changed, 43 insertions(+), 68 deletions(-) diff --git a/src/charging-station/ocpp/auth/adapters/OCPP20AuthAdapter.ts b/src/charging-station/ocpp/auth/adapters/OCPP20AuthAdapter.ts index 9be272bb..ef4ffabc 100644 --- a/src/charging-station/ocpp/auth/adapters/OCPP20AuthAdapter.ts +++ b/src/charging-station/ocpp/auth/adapters/OCPP20AuthAdapter.ts @@ -68,7 +68,6 @@ export class OCPP20AuthAdapter implements OCPPAuthAdapter { `${this.chargingStation.logPrefix()} ${moduleName}.${methodName}: Authorizing identifier ${truncateId(identifier.value)} via OCPP 2.0 Authorize` ) - // Check if remote authorization is configured const isRemoteAuth = this.isRemoteAvailable() if (!isRemoteAuth) { return { @@ -84,72 +83,14 @@ export class OCPP20AuthAdapter implements OCPPAuthAdapter { } } - try { - const idToken = this.convertFromIdentifier(identifier) - - // Validate token format - const isValidToken = this.isValidIdentifier(identifier) - if (!isValidToken) { - return { - additionalInfo: { - connectorId, - error: 'Invalid token format for OCPP 2.0', - transactionId, - }, - isOffline: false, - method: AuthenticationMethod.REMOTE_AUTHORIZATION, - status: AuthorizationStatus.INVALID, - timestamp: new Date(), - } - } - - logger.debug( - `${this.chargingStation.logPrefix()} ${moduleName}.${methodName}: Sending Authorize request (idToken: ${idToken.idToken})` - ) - - // Send Authorize request - const response = await this.chargingStation.ocppRequestService.requestHandler< - OCPP20AuthorizeRequest, - OCPP20AuthorizeResponse - >(this.chargingStation, OCPP20RequestCommand.AUTHORIZE, { - idToken, - }) - - // Extract authorization status from response - const authStatus = response.idTokenInfo.status - const cacheExpiryDateTime = response.idTokenInfo.cacheExpiryDateTime - - // Map OCPP 2.0 authorization status - const mappedStatus = mapOCPP20AuthorizationStatus(authStatus) - - logger.debug( - `${this.chargingStation.logPrefix()} ${moduleName}.${methodName}: Authorization result for ${idToken.idToken}: ${authStatus} (mapped: ${mappedStatus})` - ) - - return { - additionalInfo: { - cacheExpiryDateTime, - chargingPriority: response.idTokenInfo.chargingPriority, - connectorId, - ocpp20Status: authStatus, - tokenType: idToken.type, - tokenValue: idToken.idToken, - }, - isOffline: false, - method: AuthenticationMethod.REMOTE_AUTHORIZATION, - status: mappedStatus, - timestamp: new Date(), - } - } catch (error) { - logger.error( - `${this.chargingStation.logPrefix()} ${moduleName}.${methodName}: Authorize request failed`, - error - ) + const idToken = this.convertFromIdentifier(identifier) + const isValidToken = this.isValidIdentifier(identifier) + if (!isValidToken) { return { additionalInfo: { connectorId, - error: error instanceof Error ? error.message : 'Unknown error', + error: 'Invalid token format for OCPP 2.0', transactionId, }, isOffline: false, @@ -158,6 +99,41 @@ export class OCPP20AuthAdapter implements OCPPAuthAdapter { timestamp: new Date(), } } + + logger.debug( + `${this.chargingStation.logPrefix()} ${moduleName}.${methodName}: Sending Authorize request (idToken: ${idToken.idToken})` + ) + + const response = await this.chargingStation.ocppRequestService.requestHandler< + OCPP20AuthorizeRequest, + OCPP20AuthorizeResponse + >(this.chargingStation, OCPP20RequestCommand.AUTHORIZE, { + idToken, + }) + + const authStatus = response.idTokenInfo.status + const cacheExpiryDateTime = response.idTokenInfo.cacheExpiryDateTime + + const mappedStatus = mapOCPP20AuthorizationStatus(authStatus) + + logger.debug( + `${this.chargingStation.logPrefix()} ${moduleName}.${methodName}: Authorization result for ${idToken.idToken}: ${authStatus} (mapped: ${mappedStatus})` + ) + + return { + additionalInfo: { + cacheExpiryDateTime, + chargingPriority: response.idTokenInfo.chargingPriority, + connectorId, + ocpp20Status: authStatus, + tokenType: idToken.type, + tokenValue: idToken.idToken, + }, + isOffline: false, + method: AuthenticationMethod.REMOTE_AUTHORIZATION, + status: mappedStatus, + timestamp: new Date(), + } } catch (error) { logger.error( `${this.chargingStation.logPrefix()} ${moduleName}.${methodName}: Remote authorization failed`, diff --git a/src/charging-station/ocpp/auth/strategies/LocalAuthStrategy.ts b/src/charging-station/ocpp/auth/strategies/LocalAuthStrategy.ts index 84741033..b6f97b87 100644 --- a/src/charging-station/ocpp/auth/strategies/LocalAuthStrategy.ts +++ b/src/charging-station/ocpp/auth/strategies/LocalAuthStrategy.ts @@ -516,6 +516,6 @@ export class LocalAuthStrategy implements AuthStrategy { result: AuthorizationResult, config: AuthConfiguration ): boolean { - return result.status !== AuthorizationStatus.ACCEPTED && config.disablePostAuthorize === false + return result.status !== AuthorizationStatus.ACCEPTED && config.disablePostAuthorize !== true } } diff --git a/tests/charging-station/ocpp/auth/strategies/LocalAuthStrategy-DisablePostAuthorize.test.ts b/tests/charging-station/ocpp/auth/strategies/LocalAuthStrategy-DisablePostAuthorize.test.ts index ccef7f8c..d942a091 100644 --- a/tests/charging-station/ocpp/auth/strategies/LocalAuthStrategy-DisablePostAuthorize.test.ts +++ b/tests/charging-station/ocpp/auth/strategies/LocalAuthStrategy-DisablePostAuthorize.test.ts @@ -114,7 +114,7 @@ await describe('LocalAuthStrategy - DisablePostAuthorize', async () => { }) await describe('default behavior', async () => { - await it('should be no-op when DisablePostAuthorize not configured (default behavior preserved)', async () => { + await it('should trigger re-auth when DisablePostAuthorize not configured (defaults to enabled)', async () => { // Arrange const blockedResult = createMockAuthorizationResult({ method: AuthenticationMethod.CACHE, @@ -135,9 +135,8 @@ await describe('LocalAuthStrategy - DisablePostAuthorize', async () => { // Act const result = await strategy.authenticate(request, config) - // Assert - returns cached result as-is (disablePostAuthorize not in config) - assert.notStrictEqual(result, undefined) - assert.strictEqual(result?.status, AuthorizationStatus.BLOCKED) + // Assert - undefined signals orchestrator should try remote strategy (C10.FR.03) + assert.strictEqual(result, undefined) }) }) }) -- 2.43.0