To use SuperTokens with your own custom UI refer to our plain Javascript SDK supertokens-web-js
For any queries, or support requests, please email us at [email protected], or join our Discord server.
Frontend SDK for SuperTokens - for session management + automatically refreshing sessions
Home Page: https://supertokens.com
License: Other
To use SuperTokens with your own custom UI refer to our plain Javascript SDK supertokens-web-js
For any queries, or support requests, please email us at [email protected], or join our Discord server.
If a domain is specified while setting cookies, browsers automatically add a leading dot to it. So if the sessionScope is undefined or if it's the same as the hostname, we do not want to use it when setting the cookies.
If the domain is undefined, it get's set to the hostname whilst normalising it.
Desktop: https://zpl.io/bom7jQm
Desktop (hover state): https://zpl.io/2vRXxv7
Tab: https://zpl.io/aME3rJz
Mobile: https://zpl.io/brqODBr
Note: Please ignore the navigation (l.h.s) and only take values of the recipe boxes.
Hi there ๐,
First, I want to say I'm loving the library so far - as someone who is relatively new to coding, this makes it easy (and even fun!) to implement authentication, and I really appreciate it. Thanks so much for all your hard work on this!
I'm using the Axios interceptor in a client-side React app to make requests to my Fastify server (where Supertokens is set up).
So, I've identified a bug where if a user is already signed in and has a current session, and then they make a request to my API and receive a 401 error, the Axios interceptor assumes that the session is expired and automatically sends a request to refresh the session. But in my case, the user already has a current session, and my API is returning a 401 error for an entirely different reason. So the client keeps trying the /session/refresh
route (which is successful) and then re-trying my API route (which returns 401), and this becomes an infinite loop. (I can see this in the Network tab of Chrome Dev tools)
There are several ways for me to work around this, the easiest of which is to just have my API route return a different error code than 401. But I'm pretty sure this is a bug that needs to be addressed, so as to avoid the loop!
verifySession
middleware (i'm using Fastify, but it can be any Node server). The route returns a 401 error even after the session is verified through verifySession
I'd be happy to put together a more concrete example if needed. Let me know!
I have a hunch that this happens because of one of the status code checks below (or a similar one somewhere else), where it's only checking the response.status
code of the response, and so it automatically assumes that the session needs to be refreshed if the API returns a 401 error. But if the user already has a current session at this point, and it keeps getting a 401 error from an API route - this results in the loop. Perhaps some code could be added here to make sure that the 401 error is actually happening because of an expired session?
supertokens-website/lib/ts/axios.ts
Lines 157 to 170 in 1daef7b
supertokens-website/lib/ts/axios.ts
Lines 344 to 353 in 1daef7b
Access-Control-Expose-Headers
not set properly - which prevents frontend from reading the id-refresh-token, which prevents setting that state on the frontend.Access-Control-Expose-Headers
set to *
- which prevents frontend from reading the front-token, which prevents setting that state on the frontend. Even though it's star, it won't work cause of using credentials.api.example.com
and website is not on *.example.com
and not on example.com
), so Safari will not send cookies even if sameSite is none
. Switch to using header based auth.node-fetch
instead of browser fetch even on client side. People might be using node-fetch for server side rendering and then not realising that they are using that for client as well. Our interceptors are added to window.fetch so their node-fetch wouldn't get the interceptors added, preventing a call to the refresh API.credentials: "same-origin",
to the headers for that lib, the cookies won't be sent.CookieManager.clearAllCookies
or they might be manually adding cookies while replacing existing ones.verifySession
returns 403, and the response contains claimValidationErrors: id: st-ev
, this means that the user needs to go through the email verification flowThe init
function call can be seen here for fetch
and here for axios
.
As of now, the refreshTokenUrl
must be a full path to the user's refresh API endpoint. Instead, we need to change it so that if they give:
https://api.example.com
or https://api.example.com/
, this line assigns https://api.example.com/session/refresh
to AuthHttpRequest.refreshTokenUrl
, and similarly for index.ts
https://api.example.com/some/other/path
, this line assigns https://api.example.com/some/other/path
to AuthHttpRequest.refreshTokenUrl
, and similarly for index.ts
.
Essentially, we want them to just give the API domain for ease of use.
Important: We should also change the parameter name from refreshTokenUrl
to apiEndpoint
since that is more accurate now.
This issue will also require to write some tests for axios
and for fetch
which check that the right URL is assigned given various inputs. You could create a new function in axios.ts
and index.ts
to return the value of refreshTokenUrl
that was assigned.
When using supertokens-website
with Electron and running in production mode, supertokens-website
throws an error Error: Please provide a valid domain name
.
This is because in production mode electron return an empty string for window.location.origin
which the SDK relies on.
supertokens-website
should not throw an error when using with Electron
Add function
Add tests
Docs changes
Since the status code for unauthorise and try refresh token error is the same (401)
The current version is 4.4.x but documentation is at latest 3.2.x
Using cookies will:
anti-csrf
header.Do interceptors work from the get-go with Superagent? Would addAxiosInterceptors work with Superagent?
Currently (supertokens/supertokens-auth-react@1c9f5b3) supertokens-auth-react
relies on doesSessionExist
returning false in SessionAuth
when backend is not reachable.
Preserve the bevaiour to keep the developer experience good. It should still render the UI when backend is unreachable, because we instruct the user to setup the frontend first.
Add a unit test:
doesSessionExist
false
and NOT throw an errorNote: doesSessionExist
calls refresh session function underneath, so make sure to test for this case as well.
This will require unit tests to be set up.
Using
Nuxt.js - 2.15.8
@nuxtjs/axios - 5.13.6
SuperTokens-website - 10.0.5
SuperTokens-node - 8.2.0
Problem
Creating Axios instance from @nuxtjs/axios and adding supertoken Interceptor is breaking, causing token refreshing to work incorrectly. Interceptors are getting added after first request.
Code
/plugins/axios.js
import SuperTokens from 'supertokens-website';
export default function ({ $axios, $config }, inject) {
// Create a custom axios instance
const axiosInstance = $axios.create({
baseURL: $config.root,
});
// Add SuperTokens interceptors
SuperTokens.addAxiosInterceptors(axiosInstance);
// Inject to context as $axios
inject('axios', axiosInstance);
}
nuxt.config.js
plugins: ['~/plugins/axios']
Solution for users
Using Axios from Axios package (not from @nuxtjs/axios
) and injecting it into Nuxt.js context fixes the issue.
let sessionScope = normaliseSessionScopeOrThrowError(getWindowOrThrow().location.hostname);
if (options.sessionScope !== undefined) {
sessionScope = normaliseSessionScopeOrThrowError(options.sessionScope);
}
On Electron getWindowOrThrow().location.hostname
returns an empty string and that fails the check. Even if I have set the sessionScope
in config the location takes precedence. Would be nice for the config to be the one taken into account first.
const sessionScope = normaliseSessionScopeOrThrowError(options?.sessionScope || getWindowOrThrow().location.hostname);
If you're ok with it I can make a PR.
websiteRootDomain
OPTIONALrefreshAPICustomHeaders
OPTIONALautoAddCredentials
OPTIONALapiDomain
apiBasePath
OPTIONALI'm considering moving all auth to SuperTokens self hosted core.
Now my question is if i use verifySession() that guards my user's data API
is it making additional call to core server evertime?
Basically i see that there are 3 servers involved - front, back and core.
My goal would be to Log In user on front end by calling core
and then just call backend server (for users data) directly from front end.
(Without calling core server on each backend verifySession() )
Is this how SDK works? Or every time i use verifySession() , a call to core will be made?
Longer explanation: I consider using two Heroku dynos one for core and one for backend,
with CloudFlare worker for serving frontend.
If i would make two calls (back & core) for each API call,that would slow down
app and would increase time to get data to user by ~50%.
(And would lessen the worth of having serverless app in first place )
Ths need for this comes when someone is using us only for session recipe and another auth provider for login. In this case, the other auth provider would do a callback to this user's API layer directly which would create a new supertokens session.
In this case, the httpOnly session cookies would be correctly set, but since there are no frontend interceptors (since the auth provider made this API call directly), the frontend would either:
remove
.In case of (1), when the frontend would call doesSessionExist
, it would call the refresh API and it would all work fine. But in case of (2), doesSessionExist
would return false
. The only way to then make the frontend realise that the session actually exists is to manually clear the sIRTFrontend
cookie and then call doesSessionExist
.
So we should expose a function that does this - something like doesSessionExistCheckUsingBackendCall
, and then document that accordingly.
In the onUnauthorisedResponse function, in index.ts, we check if the response from the refresh API call is === 200. This results in an issue where if the user instead sends something like 201, it stops working.
We want to change that to check if the response is < 300.
Describe the solution you'd like
If I have a session on example.com, I should be able to have another, unrelated session on a.example.com
Additional context
If my main website is example.com, and I have a test site on test.example.com, I want to be able to test sessions in both of them. Right now, that is possible as long as I use different browsers for each (or incognito). But if I am logged into example.com and I open test.example.com in the same browser, then I cannot test sessions on test.example.com
After updating the server with cors middleware (options = { origin: true, credentials: true }) and front-end axios requests withCredentials:true, session cookies are properly received/sent from/to the server however the response sIdRefreshToken cookie is not accessible via document.cookies. This causes the anti-csrf token to be removed in the axios response interceptor.
Line 284 in 6978470
Even after this issue is resolved, the refresh endpoint would not work after the cookie has expired. I believe the solution may be to pass the sIdRefreshToken in a new response header instead of as a cookie and store it in the browser's localStorage instead.
Related to supertokens/supertokens-auth-react#256
After this is done, make sure to update supertokens-auth-react to take advantage of these hooks:
TODO:
SuperTokens frontend SDKs use local storage and frontend cookies to store information. The problem is that some web frameworks (Electron, Capacitor etc) have a very different way of handling cookies and storage.
For example, Electron apps have a separate layer for the actual frontend application and another layer for Electron functions. When setting frontend cookies these cookies are set to the electron layer and not the JS/Frontend layer, which means that when the SDK tries to read values from document.cookie
, nothing is returned.
To solve this problem, the SDK exposes a way to set custom cookie handlers that are used by the SDK instead of referring to document.cookie
directly. In the case of electron this handler can be used to get/set cookies from/to local storage.
In the case of Capacitor this is not a complete solution because:
For example in Capacitor even though window.localstorage
is available, it is not recommended to be used for long lasting information because the OS periodically clears it. The framework provides its own Storage API but only allows for async operations.
To solve this the SDK exposes a way to set custom handlers for localstorage
and sessionstorage
that will be used instead of accessing window.localstorage
and window.sessionstorage
directly.
An additional problem is that the SDK in specific cases reads from localstorage in the UI layer (which means it cannot be done asynchronously), to solve this the SDK allows storage to be handled in async and sync ways (using two sets of functions).
At the time of writing this is not a problem with Capacitor because the information that the SDK needs is not designed to be long lasting and there are no serious side effects if the OS clears the data. This consideration is not applicable to other frameworks because localstorage works as expected.
The final interface looks like this:
key: (index: number) => Promise<string | null>;
getItem: (key: string) => Promise<string | null>;
clear: () => Promise<void>;
removeItem: (key: string) => Promise<void>;
setItem: (key: string, value: string) => Promise<void>;
/**
* Sync versions of the storage functions
*/
keySync: (index: number) => string | null;
getItemSync: (key: string) => string | null;
clearSync: () => void;
removeItemSync: (key: string) => void;
setItemSync: (key: string, value: string) => void;
setCookie: (cookieString: string) => Promise<void>;
getCookie: () => Promise<string>;
I just noticed that my yarn upgrade-interactive
was suggesting moving from version 7.2.1 to 5.1.1. Doing some digging it seems the wrong tag was placed in npm package: https://www.npmjs.com/package/supertokens-website?activeTab=versions
In contradiction to the API docs, which say that sessionPossiblyExists
returns a boolean, it actually returns a Promise.
Not only did this trip me up as I didn't expect it from reading the docs, it also seems to complicate things unnecessarily since the sessionPossiblyExists
method does not actually do anything asynchronous. I'd suggest removing the async
from the method.
There is no window object when app is rendered server side (SSR)
See https://stackoverflow.com/questions/55151041/window-is-not-defined-in-next-js-react-app
Server side rendering using NextJS is impossible for supertokens-auth-react and/or for supertokens-website because of the following line which we could easily optimise
supertokens-website/lib/ts/utils.ts
Line 184 in 3fe92cb
Currently, using SuperTokens with NextJS is suboptimal as we need SuperTokens to be initialised to render logged in or authentication components, but we can only initialise SuperTokens on the client side. This defeats the purpose of using NextJS for SSR.
By making the use of window
object optional (if else statement on sessionScope object), we could init SuperTokens completely on the server side.
Note: This is the error that the app encounter during init
but there might be others, I haven't checked all use of window
object in Session.init()
yet
supertokens-website/axios
makeSuper
to addAxiosInterceptors
An instance of when we fire them is if after getting the refresh lock, and if the session doesn't exist, then we fire it. This may not be correct (see fetch.ts, line 357)
Hi there!
I do not want to fill out a form to contribute - so just a quick hint.
In the NodeJs docs: https://supertokens.io/docs/nodejs/installation
You can/should replace all occurrences of let
with const
- as far as I can see you do never assign a new value to the variables in question. Your app will still run fine - it propagates bad habits though ;).
For axios interceptor, if the refresh API throws a 500 error, then the use of axios does not get a proper error result back
I noticed (in the golang with-twirp example), that if the API revokes a session and then calls sessionContainer.GetSessionData()
, the API eventually returns a 401, unauthorised error.
The headers in this API call are as expected - the id-refresh-token header value is "remove" and the set-cookie headers remove the session tokens.
However, on the client side, we observe that it calls the refresh endpoint immediately after (which then yields a 401 too). This refresh endpoint should not be called since the frontend knows that the session has been revoked.
Perhaps this is an issue with the backend and not the frontend.. not sure.
There also seems to be an issue with fetch
doing an unnecessary refresh API call when there are no frontend cookies set, and the actual API to call returns a 500
error. In this case, it does a refresh call, then the actual APi call (which fails), and then a refresh call again. But I think that this happens only if both te refresh calls return a 500 error as well.
frontToken
, or idrefreshtoken
, or anticsrftoken
that are set in cookies will be deleted after 7 days.Finally, we decided to go with an iframe method that will allow us to share localstorage across sub domains.
TODO:
getUserId
and to doesSessionExist
(they both are now async)requireAuth
boolean in Auth wrapper and session wrapperTODO:
For example, getAccessTokenPayloadSecurely
calls doesSessionExist
internally. So if someone overrides doesSessionExist
and in that calls getAccessTokenPayloadSecurely
, it leads to an infinite loop. This can be solved by providing appropriate user context values and breaking the infinite recursion loop.
However, there is a case where refreshing a session calls recipeImpl.doesSessionExist
which then if it calls getAccessTokenPayloadSecurely
, which can also refresh the session. This causes an infinite recursive loop where the user context is no longer passed and cannot be used to break the loop.
Right now, a refresh call is done only after the access token call fails. Instead, we can detect if the access token has already expired, and if so, make the refresh call. This will save one network request.
Edit: this does pertain to ST's implementation of a fetch
wrapper.
Possibly out of scope of ST since this is for use with a third-party library (Redux Toolkit (RTK) Query), please close if so.
As described in this RTK issue: reduxjs/redux-toolkit#2205 (comment):
The expected behavior is the headers should be included in the request if included in any of the ways described (using prepareHeaders
, assigning object literal to headers
, or assigning the header within the header
key in an endpoint's query
).
The observed behavior headers are not able to be assigned (including rid
and Content-Type
to HTTP requests made with fetchBaseQuery
(see https://redux-toolkit.js.org/rtk-query/api/fetchBaseQuery#using-fetchbasequery) w/SuperTokens. This results in the request body not being parsed/sent correctly.
Thought I would open here since RTK Query is gaining traction, thanks!
Some example issues:
The current readme does not give a good overview of the project and does not guide the reader properly to use it.
Some changes, that will fix it
In our C# backend implementation, we missed a call to delete the frontend cookies when the core returned an UNAUTHORISED response when the frontend was calling the refresh session endpoint on the backend. It would be awesome to have a test for this specific scenario, as it's quite difficult for us to reproduce within the backend itself (depends on an UNAUTHORISED response from the core).
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.