web-dev-qa-db-fra.com

Déconnexion d'un utilisateur via Keycloak REST API ne fonctionne pas

J'ai un problème lors de l'appel du noeud final logout de Keycloak à partir d'une application (mobile). 

Ce scénario est pris en charge comme indiqué dans sa documentation :

/ realms/{nom-domaine}/protocole/openid-connect/logout

Le point de terminaison de déconnexion déconnecte l'utilisateur authentifié.

L'agent utilisateur peut être redirigé vers le noeud final, auquel cas la session utilisateur active est déconnectée. Ensuite, l'agent utilisateur est redirigé vers l'application.

Le noeud final peut également être appelé directement par l'application. Pour appeler directement ce noeud final, le jeton d'actualisation doit être inclus, ainsi que les informations d'identification requises pour authentifier le client.

Ma demande a le format suivant:

POST http://localhost:8080/auth/realms/<my_realm>/protocol/openid-connect/logout
Authorization: Bearer <access_token>
Content-Type: application/x-www-form-urlencoded

refresh_token=<refresh_token>

mais cette erreur se produit toujours:

HTTP/1.1 400 Bad Request
Connection: keep-alive
X-Powered-By: Undertow/1
Server: WildFly/10
Content-Type: application/json
Content-Length: 123
Date: Wed, 11 Oct 2017 12:47:08 GMT

{
  "error": "unauthorized_client",
  "error_description": "UNKNOWN_CLIENT: Client was not identified by any client authenticator"
}

