WSO2IS 5.10.0. lastLoginTime update in Custom Local Authenticator - wso2-identity-server

I wrote my custom local authenticator. When I used it the lastLoginTime doesn't update. Logging with basic authenticator is ok.
Which extension responsible update lastLoginTime? What I need to do in my authenticator to update lastLoginTime? Thanks!
Update:
I made custom authenticator for using with user credentials that authenticator gets from XML Signature. The authenticator gets XML, verify signature, extract special_user_id and then checks this id in UserStroe in custom claim. If user with this claim value exists then authentificator gets user_name from User Store and finishes processAuthenticationResponse() with success.
In short
processAuthenticationResponse() {
...
String[] logins = userStoreManager.getUserList("http://wso2.org/claims/special_user_id", specialUserIdValue, "default");
context.setSubject(AuthenticatedUser.createLocalAuthenticatedUserFromSubjectIdentifier(logins[0]));
}

In IS-5.10.0, the last login time is updated only if the idle user suspension feature https://is.docs.wso2.com/en/5.10.0/learn/user-account-suspension/ is enabled.
IS-5.10.0 contains the identity-governance component version 1.4.1. Here is the code lines related to updating the last login time https://github.com/wso2-extensions/identity-governance/blob/57e510a61dc9042b55ffa0cbdceb13f508519f3a/components/org.wso2.carbon.identity.account.suspension.notification.task/src/main/java/org/wso2/carbon/identity/account/suspension/notification/task/handler/AccountSuspensionNotificationHandler.java#L84-L85
It's irrespective whether you use the basic authenticator / a custom authenticator. If you have enabled idle user account suspension feature,at every POST_AUTHENTICATION event this AccountSuspensionNotificationHandler's handleEvent should be invoked and update the last login time.
NOTE: In IS-5.11.0, the last login time value update logic is changed. It can be updated irrespective of idle user account suspension feature. Check https://github.com/wso2/product-is/issues/4515
Update:
Here is the reason for not triggering POST_AUTHENTICATION event when using the custom authenticator.
If you use the basic authenticator, user is authenticated from the IS userstore connecting through the userstore manager.
After the authentication happens in userstore https://github.com/wso2/carbon-kernel/blob/56d068221ab46e205d5b7188e2bc55134bfc08a9/core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/common/AbstractUserStoreManager.java#L1757, the configured listeners' doPostAuthenticate method is invoked.
The POST_AUTHENTICATION event is triggered from IdentityMgtEventListener's , doPostAuthenticate method https://github.com/wso2-extensions/identity-governance/blob/57e510a61dc9042b55ffa0cbdceb13f508519f3a/components/org.wso2.carbon.identity.governance/src/main/java/org/wso2/carbon/identity/governance/listener/IdentityMgtEventListener.java#L122-L127
Since the above flow doesn't get executed when you use a custom authenticator which doesn't authenticate the user from WSO2 IS userstore, POST_AUTHENTICATION event is not triggered.
Possible options to follow to trigger POST_AUTHENTICATION event from the custom authenticator.
Override the public AuthenticatorFlowStatus process(HttpServletRequest request, HttpServletResponse response, AuthenticationContext context) throws AuthenticationFailedException, LogoutFailedException { method to user custom authenticator with same logic in the AbstractApplicationAuthenticator method. Then add
fireEvent(context, IdentityEventConstants.Event.POST_AUTHENTICATION, true);
before
return AuthenticatorFlowStatus.SUCCESS_COMPLETED;
similar to https://github.com/wso2/carbon-identity-framework/blob/527dba704487431b95c34461656cdb7496a0f0cc/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/AbstractLocalApplicationAuthenticator.java#L80-L81
Extend your custom authenticator from AbstractLocalApplicationAuthenticator instead of AbstractApplicationAuthenticator. Note that there are differences in the authentication process method in those two classes.

Related

How can I remove session for specific user for specific ServiceProvider in WSO2 IS?

There is any webservice providing by WSO2 Identity server, to remove a session for specific user to specific ServiceProvider?
I am using SAMLSSO for many web application and they all are integrating with WSO2 server.
Now let's say scenario is,User is login with 2 application at the same time. I want to logout it from one application.
There is one service provided by logout and it's providing single logout and session will be remove for all application. There is webservice provided by IS or way to achieve logout for one specific application ?
What you are asking is to have the capability of removing a specific participant from the session created in the Identity Server side. I don't see a straight forward way (OOTB) of achieving this.
Closest you can achieve is as below.
Make your application perform a forceAuth. ForceAuth will request for user credentials despite having the cookies in the browser. (This will prevent the user from experiencing the SSO comfort. Still you can authenticate against the IS)
Prevent the application from sending an SLO request to WSO2. Rather, terminate the self(application) session upon logout.
When your application really wants to perform an SLO (logout all the applications, not just yours), your application can send an SLO request to the Identity Server.
Performing a force authentication :
SAML - Send forceAuth=true as a query paramter in your login request. Or else change the SAML AuthenricationRequest payload body to indicate a force authentication as in the spec(Line 2042).
OIDC - Send prompt=login as an additional query parameter in the /authorization request.
You can do this by calling the REST API and SOAP API provided by WSO2 IS. This will remove the session at WSO2 IS but I'm not sure if it will also trigger the SLO to other service providers or not.
Reference:
https://is.docs.wso2.com/en/latest/develop/calling-admin-services/
https://is.docs.wso2.com/en/latest/develop/session-mgt-rest-api/
Trigger a SOAP request getUserProfile from the UserProfileMgtService.wsdl. The default user profile will be 'default' or you can put the customized profile name you used. This will return the details. Grab the user id from this.
Trigger a GET to the API: /{user-id}/sessions with the user-id from step 1 to get the list of all active sessions this user currently have. Go through the list of sessions and find the session ID of the Service Provider you need to clear.
Trigger a DELETE request to API: /{user-id}/sessions/{session-id} with the user id from step 1 & session id from step 2

AWS Cognito - PostSignUp Trigger Not Working

i have a post signup trigger setup to store the user details in DynamoDb table. This works fine when the user signs up on their own through the front-end but the trigger is never invoked if the user is created through AdminCreateUser API. Our assumption was after the newly added user gets an email with a temporary password and logins through the front-end, Cognito will invoke the postsignup trigger.
Is that an expected behavior? And also, how do we address this issue?
Although that's counter intuitive, seems that's the expected behavior.
Post Confirmation trigger is not invoked when a user is created via AdminCreateUser API.
Workaround could be to to use Post Authentication trigger and during processing of the fired event to check if cognito:user_status is FORCE_CHANGE_PASSWORD.
When a user is created using AdminCreateUser, status of the new user is set to FORCE_CHANGE_PASSWORD. Once user logs in and changes temporary password, status is changed to CONFIRMED.
Downside is that trigger is invoked after every login.

Unable to confirm the user registration via AWS SDK

The AWS documentation for JS SDK says:
Force Change Password
The user account is confirmed and the user can sign in using a temporary password, but on first sign-in, the user must change his or her password to a new value before doing anything else.
User accounts that are created by an administrator or developer start in this state.
But if for such a user I try to call forgotPassword method of SDK, it errors saying something like: Password cannot be reset in the current state.
SO how can I complete the registration of a user (created by admin in IAM) from my website. Which is the SDK method that should be called ?
Setting up an Auto Verify Lambda Trigger on the Pre Sign Up Trigger will allow for the user to be a confirmed state, which may get you to the point you are looking for?
Lambda -> Node.js
Give it an appropriate Title
Place the below value in the code:
exports.handler = (event, context, callback) => {
// Confirm the user
event.response.autoConfirmUser = true;
// Set the email as verified if it is in the request
if (event.request.userAttributes.hasOwnProperty("email")) {
event.response.autoVerifyEmail = true;
}
// Return to Amazon Cognito
callback(null, event);
};
Save
Then Select newly created trigger in General Settings -> Triggers -> Pre sign-up
We can do this,
I previously answered mongodb to aws cogniton migration question.
Go through step by step. I explained that the user's created by admin need to change the password(forgot password) but there's still another way to do it. Checkout my answer,
Some content from my answer,
AdminCreateUser:
Create a new user profile by using the AWS Management Console or by calling the AdminCreateUser API. Specify the temporary password or allow Amazon Cognito to automatically generate one.
Specify whether provided email addresses and phone numbers are marked as verified for new users. Specify custom SMS and email invitation messages for new users via the AWS Management Console.
Specify whether invitation messages are sent via SMS, email, or both.
After successful user creation,
authenticate user using same user credentials Use: SDK calls InitiateAuth(Username, USER_SRP_AUTH)
After success of initateAuth, amazon Cognito returns the PASSWORD_VERIFIER challenge with Salt & Secret block.
Use RespondToAuthChallenge(Username, , PASSWORD_VERIFIER)
Amazon Cognito returns the NEW_PASSWORD_REQUIRED challenge along with the current and required attributes.
The user is prompted and enters a new password and any missing values for required attributes.
Call RespondToAuthChallenge(Username, , ).
After successful password change user can be able to login using same credentials which admin created.
Refer: Unable to confirm the user registration via aws

AWS Cognito - run another lambda after migration lambda has run

Cognito has a migration lambda that allows us to confirm a user in our db. They send the email and PW to Cognito, the lambda fires, we verify matches, and the user is entered into Cognito.
At this point - behind the scenes - Cognito generates a username of some kind (UUID). The problem is, I need a way to get this username into our existing database, because our systems going forward will no longer rely on email and instead rely on this username.
Ideal flow:
Sign In
Migration Succeeds
Cognito generates username
Username is sent to our server.
Now because we have email set to auto-verified, no post-confirmation lambda can be called. The only way I see to do this with Cognito as-is is to either:
Ask users who already exist in our system to confirm their email again. This is a non-starter
Create a post-auth lambda, check user login count through a custom attribute, and if 0 (or if not already registered with the service, etc.) migrate the username to the new service.
If there is any other way to do this, please let me know.
After the user migration lambda is called your pre sign-up lambda will be called, assuming you have implemented it. The parameters received by your lambda will include username with the value being the UID you referenced. Parameters will also include user attributes containing email. You can use this information to update your database.
I did not want to add the PreSignup trigger, its a complicated way of doing it if you already rely on PostConfirmation, and if the majority of new users won't be migrations. My use case has a frontend initiate the signup process as well, which I use here.
Instead, I set a Cognito attribute on the new user during the UserMigration trigger. It could be 'user_migration': <oldUserSub>, or however you want to mark it. Just make sure you allow this property within the Cognito user pool settings.
When the UserMigration trigger returns, this information is now accessible through verifying the IdToken, or found in the JWT on the frontend if you're using that. So, when the user is migrated into Cognito and the response gets back to the Cognito client on the frontend, I can now recognize this user needs to be migrated into my personal database. Seeing this, I'll call a new endpoint on my backend to handle this. This new endpoint does exactly what PostConfirmation would typically do.
Then just delete the 'user_migration' property from the Cognito user, return the new user data to the frontend and everything should be set up.
You can use Pre sign-up trigger. In order to detect if the trigger event came from your migration trigger, you can check at the trigger_source value from the event object. In my case (i'm using migration trigger) the value is PreSignUp_AdminCreateUser. By knowing the value of trigger_source you can differentiate if it was migrated or regular user. You can also check the user attributes to know whether the email or phone is verified or not.
Here's my sample code on python:
def lambda_handler(event, context):
trigger_source = event.get('triggerSource')
user_attributes = request.get('userAttributes')
email_verified = user_attributes.get('email_verified')
if trigger_source == 'PreSignUp_AdminCreateUser' and email_verified == 'true':
# create user on db

Set Password history policy in WSO2 IS

Am having a requirement to set Password History limit in WSO2 - IS, i.e if my current password is "Abc123", and if am trying to changing the password with "Abc123" again, it should not accept.
If I set my password history limit as "3", then the new password should be different from last 3 password given for that particular account.
Please share your insight on this and help me to achieve the task.
In case you are using LDAP as user store then you may apply the password policy there itself. You may assign the password history count over there in LDAP.
In case you wan to do it in WSO2 IS itself then you may need to write a custom password policy extension
Actually, WSO2-IS does not support password history control out of the box. This is a limitation in the current implementation since it is connecting to the user store with the super admin credentials of the User Store always. The best option is that you can write custom listener (extension) and plug it with the Identity Server.
Adding new listener implementation
Listener is an extension to extend the user core functions. Any number of listeners can be plugged with the user core and they would be called one by one. By using listener, we are not overriding the user store implementation, which is good as we are not customizing the existing implementations.
Let see how it works.
Every time when user core method is called, all the listeners that are registered with that method, are called. Listeners can be registered before or after the actual method is called. Let take some example; In user core, there is a method called “addUser()“. When user is created in Identity Server, “addUser()” method is called. You can register a listener before the actual execution of “addUser()” method and also, you can register a listener after the actual execution of “addUser()” method.
“addUser()” method can be seen as follows.
addUser() {
preAddUser(); // you can implement this using listener
actualAddUser();
postAddUser(); // you can implement this using listener
}
Both “preAddUser()” and “postAddUser()” method can be customized as you want. It means, you can do some custom things before user is added or after user is added. All the method in user core has been implemented as above. It means that you can customize them before and after…
Let take following simple scenario.
When user is authenticated with LDAP, there is a requirement to add authenticated time as an attribute of the user.
Therefore we need to write some custom code after successful user authentication is happened. Following is the custom listener implementation for this. “doPostAuthenticate()” method would be called after actual user authentication is done.
package org.soasecurity.user.mgt.custom.extension;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.user.core.UserStoreException;
import org.wso2.carbon.user.core.UserStoreManager;
import org.wso2.carbon.user.core.common.AbstractUserOperationEventListener;
/**
*
*/
public class MyUserMgtCustomExtension extends AbstractUserOperationEventListener {
private static Log log = LogFactory.getLog(MyUserMgtCustomExtension.class);
#Override
public int getExecutionOrderId() {
return 9883;
}
#Override
public boolean doPreAuthenticate(String userName, Object credential,
UserStoreManager userStoreManager) throws UserStoreException {
// just log
log.info("doPreAuthenticate method is called before authenticating with user store");
return true;
}
#Override
public boolean doPostAuthenticate(String userName, boolean authenticated, UserStoreManager userStoreManager) throws UserStoreException {
// just log
log.info("doPreAuthenticate method is called after authenticating with user store");
// custom logic
// check whether user is authenticated
if(authenticated){
// persist user attribute in to user store
// "http://wso2.org/claims/lastlogontime" is the claim uri which represent the LDAP attribute
// more detail about claim management from here http://soasecurity.org/2012/05/02/claim-management-with-wso2-identity-server/
userStoreManager.setUserClaimValue(userName, "http://wso2.org/claims/lastlogontime",
Long.toString(System.currentTimeMillis()), null);
}
return true;
}
}
Likewise, you can add custom extensions to any method of the user core.
Here, I would like to note following things.
“getExecutionOrderId()” can return any random value. This would be important when there are more than one listeners are in the identity Server and you need to consider about execution order of them
All the methods are return a boolean value. This value mentioned, whether you want to execute next listener or not.
Let see what are steps to configure the custom implementation.
Step 1. Listeners are registered as OSGI components. Therefore you need to register this class in OSGI framework. You can go through this which is the complete project.
Step 2. Copy OSGI bundle file in to IS_HOME/repository/components/dropins directory.
Step 3. Restart the server.