Logout user via Keycloak REST API doesn't work

keycloak logout redirect uri
keycloak create user api
keycloak-user attributes api
keycloak account management rest api
keycloak assign role to user api
keycloak get user roles
keycloak reset password api
keycloak get user info

I have issue while calling Keycloak's logout endpoint from an (mobile) application.

This scenario is supported as stated in its documentation:

/realms/{realm-name}/protocol/openid-connect/logout

The logout endpoint logs out the authenticated user.

The user agent can be redirected to the endpoint, in which case the active user session is logged out. Afterward the user agent is redirected back to the application.

The endpoint can also be invoked directly by the application. To invoke this endpoint directly the refresh token needs to be included as well as the credentials required to authenticate the client.

My request has following format:

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>

but this error always occurs:

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"
}

It seems that Keycloak is unable to detect the current client's identity event if I've provided access_token. I've the used same access_token to access other Keycloak's APIs without any problems, like userinfo (/auth/realms//protocol/openid-connect/userinfo).

My request was based on this Keycloak's issue. The author of the issue got it worked but it is not my case.

I'm using Keycloak 3.2.1.Final.

Do you have that same problem? Have you got any idea how to solve it?

Finally, I've found the solution by looking at the Keycloak's source code: https://github.com/keycloak/keycloak/blob/9cbc335b68718443704854b1e758f8335b06c242/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java#L169. It says:

If the client is a public client, then you must include a "client_id" form parameter.

So what I was missing is the client_id form parameter. My request should have been:

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>

The session should be destroyed correctly.

[keycloak-user] How to logout, only one asking questions about this either: 1. https://stackoverflow.com/​questions/46689034/logout-user-via-keycloak-rest-api-doesnt-work  Keycloak doesn’t have an initial admin account by default; to be able to log in, you need to provide KEYCLOAK_USER and KEYCLOAK_PASSWORD environment variables, you need to specify a database for Keycloak to use, with the easiest option being an embedded H2 instance; if you’d like to provide another (PostgreSQL, MySQL,

in version 3.4 you need as x-www-form-urlencoded body key client_id, client_secret and refresh_token.

Keycloak Admin REST API, /realms/{realm-name}/protocol/openid-connect/logout. The logout endpoint logs out the authenticated user. The user agent can be redirected to  Keycloak is an open source identity and access management solution. It has a built-in RDBM system to store login information. It can help build a security layer on top of the cBioPortal web application. Keycloak boils down to three simple terms: realm: A realm secures and manages security metadata for a set of users, application,

Works with Keycloak 6.0.

Just for clarity: we do expire refreshToken, but accessToken IS STILL VALID while "Access Token Lifespan" time. Next time user tries to renew access token passing refresh token, Keycloak returns 400 Bad request, what should be catch and send as 401 Unauthorised response.

public void logout(String refreshToken) {
    try {
        MultiValueMap<String, String> requestParams = new LinkedMultiValueMap<>();
        requestParams.add("client_id", "my-client-id");
        requestParams.add("client_secret", "my-client-id-secret");
        requestParams.add("refresh_token", refreshToken);

        logoutUserSession(requestParams);

    } catch (Exception e) {
        log.info(e.getMessage(), e);
        throw e;
    }
}

private void logoutUserSession(MultiValueMap<String, String> requestParams) {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

    HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(requestParams, headers);

    String url = "/auth/realms/my-realm/protocol/openid-connect/logout";

    restTemplate.postForEntity(url, request, Object.class);
    // got response 204, no content
}

Logout user via Keycloak REST API doesn't work - keycloak, I have issue while calling Keycloak's logout endpoint from an (mobile) application​. This scenario is supported as stated in its documentation:  Keycloak Admin REST API. Table of Contents Clear any user login failures for all users This can release temporary disabled users which this client doesn’t

FYI: OIDC spec and Google's implementation has a token revocation endpoint but currently this is not implemented in Keycloak so you can vote for the feature in Keycloak JIRA

[KEYCLOAK-2476] Problem with REST logout through OpenID , The only thing that it does, when I'm invoking logout on Master realm, it logouts currently logged in admin user, even if there is no header and any other  One of Red Hat SSO’s strongest features is that we can access Keycloak directly in many ways, whether through a simple HTML login form, or an API call. In the following scenario, we will generate a JWT token and then validate it. Everything will be done using API calls, so Keycloak’s UI is not exposed to the public directly. Set up a user

I tried this with Keycloak 4.4.0.Final and 4.6.0.Final. I checked the keycloak server log and I saw the following warning messages in the console output.

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

So how did build the HTTP request? First, I retrieved the user principal from the HttpSession and cast to the internal Keycloak instance types:

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();

Second, I created the logout URL as in the top stack overflow answer (see above):

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

And now I then build the rest of the HTTP request like so:

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"));

And also build the body content string:

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*

As you can observed, I attempted many variations of theme, but I kept getting invalid user authentication. Oh yeah. I injected the keycloak credentials secret from the application.properties into object instance field with @Value

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

Any ideas from Java Spring Security experienced engineers?

ADDENDUM I created a realm in KC called 'demo' and a client called 'web-portal' with the following parameters:

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

Here is the code that rebuilds the redirect URI, I forgot to include it here.

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");

That's all

[keycloak-user] Log out server sessions when using , Now single sign out won't work, because Keycloak is neither aware of the and internally via an extended REST API tell the Keycloak server to  If a browser is not available the URL to login is displayed on the CLI. The user can copy this URL to another computer that has a browser available. The code is displayed to the user after login and the user has to copy this code back to the application. Step 7: Admin REST API. Keycloak has a Admin REST API.

keycloak/LogoutEndpoint.java at master · keycloak/keycloak · GitHub, Cannot retrieve contributors at this time Logout user session. When the logout is initiated by a remote idp, the parameter "initiating_idp" can be supplied. you must include the client-id and secret in an Basic Auth Authorization header​. If you are using Java, you can access the Keycloak Authorization Services using the Authorization Client API. It is targeted for resource servers that want to access the different endpoints provided by the server such as the Token Endpoint, Resource, and Permission management endpoints.

Removing Keycloak session after SAML Logout, I am using Keycloak v6.0.2 and the Keycloak node JS adapter. It must make a REST API call to the Keycloak server to destroy the user session, given that this does Note that our IDP doesn't support backchannel logout. Keycloak is an open source identity provider owned by Red Hat. It allows to easily add authentication to any application and offers very interesting features such as user federation, identity brokering and social login. In the .Net ecosystem, one of its “competitor” would be Identity Server or OpenIddict with Asp.Net Identity.

Logout, The next time a user is redirected to the Auth0 Lock screen, the user's information will be remembered. Log users out of Auth0 by clearing the Single Sign-on (SSO)​  Synopsis ¶. This module allows the administration of Keycloak clients via the Keycloak REST API. It requires access to the REST API via OpenID Connect; the user connecting and the client being used must have the requisite access rights.

Comments
  • Has anyone got a solution for Keycloak 4.* series?
  • From this answer, I don't understand what does your client_id map to in a Spring Web application? I tried 'IdToken.id', 'AccessTokenId.id' and also the 'context.tokenString' - every time I get the error message 'invalid client credentials'
  • @SwissNavy: you get the refresh token when you log user in (meaning you might have send a request to localhost:8080/auth/realms/<my_realm>/protocol/openid-connect/token), the response of that request might look like this: <code> { "access_token": "<access_token_value>", "expires_in": 299, "refresh_expires_in": 1799, "refresh_token": "<refresh_token_value>", "token_type": "bearer", "not-before-policy": 0, "session_state": "<session_state_value>", "scope": "profile email" } </code> You can find the refresh token in that response.
  • @SwissNavy: it depends on how you integrate with Keycloak: Which OpenID Connect flow (Implicit Flow/Authentication Flow/Resource Owner Password Grant/Client Credentials Grant), because I think that not all of these flows give you a refresh token. You might need to refer to the OpenID Connect protocol's docs for more information. What technology stack you are using is also important because certain library/framework might store differently the refresh token. Might I ask what flow + technology stack you are working on?
  • I've never used Django (because I'm a Java guy 😅) but I try to look at the framework you are using (Social Core) for Keycloak implementation. I does not seem to store refresh token anywhere: github.com/python-social-auth/social-core/blob/… . It'd be better you address your question to the library maintainers.
  • Your second question about the realm.json, I don't think that parameter means you are using implicit. You might want however analyse the network exchanges between your app and Keycloak by using developers.google.com/web/tools/chrome-devtools/network/… if you are using Chrome. Look for a parameter grant_type, it should be the flow you are looking for.
  • Sending client_id and client_secret in body is discouraged by RFC 6749, better to use HTTP Basic authentication instead. Only refresh_token needs to be sent in body, I checked that it works with Keycloak.
  • How did you manage to also expire the accessToken?. My concern is that if the access token is still valid after logout then there is a security risk. How can I handle that?
  • usually accessToken is valid for short time, like 15 min. How to force to expire it, I don't know, ask a question in keycloak forum keycloak.discourse.group
  • You are using which kind of grant_type for your login flow? I was using grant_type=password because it was a mobile client with a native (iOS) login form. Because of that, I had to POST to the endpoint but was not able to redirect user to Keycloak signout page. It seems that you are developping a web application, have you try just redirect user to the sign out page of Keycloak: keycloak.org/docs/latest/securing_apps/index.html#logout?
  • My grant type is 'public' and client protocol is 'openid-connect' and I am also using Vaadin 10 to secure a Java web application
  • I just added redirectURI code to my answer above. The other thing that Vaadin 10 has different logout mechanism compared to Vaadin 8. It uses a new Flow API. See here vaadin.com/docs/v10/flow/advanced/… I can confirm that Logout without Keycloak works, because I tested their own Vaadin Bakery Spring Security application. However, this does not log the user out of Keycloak and hence I was attempting to make a RESTful call to the Keycloak server to logout the user out and then close the Vaadin (Http) Session. Make sense? :-/
  • OpenID Connect (that Keycloak implements) use the same grant_type as OAuth2, so its value should be one of the following: oauth.net/2/grant-types . As I don't have yet any idea of why your code doesn't work, could you provide a sample Github repo to reproduce the problem? It will be easier for me or others can look at and maybe has a hint about that.
  • I am wondering if this should be a brand new stack overflow ticket. Should I create a new one?