Search code examples
javakeycloakkeycloak-services

How do I return custom error messages to frontend in custom spnego Authenticator in Keycloak (3.4.3)?


I am building a custom authenticator in Keycloak which logs a user in not only into keycloaks SSO-Instance, but also in a legacy (SAP-) System via a rest-Call.

Now I get error messages from this legacy API and I want to display the messages (which are internationalized by callee-param) to the enduser when logging in via my custom authenticator, but I don't get how to do this, yet.

I think it has something to do with e.g. the following lines from the spnego authenticator on github:

...
else {
            context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
            context.failure(AuthenticationFlowError.INVALID_CREDENTIALS);
}
...

when changing the .error()-value to "test!" this gets logged, but the error shown to the user is also the invalid credentials-line. So, AuthenticationFlowError seems to be an ENUM, which won't work with dynamic internationalized messages from thirdparty I think. But is there a way to tell my authenticationcontext to return the errormessage from my thirdparty-system?

Any help is highly appreciated


Solution

  • So, looking at the keycloak-sources (opensource ftw!) I just found out how to do this:

    Instead of the lines above, one might do it like this:

     else {
       //errorresponse from restcall to thirdparty-api (just removing prefix here)
       responseString = responseString.replace("sap_error_", "");
    
       //actually I try to do a somewhat silent idplogin, so this might fit. 
       //you can change this error to the errormsg from thirdparty, too,
       // so its collected in the logs error=-part.
       context.getEvent().error(Errors.IDENTITY_PROVIDER_ERROR);
    
       //here we're building the actual responseHandler.
       //One might even want to set the status to status from thirdparty. 
       Response challengeResponse = context.form().setError(responseString)
                    .createErrorPage(Response.Status.INTERNAL_SERVER_ERROR);
    
       //And in the end we apply the failureChallenge to the Context  
       context.failureChallenge(AuthenticationFlowError.IDENTITY_PROVIDER_ERROR,
                                         challengeResponse);
    }
    

    I commented the code to make clear what is done.

    edit: As this question just got an upvote: Please take care! The .setError()-content has to be <255 characters, else you get an exception (DB-Field too short iirc, tested up to keycloak 7).

    Best regards, Dominik