Il semble que Keycloak ne puisse pas détecter l'événement d'identité du client actuel si j'ai fourni access_token. J'ai utilisé le même access_token pour accéder aux autres API de Keycloak sans aucun problème, comme userinfo (/ auth/realms // protocole/openid-connect/userinfo). 

Ma demande était basée sur ceci Problème de Keycloak . L'auteur de la question a réussi, mais ce n'est pas mon cas. 

J'utilise Keycloak 3.2.1.Final

Avez-vous le même problème? Avez-vous une idée de la façon de le résoudre?

4
Manh Ha

Enfin, j'ai trouvé la solution en consultant le code source de Keycloak: https://github.com/keycloak/keycloak/blob/9cbc335b68718443704854b1e758f8335b06c242/services/src/main/Java/org/keycloak points de terminaison/LogoutEndpoint.Java # L169 . Ça dit:

Si le client est un client public, vous devez inclure un paramètre de formulaire "client_id".

Il me manquait donc le paramètre de formulaire client_id. Ma demande devrait avoir été:

POST http://localhost:8080/auth/realms/<my_realm>/protocol/openid-connect/logout
Authorization: Bearer <access_token>
Content-Type: application/x-www-form-urlencoded

client_id=<my_client_id>&refresh_token=<refresh_token>

La session devrait être détruite correctement.

13
Manh Ha

dans la version 3.4, vous avez besoin de la clé de corps x_ www-form-urlencoded client_id, client_secret et refresh_token.

2
Nerospeed

FYI: les spécifications OIDC et l'implémentation de Google ont un extrémité de révocation de jeton , Mais ceci n'est pas implémenté dans Keycloak, vous pouvez donc voter pour la fonctionnalité dans Keycloak JIRA

1
stokito

J'ai essayé cela avec Keycloak 4.4.0.Final et 4.6.0.Final. J'ai vérifié le journal du serveur keycloak et j'ai vu les messages d'avertissement suivants dans la sortie de la console.

10:33:22,882 WARN  [org.keycloak.events] (default task-1) type=REFRESH_TOKEN_ERROR, realmId=master, clientId=security-admin-console, userId=null, ipAddress=127.0.0.1, error=invalid_token, grant_type=refresh_token, client_auth_method=client-secret
10:40:41,376 WARN  [org.keycloak.events] (default task-5) type=LOGOUT_ERROR, realmId=demo, clientId=eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJqYTBjX18xMHJXZi1KTEpYSGNqNEdSNWViczRmQlpGS3NpSHItbDlud2F3In0.eyJqdGkiOiI1ZTdhYzQ4Zi1mYjkyLTRkZTYtYjcxNC01MTRlMTZiMmJiNDYiLCJleHAiOjE1NDM0MDE2MDksIm5iZiI6MCwiaWF0IjoxNTQzNDAxMzA5LCJpc3MiOiJodHRwOi8vMTI3Lj, userId=null, ipAddress=127.0.0.1, error=invalid_client_credentials

Alors, comment avez-vous construit la requête HTTP? Tout d'abord, j'ai récupéré le principal de l'utilisateur à partir de HttpSession et l'a converti en types d'instance interne Keycloak:

KeycloakAuthenticationToken keycloakAuthenticationToken = (KeycloakAuthenticationToken) request.getUserPrincipal();
final KeycloakPrincipal keycloakPrincipal = (KeycloakPrincipal)keycloakAuthenticationToken.getPrincipal();
final RefreshableKeycloakSecurityContext context = (RefreshableKeycloakSecurityContext) keycloakPrincipal.getKeycloakSecurityContext();
final AccessToken accessToken = context.getToken();
final IDToken idToken = context.getIdToken();

Deuxièmement, j'ai créé l'URL de déconnexion comme dans la réponse de débordement de pile supérieure (voir ci-dessus):

final String logoutURI = idToken.getIssuer() +"/protocol/openid-connect/logout?"+
            "redirect_uri="+response.encodeRedirectURL(url.toString());

Et maintenant, je construis ensuite le reste de la requête HTTP comme suit:

KeycloakRestTemplate keycloakRestTemplate = new KeycloakRestTemplate(keycloakClientRequestFactory);
HttpHeaders headers = new HttpHeaders();
headers.put("Authorization", Collections.singletonList("Bearer "+idToken.getId()));
headers.put("Content-Type", Collections.singletonList("application/x-www-form-urlencoded"));

Et aussi construire la chaîne de contenu du corps:

StringBuilder bodyContent = new StringBuilder();
bodyContent.append("client_id=").append(context.getTokenString())
            .append("&")
            .append("client_secret=").append(keycloakCredentialsSecret)
            .append("&")
            .append("user_name=").append(keycloakPrincipal.getName())
            .append("&")
            .append("user_id=").append(idToken.getId())
            .append("&")
            .append("refresh_token=").append(context.getRefreshToken())
            .append("&")
            .append("token=").append(accessToken.getId());
HttpEntity<String> entity = new HttpEntity<>(bodyContent.toString(), headers);
//   ...
ResponseEntity<String> forEntity = keycloakRestTemplate.exchange(logoutURI, HttpMethod.POST, entity, String.class); // *FAILURE*

Comme vous pouvez le constater, j’ai essayé de nombreuses variantes de thème, mais j’ai continué à obtenir une authentification non valide des utilisateurs . Oh, oui. J'ai injecté le secret des identifiants de porte-clés du application.properties dans le champ d'instance d'objet avec @Value

@Value("${keycloak.credentials.secret}")
private String keycloakCredentialsSecret;

Des idées d'ingénieurs expérimentés de Java Spring Security?

ADDENDUM J'ai créé un royaume dans KC appelé "démo" et un client appelé "portail Web" avec les paramètres suivants:

Client Protocol: openid-connect
Access Type: public
Standard Flow Enabled: On
Implicit Flow Enabled: Off
Direct Access Grants Enabled: On
Authorization Enabled: Off

Voici le code qui reconstruit l'URI de redirection, j'ai oublié de l'inclure ici.

final String scheme = request.getScheme();             // http
final String serverName = request.getServerName();     // hostname.com
final int serverPort = request.getServerPort();        // 80
final String contextPath = request.getContextPath();   // /mywebapp

// Reconstruct original requesting URL
StringBuilder url = new StringBuilder();
url.append(scheme).append("://").append(serverName);

if (serverPort != 80 && serverPort != 443) {
    url.append(":").append(serverPort);
}

url.append(contextPath).append("/offline-page.html");

C'est tout

0
peter_pilgrim