Formatée une réponse 401 suite à la mise en oeuvre d'un RESTServiceExternalObject

Request description

Bonjour,

Comment est-il possible de renvoyer une réponse 401 formatée en cas de token non valide (expiré, révoqué, etc.) suite à la mise en œuvre d’un RESTServiceExternalObject.

Exemple de format :

{
  "status": 401,
  "title": "Invalid Access Token",
  "detail": "The Bearer access token found in the Authorization HTTP header is invalid"
}

Steps to reproduce

N/A

Technical information

Instance /health

[Platform]
Status=OK
Version=5.3.36
BuiltOn=2024-04-23 19:35
Git=5.3/ca923dd89cde687c9dfe4c0c3868d647e379a8a0
Encoding=UTF-8
EndpointIP=127.0.0.1
EndpointURL=https://xxxxxx.fr:11403
TimeZone=Europe/Paris
SystemDate=2024-04-24 09:22:53

[Application]
ApplicationVersion=1.0.0
ContextPath=
ContextURL=https://xxxxxx.fr
ActiveSessions=2
TotalUsers=21
EnabledUsers=19
LastLoginDate=2024-04-24 09:22:43

[Server]
ServerInfo=Apache Tomcat/9.0.88
ServerType=WEB
ServerActiveSessions=3
ServerSessionTimeout=30
CronStarted=true

[OS]
Name=Linux
Architecture=amd64
Version=3.10.0-1160.114.2.el7.x86_64
SystemEncoding=UTF-8

[Disk]
DiskFree=17233
DiskUsable=13076
DiskTotal=100701

[JavaVM]
Version=17.0.1
Vendor=Eclipse Adoptium
VMName=OpenJDK 64-Bit Server VM
VMVersion=17.0.1+12
ScriptEngine=rhino
ScriptEngineVersion=Rhino 1.7.13 2020 09 02
HeapFree=224355
HeapSize=399360
HeapMaxSize=524288
TotalFreeSize=349283

[Cache]
ObjectCache=510
ObjectCacheMax=10000
ObjectCacheRatio=5
ProcessCache=510
ProcessCacheMax=10000
ProcessCacheRatio=5
APIGrantCache=0
APIGrantCacheMax=1000
APIGrantRatio=0

[Database]
Vendor=3
VendorName=postgresql
ProductName=PostgreSQL
ProductVersion=9.2.24
DriverName=PostgreSQL JDBC Driver
DriverVersion=42.7.3
DBDate=2024-04-24 09:22:53
DBDateOffset=0
DBPatchLevel=5;P03;29131276dea85455af3d320043396699;35
UsingBLOBs=true

[Healthcheck]
Date=2024-04-24 09:22:53
ElapsedTime=10

De manière générale il est possible de surcharger les méthodes d’erreur de ce type d’objet externe, cf. RESTServiceExternalObject

Mais dans le cas d’une erreur de token invalide c’est beaucoup plus bas niveau => le check est fait avant d’instancier et d’invoker l’objet externe, du coup je ne pense pas qu’en l’état on puisse modifier ce que ça répond.

Je regarde et je te tiens au courant.

Bonjour,

Merci pour ton retour, on a le même besoin pour une 403 (Si le token est valide mais que l’application n’est pas autorisée à consommer l’API)

Cordialement,

Le cas du forbidden (403) est de même nature que le unauthorized (401), ce sont des vérifications qui sont faites en amont de l’instanciation de l’appel de l’objet externe

  1. as tu un token valide ? non => unauthorized
  2. as tu le droit d’appeler cet objet externe ? non = forbidden
  3. ok on instancie et on invoque l’objet externe…

Les 1) et 2) ne sont donc pas hookables en l’état.

On va donc regarder s’il est envisageable d’ajouter un ou des platform hooks pour reformatter si besoin les réponses HTTP (à minima, les réponses d’erreur bas niveau : 404, 401, 403)

NB: Je note au passage que, contrairement au unauthorized, le forbidden ne tient visiblement pas compte du Accept: application/json de la requête et renvoie systématiquement du HTML, ça c’est une anomalie

En attendant la seule manière de customiser l’ensemble des réponses ok ou ko d’un objet externe c’est de le rendre public (i.e. l’habiliter à PUBLIC) et de tout gérer applicativement, y compris l’ident/authent (et via un mécanisme autre que le header Authorization) et la vérification des droits ce qui est dommage car ça revient à réinventer l’eau chaude en prenant le risque de ne pas bien le faire.

Le pb de non prise en compte du Accept: application/json dans les messages d’erreur Forbidden et Internal server error sur les appels d’objet externes sur le endpoint API endpoint est corrigé => désormais ça renverra bien du JSON:

Ce sera livré dans le cadre des prochaines révisions 6.0.9 et 5.3.37

On regarde pour ce qui est d’ajouter un platform hook pour pouvoir customiser ces réponses si besoin.

Voilà on a ajouté un platform hook dédié à la surcharge des réponses d’erreurs : customErrorResponse

Attention: quand on surcharge ce hook il faut bien le faire en fct de l’URI appelée car ce hook est commun à tous les cas de réponses d’erreur (et en fct de l’appel la nature de data est potentiellement différente : String ,JSONObject, …)

Ex: pour surcharger les erreurs d’appel d’objets externes sur le endpoint API :

package com.simplicite.commons.Application;

import org.json.JSONObject;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.simplicite.util.Globals;
import com.simplicite.webapp.tools.ServletTool;

public class PlatformHooks extends com.simplicite.util.engine.PlatformHooksInterface {
	@Override
	public void customErrorResponse(HttpServletRequest request, HttpServletResponse response, int status, Object data, Map<String, String> headers) throws Exception {
		if (request.getRequestURI().startsWith(Globals.getContextPath() + Globals.WEB_API_PATH + Globals.WEB_EXT_PATH)) {
			JSONObject res = new JSONObject()
				.put("status", status)
				.put("title", ((JSONObject)data).getString("error"))
				.put("detail", "Some additional information...");
			ServletTool.responseWithStatus(request, response, status, res, headers);
		} else {
			super.customErrorResponse(request, response, status, data, headers);
		}
	}
}

Résultat:

Ce sera backporté sur la future 6.0.10 et, sauf incompatibilité ou de risques, sur la future 5.3.37

PS: pour bénéficier de ce genre d’évolution il vaudrait quand même plutôt passer en v6 car normalement sur la v5 qui est désormais en maintenance long terme on n’est pas sensé backporter d’évolutions, uniquement du correctif.