What is the proper way to invoke Keycloak's end_session_endpoint - keycloak

Environment: Keycloak 12.0.4
We plan to allow various OpenIDC-protected applications to initiate a "single sign-out" using Keycloak's end_session_endpoint. In our case, we'd like to provide our own URL that applications will hit when they want to sign-out. That URL will either programmatically invoke the end_session_endpoint or simply redirect the user's browser to that endpoint.
The Keycloak documentation and examples I've seen so far are a bit confusing regarding how to invoke this endpoint. For example, do I need to add a query parameter with the id token as a value? (I noticed, for example, that mod_auth_openidc includes an id_token_hint query param.) I assume this call has to be authenticated, so do I include the client id/secret as the credentials?
Thanks!

You have standard how to call RP-Initiated Logout:
https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout
An RP requests that the OP log out the End-User by redirecting the End-User's User Agent to the OP's Logout Endpoint.
It is a redirect, not API call, because you very likely wants to delete also IDP session (cookies on used Keycloak domain in your case).

Here's some additional information from the keycloak-user group which I think completely answers the question:
You can look at
https://openid.net/specs/openid-connect-session-1_0-17.html#RPLogout.
From our implementation, we rely on either cookies or the
id_token_hint to identify the user and logout sessions. So you don't
necessarily need to send id_token_hint if cookies are sent when
redirecting the user from your application to Keycloak. You can also
send a state param to match if the logout action sent to your
application originated from a valid logout request.
Note, however, that we don't force the id_token_hint and do not ask
the user for confirmation, as per spec. But there are discussions to
introduce a consent page.
In regards to logout, messages are sent to clients through the
backchannel.
Edit: Here's the actual (latest) RP-initated logout spec: https://openid.net/specs/openid-connect-rpinitiated-1_0.html.

The logout URL needs to be build from
the OpenID Connect end session endpoint
a target redirect URI where the user should be sent after logout, provided as query param post_logout_redirect_uri
The encoded ID token as query param id_token_hint
In Spring it can be built like this:
String logoutLink = UriComponentsBuilder.fromUri(endSessionEndpoint)
.queryParam("post_logout_redirect_uri", logoutUrlEncoded)
.queryParam("id_token_hint",
((OidcUser) authentication.getPrincipal()).getIdToken().getTokenValue())
.build().toUriString();

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

Restrict front client connexion with groups / roles in a realm

I'm looking for a way to restrict user access to specific clients in a realm.
I know I can do it with client where Authorization is enabled (fine-grained authorization support) but it doesn't work when trying to connect from front (client need to be public and not confidential).
I'm using a javascript application to login from front-end.
Is there a way to enable Authorization for public client or a work around ?
Thanks.
I'm not sure if this will totally answer your question because it's still not specific enougth but it may give you some further help.
In case you're new to the topic, please see difference between public and confidential clients here.
The current best practice for public clients like HTML/Javascipt applications is to use OpenId Connect with the Authorization Code Flow + PKCE. HTTPS is of course a must have. I recommend you use a javascript openid connect adapter for this like the following for example:
https://github.com/panva/node-openid-client
Basically your authentication / authorization flow is shown here:
When the user wants to login from your frontend client application first a unique verifier is generated which is only available to the exact user / browser session. This value get's hashed as a code challege. Then the user gets redirected to the login page of your authorization server (Keycloak for example) passing some parameters like a redirect uri and the challenge.
With successful login the user get's a session at the keycloak server which also stores the hashed challenge. Then the user gets redirected to given redirect uri (a path in your application) together with a code to obtain an access token. Back in your application you application uses the original value together with the code to get the actual token. The authorization server ckecks the value against the stored challenge and geturns the access token if it matches. You see the extra verifier is to prevent that anybody compromises your code fragment to obtain a token on your behalf.
Now you have an encoded access token in your browser app. Note the token itself is normally only encoded not encrypted but it can be signed. Those signatures can later be used from your backend to ckeck the token integrity but we will come to that soon. Roles, claimes, scopes and so on included in your access token tell you the privileges of the user/identity. You can of course use them to enable/disable functions in your app, block routes etc. but in the end client protection is never really effective so your real authorization ande resource protection happens at your resource server which is your backend (node, .net core, java etc.) maybe a restful Web Api. You pass your access token as a part of the http request header with every request to the backend. Now your backend checks the token integrity (optional) expiration time etc. analyzes scopes, claimes and roles to restrict the resource access.
For example a simple GET myapi/car/{1} may only need a token or can even be annonymous while a POST myapi/cars or PUT myapi/car/{1} may need a special role or higher privileges.
Does that help you out?

Keycloak Silent Authentication

