Keycloak logout response 414 - keycloak

GET /realms/[REALM_NAME]/protocol/openid-connect/logout?post_logout_redirect_uri=[...]&id_token_hint=[..very large jwt-token >4096..]
Response 414 (Uri to long (link)
Keycloak version: v20.0.1
Expected: Successful logout redirect. The above request should response 302.

AFAIK, you should pass an ID token to the id_token_hint and not and access token. From the OpenID Connect standard (section 2.RP-Initiated Logout) one can read:
This specification defines the following parameters that are used in
the logout request at the Logout Endpoint:
id_token_hint RECOMMENDED. ID Token previously issued by the OP to the
RP passed to the Logout Endpoint as a hint about the End-User's
current authenticated session with the Client. This is used as an
indication of the identity of the End-User that the RP is requesting
be logged out by the OP.
So you need to pass id_token_hint=<id_token>. You get the ID token by calling the token endpoint with the scope=openid. For example, when a user logs in via browser if you request includes the scope=openid you will get (along with the refresh and access tokens) the user ID token.

It seems that the logout URI path is to long. The default length of the keycloak quarkus service is 4096 bytes (link).
this occur when Your id_token_hint is to large
Set higher length for quarkus parameter quarkus.http.limits.max-initial-line-length ...
as java parameter -quarkus.http.limits.max-initial-line-length=8192
or environment variable QUARKUS_HTTP_LIMITS_MAX_INITIAL_LINE_LENGTH=8192

Related

Keycloak 18.0.2 get id_token_hint for logout url by the API call

Is it possible with Keycloak 18 to get id_token_hint value, required for logout url via direct API call to the Keycloak server? If so, could you please show how?
Also, is this safe to keep id_token_hint value on the client side, let's say in JWT claim?
I am not sure if I fully understood your question, nonetheless from the OpenID Connect standard (section 2.RP-Initiated Logout) one can read:
This specification defines the following parameters that are used in
the logout request at the Logout Endpoint:
id_token_hint RECOMMENDED. ID Token previously issued by the OP to the
RP passed to the Logout Endpoint as a hint about the End-User's
current authenticated session with the Client. This is used as an
indication of the identity of the End-User that the RP is requesting
be logged out by the OP.
So you need to pass id_token_hint=<id_token>. You get the id token by calling the token endpoint with the scope=openid. For example, when a user logs in via browser if you request includes the scope=openid you will get (along with the refresh and access tokens) the user id token.
Not the best option, but works, and you dont need id_token_hint
when start keycloak add the following parameter to the command line:
kc.sh start --spi-login-protocol-openid-connect-legacy-logout-redirect-uri=true
Still have to confirm the logout when you call logout page,but you can use redirect_uri in the old way.
https://keycloak.lvh.me/realms/airports/protocol/openid-connect/logout?redirect_uri=https%3A%2F%2Foauth2.lvh.me%2Foauth2%2Fsign_out

How does a Keycloak client validate a token

I would like to know how does a Keycloak client validate the token, other than checking the signature.
I mean if a user has issued a logout request to the OID '/logout' endpoint, the token signature verification would still pass, so if the token has been invalidated by a logout, the client has to go to the KC server. So my question is, does the client go to Keycloak to check that the token is still valid, for every request that my app receives?
In OIDC normally the token is not actively checked by sending it to the keycloak server everytime. Instead the token is only checked locally (by signature verfication with the servers public key).
So you are right, a logout is not immediately recognized by a client.
Because of that you often have a flow where you distinguish between a refresh and access token. The access_token has a really short lifetime (<= 5 minutes), is used to authenticate the user and checked via signature validation.
When the access_token is not valid anymore the client need to use the refresh_token to actively obtain a new access_token from keycloak. In this step keycloak will check if the user is still logged-in before issuing the new token.
Because of that the refresh_token can have a much longer lifetime and it's not always necessary for the user to enter his credentials again.

Keycloak - Retrieve JWT token via OIDC Endpoint

I'm currently trying to retrieve a user token from the keycloak token endpoint using a POST request (instead of using one of the designated adapters). I have set up a keycloak realm and added my own machine as a client. In the documentation the Token Endpoint is described as:
/realms/{realm-name}/protocol/openid-connect/token
As far as I have read in the openid specification, I will need to set the body parameter grant_type=authorization_code as well as the parameters code and redirect_uri. I will also need to set the Authorization header, for which I will need a Basic Token.
So far I will get the response:
"error": "unauthorized_client", "error_description":
"INVALID_CREDENTIALS: Invalid client credentials"
Where do I get the Basic Authorization Token from? I expected that I need to provide a username and a password, since the JWT token is what I'm trying to recieve as response. Do I need to set the redirect_url if I just want to request a token?
Keycloak offers more than one way to retrieve a user access token, following the OpenId Connect spec. Here you have the steps to do it for Authorization code flow (the one recommended for web applications) according to the openid connect spec: https://rograce.github.io/openid-connect-documentation/explore_auth_code_flow
Basically, if you're not using any adapter, when detecting a request to some protected resource you should:
Perform a redirection to the keycloak login page (keep in mind keycloak uses the REALM entity, so you'll need to specify it too):
HTTP/1.1 302 Found
Location: https://mykeycloakinstance.org/auth/realms/demo/protocol/openid-connect/auth?
response_type=code
&scope=openid
&client_id=s6BhdRkqt3
&state=af0ifjsldkj
&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
You'll need to keep the state value in the client, as it needs to survive the redirection process:
It is recommended that client’s use this parameter to maintain state
between the request and the callback. Typically, Cross-Site Request
Forgery (CSRF, XSRF) mitigation is done by cryptographically binding
the value of this parameter with a browser cookie.
You don't interact with username/passwords. The keycloak authentication page does. Once the login is successful, it will redirect to your page with a valid code:
HTTP/1.1 302 Found
Location: https://client.example.org/cb?
code=SplxlOBeZQQYbYS6WxSbIA
&state=af0ifjsldkj
Here you'll need to either check that the state is the one you originally sent (you may need to track it through web session, using cookies) and also to obtain the token using that code. You do a POST to the authorization endpoint with this code:
POST /auth/realms/demo/protocol/openid-connect/auth HTTP/1.1
Host: https://mykeycloakinstance.org
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
This is the flow in summary, I haven't tested the code myself, so use it as an example and don't hesitate to fix it if you consider ;-)
See also:
What are Keycloak's OAuth2 / OpenID Connect endpoints?

what is the function of access token in mobilefirst 8

At first, i expected access token to be necessary while communicating with WL resource.
Expected flow:
1. WLAuthorizationManager.login(this.securityCheck ,{'username':username, 'password':password, rememberMe: true}).then(
2. WLAuthorizationManager.obtainAccessToken(this.securityCheck).then(
3. let resourceRequest = new WLResourceRequest('someURL', WLResourceRequest.GET)
resourceRequest.addHeader("Authorization", "Bearer " + accessToken);
4.
resourceRequest.send().then(
where 1 = login, 2 = get access token, 3 = add access token to header, 4 = access resource
However, i find that without 2, 3, i can still access the resource.
It comes to my concern what is the meaning of obtainAccessToken and add Authorization header.
Is there any token auto bound to WLResourceRequest after login?
Is there other way to login without using WLAuthorizationManager.login?
If ok, how to let server know the user logged in like using WLAuthorizationManager.login?
If the above is true, after custom login, can obtain access token?
Here's some background about these methods and their working:
WLAuthorizatonManager.login(securityCheck, credentials) logs into a
specified security check. This method does not create an OAuth token.
More details about WLAuthorizatonManager.login(securityCheck, credentials).
WLAuthorizationManager.obtainAccessToken(scope) returns an OAuth token
containing the specified scope.If the scope is mapped to a security check, it will trigger a corresponding challenge, which the client will have to handle to obtain the token. If obtainAccessToken(scope) is invoke after a successful login(securitycheck) call and if the scope is mapped to the same securitycheck, then you will not see a challenge. The OAuth token will be granted.
More details about obtainAccessToken().
WLResourceRequest object is used to send a request to any protected or
unprotected resource using an absolute or relative URL.
WLResourceRequest object automatically handles the MobileFirst
OAuth-based security model protocol and invokes the required
challenges.
Details about WLResourceRequest.
To answer your questions:
Is there any token auto bound to WLResourceRequest after login?
As mentioned earlier, WLResourceRequest automatically negotiates an OAuth token from MFP server containing the scope that protects the endpoint it is accessing. This may include multiple rounds of OAuth negotiation and also invoke the challenge handlers to handle challenges originating from the server. Once the right token has been obtained, the API automatically adds the token to the request in an 'Authorization' header.
Is there other way to login without using WLAuthorizationManager.login?
Instead of WLAuthorizatonManager.login(securityCheck, credentials), if a protected resource is accessed via WLResourceRequest or if WLAuthorizationManager.obtainAccessToken(scope) is invoked for a scope that is mapped to a security check, this will trigger a challenge response cycle that will end with a user identity and a token.
If ok, how to let server know the user logged in like using WLAuthorizationManager.login(securityCheck, credentials)?
MFP runtime takes care of this - regardless of if the user identity is set via WLAuthorizatonManager.login() or if WLResourceRequest / WLAuthorizationManager.obtainAccessToken(scope) triggers a challenge response cycle that goes through the securitychecl.
If the above is true, after custom login, can obtain access token?
Not sure what you mean by 'custom login', but you can always obtain an OAuth token using WLAuthorizationManager.obtainAccessToken(scope) or WLResourceRequest. The difference is that obtainAccessToken() will get you a token for the scope you specify, while WLResourceRequest will invoke an endpoint by obtaining an OAuth token covering all the required scopes automatically.

Is checking subsequent REST API request with token secure enough?

In my REST API, user will log in with their username and password for the first time.
When they logged in successfully, we will response with the following format.
{
"token": "0c7f8b870675bc61d92baeef1e274c2d31343736393530373230",
"expire_on": "2016-11-19T18:05:20+0000",
"user_id": 30,
"user": {...}
}
On the subsequent to the REST API, we will just send token in the header to verify the user. token is 52 letters long.
Is it secure enough?
Should I send both token and user_id to verify to secure more?
It depends on how do you generate your token.
If someone can guess how they are generated then it can create a fake one.
If this is a random string that is saved on your server and for each request you check the existence in your server then you are safe.
The best current solution for stateless token is JWT. Take a look at https://jwt.io/
Did you implement your authentication layer? I would suggest having a look at the Oauth2 specification and you are interested in the section when the grant_type is password. If you follow it, it will be safe to return just the access token:
5.1. Successful Response
The authorization server issues an access token and optional
refresh token, and constructs the response by adding the following
parameters to the entity-body of the HTTP response with a 200 (OK)
status code:
access_token
REQUIRED. The access token issued by the authorization server.
token_type
REQUIRED. The type of the token issued as described in
Section 7.1. Value is case insensitive.
expires_in
RECOMMENDED. The lifetime in seconds of the access token. For
example, the value "3600" denotes that the access token will
expire in one hour from the time the response was generated.
If omitted, the authorization server SHOULD provide the
expiration time via other means or document the default value
.