diff --git a/examples/astro/with-thirdpartyemailpassword/package.json b/examples/astro/with-thirdpartyemailpassword/package.json index 92369741..e65f3455 100644 --- a/examples/astro/with-thirdpartyemailpassword/package.json +++ b/examples/astro/with-thirdpartyemailpassword/package.json @@ -16,8 +16,8 @@ "jsonwebtoken": "^9.0.2", "jwks-rsa": "^3.1.0", "micromatch": "^4.0.7", - "supertokens-node": "^20.0.2", - "supertokens-web-js": "^0.13.0", + "supertokens-node": "latest", + "supertokens-web-js": "latest", "typescript": "^5.5.4" }, "devDependencies": { diff --git a/examples/react/with-thirdpartyemailpassword/.gitignore b/examples/react/with-thirdpartyemailpassword/.gitignore new file mode 100644 index 00000000..a547bf36 --- /dev/null +++ b/examples/react/with-thirdpartyemailpassword/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/examples/react/with-thirdpartyemailpassword/README.md b/examples/react/with-thirdpartyemailpassword/README.md new file mode 100644 index 00000000..b52b429e --- /dev/null +++ b/examples/react/with-thirdpartyemailpassword/README.md @@ -0,0 +1,34 @@ +![SuperTokens banner](https://raw.githubusercontent.com/supertokens/supertokens-logo/master/images/Artboard%20%E2%80%93%2027%402x.png) + +# SuperTokens ThirdPartyEmailPassword Demo app for React + +This demo app demonstrates the following use cases: + +- Social Login / Sign up +- Email & Password login +- Logout +- Session management & Calling APIs + +## Project setup + +Use `npm` to install the project dependencies: + +```bash +npm install +``` + +## Run the demo app + +```bash +npm run dev +``` + +The app will start on `http://localhost:3000` + +## Author + +Created with :heart: by the folks at supertokens.com. + +## License + +This project is licensed under the Apache 2.0 license. diff --git a/examples/react/with-thirdpartyemailpassword/eslint.config.js b/examples/react/with-thirdpartyemailpassword/eslint.config.js new file mode 100644 index 00000000..e1770e4a --- /dev/null +++ b/examples/react/with-thirdpartyemailpassword/eslint.config.js @@ -0,0 +1,25 @@ +import js from "@eslint/js"; +import globals from "globals"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; +import tseslint from "typescript-eslint"; + +export default tseslint.config( + { ignores: ["dist"] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ["**/*.{ts,tsx}"], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + "react-hooks": reactHooks, + "react-refresh": reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + "react-refresh/only-export-components": ["warn", { allowConstantExport: true }], + }, + } +); diff --git a/examples/react/with-thirdpartyemailpassword/index.html b/examples/react/with-thirdpartyemailpassword/index.html new file mode 100644 index 00000000..81aefabd --- /dev/null +++ b/examples/react/with-thirdpartyemailpassword/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + React + TS + + +
+ + + diff --git a/examples/react/with-thirdpartyemailpassword/package.json b/examples/react/with-thirdpartyemailpassword/package.json new file mode 100644 index 00000000..f6cd914b --- /dev/null +++ b/examples/react/with-thirdpartyemailpassword/package.json @@ -0,0 +1,37 @@ +{ + "name": "react-supertokens", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "concurrently --kill-others \"npm run dev-server\" \"npm run dev-client\"", + "dev-client": "vite", + "dev-server": "tsx server.ts", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "cors": "^2.8.5", + "express": "^4.19.2", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^6.26.1", + "supertokens-node": "latest", + "supertokens-web-js": "latest" + }, + "devDependencies": { + "@eslint/js": "^9.9.0", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.3.1", + "concurrently": "^8.2.2", + "eslint": "^9.9.0", + "eslint-plugin-react-hooks": "^5.1.0-rc.0", + "eslint-plugin-react-refresh": "^0.4.9", + "globals": "^15.9.0", + "tsx": "^4.19.0", + "typescript": "^5.5.3", + "typescript-eslint": "^8.0.1", + "vite": "^5.4.1" + } +} diff --git a/examples/react/with-thirdpartyemailpassword/public/vite.svg b/examples/react/with-thirdpartyemailpassword/public/vite.svg new file mode 100644 index 00000000..e7b8dfb1 --- /dev/null +++ b/examples/react/with-thirdpartyemailpassword/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/react/with-thirdpartyemailpassword/server.ts b/examples/react/with-thirdpartyemailpassword/server.ts new file mode 100644 index 00000000..e66fdc6b --- /dev/null +++ b/examples/react/with-thirdpartyemailpassword/server.ts @@ -0,0 +1,101 @@ +import express from "express"; +import cors from "cors"; +import supertokens from "supertokens-node"; +import { verifySession } from "supertokens-node/recipe/session/framework/express"; +import { middleware, errorHandler, SessionRequest } from "supertokens-node/framework/express"; +import Session from "supertokens-node/recipe/session"; +import EmailPassword from "supertokens-node/recipe/emailpassword"; +import ThirdParty from "supertokens-node/recipe/thirdparty"; + +supertokens.init({ + framework: "express", + supertokens: { + connectionURI: "https://try.supertokens.com", + }, + appInfo: { + appName: "Hacking With SuperTokens", + apiDomain: "http://localhost:3001", + websiteDomain: "http://localhost:3000", + apiBasePath: "", + websiteBasePath: "/", + }, + recipeList: [ + EmailPassword.init(), + ThirdParty.init({ + // We have provided you with development keys which you can use for testing. + // IMPORTANT: Please replace them with your own OAuth keys for production use. + signInAndUpFeature: { + providers: [ + { + config: { + thirdPartyId: "google", + clients: [ + { + clientId: + "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + clientSecret: "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", + }, + ], + }, + }, + { + config: { + thirdPartyId: "github", + clients: [ + { + clientId: "467101b197249757c71f", + clientSecret: "e97051221f4b6426e8fe8d51486396703012f5bd", + }, + ], + }, + }, + { + config: { + thirdPartyId: "apple", + clients: [ + { + clientId: "4398792-io.supertokens.example.service", + additionalConfig: { + keyId: "7M48Y4RYDL", + privateKey: + "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", + teamId: "YWQCXGJRJL", + }, + }, + ], + }, + }, + ], + }, + }), + Session.init(), // initializes session features + ], +}); + +const app = express(); + +app.use( + cors({ + origin: "http://localhost:3000", + allowedHeaders: ["content-type", ...supertokens.getAllCORSHeaders()], + methods: ["GET", "PUT", "POST", "DELETE"], + credentials: true, + }) +); + +app.use(middleware()); + +app.get("/sessioninfo", verifySession(), async (req: SessionRequest, res) => { + const session = req.session; + res.send({ + sessionHandle: session!.getHandle(), + userId: session!.getUserId(), + accessTokenPayload: session!.getAccessTokenPayload(), + }); +}); + +// In case of session related errors, this error handler +// returns 401 to the client. +app.use(errorHandler()); + +app.listen(3001, () => console.log(`API Server listening on port 3001`)); diff --git a/examples/react/with-thirdpartyemailpassword/src/App.css b/examples/react/with-thirdpartyemailpassword/src/App.css new file mode 100644 index 00000000..0f650a48 --- /dev/null +++ b/examples/react/with-thirdpartyemailpassword/src/App.css @@ -0,0 +1,132 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; + color: rgba(255, 255, 255, 0.87); +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} + +.form-wrap { + display: flex; + flex-direction: column; + gap: 1em; + max-width: 320px; +} + +.form-wrap input { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + border: 1px solid #646cff; + border-radius: 8px; + padding: 0.6em 1em; + font-size: 1em; + font-family: inherit; +} + +div.wrapper { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 1em; + height: 100vh; + width: 100vw; + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +div.wrapper header { + height: 80px; + background: #ff9933; + display: flex; + align-items: center; + justify-content: center; +} + +div.wrapper main { + flex-grow: 1; + display: flex; + align-items: center; + justify-content: center; + min-width: 60%; +} + +div.wrapper footer { + height: 60px; + background: #1f1f1f; + display: flex; + align-items: center; + justify-content: center; +} diff --git a/examples/react/with-thirdpartyemailpassword/src/App.tsx b/examples/react/with-thirdpartyemailpassword/src/App.tsx new file mode 100644 index 00000000..a5a32aaf --- /dev/null +++ b/examples/react/with-thirdpartyemailpassword/src/App.tsx @@ -0,0 +1,31 @@ +import { useState } from "react"; +import reactLogo from "./assets/react.svg"; +import viteLogo from "/vite.svg"; +import "./App.css"; + +function App() { + const [count, setCount] = useState(0); + + return ( + <> +
+ + Vite logo + + + React logo + +
+

Vite + React

+
+ +

+ Edit src/App.tsx and save to test HMR +

+
+

Click on the Vite and React logos to learn more

+ + ); +} + +export default App; diff --git a/examples/react/with-thirdpartyemailpassword/src/Auth.tsx b/examples/react/with-thirdpartyemailpassword/src/Auth.tsx new file mode 100644 index 00000000..0e5676df --- /dev/null +++ b/examples/react/with-thirdpartyemailpassword/src/Auth.tsx @@ -0,0 +1,398 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { doesEmailExist, signIn, signUp } from "supertokens-web-js/recipe/emailpassword"; +import { getAuthorisationURLWithQueryParamsAndSetState } from "supertokens-web-js/recipe/thirdparty"; +// import { createSignal, onMount, Show } from "solid-js"; +import "./App.css"; +import { superTokensInit } from "./config/supertokens"; +import { useNavigate } from "react-router-dom"; +import { signInAndUp } from "supertokens-web-js/recipe/thirdparty"; +import { useState, useEffect } from "react"; + +async function handleGoogleCallback(navigate: (path: string) => void) { + try { + const response = await signInAndUp(); + + if (response.status === "OK") { + console.log(response.user); + if (response.createdNewRecipeUser && response.user.loginMethods.length === 1) { + console.log("sign up successful, google"); + } else { + console.log("sign in successful, google"); + } + // window.location.assign("/home"); + navigate("/dashboard/"); + } else if (response.status === "SIGN_IN_UP_NOT_ALLOWED") { + // the reason string is a user friendly message + // about what went wrong. It can also contain a support code which users + // can tell you so you know why their sign in / up was not allowed. + window.alert(response.reason); + } else { + // SuperTokens requires that the third party provider + // gives an email for the user. If that's not the case, sign up / in + // will fail. + + // As a hack to solve this, you can override the backend functions to create a fake email for the user. + + window.alert("No email provided by social login. Please use another form of login"); + navigate("/auth"); // redirect back to login page + } + } catch (err: any) { + if (err.isSuperTokensGeneralError === true) { + // this may be a custom error message sent from the API by you. + window.alert(err.message); + } else { + window.alert("Oops! Something went wrong."); + } + } +} + +async function googleSignInClicked() { + try { + const authUrl = await getAuthorisationURLWithQueryParamsAndSetState({ + thirdPartyId: "google", + + // This is where Google should redirect the user back after login or error. + // This URL goes on the Google's dashboard as well. + frontendRedirectURI: "http://localhost:3000/auth/callback/google", + }); + + // we redirect the user to google for auth. + window.location.assign(authUrl); + } catch (err: any) { + if (err.isSuperTokensGeneralError === true) { + // this may be a custom error message sent from the API by you. + window.alert(err.message); + } else { + window.alert("Oops! Something went wrong."); + } + } +} + +async function handleGitHubCallback(navigate: (path: string) => void) { + try { + const response = await signInAndUp(); + + if (response.status === "OK") { + console.log(response.user); + if (response.createdNewRecipeUser && response.user.loginMethods.length === 1) { + console.log("sign up successful, github"); + } else { + console.log("sign in successful, github"); + } + // window.location.assign("/home"); + navigate("/dashboard/"); + } else if (response.status === "SIGN_IN_UP_NOT_ALLOWED") { + // the reason string is a user friendly message + // about what went wrong. It can also contain a support code which users + // can tell you so you know why their sign in / up was not allowed. + window.alert(response.reason); + } else { + // SuperTokens requires that the third party provider + // gives an email for the user. If that's not the case, sign up / in + // will fail. + + // As a hack to solve this, you can override the backend functions to create a fake email for the user. + + window.alert("No email provided by social login. Please use another form of login"); + navigate("/auth"); // redirect back to login page + } + } catch (err: any) { + if (err.isSuperTokensGeneralError === true) { + // this may be a custom error message sent from the API by you. + window.alert(err.message); + } else { + window.alert("Oops! Something went wrong."); + } + } +} + +async function githubSignInClicked() { + try { + const authUrl = await getAuthorisationURLWithQueryParamsAndSetState({ + thirdPartyId: "github", + + // This is where Google should redirect the user back after login or error. + // This URL goes on the Google's dashboard as well. + frontendRedirectURI: "http://localhost:3000/auth/callback/github", + }); + + // we redirect the user to google for auth. + window.location.assign(authUrl); + } catch (err: any) { + if (err.isSuperTokensGeneralError === true) { + // this may be a custom error message sent from the API by you. + window.alert(err.message); + } else { + window.alert("Oops! Something went wrong."); + } + } +} + +async function handleAppleCallback(navigate: (path: string) => void) { + try { + const response = await signInAndUp(); + + if (response.status === "OK") { + console.log(response.user); + if (response.createdNewRecipeUser && response.user.loginMethods.length === 1) { + console.log("sign up successful, apple"); + } else { + console.log("sign in successful, apple"); + } + // window.location.assign("/home"); + navigate("/dashboard/"); + } else if (response.status === "SIGN_IN_UP_NOT_ALLOWED") { + // the reason string is a user friendly message + // about what went wrong. It can also contain a support code which users + // can tell you so you know why their sign in / up was not allowed. + window.alert(response.reason); + } else { + // SuperTokens requires that the third party provider + // gives an email for the user. If that's not the case, sign up / in + // will fail. + + // As a hack to solve this, you can override the backend functions to create a fake email for the user. + + window.alert("No email provided by social login. Please use another form of login"); + navigate("/auth"); // redirect back to login page + } + } catch (err: any) { + if (err.isSuperTokensGeneralError === true) { + // this may be a custom error message sent from the API by you. + window.alert(err.message); + } else { + window.alert("Oops! Something went wrong."); + } + } +} + +async function appleSignInClicked() { + try { + const authUrl = await getAuthorisationURLWithQueryParamsAndSetState({ + thirdPartyId: "apple", + + frontendRedirectURI: "http://localhost:3000/auth/callback/apple", // This is an example callback URL on your frontend. You can use another path as well. + redirectURIOnProviderDashboard: "http://localhost:3000/auth/callback/apple", // This URL goes on the Apple's dashboard + }); + + // we redirect the user to apple for auth. + window.location.assign(authUrl); + } catch (err: any) { + if (err.isSuperTokensGeneralError === true) { + // this may be a custom error message sent from the API by you. + window.alert(err.message); + } else { + window.alert("Oops! Something went wrong."); + } + } +} + +async function signUpClicked(email: string, password: string, navigate: (path: string) => void) { + try { + const response = await signUp({ + formFields: [ + { + id: "email", + value: email, + }, + { + id: "password", + value: password, + }, + ], + }); + + if (response.status === "FIELD_ERROR") { + // one of the input formFields failed validaiton + response.formFields.forEach((formField) => { + if (formField.id === "email") { + // Email validation failed (for example incorrect email syntax), + // or the email is not unique. + window.alert(formField.error); + } else if (formField.id === "password") { + // Password validation failed. + // Maybe it didn't match the password strength + window.alert(formField.error); + } + }); + } else if (response.status === "SIGN_UP_NOT_ALLOWED") { + // the reason string is a user friendly message + // about what went wrong. It can also contain a support code which users + // can tell you so you know why their sign up was not allowed. + window.alert(response.reason); + } else { + // sign up successful. The session tokens are automatically handled by + // the frontend SDK. + navigate("/dashboard/"); + } + } catch (err: any) { + console.log(err); + + if (err.isSuperTokensGeneralError === true) { + // this may be a custom error message sent from the API by you. + window.alert(err.message); + } else { + window.alert("Oops! Something went wrong."); + } + } +} + +async function checkEmail(email: string) { + try { + const response = await doesEmailExist({ + email, + }); + + return response; + } catch (err: any) { + if (err.isSuperTokensGeneralError === true) { + // this may be a custom error message sent from the API by you. + window.alert(err.message); + } else { + window.alert("Oops! Something went wrong."); + } + } +} + +async function signInClicked(email: string, password: string, navigate: (path: string) => void) { + try { + const response = await signIn({ + formFields: [ + { + id: "email", + value: email, + }, + { + id: "password", + value: password, + }, + ], + }); + + if (response.status === "FIELD_ERROR") { + response.formFields.forEach((formField) => { + if (formField.id === "email") { + // Email validation failed (for example incorrect email syntax). + window.alert(formField.error); + } + }); + } else if (response.status === "WRONG_CREDENTIALS_ERROR") { + window.alert("Email password combination is incorrect."); + } else if (response.status === "SIGN_IN_NOT_ALLOWED") { + // the reason string is a user friendly message + // about what went wrong. It can also contain a support code which users + // can tell you so you know why their sign in was not allowed. + window.alert(response.reason); + } else { + // sign in successful. The session tokens are automatically handled by + // the frontend SDK. + navigate("/dashboard/"); + } + } catch (err: any) { + if (err.isSuperTokensGeneralError === true) { + // this may be a custom error message sent from the API by you. + window.alert(err.message); + } else { + window.alert("Oops! Something went wrong."); + } + } +} + +function Auth() { + const navigate = useNavigate(); + superTokensInit(); + const [showOAuthLoading] = useState( + (() => { + if (window.location.pathname === "/auth/callback/google") { + return "Google"; + } + + if (window.location.pathname === "/auth/callback/github") { + return "GitHub"; + } + + if (window.location.pathname === "/auth/callback/apple") { + return "Apple"; + } + + return false; + })() + ); + + useEffect(() => { + if (window.location.pathname === "/auth/callback/google") { + handleGoogleCallback(navigate); + } + + if (window.location.pathname === "/auth/callback/github") { + handleGitHubCallback(navigate); + } + + if (window.location.pathname === "/auth/callback/apple") { + handleAppleCallback(navigate); + } + }, []); + + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + + const handleSignUpClicked = async () => { + const res = await checkEmail(email); + if (!res?.doesExist) { + signUpClicked(email, password, navigate); + } else { + window.alert("Email already exists. Please sign in instead"); + } + }; + + const handleSignInClicked = async () => { + const res = await checkEmail(email); + if (res?.doesExist) { + signInClicked(email, password, navigate); + } else { + window.alert("Email does not exist. Please sign up instead"); + } + }; + + const handleGoogleSignInClicked = async () => { + googleSignInClicked(); + }; + + const handleGithubSignInClicked = async () => { + githubSignInClicked(); + }; + + const handleAppleSignInClicked = async () => { + appleSignInClicked(); + }; + + return ( +
+
+ {showOAuthLoading ? ( + <>Logging-in via {showOAuthLoading}, please wait... + ) : ( + <> + setEmail((e.target as HTMLInputElement).value)} + /> + setPassword((e.target as HTMLInputElement).value)} + /> + + + + + + + )} +
+
+ ); +} + +export default Auth; diff --git a/examples/react/with-thirdpartyemailpassword/src/Dashboard.tsx b/examples/react/with-thirdpartyemailpassword/src/Dashboard.tsx new file mode 100644 index 00000000..edfa2b14 --- /dev/null +++ b/examples/react/with-thirdpartyemailpassword/src/Dashboard.tsx @@ -0,0 +1,59 @@ +// import { Show, createEffect, createSignal } from "solid-js"; +import "./App.css"; +import { superTokensInit } from "./config/supertokens"; +import Session from "supertokens-web-js/recipe/session"; +import { useNavigate } from "react-router-dom"; +import { useState, useEffect } from "react"; + +function Dashboard() { + const navigate = useNavigate(); + superTokensInit(); + + const [loading, setLoading] = useState(true); + + const getSessionInfo = async () => { + const response = await fetch("http://localhost:3001/sessioninfo", { + headers: { + "Content-Type": "application/json", + }, + method: "GET", + credentials: "include", + }); + + const data = await response.json(); + + alert(JSON.stringify(data)); + }; + + async function signOut() { + await Session.signOut(); + navigate("/"); + } + + useEffect(() => { + (async () => { + if (await Session.doesSessionExist()) { + setLoading(false); + } else { + navigate("/"); + } + })(); + }, []); + + return ( +
+
+ {loading ? ( +
Loading...
+ ) : ( + <> + + + + )} +
+
+ ); +} + +export default Dashboard; diff --git a/examples/react/with-thirdpartyemailpassword/src/assets/react.svg b/examples/react/with-thirdpartyemailpassword/src/assets/react.svg new file mode 100644 index 00000000..6c87de9b --- /dev/null +++ b/examples/react/with-thirdpartyemailpassword/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/react/with-thirdpartyemailpassword/src/config/supertokens.ts b/examples/react/with-thirdpartyemailpassword/src/config/supertokens.ts new file mode 100644 index 00000000..739ae642 --- /dev/null +++ b/examples/react/with-thirdpartyemailpassword/src/config/supertokens.ts @@ -0,0 +1,23 @@ +import SuperTokens from "supertokens-web-js"; +import Session from "supertokens-web-js/recipe/session"; +import EmailPassword from "supertokens-web-js/recipe/emailpassword"; +import ThirdParty from "supertokens-web-js/recipe/thirdparty"; + +let initialized = false; + +export const superTokensInit = () => { + if (initialized) { + return; + } + + SuperTokens.init({ + appInfo: { + apiDomain: "http://localhost:3001", + apiBasePath: "", + appName: "Hacking With SuperTokens", + }, + recipeList: [Session.init(), EmailPassword.init(), ThirdParty.init()], + }); + + initialized = true; +}; diff --git a/examples/react/with-thirdpartyemailpassword/src/index.css b/examples/react/with-thirdpartyemailpassword/src/index.css new file mode 100644 index 00000000..279fff56 --- /dev/null +++ b/examples/react/with-thirdpartyemailpassword/src/index.css @@ -0,0 +1,68 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/examples/react/with-thirdpartyemailpassword/src/main.tsx b/examples/react/with-thirdpartyemailpassword/src/main.tsx new file mode 100644 index 00000000..db435bfd --- /dev/null +++ b/examples/react/with-thirdpartyemailpassword/src/main.tsx @@ -0,0 +1,27 @@ +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import Auth from "./Auth.tsx"; +import Dashboard from "./Dashboard.tsx"; +import "./index.css"; +import { createBrowserRouter, RouterProvider } from "react-router-dom"; + +const router = createBrowserRouter([ + { + path: "/", + element: , + }, + { + path: "/auth/callback/*", + element: , + }, + { + path: "/dashboard", + element: , + }, +]); + +createRoot(document.getElementById("root")!).render( + + + +); diff --git a/examples/react/with-thirdpartyemailpassword/src/vite-env.d.ts b/examples/react/with-thirdpartyemailpassword/src/vite-env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/examples/react/with-thirdpartyemailpassword/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/react/with-thirdpartyemailpassword/tsconfig.app.json b/examples/react/with-thirdpartyemailpassword/tsconfig.app.json new file mode 100644 index 00000000..dd60786c --- /dev/null +++ b/examples/react/with-thirdpartyemailpassword/tsconfig.app.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/examples/react/with-thirdpartyemailpassword/tsconfig.json b/examples/react/with-thirdpartyemailpassword/tsconfig.json new file mode 100644 index 00000000..eb69b0d0 --- /dev/null +++ b/examples/react/with-thirdpartyemailpassword/tsconfig.json @@ -0,0 +1,4 @@ +{ + "files": [], + "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }] +} diff --git a/examples/react/with-thirdpartyemailpassword/tsconfig.node.json b/examples/react/with-thirdpartyemailpassword/tsconfig.node.json new file mode 100644 index 00000000..716bc288 --- /dev/null +++ b/examples/react/with-thirdpartyemailpassword/tsconfig.node.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["vite.config.ts"] +} diff --git a/examples/react/with-thirdpartyemailpassword/vite.config.ts b/examples/react/with-thirdpartyemailpassword/vite.config.ts new file mode 100644 index 00000000..00e44f82 --- /dev/null +++ b/examples/react/with-thirdpartyemailpassword/vite.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], + server: { + port: 3000, + }, +}); diff --git a/examples/solidjs/with-thirdpartyemailpassword/package.json b/examples/solidjs/with-thirdpartyemailpassword/package.json index afe2b55f..c45991fe 100644 --- a/examples/solidjs/with-thirdpartyemailpassword/package.json +++ b/examples/solidjs/with-thirdpartyemailpassword/package.json @@ -15,8 +15,8 @@ "cors": "^2.8.5", "express": "^4.19.2", "solid-js": "^1.8.17", - "supertokens-node": "^18.0.1", - "supertokens-web-js": "^0.12.0" + "supertokens-node": "latest", + "supertokens-web-js": "latest" }, "devDependencies": { "concurrently": "^8.2.2",