Keycloak token doesn't include mapped user session note on first login - keycloak

I have created a custom IDP and whenever I login I need to set a User Session Note which is mapped in Keycloak and then added to the access token. The problem I am facing is that this User Session Note is not saved the first time a user logs in. It works the second time, and third and so on, but on the first login the user does not simply get the key/value added to the token.
The login succeeds every time, however it is missing the user session note that I wanted to be mapped on the token.
I'm not sure what the problem is. The IDP is set up to have a custom "First Login Flow" which has Create User If Unique (alternative) and Automatically Set Existing User (alternative). Maybe there is something here that I need to change so that it also includes User Session Notes on token for the first login?

I found that you need to implement the importNewUser() method in your extension of AbstractIdentityProviderMapper and set your notes on the authenticationSessionModel there.
Inside of the authenticated method, where preprocessFederatedIdentity() is called, Keycloak checks if federatedUser == null. At this point, you are in the state of preprocessing the federated user; it does not yet exist. Keycloak then calls resetFlow() within this if block where it then clears auth notes from the authenticationSessionModel.

Hopefully someone finds a more delicate solution than I did, but I managed to solve this by a little hack.
I noticed that in the provider I could override the method "preprocessFederatedIdentity" and when listing the User Session Notes there I saw that they do in fact exist on the session. However, when reaching the method "authenticationFinished" (also overridden) they have been cleared from the session (the clearing seems to happen right before method "updateBrokeredUser" is reached). This odd behaviour only happens on the first login.
What I then noticed is that User Attributes stored on the context however are not cleared. They persist, and this was key to my solution. Then what I did was simply to loop through all of the User Session Notes in the method "preprocessFederatedIdentity" and store them as User Attributes (with the key prefixed with "UserSessionNote:"), and in the latter step "authenticationFinished" I add them to User Session Notes again. I also stored all of the keys of the User Session Notes in a User Attribute called "UserSessionNotesKeys" as a comma separated string so that I could simply split that string and loop through the attributes in the "authenticationFinished" method and then add them to User Session Notes.
This basically solved my issue in a generic way, and also made sure it would work if future User Session Notes were added.

Related

Keycloak - User Session Note Expires Too Early

I have a custom required action that attaches a custom attribute to the user's session by setting a session note.
requiredActionContext.getAuthenticationSession().setUserSessionNote("key", "value");
The problem is that this note disappears (gets deleted from the session) after about a minute (I believe that it's the expiry of the access token) and it means that every minute I'm required to fill the required action form again.
The session itself doesn't expired because I'm not prompted to enter my username/password again. Only the session notes expire.
Is there a way to attach custom attributes to the user's session that would last as long as the actual user session (will last as long as I'm not required to re-enter credentials)?
My problem was that I was setting the required action (addRequiredAction) in the custom user storage provider. It seems like calling addRequiredAction clears the session notes.
I fixed it by creating a custom authenticator that would run after the username/password form and setting the required action there.

JWT logout feature without using blacklist?

I have used JWT before, but they were API that didn't need logout feature.
I need to implement logout feature for a API of an android app and SPA. When I looked it up I found that there are two ways to do it.
the easiest way is to delete the JWT Token from client side and call it a day.
The logic behind this is that since no session of any kind is maintained in server deleting the token in client side should be enough.
But it still leaves the possibility that, if the token falls in wrong hands they can still use it even after the user is no longer using the token.
Given if the app is well designed and uses HTTPS then chances of this happening is very low and can be minimized by keeping the valid time for the token short. But in my case the tokens are valid for 30 days.
the second option is to maintain a blacklist of tokens in server side
This solves the problem of the token still being usable even after user has logged out and stopped using it.
But it adds a complication of needing to run a cronjob to remove expired token form the blacklist table. Otherwise the table will eventually become ridiculously large.
It also kinda defeats the point of using JWT. Maintaining blacklist is very similar to maintaining session. We have to run an additional db query for every request. And it scales badly, as the no. of users grows the no. of token that needs to be blacklisted will also grow (this will be a bigger problem for API like mine with multiple front end apps and long validity period for the tokens).
Then I got an idea for third way.
Add jwt_secret row in user table that stores randomly generated string. Use it to sign the JWT Token then on every request use user id in the jwt payload to get the user form db(which is not an extra query, we have to do this anyway) and validate the token signature using jwt_secret of the user. When the user logs out we change the jwt_secret which makes all token out there useless.
At first I thought this was a great solution only to realize that in this setup if user logs out of one device or browser he/she gets logged out of all devices.
So is there a another option? Or a way to modify any of above approach to solve the problem. Or am I over thinking this and one of the above option should be used?
For logging out, which as you pointed out is a user initiated action, I don't think you need to do anything extra. If the user somehow did not delete his JWT, then so be it. He wouldn't be getting any extra access over to what he is already entitled.
However, your question seems to hint on the problem of how to know that a JWT is valid. Again, as you pointed out, if a JWT somehow fell into the wrong hands, then there may be no avoiding this. But, with each request you would typically be doing several types of validation against that JWT, e.g.
checking the claims of the JWT, such as the token expiry date
assuming the claims pass, then checking that user's ID against your database table to make sure the account is active, has not been suspended, etc.
My point here is that if you need to keep track on the server side that a logout has happened, you might need to persist this to a database. But, I don't think you would need this.

