I have a use case that requires the user to be authenticated. If the user is already authenticated, he can just go straight to the action, however, if he isn’t logged in, I redirect him to a login using a FollowupAction.
The stories for the two scenarios could look like this:
Authenticated user:
* agent.add_wishlist_items{"shopping_item": "milk"}
- slot{"shopping_item": ["milk"]}
- action_search_items # This will display buttons with milk items
* user.selected_products{"products": "33"}
- slot{"products": ["33"]}
- action_add_wishlist_items
Unauthenticated user:
* agent.add_wishlist_items{"shopping_item": "milk"}
- slot{"shopping_item": ["milk"]}
- action_search_items # This will display buttons with milk items
* user.selected_products{"products": "33"}
- slot{"products": ["33"]}
- action_add_wishlist_items
- action_login # if user is not authenticated, which is checked in action_add_wishlist_items
* account_linked{"access_token": "2A2b0zdf614"}
- slot{"access_token": "2A2b0zdf614"}
- action_account_linked
* user.selected_products{"products": "33"}
- slot{"products": ["33"]}
- action_add_wishlist_items
Now I have two questions:
When adding the stories just like that, the dialogue model is learning to sometimes show action_login after action_add_wishlist_items even for already authenticated users, right? How do I make it clear that action_login should only be executed if the user is not authenticated yet?
The action_login is causing a ‘redirect’ here. What’s the best way to make sure that ‘milk is added to the wishlist’ after the login? So far, I just sent the user.selected_products message again. But is there a way that it’s executed again automatically after the action_login completed successfully?
I think you need to write a proper slot value indicating user is logged in after the action action_add_wishlist_items. Sucht that the algo learns to show only action_login if user is not logged in (using a categorical slot). You need to return this slot in action_add_wishlist_items
Btw as I am interested to use a log in feature in the futire too, how did you do that?
Regarding login: I’ve been working on three different authentication flows:
Basic Authentication
Token Authentication
OAuth2 Authentication
Unfortunately, the auth flows somewhat depend on the channel you’re using. For example, I read that Google Assistant and Alexa are fully OAuth2 compatible, whereas the Messenger Platform is not. So the authentication really depends on the details, but in general I have a setup similar to this:
Slot(s) indicating whether user is authenticated
If user tries to execute something without permissions, I send a FollowupAction(‘login’) that triggers a login form to be sent to the user. Login form asks for different things, e.g. login type (Basic, Token, OAuth2), username and password if necessary.
Afterwards, I sent an authorization request to my backend containing the credentials received through the form
The authorization webhook returns status code (success / failure) and, if necessary, an authorization token which I store in a slot
Credentials are then added to all future requests that need permissions
For example, when using the Messenger channel, I used OAuth2 and customized the account_linking method of the channels.facebook.Messenger class to link the OAuth2 token to Messenger’s PSID.
If you want, I could publish a small guide here over the next week or so.
Essentially depending on the authorization flow and channel I first send a login action. For Basic Auth and Token Auth, this could be a simple FormAction asking for username and password. For OAuth2 this is sending a Postback button that redirects the user to the login screen of my backend.
Then, the backend responds with either success (and potentially an access token) or failure that causes a Linked or Linking_Error action to be executed. In the success case the Linked action just sets some slots (login=True and potentially access_token=“xxx”) and redirects the user to the previous action (the one he needed to login for). In the Linking_Error case, I just utter some error message.
Cleaning up my authorization flows at the moment. I’ll try to write something and post it here for everyone once I’m done
Want to bring up this issue again. What is the correct way to write a story with followup actions? I ran it in interactive mode and it exports to something like this (i shortened it):
In this run, the user wanted to add a product, which triggers the custom action action_shopping.add_cart_product. However, the action noticed the user isn’t authenticated, so it returned a followup action auth.login_form. The login form then takes the user through the login process, confirms with action_auth.account_linked and then runs the initial action_shopping.add_cart_product action again to now successfully add the product to the customers cart.
So now I was wondering whether this story is messing up training for users who are already logged in where the story would be like this (since action_shopping.add_cart_product won’t issue a FollowupAction):
@akelad@souvikg10: Any ideas on this? I’d like to write a guide on this sooner or later since others seem to struggle with login related issues as well, but want to make sure I fully understand it myself.
Bringing this up one more time with an additional question. To summarize, I have many actions that are protected and require the user to be logged in. So a simple use case like this:
shopping.list_products_in_cart
action_shopping.list_products_in_cart
Might end up like this if the user isn’t logged in already:
Do I need to add these long stories to the training data? Or does it not matter, since the followup actions will be executed anyway?
If I need to add these protected paths to the stories, is there a way I can generalize it? Let’s say I have a dozen such stories for listing products, adding products, removing products, wishlists, etc. I would have to duplicate these stories a dozen times, but only 4 lines would change. Or is there a way to do something like that:
shopping.list_products_in_cart OR shopping.add_product_to_cart OR …
action_shopping.list_products_in_cart OR action_shopping.add_product_to_cart OR …
action_shopping.list_products_in_cart OR shopping.add_product_to_cart OR …
I’d just like to train the core model that sometimes, an action might cause a login to be required, but that it should continue with the original action afterwards.
I have need of similar logic and tried to control the flow in story with least repetitive way and the solution end up being the best was to do this control within FormAction programatically (so predictable…)
So my story look like this
Within this auth_form, I set / check authenticated slot and determine whether user need authentication or not. With this way, I can provide finer control of how long the session is good for etc by setting authenticated to False if it elapsed more than 5 min since last authenticated etc.
In FormAction,
to set authenticated slot to True if token is validated. I do this in validate func.
Then in request_next_slot function, if authenticated is set to True, I simply return None so that it gets out of auth_form and next actionutter_share_secret is correctly predicted to move on.
So yeah, every action that requires authentication you will need to repeat 3 lines
Hi @naoko, I initially thought about this process as well. However, in this case you rely on core to predict the auth_form as next action. Therefore, you could run into a situation, where core directly jumps from * select_menu to utter_share_secret without requiring login through the auth_form. If you need to have 100% prove that utter_share_secret is not executed without being logged in, you need a different solution.
Hi @smn-snkl! Did you manage to implement properly an authentication process for RASA in the end ? If yes, may I ask you to share your final experience please.
If you want a story to execute conditionally, add a slot command to the story. For example, I have a profile slot (categorical with values “vip”, “standard”, “anonymous”).
In this case, I’m assuming the slot already contains the correct value. Have a look here: Rasa documentation on slots, to see what can be detected. (A string for example, can’t be matched, you can just check if it is set or not)