I have a react app that uses Keycloak endpoint token_endpoint to authenticate the user. The problem I'm facing now is how to update/refresh this token. I know I could use a refresh token but that's a bad practice since has security issue when it comes to a web application. It seems to me that the best approach would be to use Silent Authentication.
Unfortunately, I couldn't find any example or documentation that allows me to achieve this. In case you guys have another approach I'm open to suggestions.
Tks
It would consist just of providing the prompt=none parameter on the authentication request as stated in the OpenID Connect 1.0 Core spec:
https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
prompt OPTIONAL.
Space delimited, case sensitive list of ASCII string
values that specifies whether the Authorization Server prompts the
End-User for reauthentication and consent. The defined values are:
none
The Authorization Server MUST NOT display any authentication or
consent user interface pages. An error is returned if an End-User is
not already authenticated or the Client does not have pre-configured
consent for the requested Claims or does not fulfill other conditions
for processing the request. The error code will typically be
login_required, interaction_required, or another code defined in
Section 3.1.2.6. This can be used as a method to check for existing
authentication and/or consent.
Authenticate to obtain an access_token with /auth (prompt=login).
Just call OIDC /token with grant_type=refresh_token to refresh token with the access_token. the new response include access_token, refresh_token and so.
After that you must update them for the new api calls.

django rest framework - understanding authentication and logging in

I am a beginner to django rest framework (and to REST in general) and I have a server side which (for now) has a UserViewSet which allows to register new users and I can POST to the url from my android app just fine (I get 201 CREATED).
I read a lot about it, but I don't seem to fully the understand the concept of Login and Authentication in REST frameworks and specifically in django rest framework, and how it works.
Do you "Log in" (like in facebook for example) and then you can make requests?
What I understand\heard off:
you can Login to a API\website using your username and password (assuming off course that you have registered as a user and you are in the user database).
After you are Logged in - you will be able to make requests to views that allow access only to logged in\authenticated users.
Is that somewhat correct? I mean, is there a "Log in" url where you login and that's it? you are authenticated?
Also read somewhere that there isn't actually a login url, and you have to add your username and password to each request and then the request has to check if your details are in the User database?
To sum up, I am not really sure how does authentication/logging in (same thing?) happens in django REST framework... and would really appreciate a good explanation or an example..
Thanks a lot!
In a normal web application (removing the API from the question), a user would "log" in with their credentials (username/password, social tokens, etc.) and would receive a session cookie (assigned by Django) that allows them to authenticate in future requests on behalf of a user (realistically, themselves). This session cookie stays on their system for a limited period of time (two weeks by default) and allows them to freely use the website without authenticating again. If the session cookie needs to be removed, such that the person can no longer authenticate, the web application typically destroys the session cookie (or clears the session) which effectives "logs them out".
In the case of an API, it all depends on how the authentication works.
SessionAuthentication works just like as described above, as it uses Django's internal session system.
TokenAuthentication remembers the authentication information through a database-backed token (which is transmitted in the Authorization header) instead of a session cookie.
BasicAuthentication authenticates on every session (no persistent session) by passing the username and password on every request (base64 encoded through the Authorization header).
Other authentication methods generally work in the same way as TokenAuthentication.
So, here are some answers to specific questions which were raised
Do you "Log in" (like in facebook for example) and then you can make requests?
Using BasicAuthentication, you "log in" on every request by providing your credentials. With token-based authentication (TokenAuthentication, OAuth 2, JWT, etc.), you "log in" to receive the initial token and then your authorization is confirmed on every request.
Also read somewhere that there isn't actually a login url, and you have to add your username and password to each request and then the request has to check if your details are in the User database?
This is basic access authentication which you can use in DRF using the BasicAuthentication class.

Use LinkedIn JSAPI credentials cookie to authenticate a user

We would like to implement "Sign-in with LinkedIn" in our app. Since the app has JS fronted and RESt-based backend, we decided to exchange JSAPI tokens for REST API OAuth tokens as described here.
If a user successfully signs in, the frontend sends credentials cookie with client-side bearer token and member ID to the backend. On the backend we check if a user with such a member ID already exists and if not, we exchange JSAPI token for REST API OAuth token, retrieve user details from LinkedIn a store it in our database.
Now the question is if we can use that cookie to authenticate each user's request to our REST backend. After a user successfully signed in via JSAPI, the cookie should be automatically passed to our backend on all subsequent requests so we can check member ID. Are there any drawbacks that we missed? Or is this idea as a whole wrong?
Should we rather authenticate a user only once by means of the cookie and then issue our own authentication token and send it back to the client?
The way cookies work in general is they are passed on every request to the domain they belong to. LinkedIn is setting a credentials cookie to your domain.
As long as you are validating those credentials on every request it's perfectly acceptable to use their tokens as authentication.
Personally I don't find that to be a great idea and would prefer to validate their credentials once and create my own auth token to use from there on out. You can always set that token to expire at some-point and re-validate the LinkedIn credentials (which will still be getting sent on every request anyway). This limits the amount of times you're checking with LinkedIn and should increase the responsiveness of your app.
Either way could work.
If you are using the LinkedIn cookie to validate a user by member id, you should validate the cookie's signature on each request per section 2 of the doc you linked and question 2 of the FAQ.
Using your own token could make it easier to implement an account which belongs to your app and is not necessarily connected to LinkedIn, assuming there's the potential to either connect solely with some other service(s) or no 3rd part(y/ies). Still should validate any time you trust the member id in the cookie though.
The doc provides a validation example in PHP, and if you're interested in improving a ruby version, I have a shameless plug.
The flow that you've outlined in your latest comment of going straight for the OAuth tokens is the best way to go if you were only signing in to convert the JSAPI tokens to OAuth tokens and then not using the JSAPI further. If you were planning to actually use both the JSAPI tokens within your front-end app and the OAuth tokens on your back-end, then it's better to take the conversion route.