How to act upon user registration (onRegister) - yesod

I'd like to perform some actions when a new user registers to my Yesod app.
What is the way to hook into that event?

Assuming your user registration process is performed by a POST request to your application, you can hook into handler function, that handles this request.
Assuming you use the usual scaffold layout, the info which function that is can be found in the file config/routes. The name convention is to prepend "post" to the name of the route (i.e.: RegisterUserR -> postRegisterUserR).

Related

WSO2IS 5.10.0. lastLoginTime update in Custom Local Authenticator

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.

AWS Cognito custom auth - sending metadata to a challenge lambda functions

I'm developing a custom passwordless auth to sign into a Cognito user pool. I will describe what I'm trying to implement in case anything is silly. I want a user to enter their email address, then receive a magic login link via email, and when they click on that be taken back to the site and be logged in.
This uses custom auth lambda functions to define/create a challenge with a time based password and send it to the user in an email. I am having a couple of problems:
Problem 1)
When the user returns with the code they might not be in the same browser/device and certainly won't be in the same tab so they don't have the session, meaning I need to call cognitoUser.initiateAuth again. This goes through the define/create challenge lambdas again so a second email gets sent even though at this point the user is coming from the email link so already has the code. Note: the session id is not available in the event object when the challenge is created, also I've read these sessions only last 3 minutes and my time based passwords will last ~15minutes, so I don't think I can include the session id in the email.
Problem 2)
You can login from a few places (browser, android app, etc) and I would like to be able to include the url or at least protocol as a parameter to control what gets sent in the email, e.g. if you entered your email address in the android app then the email you get would be myapp://login?code=xxx and if you did it on the web it would be https://example.com/login?code=xxx
It seems like I would be able to implement both of these to work properly if only I could find some way to send custom metadata through to the DefineChallenge and CreateChallenge lambda such that it would appear in the event object. I thought adding ValidationData to the AuthenticationDetails object would do this, but that information doesn't appear in the event object in the Lambda fns.
The workaround I've found is to create a new client id for every situation - one for initiating auth, one for redeeming token, and repeat for each different protocol. But that is a lot of client ids quickly - a pain to mantain and clumsy.
So tl;dr is: I want to send custom metadata from my cognitoUser.initiateAuth(...) call in JS and have it available in my Define/Create Challenge lambda fns.
You can split the authentication process into multiple custom auth challenges. This allows custom auth state to be supplied via the challenge response as client metadata.
Auth session state must be persisted in a database in order to be shared between devices.
Your custom login flow will probably have two challenge steps: the first prompts for auth type and the second prompts for the secret code. The action taken by the "Create Auth Challenge" Lambda will depend on the auth type. If the auth type is "Email" then the secret code and magic link are generated, stored in DynamoDB and emailed. If the auth type is "MagicLink" then the secret is loaded from DynamoDB. Clicking on a Magic link will initiate a new auth session and automatically supply all the challenge answers.
There are a few other things to consider:
Your magic link needs to encapsulate the Cognito username as well as the one-time secret and probably some other session id that is used as a key in dynamodb.
You probably should not put app-specific links into your emails. Instead associate your domain with your app and/or leverage the redirect URI parameter of your web-based login page.
You can also access custom Cognito user attributes from the Lambda function which can be used to indicate user login preferences (eg Email vs SMS for login codes).

make custom API call with authentication

I am trying to develop an application that should eventually replace an existing (non-Ember) one and provide additional functionality.
For a start, for anything not yet implemented in the new app, I want to redirect users to the existing one, using the latter's single-sign-on capability. The workflow I imagined is this:
User logged in to new (Ember) app clicks link or button there
New app makes an API call to an endpoint that returns an SSO token
New app generated link including SSO token, opens it (in new or same window)
I use ember-simple-auth to authenticate the user for API calls that return user-specific information, using a JSON web token that contains the user id.
For step 2 above I would need to include that token in the API call, but I am at loss how, and even where to implement the call. Do I need an Ember.Route for this (where I could throw in the AuthenticatedRouteMixin)? I would not consider the SSO token to be part of my model, so that does not seem right. Can I get the session's token somehow and include it in a direct ajax call? Should I?
ember-simple-auth provides the SessionService where you can access that information.
My recommendation is to use ember-ajax to make the actual request, and override the ajax service to call the session services authorize method.
Then you need to implement your authorizer to authorize that request.
The detail implementation depends on your authorizer and how you want to include the token in your request. As header, query param, or in the body.

How can I store request-local information in Yesod middleware and retrieve it from a Handler?

Before any handler code is executed, I need to load some information about the user, which is determined by the request cookies, and then access that in all of my handler code.
I've looked at the yesod-core codebase and found that I could override the yesodMiddleware function and provide a custom middleware which would load the data before the handler is executed.
The problem is that I don't know where to store this data, so that I can later retrieve it from the Handler.
You can use cached and cachedBy for per-request caching.

How to access session data in a signal handler?

Whenever a visitor who is not a user adds content to my site I just create a dummy user account and store it's id in the session of that visitor.
Now when a visitor finally logs in using Facebook, Google or any other OAuth I want to merge the dummy user with the real user account. I figure a good way would be to catch the login signal. But in order to find the previous dummy user account I need to enter the session data. How can I get to it from within the handler?
If it's not possible than how to approach this problem?
Please note that I want to be able to use various login apps like django-social-auth so I need a solution that hooks in very clean.
Thank you for your time.
The login signal includes the current request as one of it's parameters:
https://docs.djangoproject.com/en/1.7/ref/contrib/auth/#module-django.contrib.auth.signals
Something like:
#receiver(signal=user_logged_in)
def merge_accounts(sender, request, user, **kwargs):
user.update_details_from(request.session['your-dummy-data'])