Hey! Is it possible to override the internal super...
# support-questions
f
Hey! Is it possible to override the internal supertoken redirection function? I am using NextJS and emailVerificationMode "REQUIRED". However my frontend is trying to check if the email is verified and redirect accordingly. I guess it uses the window.location.href thingy which is slow and inefficient compared to NextRouter...
r
hey @flixoflax At the moment, we do not allow you to override how the actual redirection happens. The only router we support is react router dom. If that's not used, it uses window.location.href. Perhaps you could open an issue about this and we can have a look sometime soon
f
This would be great because it slows down my app. I'll open an issue.
r
Actually, maybe you can hack your way through this
So maybe, on each page load, you could "find" this function in memory and overwrite it with a method that uses nextrouter
oh wait, i think you can do that farily easily.
f
Man, I really love watching you think 😄
r
hahaha
f
I'll try to do it. I will still open an issue about it, ok?
r
Try this:
Copy code
ts
import Router from 'next/router'

SuperTokens.init({
    appInfo: {...},
    recipeList: [...],
    windowHandler: (oI) => {
        return {
            ...oI,
            location: {
                ...oI.location,
                setHref: (href) => {
                    Router.push(href)
                }
            }
        }
    }
})
f
Okay, I'll check
I'll need to include the window handler function in the Session.init({override: }) block right?
r
no . it's in the supertokens.init level
f
yes, sorry realized it as well 😄
r
yea. just tested this - it seems to work
f
Alright, it actually works. But it didn't solve my problem... But that's a different story 😄
r
huh.. so what problem did you have?
f
I'll try to describe. Basically I see my login page flash twice before I redirect to the protected routes. I first encountered this issue when adding the emailVerificationMode "REQUIRED"
r
hmmm. This is strange
any console logs? Any extra network requests?
f
Let me check maybe it's the way how I need to do the redirection by myself after signin since I'm using a custom ui
r
ah right. Fair enough
f
It appears that the request to check if an email has been verified is sent every time I navigate... so while the request is sent I'll have no session which leaves me at the auth path and then it rehydrates, recognizes that the email has been verified and redirects me to my dashboard... But this is rather annoying ....
r
yeaaa.. you need to show a null state while it's fetching. We are working on a way to optimise this, but it will take 1 or 2 months. What you can do in the meantime is: - Override the on session creation function in the backend to add a flag of if the email is verified or not. - Override the email verification post API to modify this flag in the session to mark that the email has been verified. - override the function on the frontend that checks if the email is verified to read that flag off the access token payload instead of querying the API. This way, it will avoid network calls and the glitch should go
f
Yeah well, but does a null state resolve this issue? I mean that way I have a white screen while i wait for the request to be finished, right?
r
yea but if you reading from the localstorage instead of making a network call, that's SUPER quick. So the user won't notice anything
f
Right!! Alright so you say this is going to be resolved in about 1 to 2months?
r
yea. But as i said, you can do the overrides and have it fixed right now
an even simpler alternative would be just to override the isEmailVerified function on the frontend and cache the result in localstorage if the email verified returns true.
wait, i'll share some code
Which recipe are you using?
f
then I would wait but if not I would start to override..
r
i mean it's a really simple override
which recipe? I can show you
f
thirdpartyemailpassword
golang sdk
and react auth
r
try this on the frontend:
Copy code
ThirdPartyEmailPassword.init({
    override: {
        functions: (oI) => {
            return {
                ...oI,
                emailPasswordSignIn: async function (input) {
                    window.localStorage.removeItem("isEmailverified");
                    return oI.emailPasswordSignIn(input);
                },
                emailPasswordSignUp: async function (input) {
                    window.localStorage.removeItem("isEmailverified");
                    return oI.emailPasswordSignUp(input);
                },
                thirdPartySignInAndUp: async function (input) {
                    window.localStorage.removeItem("isEmailverified");
                    return oI.thirdPartySignInAndUp(input);
                },
            }
        },
        emailVerification: {
            functions: (oI) => {
                return {
                    ...oI,
                    isEmailVerified: async function (input) {
                        let fromLocalstorage = window.localStorage.getItem("isEmailverified");
                        if (fromLocalstorage == null) {
                            let fromAPI = await oI.isEmailVerified(input);
                            if (fromAPI.isVerified) {
                                window.localStorage.setItem("isEmailverified", "true");
                            }
                            return fromAPI;
                        } else {
                            return {
                                fetchResponse: new Response(),
                                isVerified: fromLocalstorage === "true",
                                status: "OK",
                            }
                        }
                    }
                }
            }
        }
    }
})
this should send the API call just when the user signs in, else it will cache it from localstorage
f
Well I think it works but the Problem still persists.
r
well, it will do the API call on sign in
f
Which makes send because I still need to wait for the API result on signin and signup
yeah
r
yea.. so then the other way is to inject the info about email verification from the backend into the access token
and then you won;t need to make API calls from the frontend at all
f
yea. how would i do this?
f
how would i verify this on the frontend?
I think I know how to do it on the backend... but how would i verify this on the frontend
r
well, you can get the access token payload on the frontend and read it from that
f
would i overwrite the emailVerification?
Yea, sure I know how to do this but I would need to overwrite the email verification, right?
r
Copy code
ThirdPartyEmailPassword.init({
    override: {
        emailVerification: {
            functions: (oI) => {
                return {
                    ...oI,
                    isEmailVerified: async function (input) {
                        let accessTokenPayload = await Session.getAccessTokenPayloadSecurely();
                        return {
                            fetchResponse: new Response(),
                            isVerified: accessTokenPayload.isEmailVerified,
                            status: "OK",
                        }
                    }
                }
            }
        }
    }
})
and on the backend, you can set the value of
isEmailVerified
in the access token payload.
f
top
thanks
how can I access the isEmailVerified in the CreateNewSession overwrite?
sorry got it
checked the docs 🙂
No, actually it doesnt work I cant use the recipe
thirdparty.isEmailVerified(userId)
before the thing was actually initialized
nvm
I'm stupid lol I'm using another recipe lol
@rp okay now I need to update the AccessTokenPayload on successful verification right?
how would i do this
f
EmailVerificationFeature.APIs: func(originalImplementation evmodels.APIInterface) evmodels.APIInterface { originalVerifyEmailPOST := *originalImplementation.VerifyEmailPOST (*originalImplementation.VerifyEmailPOST) = func(token string, options evmodels.APIOptions, userContext supertokens.UserContext) (evmodels.VerifyEmailUsingTokenResponse, error) { resp, err := originalVerifyEmailPOST(token, options, userContext) if err != nil { return evmodels.VerifyEmailUsingTokenResponse{}, err } if resp.OK != nil { // TODO: Update access token } return resp, err } return originalImplementation }
I've already overwritten the CreateNewSession parameter
The thing is on successful email verification I need to update the AccessTokenPayload...
Copy code
Override: &tpepmodels.OverrideStruct{
                    EmailVerificationFeature: &evmodels.OverrideStruct{
                        APIs: func(originalImplementation evmodels.APIInterface) evmodels.APIInterface {
                            originalVerifyEmailPOST := *originalImplementation.VerifyEmailPOST

                            (*originalImplementation.VerifyEmailPOST) = func(token string, options evmodels.APIOptions, userContext supertokens.UserContext) (evmodels.VerifyEmailUsingTokenResponse, error) {
                                resp, err := originalVerifyEmailPOST(token, options, userContext)

                                if err != nil {
                                    return evmodels.VerifyEmailUsingTokenResponse{}, err
                                }

                                if resp.OK != nil {
                                    // TODO: Update AccessTokenPayload
                                }

                                return resp, err
                            }

                            return originalImplementation
                        },
                    },
                },
r
Yes. Correct.
f
Yes but I can’t access the session and update the access token payload.
Which makes total sense because You don't necessarily have to be logged in to verify your email address
So i could Update the atp with method 2 from the docs
But this way the email is only verified on sessionRefresh
@rp
r
Well, yea. You may have the session if the user uses the same browser, so in that case, it will reflect instantly. Else yea, you will have to wait for session refresh. You can check for a session like:
Copy code
go
sessionRequired := false;
session, err := session.GetSessionWithContext(options.Req, options.Res, &sessmodels.VerifySessionOptions{
  SessionRequired: &sessionRequired,
}, userContext)

if err != nil {
  return evmodels.GenerateEmailVerifyTokenPOSTResponse{}, err
}

if session != nil {
   // TODO: update access token payload of current session -> this will reflect instantly
}

// get all sessions belonging resp.OK.User.ID and update their access token payload as well.
f
Yea but this is suboptimal because the front end will think I’ve not verified my mail And I’m stuck in the verification screen
And if I revoke all the sessions for the user I’m still stuck in the screen
r
that's why, you should make an API call if the access token payload says that the email is not verified, and make your UI such that it can handle this case. I don't think there is a way around that.
f
okay so how would I do this on the frontend then? to either use the atp or to make the api call
well i'll just revoke all sessions
log the user out and if he signs in the atp is done
r
well, even if you revoke all sessions, it won't get reflected immediatekly
cause the access token is stateless
you can enable access token blacklisting on the core's side which will make the sign out happen immediately, but then all your session verifications from there on will query the core
so i would really suggest that you make your UI just show null while the API call is being made, and make the API call only when the access token payload says that the email is not verified.
Oh and you would also need to override the isEmailVerified API to change the access token payload.
f
Does it make sense to just revoke alls sessions then create a new one?
The thing is also that I dont make the API call supertokens does
This way it works still the ui is completely bugged
I dont even understand why...
I took a closer look at the example of supertokens-auth-react where the signin then verification and then set password happens. Because I have a similar usecase where I set a username after signup
r
hmm. That example app is quite different than setting a username post sign up
> Does it make sense to just revoke alls sessions then create a new one? I don't think this is a good idea
f
why?
r
cause it's a bad user experience
they have to login again
f
well you have to verify the email on signup
r
so they sign up, verify your email and then sign in again
that's odd
or maybe not.. depends
f
I'll revoke all sessions and create a new session programmatically
that way they dont have to sign in again
their sessions just gets a new one
but seemlesly
r
but then you would be creating a new session for the browser on which they opened the link - which might not be the browser they intend to use
f
Oh man thats true
tough.
I dont know what to do actually.
Because how can I return null while supertokens makes the api call
r
so, i would say you should query the API if their email verification is not done each time, and then once it's done, the access token payload will reflect that and it should be good enough
well, the API call happens quickly
so the user will see am empty screen for a very short amount of time
f
yes but how does my ui know that the api call is in progress
And what do I need to change on backend and frontend ....
r
so you are using auth guard right?
like EmailPasswordAuth
f
Yes
Yeah
r
so that should render your UI only when the session exists and the email is verifeid
f
I'm using ThirdPartyAuthNoSSR
r
just wrap your pages in that and it should be fine
huh.. which recipe are you using on the frontend?
EmailPassword or ThirdParty?
f
ThirdPartyEmailPasswordNoSSR my bad.
r
right ok
so wrap all your pages in that
and it should be fine
f
I did that.
But the problem is that my auth pages show up
twice
r
how are you rendering your auth page?
f
and also I currently have a flow where I first signup then set a username and then redirect to my dashboard
Just a normal next page wrapped in the ThirdPartyEmailPasswordAuthNoSSR context with authRequired set to false.
I now check if doesSessionExist is true and if so return null and redirect to dashboard
I do this for almost every auth page
r
and how do you redirect to dashboard?
f
router.replace("/")
r
and in other routes, you dont have have the requiredAuth prop to false?
f
But it works now but the session is only created in the browser I use to verify my email
yes.
r
there could be some other issue which is causing the auth screen to show - it should not be happening
can you upload your code to github or something so that I can see?
f
But the bug is gone because now I did the thing where I create a new session and use the ATP to verify if a email is verified
But this is (obviously) not the right way to do it as stated by you as well. So how exactly would I need to change on the frontend and backend to use atp and the api call.
r
i mean, you should not have to change anything anywhere ideally. There is something else that's probably causing the flicker thing to happen.
if you can show your code base, we can help
f
Yes let me change some things real quick and then I’ll invite you to the two repositories on GitHub
r
sounds good
f
It seems to work now. I've changed the pages so that they return null while the api call is made.
I will still invite you if you provide me with your github username
r
rishabhpoddar
f
Alright I've invited you. I'm having trouble to secure the verify-email route and the set-username route.
Also I'm not sure If I implemented everything super clean.
Nah for sure I didn't do everything super clean 😄
r
ok thanks. Will have a look
f
I also just updated the readme so you can get onboarded a bit better.
r
thanks!
f
If you have any questions feel free to ask