Single Sign Off do not clear RP's own session

I am new to WIF.
And now I encountered a problem when performing single sign off.
The following is the background of my problem:
First of all, I am working on two old application A and B, which they stores the user's information in their own session variable after authenticating a user. And App A and B have their own local database. Now, my work is to use ADFS to enable SSO (Single Sign On) between two applications.
Now, I create a new active directory to store the users centrally.
When a user try to login application A through ADFS, application A need to check the user name in the claim is exist in the local database. If exists, the user can login to App A. Otherwise, App A will reject the claim.
Here is the technical part:
I extend the WSFederationAuthenticationModule and check the incoming claim with the local database in the method OnSessionSecurityTokenCreated. If the result is matched, I retrieve the user information from local database and store it in the session variable (so that i can minimized the code change). App B uses the same approach.
I am happy with the SSO from here. But the problem come when i perform single sign off:
When I logout from App A, i first clean up the own session in App A and call the WSFederationAuthenticationModule.FederatedSignOut method. However, App B do not clean up its own session. I suppose the OnSignedOut in WSFederationAuthenticationModule will be invoked. How can i clean up the App B's session when App A is signed out?
It may be very confusing. Please leave any comments if you find something unclear or need further explanation.
Signout in WS-Fed is implemented by ADFS (it asks for a gif with app/?wa=wsignoutcleanup1.0), not only locally. That is the only way to notify the other app.
Your app has to to redirect to ADFS with a signouturl. Then both apps will be notified and can cleanup their session state (with the gif request).

Stop regenerating/invalidating CSRF on authentication

I currently have forms on my page, which are there regardless of if a user is logged in or not. Once a user logs in, they are presented with one of these forms (which use CSRF).
The issue is that if this box is presented after the authentication, the CSRF tokens are invalidated. I have confirmed this by allowing myself to submit the form without authentication checks and $form->isValid() returns true whereas after login, it gives me false with the error of:
The CSRF token is invalid. Please try to resubmit the form.
I guess there are three solutions - stop Symfony from regenerating/invalidating the CSRF tokens on authentication, remove the CSRF tokens from these forms or generate my form after authentication (I'd rather avoid this, however). My current solution is to pass a new CSRF token back with the authentication and set forms token input value.
Additional: Does anyone know how to view all CSRF tokens that are currently assigned? The session doesn't seem to hold them.
The usual approach is that when someone logs in successfully, the whole page is refreshed, as potentially different data is going to be displayed, customised to the user.
However if you really don't want to do that, You said:
My current solution is to pass a new CSRF token back with the authentication and set forms token input value.
Just do that. There is nothing wrong with that.
Thanks to #MarkFox for pointing me to this CWE - although I knew it was through design, I'd hoped there would be a way for me to avoid going down the route I ended up.
For anyone interested, the CWE states:
Authenticating a user, or otherwise establishing a new user session, without invalidating any existing session identifier gives an attacker the opportunity to steal authenticated sessions.
For this reason, I recommend that you go down the path I ended up, sending the new request tokens via AJAX and place them into the relevant forms.

Is it Safe/Good practice to save global values in NSUserDefaults?

I am making an IPhone app in which
userid and password is required in
all the screens to make requests to
the server, and I am thinking of
saving those 2 values in
NSUserDefault instead of passing an
object around.
I am also thinking it will be useful if user has logged in once,
and use the app again then user
don't have to enter his/her details
again.
But I am curious if it will be safe/good practice to use for first requirement?
I don't have anything against save these data on the user defaults. What I don't get is the idea to expose the user credentials on each request.
I would suggest you to ask for the credentials once, authenticate with your server and return a "session token". save this token and use it to validate the user on each request. (it means that you will save the token on you server or you will check the token using an algorithm)
Doing this you don't expose the user credentials all the time, you have control over the session, and you can expire it when you want, forcing the user to logging again.
For more complex implementations, you could Google for OAuth or XAuth and some related methods of authentication.
Cheers,
vfn
It's reasonable to save global values in NSUserDefault that you want to survive your app being killed and restarted (as can happen under iOS4.0).
Passwords should be saved in memory (maybe a singleton model object), or in the keychain, as various iTunes backup databases might expose stuff stored in user defaults.