supertokens / supertokens-auth-react Goto Github PK
View Code? Open in Web Editor NEWReactJS authentication module for SuperTokens
Home Page: https://supertokens.com
License: Other
ReactJS authentication module for SuperTokens
Home Page: https://supertokens.com
License: Other
typo in ResetPasswordFeature
shadowRoot
error.TODO
in the code to add relevant links to documentations.
If I go to /auth/hello
, I see a white screen. Instead, we should maybe redirect the user to /
? Or to /auth
(or whatever the base path is as specified by the user)
import Session from "supertokens-auth-react/recipe/session"
supertokens-website
provides.Below the sign up form, it is written:
"By signin up, you agree to ourTerms of Service and Privacy Policy"
init
function. If they provide a link to:
import { ResetPasswordUsingToken } from "supertokens-auth-react/recipe/emailpassword"
${websiteBasePath}/reset-password
shadow-root
to prevent CSS clashesinit
function as explained in this comment${websiteBasePath}/reset-password
, with a token in the URL, then continue the flow normally.${websiteBasePath}/reset-password
without a token in the URL, the enter email form is showed, otherwise the submit new password form is showed${websiteBasePath}/reset-password?token=...&rid=email-password
Our theme right now supports only string
values, but if someone makes their own theme, that may have other input values too. An input type of a number / boolean etc.. should be allowed in the form fields array when sending to the backend.
To allow for this on the backend, I had to make the following changes (pick whichever apply on the frontend):
value
from string
to any
string
Here is the backend commit with the above changes for your reference: supertokens/supertokens-node@20691af
Generic StyleProvider taking a getDefaultStyle ร sprops, move library to components/common for use in other themes from other repos.
import { ResetPasswordUsingTokenTheme } from "supertokens-auth-react/recipe/emailpassword"
ThemeProvider
which can be used to overwrite the default theme based on the passed props.import EmailPassword from "supertokens-auth-react/recipe/emailpassword"
All features in this module will have a default full page layout, and a widget component that the user can use to add that anywhere on their website. The full page layouts must use these same widget components.
The default full page of all features must be able to be disabled via a disableDefaultImplementation
boolean
passed to them in their init
function.
All features must follow their respective parts of the new FDI (v1.3.0)
Following are the sets of feature components:
The init
function of this module takes options as per the above features:
EmailPassword.init({
palette: {...},
useShadowDom: false,
signInAndUpFeature: {...},
resetPasswordUsingTokenFeature: {...}
})
palette represents the styling theme (primary colours, secondary colours, link, background colours ...)
useShadowDom: Represents wether we should use the shadow root dom to encapsulate our components. Pros and cons are discribed in #22
This might affect how error handling of queries to the API behaves.
To see fetch
behaviour, please run the following on any browser:
fetch("https://httpstat.us/500")
.then(function(response) {
console.log("error not thrown", response.status);
})
And the output of the above will be "error not thrown 500"
It seems that the login box's height and width takes precedence over other item's height and width.
If you run the demo app and go from sign in to reset password UI, you will see that the footer size increases. That is because the sign in form is squashing the footer it seems.
I am not sure about this issue since it could be something wrong with my CSS
package-lock's purpose is to generate the exact same dependencies tree wherever the package is installed.
It is intended to be commited. See Stackoverflow
Any reason why we are not commiting it atm?
Should be responsible for:
Provide routing functionality. The algorithm is here.
Has a way for any module to get the appInfo
if needed.
Has an init function that takes:
recipeList
: RecipeModule[]Provides an abstract recipeModule
class that modules must extend
:
websiteBasePath
).rId
. The rId
will be an argument to the constructor.Recipe modules must be build such that parent recipe's rId
can be propagated to them. This is how
You need to pass this along with whatever else the user has give. So the header should look like (Or something equivalent):
let userHeaders = {};
if (config.refreshAPICustomHeaders !== undefined) {
usersHeaders = config.refreshAPICustomHeaders
}
supertokensRequest.init({
refreshAPICustomHeaders: {
rid: this.getRecipeId(),
...usersHeaders
}
})
We should allow the user to do the following in the render function:
It's not recommended to do those in the render function at the moment because they all read the browser storage, which is slow. Since render can be called many times, that means each time a slow operation like the above will cause a UI lag.
SuperTokensRouteWithRecipeId
in superTokensRoute.tsx
equal to the render function of that component?index.ts
of the recipe, you also need to export all the functions inside the Wrapper
class, so that they can be accessed via import * as ___ from "..."
index.ts
of the recipe, you also need to export all the things that are outside the Wrapper
class so that they can be accssed via import ___ from "..."
if
statements without {}
. I found one instance in getPathsToComponentWithRecipeIdMap
in superTokens.tsx
. Also, please use explicit comparison.. Does if ([])
equal to if (false)
?getMatchingComponentForRouteAndRecipeId
in superTokens.tsx
getMatchingComponentForRouteAndRecipeId
should take path as NormalisedURLPath
. Not as a string. You want to convert this class to a string as late as possible, and only if absolutely necessary.getMatchingComponentForRouteAndRecipeId
is written in a way that if a rId
is provided in the URL, but that doesn't match any recipe, then we will treat it as if rId
is not there is not there in the URL. Is this expected?canHandleRoute
, I think it's better to call the getRoutingComponent
and check if that is undefined
or not. This way, it's guaranteed that if we change the routing algo, then canHandleRoute
will change accordingly.new NormalisedURLPath(window.location.pathname)
into a function in utils. This way, if it turns out later that window.location.pathname
is not correct, then we can easily change it. It also gives the advantage that we do not use window.location.pathname
without first normalising it anywhere.httpRequest.ts
, if config
is undefined
, and we do ...config
, will that throw an error?httpRequest.ts
, no need for supertokens-auth-react-version
canHandleRoute
and getRoutingComponent
in recipeModule.ts
?signInAndUpFeature
object in the init
call, I suggest you create a function like validateAndNormaliseSignInAndUpFeatureConfig
which takes what the user gave and returns the fully normalised config back which you can then propagate to the features. So this function will:
onSuccessRedirectURL
to /
if the user has not provided onedisabledDefaultImplementation
, you are using the user's provided config before actually normalising it first. The first thing we want to do in the recipe is normalise all of the user's input, and add all defaults and then use that object.SignInAndUp
to a normal class that extends React? Having functions inside a function, not being able to control things like shouldComponentUpdate
etc.. are all restrictive. I prefer to use functional component only if they are not complex.validateFormOrThrow
in utils.ts
does not throw any error.SignInAndUp
, I prefer if we first normalise (add defaults) the config / props given by the user and store the normalised version of that as an instance variable. So this way, when you are calling the signUp API, you do not have to check if props.signUpApi === undefined
. You can just do this.config.signUpApi(..)
.signUpApi
and signInApi
in emailPassword.ts
do not take care of error handling?axios
or fetch
.SignInAndUp
, signInAPI
should not change state.. it should only return the response of the API call in a normalised way. We need to segregate API call layer from state layer of a component.useEffect
carefullySuperTokensRouteWithRecipeId
runs every render or every route change?fdi-version
header to requests as per supertokens/frontend-driver-interface#2. The index.ts
of emailpassword does not does not export the components as part of the default export. Is that intentional? This means that users cannot do <EmailPassword.SignInAndUp />
Why do you have export { SessionAPIWrapper };
in the index.ts
of session recipe?
getMatchingComponentForRouteAndRecipeId
should take path as NormalisedURLPath. Not as a string. You want to convert this class to a string as late as possible, and only if absolutely necessary.
The below
(My comment) getMatchingComponentForRouteAndRecipeId is written in a way that if a rId is provided in the URL, but that doesn't match any recipe, then we will treat it as if rId is not there is not there in the URL. Is this expected?
(Your reply) "Yes that's what we decided in our previous discussion around routing. If no rid is provided, or if rid is unknown we want to use the first recipe feature that matches."
Are you sure we had this conversation? Because on the backend, if the rId
is there, but it not matching any route, then I pass the request to the user.
If we are allowing users to change the background for the default routes, then where will that go? If we allow them to do it via style in the init, what if they give a different colour for sign up and sign in?
The below
validateFormOrThrow in utils.ts does not throw any error.
It actually does throw if length is different as per our conversation.
Yes, but the name suggests that if the form is valid, then it will not throw an error, else it will. Which means, as a consumer of this function I would not care about the return type.
It would be nice to move the API calls in SignInAndUp (and other components) to a different file that only contains API calls.
Does fetch
throw an error in case the status code is >= 300?
onSuccessRedirectURL
in the config can be a full URL right (in case of multi-tenancy)? In that case, you do not want to normalise it with NormalisedURLPath
. Perhaps there is no normalisation for this value that is required? (This applies to signInUp and to resetPassword feature)
When creating the defaultSignInFields
in utils.ts
(line 146), I think you forgot to add the email validator? If not that, you added the default password validator, overriding the user's password validator?
I feel the way the signIn form fields has been made can have bugs cause it's hard to understand for the following reasons (feel free to ignore this if you are very confident that your code regarding this is bug free):
You are doing an implicit check with if (config.resetPasswordURL) {
in utils.ts
. What is resetPasswordURL === ""
? Then is if ("") === if (true)
. Please, no more implicit checks. Which also means no more of config.style || {}
, and no more if (this.props.onHandleForgotPasswordClicked)
etc..
I may be wrong, but the normaliseResetPasswordUsingTokenFeature
does not seem to take the email and password validators from sign up.
There are two places you use Object.assign
, but everywhere else you use the spread operator. I prefer you stick on one style of coding.
I am not 100% sure about this, but extending component classes with PureComponent
may be better than doing so with Component
. PureComponent
does a shallow comparison of state and props to check if a render is needed which may be more efficient than Component
which always returns true
by default.
When setting state in components that are class based, you should use this.setState(oldState => {...})
method. This is a more reliable method since you are guaranteed to mutate the "actual current state" in case the setState runs after a couple other setStates
.
The return type of signUpAPI
(line 197 in SignInAndUp) (and other similar functions) is very confusing. Please rename response
and responseJson
to something better.
The FormBase
component is not using controlled input. I'm not sure what the benefit of that is, but maybe it's worth doing that?
The state for SignInAndUp can be improved from a type point of view (Not sure if the below will exactly work according to your logic, but something like this where you divide the state into clear distinct sub types based on the state of the form):
{
status: "LOADING"
} | {
status: "NOT_SUBMITTED",
} | {
status: "SUBMITTED",
user: User,
responseJson: any
}
Since supertokens-website
is in the dependencies
in package.json, you can use a normal import statement for it in session.ts
addAxiosInterceptors
, setAuth0API
and getAuth0API
are missing from the session recipe.
Is all the UI responsive for tablet, mobile, 4k screens etc..?
Please make sure this doesn't happen with any other navgiation as well.
tests/end-to-end/signin.test.js
tests/end-to-end/signup.test.js
This issue is intended as a tracker on which versions we will support for our peer dependencies.
react
: Currently ^16.14.0
but we can definitely go down as this is the latest version that was installed when I ran npm install react
react-router-dom
: Currently ^5.2.0
which is latest. I think we could support up to 4.0
since the API never changes. I'll have to test.
We want to allow to extend the default EmailPassword theme to add custom behaviour such as :
We want to do this without having to:
The idea is to add an optional property called decorators
to formFields
. This decorator consists of a React element that will be inserted below the input field and that will take its value as props.
Example:
SuperTokens.init({
appInfo: {},
recipeList: [
EmailPassword.init({
signInAndUpFeature: {
onSuccessRedirectURL: '/dashboard',
signUpForm: {
formFields: [{
id: "password",
decorator: <PasswordStrengthMeterDecorator />
}]
}
}
})
]
});
With PasswordStrengthMeterDecorator
a React element that takes a value as props (the pasword which is updated on each changes), and displays a UI below the input.
We can provide few decorators as examples but the idea is that the developers can implement their own to extend the default theme easily if they like.
Implementation is straightforward, we simply need to:
decorator?: JSX.Element
to FormFieldBaseConfig
type.onChange in
input.tsx` to make sure that the state is updated each time the value is updated (as opposed to using React refs).decorator
element, give it input value
as props.Autocomplete for extensions such as 1Password, Dashlane and Lastpass does not work within shadow dom:
It only works when shadow dom is disabled. This is not an issue related to us directly as stated in https://1password.community/discussion/79137/shadow-dom-polymer-forms-do-not-fill-companion-extension-v4-7-4-now-supports-shadow-doms
Basically 1Password does a querySelectorAll
which won't work with shadow Dom. That is their responsibility to catch shadow root dom component in the future with their extensions. It seems pretty straightforward and I don't understand why they haven't done such a thing yet (maybe not a top priority for them).
I think it's pretty important for us as an open source authentication solution to support password managers.
We can't provide support for password managers while shadow root is enabled at the moment. Password manager providers do not seem to be actively working on solving the issue.
Bunch of other open source frameworks are facing this issue: Ionic, Polymer.
Other resource on the subject: WICG/webcomponents#572
In case they want support for password managers, we could provide an "obscure" config to allow users to disable shadow dom letting them know that doing such a thing might result in CSS clashes because they are removing CSS encapsulation.
SuperTokens.init({
appName: "",
{...},
useShadowDom: false // default true
/auth/signout
.To use like this:
import { signOut } from "supertokens-auth-react/recipe/emailpassword"
function NavBar () {
function async onLogout () {
await signOut();
window.location.href = "/";
}
return (
<ul>
<li>Home</li>
<li onClick={onLogout}>Logout</li>
</ul>
)
}
You can do so by copying the tests on the supertokens-website
. You need to run tests for axios
and for fetch
. Just adding one test for both should be enough since this SDK is a wrapper to the actual SDK. The one test can be a simple refresh test as done here:
Getting a session expired status code from sign out should not throw that error to the user. Instead, it should be treated as successful. Please make sure to not hardcode 401
and instead use what the user has given to the session recipe's expired status code.
You are giving the header as rid: EmailPassword.RECIPE_ID
. Arent you supposed to instead do rid: this.getRecipeId()
so that if recipe is being overided, the new recipeId will be used? Please make sure this type of error does not exist anywhere else.
Since we are in this subject, why do you make the react components propogate the rid to the functions in the EmailPassword recipe? Instead, those functions should add the rid
header like rid: this.getRecipeId()
. Which is much cleaner.
This is so that the end user doesn't see messages for password syntax validation during sign in (which no site does)
Similar to https://login.retool.com/auth/login for incorrect email.
Implement animations for default themes:
Steps to reproduce:
1/ Go to /auth/reset-password
2/ Component is not displayed.
We would like to call this function at the top of the file near the import statement. However, that means this may get executed before the supertokens.init
function. In this case, it throws Error: No instance of Function found. Make sure to call the "init" method.
error. A few points:
addAxiosInterceptors
, we call the getInstanceOrThrowError
function. Because the init
function has not been called yet, an error is thrown. We can fix this by simply calling the supertokens-website
functions from the static functions directly (for all the functions related to session).Emotion v11 and shadowDom support for v11
React allows to add styles directly to its components using the style
props.
Nevertheless, some styling libraries are becoming very popular to do styling in react. Styled components and aphrodite coming first.
Pros of using an external lib:
Cons:
I think we should go with Aphrodite because after exploring react styling for an hour or so, I realised that it is too restrictive and we would need to add a stylesheet at some point (for media query particularly).
Shadow root does not prevent from propagating the CSS from parent to child, it only works the other way around, making sure that 3rd party styling does not clash with parent styling. We don't have any good way to prevent parent style to clash with our modules. Shadow-root helps to isolate an inserted stylesheet but if we only use randomised classes with aphrodite we do not need to isolate our component with a shadow root.
Is your code architected in a way to allow:
package.json
"supertokens-website": "git+https://github.com/supertokens/supertokens-website.git#5.0"
test/server/package.json
"supertokens-node": "git+https://github.com/supertokens/supertokens-node.git#3.0"
with npm package.
import { SignInAndUpTheme } from "supertokens-auth-react/recipe/emailpassword"
SignUpTheme
and SignInTheme
.ThemeProvider
which can be used to overwrite the default theme based on the passed props.import { SignInAndUp } from "supertokens-auth-react/recipe/emailpassword"
${websiteBasePath}/
shadow-root
to prevent CSS clashesinit
function as explained in this commentIt's not about the number of steps required, but about making sure that each step runs successfully and at the end, I am able to run the tests.
This happens when I use manual routing only.
/auth/reset-password
./auth/reset-password
, it should also contain the rid
. In general, whenever we navgiate to a page controlled by us (from a button controlled by us), we want to add the rid
. For example, in case of forgot password, we want to add the rid
to the route if they have not provided the resetPasswordURL
in the signInForm
object during init.By default the "Sign In" page is displayed.
Should we:
EmailPassword.init({
signInAndUpFeature: {
defaultToSignIn: true
}
})
Another thing to take into account is:
People might want to post/tweet/email links to their signup page, so if we keep the "Sign-In" page, we should have a way to add a query param to toggle to Sign-up.
import {SuperTokensRoute} from "supertokens-auth-react"
<Router history={history}>
<NavBar />
<Switch>
<SuperTokensRoute />
</Switch>
<Footer />
</Router>
import SuperTokens from "supertokens-auth-react"
if (SuperTokens.canHandleRoute()) {
return SuperTokens.getRoutingComponent()
}
SuperTokensRoute
uses SuperTokens.getRoutingComponent()
"You will receive a password recovery link at your email address in a few minutes" -> Saying few minutes is not a good idea. Change it to "Please check your email for the password recovery link"
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.