Post signin / signup callbacks
#
1) On the frontend- ReactJS
- Angular
- Vue
Important
supertokens-auth-react
SDK and will inject the React components to show the UI. Therefore, the code snippet below refers to the supertokens-auth-react
SDK.This method allows you to fire events immediately after a successful sign in / up. For example to send analytics events post sign in / up.
import SuperTokens from "supertokens-auth-react";
import ThirdPartyEmailPassword from "supertokens-auth-react/recipe/thirdpartyemailpassword";
import Session from "supertokens-auth-react/recipe/session";
SuperTokens.init({
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "..."
},
recipeList: [
ThirdPartyEmailPassword.init({
onHandleEvent: async (context) => {
if (context.action === "SESSION_ALREADY_EXISTS") {
// TODO:
} else if (context.action === "SUCCESS") {
let { id, email } = context.user;
if (context.isNewUser) {
// TODO: Sign up
} else {
// TODO: Sign in
}
}
}
}),
Session.init()
]
});
info
Please refer to this page to learn more about the onHandleEvent
hook.
Important
supertokens-auth-react
SDK and will inject the React components to show the UI. Therefore, the code snippet below refers to the supertokens-auth-react
SDK.This method allows you to fire events immediately after a successful sign in / up. For example to send analytics events post sign in / up.
import SuperTokens from "supertokens-auth-react";
import ThirdPartyEmailPassword from "supertokens-auth-react/recipe/thirdpartyemailpassword";
import Session from "supertokens-auth-react/recipe/session";
SuperTokens.init({
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "..."
},
recipeList: [
ThirdPartyEmailPassword.init({
onHandleEvent: async (context) => {
if (context.action === "SESSION_ALREADY_EXISTS") {
// TODO:
} else if (context.action === "SUCCESS") {
let { id, email } = context.user;
if (context.isNewUser) {
// TODO: Sign up
} else {
// TODO: Sign in
}
}
}
}),
Session.init()
]
});
info
Please refer to this page to learn more about the onHandleEvent
hook.
This method allows you to fire events immediately after a successful sign in / up. For example to send analytics events post sign in / up.
import SuperTokens from "supertokens-auth-react";
import ThirdPartyEmailPassword from "supertokens-auth-react/recipe/thirdpartyemailpassword";
import Session from "supertokens-auth-react/recipe/session";
SuperTokens.init({
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "..."
},
recipeList: [
ThirdPartyEmailPassword.init({
onHandleEvent: async (context) => {
if (context.action === "SESSION_ALREADY_EXISTS") {
// TODO:
} else if (context.action === "SUCCESS") {
let { id, email } = context.user;
if (context.isNewUser) {
// TODO: Sign up
} else {
// TODO: Sign in
}
}
}
}),
Session.init()
]
});
info
Please refer to this page to learn more about the onHandleEvent
hook.
#
2) On the backend#
Sign in / sign up overrideFor this, you'll have to override the following recipe functions in the init
function call, on the backend:
emailPasswordSignUp
: Sign up with email & passwordemailPasswordSignIn
: Sign in with email & passwordthirdPartySignInUp
: Sign in or up with third party
- NodeJS
- GoLang
- Python
- Other Frameworks
Important
import SuperTokens from "supertokens-node";
import ThirdPartyEmailPassword from "supertokens-node/recipe/thirdpartyemailpassword";
import Session from "supertokens-node/recipe/session";
SuperTokens.init({
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "..."
},
supertokens: {
connectionURI: "...",
},
recipeList: [
ThirdPartyEmailPassword.init({
override: {
functions: (originalImplementation) => {
return {
...originalImplementation,
// override the email password sign up API
emailPasswordSignUpPOST: async function(input) {
if (originalImplementation.emailPasswordSignUpPOST === undefined) {
throw Error("Should never come here");
}
// TODO: some pre sign up logic
let response = await originalImplementation.emailPasswordSignUpPOST(input);
if (response.status === "OK") {
// TODO: some post sign up logic
}
return response;
},
// override the email password sign in API
emailPasswordSignInPOST: async function(input) {
if (originalImplementation.emailPasswordSignInPOST === undefined) {
throw Error("Should never come here");
}
// TODO: some pre sign in logic
let response = await originalImplementation.emailPasswordSignInPOST(input);
if (response.status === "OK") {
// TODO: some post sign in logic
}
return response;
},
// override the thirdparty sign in / up API
thirdPartySignInUpPOST: async function(input) {
if (originalImplementation.thirdPartySignInUpPOST === undefined) {
throw Error("Should never come here");
}
// TODO: Some pre sign in / up logic
let response = await originalImplementation.thirdPartySignInUpPOST(input);
if (response.status === "OK") {
if (response.createdNewUser) {
// TODO: some post sign up logic
} else {
// TODO: some post sign in logic
}
}
return response;
}
}
}
}
}),
Session.init({ /* ... */ })
]
});
import (
"fmt"
"github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels"
"github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword"
"github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels"
"github.com/supertokens/supertokens-golang/supertokens"
)
func main() {
supertokens.Init(supertokens.TypeInput{
RecipeList: []supertokens.Recipe{
thirdpartyemailpassword.Init(&tpepmodels.TypeInput{
Override: &tpepmodels.OverrideStruct{
Functions: func(originalImplementation tpepmodels.RecipeInterface) tpepmodels.RecipeInterface {
// create a copy of the originalImplementation
originalEmailPasswordSignUp := *originalImplementation.EmailPasswordSignUp
originalEmailPasswordSignIn := *originalImplementation.EmailPasswordSignIn
originalThirdPartySignInUp := *originalImplementation.ThirdPartySignInUp
// override the email password sign up function
(*originalImplementation.EmailPasswordSignUp) = func(email, password string, userContext supertokens.UserContext) (tpepmodels.SignUpResponse, error) {
// TODO: some pre sign up logic
resp, err := originalEmailPasswordSignUp(email, password, userContext)
if err != nil {
return tpepmodels.SignUpResponse{}, err
}
if resp.OK != nil {
// TODO: some post sign up logic
}
return resp, err
}
// override the email password sign in function
(*originalImplementation.EmailPasswordSignIn) = func(email, password string, userContext supertokens.UserContext) (tpepmodels.SignInResponse, error) {
// TODO: some pre sign in logic
resp, err := originalEmailPasswordSignIn(email, password, userContext)
if err != nil {
return tpepmodels.SignInResponse{}, err
}
if resp.OK != nil {
// TODO: some post sign in logic
}
return resp, err
}
// override the thirdparty sign in / up function
(*originalImplementation.ThirdPartySignInUp) = func(thirdPartyID, thirdPartyUserID, email string, oAuthTokens tpmodels.TypeOAuthTokens, rawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider, userContext supertokens.UserContext) (tpepmodels.SignInUpResponse, error) {
// TODO: some pre sign in / up logic
resp, err := originalThirdPartySignInUp(thirdPartyID, thirdPartyUserID, email, oAuthTokens, rawUserInfoFromProvider, userContext)
if err != nil {
return tpepmodels.SignInUpResponse{}, err
}
if resp.OK != nil {
user := resp.OK.User
fmt.Println(user)
// This is the response from the OAuth tokens provided by the third party provider
fmt.Println(resp.OK.OAuthTokens["access_token"])
// This gives the user's info as returned by the provider's user profile endpoint.
fmt.Println(resp.OK.RawUserInfoFromProvider.FromUserInfoAPI["first_name"])
if resp.OK.CreatedNewUser {
// TODO: Post sign up logic
} else {
// TODO: Post sign in logic
}
}
return resp, err
}
return originalImplementation
},
},
}),
},
})
}
from supertokens_python import init, InputAppInfo
from supertokens_python.recipe import thirdpartyemailpassword
from supertokens_python.recipe.thirdpartyemailpassword.interfaces import APIInterface, ThirdPartyAPIOptions, EmailPasswordAPIOptions, ThirdPartySignInUpPostOkResult, EmailPasswordSignInPostOkResult, EmailPasswordSignUpPostOkResult
from typing import Union, Dict, Any, List
from supertokens_python.recipe.thirdparty.provider import Provider
from supertokens_python.recipe.emailpassword.types import FormField
def override_thirdpartyemailpassword_apis(original_implementation: APIInterface):
original_thirdparty_sign_in_up_post = original_implementation.thirdparty_sign_in_up_post
original_emailpassword_sign_in_post = original_implementation.emailpassword_sign_in_post
original_emailpassword_sign_up_post = original_implementation.emailpassword_sign_up_post
async def thirdparty_sign_in_up_post(provider: Provider, code: str, redirect_uri: str, client_id: Union[str, None], auth_code_response: Union[Dict[str, Any], None],
api_options: ThirdPartyAPIOptions, user_context: Dict[str, Any]):
# call the default behaviour as show below
result = await original_thirdparty_sign_in_up_post(provider, code, redirect_uri, client_id, auth_code_response, api_options, user_context)
if isinstance(result, ThirdPartySignInUpPostOkResult):
if result.created_new_user:
pass # TODO: some post sign up logic
else:
pass # TODO: some post sign in logic
return result
async def emailpassword_sign_in_post(form_fields: List[FormField],
api_options: EmailPasswordAPIOptions, user_context: Dict[str, Any]):
# call the default behaviour as show below
result = await original_emailpassword_sign_in_post(form_fields, api_options, user_context)
if isinstance(result, EmailPasswordSignInPostOkResult):
pass # TODO: some post sign in logic
return result
async def emailpassword_sign_up_post(form_fields: List[FormField],
api_options: EmailPasswordAPIOptions, user_context: Dict[str, Any]):
# call the default behaviour as show below
result = await original_emailpassword_sign_up_post(form_fields, api_options, user_context)
if isinstance(result, EmailPasswordSignUpPostOkResult):
pass # TODO: some post sign up logic
return result
original_implementation.thirdparty_sign_in_up_post = thirdparty_sign_in_up_post
original_implementation.emailpassword_sign_in_post = emailpassword_sign_in_post
original_implementation.emailpassword_sign_up_post = emailpassword_sign_up_post
return original_implementation
init(
app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
framework='...',
recipe_list=[
thirdpartyemailpassword.init(
override=thirdpartyemailpassword.InputOverrideConfig(
functions=override_thirdpartyemailpassword_apis
)
)
]
)
Using the code above, if createdNewUser
is true
or in emailPasswordSignUpPOST
, you can (for example):
- Add the user's ID and their info to your own database (in addition to it being stored in SuperTokens).
- Send analytics events about a sign up.
- Send a welcome email to the user.
- You can associate a role to the user.
#
Accessing custom form fields during email password sign upDuring email password sign up, if you would like to access the custom form fields that you may have added, you can do so by overriding the emailPasswordSignUpPOST
API:
- NodeJS
- GoLang
- Python
- Other Frameworks
Important
TODO
import (
"fmt"
"github.com/supertokens/supertokens-golang/recipe/emailpassword/epmodels"
"github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword"
"github.com/supertokens/supertokens-golang/recipe/thirdpartyemailpassword/tpepmodels"
"github.com/supertokens/supertokens-golang/supertokens"
)
func main() {
supertokens.Init(supertokens.TypeInput{
RecipeList: []supertokens.Recipe{
thirdpartyemailpassword.Init(&tpepmodels.TypeInput{
Override: &tpepmodels.OverrideStruct{
APIs: func(originalImplementation tpepmodels.APIInterface) tpepmodels.APIInterface {
originalEmailPasswordSignUpPOST := *originalImplementation.EmailPasswordSignUpPOST
(*originalImplementation.EmailPasswordSignUpPOST) = func(formFields []epmodels.TypeFormField, options epmodels.APIOptions, userContext supertokens.UserContext) (tpepmodels.SignUpPOSTResponse, error) {
// pre API logic...
resp, err := originalEmailPasswordSignUpPOST(formFields, options, userContext)
if err != nil {
return tpepmodels.SignUpPOSTResponse{}, err
}
if resp.OK != nil {
userId := resp.OK.User.ID
fmt.Println(userId)
// sign up successful
// here we fetch a custom form field for the user's name.
// Note that for this to be available, you need to define
// this custom form field.
name := ""
for _, field := range formFields {
if field.ID == "name" {
name = field.Value
}
}
fmt.Println(name)
}
return resp, nil
}
return originalImplementation
},
Functions: func(originalImplementation tpepmodels.RecipeInterface) tpepmodels.RecipeInterface {
// TODO: From previous code snippet...
return originalImplementation
},
},
}),
},
})
}
TODO
#
API override vs Functions overrideFor most purposes, you should be using Functions override. Functions override logic is called whenever the API is called from the frontend as well as if you call the sign up / sign in functions yourself manually via the SDK (like thirdpartyemailpassword.emailPasswordSignIn(...)
).
For example, when the frontend calls the email password sign up API, the SDK invokes the API.emailPasswordSignUpPOST
above. When you call the original implementation of this function, that function calls the Functions.emailPasswordSignUp
function first, and if that's successful, it calls the Session recipe's createNewSession
function.
Therefore, if you want to associate a role with a user, you would want to do that in the Functions.emailPasswordSignUp
function since then those roles would get added to the session in the subsequent call to the Session.createNewSession
function. If instead, you associate a role to the user in the API.emailPasswordSignUpPOST
(after calling the original implementation), the role will not be automatically added to the session since the Session.createNewSession
would have already been called before the original implementation returns.
The only time it makes sense to override the API functions is if you want to access an argument that's not available in the recipe function. For example, the custom form fields for email password sign up is an input to the API.emailPasswordSignUpPOST
, but not to the Functions.emailPasswordSignUp
, so if you want to access the form fields, you should override the API.emailPasswordSignUpPOST
as shown above. You can also always add the formFields to the userContext object and read it later in the Functions.emailPasswordSignUp
override, but then you would lose the typing of the form field array structure (which is not a runtime problem, but just a slightly bad developer experience).