diff --git a/boilerplate/frontend/react/README.md b/boilerplate/frontend/react/README.md index 4a7188d1..408eab7e 100644 --- a/boilerplate/frontend/react/README.md +++ b/boilerplate/frontend/react/README.md @@ -23,13 +23,14 @@ This project aims to demonstrate how to integrate SuperTokens into a React appli ┃ ┣ 📜guide-icon.svg ┃ ┣ 📜separator-line.svg ┃ ┗ 📜signout-icon.svg -┣ 📂Home --> "Dashboard" component, accessible only via the logged-in state of the app -┃ ┣ 📜CallAPIView.tsx -┃ ┣ 📜Home.css -┃ ┣ 📜SuccessView.tsx +┣ 📂Dashboard --> Protected route component, only accessible to authenticated users +┃ ┗ 📜index.tsx +┣ 📂Home --> Public landing page component, accessible regardless of auth state ┃ ┗ 📜index.tsx ┣ 📜App.css ┣ 📜App.tsx --> Root component of the app +┣ 📜config.tsx --> SuperTokens configuration +┣ 📜vite-env.d.ts ┗ 📜main.tsx --> Entry point of the app ``` @@ -70,17 +71,12 @@ The application uses [React Router](https://reactrouter.com/) for routing and co - Public landing page accessible to all users - Provides navigation to authentication and dashboard - - Displays basic application information and links + - Serves as the entry point for new users 4. **Dashboard Component (`/dashboard` route, `/Dashboard/index.tsx` component)** - Protected route only accessible to authenticated users - Protected by `SessionAuth` component - - Displays user information and session details - - Provides functionality to: - - View user ID - - Call test API endpoints - - Access documentation - - Sign out + - Displays user information and provides authenticated functionality When a user visits the application, they start at the home page (`/`). They can choose to authenticate through the `/auth` route, and once authenticated, they gain access to the protected dashboard. The session state is managed throughout the application using SuperTokens' session management. diff --git a/boilerplate/frontend/react/config/all_auth.tsx b/boilerplate/frontend/react/config/all_auth.tsx index c80d55fa..d2326937 100644 --- a/boilerplate/frontend/react/config/all_auth.tsx +++ b/boilerplate/frontend/react/config/all_auth.tsx @@ -45,9 +45,10 @@ export const SuperTokensConfig = { Session.init(), ], getRedirectionURL: async (context) => { - if (context.action === "SUCCESS" && context.newSessionCreated) { + if (context.action === "SUCCESS") { return "/dashboard"; } + return undefined; }, }; diff --git a/boilerplate/frontend/react/config/emailpassword.tsx b/boilerplate/frontend/react/config/emailpassword.tsx index c7ca086f..2478ac9b 100644 --- a/boilerplate/frontend/react/config/emailpassword.tsx +++ b/boilerplate/frontend/react/config/emailpassword.tsx @@ -24,9 +24,10 @@ export const SuperTokensConfig = { // use from SuperTokens. See the full list here: https://supertokens.com/docs/guides recipeList: [EmailPassword.init(), Session.init()], getRedirectionURL: async (context) => { - if (context.action === "SUCCESS" && context.newSessionCreated) { + if (context.action === "SUCCESS") { return "/dashboard"; } + return undefined; }, }; diff --git a/boilerplate/frontend/react/config/multifactorauth.tsx b/boilerplate/frontend/react/config/multifactorauth.tsx index 6f75a36a..0469b717 100644 --- a/boilerplate/frontend/react/config/multifactorauth.tsx +++ b/boilerplate/frontend/react/config/multifactorauth.tsx @@ -50,9 +50,10 @@ export const SuperTokensConfig = { Session.init(), ], getRedirectionURL: async (context) => { - if (context.action === "SUCCESS" && context.newSessionCreated) { + if (context.action === "SUCCESS") { return "/dashboard"; } + return undefined; }, }; diff --git a/boilerplate/frontend/react/config/passwordless.tsx b/boilerplate/frontend/react/config/passwordless.tsx index 00f674be..16b657e1 100644 --- a/boilerplate/frontend/react/config/passwordless.tsx +++ b/boilerplate/frontend/react/config/passwordless.tsx @@ -30,9 +30,10 @@ export const SuperTokensConfig = { Session.init(), ], getRedirectionURL: async (context) => { - if (context.action === "SUCCESS" && context.newSessionCreated) { + if (context.action === "SUCCESS") { return "/dashboard"; } + return undefined; }, }; diff --git a/boilerplate/frontend/react/config/thirdparty.tsx b/boilerplate/frontend/react/config/thirdparty.tsx index b071dd04..581c7166 100644 --- a/boilerplate/frontend/react/config/thirdparty.tsx +++ b/boilerplate/frontend/react/config/thirdparty.tsx @@ -31,9 +31,10 @@ export const SuperTokensConfig = { Session.init(), ], getRedirectionURL: async (context) => { - if (context.action === "SUCCESS" && context.newSessionCreated) { + if (context.action === "SUCCESS") { return "/dashboard"; } + return undefined; }, }; diff --git a/boilerplate/frontend/react/config/thirdpartyemailpassword.tsx b/boilerplate/frontend/react/config/thirdpartyemailpassword.tsx index d5562f53..ecc18d81 100644 --- a/boilerplate/frontend/react/config/thirdpartyemailpassword.tsx +++ b/boilerplate/frontend/react/config/thirdpartyemailpassword.tsx @@ -34,9 +34,10 @@ export const SuperTokensConfig = { Session.init(), ], getRedirectionURL: async (context) => { - if (context.action === "SUCCESS" && context.newSessionCreated) { + if (context.action === "SUCCESS") { return "/dashboard"; } + return undefined; }, }; diff --git a/boilerplate/frontend/react/config/thirdpartypasswordless.tsx b/boilerplate/frontend/react/config/thirdpartypasswordless.tsx index f6045ebe..3b0bec07 100644 --- a/boilerplate/frontend/react/config/thirdpartypasswordless.tsx +++ b/boilerplate/frontend/react/config/thirdpartypasswordless.tsx @@ -42,9 +42,10 @@ export const SuperTokensConfig = { Session.init(), ], getRedirectionURL: async (context) => { - if (context.action === "SUCCESS" && context.newSessionCreated) { + if (context.action === "SUCCESS") { return "/dashboard"; } + return undefined; }, }; diff --git a/boilerplate/frontend/react/public/assets/fonts/MenloRegular.ttf b/boilerplate/frontend/react/public/assets/fonts/MenloRegular.ttf new file mode 100644 index 00000000..033dc6d2 Binary files /dev/null and b/boilerplate/frontend/react/public/assets/fonts/MenloRegular.ttf differ diff --git a/boilerplate/frontend/react/public/assets/images/arrow-right-icon.svg b/boilerplate/frontend/react/public/assets/images/arrow-right-icon.svg new file mode 100644 index 00000000..95aa1fec --- /dev/null +++ b/boilerplate/frontend/react/public/assets/images/arrow-right-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/boilerplate/frontend/react/public/assets/images/background.png b/boilerplate/frontend/react/public/assets/images/background.png new file mode 100644 index 00000000..2147c15c Binary files /dev/null and b/boilerplate/frontend/react/public/assets/images/background.png differ diff --git a/boilerplate/frontend/react/public/assets/images/blogs-icon.svg b/boilerplate/frontend/react/public/assets/images/blogs-icon.svg new file mode 100644 index 00000000..a2fc9dd6 --- /dev/null +++ b/boilerplate/frontend/react/public/assets/images/blogs-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/boilerplate/frontend/react/public/assets/images/celebrate-icon.svg b/boilerplate/frontend/react/public/assets/images/celebrate-icon.svg new file mode 100644 index 00000000..3b40b1ef --- /dev/null +++ b/boilerplate/frontend/react/public/assets/images/celebrate-icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/boilerplate/frontend/react/public/assets/images/guide-icon.svg b/boilerplate/frontend/react/public/assets/images/guide-icon.svg new file mode 100644 index 00000000..bd85af72 --- /dev/null +++ b/boilerplate/frontend/react/public/assets/images/guide-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/boilerplate/frontend/react/public/assets/images/index.ts b/boilerplate/frontend/react/public/assets/images/index.ts new file mode 100644 index 00000000..7adf036c --- /dev/null +++ b/boilerplate/frontend/react/public/assets/images/index.ts @@ -0,0 +1,8 @@ +import SeparatorLine from "./separator-line.svg"; +import ArrowRight from "./arrow-right-icon.svg"; +import SignOutIcon from "./sign-out-icon.svg"; +import GuideIcon from "./guide-icon.svg"; +import BlogsIcon from "./blogs-icon.svg"; +import CelebrateIcon from "./celebrate-icon.svg"; + +export { SeparatorLine, ArrowRight, SignOutIcon, GuideIcon, BlogsIcon, CelebrateIcon }; diff --git a/boilerplate/frontend/react/public/assets/images/separator-line.svg b/boilerplate/frontend/react/public/assets/images/separator-line.svg new file mode 100644 index 00000000..7127a00d --- /dev/null +++ b/boilerplate/frontend/react/public/assets/images/separator-line.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/boilerplate/frontend/react/public/assets/images/sign-out-icon.svg b/boilerplate/frontend/react/public/assets/images/sign-out-icon.svg new file mode 100644 index 00000000..6cc4f85f --- /dev/null +++ b/boilerplate/frontend/react/public/assets/images/sign-out-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/boilerplate/frontend/react/src/App.css b/boilerplate/frontend/react/src/App.css index e4b74914..d73bc9c1 100644 --- a/boilerplate/frontend/react/src/App.css +++ b/boilerplate/frontend/react/src/App.css @@ -1,3 +1,12 @@ +/* Base styles */ +:root { + --primary-color: #ff9933; + --primary-hover: #ff8a15; + --success-color: #3eb655; + --success-bg: #e7ffed; + --border-color: #e0e0e0; +} + @font-face { font-family: Menlo; src: url("./assets/fonts/MenloRegular.ttf"); @@ -11,32 +20,17 @@ body { -moz-osx-font-smoothing: grayscale; } -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; -} - -.logos { - display: flex; - align-items: center; - gap: 20px; - padding-bottom: 80px; -} - -.logos span { - font-size: 8rem; - font-weight: bold; -} - -.logos img { - width: 240px; +* { + box-sizing: border-box; } -.App { +/* Layout */ +.app-container { display: flex; flex-direction: column; width: 100%; - height: 100%; - font-family: Rubik; + min-height: 100vh; + font-family: Rubik, sans-serif; } .fill { @@ -46,239 +40,140 @@ code { flex: 1; } -.buttons { - display: flex; - gap: 4px; -} - -.sessionButton { - padding-left: 13px; - padding-right: 13px; - padding-top: 8px; - padding-bottom: 8px; - background-color: black; - border-radius: 10px; - cursor: pointer; - color: white; - font-weight: bold; - font-size: 17px; - text-decoration: none; -} - +/* Header */ header { - display: flex; - justify-content: space-between; - align-items: center; - gap: 10px; + border-bottom: 2px solid var(--border-color); padding: 8px; - border-bottom: 2px solid #e0e0e0; } -.header-container { - height: 100%; +header nav { display: flex; align-items: center; + justify-content: space-between; + width: 100%; } -.header-container-right { - display: flex; - gap: 12px; -} - -.header-container-right a { - color: #ff8a15; - font-weight: 600; - - &:hover { - color: #ff9933; - } -} - -.header-container img { +header img { width: 40px; } -.app-container { - font-family: Rubik, sans-serif; +header ul { + display: flex; + gap: 12px; + list-style: none; + margin: 0; + padding: 0; } -.app-container * { - box-sizing: border-box; +header a { + color: var(--primary-color); + font-weight: 600; + text-decoration: none; } -.bold-400 { - font-variation-settings: "wght" 400; +header a:hover { + color: var(--primary-hover); } -.bold-500 { - font-variation-settings: "wght" 500; +/* Home page */ +#home-container { + align-items: center; + min-height: calc(100vh - 58px); + background: url("./assets/images/background.png") center/cover; } -.bold-600 { - font-variation-settings: "wght" 600; +.logos { + display: flex; + align-items: center; + gap: 20px; + padding-bottom: 80px; } -#home-container { - align-items: center; - min-height: calc(100vh - 58px); - background: url("./assets/images/background.png"); - background-size: cover; +.logos span { + font-size: 8rem; + font-weight: bold; } -.bold-700 { - font-variation-settings: "wght" 700; +.logos img { + height: 240px; + width: auto; } -.app-container .main-container { - box-shadow: 0px 0px 60px 0px rgba(0, 0, 0, 0.16); +/* Main content */ +.main-container { + box-shadow: 0 0 60px rgba(0, 0, 0, 0.16); width: min(635px, calc(100% - 24px)); border-radius: 16px; - margin-block-end: 159px; - background-color: #ffffff; + margin-bottom: 159px; + background-color: white; } -.main-container .success-title { - line-height: 1; - padding-block: 26px; - background-color: #e7ffed; - text-align: center; - color: #3eb655; +.success-title { display: flex; justify-content: center; - align-items: flex-end; + align-items: center; + padding: 26px; + background-color: var(--success-bg); + color: var(--success-color); font-size: 20px; + line-height: 1; } -.success-title img.success-icon { +.success-title img { margin-right: 8px; } -.main-container .inner-content { - padding-block: 48px; +.inner-content { + padding: 48px; text-align: center; display: flex; flex-direction: column; align-items: center; } -.inner-content #user-id { +/* User ID display */ +#user-id { position: relative; - padding: 14px 17px; - border-image-slice: 1; width: min(430px, calc(100% - 30px)); - margin-inline: auto; - margin-block: 11px 23px; + margin: 11px auto 23px; + padding: 14px 17px; border-radius: 9px; + font-family: Menlo, monospace; line-height: 1; - font-family: Menlo, serif; cursor: text; + border: 3px solid var(--primary-color); } -.inner-content #user-id:before { - content: ""; - position: absolute; - inset: 0; - border-radius: 9px; - padding: 2px; - background: linear-gradient(90.31deg, #ff9933 0.11%, #ff3f33 99.82%); - -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); - mask-composite: exclude; - -webkit-mask-composite: xor; -} - -.main-container > .top-band, -.main-container > .bottom-band { - border-radius: inherit; -} - -.main-container .top-band { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; -} - -.main-container .bottom-band { - border-top-left-radius: 0; - border-top-right-radius: 0; +/* Buttons and navigation */ +.buttons { + display: flex; + gap: 4px; } -.main-container .sessionButton { - box-sizing: border-box; - background: #ff9933; - border: 1px solid #ff8a15; - box-shadow: 0px 3px 6px rgba(255, 153, 51, 0.16); +.dashboard-button { + padding: 8px 13px; + background: var(--primary-color); + border: 1px solid var(--primary-hover); border-radius: 6px; + box-shadow: 0 3px 6px rgba(255, 153, 51, 0.16); + color: white; + font-weight: bold; font-size: 16px; -} - -.bottom-cta-container { - display: flex; - justify-content: flex-end; - padding-inline: 21px; - background-color: #212d4f; -} - -.bottom-cta-container .view-code { - padding-block: 11px; - color: #bac9f5; cursor: pointer; - font-size: 14px; -} - -.bottom-links-container { - display: grid; - grid-template-columns: repeat(4, auto); - margin-bottom: 22px; -} - -.bottom-links-container .link { - display: flex; - align-items: center; - margin-inline-end: 68px; - cursor: pointer; -} - -.bottom-links-container .link:last-child { - margin-right: 0; -} - -.truncate { - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; -} - -.separator-line { - max-width: 100%; + text-decoration: none; } -.link .link-icon { - width: 15px; - margin-right: 5px; +/* Footer */ +footer { + padding: 10px; } -@media screen and (max-width: 768px) { - .bottom-links-container { - grid-template-columns: repeat(2, 1fr); - column-gap: 64px; - row-gap: 34px; - } - - .bottom-links-container .link { - margin-inline-end: 0; - } - - .separator-line { - max-width: 200px; - } +footer a { + color: var(--primary-color); + font-weight: 600; + /* text-decoration: none; */ } -@media screen and (max-width: 480px) { - #home-container { - justify-content: start; - padding-block-start: 25px; - } - - .app-container .main-container { - margin-block-end: 90px; - } +footer a:hover { + color: var(--primary-hover); } diff --git a/boilerplate/frontend/react/src/App.tsx b/boilerplate/frontend/react/src/App.tsx index 0cd7c025..020ec272 100644 --- a/boilerplate/frontend/react/src/App.tsx +++ b/boilerplate/frontend/react/src/App.tsx @@ -1,45 +1,57 @@ import SuperTokens, { SuperTokensWrapper } from "supertokens-auth-react"; import { getSuperTokensRoutesForReactRouterDom } from "supertokens-auth-react/ui"; import { SessionAuth } from "supertokens-auth-react/recipe/session"; -import { BrowserRouter, Routes, Route } from "react-router-dom"; +import { BrowserRouter, Routes, Route, Link } from "react-router-dom"; import * as ReactRouter from "react-router-dom"; import Dashboard from "./Dashboard"; import { PreBuiltUIList, SuperTokensConfig, ComponentWrapper } from "./config"; import Home from "./Home"; -// Initialize SuperTokens - ideally in the global +// Initialize SuperTokens - ideally in the global scope SuperTokens.init(SuperTokensConfig); function App() { return ( -
-
- - SuperTokens - -
-
- - Docs - - - CLI Repo - -
-
- -
- -
+ +
+
+ +
+
+ } /> {/* This shows the login UI on "/auth" route */} {getSuperTokensRoutesForReactRouterDom(ReactRouter, PreBuiltUIList)} - {/* This protects the "/" route so that it shows - only if the user is logged in. - Else it redirects the user to "/auth" */} + {/* This protects the "/dashboard" route so that it shows + only if the user is logged in. + Else it redirects the user to "/auth" */} -
- -
- + + + separator +
+ +
); } diff --git a/boilerplate/frontend/react/src/Dashboard/Footer.tsx b/boilerplate/frontend/react/src/Dashboard/Footer.tsx deleted file mode 100644 index 63be3ecf..00000000 --- a/boilerplate/frontend/react/src/Dashboard/Footer.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { SeparatorLine } from "../assets/images"; -import { Link } from "./index"; - -interface FooterProps { - links: Link[]; -} - -export default function Footer({ links }: FooterProps) { - return ( - <> -
- {links.map((link) => ( -
- {link.name} -
- {link.name} -
-
- ))} -
- separator - - ); -} diff --git a/boilerplate/frontend/react/src/Dashboard/SessionInfo.tsx b/boilerplate/frontend/react/src/Dashboard/SessionInfo.tsx deleted file mode 100644 index 82f9e251..00000000 --- a/boilerplate/frontend/react/src/Dashboard/SessionInfo.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { getApiDomain } from "../config"; - -export default function SessionInfo() { - async function callAPIClicked() { - const response = await fetch(getApiDomain() + "/sessioninfo"); - const data = await response.json(); - window.alert("Session Information:\n" + JSON.stringify(data, null, 2)); - } - - return ( -
- Call API -
- ); -} diff --git a/boilerplate/frontend/react/src/Dashboard/index.tsx b/boilerplate/frontend/react/src/Dashboard/index.tsx index b86909ef..4f983b1c 100644 --- a/boilerplate/frontend/react/src/Dashboard/index.tsx +++ b/boilerplate/frontend/react/src/Dashboard/index.tsx @@ -1,67 +1,48 @@ -import { useSessionContext } from "supertokens-auth-react/recipe/session"; -import { signOut } from "supertokens-auth-react/recipe/session"; -import { recipeDetails } from "../config"; -import SessionInfo from "./SessionInfo"; -import { BlogsIcon, CelebrateIcon, GuideIcon, SignOutIcon } from "../assets/images"; +import { useSessionContext, signOut } from "supertokens-auth-react/recipe/session"; +import { getApiDomain } from "../config"; import { useNavigate } from "react-router-dom"; -import Footer from "./Footer"; - -export interface Link { - name: string; - onClick: () => void; - icon: string; -} export default function Dashboard() { - const sessionContext = useSessionContext(); const navigate = useNavigate(); + const sessionContext = useSessionContext(); - if (sessionContext.loading === true) { - return null; + async function callAPIClicked() { + try { + const response = await fetch(getApiDomain() + "/sessioninfo"); + const data = await response.json(); + window.alert("Session Information:\n" + JSON.stringify(data, null, 2)); + } catch (err) { + window.alert("Error calling API: " + err.message); + } } async function logoutClicked() { await signOut(); - navigate("/auth"); + navigate("/"); } - function openLink(url: string) { - window.open(url, "_blank"); - } - - const links: Link[] = [ - { - name: "Blogs", - onClick: () => openLink("https://supertokens.com/blog"), - icon: BlogsIcon, - }, - { - name: "Documentation", - onClick: () => openLink(recipeDetails.docsLink), - icon: GuideIcon, - }, - { - name: "Sign Out", - onClick: logoutClicked, - icon: SignOutIcon, - }, - ]; - return ( -
+ <>
- Login successful Login successful + Login successful + Login successful
Your userID is:
{sessionContext.userId}
- +
+ + +
-
+ ); } diff --git a/boilerplate/frontend/react/src/Home/CallAPIView.tsx b/boilerplate/frontend/react/src/Home/CallAPIView.tsx deleted file mode 100644 index f1f8f79e..00000000 --- a/boilerplate/frontend/react/src/Home/CallAPIView.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { getApiDomain } from "../config"; - -export default function CallAPIView() { - async function callAPIClicked() { - const response = await fetch(getApiDomain() + "/sessioninfo"); - const data = await response.json(); - window.alert("Session Information:\n" + JSON.stringify(data, null, 2)); - } - - return ( -
- Call API -
- ); -} diff --git a/boilerplate/frontend/react/src/Home/Home.css b/boilerplate/frontend/react/src/Home/Home.css deleted file mode 100644 index a056cb2e..00000000 --- a/boilerplate/frontend/react/src/Home/Home.css +++ /dev/null @@ -1,189 +0,0 @@ -@font-face { - font-family: Menlo; - src: url("../assets/fonts/MenloRegular.ttf"); -} - -.app-container { - font-family: Rubik, sans-serif; -} - -.app-container * { - box-sizing: border-box; -} - -.bold-400 { - font-variation-settings: "wght" 400; -} - -.bold-500 { - font-variation-settings: "wght" 500; -} - -.bold-600 { - font-variation-settings: "wght" 600; -} - -#home-container { - align-items: center; - min-height: 100vh; - background: url("../assets/images/background.png"); - background-size: cover; -} - -.bold-700 { - font-variation-settings: "wght" 700; -} - -.app-container .main-container { - box-shadow: 0px 0px 60px 0px rgba(0, 0, 0, 0.16); - width: min(635px, calc(100% - 24px)); - border-radius: 16px; - margin-block-end: 159px; - background-color: #ffffff; -} - -.main-container .success-title { - line-height: 1; - padding-block: 26px; - background-color: #e7ffed; - text-align: center; - color: #3eb655; - display: flex; - justify-content: center; - align-items: flex-end; - font-size: 20px; -} - -.success-title img.success-icon { - margin-right: 8px; -} - -.main-container .inner-content { - padding-block: 48px; - text-align: center; - display: flex; - flex-direction: column; - align-items: center; -} - -.inner-content #user-id { - position: relative; - padding: 14px 17px; - border-image-slice: 1; - width: min(430px, calc(100% - 30px)); - margin-inline: auto; - margin-block: 11px 23px; - border-radius: 9px; - line-height: 1; - font-family: Menlo, serif; - cursor: text; -} - -.inner-content #user-id:before { - content: ""; - position: absolute; - inset: 0; - border-radius: 9px; - padding: 2px; - background: linear-gradient(90.31deg, #ff9933 0.11%, #ff3f33 99.82%); - -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); - mask-composite: exclude; - -webkit-mask-composite: xor; -} - -.main-container > .top-band, -.main-container > .bottom-band { - border-radius: inherit; -} - -.main-container .top-band { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; -} - -.main-container .bottom-band { - border-top-left-radius: 0; - border-top-right-radius: 0; -} - -.main-container .sessionButton { - box-sizing: border-box; - background: #ff9933; - border: 1px solid #ff8a15; - box-shadow: 0px 3px 6px rgba(255, 153, 51, 0.16); - border-radius: 6px; - font-size: 16px; -} - -.bottom-cta-container { - display: flex; - justify-content: flex-end; - padding-inline: 21px; - background-color: #212d4f; -} - -.bottom-cta-container .view-code { - padding-block: 11px; - color: #bac9f5; - cursor: pointer; - font-size: 14px; -} - -.bottom-links-container { - display: grid; - grid-template-columns: repeat(4, auto); - margin-bottom: 22px; -} - -.bottom-links-container .link { - display: flex; - align-items: center; - margin-inline-end: 68px; - cursor: pointer; -} - -.bottom-links-container .link:last-child { - margin-right: 0; -} - -.truncate { - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; -} - -.separator-line { - max-width: 100%; -} - -.link .link-icon { - width: 15px; - margin-right: 5px; -} - -@media screen and (max-width: 768px) { - .bottom-links-container { - grid-template-columns: repeat(2, 1fr); - column-gap: 64px; - row-gap: 34px; - } - - .bottom-links-container .link { - margin-inline-end: 0; - } - - .separator-line { - max-width: 200px; - } -} - -@media screen and (max-width: 480px) { - #home-container { - justify-content: start; - padding-block-start: 25px; - } - - .app-container .main-container { - margin-block-end: 90px; - } -} diff --git a/boilerplate/frontend/react/src/Home/SuccessView.tsx b/boilerplate/frontend/react/src/Home/SuccessView.tsx deleted file mode 100644 index 7a888065..00000000 --- a/boilerplate/frontend/react/src/Home/SuccessView.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { useNavigate } from "react-router-dom"; -import { signOut } from "supertokens-auth-react/recipe/session"; -import { recipeDetails } from "../config"; -import CallAPIView from "./CallAPIView"; -import { BlogsIcon, CelebrateIcon, GuideIcon, SeparatorLine, SignOutIcon } from "../assets/images"; - -interface ILink { - name: string; - onClick: () => void; - icon: string; -} - -export default function SuccessView(props: { userId: string }) { - const userId = props.userId; - - const navigate = useNavigate(); - - async function logoutClicked() { - await signOut(); - navigate("/auth"); - } - - function openLink(url: string) { - window.open(url, "_blank"); - } - - const links: ILink[] = [ - { - name: "Blogs", - onClick: () => openLink("https://supertokens.com/blog"), - icon: BlogsIcon, - }, - { - name: "Documentation", - onClick: () => openLink(recipeDetails.docsLink), - icon: GuideIcon, - }, - { - name: "Sign Out", - onClick: logoutClicked, - icon: SignOutIcon, - }, - ]; - - return ( - <> -
-
- Login successful Login successful -
-
-
Your userID is:
-
- {userId} -
- -
-
-
- {links.map((link) => ( -
- {link.name} -
- {link.name} -
-
- ))} -
- separator - - ); -} diff --git a/boilerplate/frontend/react/src/Home/index.tsx b/boilerplate/frontend/react/src/Home/index.tsx index 5c8948cd..54fcd430 100644 --- a/boilerplate/frontend/react/src/Home/index.tsx +++ b/boilerplate/frontend/react/src/Home/index.tsx @@ -1,26 +1,47 @@ +import { Link } from "react-router-dom"; +import { useSessionContext } from "supertokens-auth-react/recipe/session"; + export default function Home() { + const session = useSessionContext(); + + if (session.loading) { + return null; + } + return ( -
-
+ <> +
SuperTokens x React -
-
+ +
-

+

SuperTokens x React
example project -

-

+
+ {session.doesSessionExist ? ( +

+ You're signed in already,
check out the Dashboard! 👇 +

+ ) : ( +

Sign-in to continue

+ )}
+
-
-
+ + ); } diff --git a/boilerplate/frontend/solid-prebuilt/README.md b/boilerplate/frontend/solid-prebuilt/README.md deleted file mode 100644 index 6a36d959..00000000 --- a/boilerplate/frontend/solid-prebuilt/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# SolidJS with SuperTokens Prebuilt UI - -This demo app demonstrates how to integrate SuperTokens into a SolidJS application. - -Features: - -- Initializes SuperTokens with frontend and backend configurations -- Creates a frontend page to handle authentication-related tasks -- Integrates the SuperTokens' pre-built login UI for secure user authentication -- Protects frontend to ensure only authenticated users can access the dashboard - -## Development server - -Run `npm run start` for a dev server. Navigate to `http://localhost:3000/`. The application will automatically reload if you change any of the source files. - -## Build - -Run `npm run build` to build the project. The build artifacts will be stored in the `dist/` directory. - -## Author - -Created with :heart: by the folks at supertokens.com. - -## License - -This project is licensed under the Apache 2.0 license. - -## Notes - -- To know more about how this app works and to learn how to customize it based on your use cases refer to the [SuperTokens Documentation](https://supertokens.com/docs/guides) -- We have provided development OAuth keys for the various built-in third party providers in the `/app/config/backend.ts` file. Feel free to use them for development purposes, but **please create your own keys for production use**. diff --git a/boilerplate/frontend/solid-prebuilt/config/emailpassword.ts b/boilerplate/frontend/solid-prebuilt/config/emailpassword.ts deleted file mode 100644 index ea5fdea2..00000000 --- a/boilerplate/frontend/solid-prebuilt/config/emailpassword.ts +++ /dev/null @@ -1,23 +0,0 @@ -import SuperTokens from "supertokens-web-js"; -import Session from "supertokens-web-js/recipe/session"; - -export function initSuperTokensUI() { - (window as any).supertokensUIInit("supertokensui", { - appInfo: { - websiteDomain: "http://localhost:3000", - apiDomain: "http://localhost:3001", - appName: "SuperTokens Demo App", - }, - recipeList: [(window as any).supertokensUIEmailPassword.init(), (window as any).supertokensUISession.init()], - }); -} - -export function initSuperTokensWebJS() { - SuperTokens.init({ - appInfo: { - appName: "SuperTokens Demo App", - apiDomain: "http://localhost:3001", - }, - recipeList: [Session.init()], - }); -} diff --git a/boilerplate/frontend/solid-prebuilt/config/multifactorauth.ts b/boilerplate/frontend/solid-prebuilt/config/multifactorauth.ts deleted file mode 100644 index e71163d6..00000000 --- a/boilerplate/frontend/solid-prebuilt/config/multifactorauth.ts +++ /dev/null @@ -1,48 +0,0 @@ -import SuperTokens from "supertokens-web-js"; -import Session from "supertokens-web-js/recipe/session"; -import EmailVerification from "supertokens-web-js/recipe/emailverification"; -import MultiFactorAuth from "supertokens-web-js/recipe/multifactorauth"; - -export function initSuperTokensUI() { - (window as any).supertokensUIInit("supertokensui", { - appInfo: { - websiteDomain: "http://localhost:3000", - apiDomain: "http://localhost:3001", - appName: "SuperTokens Demo App", - }, - recipeList: [ - (window as any).supertokensUIEmailPassword.init(), - (window as any).supertokensUIThirdParty.init({ - signInAndUpFeature: { - providers: [ - (window as any).supertokensUIThirdParty.Github.init(), - (window as any).supertokensUIThirdParty.Google.init(), - (window as any).supertokensUIThirdParty.Apple.init(), - (window as any).supertokensUIThirdParty.Twitter.init(), - ], - }, - }), - (window as any).supertokensUIPasswordless.init({ - contactMethod: "EMAIL_OR_PHONE", - }), - (window as any).supertokensUIEmailVerification.init({ - mode: "REQUIRED", - }), - (window as any).supertokensUIMultiFactorAuth.init({ - firstFactors: ["thirdparty", "emailpassword"], - }), - (window as any).supertokensUITOTP.init(), - (window as any).supertokensUISession.init(), - ], - }); -} - -export function initSuperTokensWebJS() { - SuperTokens.init({ - appInfo: { - appName: "SuperTokens Demo App", - apiDomain: "http://localhost:3001", - }, - recipeList: [Session.init(), EmailVerification.init(), MultiFactorAuth.init()], - }); -} diff --git a/boilerplate/frontend/solid-prebuilt/config/passwordless.ts b/boilerplate/frontend/solid-prebuilt/config/passwordless.ts deleted file mode 100644 index b8dee222..00000000 --- a/boilerplate/frontend/solid-prebuilt/config/passwordless.ts +++ /dev/null @@ -1,28 +0,0 @@ -import SuperTokens from "supertokens-web-js"; -import Session from "supertokens-web-js/recipe/session"; - -export function initSuperTokensUI() { - (window as any).supertokensUIInit("supertokensui", { - appInfo: { - websiteDomain: "http://localhost:3000", - apiDomain: "http://localhost:3001", - appName: "SuperTokens Demo App", - }, - recipeList: [ - (window as any).supertokensUIPasswordless.init({ - contactMethod: "EMAIL_OR_PHONE", - }), - (window as any).supertokensUISession.init(), - ], - }); -} - -export function initSuperTokensWebJS() { - SuperTokens.init({ - appInfo: { - appName: "SuperTokens Demo App", - apiDomain: "http://localhost:3001", - }, - recipeList: [Session.init()], - }); -} diff --git a/boilerplate/frontend/solid-prebuilt/config/thirdparty.ts b/boilerplate/frontend/solid-prebuilt/config/thirdparty.ts deleted file mode 100644 index e054a621..00000000 --- a/boilerplate/frontend/solid-prebuilt/config/thirdparty.ts +++ /dev/null @@ -1,35 +0,0 @@ -import SuperTokens from "supertokens-web-js"; -import Session from "supertokens-web-js/recipe/session"; - -export function initSuperTokensUI() { - (window as any).supertokensUIInit("supertokensui", { - appInfo: { - websiteDomain: "http://localhost:3000", - apiDomain: "http://localhost:3001", - appName: "SuperTokens Demo App", - }, - recipeList: [ - (window as any).supertokensUIThirdParty.init({ - signInAndUpFeature: { - providers: [ - (window as any).supertokensUIThirdParty.Github.init(), - (window as any).supertokensUIThirdParty.Google.init(), - (window as any).supertokensUIThirdParty.Apple.init(), - (window as any).supertokensUIThirdParty.Twitter.init(), - ], - }, - }), - (window as any).supertokensUISession.init(), - ], - }); -} - -export function initSuperTokensWebJS() { - SuperTokens.init({ - appInfo: { - appName: "SuperTokens Demo App", - apiDomain: "http://localhost:3001", - }, - recipeList: [Session.init()], - }); -} diff --git a/boilerplate/frontend/solid-prebuilt/config/thirdpartyemailpassword.ts b/boilerplate/frontend/solid-prebuilt/config/thirdpartyemailpassword.ts deleted file mode 100644 index 1879ee08..00000000 --- a/boilerplate/frontend/solid-prebuilt/config/thirdpartyemailpassword.ts +++ /dev/null @@ -1,36 +0,0 @@ -import SuperTokens from "supertokens-web-js"; -import Session from "supertokens-web-js/recipe/session"; - -export function initSuperTokensUI() { - (window as any).supertokensUIInit("supertokensui", { - appInfo: { - websiteDomain: "http://localhost:3000", - apiDomain: "http://localhost:3001", - appName: "SuperTokens Demo App", - }, - recipeList: [ - (window as any).supertokensUIEmailPassword.init(), - (window as any).supertokensUIThirdParty.init({ - signInAndUpFeature: { - providers: [ - (window as any).supertokensUIThirdParty.Github.init(), - (window as any).supertokensUIThirdParty.Google.init(), - (window as any).supertokensUIThirdParty.Apple.init(), - (window as any).supertokensUIThirdParty.Twitter.init(), - ], - }, - }), - (window as any).supertokensUISession.init(), - ], - }); -} - -export function initSuperTokensWebJS() { - SuperTokens.init({ - appInfo: { - appName: "SuperTokens Demo App", - apiDomain: "http://localhost:3001", - }, - recipeList: [Session.init()], - }); -} diff --git a/boilerplate/frontend/solid-prebuilt/config/thirdpartypasswordless.ts b/boilerplate/frontend/solid-prebuilt/config/thirdpartypasswordless.ts deleted file mode 100644 index 74d0331b..00000000 --- a/boilerplate/frontend/solid-prebuilt/config/thirdpartypasswordless.ts +++ /dev/null @@ -1,38 +0,0 @@ -import SuperTokens from "supertokens-web-js"; -import Session from "supertokens-web-js/recipe/session"; - -export function initSuperTokensUI() { - (window as any).supertokensUIInit("supertokensui", { - appInfo: { - websiteDomain: "http://localhost:3000", - apiDomain: "http://localhost:3001", - appName: "SuperTokens Demo App", - }, - recipeList: [ - (window as any).supertokensUIThirdParty.init({ - signInAndUpFeature: { - providers: [ - (window as any).supertokensUIThirdParty.Github.init(), - (window as any).supertokensUIThirdParty.Google.init(), - (window as any).supertokensUIThirdParty.Apple.init(), - (window as any).supertokensUIThirdParty.Twitter.init(), - ], - }, - }), - (window as any).supertokensUIPasswordless.init({ - contactMethod: "EMAIL_OR_PHONE", - }), - (window as any).supertokensUISession.init(), - ], - }); -} - -export function initSuperTokensWebJS() { - SuperTokens.init({ - appInfo: { - appName: "SuperTokens Demo App", - apiDomain: "http://localhost:3001", - }, - recipeList: [Session.init()], - }); -} diff --git a/boilerplate/frontend/solid-prebuilt/public/vite.svg b/boilerplate/frontend/solid-prebuilt/public/vite.svg deleted file mode 100644 index e7b8dfb1..00000000 --- a/boilerplate/frontend/solid-prebuilt/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/boilerplate/frontend/solid-prebuilt/src/App.css b/boilerplate/frontend/solid-prebuilt/src/App.css deleted file mode 100644 index 14fa4ff7..00000000 --- a/boilerplate/frontend/solid-prebuilt/src/App.css +++ /dev/null @@ -1,61 +0,0 @@ -header { - line-height: 1.5; - max-height: 100vh; -} - -.logo { - display: block; - margin: 0 auto 2rem; -} - -nav { - width: 100%; - font-size: 12px; - text-align: center; - margin-top: 2rem; -} - -nav a.router-link-exact-active { - color: var(--color-text); -} - -nav a.router-link-exact-active:hover { - background-color: transparent; -} - -nav a { - display: inline-block; - padding: 0 1rem; - border-left: 1px solid var(--color-border); -} - -nav a:first-of-type { - border: 0; -} - -@media (min-width: 1024px) { - header { - display: flex; - place-items: center; - padding-right: calc(var(--section-gap) / 2); - } - - .logo { - margin: 0 2rem 0 0; - } - - header .wrapper { - display: flex; - place-items: flex-start; - flex-wrap: wrap; - } - - nav { - text-align: left; - margin-left: -1rem; - font-size: 1rem; - - padding: 1rem 0; - margin-top: 1rem; - } -} diff --git a/boilerplate/frontend/solid-prebuilt/src/App.tsx b/boilerplate/frontend/solid-prebuilt/src/App.tsx deleted file mode 100644 index f79c9f93..00000000 --- a/boilerplate/frontend/solid-prebuilt/src/App.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { createSignal, onMount, Show } from "solid-js"; -import * as Session from "supertokens-web-js/recipe/session"; -import "./App.css"; - -async function getUserInfo() { - const session = await Session.doesSessionExist(); - if (session) { - const userId = await Session.getUserId(); - - return { userId, session }; - } - - return { userId: null, session: false }; -} - -async function signOut() { - await Session.signOut(); - window.location.reload(); -} - -function redirectToSignIn() { - window.location.href = "/auth"; -} - -function App() { - const [data, setData] = createSignal<{ - userId: string | null; - session: boolean; - }>({ - userId: null, - session: false, - }); - - onMount(async () => { - const { userId, session } = await getUserInfo(); - setData({ userId, session }); - }); - - return ( -
-
-

Hello!

- - -
- UserId: -

{data().userId}

- - -
-
- - -

- Visit the SuperTokens tutorial to learn how to build Auth - under a day. -

- -
-
-
- ); -} - -export default App; diff --git a/boilerplate/frontend/solid-prebuilt/src/config.ts b/boilerplate/frontend/solid-prebuilt/src/config.ts deleted file mode 100644 index 0f551511..00000000 --- a/boilerplate/frontend/solid-prebuilt/src/config.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function initSuperTokensUI() {} - -export function initSuperTokensWebJS() {} diff --git a/boilerplate/frontend/solid-prebuilt/src/index.css b/boilerplate/frontend/solid-prebuilt/src/index.css deleted file mode 100644 index 458671e6..00000000 --- a/boilerplate/frontend/solid-prebuilt/src/index.css +++ /dev/null @@ -1,69 +0,0 @@ -: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; - align-items: center; - justify-content: 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/boilerplate/frontend/solid-prebuilt/src/index.tsx b/boilerplate/frontend/solid-prebuilt/src/index.tsx deleted file mode 100644 index 2b4ded1c..00000000 --- a/boilerplate/frontend/solid-prebuilt/src/index.tsx +++ /dev/null @@ -1,21 +0,0 @@ -/* @refresh reload */ -import { render } from "solid-js/web"; -import { Router, Route } from "@solidjs/router"; -import "./index.css"; -import App from "./App"; -import { Auth } from "./Auth"; -import { initSuperTokensWebJS } from "./config"; - -initSuperTokensWebJS(); - -const root = document.getElementById("root"); - -render( - () => ( - - - - - ), - root! -); diff --git a/boilerplate/frontend/solid/README.md b/boilerplate/frontend/solid/README.md new file mode 100644 index 00000000..52a2efdc --- /dev/null +++ b/boilerplate/frontend/solid/README.md @@ -0,0 +1,111 @@ +# SuperTokens + SolidJS + +A demo implementation of [SuperTokens](https://supertokens.com/) with [SolidJS](https://www.solidjs.com/). Based on [Vite](https://vite.dev/). + +## General Info + +This project aims to demonstrate how to integrate SuperTokens into a SolidJS application. Its primary purpose is to serve as an educational tool, but it can also be used as a starting point for your own project. + +## Repo Structure + +### Source + +``` +📦src +┣ 📂assets +┃ ┣ 📂fonts +┃ ┃ ┗ 📜MenloRegular.ttf +┃ ┗ 📂images +┃ ┣ 📜arrow-right-icon.svg +┃ ┣ 📜background.png +┃ ┣ 📜blogs-icon.svg +┃ ┣ 📜celebrate-icon.svg +┃ ┣ 📜guide-icon.svg +┃ ┣ 📜separator-line.svg +┃ ┗ 📜signout-icon.svg +┣ 📂Dashboard --> Protected route component, only accessible to authenticated users +┃ ┗ 📜index.tsx +┣ 📂Home --> Public landing page component, accessible regardless of auth state +┃ ┗ 📜index.tsx +┣ 📜App.css +┣ 📜App.tsx --> Root component of the app +┣ 📜Auth.tsx --> Auth component for handling authentication routes +┣ 📜config.ts --> SuperTokens configuration +┣ 📜vite-env.d.ts +┗ 📜main.tsx --> Entry point of the app +``` + +### Config + +#### Vite + +Given that the project is a standard Vite project, everything available in the [Vite configuration docs](https://vite.dev/config/) is available to use here (refer to the `vite.config.ts` file). The only customization we've done is changing the port to `3000`. + +#### SuperTokens + +The full configuration needed for SuperTokens (the frontend part) to work is in the `src/config.ts` file. This file will differ based on the [auth recipe](https://supertokens.com/docs/guides) you choose. + +If you choose to use this as a starting point for your own project, you can further customize the options and config in the `src/config.ts` file. Refer to our [docs](https://supertokens.com/docs) (and make sure to choose the correct recipe) for more details. + +## Application Flow + +The application uses [@solidjs/router](https://github.com/solidjs/solid-router) for routing and consists of five main parts: + +1. **Entry Point (`main.tsx`)** + + - Initializes the SolidJS application + - Renders the main `App` component + +2. **Root Component (`App.tsx`)** + + - Initializes SuperTokens with the provided configuration (generated by the CLI) + - Sets up the routing structure using `@solidjs/router` + - Wraps the application with necessary providers + - Defines three main routes: + - `/`: Public landing page - accessible regardless of auth state + - `/auth`: Renders SuperTokens' pre-built auth UI - accessible regardless of auth state + - `/dashboard`: Protected route requiring authentication + +3. **Auth Component (`Auth.tsx`)** + + - Handles the authentication routes and UI + - Integrates with SuperTokens' pre-built auth UI + - Manages the authentication flow + +4. **Home Component (`/` route, `/Home/index.tsx` component)** + + - Public landing page accessible to all users + - Provides navigation to authentication and dashboard + - Serves as the entry point for new users + +5. **Dashboard Component (`/dashboard` route, `/Dashboard/index.tsx` component)** + - Protected route only accessible to authenticated users + - Protected by session verification + - Displays user information and provides authenticated functionality + +When a user visits the application, they start at the home page (`/`). They can choose to authenticate through the `/auth` route, and once authenticated, they gain access to the protected dashboard. The session state is managed throughout the application using SuperTokens' session management. + +## Customizations + +If you want to customize the default auth UI, you have two options: + +1. Refer to the [docs](https://supertokens.com/docs/thirdpartyemailpassword/advanced-customizations/react-component-override/usage) on how to customize the pre-built UI. +2. Roll your own UI by choosing "Custom UI" in the right sidebar in the [docs](https://supertokens.com/docs/thirdpartyemailpassword/quickstart/frontend-setup). + +## Additional resources + +- Custom UI Example: https://github.com/supertokens/supertokens-web-js/tree/master/examples/solidjs/with-thirdpartyemailpassword +- Custom UI Blog post: https://supertokens.medium.com/adding-social-login-to-your-website-with-supertokens-custom-ui-only-5fa4d7ab6402 +- Awesome SuperTokens: https://github.com/kohasummons/awesome-supertokens + +## Contributing + +Please refer to the [CONTRIBUTING.md](https://github.com/supertokens/create-supertokens-app/blob/master/CONTRIBUTING.md) file in the root of the [`create-supertokens-app`](https://github.com/supertokens/create-supertokens-app) repo. + +## Contact us + +For any questions, or support requests, please email us at team@supertokens.io, or join our [Discord](https://supertokens.io/discord) server. + +## Authors + +Created with :heart: by the folks at SuperTokens.io. diff --git a/boilerplate/frontend/solid-prebuilt/config/all_auth.ts b/boilerplate/frontend/solid/config/all_auth.ts similarity index 50% rename from boilerplate/frontend/solid-prebuilt/config/all_auth.ts rename to boilerplate/frontend/solid/config/all_auth.ts index 6849c728..d0389c95 100644 --- a/boilerplate/frontend/solid-prebuilt/config/all_auth.ts +++ b/boilerplate/frontend/solid/config/all_auth.ts @@ -1,13 +1,42 @@ import SuperTokens from "supertokens-web-js"; import Session from "supertokens-web-js/recipe/session"; +export function getApiDomain() { + const apiPort = import.meta.env.VITE_API_PORT || 3001; + const apiUrl = import.meta.env.VITE_API_URL || `http://localhost:${apiPort}`; + return apiUrl; +} + +export function getWebsiteDomain() { + const websitePort = import.meta.env.VITE_WEBSITE_PORT || 3000; + const websiteUrl = import.meta.env.VITE_WEBSITE_URL || `http://localhost:${websitePort}`; + return websiteUrl; +} + +export const SuperTokensConfig = { + appInfo: { + appName: "SuperTokens Demo App", + apiDomain: getApiDomain(), + websiteDomain: getWebsiteDomain(), + apiBasePath: "/auth", + websiteBasePath: "/auth", + }, + recipeList: [Session.init()], + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, +}; + +export const recipeDetails = { + docsLink: "https://supertokens.com/docs/guides", +}; + export function initSuperTokensUI() { (window as any).supertokensUIInit("supertokensui", { - appInfo: { - websiteDomain: "http://localhost:3000", - apiDomain: "http://localhost:3001", - appName: "SuperTokens Demo App", - }, + ...SuperTokensConfig, recipeList: [ (window as any).supertokensUIEmailPassword.init(), (window as any).supertokensUIThirdParty.init({ @@ -29,11 +58,5 @@ export function initSuperTokensUI() { } export function initSuperTokensWebJS() { - SuperTokens.init({ - appInfo: { - appName: "SuperTokens Demo App", - apiDomain: "http://localhost:3001", - }, - recipeList: [Session.init()], - }); + SuperTokens.init(SuperTokensConfig); } diff --git a/boilerplate/frontend/solid/config/emailpassword.ts b/boilerplate/frontend/solid/config/emailpassword.ts new file mode 100644 index 00000000..98b7330d --- /dev/null +++ b/boilerplate/frontend/solid/config/emailpassword.ts @@ -0,0 +1,46 @@ +import SuperTokens from "supertokens-web-js"; +import Session from "supertokens-web-js/recipe/session"; + +export function getApiDomain() { + const apiPort = import.meta.env.VITE_API_PORT || 3001; + const apiUrl = import.meta.env.VITE_API_URL || `http://localhost:${apiPort}`; + return apiUrl; +} + +export function getWebsiteDomain() { + const websitePort = import.meta.env.VITE_WEBSITE_PORT || 3000; + const websiteUrl = import.meta.env.VITE_WEBSITE_URL || `http://localhost:${websitePort}`; + return websiteUrl; +} + +export const SuperTokensConfig = { + appInfo: { + appName: "SuperTokens Demo App", + apiDomain: getApiDomain(), + websiteDomain: getWebsiteDomain(), + apiBasePath: "/auth", + websiteBasePath: "/auth", + }, + recipeList: [Session.init()], + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, +}; + +export const recipeDetails = { + docsLink: "https://supertokens.com/docs/emailpassword/introduction", +}; + +export function initSuperTokensUI() { + (window as any).supertokensUIInit("supertokensui", { + ...SuperTokensConfig, + recipeList: [(window as any).supertokensUIEmailPassword.init(), (window as any).supertokensUISession.init()], + }); +} + +export function initSuperTokensWebJS() { + SuperTokens.init(SuperTokensConfig); +} diff --git a/boilerplate/frontend/solid/config/multifactorauth.ts b/boilerplate/frontend/solid/config/multifactorauth.ts new file mode 100644 index 00000000..24ea6efc --- /dev/null +++ b/boilerplate/frontend/solid/config/multifactorauth.ts @@ -0,0 +1,62 @@ +import SuperTokens from "supertokens-web-js"; +import Session from "supertokens-web-js/recipe/session"; +import EmailVerification from "supertokens-web-js/recipe/emailverification"; +import MultiFactorAuth from "supertokens-web-js/recipe/multifactorauth"; + +export function getApiDomain() { + const apiPort = import.meta.env.VITE_API_PORT || 3001; + const apiUrl = import.meta.env.VITE_API_URL || `http://localhost:${apiPort}`; + return apiUrl; +} + +export function getWebsiteDomain() { + const websitePort = import.meta.env.VITE_WEBSITE_PORT || 3000; + const websiteUrl = import.meta.env.VITE_WEBSITE_URL || `http://localhost:${websitePort}`; + return websiteUrl; +} + +export const SuperTokensConfig = { + appInfo: { + appName: "SuperTokens Demo App", + apiDomain: getApiDomain(), + websiteDomain: getWebsiteDomain(), + apiBasePath: "/auth", + websiteBasePath: "/auth", + }, + recipeList: [Session.init(), EmailVerification.init(), MultiFactorAuth.init()], + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, +}; + +export const recipeDetails = { + docsLink: "https://supertokens.com/docs/mfa/introduction", +}; + +export function initSuperTokensUI() { + (window as any).supertokensUIInit("supertokensui", { + ...SuperTokensConfig, + recipeList: [ + (window as any).supertokensUIEmailPassword.init(), + (window as any).supertokensUIThirdParty.init({ + signInAndUpFeature: { + providers: [ + (window as any).supertokensUIThirdParty.Github.init(), + (window as any).supertokensUIThirdParty.Google.init(), + (window as any).supertokensUIThirdParty.Apple.init(), + (window as any).supertokensUIThirdParty.Twitter.init(), + ], + }, + }), + (window as any).supertokensUIPasswordless.init({ contactMethod: "EMAIL_OR_PHONE" }), + (window as any).supertokensUISession.init(), + ], + }); +} + +export function initSuperTokensWebJS() { + SuperTokens.init(SuperTokensConfig); +} diff --git a/boilerplate/frontend/solid/config/passwordless.ts b/boilerplate/frontend/solid/config/passwordless.ts new file mode 100644 index 00000000..7a0f7029 --- /dev/null +++ b/boilerplate/frontend/solid/config/passwordless.ts @@ -0,0 +1,46 @@ +import SuperTokens from "supertokens-web-js"; +import Session from "supertokens-web-js/recipe/session"; + +export function getApiDomain() { + const apiPort = import.meta.env.VITE_API_PORT || 3001; + const apiUrl = import.meta.env.VITE_API_URL || `http://localhost:${apiPort}`; + return apiUrl; +} + +export function getWebsiteDomain() { + const websitePort = import.meta.env.VITE_WEBSITE_PORT || 3000; + const websiteUrl = import.meta.env.VITE_WEBSITE_URL || `http://localhost:${websitePort}`; + return websiteUrl; +} + +export const SuperTokensConfig = { + appInfo: { + appName: "SuperTokens Demo App", + apiDomain: getApiDomain(), + websiteDomain: getWebsiteDomain(), + apiBasePath: "/auth", + websiteBasePath: "/auth", + }, + recipeList: [Session.init()], + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, +}; + +export const recipeDetails = { + docsLink: "https://supertokens.com/docs/passwordless/introduction", +}; + +export function initSuperTokensUI() { + (window as any).supertokensUIInit("supertokensui", { + ...SuperTokensConfig, + recipeList: [(window as any).supertokensUIPasswordless.init(), (window as any).supertokensUISession.init()], + }); +} + +export function initSuperTokensWebJS() { + SuperTokens.init(SuperTokensConfig); +} diff --git a/boilerplate/frontend/solid/config/thirdparty.ts b/boilerplate/frontend/solid/config/thirdparty.ts new file mode 100644 index 00000000..31a4d328 --- /dev/null +++ b/boilerplate/frontend/solid/config/thirdparty.ts @@ -0,0 +1,58 @@ +import SuperTokens from "supertokens-web-js"; +import Session from "supertokens-web-js/recipe/session"; + +export function getApiDomain() { + const apiPort = import.meta.env.VITE_API_PORT || 3001; + const apiUrl = import.meta.env.VITE_API_URL || `http://localhost:${apiPort}`; + return apiUrl; +} + +export function getWebsiteDomain() { + const websitePort = import.meta.env.VITE_WEBSITE_PORT || 3000; + const websiteUrl = import.meta.env.VITE_WEBSITE_URL || `http://localhost:${websitePort}`; + return websiteUrl; +} + +export const SuperTokensConfig = { + appInfo: { + appName: "SuperTokens Demo App", + apiDomain: getApiDomain(), + websiteDomain: getWebsiteDomain(), + apiBasePath: "/auth", + websiteBasePath: "/auth", + }, + recipeList: [Session.init()], + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, +}; + +export const recipeDetails = { + docsLink: "https://supertokens.com/docs/thirdparty/introduction", +}; + +export function initSuperTokensUI() { + (window as any).supertokensUIInit("supertokensui", { + ...SuperTokensConfig, + recipeList: [ + (window as any).supertokensUIThirdParty.init({ + signInAndUpFeature: { + providers: [ + (window as any).supertokensUIThirdParty.Github.init(), + (window as any).supertokensUIThirdParty.Google.init(), + (window as any).supertokensUIThirdParty.Apple.init(), + (window as any).supertokensUIThirdParty.Twitter.init(), + ], + }, + }), + (window as any).supertokensUISession.init(), + ], + }); +} + +export function initSuperTokensWebJS() { + SuperTokens.init(SuperTokensConfig); +} diff --git a/boilerplate/frontend/solid/config/thirdpartyemailpassword.ts b/boilerplate/frontend/solid/config/thirdpartyemailpassword.ts new file mode 100644 index 00000000..c3000064 --- /dev/null +++ b/boilerplate/frontend/solid/config/thirdpartyemailpassword.ts @@ -0,0 +1,59 @@ +import SuperTokens from "supertokens-web-js"; +import Session from "supertokens-web-js/recipe/session"; + +export function getApiDomain() { + const apiPort = import.meta.env.VITE_API_PORT || 3001; + const apiUrl = import.meta.env.VITE_API_URL || `http://localhost:${apiPort}`; + return apiUrl; +} + +export function getWebsiteDomain() { + const websitePort = import.meta.env.VITE_WEBSITE_PORT || 3000; + const websiteUrl = import.meta.env.VITE_WEBSITE_URL || `http://localhost:${websitePort}`; + return websiteUrl; +} + +export const SuperTokensConfig = { + appInfo: { + appName: "SuperTokens Demo App", + apiDomain: getApiDomain(), + websiteDomain: getWebsiteDomain(), + apiBasePath: "/auth", + websiteBasePath: "/auth", + }, + recipeList: [Session.init()], + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, +}; + +export const recipeDetails = { + docsLink: "https://supertokens.com/docs/thirdpartyemailpassword/introduction", +}; + +export function initSuperTokensUI() { + (window as any).supertokensUIInit("supertokensui", { + ...SuperTokensConfig, + recipeList: [ + (window as any).supertokensUIEmailPassword.init(), + (window as any).supertokensUIThirdParty.init({ + signInAndUpFeature: { + providers: [ + (window as any).supertokensUIThirdParty.Github.init(), + (window as any).supertokensUIThirdParty.Google.init(), + (window as any).supertokensUIThirdParty.Apple.init(), + (window as any).supertokensUIThirdParty.Twitter.init(), + ], + }, + }), + (window as any).supertokensUISession.init(), + ], + }); +} + +export function initSuperTokensWebJS() { + SuperTokens.init(SuperTokensConfig); +} diff --git a/boilerplate/frontend/solid/config/thirdpartypasswordless.ts b/boilerplate/frontend/solid/config/thirdpartypasswordless.ts new file mode 100644 index 00000000..2b691161 --- /dev/null +++ b/boilerplate/frontend/solid/config/thirdpartypasswordless.ts @@ -0,0 +1,61 @@ +import SuperTokens from "supertokens-web-js"; +import Session from "supertokens-web-js/recipe/session"; + +export function getApiDomain() { + const apiPort = import.meta.env.VITE_API_PORT || 3001; + const apiUrl = import.meta.env.VITE_API_URL || `http://localhost:${apiPort}`; + return apiUrl; +} + +export function getWebsiteDomain() { + const websitePort = import.meta.env.VITE_WEBSITE_PORT || 3000; + const websiteUrl = import.meta.env.VITE_WEBSITE_URL || `http://localhost:${websitePort}`; + return websiteUrl; +} + +export const SuperTokensConfig = { + appInfo: { + appName: "SuperTokens Demo App", + apiDomain: getApiDomain(), + websiteDomain: getWebsiteDomain(), + apiBasePath: "/auth", + websiteBasePath: "/auth", + }, + recipeList: [Session.init()], + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, +}; + +export const recipeDetails = { + docsLink: "https://supertokens.com/docs/thirdpartypasswordless/introduction", +}; + +export function initSuperTokensUI() { + (window as any).supertokensUIInit("supertokensui", { + ...SuperTokensConfig, + recipeList: [ + (window as any).supertokensUIThirdParty.init({ + signInAndUpFeature: { + providers: [ + (window as any).supertokensUIThirdParty.Github.init(), + (window as any).supertokensUIThirdParty.Google.init(), + (window as any).supertokensUIThirdParty.Apple.init(), + (window as any).supertokensUIThirdParty.Twitter.init(), + ], + }, + }), + (window as any).supertokensUIPasswordless.init({ + contactMethod: "EMAIL_OR_PHONE", + }), + (window as any).supertokensUISession.init(), + ], + }); +} + +export function initSuperTokensWebJS() { + SuperTokens.init(SuperTokensConfig); +} diff --git a/boilerplate/frontend/solid-prebuilt/index.html b/boilerplate/frontend/solid/index.html similarity index 57% rename from boilerplate/frontend/solid-prebuilt/index.html rename to boilerplate/frontend/solid/index.html index d29a34b4..419bd50d 100644 --- a/boilerplate/frontend/solid-prebuilt/index.html +++ b/boilerplate/frontend/solid/index.html @@ -2,12 +2,12 @@ - + - Vite + Solid + TS + SuperTokens + Solid
- + diff --git a/boilerplate/frontend/solid-prebuilt/package.json b/boilerplate/frontend/solid/package.json similarity index 54% rename from boilerplate/frontend/solid-prebuilt/package.json rename to boilerplate/frontend/solid/package.json index 1a8f5bc9..39b6c52b 100644 --- a/boilerplate/frontend/solid-prebuilt/package.json +++ b/boilerplate/frontend/solid/package.json @@ -9,13 +9,14 @@ "preview": "vite preview" }, "dependencies": { - "@solidjs/router": "^0.14.7", - "solid-js": "^1.8.20", - "supertokens-web-js": "^0.13.0" + "@solidjs/router": "^0.10.1", + "solid-js": "^1.8.7", + "supertokens-web-js": "^0.8.0" }, "devDependencies": { - "typescript": "^5.5.3", - "vite": "^5.4.1", - "vite-plugin-solid": "^2.10.2" + "typescript": "^5.3.3", + "vite": "^5.0.0", + "vite-plugin-solid": "^2.8.0", + "@types/node": "^20.10.4" } } diff --git a/boilerplate/frontend/solid/public/ST.svg b/boilerplate/frontend/solid/public/ST.svg new file mode 100644 index 00000000..30b435bd --- /dev/null +++ b/boilerplate/frontend/solid/public/ST.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/boilerplate/frontend/solid/public/assets/fonts/MenloRegular.ttf b/boilerplate/frontend/solid/public/assets/fonts/MenloRegular.ttf new file mode 100644 index 00000000..033dc6d2 Binary files /dev/null and b/boilerplate/frontend/solid/public/assets/fonts/MenloRegular.ttf differ diff --git a/boilerplate/frontend/solid/public/assets/images/arrow-right-icon.svg b/boilerplate/frontend/solid/public/assets/images/arrow-right-icon.svg new file mode 100644 index 00000000..95aa1fec --- /dev/null +++ b/boilerplate/frontend/solid/public/assets/images/arrow-right-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/boilerplate/frontend/solid/public/assets/images/background.png b/boilerplate/frontend/solid/public/assets/images/background.png new file mode 100644 index 00000000..2147c15c Binary files /dev/null and b/boilerplate/frontend/solid/public/assets/images/background.png differ diff --git a/boilerplate/frontend/solid/public/assets/images/blogs-icon.svg b/boilerplate/frontend/solid/public/assets/images/blogs-icon.svg new file mode 100644 index 00000000..a2fc9dd6 --- /dev/null +++ b/boilerplate/frontend/solid/public/assets/images/blogs-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/boilerplate/frontend/solid/public/assets/images/celebrate-icon.svg b/boilerplate/frontend/solid/public/assets/images/celebrate-icon.svg new file mode 100644 index 00000000..3b40b1ef --- /dev/null +++ b/boilerplate/frontend/solid/public/assets/images/celebrate-icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/boilerplate/frontend/solid/public/assets/images/guide-icon.svg b/boilerplate/frontend/solid/public/assets/images/guide-icon.svg new file mode 100644 index 00000000..bd85af72 --- /dev/null +++ b/boilerplate/frontend/solid/public/assets/images/guide-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/boilerplate/frontend/solid/public/assets/images/index.ts b/boilerplate/frontend/solid/public/assets/images/index.ts new file mode 100644 index 00000000..7adf036c --- /dev/null +++ b/boilerplate/frontend/solid/public/assets/images/index.ts @@ -0,0 +1,8 @@ +import SeparatorLine from "./separator-line.svg"; +import ArrowRight from "./arrow-right-icon.svg"; +import SignOutIcon from "./sign-out-icon.svg"; +import GuideIcon from "./guide-icon.svg"; +import BlogsIcon from "./blogs-icon.svg"; +import CelebrateIcon from "./celebrate-icon.svg"; + +export { SeparatorLine, ArrowRight, SignOutIcon, GuideIcon, BlogsIcon, CelebrateIcon }; diff --git a/boilerplate/frontend/solid/public/assets/images/separator-line.svg b/boilerplate/frontend/solid/public/assets/images/separator-line.svg new file mode 100644 index 00000000..7127a00d --- /dev/null +++ b/boilerplate/frontend/solid/public/assets/images/separator-line.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/boilerplate/frontend/solid/public/assets/images/sign-out-icon.svg b/boilerplate/frontend/solid/public/assets/images/sign-out-icon.svg new file mode 100644 index 00000000..6cc4f85f --- /dev/null +++ b/boilerplate/frontend/solid/public/assets/images/sign-out-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/boilerplate/frontend/solid/public/favicon.ico b/boilerplate/frontend/solid/public/favicon.ico new file mode 100644 index 00000000..a11777cc Binary files /dev/null and b/boilerplate/frontend/solid/public/favicon.ico differ diff --git a/boilerplate/frontend/solid/public/index.html b/boilerplate/frontend/solid/public/index.html new file mode 100644 index 00000000..6f1f7cb5 --- /dev/null +++ b/boilerplate/frontend/solid/public/index.html @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + React App + + + +
+ + diff --git a/boilerplate/frontend/solid/public/manifest.json b/boilerplate/frontend/solid/public/manifest.json new file mode 100644 index 00000000..f01493ff --- /dev/null +++ b/boilerplate/frontend/solid/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/boilerplate/frontend/solid/public/robots.txt b/boilerplate/frontend/solid/public/robots.txt new file mode 100644 index 00000000..e9e57dc4 --- /dev/null +++ b/boilerplate/frontend/solid/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/boilerplate/frontend/solid-prebuilt/src/assets/solid.svg b/boilerplate/frontend/solid/public/solid.svg similarity index 100% rename from boilerplate/frontend/solid-prebuilt/src/assets/solid.svg rename to boilerplate/frontend/solid/public/solid.svg diff --git a/boilerplate/frontend/solid/src/App.css b/boilerplate/frontend/solid/src/App.css new file mode 100644 index 00000000..d73bc9c1 --- /dev/null +++ b/boilerplate/frontend/solid/src/App.css @@ -0,0 +1,179 @@ +/* Base styles */ +:root { + --primary-color: #ff9933; + --primary-hover: #ff8a15; + --success-color: #3eb655; + --success-bg: #e7ffed; + --border-color: #e0e0e0; +} + +@font-face { + font-family: Menlo; + src: url("./assets/fonts/MenloRegular.ttf"); +} + +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", + "Droid Sans", "Helvetica Neue", sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +* { + box-sizing: border-box; +} + +/* Layout */ +.app-container { + display: flex; + flex-direction: column; + width: 100%; + min-height: 100vh; + font-family: Rubik, sans-serif; +} + +.fill { + display: flex; + flex-direction: column; + justify-content: center; + flex: 1; +} + +/* Header */ +header { + border-bottom: 2px solid var(--border-color); + padding: 8px; +} + +header nav { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; +} + +header img { + width: 40px; +} + +header ul { + display: flex; + gap: 12px; + list-style: none; + margin: 0; + padding: 0; +} + +header a { + color: var(--primary-color); + font-weight: 600; + text-decoration: none; +} + +header a:hover { + color: var(--primary-hover); +} + +/* Home page */ +#home-container { + align-items: center; + min-height: calc(100vh - 58px); + background: url("./assets/images/background.png") center/cover; +} + +.logos { + display: flex; + align-items: center; + gap: 20px; + padding-bottom: 80px; +} + +.logos span { + font-size: 8rem; + font-weight: bold; +} + +.logos img { + height: 240px; + width: auto; +} + +/* Main content */ +.main-container { + box-shadow: 0 0 60px rgba(0, 0, 0, 0.16); + width: min(635px, calc(100% - 24px)); + border-radius: 16px; + margin-bottom: 159px; + background-color: white; +} + +.success-title { + display: flex; + justify-content: center; + align-items: center; + padding: 26px; + background-color: var(--success-bg); + color: var(--success-color); + font-size: 20px; + line-height: 1; +} + +.success-title img { + margin-right: 8px; +} + +.inner-content { + padding: 48px; + text-align: center; + display: flex; + flex-direction: column; + align-items: center; +} + +/* User ID display */ +#user-id { + position: relative; + width: min(430px, calc(100% - 30px)); + margin: 11px auto 23px; + padding: 14px 17px; + border-radius: 9px; + font-family: Menlo, monospace; + line-height: 1; + cursor: text; + border: 3px solid var(--primary-color); +} + +/* Buttons and navigation */ +.buttons { + display: flex; + gap: 4px; +} + +.dashboard-button { + padding: 8px 13px; + background: var(--primary-color); + border: 1px solid var(--primary-hover); + border-radius: 6px; + box-shadow: 0 3px 6px rgba(255, 153, 51, 0.16); + color: white; + font-weight: bold; + font-size: 16px; + cursor: pointer; + text-decoration: none; +} + +/* Footer */ +footer { + padding: 10px; +} + +footer a { + color: var(--primary-color); + font-weight: 600; + /* text-decoration: none; */ +} + +footer a:hover { + color: var(--primary-hover); +} diff --git a/boilerplate/frontend/solid/src/App.tsx b/boilerplate/frontend/solid/src/App.tsx new file mode 100644 index 00000000..278f6fa1 --- /dev/null +++ b/boilerplate/frontend/solid/src/App.tsx @@ -0,0 +1,74 @@ +import { Component, onMount } from "solid-js"; +import { Router, Route, useNavigate } from "@solidjs/router"; +import { SuperTokensConfig, initSuperTokensWebJS } from "./config"; +import Home from "./Home"; +import Dashboard from "./Dashboard"; +import { Auth } from "./Auth"; +import * as Session from "supertokens-web-js/recipe/session"; +import "./App.css"; + +// Initialize SuperTokens +initSuperTokensWebJS(); + +const ProtectedRoute: Component<{ component: Component }> = (props) => { + const navigate = useNavigate(); + + onMount(async () => { + if (!(await Session.doesSessionExist())) { + navigate("/auth"); + } + }); + + return ; +}; + +const App: Component = () => { + return ( +
+
+ +
+
+ + + + } /> + + + separator +
+
+ ); +}; + +export default App; diff --git a/boilerplate/frontend/solid-prebuilt/src/Auth.tsx b/boilerplate/frontend/solid/src/Auth.tsx similarity index 100% rename from boilerplate/frontend/solid-prebuilt/src/Auth.tsx rename to boilerplate/frontend/solid/src/Auth.tsx diff --git a/boilerplate/frontend/solid/src/Dashboard/index.tsx b/boilerplate/frontend/solid/src/Dashboard/index.tsx new file mode 100644 index 00000000..e5f9b5a4 --- /dev/null +++ b/boilerplate/frontend/solid/src/Dashboard/index.tsx @@ -0,0 +1,60 @@ +import { Component, createSignal, onMount } from "solid-js"; +import { useNavigate } from "@solidjs/router"; +import * as Session from "supertokens-web-js/recipe/session"; +import { getApiDomain } from "../config"; + +const Dashboard: Component = () => { + const navigate = useNavigate(); + const [userId, setUserId] = createSignal(null); + + onMount(async () => { + try { + const id = await Session.getUserId(); + setUserId(id); + } catch (err) { + navigate("/auth"); + } + }); + + async function callAPIClicked() { + try { + const response = await fetch(getApiDomain() + "/sessioninfo"); + const data = await response.json(); + window.alert("Session Information:\n" + JSON.stringify(data, null, 2)); + } catch (err) { + window.alert("Error calling API: " + err.message); + } + } + + async function logoutClicked() { + await Session.signOut(); + navigate("/"); + } + + return ( + <> +
+
+ Login successful + Login successful +
+
+
Your userID is:
+
+ {userId()} +
+
+ + +
+
+
+ + ); +}; + +export default Dashboard; diff --git a/boilerplate/frontend/solid/src/Home/index.tsx b/boilerplate/frontend/solid/src/Home/index.tsx new file mode 100644 index 00000000..1f34d572 --- /dev/null +++ b/boilerplate/frontend/solid/src/Home/index.tsx @@ -0,0 +1,49 @@ +import { Component, createSignal, onMount, Show } from "solid-js"; +import Session from "supertokens-web-js/recipe/session"; + +const Home: Component = () => { + const [sessionExists, setSessionExists] = createSignal(false); + + onMount(async () => { + setSessionExists(await Session.doesSessionExist()); + }); + + return ( + <> +
+ SuperTokens + x + Solid +
+
+
+

+ SuperTokens x Solid
example project +

+
+ {sessionExists() ? ( +

+ You're signed in already,
check out the Dashboard! 👇 +

+ ) : ( +

Sign-in to continue

+ )} +
+ +
+
+ + ); +}; + +export default Home; diff --git a/boilerplate/frontend/solid/src/assets/fonts/MenloRegular.ttf b/boilerplate/frontend/solid/src/assets/fonts/MenloRegular.ttf new file mode 100644 index 00000000..033dc6d2 Binary files /dev/null and b/boilerplate/frontend/solid/src/assets/fonts/MenloRegular.ttf differ diff --git a/boilerplate/frontend/solid/src/assets/images/arrow-right-icon.svg b/boilerplate/frontend/solid/src/assets/images/arrow-right-icon.svg new file mode 100644 index 00000000..95aa1fec --- /dev/null +++ b/boilerplate/frontend/solid/src/assets/images/arrow-right-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/boilerplate/frontend/solid/src/assets/images/background.png b/boilerplate/frontend/solid/src/assets/images/background.png new file mode 100644 index 00000000..2147c15c Binary files /dev/null and b/boilerplate/frontend/solid/src/assets/images/background.png differ diff --git a/boilerplate/frontend/solid/src/assets/images/blogs-icon.svg b/boilerplate/frontend/solid/src/assets/images/blogs-icon.svg new file mode 100644 index 00000000..a2fc9dd6 --- /dev/null +++ b/boilerplate/frontend/solid/src/assets/images/blogs-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/boilerplate/frontend/solid/src/assets/images/celebrate-icon.svg b/boilerplate/frontend/solid/src/assets/images/celebrate-icon.svg new file mode 100644 index 00000000..3b40b1ef --- /dev/null +++ b/boilerplate/frontend/solid/src/assets/images/celebrate-icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/boilerplate/frontend/solid/src/assets/images/guide-icon.svg b/boilerplate/frontend/solid/src/assets/images/guide-icon.svg new file mode 100644 index 00000000..bd85af72 --- /dev/null +++ b/boilerplate/frontend/solid/src/assets/images/guide-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/boilerplate/frontend/solid/src/assets/images/index.ts b/boilerplate/frontend/solid/src/assets/images/index.ts new file mode 100644 index 00000000..7adf036c --- /dev/null +++ b/boilerplate/frontend/solid/src/assets/images/index.ts @@ -0,0 +1,8 @@ +import SeparatorLine from "./separator-line.svg"; +import ArrowRight from "./arrow-right-icon.svg"; +import SignOutIcon from "./sign-out-icon.svg"; +import GuideIcon from "./guide-icon.svg"; +import BlogsIcon from "./blogs-icon.svg"; +import CelebrateIcon from "./celebrate-icon.svg"; + +export { SeparatorLine, ArrowRight, SignOutIcon, GuideIcon, BlogsIcon, CelebrateIcon }; diff --git a/boilerplate/frontend/solid/src/assets/images/separator-line.svg b/boilerplate/frontend/solid/src/assets/images/separator-line.svg new file mode 100644 index 00000000..7127a00d --- /dev/null +++ b/boilerplate/frontend/solid/src/assets/images/separator-line.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/boilerplate/frontend/solid/src/assets/images/sign-out-icon.svg b/boilerplate/frontend/solid/src/assets/images/sign-out-icon.svg new file mode 100644 index 00000000..6cc4f85f --- /dev/null +++ b/boilerplate/frontend/solid/src/assets/images/sign-out-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/boilerplate/frontend/solid/src/config.ts b/boilerplate/frontend/solid/src/config.ts new file mode 100644 index 00000000..da162e28 --- /dev/null +++ b/boilerplate/frontend/solid/src/config.ts @@ -0,0 +1,25 @@ +import { JSX } from "solid-js"; + +// This is a temporary file, if you see this comment in this file report this as an issue for create-supertokens-app +export function getApiDomain() { + return ""; +} + +export function getWebsiteDomain() { + return ""; +} + +export const SuperTokensConfig = { + appInfo: { + appName: "", + apiDomain: "", + websiteDomain: "", + }, + // recipeList contains all the modules that you want to + // use from SuperTokens. See the full list here: https://supertokens.com/docs/guides + recipeList: [], +}; + +export const recipeDetails = { + docsLink: "", +}; diff --git a/boilerplate/frontend/solid/src/main.tsx b/boilerplate/frontend/solid/src/main.tsx new file mode 100644 index 00000000..099aba17 --- /dev/null +++ b/boilerplate/frontend/solid/src/main.tsx @@ -0,0 +1,7 @@ +/* @refresh reload */ +import { render } from "solid-js/web"; +import App from "./App"; + +const root = document.getElementById("root"); + +render(() => , root!); diff --git a/boilerplate/frontend/solid-prebuilt/src/vite-env.d.ts b/boilerplate/frontend/solid/src/vite-env.d.ts similarity index 100% rename from boilerplate/frontend/solid-prebuilt/src/vite-env.d.ts rename to boilerplate/frontend/solid/src/vite-env.d.ts diff --git a/boilerplate/frontend/solid-prebuilt/tsconfig.app.json b/boilerplate/frontend/solid/tsconfig.app.json similarity index 100% rename from boilerplate/frontend/solid-prebuilt/tsconfig.app.json rename to boilerplate/frontend/solid/tsconfig.app.json diff --git a/boilerplate/frontend/solid-prebuilt/tsconfig.json b/boilerplate/frontend/solid/tsconfig.json similarity index 100% rename from boilerplate/frontend/solid-prebuilt/tsconfig.json rename to boilerplate/frontend/solid/tsconfig.json diff --git a/boilerplate/frontend/solid-prebuilt/tsconfig.node.json b/boilerplate/frontend/solid/tsconfig.node.json similarity index 100% rename from boilerplate/frontend/solid-prebuilt/tsconfig.node.json rename to boilerplate/frontend/solid/tsconfig.node.json diff --git a/boilerplate/frontend/solid-prebuilt/vite.config.ts b/boilerplate/frontend/solid/vite.config.ts similarity index 100% rename from boilerplate/frontend/solid-prebuilt/vite.config.ts rename to boilerplate/frontend/solid/vite.config.ts diff --git a/boilerplate/frontend/supertokens-react-multitenancy/src/Home/index.tsx b/boilerplate/frontend/supertokens-react-multitenancy/src/Home/index.tsx index e7a35ba9..c68850f5 100644 --- a/boilerplate/frontend/supertokens-react-multitenancy/src/Home/index.tsx +++ b/boilerplate/frontend/supertokens-react-multitenancy/src/Home/index.tsx @@ -9,7 +9,6 @@ export default function Home() { return null; } - console.log(sessionContext); return (
React component for auth UI +┃ ┣ 📜Dashboard.tsx --> React component for dashboard functionality +┃ ┗ 📜Home.tsx --> React component for home page content +┃ ┗ 📜Root.tsx --> Root React component for auth state +┣ 📂config +┃ ┣ 📜appInfo.ts --> App info / config, reused across both frontend and backend +┃ ┗ 📜frontend.ts --> Frontend config +┃ ┗ 📜backend.ts --> Backend config +┣ 📂layouts +┃ ┗ 📜Base.astro --> Common layout with header and footer +┣ 📂pages +┃ ┣ 📂auth +┃ ┣ ┣ 📂[...path] +┃ ┃ ┃ ┗ 📜[...route].astro --> Auth routes +┃ ┃ ┗ 📜[...route].astro --> Auth routes +┃ ┣ 📂api +┃ ┃ ┣ 📜 auth.ts --> Auth request handler +┃ ┃ ┣ 📜 ping.ts --> Public API endpoint +┃ ┃ ┣ 📜 sessioninfo.ts --> Protected API endpoint +┃ ┃ ┗ 📂auth +┃ ┃ ┣ 📂[...path] +┃ ┃ ┃ ┗ 📜[...route].ts --> Auth request handler +┃ ┃ ┗ 📜[...route].ts --> Auth request handler +┃ ┣ 📜auth.astro --> Main auth page +┃ ┣ 📜dashboard.astro --> Protected dashboard page +┃ ┗ 📜index.astro --> Public landing page +┣ 📂styles +┃ ┗ 📜app.css --> Global styles +┗ 📜env.d.ts --> TypeScript declarations +``` + +> Note: the nested routes are required due to how Astro handles routing, and how SuperTokens expects wildcard routes. + +### Config + +#### Astro + +The project is a standard Astro application with the Astro React integration for interactive components. + +You can customize the Astro configuration in `astro.config.mjs`. Refer to the [Astro configuration docs](https://docs.astro.build/en/reference/configuration-reference/) for more options. + +#### SuperTokens + +SuperTokens configuration is managed through recipe-specific files in the `config/` directory. Each recipe comes in two parts (due to Astro being treated as a full-stack framework): + +- `frontend.ts` - Frontend config +- `backend.ts` - Backend config + +The `appInfo.ts` file is used to configure the app info / config, and is reused across both frontend and backend. + +## Application Flow + +The application uses Astro's file-based routing and consists of four main parts: + +1. **Entry Point (`index.astro`)** + + - Public landing page + - Navigation to auth and dashboard + - Project information display + +2. **Auth Routes (`/auth/*`)** + + - Handles all authentication flows using React components + - Uses SuperTokens' pre-built UI + - Manages login, signup, and password reset + - Social login integration (when configured) + +3. **Protected Dashboard (`/dashboard`)** + + - Only accessible to authenticated users + - Displays user information + - Provides authenticated functionality + - API integration example + +4. **API Routes (`/api/*`)** + - Protected session info endpoint + - Public ping endpoint + - Server-side session validation + +When a user visits the application, they start at the home page (`/`). They can choose to authenticate through the `/auth` routes, and once authenticated, they gain access to the protected dashboard. The session state is managed throughout the application using SuperTokens' session management. + +## Customizations + +If you want to customize the default auth UI, you have two options: + +1. Refer to the [docs](https://supertokens.com/docs/thirdpartyemailpassword/advanced-customizations/react-component-override/usage) on how to customize the pre-built UI. +2. Roll your own UI by choosing "Custom UI" in the right sidebar in the [docs](https://supertokens.com/docs/thirdpartyemailpassword/quickstart/frontend-setup). + +## Additional Resources + +- Custom UI Example: https://github.com/supertokens/supertokens-web-js/tree/master/examples/react/with-thirdpartyemailpassword +- Custom UI Blog post: https://supertokens.medium.com/adding-social-login-to-your-website-with-supertokens-custom-ui-only-5fa4d7ab6402 +- Awesome SuperTokens: https://github.com/kohasummons/awesome-supertokens + +## Contributing + +Please refer to the [CONTRIBUTING.md](https://github.com/supertokens/create-supertokens-app/blob/master/CONTRIBUTING.md) file in the root of the [`create-supertokens-app`](https://github.com/supertokens/create-supertokens-app) repo. + +## Contributing + +Please refer to the [CONTRIBUTING.md](https://github.com/supertokens/create-supertokens-app/blob/master/CONTRIBUTING.md) file in the root of the [`create-supertokens-app`](https://github.com/supertokens/create-supertokens-app) repo. + +## Contact us + +For any questions, or support requests, please email us at team@supertokens.io, or join our [Discord](https://supertokens.io/discord) server. + +## Authors + +Created with :heart: by the folks at SuperTokens.io. diff --git a/boilerplate/fullstack/astro-react/astro.config.mjs b/boilerplate/fullstack/astro-react/astro.config.mjs new file mode 100644 index 00000000..d2c8cc4e --- /dev/null +++ b/boilerplate/fullstack/astro-react/astro.config.mjs @@ -0,0 +1,13 @@ +import { defineConfig } from "astro/config"; +import node from "@astrojs/node"; + +import react from "@astrojs/react"; + +// https://astro.build/config +export default defineConfig({ + output: "server", + adapter: node({ + mode: "standalone", + }), + integrations: [react()], +}); diff --git a/boilerplate/fullstack/astro-react/package.json b/boilerplate/fullstack/astro-react/package.json new file mode 100644 index 00000000..c2b92714 --- /dev/null +++ b/boilerplate/fullstack/astro-react/package.json @@ -0,0 +1,30 @@ +{ + "private": true, + "scripts": { + "dev": "astro dev", + "start": "astro dev", + "build": "astro check && astro build", + "preview": "astro preview", + "astro": "astro" + }, + "dependencies": { + "@astrojs/check": "latest", + "@astrojs/node": "latest", + "@astrojs/react": "latest", + "@types/react": "latest", + "@types/react-dom": "latest", + "astro": "latest", + "cookie": "^0.6.0", + "jose": "^5.9.3", + "jwks-rsa": "^3.1.0", + "react": "latest", + "react-dom": "latest", + "supertokens-auth-react": "latest", + "supertokens-node": "latest", + "supertokens-web-js": "latest" + }, + "devDependencies": { + "typescript": "latest", + "@types/cookie": "^0.6.0" + } +} diff --git a/boilerplate/fullstack/astro-react/public/ST.svg b/boilerplate/fullstack/astro-react/public/ST.svg new file mode 100644 index 00000000..30b435bd --- /dev/null +++ b/boilerplate/fullstack/astro-react/public/ST.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/boilerplate/fullstack/astro-react/public/assets/fonts/MenloRegular.ttf b/boilerplate/fullstack/astro-react/public/assets/fonts/MenloRegular.ttf new file mode 100644 index 00000000..033dc6d2 Binary files /dev/null and b/boilerplate/fullstack/astro-react/public/assets/fonts/MenloRegular.ttf differ diff --git a/boilerplate/fullstack/astro-react/public/assets/images/arrow-right-icon.svg b/boilerplate/fullstack/astro-react/public/assets/images/arrow-right-icon.svg new file mode 100644 index 00000000..95aa1fec --- /dev/null +++ b/boilerplate/fullstack/astro-react/public/assets/images/arrow-right-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/boilerplate/fullstack/astro-react/public/assets/images/background.png b/boilerplate/fullstack/astro-react/public/assets/images/background.png new file mode 100644 index 00000000..2147c15c Binary files /dev/null and b/boilerplate/fullstack/astro-react/public/assets/images/background.png differ diff --git a/boilerplate/fullstack/astro-react/public/assets/images/blogs-icon.svg b/boilerplate/fullstack/astro-react/public/assets/images/blogs-icon.svg new file mode 100644 index 00000000..a2fc9dd6 --- /dev/null +++ b/boilerplate/fullstack/astro-react/public/assets/images/blogs-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/boilerplate/fullstack/astro-react/public/assets/images/celebrate-icon.svg b/boilerplate/fullstack/astro-react/public/assets/images/celebrate-icon.svg new file mode 100644 index 00000000..3b40b1ef --- /dev/null +++ b/boilerplate/fullstack/astro-react/public/assets/images/celebrate-icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/boilerplate/fullstack/astro-react/public/assets/images/guide-icon.svg b/boilerplate/fullstack/astro-react/public/assets/images/guide-icon.svg new file mode 100644 index 00000000..bd85af72 --- /dev/null +++ b/boilerplate/fullstack/astro-react/public/assets/images/guide-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/boilerplate/fullstack/astro-react/public/assets/images/index.ts b/boilerplate/fullstack/astro-react/public/assets/images/index.ts new file mode 100644 index 00000000..7adf036c --- /dev/null +++ b/boilerplate/fullstack/astro-react/public/assets/images/index.ts @@ -0,0 +1,8 @@ +import SeparatorLine from "./separator-line.svg"; +import ArrowRight from "./arrow-right-icon.svg"; +import SignOutIcon from "./sign-out-icon.svg"; +import GuideIcon from "./guide-icon.svg"; +import BlogsIcon from "./blogs-icon.svg"; +import CelebrateIcon from "./celebrate-icon.svg"; + +export { SeparatorLine, ArrowRight, SignOutIcon, GuideIcon, BlogsIcon, CelebrateIcon }; diff --git a/boilerplate/fullstack/astro-react/public/assets/images/separator-line.svg b/boilerplate/fullstack/astro-react/public/assets/images/separator-line.svg new file mode 100644 index 00000000..7127a00d --- /dev/null +++ b/boilerplate/fullstack/astro-react/public/assets/images/separator-line.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/boilerplate/fullstack/astro-react/public/assets/images/sign-out-icon.svg b/boilerplate/fullstack/astro-react/public/assets/images/sign-out-icon.svg new file mode 100644 index 00000000..6cc4f85f --- /dev/null +++ b/boilerplate/fullstack/astro-react/public/assets/images/sign-out-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/boilerplate/fullstack/astro-react/public/astro-icon-dark.svg b/boilerplate/fullstack/astro-react/public/astro-icon-dark.svg new file mode 100644 index 00000000..e5211819 --- /dev/null +++ b/boilerplate/fullstack/astro-react/public/astro-icon-dark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/boilerplate/fullstack/astro-react/public/favicon.svg b/boilerplate/fullstack/astro-react/public/favicon.svg new file mode 100644 index 00000000..f157bd1c --- /dev/null +++ b/boilerplate/fullstack/astro-react/public/favicon.svg @@ -0,0 +1,9 @@ + + + + diff --git a/boilerplate/fullstack/astro/src/components/Auth.tsx b/boilerplate/fullstack/astro-react/src/components/Auth.tsx similarity index 100% rename from boilerplate/fullstack/astro/src/components/Auth.tsx rename to boilerplate/fullstack/astro-react/src/components/Auth.tsx diff --git a/boilerplate/fullstack/astro-react/src/components/Dashboard.tsx b/boilerplate/fullstack/astro-react/src/components/Dashboard.tsx new file mode 100644 index 00000000..60fdd279 --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/components/Dashboard.tsx @@ -0,0 +1,60 @@ +import { signOut } from "supertokens-auth-react/recipe/session"; +import { getApiDomain } from "../config/frontend"; +import { useEffect, useState } from "react"; + +const fetchSessionInfo = async () => { + const response = await fetch(getApiDomain() + "/api/sessioninfo"); + const data = await response.json(); + return data; +}; + +export default function Dashboard() { + const [userId, setUserId] = useState(""); + + useEffect(() => { + const fetchUserId = async () => { + const data = await fetchSessionInfo(); + setUserId(data.userId); + }; + fetchUserId(); + }, []); + + async function callAPIClicked() { + try { + const data = await fetchSessionInfo(); + window.alert("Session Information:\n" + JSON.stringify(data, null, 2)); + } catch (err) { + window.alert("Error calling API: " + err.message); + } + } + + async function logoutClicked() { + await signOut(); + window.location.href = "/"; + } + + return ( + <> +
+
+ Login successful + Login successful +
+
+
Your userID is:
+
+ {userId} +
+
+ + +
+
+
+ + ); +} diff --git a/boilerplate/fullstack/astro-react/src/components/Home.tsx b/boilerplate/fullstack/astro-react/src/components/Home.tsx new file mode 100644 index 00000000..246ededb --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/components/Home.tsx @@ -0,0 +1,48 @@ +import { useEffect, useState } from "react"; +import Session from "supertokens-auth-react/recipe/session"; + +export default function Home() { + const [sessionExists, setSessionExists] = useState(false); + useEffect(() => { + Session.doesSessionExist().then((exists) => { + setSessionExists(exists); + }); + }, []); + + return ( + <> +
+ SuperTokens + x + Astro +
+
+
+

+ SuperTokens x Astro (React)
example project +

+
+ {sessionExists ? ( +

+ You're signed in already,
check out the Dashboard! 👇 +

+ ) : ( +

Sign-in to continue

+ )} +
+ +
+
+ + ); +} diff --git a/boilerplate/fullstack/astro/src/components/Root.tsx b/boilerplate/fullstack/astro-react/src/components/Root.tsx similarity index 93% rename from boilerplate/fullstack/astro/src/components/Root.tsx rename to boilerplate/fullstack/astro-react/src/components/Root.tsx index 19fa715a..2e1561a8 100644 --- a/boilerplate/fullstack/astro/src/components/Root.tsx +++ b/boilerplate/fullstack/astro-react/src/components/Root.tsx @@ -5,7 +5,7 @@ import { SessionAuth } from "supertokens-auth-react/recipe/session/index.js"; SuperTokens.init(frontendConfig()); export default function App({ children }: { children: React.ReactNode }) { - const isUnprotectedRoute = location.pathname.startsWith("/auth"); + const isUnprotectedRoute = location.pathname.startsWith("/auth") || location.pathname === "/"; return ( {isUnprotectedRoute ? children : {children}} diff --git a/boilerplate/fullstack/astro-react/src/config/appInfo.ts b/boilerplate/fullstack/astro-react/src/config/appInfo.ts new file mode 100644 index 00000000..d4d7e19c --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/config/appInfo.ts @@ -0,0 +1,8 @@ +export const appInfo = { + // learn more about this on https://supertokens.com/docs/thirdpartyemailpassword/appinfo + appName: "SuperTokens Astro demo app", + apiDomain: "http://localhost:4321", + websiteDomain: "http://localhost:4321", + apiBasePath: "/api/auth", + websiteBasePath: "/auth", +}; diff --git a/boilerplate/fullstack/astro-react/src/config/backend.ts b/boilerplate/fullstack/astro-react/src/config/backend.ts new file mode 100644 index 00000000..eb344e5a --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/config/backend.ts @@ -0,0 +1,24 @@ +import EmailPasswordNode from "supertokens-node/recipe/emailpassword/index.js"; +import SessionNode from "supertokens-node/recipe/session/index.js"; +import Dashboard from "supertokens-node/recipe/dashboard/index.js"; +import UserRoles from "supertokens-node/recipe/userroles/index.js"; +import { appInfo } from "./appInfo"; +import { type TypeInput } from "supertokens-node/types"; +import SuperTokens from "supertokens-node"; +export const backendConfig = (): TypeInput => { + return { + appInfo, + supertokens: { + connectionURI: "https://try.supertokens.io", + }, + recipeList: [], + }; +}; + +let initialized = false; +export function ensureSuperTokensInit() { + if (!initialized) { + SuperTokens.init(backendConfig()); + initialized = true; + } +} diff --git a/boilerplate/fullstack/astro-react/src/config/backend/all_auth.ts b/boilerplate/fullstack/astro-react/src/config/backend/all_auth.ts new file mode 100644 index 00000000..42c9fcb2 --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/config/backend/all_auth.ts @@ -0,0 +1,99 @@ +import EmailPasswordNode from "supertokens-node/recipe/emailpassword/index.js"; +import ThirdPartyNode from "supertokens-node/recipe/thirdparty/index.js"; +import PasswordlessNode from "supertokens-node/recipe/passwordless/index.js"; +import SessionNode from "supertokens-node/recipe/session/index.js"; +import Dashboard from "supertokens-node/recipe/dashboard/index.js"; +import UserRoles from "supertokens-node/recipe/userroles/index.js"; +import { appInfo } from "./appInfo"; +import { type TypeInput } from "supertokens-node/types"; +import SuperTokens from "supertokens-node"; + +export let backendConfig = (): TypeInput => { + return { + supertokens: { + // this is the location of the SuperTokens core. + connectionURI: "https://try.supertokens.com", + }, + appInfo, + // recipeList contains all the modules that you want to + // use from SuperTokens. See the full list here: https://supertokens.com/docs/guides + recipeList: [ + EmailPasswordNode.init(), + ThirdPartyNode.init({ + signInAndUpFeature: { + providers: [ + // 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. + { + 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", + }, + }, + ], + }, + }, + { + config: { + thirdPartyId: "twitter", + clients: [ + { + clientId: "4398792-WXpqVXRiazdRMGNJdEZIa3RVQXc6MTpjaQ", + clientSecret: "BivMbtwmcygbRLNQ0zk45yxvW246tnYnTFFq-LH39NwZMxFpdC", + }, + ], + }, + }, + ], + }, + }), + PasswordlessNode.init({ + contactMethod: "EMAIL_OR_PHONE", + flowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + }), + SessionNode.init(), + Dashboard.init(), + UserRoles.init(), + ], + isInServerlessEnv: true, + framework: "custom", + }; +}; + +let initialized = false; +export function ensureSuperTokensInit() { + if (!initialized) { + SuperTokens.init(backendConfig()); + initialized = true; + } +} diff --git a/boilerplate/fullstack/astro-react/src/config/backend/emailpassword.ts b/boilerplate/fullstack/astro-react/src/config/backend/emailpassword.ts new file mode 100644 index 00000000..b8563dfa --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/config/backend/emailpassword.ts @@ -0,0 +1,30 @@ +import EmailPasswordNode from "supertokens-node/recipe/emailpassword/index.js"; +import SessionNode from "supertokens-node/recipe/session/index.js"; +import Dashboard from "supertokens-node/recipe/dashboard/index.js"; +import UserRoles from "supertokens-node/recipe/userroles/index.js"; +import { appInfo } from "./appInfo"; +import { type TypeInput } from "supertokens-node/types"; +import SuperTokens from "supertokens-node"; + +export let backendConfig = (): TypeInput => { + return { + supertokens: { + // this is the location of the SuperTokens core. + connectionURI: "https://try.supertokens.com", + }, + appInfo, + // recipeList contains all the modules that you want to + // use from SuperTokens. See the full list here: https://supertokens.com/docs/guides + recipeList: [EmailPasswordNode.init(), SessionNode.init(), Dashboard.init(), UserRoles.init()], + isInServerlessEnv: true, + framework: "custom", + }; +}; + +let initialized = false; +export function ensureSuperTokensInit() { + if (!initialized) { + SuperTokens.init(backendConfig()); + initialized = true; + } +} diff --git a/boilerplate/fullstack/astro-react/src/config/backend/multifactorauth.ts b/boilerplate/fullstack/astro-react/src/config/backend/multifactorauth.ts new file mode 100644 index 00000000..9caad834 --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/config/backend/multifactorauth.ts @@ -0,0 +1,130 @@ +import ThirdPartyNode from "supertokens-node/recipe/thirdparty/index.js"; +import EmailPasswordNode from "supertokens-node/recipe/emailpassword/index.js"; +import Passwordless from "supertokens-node/recipe/passwordless/index.js"; +import SessionNode from "supertokens-node/recipe/session/index.js"; +import Dashboard from "supertokens-node/recipe/dashboard/index.js"; +import UserRoles from "supertokens-node/recipe/userroles/index.js"; +import MultiFactorAuth from "supertokens-node/recipe/multifactorauth/index.js"; +import AccountLinking from "supertokens-node/recipe/accountlinking/index.js"; +import EmailVerification from "supertokens-node/recipe/emailverification/index.js"; +import TOTP from "supertokens-node/recipe/totp/index.js"; +import { appInfo } from "./appInfo"; +import { type TypeInput } from "supertokens-node/types"; +import SuperTokens from "supertokens-node"; + +export let backendConfig = (): TypeInput => { + return { + supertokens: { + // this is the location of the SuperTokens core. + connectionURI: "https://try.supertokens.com", + }, + appInfo, + // recipeList contains all the modules that you want to + // use from SuperTokens. See the full list here: https://supertokens.com/docs/guides + recipeList: [ + EmailPasswordNode.init(), + ThirdPartyNode.init({ + signInAndUpFeature: { + providers: [ + // 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. + { + 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", + }, + }, + ], + }, + }, + { + config: { + thirdPartyId: "twitter", + clients: [ + { + clientId: "4398792-WXpqVXRiazdRMGNJdEZIa3RVQXc6MTpjaQ", + clientSecret: "BivMbtwmcygbRLNQ0zk45yxvW246tnYnTFFq-LH39NwZMxFpdC", + }, + ], + }, + }, + ], + }, + }), + Passwordless.init({ + contactMethod: "EMAIL_OR_PHONE", + flowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + }), + EmailVerification.init({ + mode: "REQUIRED", + }), + AccountLinking.init({ + shouldDoAutomaticAccountLinking: async () => ({ + shouldAutomaticallyLink: true, + shouldRequireVerification: true, + }), + }), + MultiFactorAuth.init({ + firstFactors: ["thirdparty", "emailpassword"], + override: { + functions: (oI) => ({ + ...oI, + getMFARequirementsForAuth: () => [ + { + oneOf: [ + MultiFactorAuth.FactorIds.TOTP, + MultiFactorAuth.FactorIds.OTP_EMAIL, + MultiFactorAuth.FactorIds.OTP_PHONE, + ], + }, + ], + }), + }, + }), + TOTP.init(), + SessionNode.init(), + Dashboard.init(), + UserRoles.init(), + ], + isInServerlessEnv: true, + framework: "custom", + }; +}; + +let initialized = false; +export function ensureSuperTokensInit() { + if (!initialized) { + SuperTokens.init(backendConfig()); + initialized = true; + } +} diff --git a/boilerplate/fullstack/astro-react/src/config/backend/passwordless.ts b/boilerplate/fullstack/astro-react/src/config/backend/passwordless.ts new file mode 100644 index 00000000..df2c32a6 --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/config/backend/passwordless.ts @@ -0,0 +1,38 @@ +import PasswordlessNode from "supertokens-node/recipe/passwordless/index.js"; +import SessionNode from "supertokens-node/recipe/session/index.js"; +import Dashboard from "supertokens-node/recipe/dashboard/index.js"; +import UserRoles from "supertokens-node/recipe/userroles/index.js"; +import { appInfo } from "./appInfo"; +import { type TypeInput } from "supertokens-node/types"; +import SuperTokens from "supertokens-node"; + +export let backendConfig = (): TypeInput => { + return { + supertokens: { + // this is the location of the SuperTokens core. + connectionURI: "https://try.supertokens.com", + }, + appInfo, + // recipeList contains all the modules that you want to + // use from SuperTokens. See the full list here: https://supertokens.com/docs/guides + recipeList: [ + PasswordlessNode.init({ + contactMethod: "EMAIL_OR_PHONE", + flowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + }), + SessionNode.init(), + Dashboard.init(), + UserRoles.init(), + ], + isInServerlessEnv: true, + framework: "custom", + }; +}; + +let initialized = false; +export function ensureSuperTokensInit() { + if (!initialized) { + SuperTokens.init(backendConfig()); + initialized = true; + } +} diff --git a/boilerplate/fullstack/astro-react/src/config/backend/thirdparty.ts b/boilerplate/fullstack/astro-react/src/config/backend/thirdparty.ts new file mode 100644 index 00000000..46723e5d --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/config/backend/thirdparty.ts @@ -0,0 +1,92 @@ +import ThirdPartyNode from "supertokens-node/recipe/thirdparty/index.js"; +import SessionNode from "supertokens-node/recipe/session/index.js"; +import Dashboard from "supertokens-node/recipe/dashboard/index.js"; +import UserRoles from "supertokens-node/recipe/userroles/index.js"; +import { appInfo } from "./appInfo"; +import { type TypeInput } from "supertokens-node/types"; +import SuperTokens from "supertokens-node"; + +export let backendConfig = (): TypeInput => { + return { + supertokens: { + // this is the location of the SuperTokens core. + connectionURI: "https://try.supertokens.com", + }, + appInfo, + // recipeList contains all the modules that you want to + // use from SuperTokens. See the full list here: https://supertokens.com/docs/guides + recipeList: [ + ThirdPartyNode.init({ + signInAndUpFeature: { + providers: [ + // 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. + { + 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", + }, + }, + ], + }, + }, + { + config: { + thirdPartyId: "twitter", + clients: [ + { + clientId: "4398792-WXpqVXRiazdRMGNJdEZIa3RVQXc6MTpjaQ", + clientSecret: "BivMbtwmcygbRLNQ0zk45yxvW246tnYnTFFq-LH39NwZMxFpdC", + }, + ], + }, + }, + ], + }, + }), + SessionNode.init(), + Dashboard.init(), + UserRoles.init(), + ], + isInServerlessEnv: true, + framework: "custom", + }; +}; + +let initialized = false; +export function ensureSuperTokensInit() { + if (!initialized) { + SuperTokens.init(backendConfig()); + initialized = true; + } +} diff --git a/boilerplate/fullstack/astro-react/src/config/backend/thirdpartyemailpassword.ts b/boilerplate/fullstack/astro-react/src/config/backend/thirdpartyemailpassword.ts new file mode 100644 index 00000000..017fdcf5 --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/config/backend/thirdpartyemailpassword.ts @@ -0,0 +1,94 @@ +import ThirdPartyNode from "supertokens-node/recipe/thirdparty/index.js"; +import EmailPasswordNode from "supertokens-node/recipe/emailpassword/index.js"; +import SessionNode from "supertokens-node/recipe/session/index.js"; +import Dashboard from "supertokens-node/recipe/dashboard/index.js"; +import UserRoles from "supertokens-node/recipe/userroles/index.js"; +import { appInfo } from "./appInfo"; +import { type TypeInput } from "supertokens-node/types"; +import SuperTokens from "supertokens-node"; + +export let backendConfig = (): TypeInput => { + return { + supertokens: { + // this is the location of the SuperTokens core. + connectionURI: "https://try.supertokens.com", + }, + appInfo, + // recipeList contains all the modules that you want to + // use from SuperTokens. See the full list here: https://supertokens.com/docs/guides + recipeList: [ + EmailPasswordNode.init(), + ThirdPartyNode.init({ + signInAndUpFeature: { + providers: [ + // 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. + { + 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", + }, + }, + ], + }, + }, + { + config: { + thirdPartyId: "twitter", + clients: [ + { + clientId: "4398792-WXpqVXRiazdRMGNJdEZIa3RVQXc6MTpjaQ", + clientSecret: "BivMbtwmcygbRLNQ0zk45yxvW246tnYnTFFq-LH39NwZMxFpdC", + }, + ], + }, + }, + ], + }, + }), + SessionNode.init(), + Dashboard.init(), + UserRoles.init(), + ], + isInServerlessEnv: true, + framework: "custom", + }; +}; + +let initialized = false; +export function ensureSuperTokensInit() { + if (!initialized) { + SuperTokens.init(backendConfig()); + initialized = true; + } +} diff --git a/boilerplate/fullstack/astro-react/src/config/backend/thirdpartypasswordless.ts b/boilerplate/fullstack/astro-react/src/config/backend/thirdpartypasswordless.ts new file mode 100644 index 00000000..b4d2e9b3 --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/config/backend/thirdpartypasswordless.ts @@ -0,0 +1,97 @@ +import ThirdPartyNode from "supertokens-node/recipe/thirdparty/index.js"; +import PasswordlessNode from "supertokens-node/recipe/passwordless/index.js"; +import SessionNode from "supertokens-node/recipe/session/index.js"; +import Dashboard from "supertokens-node/recipe/dashboard/index.js"; +import UserRoles from "supertokens-node/recipe/userroles/index.js"; +import { appInfo } from "./appInfo"; +import { type TypeInput } from "supertokens-node/types"; +import SuperTokens from "supertokens-node"; + +export let backendConfig = (): TypeInput => { + return { + supertokens: { + // this is the location of the SuperTokens core. + connectionURI: "https://try.supertokens.com", + }, + appInfo, + // recipeList contains all the modules that you want to + // use from SuperTokens. See the full list here: https://supertokens.com/docs/guides + recipeList: [ + ThirdPartyNode.init({ + signInAndUpFeature: { + providers: [ + // 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. + { + 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", + }, + }, + ], + }, + }, + { + config: { + thirdPartyId: "twitter", + clients: [ + { + clientId: "4398792-WXpqVXRiazdRMGNJdEZIa3RVQXc6MTpjaQ", + clientSecret: "BivMbtwmcygbRLNQ0zk45yxvW246tnYnTFFq-LH39NwZMxFpdC", + }, + ], + }, + }, + ], + }, + }), + PasswordlessNode.init({ + contactMethod: "EMAIL_OR_PHONE", + flowType: "USER_INPUT_CODE_AND_MAGIC_LINK", + }), + SessionNode.init(), + Dashboard.init(), + UserRoles.init(), + ], + isInServerlessEnv: true, + framework: "custom", + }; +}; + +let initialized = false; +export function ensureSuperTokensInit() { + if (!initialized) { + SuperTokens.init(backendConfig()); + initialized = true; + } +} diff --git a/boilerplate/fullstack/astro-react/src/config/frontend.ts b/boilerplate/fullstack/astro-react/src/config/frontend.ts new file mode 100644 index 00000000..5d05dd4f --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/config/frontend.ts @@ -0,0 +1,16 @@ +// This file is responsible for bootstrapping your Remix application on the client-side. It typically imports the necessary dependencies and initializes the client-side rendering environment. In this file, you might initialize client-side libraries, set up event listeners, or perform any other client-specific initialization tasks. It's the starting point for client-side code execution. +import Session from "supertokens-auth-react/recipe/session/index.js"; +import { appInfo } from "./appInfo"; +import { type SuperTokensConfig } from "supertokens-auth-react/lib/build/types"; + +export const frontendConfig = (): SuperTokensConfig => { + return { + appInfo, + recipeList: [], + }; +}; +export const recipeDetails = { + docsLink: "https://supertokens.com/docs/thirdpartyemailpassword/introduction", +}; + +export const PreBuiltUIList = []; diff --git a/boilerplate/fullstack/astro-react/src/config/frontend/all_auth.ts b/boilerplate/fullstack/astro-react/src/config/frontend/all_auth.ts new file mode 100644 index 00000000..ef2e4387 --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/config/frontend/all_auth.ts @@ -0,0 +1,50 @@ +import EmailPasswordReact from "supertokens-auth-react/recipe/emailpassword/index.js"; +import ThirdPartyReact from "supertokens-auth-react/recipe/thirdparty/index.js"; +import PasswordlessReact from "supertokens-auth-react/recipe/passwordless/index.js"; +import Session from "supertokens-auth-react/recipe/session/index.js"; +import { appInfo } from "./appInfo"; +import { ThirdPartyPreBuiltUI } from "supertokens-auth-react/recipe/thirdparty/prebuiltui.js"; +import { PasswordlessPreBuiltUI } from "supertokens-auth-react/recipe/passwordless/prebuiltui.js"; +import { EmailPasswordPreBuiltUI } from "supertokens-auth-react/recipe/emailpassword/prebuiltui"; + +export const getApiDomain = () => { + return appInfo.apiDomain; +}; + +export const getWebsiteDomain = () => { + return appInfo.websiteDomain; +}; + +export const frontendConfig = () => { + return { + appInfo, + recipeList: [ + EmailPasswordReact.init(), + ThirdPartyReact.init({ + signInAndUpFeature: { + providers: [ + ThirdPartyReact.Github.init(), + ThirdPartyReact.Google.init(), + ThirdPartyReact.Apple.init(), + ], + }, + }), + PasswordlessReact.init({ + contactMethod: "EMAIL_OR_PHONE", + }), + Session.init(), + ], + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, + }; +}; + +export const recipeDetails = { + docsLink: "https://supertokens.com/docs/thirdpartypasswordless/introduction", +}; + +export const PreBuiltUIList = [EmailPasswordPreBuiltUI, ThirdPartyPreBuiltUI, PasswordlessPreBuiltUI]; diff --git a/boilerplate/fullstack/astro-react/src/config/frontend/emailpassword.ts b/boilerplate/fullstack/astro-react/src/config/frontend/emailpassword.ts new file mode 100644 index 00000000..d8a3e427 --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/config/frontend/emailpassword.ts @@ -0,0 +1,31 @@ +import EmailPasswordReact from "supertokens-auth-react/recipe/emailpassword/index.js"; +import Session from "supertokens-auth-react/recipe/session/index.js"; +import { appInfo } from "./appInfo"; +import { EmailPasswordPreBuiltUI } from "supertokens-auth-react/recipe/emailpassword/prebuiltui.js"; + +export const getApiDomain = () => { + return appInfo.apiDomain; +}; + +export const getWebsiteDomain = () => { + return appInfo.websiteDomain; +}; + +export const frontendConfig = () => { + return { + appInfo, + recipeList: [EmailPasswordReact.init(), Session.init()], + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, + }; +}; + +export const recipeDetails = { + docsLink: "https://supertokens.com/docs/emailpassword/introduction", +}; + +export const PreBuiltUIList = [EmailPasswordPreBuiltUI]; diff --git a/boilerplate/fullstack/astro-react/src/config/frontend/multifactorauth.ts b/boilerplate/fullstack/astro-react/src/config/frontend/multifactorauth.ts new file mode 100644 index 00000000..956cabeb --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/config/frontend/multifactorauth.ts @@ -0,0 +1,66 @@ +import ThirdPartyReact from "supertokens-auth-react/recipe/thirdparty/index.js"; +import EmailPasswordReact from "supertokens-auth-react/recipe/emailpassword/index.js"; +import MultiFactorAuthReact from "supertokens-auth-react/recipe/multifactorauth/index.js"; +import EmailVerification from "supertokens-auth-react/recipe/emailverification/index.js"; +import PasswordlessReact from "supertokens-auth-react/recipe/passwordless/index.js"; +import TOTPReact from "supertokens-auth-react/recipe/totp/index.js"; +import Session from "supertokens-auth-react/recipe/session/index.js"; +import { appInfo } from "./appInfo"; +import { ThirdPartyPreBuiltUI } from "supertokens-auth-react/recipe/thirdparty/prebuiltui.js"; +import { EmailPasswordPreBuiltUI } from "supertokens-auth-react/recipe/emailpassword/prebuiltui.js"; +import { MultiFactorAuthPreBuiltUI } from "supertokens-auth-react/recipe/multifactorauth/prebuiltui.js"; +import { EmailVerificationPreBuiltUI } from "supertokens-auth-react/recipe/emailverification/prebuiltui.js"; +import { TOTPPreBuiltUI } from "supertokens-auth-react/recipe/totp/prebuiltui.js"; +import { PasswordlessPreBuiltUI } from "supertokens-auth-react/recipe/passwordless/prebuiltui.js"; + +export const getApiDomain = () => { + return appInfo.apiDomain; +}; + +export const getWebsiteDomain = () => { + return appInfo.websiteDomain; +}; + +export const frontendConfig = () => { + return { + appInfo, + recipeList: [ + EmailPasswordReact.init(), + ThirdPartyReact.init({ + signInAndUpFeature: { + providers: [ + ThirdPartyReact.Google.init(), + ThirdPartyReact.Github.init(), + ThirdPartyReact.Apple.init(), + ], + }, + }), + PasswordlessReact.init({ + contactMethod: "EMAIL_OR_PHONE", + }), + EmailVerification.init({ mode: "REQUIRED" }), + MultiFactorAuthReact.init({ firstFactors: ["thirdparty", "emailpassword"] }), + TOTPReact.init(), + Session.init(), + ], + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, + }; +}; + +export const recipeDetails = { + docsLink: "https://supertokens.com/docs/mfa/introduction", +}; + +export const PreBuiltUIList = [ + ThirdPartyPreBuiltUI, + EmailPasswordPreBuiltUI, + PasswordlessPreBuiltUI, + MultiFactorAuthPreBuiltUI, + EmailVerificationPreBuiltUI, + TOTPPreBuiltUI, +]; diff --git a/boilerplate/fullstack/astro-react/src/config/frontend/passwordless.ts b/boilerplate/fullstack/astro-react/src/config/frontend/passwordless.ts new file mode 100644 index 00000000..8bb40c15 --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/config/frontend/passwordless.ts @@ -0,0 +1,36 @@ +import PasswordlessReact from "supertokens-auth-react/recipe/passwordless/index.js"; +import Session from "supertokens-auth-react/recipe/session/index.js"; +import { appInfo } from "./appInfo"; +import { PasswordlessPreBuiltUI } from "supertokens-auth-react/recipe/passwordless/prebuiltui.js"; + +export const getApiDomain = () => { + return appInfo.apiDomain; +}; + +export const getWebsiteDomain = () => { + return appInfo.websiteDomain; +}; + +export const frontendConfig = () => { + return { + appInfo, + recipeList: [ + PasswordlessReact.init({ + contactMethod: "EMAIL_OR_PHONE", + }), + Session.init(), + ], + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, + }; +}; + +export const recipeDetails = { + docsLink: "https://supertokens.com/docs/passwordless/introduction", +}; + +export const PreBuiltUIList = [PasswordlessPreBuiltUI]; diff --git a/boilerplate/fullstack/astro-react/src/config/frontend/thirdparty.ts b/boilerplate/fullstack/astro-react/src/config/frontend/thirdparty.ts new file mode 100644 index 00000000..e5103ca1 --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/config/frontend/thirdparty.ts @@ -0,0 +1,42 @@ +import ThirdPartyReact from "supertokens-auth-react/recipe/thirdparty/index.js"; +import Session from "supertokens-auth-react/recipe/session/index.js"; +import { appInfo } from "./appInfo"; +import { ThirdPartyPreBuiltUI } from "supertokens-auth-react/recipe/thirdparty/prebuiltui.js"; + +export const getApiDomain = () => { + return appInfo.apiDomain; +}; + +export const getWebsiteDomain = () => { + return appInfo.websiteDomain; +}; + +export const frontendConfig = () => { + return { + appInfo, + recipeList: [ + ThirdPartyReact.init({ + signInAndUpFeature: { + providers: [ + ThirdPartyReact.Google.init(), + ThirdPartyReact.Github.init(), + ThirdPartyReact.Apple.init(), + ], + }, + }), + Session.init(), + ], + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, + }; +}; + +export const recipeDetails = { + docsLink: "https://supertokens.com/docs/thirdparty/introduction", +}; + +export const PreBuiltUIList = [ThirdPartyPreBuiltUI]; diff --git a/boilerplate/fullstack/astro-react/src/config/frontend/thirdpartyemailpassword.ts b/boilerplate/fullstack/astro-react/src/config/frontend/thirdpartyemailpassword.ts new file mode 100644 index 00000000..3248f9c1 --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/config/frontend/thirdpartyemailpassword.ts @@ -0,0 +1,41 @@ +import ThirdParty, { Twitter, Apple, Github, Google } from "supertokens-auth-react/recipe/thirdparty"; +import EmailPassword from "supertokens-auth-react/recipe/emailpassword"; +import Session from "supertokens-auth-react/recipe/session"; +import { appInfo } from "./appInfo"; +import { ThirdPartyPreBuiltUI } from "supertokens-auth-react/recipe/thirdparty/prebuiltui"; +import { EmailPasswordPreBuiltUI } from "supertokens-auth-react/recipe/emailpassword/prebuiltui"; + +export const getApiDomain = () => { + return appInfo.apiDomain; +}; + +export const getWebsiteDomain = () => { + return appInfo.websiteDomain; +}; + +export const frontendConfig = () => { + return { + appInfo, + recipeList: [ + EmailPassword.init(), + ThirdParty.init({ + signInAndUpFeature: { + providers: [Github.init(), Google.init(), Apple.init(), Twitter.init()], + }, + }), + Session.init(), + ], + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, + }; +}; + +export const recipeDetails = { + docsLink: "https://supertokens.com/docs/thirdpartyemailpassword/introduction", +}; + +export const PreBuiltUIList = [ThirdPartyPreBuiltUI, EmailPasswordPreBuiltUI]; diff --git a/boilerplate/fullstack/astro-react/src/config/frontend/thirdpartypasswordless.ts b/boilerplate/fullstack/astro-react/src/config/frontend/thirdpartypasswordless.ts new file mode 100644 index 00000000..70d9dc88 --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/config/frontend/thirdpartypasswordless.ts @@ -0,0 +1,47 @@ +import ThirdPartyReact from "supertokens-auth-react/recipe/thirdparty/index.js"; +import PasswordlessReact from "supertokens-auth-react/recipe/passwordless/index.js"; +import Session from "supertokens-auth-react/recipe/session/index.js"; +import { appInfo } from "./appInfo"; +import { ThirdPartyPreBuiltUI } from "supertokens-auth-react/recipe/thirdparty/prebuiltui.js"; +import { PasswordlessPreBuiltUI } from "supertokens-auth-react/recipe/passwordless/prebuiltui.js"; + +export const getApiDomain = () => { + return appInfo.apiDomain; +}; + +export const getWebsiteDomain = () => { + return appInfo.websiteDomain; +}; + +export const frontendConfig = () => { + return { + appInfo, + recipeList: [ + ThirdPartyReact.init({ + signInAndUpFeature: { + providers: [ + ThirdPartyReact.Github.init(), + ThirdPartyReact.Google.init(), + ThirdPartyReact.Apple.init(), + ], + }, + }), + PasswordlessReact.init({ + contactMethod: "EMAIL_OR_PHONE", + }), + Session.init(), + ], + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, + }; +}; + +export const recipeDetails = { + docsLink: "https://supertokens.com/docs/thirdpartypasswordless/introduction", +}; + +export const PreBuiltUIList = [ThirdPartyPreBuiltUI, PasswordlessPreBuiltUI]; diff --git a/boilerplate/fullstack/astro-react/src/env.d.ts b/boilerplate/fullstack/astro-react/src/env.d.ts new file mode 100644 index 00000000..61a7b230 --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/env.d.ts @@ -0,0 +1,13 @@ +/// + +interface Session { + accessTokenPayload: any; + hasToken: boolean; + error: any; +} + +declare namespace App { + interface Locals { + session: Session; + } +} diff --git a/boilerplate/fullstack/astro-react/src/layouts/Base.astro b/boilerplate/fullstack/astro-react/src/layouts/Base.astro new file mode 100644 index 00000000..b775a390 --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/layouts/Base.astro @@ -0,0 +1,57 @@ +--- +import Root from "../components/Root"; +import "../styles/app.css"; +--- + + + + + + + Astro SuperTokens demo + + +
+
+ +
+
+ + + + + separator +
+
+ + \ No newline at end of file diff --git a/boilerplate/fullstack/astro-react/src/pages/api/auth.ts b/boilerplate/fullstack/astro-react/src/pages/api/auth.ts new file mode 100644 index 00000000..237e662a --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/pages/api/auth.ts @@ -0,0 +1,18 @@ +import { ensureSuperTokensInit } from "../../config/backend"; +import { handleAuthAPIRequest } from "supertokens-node/custom"; +import type { APIRoute } from "astro"; + +const handleCall = handleAuthAPIRequest(); + +export const ALL: APIRoute = async ({ request }) => { + ensureSuperTokensInit(); + + try { + return await handleCall(request); + } catch (error) { + console.error(error); + return new Response(JSON.stringify({ error: "Internal server error" }), { + status: 500, + }); + } +}; diff --git a/boilerplate/fullstack/astro-react/src/pages/api/auth/[...path]/[...route].ts b/boilerplate/fullstack/astro-react/src/pages/api/auth/[...path]/[...route].ts new file mode 100644 index 00000000..a46b10b8 --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/pages/api/auth/[...path]/[...route].ts @@ -0,0 +1,18 @@ +import { ensureSuperTokensInit } from "../../../../config/backend"; +import { handleAuthAPIRequest } from "supertokens-node/custom"; +import type { APIRoute } from "astro"; + +const handleCall = handleAuthAPIRequest(); + +export const ALL: APIRoute = async ({ request }) => { + ensureSuperTokensInit(); + + try { + return await handleCall(request); + } catch (error) { + console.error(error); + return new Response(JSON.stringify({ error: "Internal server error" }), { + status: 500, + }); + } +}; diff --git a/boilerplate/fullstack/astro-react/src/pages/api/auth/[...route].ts b/boilerplate/fullstack/astro-react/src/pages/api/auth/[...route].ts new file mode 100644 index 00000000..66f1a97e --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/pages/api/auth/[...route].ts @@ -0,0 +1,18 @@ +import { ensureSuperTokensInit } from "../../../config/backend"; +import { handleAuthAPIRequest } from "supertokens-node/custom"; +import type { APIRoute } from "astro"; + +const handleCall = handleAuthAPIRequest(); + +export const ALL: APIRoute = async ({ request }) => { + ensureSuperTokensInit(); + + try { + return await handleCall(request); + } catch (error) { + console.error(error); + return new Response(JSON.stringify({ error: "Internal server error" }), { + status: 500, + }); + } +}; diff --git a/boilerplate/fullstack/astro-react/src/pages/api/ping.ts b/boilerplate/fullstack/astro-react/src/pages/api/ping.ts new file mode 100644 index 00000000..428697b3 --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/pages/api/ping.ts @@ -0,0 +1,10 @@ +import type { APIRoute } from "astro"; + +export const GET: APIRoute = async () => { + return new Response(JSON.stringify({ message: "pong" }), { + status: 200, + headers: { + "Content-Type": "application/json", + }, + }); +}; diff --git a/boilerplate/fullstack/astro-react/src/pages/api/sessioninfo.ts b/boilerplate/fullstack/astro-react/src/pages/api/sessioninfo.ts new file mode 100644 index 00000000..e6565821 --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/pages/api/sessioninfo.ts @@ -0,0 +1,29 @@ +import type { APIRoute } from "astro"; +import { getSessionForSSR } from "supertokens-node/custom"; + +export const GET: APIRoute = async ({ request }) => { + const { accessTokenPayload, hasToken, error } = await getSessionForSSR(request); + + if (!hasToken || error) { + return new Response(JSON.stringify({ error: "Unauthorized" }), { + status: 401, + headers: { + "Content-Type": "application/json", + }, + }); + } + + return new Response( + JSON.stringify({ + sessionHandle: accessTokenPayload.sessionHandle, + userId: accessTokenPayload.sub, + accessTokenPayload, + }), + { + status: 200, + headers: { + "Content-Type": "application/json", + }, + } + ); +}; diff --git a/boilerplate/fullstack/astro-react/src/pages/auth.astro b/boilerplate/fullstack/astro-react/src/pages/auth.astro new file mode 100644 index 00000000..6c0f44c4 --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/pages/auth.astro @@ -0,0 +1,8 @@ +--- +import Base from "../layouts/Base.astro"; +import AuthComponent from "../components/Auth"; +--- + + + + diff --git a/boilerplate/fullstack/astro-react/src/pages/auth/[...path]/[...route].astro b/boilerplate/fullstack/astro-react/src/pages/auth/[...path]/[...route].astro new file mode 100644 index 00000000..93c16a97 --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/pages/auth/[...path]/[...route].astro @@ -0,0 +1,8 @@ +--- +import Base from "../../../layouts/Base.astro"; +import AuthComponent from "../../../components/Auth"; +--- + + + + diff --git a/boilerplate/fullstack/astro-react/src/pages/auth/[...route].astro b/boilerplate/fullstack/astro-react/src/pages/auth/[...route].astro new file mode 100644 index 00000000..724e4da9 --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/pages/auth/[...route].astro @@ -0,0 +1,8 @@ +--- +import Base from "../../layouts/Base.astro"; +import AuthComponent from "../../components/Auth"; +--- + + + + diff --git a/boilerplate/fullstack/astro-react/src/pages/dashboard.astro b/boilerplate/fullstack/astro-react/src/pages/dashboard.astro new file mode 100644 index 00000000..8902c12c --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/pages/dashboard.astro @@ -0,0 +1,10 @@ +--- +import Base from "../layouts/Base.astro"; +import DashboardContent from "../components/Dashboard"; +--- + + + + \ No newline at end of file diff --git a/boilerplate/fullstack/astro-react/src/pages/index.astro b/boilerplate/fullstack/astro-react/src/pages/index.astro new file mode 100644 index 00000000..4c347da1 --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/pages/index.astro @@ -0,0 +1,8 @@ +--- +import Base from "../layouts/Base.astro"; +import Home from "../components/Home.tsx"; +--- + + + + diff --git a/boilerplate/fullstack/astro-react/src/styles/app.css b/boilerplate/fullstack/astro-react/src/styles/app.css new file mode 100644 index 00000000..d73bc9c1 --- /dev/null +++ b/boilerplate/fullstack/astro-react/src/styles/app.css @@ -0,0 +1,179 @@ +/* Base styles */ +:root { + --primary-color: #ff9933; + --primary-hover: #ff8a15; + --success-color: #3eb655; + --success-bg: #e7ffed; + --border-color: #e0e0e0; +} + +@font-face { + font-family: Menlo; + src: url("./assets/fonts/MenloRegular.ttf"); +} + +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", + "Droid Sans", "Helvetica Neue", sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +* { + box-sizing: border-box; +} + +/* Layout */ +.app-container { + display: flex; + flex-direction: column; + width: 100%; + min-height: 100vh; + font-family: Rubik, sans-serif; +} + +.fill { + display: flex; + flex-direction: column; + justify-content: center; + flex: 1; +} + +/* Header */ +header { + border-bottom: 2px solid var(--border-color); + padding: 8px; +} + +header nav { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; +} + +header img { + width: 40px; +} + +header ul { + display: flex; + gap: 12px; + list-style: none; + margin: 0; + padding: 0; +} + +header a { + color: var(--primary-color); + font-weight: 600; + text-decoration: none; +} + +header a:hover { + color: var(--primary-hover); +} + +/* Home page */ +#home-container { + align-items: center; + min-height: calc(100vh - 58px); + background: url("./assets/images/background.png") center/cover; +} + +.logos { + display: flex; + align-items: center; + gap: 20px; + padding-bottom: 80px; +} + +.logos span { + font-size: 8rem; + font-weight: bold; +} + +.logos img { + height: 240px; + width: auto; +} + +/* Main content */ +.main-container { + box-shadow: 0 0 60px rgba(0, 0, 0, 0.16); + width: min(635px, calc(100% - 24px)); + border-radius: 16px; + margin-bottom: 159px; + background-color: white; +} + +.success-title { + display: flex; + justify-content: center; + align-items: center; + padding: 26px; + background-color: var(--success-bg); + color: var(--success-color); + font-size: 20px; + line-height: 1; +} + +.success-title img { + margin-right: 8px; +} + +.inner-content { + padding: 48px; + text-align: center; + display: flex; + flex-direction: column; + align-items: center; +} + +/* User ID display */ +#user-id { + position: relative; + width: min(430px, calc(100% - 30px)); + margin: 11px auto 23px; + padding: 14px 17px; + border-radius: 9px; + font-family: Menlo, monospace; + line-height: 1; + cursor: text; + border: 3px solid var(--primary-color); +} + +/* Buttons and navigation */ +.buttons { + display: flex; + gap: 4px; +} + +.dashboard-button { + padding: 8px 13px; + background: var(--primary-color); + border: 1px solid var(--primary-hover); + border-radius: 6px; + box-shadow: 0 3px 6px rgba(255, 153, 51, 0.16); + color: white; + font-weight: bold; + font-size: 16px; + cursor: pointer; + text-decoration: none; +} + +/* Footer */ +footer { + padding: 10px; +} + +footer a { + color: var(--primary-color); + font-weight: 600; + /* text-decoration: none; */ +} + +footer a:hover { + color: var(--primary-hover); +} diff --git a/boilerplate/fullstack/astro-react/tsconfig.json b/boilerplate/fullstack/astro-react/tsconfig.json new file mode 100644 index 00000000..a1b4217e --- /dev/null +++ b/boilerplate/fullstack/astro-react/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "astro/tsconfigs/strict", + "compilerOptions": { + "jsx": "react-jsx", + "jsxImportSource": "react", + "lib": ["ES2015"] + } +} diff --git a/boilerplate/fullstack/astro/README.md b/boilerplate/fullstack/astro/README.md index bdebb658..c71af6cc 100644 --- a/boilerplate/fullstack/astro/README.md +++ b/boilerplate/fullstack/astro/README.md @@ -1,141 +1,122 @@ -![SuperTokens banner](https://raw.githubusercontent.com/supertokens/supertokens-logo/master/images/Artboard%20%E2%80%93%2027%402x.png) - -# SuperTokens with Astro - -This demo app demonstrates how to integrate SuperTokens into an Astro application. - -Features: - -- Initializes SuperTokens with frontend and backend configurations -- Creates a frontend page to handle authentication-related tasks -- Integrates the SuperTokens' pre-built login UI for secure user authentication -- Protects frontend to ensure only authenticated users can access the dashboard -- Exposes the SuperTokens authentication APIs used by frontend widgets - -## Project structure - -```txt -📦[your-app-name] -┣ 📂public -┃ ┣ 📂assets -┃ ┃ ┣ 📂fonts -┃ ┃ ┗ 📂images -┃ ┗ 📜favicon.svg -┣ 📂src -┃ ┣ 📂components -┃ ┃ ┣ 📜Auth.tsx -┃ ┃ ┣ 📜Home.tsx -┃ ┃ ┣ 📜Root.tsx -┃ ┃ ┣ 📜sessionAuthForAstro.tsx -┃ ┃ ┗ 📜tryRefreshClientComponent.tsx -┃ ┣ 📂layouts -┃ ┃ ┗ 📜Base.astro -┃ ┣ 📂config -┃ ┃ ┣ 📜appInfo.tsx -┃ ┃ ┣ 📜backend.tsx -┃ ┃ ┗ 📜frontend.tsx -┃ ┣ 📂pages -┃ ┃ ┣ 📂auth -┃ ┃ ┃ ┣ 📂[...path] -┃ ┃ ┃ ┃ ┗ 📜[...route].astro -┃ ┃ ┃ ┗ 📜[...route].astro -┃ ┃ ┣ 📂api -┃ ┃ ┃ ┣ 📂auth -┃ ┃ ┃ ┃ ┣ 📂[...path] -┃ ┃ ┃ ┃ ┃ ┗ 📜[...route].ts -┃ ┃ ┃ ┃ ┗ 📜[...route].ts -┃ ┃ ┣ 📜auth.astro -┃ ┃ ┣ 📜index.astro -┃ ┃ ┗ 📜sessioninfo.ts -┃ ┣ 📂styles -┃ ┃ ┗ 📜app.css -┃ ┣ 📜env.d.ts -┃ ┗ 📜superTokensHelpers.ts -┣ 📜astro.config.mjs -┣ 📜package.json -┣ 📜README.md -┗ 📜tsconfig.json +# SuperTokens + Astro + +A demo implementation of [SuperTokens](https://supertokens.com/) with [Astro](https://astro.build/), using SuperTokens' prebuilt UI. + +## General Info + +This project aims to demonstrate how to integrate SuperTokens into an Astro application using our prebuilt UI. Its primary purpose is to serve as an educational tool, but it can also be used as a starting point for your own project. + +## Repo Structure + +### Source + +``` +📦src +┣ 📂components +┃ ┣ 📜auth.astro --> Astro component for auth UI +┃ ┗ 📜dashboard.astro --> Astro component for dashboard functionality +┣ 📂config +┃ ┣ 📜appInfo.ts --> App info / config, reused across both frontend and backend +┃ ┗ 📜frontend.ts --> Frontend config +┃ ┗ 📜backend.ts --> Backend config +┣ 📂layouts +┃ ┗ 📜Base.astro --> Common layout with header and footer +┣ 📂pages +┃ ┣ 📂auth +┃ ┣ ┣ 📂[...path] +┃ ┃ ┃ ┗ 📜[...route].astro --> Auth routes +┃ ┃ ┗ 📜[...route].astro --> Auth routes +┃ ┣ 📂api +┃ ┃ ┣ 📜 auth.ts --> Auth request handler +┃ ┃ ┣ 📜 ping.ts --> Public API endpoint +┃ ┃ ┣ 📜 sessioninfo.ts --> Protected API endpoint +┃ ┃ ┗ 📂auth +┃ ┃ ┣ 📂[...path] +┃ ┃ ┃ ┗ 📜[...route].ts --> Auth request handler +┃ ┃ ┗ 📜[...route].ts --> Auth request handler +┃ ┣ 📜auth.astro --> Main auth page +┃ ┣ 📜dashboard.astro --> Protected dashboard page +┃ ┗ 📜index.astro --> Public landing page +┣ 📂styles +┃ ┗ 📜app.css --> Global styles +┗ 📜env.d.ts --> TypeScript declarations ``` -Let's explore the important files: +> Note: the nested routes are required due to how Astro handles routing, and how SuperTokens expects wildcard routes. -| Directory/File | Description | -| -------------- | -------------------------------------------------------------------------------------- | -| **src** | Contains configuration files, pages and components for your application. | -| **src/config** | Contains configuration files for your application. | -| | `appInfo.tsx` : Includes information about your application reused throughout the app. | -| | `backend.tsx` : Backend-related configuration, including settings for SuperTokens. | -| | `frontend.tsx` : Frontend configuration, including settings for SuperTokens. | -| **pages** | Contains route files for your application. | -| | `index.astro` : Represents the default route or landing page. | -| | `auth.astro` : Shows the pre-built auth UI | -| | `auth/...` : Deals with authentication callbacks using SuperTokens. | -| | `supertokens/...` : Deals with authentication routes or components using SuperTokens. | +### Config -## Run application locally +#### Astro -Follow the steps outlined below to run the application locally: +The project is a standard Astro application. -1. Change directory to the **[your-app-name]** folder. +You can customize the Astro configuration in `astro.config.mjs`. Refer to the [Astro configuration docs](https://docs.astro.build/en/reference/configuration-reference/) for more options. - ```shell - cd your-app-name - ``` +#### SuperTokens -2. Run the application with the command below: +SuperTokens configuration is managed through recipe-specific files in the `config/` directory. Each recipe comes in two parts (due to Astro being treated as a full-stack framework): - ```shell - npm run dev - ``` +- `frontend.ts` - Frontend config +- `backend.ts` - Backend config -## How to use +The `appInfo.ts` file is used to configure the app info / config, and is reused across both frontend and backend. -### Using `create-supertokens-app` +## Application Flow -- Run the following command +The application uses Astro's file-based routing and consists of four main parts: -```bash -npx create-supertokens-app@latest --frontend=astro -``` +1. **Entry Point (`index.astro`)** -- Follow the instructions on screen + - Public landing page + - Navigation to auth and dashboard + - Project information display -### Specific Astro considerations for the pre-built UI method using React directly +2. **Auth Routes (`/auth/*`)** -Since this demo is using out [pre-built UI](https://supertokens.com/docs/thirdpartyemailpassword/pre-built-ui/setup/frontend), it relies heavily on React. As such, in order to customize the auth and protected / unprotected routes behavior, take a look at the Root component in `src/components/Root.tsx`. The Root component is used in the `src/layouts/Base.astro` layout file, which is used in all demo pages. + - Handles all authentication flows using React components + - Uses SuperTokens' pre-built UI + - Manages login, signup, and password reset + - Social login integration (when configured) -In order to create a public route, you can either customize the `isUnprotectedRoute` behavior in the Root component: +3. **Protected Dashboard (`/dashboard`)** -```tsx -const unprotectedRoutes = ["/auth", "/my-public-route"]; + - Only accessible to authenticated users + - Displays user information + - Provides authenticated functionality + - API integration example -export default function App({ children }: { children: React.ReactNode }) { - // const isUnprotectedRoute = location.pathname.startsWith("/auth"); -> remove this - const isUnprotectedRoute = unprotectedRoutes.some((route) => location.pathname.startsWith(route)); +4. **API Routes (`/api/*`)** + - Protected session info endpoint + - Public ping endpoint + - Server-side session validation - return ( - {isUnprotectedRoute ? children : {children}} - ); -} -``` +When a user visits the application, they start at the home page (`/`). They can choose to authenticate through the `/auth` routes, and once authenticated, they gain access to the protected dashboard. The session state is managed throughout the application using SuperTokens' session management. + +## Customizations + +If you want to customize the default auth UI, you have two options: + +1. Refer to the [docs](https://supertokens.com/docs/thirdpartyemailpassword/advanced-customizations/react-component-override/usage) on how to customize the pre-built UI. +2. Roll your own UI by choosing "Custom UI" in the right sidebar in the [docs](https://supertokens.com/docs/thirdpartyemailpassword/quickstart/frontend-setup). -or, use your own layout component, but make sure to check whether a session exists if you'd like to protect that route (see `index.astro` for an example, using `getSessionForSSR`). +## Additional Resources -### Alternatives +- Custom UI Astro Example: https://github.com/supertokens/supertokens-web-js/tree/master/examples/astro/with-thirdpartyemailpassword +- Custom UI Blog post: https://supertokens.medium.com/adding-social-login-to-your-website-with-supertokens-custom-ui-only-5fa4d7ab6402 +- Awesome SuperTokens: https://github.com/kohasummons/awesome-supertokens -If you'd like a more low-level approach, you can go for a custom UI setup. This amount to using our SDK methods with your UI - you can find an example [here](https://github.com/supertokens/supertokens-web-js/tree/master/examples/astro/with-thirdpartyemailpassword). +## Contributing -If you'd like a simpler setup, you can use our universal pre-built UI. It's still build on React, but it doesn't explicitly depend on having React in your project. As a result, you only have to provide some routess and a div element to render the UI in. You can find an example [here](). +Please refer to the [CONTRIBUTING.md](https://github.com/supertokens/create-supertokens-app/blob/master/CONTRIBUTING.md) file in the root of the [`create-supertokens-app`](https://github.com/supertokens/create-supertokens-app) repo. -## Author +## Contributing -Created with :heart: by the folks at supertokens.com. +Please refer to the [CONTRIBUTING.md](https://github.com/supertokens/create-supertokens-app/blob/master/CONTRIBUTING.md) file in the root of the [`create-supertokens-app`](https://github.com/supertokens/create-supertokens-app) repo. -## License +## Contact us -This project is licensed under the Apache 2.0 license. +For any questions, or support requests, please email us at team@supertokens.io, or join our [Discord](https://supertokens.io/discord) server. -## Notes +## Authors -- To know more about how this app works and to learn how to customise it based on your use cases refer to the [SuperTokens Documentation](https://supertokens.com/docs/guides) -- We have provided development OAuth keys for the various built-in third party providers in the `/app/config/backend.ts` file. Feel free to use them for development purposes, but **please create your own keys for production use**. +Created with :heart: by the folks at SuperTokens.io. diff --git a/boilerplate/fullstack/astro/astro.config.mjs b/boilerplate/fullstack/astro/astro.config.mjs index d2c8cc4e..6c79776a 100644 --- a/boilerplate/fullstack/astro/astro.config.mjs +++ b/boilerplate/fullstack/astro/astro.config.mjs @@ -1,13 +1,10 @@ import { defineConfig } from "astro/config"; import node from "@astrojs/node"; -import react from "@astrojs/react"; - // https://astro.build/config export default defineConfig({ output: "server", adapter: node({ mode: "standalone", }), - integrations: [react()], }); diff --git a/boilerplate/fullstack/astro/package.json b/boilerplate/fullstack/astro/package.json index c2b92714..ab70728e 100644 --- a/boilerplate/fullstack/astro/package.json +++ b/boilerplate/fullstack/astro/package.json @@ -10,16 +10,10 @@ "dependencies": { "@astrojs/check": "latest", "@astrojs/node": "latest", - "@astrojs/react": "latest", - "@types/react": "latest", - "@types/react-dom": "latest", "astro": "latest", "cookie": "^0.6.0", "jose": "^5.9.3", "jwks-rsa": "^3.1.0", - "react": "latest", - "react-dom": "latest", - "supertokens-auth-react": "latest", "supertokens-node": "latest", "supertokens-web-js": "latest" }, diff --git a/boilerplate/fullstack/astro/public/ST.svg b/boilerplate/fullstack/astro/public/ST.svg new file mode 100644 index 00000000..30b435bd --- /dev/null +++ b/boilerplate/fullstack/astro/public/ST.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/boilerplate/fullstack/astro/public/astro-icon-dark.svg b/boilerplate/fullstack/astro/public/astro-icon-dark.svg new file mode 100644 index 00000000..e5211819 --- /dev/null +++ b/boilerplate/fullstack/astro/public/astro-icon-dark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/boilerplate/fullstack/astro/src/components/Home.tsx b/boilerplate/fullstack/astro/src/components/Home.tsx deleted file mode 100644 index 45f67716..00000000 --- a/boilerplate/fullstack/astro/src/components/Home.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import { recipeDetails } from "../config/frontend"; -import SuperTokens from "supertokens-auth-react"; -import SessionReact from "supertokens-auth-react/recipe/session/index.js"; -import { TryRefreshComponent } from "../components/tryRefreshClientComponent"; -import { SessionAuthForAstro } from "./sessionAuthForAstro"; -import type { JWTPayload } from "jose"; - -export default function Home({ - accessTokenPayload, - hasToken, - error, -}: { - accessTokenPayload: JWTPayload | undefined; - hasToken: boolean; - error: Error | undefined; -}) { - async function logoutClicked() { - await SessionReact.signOut(); - SuperTokens.redirectToAuth(); - } - - if (error) { - return
Something went wrong while trying to get the session. Error - {error.message}
; - } - - // `accessTokenPayload` will be undefined if it the session does not exist or has expired - if (accessTokenPayload === undefined) { - /** - * This means that the user is not logged in. If you want to display some other UI in this - * case, you can do so here. - */ - if (!hasToken) { - location.href = "/auth"; - return; - } - - /** - * This means that the session does not exist but we have session tokens for the user. In this case - * the `TryRefreshComponent` will try to refresh the session. - * - * To learn about why the 'key' attribute is required refer to: https://github.com/supertokens/supertokens-node/issues/826#issuecomment-2092144048 - */ - return ; - } - - const fetchUserData = async () => { - const userInfoResponse = await fetch("http://localhost:4321/sessioninfo"); - - alert(JSON.stringify(await userInfoResponse.json())); - }; - - const links: { - name: string; - link: string; - icon: string; - }[] = [ - { - name: "Blogs", - link: "https://supertokens.com/blog", - icon: "/assets/images/blogs-icon.svg", - }, - { - name: "Guides", - link: recipeDetails.docsLink, - icon: "/assets/images/guide-icon.svg", - }, - { - name: "Sign Out", - link: "", - icon: "/assets/images/sign-out-icon.svg", - }, - ]; - - /** - * SessionAuthForAstro will handle proper redirection for the user based on the different session states. - * It will redirect to the login page if the session does not exist etc. - */ - return ( - -
-
-
- Login successful - Login successful -
-
-
Your userID is:
- -
{accessTokenPayload.sub}
- - -
-
- -
- {links.map((link) => { - if (link.name === "Sign Out") { - return ( - - ); - } - return ( - - {link.name} -
{link.name}
-
- ); - })} -
- - separator -
-
- ); -} diff --git a/boilerplate/fullstack/astro/src/components/auth.astro b/boilerplate/fullstack/astro/src/components/auth.astro new file mode 100644 index 00000000..6779a725 --- /dev/null +++ b/boilerplate/fullstack/astro/src/components/auth.astro @@ -0,0 +1,22 @@ + + +
\ No newline at end of file diff --git a/boilerplate/fullstack/astro/src/components/dashboard.astro b/boilerplate/fullstack/astro/src/components/dashboard.astro new file mode 100644 index 00000000..ecab2d0a --- /dev/null +++ b/boilerplate/fullstack/astro/src/components/dashboard.astro @@ -0,0 +1,69 @@ +--- +--- + +
+
+ Login successful + Login successful +
+
+
Your userID is:
+
+ +
+
+ + +
+
+
+ + diff --git a/boilerplate/fullstack/astro/src/components/sessionAuthForAstro.tsx b/boilerplate/fullstack/astro/src/components/sessionAuthForAstro.tsx deleted file mode 100644 index 1d06e53b..00000000 --- a/boilerplate/fullstack/astro/src/components/sessionAuthForAstro.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { useState, useEffect } from "react"; -import { SessionAuth } from "supertokens-auth-react/recipe/session/index.js"; - -type Props = Parameters[0]; - -export const SessionAuthForAstro = (props: Props) => { - const [loaded, setLoaded] = useState(false); - - useEffect(() => { - setLoaded(true); - }, []); - - if (!loaded) { - return props.children; - } - - return {props.children}; -}; diff --git a/boilerplate/fullstack/astro/src/components/tryRefreshClientComponent.tsx b/boilerplate/fullstack/astro/src/components/tryRefreshClientComponent.tsx deleted file mode 100644 index d82cde09..00000000 --- a/boilerplate/fullstack/astro/src/components/tryRefreshClientComponent.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { useEffect, useState } from "react"; -import Session from "supertokens-auth-react/recipe/session/index.js"; -import SuperTokens from "supertokens-auth-react"; - -export const TryRefreshComponent = () => { - // Get the navigation function from Remix - // const navigate = useNavigate(); - // State to track if an error occurred during session refresh - const [didError, setDidError] = useState(false); - - // Effect to attempt refreshing the session when the component mounts - useEffect(() => { - void Session.attemptRefreshingSession() - .then((hasSession) => { - if (hasSession) { - location.href = window.location.pathname; - } else { - SuperTokens.redirectToAuth(); - } - }) - .catch(() => { - setDidError(true); - }); - }, []); - - if (didError) { - return
Something went wrong, please reload the page
; - } - return
Loading...
; -}; diff --git a/boilerplate/fullstack/astro/src/config/frontend/all_auth.ts b/boilerplate/fullstack/astro/src/config/frontend/all_auth.ts index b8131064..a9d88a8b 100644 --- a/boilerplate/fullstack/astro/src/config/frontend/all_auth.ts +++ b/boilerplate/fullstack/astro/src/config/frontend/all_auth.ts @@ -1,36 +1,47 @@ -import EmailPasswordReact from "supertokens-auth-react/recipe/emailpassword/index.js"; -import ThirdPartyReact from "supertokens-auth-react/recipe/thirdparty/index.js"; -import PasswordlessReact from "supertokens-auth-react/recipe/passwordless/index.js"; -import Session from "supertokens-auth-react/recipe/session/index.js"; +import SuperTokens from "supertokens-web-js"; +import Session from "supertokens-web-js/recipe/session"; import { appInfo } from "./appInfo"; -import { ThirdPartyPreBuiltUI } from "supertokens-auth-react/recipe/thirdparty/prebuiltui.js"; -import { PasswordlessPreBuiltUI } from "supertokens-auth-react/recipe/passwordless/prebuiltui.js"; -import { EmailPasswordPreBuiltUI } from "supertokens-auth-react/recipe/emailpassword/prebuiltui"; -export const frontendConfig = () => { - return { +export const getApiDomain = () => { + return appInfo.apiDomain; +}; + +export const getWebsiteDomain = () => { + return appInfo.websiteDomain; +}; + +export function initSuperTokensUI() { + (window as any).supertokensUIInit("supertokensui", { appInfo, recipeList: [ - EmailPasswordReact.init(), - ThirdPartyReact.init({ + (window as any).supertokensUIEmailPassword.init(), + (window as any).supertokensUIThirdParty.init({ signInAndUpFeature: { providers: [ - ThirdPartyReact.Github.init(), - ThirdPartyReact.Google.init(), - ThirdPartyReact.Apple.init(), + (window as any).supertokensUIThirdParty.Github.init(), + (window as any).supertokensUIThirdParty.Google.init(), + (window as any).supertokensUIThirdParty.Apple.init(), + (window as any).supertokensUIThirdParty.Twitter.init(), ], }, }), - PasswordlessReact.init({ + (window as any).supertokensUIPasswordless.init({ contactMethod: "EMAIL_OR_PHONE", }), - Session.init(), + (window as any).supertokensUISession.init(), ], - }; -}; - -export const recipeDetails = { - docsLink: "https://supertokens.com/docs/thirdpartypasswordless/introduction", -}; + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, + }); +} -export const PreBuiltUIList = [EmailPasswordPreBuiltUI, ThirdPartyPreBuiltUI, PasswordlessPreBuiltUI]; +export function initSuperTokensWebJS() { + SuperTokens.init({ + appInfo, + recipeList: [Session.init()], + }); +} diff --git a/boilerplate/fullstack/astro/src/config/frontend/emailpassword.ts b/boilerplate/fullstack/astro/src/config/frontend/emailpassword.ts index 577dfe73..6b91d541 100644 --- a/boilerplate/fullstack/astro/src/config/frontend/emailpassword.ts +++ b/boilerplate/fullstack/astro/src/config/frontend/emailpassword.ts @@ -1,17 +1,31 @@ -import EmailPasswordReact from "supertokens-auth-react/recipe/emailpassword/index.js"; -import Session from "supertokens-auth-react/recipe/session/index.js"; +import SuperTokens from "supertokens-web-js"; +import Session from "supertokens-web-js/recipe/session"; import { appInfo } from "./appInfo"; -import { EmailPasswordPreBuiltUI } from "supertokens-auth-react/recipe/emailpassword/prebuiltui.js"; -export const frontendConfig = () => { - return { - appInfo, - recipeList: [EmailPasswordReact.init(), Session.init()], - }; +export const getApiDomain = () => { + return appInfo.apiDomain; }; -export const recipeDetails = { - docsLink: "https://supertokens.com/docs/emailpassword/introduction", +export const getWebsiteDomain = () => { + return appInfo.websiteDomain; }; -export const PreBuiltUIList = [EmailPasswordPreBuiltUI]; +export function initSuperTokensUI() { + (window as any).supertokensUIInit("supertokensui", { + appInfo, + recipeList: [(window as any).supertokensUIEmailPassword.init(), (window as any).supertokensUISession.init()], + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, + }); +} + +export function initSuperTokensWebJS() { + SuperTokens.init({ + appInfo, + recipeList: [Session.init()], + }); +} diff --git a/boilerplate/fullstack/astro/src/config/frontend/multifactorauth.ts b/boilerplate/fullstack/astro/src/config/frontend/multifactorauth.ts index 6985460b..99fb8748 100644 --- a/boilerplate/fullstack/astro/src/config/frontend/multifactorauth.ts +++ b/boilerplate/fullstack/astro/src/config/frontend/multifactorauth.ts @@ -1,52 +1,56 @@ -import ThirdPartyReact from "supertokens-auth-react/recipe/thirdparty/index.js"; -import EmailPasswordReact from "supertokens-auth-react/recipe/emailpassword/index.js"; -import MultiFactorAuthReact from "supertokens-auth-react/recipe/multifactorauth/index.js"; -import EmailVerification from "supertokens-auth-react/recipe/emailverification/index.js"; -import PasswordlessReact from "supertokens-auth-react/recipe/passwordless/index.js"; -import TOTPReact from "supertokens-auth-react/recipe/totp/index.js"; -import Session from "supertokens-auth-react/recipe/session/index.js"; +import SuperTokens from "supertokens-web-js"; +import Session from "supertokens-web-js/recipe/session"; +import EmailVerification from "supertokens-web-js/recipe/emailverification"; +import MultiFactorAuth from "supertokens-web-js/recipe/multifactorauth"; import { appInfo } from "./appInfo"; -import { ThirdPartyPreBuiltUI } from "supertokens-auth-react/recipe/thirdparty/prebuiltui.js"; -import { EmailPasswordPreBuiltUI } from "supertokens-auth-react/recipe/emailpassword/prebuiltui.js"; -import { MultiFactorAuthPreBuiltUI } from "supertokens-auth-react/recipe/multifactorauth/prebuiltui.js"; -import { EmailVerificationPreBuiltUI } from "supertokens-auth-react/recipe/emailverification/prebuiltui.js"; -import { TOTPPreBuiltUI } from "supertokens-auth-react/recipe/totp/prebuiltui.js"; -import { PasswordlessPreBuiltUI } from "supertokens-auth-react/recipe/passwordless/prebuiltui.js"; -export const frontendConfig = () => { - return { +export const getApiDomain = () => { + return appInfo.apiDomain; +}; + +export const getWebsiteDomain = () => { + return appInfo.websiteDomain; +}; + +export function initSuperTokensUI() { + (window as any).supertokensUIInit("supertokensui", { appInfo, recipeList: [ - EmailPasswordReact.init(), - ThirdPartyReact.init({ + (window as any).supertokensUIEmailPassword.init(), + (window as any).supertokensUIThirdParty.init({ signInAndUpFeature: { providers: [ - ThirdPartyReact.Google.init(), - ThirdPartyReact.Github.init(), - ThirdPartyReact.Apple.init(), + (window as any).supertokensUIThirdParty.Github.init(), + (window as any).supertokensUIThirdParty.Google.init(), + (window as any).supertokensUIThirdParty.Apple.init(), + (window as any).supertokensUIThirdParty.Twitter.init(), ], }, }), - PasswordlessReact.init({ + (window as any).supertokensUIPasswordless.init({ contactMethod: "EMAIL_OR_PHONE", }), - EmailVerification.init({ mode: "REQUIRED" }), - MultiFactorAuthReact.init({ firstFactors: ["thirdparty", "emailpassword"] }), - TOTPReact.init(), - Session.init(), + (window as any).supertokensUIEmailVerification.init({ + mode: "REQUIRED", + }), + (window as any).supertokensUIMultiFactorAuth.init({ + firstFactors: ["thirdparty", "emailpassword"], + }), + (window as any).supertokensUITOTP.init(), + (window as any).supertokensUISession.init(), ], - }; -}; + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, + }); +} -export const recipeDetails = { - docsLink: "https://supertokens.com/docs/mfa/introduction", -}; - -export const PreBuiltUIList = [ - ThirdPartyPreBuiltUI, - EmailPasswordPreBuiltUI, - PasswordlessPreBuiltUI, - MultiFactorAuthPreBuiltUI, - EmailVerificationPreBuiltUI, - TOTPPreBuiltUI, -]; +export function initSuperTokensWebJS() { + SuperTokens.init({ + appInfo, + recipeList: [Session.init(), EmailVerification.init(), MultiFactorAuth.init()], + }); +} diff --git a/boilerplate/fullstack/astro/src/config/frontend/passwordless.ts b/boilerplate/fullstack/astro/src/config/frontend/passwordless.ts index 071d56ab..892d71b0 100644 --- a/boilerplate/fullstack/astro/src/config/frontend/passwordless.ts +++ b/boilerplate/fullstack/astro/src/config/frontend/passwordless.ts @@ -1,22 +1,36 @@ -import PasswordlessReact from "supertokens-auth-react/recipe/passwordless/index.js"; -import Session from "supertokens-auth-react/recipe/session/index.js"; +import SuperTokens from "supertokens-web-js"; +import Session from "supertokens-web-js/recipe/session"; import { appInfo } from "./appInfo"; -import { PasswordlessPreBuiltUI } from "supertokens-auth-react/recipe/passwordless/prebuiltui.js"; -export const frontendConfig = () => { - return { +export const getApiDomain = () => { + return appInfo.apiDomain; +}; + +export const getWebsiteDomain = () => { + return appInfo.websiteDomain; +}; + +export function initSuperTokensUI() { + (window as any).supertokensUIInit("supertokensui", { appInfo, recipeList: [ - PasswordlessReact.init({ + (window as any).supertokensUIPasswordless.init({ contactMethod: "EMAIL_OR_PHONE", }), - Session.init(), + (window as any).supertokensUISession.init(), ], - }; -}; - -export const recipeDetails = { - docsLink: "https://supertokens.com/docs/passwordless/introduction", -}; + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, + }); +} -export const PreBuiltUIList = [PasswordlessPreBuiltUI]; +export function initSuperTokensWebJS() { + SuperTokens.init({ + appInfo, + recipeList: [Session.init()], + }); +} diff --git a/boilerplate/fullstack/astro/src/config/frontend/thirdparty.ts b/boilerplate/fullstack/astro/src/config/frontend/thirdparty.ts index d01e3e60..4cb00e23 100644 --- a/boilerplate/fullstack/astro/src/config/frontend/thirdparty.ts +++ b/boilerplate/fullstack/astro/src/config/frontend/thirdparty.ts @@ -1,28 +1,43 @@ -import ThirdPartyReact from "supertokens-auth-react/recipe/thirdparty/index.js"; -import Session from "supertokens-auth-react/recipe/session/index.js"; +import SuperTokens from "supertokens-web-js"; +import Session from "supertokens-web-js/recipe/session"; import { appInfo } from "./appInfo"; -import { ThirdPartyPreBuiltUI } from "supertokens-auth-react/recipe/thirdparty/prebuiltui.js"; -export const frontendConfig = () => { - return { +export const getApiDomain = () => { + return appInfo.apiDomain; +}; + +export const getWebsiteDomain = () => { + return appInfo.websiteDomain; +}; + +export function initSuperTokensUI() { + (window as any).supertokensUIInit("supertokensui", { appInfo, recipeList: [ - ThirdPartyReact.init({ + (window as any).supertokensUIThirdParty.init({ signInAndUpFeature: { providers: [ - ThirdPartyReact.Google.init(), - ThirdPartyReact.Github.init(), - ThirdPartyReact.Apple.init(), + (window as any).supertokensUIThirdParty.Github.init(), + (window as any).supertokensUIThirdParty.Google.init(), + (window as any).supertokensUIThirdParty.Apple.init(), + (window as any).supertokensUIThirdParty.Twitter.init(), ], }, }), - Session.init(), + (window as any).supertokensUISession.init(), ], - }; -}; - -export const recipeDetails = { - docsLink: "https://supertokens.com/docs/thirdparty/introduction", -}; + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, + }); +} -export const PreBuiltUIList = [ThirdPartyPreBuiltUI]; +export function initSuperTokensWebJS() { + SuperTokens.init({ + appInfo, + recipeList: [Session.init()], + }); +} diff --git a/boilerplate/fullstack/astro/src/config/frontend/thirdpartyemailpassword.ts b/boilerplate/fullstack/astro/src/config/frontend/thirdpartyemailpassword.ts index 6cf7c34c..7f49d22d 100644 --- a/boilerplate/fullstack/astro/src/config/frontend/thirdpartyemailpassword.ts +++ b/boilerplate/fullstack/astro/src/config/frontend/thirdpartyemailpassword.ts @@ -1,31 +1,44 @@ -import ThirdPartyReact from "supertokens-auth-react/recipe/thirdparty/index.js"; -import EmailPasswordReact from "supertokens-auth-react/recipe/emailpassword/index.js"; -import Session from "supertokens-auth-react/recipe/session/index.js"; +import SuperTokens from "supertokens-web-js"; +import Session from "supertokens-web-js/recipe/session"; import { appInfo } from "./appInfo"; -import { ThirdPartyPreBuiltUI } from "supertokens-auth-react/recipe/thirdparty/prebuiltui.js"; -import { EmailPasswordPreBuiltUI } from "supertokens-auth-react/recipe/emailpassword/prebuiltui.js"; -export const frontendConfig = () => { - return { +export const getApiDomain = () => { + return appInfo.apiDomain; +}; + +export const getWebsiteDomain = () => { + return appInfo.websiteDomain; +}; + +export function initSuperTokensUI() { + (window as any).supertokensUIInit("supertokensui", { appInfo, recipeList: [ - EmailPasswordReact.init(), - ThirdPartyReact.init({ + (window as any).supertokensUIEmailPassword.init(), + (window as any).supertokensUIThirdParty.init({ signInAndUpFeature: { providers: [ - ThirdPartyReact.Google.init(), - ThirdPartyReact.Github.init(), - ThirdPartyReact.Apple.init(), + (window as any).supertokensUIThirdParty.Github.init(), + (window as any).supertokensUIThirdParty.Google.init(), + (window as any).supertokensUIThirdParty.Apple.init(), + (window as any).supertokensUIThirdParty.Twitter.init(), ], }, }), - Session.init(), + (window as any).supertokensUISession.init(), ], - }; -}; - -export const recipeDetails = { - docsLink: "https://supertokens.com/docs/thirdpartyemailpassword/introduction", -}; + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, + }); +} -export const PreBuiltUIList = [ThirdPartyPreBuiltUI, EmailPasswordPreBuiltUI]; +export function initSuperTokensWebJS() { + SuperTokens.init({ + appInfo, + recipeList: [Session.init()], + }); +} diff --git a/boilerplate/fullstack/astro/src/config/frontend/thirdpartypasswordless.ts b/boilerplate/fullstack/astro/src/config/frontend/thirdpartypasswordless.ts index b6f703c3..eee227ba 100644 --- a/boilerplate/fullstack/astro/src/config/frontend/thirdpartypasswordless.ts +++ b/boilerplate/fullstack/astro/src/config/frontend/thirdpartypasswordless.ts @@ -1,33 +1,46 @@ -import ThirdPartyReact from "supertokens-auth-react/recipe/thirdparty/index.js"; -import PasswordlessReact from "supertokens-auth-react/recipe/passwordless/index.js"; -import Session from "supertokens-auth-react/recipe/session/index.js"; +import SuperTokens from "supertokens-web-js"; +import Session from "supertokens-web-js/recipe/session"; import { appInfo } from "./appInfo"; -import { ThirdPartyPreBuiltUI } from "supertokens-auth-react/recipe/thirdparty/prebuiltui.js"; -import { PasswordlessPreBuiltUI } from "supertokens-auth-react/recipe/passwordless/prebuiltui.js"; -export const frontendConfig = () => { - return { +export const getApiDomain = () => { + return appInfo.apiDomain; +}; + +export const getWebsiteDomain = () => { + return appInfo.websiteDomain; +}; + +export function initSuperTokensUI() { + (window as any).supertokensUIInit("supertokensui", { appInfo, recipeList: [ - ThirdPartyReact.init({ + (window as any).supertokensUIThirdParty.init({ signInAndUpFeature: { providers: [ - ThirdPartyReact.Github.init(), - ThirdPartyReact.Google.init(), - ThirdPartyReact.Apple.init(), + (window as any).supertokensUIThirdParty.Github.init(), + (window as any).supertokensUIThirdParty.Google.init(), + (window as any).supertokensUIThirdParty.Apple.init(), + (window as any).supertokensUIThirdParty.Twitter.init(), ], }, }), - PasswordlessReact.init({ + (window as any).supertokensUIPasswordless.init({ contactMethod: "EMAIL_OR_PHONE", }), - Session.init(), + (window as any).supertokensUISession.init(), ], - }; -}; - -export const recipeDetails = { - docsLink: "https://supertokens.com/docs/thirdpartypasswordless/introduction", -}; + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, + }); +} -export const PreBuiltUIList = [ThirdPartyPreBuiltUI, PasswordlessPreBuiltUI]; +export function initSuperTokensWebJS() { + SuperTokens.init({ + appInfo, + recipeList: [Session.init()], + }); +} diff --git a/boilerplate/fullstack/astro/src/env.d.ts b/boilerplate/fullstack/astro/src/env.d.ts index 4328e05b..61a7b230 100644 --- a/boilerplate/fullstack/astro/src/env.d.ts +++ b/boilerplate/fullstack/astro/src/env.d.ts @@ -1,7 +1,13 @@ /// +interface Session { + accessTokenPayload: any; + hasToken: boolean; + error: any; +} + declare namespace App { interface Locals { - session: any; + session: Session; } } diff --git a/boilerplate/fullstack/astro/src/layouts/Base.astro b/boilerplate/fullstack/astro/src/layouts/Base.astro index 51109b80..c480e21a 100644 --- a/boilerplate/fullstack/astro/src/layouts/Base.astro +++ b/boilerplate/fullstack/astro/src/layouts/Base.astro @@ -1,18 +1,61 @@ --- -import Root from "../components/Root"; import "../styles/app.css"; +import { ensureSuperTokensInit } from "../config/backend"; + +ensureSuperTokensInit(); --- - + Astro SuperTokens demo - - - +
+
+ +
+
+ + + separator +
+ +
- + \ No newline at end of file diff --git a/boilerplate/fullstack/astro/src/pages/api/auth.ts b/boilerplate/fullstack/astro/src/pages/api/auth.ts new file mode 100644 index 00000000..237e662a --- /dev/null +++ b/boilerplate/fullstack/astro/src/pages/api/auth.ts @@ -0,0 +1,18 @@ +import { ensureSuperTokensInit } from "../../config/backend"; +import { handleAuthAPIRequest } from "supertokens-node/custom"; +import type { APIRoute } from "astro"; + +const handleCall = handleAuthAPIRequest(); + +export const ALL: APIRoute = async ({ request }) => { + ensureSuperTokensInit(); + + try { + return await handleCall(request); + } catch (error) { + console.error(error); + return new Response(JSON.stringify({ error: "Internal server error" }), { + status: 500, + }); + } +}; diff --git a/boilerplate/fullstack/astro/src/pages/api/ping.ts b/boilerplate/fullstack/astro/src/pages/api/ping.ts new file mode 100644 index 00000000..428697b3 --- /dev/null +++ b/boilerplate/fullstack/astro/src/pages/api/ping.ts @@ -0,0 +1,10 @@ +import type { APIRoute } from "astro"; + +export const GET: APIRoute = async () => { + return new Response(JSON.stringify({ message: "pong" }), { + status: 200, + headers: { + "Content-Type": "application/json", + }, + }); +}; diff --git a/boilerplate/fullstack/astro/src/pages/api/sessioninfo.ts b/boilerplate/fullstack/astro/src/pages/api/sessioninfo.ts new file mode 100644 index 00000000..e6565821 --- /dev/null +++ b/boilerplate/fullstack/astro/src/pages/api/sessioninfo.ts @@ -0,0 +1,29 @@ +import type { APIRoute } from "astro"; +import { getSessionForSSR } from "supertokens-node/custom"; + +export const GET: APIRoute = async ({ request }) => { + const { accessTokenPayload, hasToken, error } = await getSessionForSSR(request); + + if (!hasToken || error) { + return new Response(JSON.stringify({ error: "Unauthorized" }), { + status: 401, + headers: { + "Content-Type": "application/json", + }, + }); + } + + return new Response( + JSON.stringify({ + sessionHandle: accessTokenPayload.sessionHandle, + userId: accessTokenPayload.sub, + accessTokenPayload, + }), + { + status: 200, + headers: { + "Content-Type": "application/json", + }, + } + ); +}; diff --git a/boilerplate/fullstack/astro/src/pages/auth.astro b/boilerplate/fullstack/astro/src/pages/auth.astro index 71e17924..479e0e32 100644 --- a/boilerplate/fullstack/astro/src/pages/auth.astro +++ b/boilerplate/fullstack/astro/src/pages/auth.astro @@ -1,8 +1,8 @@ --- import Base from "../layouts/Base.astro"; -import AuthComponent from "../components/Auth"; +import AuthComponent from "../components/auth.astro"; --- - + diff --git a/boilerplate/fullstack/astro/src/pages/auth/[...path]/[...route].astro b/boilerplate/fullstack/astro/src/pages/auth/[...path]/[...route].astro index 5532458d..c483115f 100644 --- a/boilerplate/fullstack/astro/src/pages/auth/[...path]/[...route].astro +++ b/boilerplate/fullstack/astro/src/pages/auth/[...path]/[...route].astro @@ -1,8 +1,8 @@ --- import Base from "../../../layouts/Base.astro"; -import AuthComponent from "../../../components/Auth"; +import AuthComponent from "../../../components/auth.astro"; --- - + diff --git a/boilerplate/fullstack/astro/src/pages/auth/[...route].astro b/boilerplate/fullstack/astro/src/pages/auth/[...route].astro index 7a095d2c..d9b0d616 100644 --- a/boilerplate/fullstack/astro/src/pages/auth/[...route].astro +++ b/boilerplate/fullstack/astro/src/pages/auth/[...route].astro @@ -1,8 +1,8 @@ --- import Base from "../../layouts/Base.astro"; -import AuthComponent from "../../components/Auth"; +import AuthComponent from "../../components/auth.astro"; --- - + diff --git a/boilerplate/fullstack/astro/src/pages/dashboard.astro b/boilerplate/fullstack/astro/src/pages/dashboard.astro new file mode 100644 index 00000000..6047f413 --- /dev/null +++ b/boilerplate/fullstack/astro/src/pages/dashboard.astro @@ -0,0 +1,8 @@ +--- +import Base from "../layouts/Base.astro"; +import DashboardContent from "../components/dashboard.astro"; +--- + + + + \ No newline at end of file diff --git a/boilerplate/fullstack/astro/src/pages/index.astro b/boilerplate/fullstack/astro/src/pages/index.astro index 9c372a80..82f5d339 100644 --- a/boilerplate/fullstack/astro/src/pages/index.astro +++ b/boilerplate/fullstack/astro/src/pages/index.astro @@ -1,22 +1,59 @@ --- import Base from "../layouts/Base.astro"; -import Home from "../components/Home"; -import { getSessionForSSR } from "supertokens-node/custom"; - -const { accessTokenPayload, hasToken, error } = await getSessionForSSR(Astro.request); - -const SSRData = { - accessTokenPayload, - hasToken, - error, -}; +import { Image } from "astro:assets"; --- - +
+ SuperTokens + x + Astro +
+
+ +
+

+ SuperTokens x Astro
example project +

+
+

Sign-in to continue

+

+ You're signed in already,
check out the Dashboard! 👇 +

+
+ +
+ +
+ + diff --git a/boilerplate/fullstack/astro/src/pages/sessioninfo.ts b/boilerplate/fullstack/astro/src/pages/sessioninfo.ts deleted file mode 100644 index e7531231..00000000 --- a/boilerplate/fullstack/astro/src/pages/sessioninfo.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { type APIRoute } from "astro"; -import { withSession } from "supertokens-node/custom"; - -export const GET: APIRoute = async ({ params, request }) => { - return withSession(request, async (err, session) => { - if (err) { - return new Response(JSON.stringify(err), { status: 500 }); - } - - return new Response( - JSON.stringify({ - note: "Fetch any data from your application for authenticated user after using verifySession middleware", - userId: session!.getUserId(), - sessionHandle: session!.getHandle(), - accessTokenPayload: session!.getAccessTokenPayload(), - }) - ); - }); -}; diff --git a/boilerplate/fullstack/astro/src/styles/app.css b/boilerplate/fullstack/astro/src/styles/app.css index a1461c4e..d73bc9c1 100644 --- a/boilerplate/fullstack/astro/src/styles/app.css +++ b/boilerplate/fullstack/astro/src/styles/app.css @@ -1,217 +1,179 @@ -html { - height: 100%; +/* Base styles */ +:root { + --primary-color: #ff9933; + --primary-hover: #ff8a15; + --success-color: #3eb655; + --success-bg: #e7ffed; + --border-color: #e0e0e0; } -.main { +@font-face { + font-family: Menlo; + src: url("./assets/fonts/MenloRegular.ttf"); +} + +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", + "Droid Sans", "Helvetica Neue", sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +* { + box-sizing: border-box; +} + +/* Layout */ +.app-container { display: flex; flex-direction: column; - align-items: center; - justify-content: center; + width: 100%; min-height: 100vh; + font-family: Rubik, sans-serif; } -.appContainer { - font-family: Rubik, sans-serif; +.fill { + display: flex; + flex-direction: column; + justify-content: center; + flex: 1; } -.appContainer * { - box-sizing: border-box; +/* Header */ +header { + border-bottom: 2px solid var(--border-color); + padding: 8px; } -.bold400 { - font-variation-settings: "wght" 400; +header nav { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; } -.bold500 { - font-variation-settings: "wght" 500; +header img { + width: 40px; } -.bold600 { - font-variation-settings: "wght" 600; +header ul { + display: flex; + gap: 12px; + list-style: none; + margin: 0; + padding: 0; } -.homeContainer { - min-height: 100vh; - min-width: 100vw; - background: url("../assets/images/background.png"); - background-size: cover; +header a { + color: var(--primary-color); + font-weight: 600; + text-decoration: none; +} +header a:hover { + color: var(--primary-hover); +} + +/* Home page */ +#home-container { + align-items: center; + min-height: calc(100vh - 58px); + background: url("./assets/images/background.png") center/cover; +} + +.logos { display: flex; - flex-direction: column; - justify-content: center; align-items: center; + gap: 20px; + padding-bottom: 80px; } -.bold700 { - font-variation-settings: "wght" 700; +.logos span { + font-size: 8rem; + font-weight: bold; +} + +.logos img { + height: 240px; + width: auto; } -.mainContainer { - box-shadow: 0px 0px 60px 0px rgba(0, 0, 0, 0.16); +/* Main content */ +.main-container { + box-shadow: 0 0 60px rgba(0, 0, 0, 0.16); width: min(635px, calc(100% - 24px)); border-radius: 16px; - margin-block-end: 159px; - background-color: #ffffff; + margin-bottom: 159px; + background-color: white; } -.successTitle { - line-height: 1; - padding-block: 26px; - background-color: #e7ffed; - text-align: center; - color: #3eb655; +.success-title { display: flex; justify-content: center; - align-items: flex-end; + align-items: center; + padding: 26px; + background-color: var(--success-bg); + color: var(--success-color); font-size: 20px; + line-height: 1; } -.successIcon { +.success-title img { margin-right: 8px; } -.innerContent { - padding-block: 48px; +.inner-content { + padding: 48px; text-align: center; display: flex; flex-direction: column; align-items: center; } -.userId { +/* User ID display */ +#user-id { position: relative; - padding: 14px 17px; - border-image-slice: 1; width: min(430px, calc(100% - 30px)); - margin-inline: auto; - margin-block: 11px 23px; + margin: 11px auto 23px; + padding: 14px 17px; border-radius: 9px; + font-family: Menlo, monospace; line-height: 1; - font-family: Menlo, serif; cursor: text; + border: 3px solid var(--primary-color); } -.userId:before { - content: ""; - position: absolute; - inset: 0; - border-radius: 9px; - padding: 2px; - background: linear-gradient(90.31deg, #ff9933 0.11%, #ff3f33 99.82%); - mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); - -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); - -webkit-mask-composite: xor; - mask-composite: exclude; -} - -.topBand, -.bottomBand { - border-radius: inherit; -} - -.topBand { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; -} - -.bottomBand { - border-top-left-radius: 0; - border-top-right-radius: 0; +/* Buttons and navigation */ +.buttons { + display: flex; + gap: 4px; } -.sessionButton { - padding-left: 13px; - padding-right: 13px; - padding-top: 8px; - padding-bottom: 8px; - cursor: pointer; +.dashboard-button { + padding: 8px 13px; + background: var(--primary-color); + border: 1px solid var(--primary-hover); + border-radius: 6px; + box-shadow: 0 3px 6px rgba(255, 153, 51, 0.16); color: white; font-weight: bold; - font-size: 17px; - - box-sizing: border-box; - background: #ff9933; - border: 1px solid #ff8a15; - box-shadow: 0px 3px 6px rgba(255, 153, 51, 0.16); - border-radius: 6px; font-size: 16px; -} - -.bottomCTAContainer { - display: flex; - justify-content: flex-end; - padding-inline: 21px; - background-color: #212d4f; -} - -.viewCode { - padding-block: 11px; - color: #bac9f5; - cursor: pointer; - font-size: 14px; -} - -.bottomLinksContainer { - display: grid; - grid-template-columns: repeat(4, auto); - margin-bottom: 22px; -} - -.linksContainerLink { - display: flex; - align-items: center; - margin-inline-end: 68px; cursor: pointer; text-decoration: none; - color: #000000; -} - -.signOutLink { - border: 0; } -.linksContainerLink:last-child { - margin-right: 0; +/* Footer */ +footer { + padding: 10px; } -.truncate { - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; +footer a { + color: var(--primary-color); + font-weight: 600; + /* text-decoration: none; */ } -.separatorLine { - max-width: 100%; -} - -.linkIcon { - width: 15px; - margin-right: 5px; -} - -@media screen and (max-width: 768px) { - .bottomLinksContainer { - grid-template-columns: repeat(2, 1fr); - column-gap: 64px; - row-gap: 34px; - } - - .linksContainerLink { - margin-inline-end: 0; - } - - .separatorLine { - max-width: 200px; - } -} - -@media screen and (max-width: 480px) { - .homeContainer { - justify-content: start; - padding-block-start: 25px; - } - - .mainContainer { - margin-block-end: 90px; - } +footer a:hover { + color: var(--primary-hover); } diff --git a/boilerplate/fullstack/sveltekit/README.md b/boilerplate/fullstack/sveltekit/README.md index a36efa79..018c1667 100644 --- a/boilerplate/fullstack/sveltekit/README.md +++ b/boilerplate/fullstack/sveltekit/README.md @@ -1,98 +1,116 @@ -![SuperTokens banner](https://raw.githubusercontent.com/supertokens/supertokens-logo/master/images/Artboard%20%E2%80%93%2027%402x.png) +# SuperTokens + SvelteKit Demo -# SuperTokens with SvelteKit +A demo implementation of [SuperTokens](https://supertokens.com/) with [SvelteKit](https://kit.svelte.dev/), using SuperTokens' prebuilt UI. -This demo app demonstrates how to integrate SuperTokens into an SvelteKit application. +## General Info -Features: +This project aims to demonstrate how to integrate SuperTokens into a SvelteKit application using our prebuilt UI. Its primary purpose is to serve as an educational tool, but it can also be used as a starting point for your own project. -- Initializes SuperTokens with frontend and backend configurations -- Creates a frontend page to handle authentication-related tasks -- Integrates the SuperTokens' pre-built login UI for secure user authentication -- Protects frontend to ensure only authenticated users can access the dashboard -- Exposes the SuperTokens authentication APIs used by frontend widgets +## Repo Structure -## Project structure +### Source ```txt -📦[your-app-name] -┣ 📂static -┣ 📂src -┃ ┣ 📂config -┃ ┃ ┣ 📜appInfo.ts -┃ ┃ ┣ 📜backend.ts -┃ ┃ ┗ 📜frontend.ts -┃ ┣ 📂routes +📦src +┣ 📂config +┃ ┣ 📜appInfo.ts --> App info / config, reused across both frontend and backend +┃ ┣ 📜backend.ts --> Backend initialization and configuration +┃ ┗ 📜frontend.ts --> Frontend initialization and configuration +┣ 📂routes +┃ ┣ 📂api ┃ ┃ ┣ 📂auth -┃ ┃ ┃ ┣ 📂[...anything] -┃ ┃ ┃ ┃ ┗ 📜+page.svelte -┃ ┃ ┃ ┗ 📜+page.svelte -┃ ┃ ┣ 📂api -┃ ┃ ┃ ┣ 📂auth -┃ ┃ ┃ ┃ ┣ 📂[...path] -┃ ┃ ┃ ┃ ┃ ┗ 📜+server.ts -┃ ┃ ┃ ┃ ┗ 📜+server.ts -┃ ┃ ┣ 📜+page.svelte +┃ ┃ ┃ ┗ 📜[...path].ts --> Catch-all Auth API route +┃ ┃ ┣ 📂ping +┃ ┃ ┃ ┗ 📜+server.ts --> Public route (no auth required) ┃ ┃ ┗ 📂sessioninfo -┃ ┃ ┃ ┗ 📜+server.ts -┃ ┗ 📜superTokensHelpers.ts -┣ 📜svelte.config.js -┣ 📜package.json -┣ 📜README.md -┗ 📜tsconfig.json +┃ ┃ ┗ 📜+server.ts --> Session information endpoint (auth required) +┃ ┣ 📂auth +┃ ┃ ┣ 📜+page.svelte --> SuperTokens Auth UI page +┃ ┃ ┗ 📂[...anything] +┃ ┃ ┗ 📜+page.svelte --> Catch-all route for auth paths +┃ ┣ 📂dashboard +┃ ┃ ┗ 📜+page.svelte --> Dashboard page +┃ ┣ 📜+layout.svelte --> Main app layout with auth initialization +┃ ┗ 📜+page.svelte --> Home page with auth status +┗ 📂styles +┃ ┗ 📜app.css --> Global styles +┗ 📜app.html --> Base HTML template ``` -Let's explore the important files: +> Note: the nested routes are required due to how SvelteKit handles routing, and how SuperTokens expects wildcard routes. -| Directory/File | Description | -| -------------- | ------------------------------------------------------------------------------------- | -| **src** | Contains configuration files, pages and components for your application. | -| **src/config** | Contains configuration files for your application. | -| | `appInfo.ts` : Includes information about your application reused throughout the app. | -| | `backend.ts` : Backend-related configuration, including settings for SuperTokens. | -| | `frontend.ts` : Frontend configuration, including settings for SuperTokens. | -| **routes** | Contains route files for your application. | -| | `+page.svelte` : Represents the default route or landing page. | -| | `auth/...` : Deals with authentication callbacks using SuperTokens. | -| | `api/auth...` : Deals with authentication routes or components using SuperTokens. | +### Config -## Run application locally +#### SvelteKit -Follow the steps outlined below to run the application locally: +The project is a standard SvelteKit application. -1. Change directory to the **[your-app-name]** folder. +You can customize the SvelteKit configuration in `svelte.config.js`. Refer to the [SvelteKit configuration docs](https://kit.svelte.dev/docs/configuration) for more options. - ```shell - cd your-app-name - ``` +#### SuperTokens -2. Run the application with the command below: +SuperTokens configuration is managed through recipe-specific files in the `config/` directory. Each recipe comes in two parts (due to Astro being treated as a full-stack framework): - ```shell - npm run dev - ``` +- `frontend.ts` - Frontend config +- `backend.ts` - Backend config -## How to use +The `appInfo.ts` file is used to configure the app info / config, and is reused across both frontend and backend. -### Using `create-supertokens-app` +## Application Flow -- Run the following command +The application uses SvelteKit's file-based routing and consists of four main parts: -```bash -npx create-supertokens-app@latest --frontend=sveltekit -``` +1. **Entry Point (`+page.svelte`)** + + - Public landing page + - Navigation to auth and dashboard + - Project information display + +2. **Auth Routes (`/auth/*`)** + + - Handles all authentication flows using React components + - Uses SuperTokens' pre-built UI + - Manages login, signup, and password reset + - Social login integration (when configured) + +3. **Protected Dashboard (`/dashboard`)** + + - Only accessible to authenticated users + - Displays user information + - Provides authenticated functionality + - API integration example + +4. **API Routes (`/api/*`)** + - Protected session info endpoint + - Public ping endpoint + - Server-side session validation + +When a user visits the application, they start at the home page (`/`). They can choose to authenticate through the `/auth` routes, and once authenticated, they gain access to the protected dashboard. The session state is managed throughout the application using SuperTokens' session management. + +## Customizations + +If you want to customize the default auth UI, you have two options: + +1. Refer to the [docs](https://supertokens.com/docs/thirdpartyemailpassword/advanced-customizations/react-component-override/usage) on how to customize the pre-built UI. +2. Roll your own UI by choosing "Custom UI" in the right sidebar in the [docs](https://supertokens.com/docs/thirdpartyemailpassword/quickstart/frontend-setup). + +## Additional Resources + +- Custom UI Blog post: https://supertokens.medium.com/adding-social-login-to-your-website-with-supertokens-custom-ui-only-5fa4d7ab6402 +- Awesome SuperTokens: https://github.com/kohasummons/awesome-supertokens + +## Contributing -- Follow the instructions on screen +Please refer to the [CONTRIBUTING.md](https://github.com/supertokens/create-supertokens-app/blob/master/CONTRIBUTING.md) file in the root of the [`create-supertokens-app`](https://github.com/supertokens/create-supertokens-app) repo. -## Author +## Contributing -Created with :heart: by the folks at supertokens.com. +Please refer to the [CONTRIBUTING.md](https://github.com/supertokens/create-supertokens-app/blob/master/CONTRIBUTING.md) file in the root of the [`create-supertokens-app`](https://github.com/supertokens/create-supertokens-app) repo. -## License +## Contact us -This project is licensed under the Apache 2.0 license. +For any questions, or support requests, please email us at team@supertokens.io, or join our [Discord](https://supertokens.io/discord) server. -## Notes +## Authors -- To know more about how this app works and to learn how to customise it based on your use cases refer to the [SuperTokens Documentation](https://supertokens.com/docs/guides) -- We have provided development OAuth keys for the various built-in third party providers in the `/app/config/backend.ts` file. Feel free to use them for development purposes, but **please create your own keys for production use**. +Created with :heart: by the folks at SuperTokens.io. diff --git a/boilerplate/fullstack/sveltekit/package.json b/boilerplate/fullstack/sveltekit/package.json index 00ff60f7..0bdfc9d8 100644 --- a/boilerplate/fullstack/sveltekit/package.json +++ b/boilerplate/fullstack/sveltekit/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "private": true, "scripts": { - "dev": "vite dev", + "start": "vite dev", "build": "vite build", "preview": "vite preview", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", @@ -12,27 +12,27 @@ "format": "prettier --write ." }, "devDependencies": { - "@sveltejs/adapter-auto": "^3.0.0", - "@sveltejs/kit": "^2.0.0", - "@sveltejs/vite-plugin-svelte": "^3.0.0", - "@types/eslint": "^9.6.0", - "eslint": "^9.0.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-svelte": "^2.36.0", - "globals": "^15.0.0", - "prettier": "^3.1.1", - "prettier-plugin-svelte": "^3.1.2", - "svelte": "^4.2.7", - "svelte-check": "^4.0.0", + "@sveltejs/adapter-auto": "latest", + "@sveltejs/kit": "latest", + "@sveltejs/vite-plugin-svelte": "latest", + "@types/eslint": "latest", + "eslint": "latest", + "eslint-config-prettier": "latest", + "eslint-plugin-svelte": "latest", + "globals": "latest", + "prettier": "latest", + "prettier-plugin-svelte": "latest", + "svelte": "latest", + "svelte-check": "latest", "typescript": "^5.0.0", "typescript-eslint": "^8.0.0", - "vite": "^5.0.3" + "vite": "latest" }, "type": "module", "dependencies": { "jsonwebtoken": "^9.0.2", "jwks-rsa": "^3.1.0", "supertokens-node": "latest", - "supertokens-web-js": "^0.13.0" + "supertokens-web-js": "latest" } } diff --git a/boilerplate/fullstack/sveltekit/src/app.html b/boilerplate/fullstack/sveltekit/src/app.html index 88592f7a..56e029ed 100644 --- a/boilerplate/fullstack/sveltekit/src/app.html +++ b/boilerplate/fullstack/sveltekit/src/app.html @@ -2,7 +2,7 @@ - + %sveltekit.head% diff --git a/boilerplate/fullstack/sveltekit/src/config/frontend/all_auth.ts b/boilerplate/fullstack/sveltekit/src/config/frontend/all_auth.ts index f87b9869..a9d88a8b 100644 --- a/boilerplate/fullstack/sveltekit/src/config/frontend/all_auth.ts +++ b/boilerplate/fullstack/sveltekit/src/config/frontend/all_auth.ts @@ -2,6 +2,14 @@ import SuperTokens from "supertokens-web-js"; import Session from "supertokens-web-js/recipe/session"; import { appInfo } from "./appInfo"; +export const getApiDomain = () => { + return appInfo.apiDomain; +}; + +export const getWebsiteDomain = () => { + return appInfo.websiteDomain; +}; + export function initSuperTokensUI() { (window as any).supertokensUIInit("supertokensui", { appInfo, @@ -22,6 +30,12 @@ export function initSuperTokensUI() { }), (window as any).supertokensUISession.init(), ], + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, }); } diff --git a/boilerplate/fullstack/sveltekit/src/config/frontend/emailpassword.ts b/boilerplate/fullstack/sveltekit/src/config/frontend/emailpassword.ts index 85b37e5f..6b91d541 100644 --- a/boilerplate/fullstack/sveltekit/src/config/frontend/emailpassword.ts +++ b/boilerplate/fullstack/sveltekit/src/config/frontend/emailpassword.ts @@ -2,10 +2,24 @@ import SuperTokens from "supertokens-web-js"; import Session from "supertokens-web-js/recipe/session"; import { appInfo } from "./appInfo"; +export const getApiDomain = () => { + return appInfo.apiDomain; +}; + +export const getWebsiteDomain = () => { + return appInfo.websiteDomain; +}; + export function initSuperTokensUI() { (window as any).supertokensUIInit("supertokensui", { appInfo, recipeList: [(window as any).supertokensUIEmailPassword.init(), (window as any).supertokensUISession.init()], + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, }); } diff --git a/boilerplate/fullstack/sveltekit/src/config/frontend/multifactorauth.ts b/boilerplate/fullstack/sveltekit/src/config/frontend/multifactorauth.ts index e77550e9..99fb8748 100644 --- a/boilerplate/fullstack/sveltekit/src/config/frontend/multifactorauth.ts +++ b/boilerplate/fullstack/sveltekit/src/config/frontend/multifactorauth.ts @@ -4,6 +4,14 @@ import EmailVerification from "supertokens-web-js/recipe/emailverification"; import MultiFactorAuth from "supertokens-web-js/recipe/multifactorauth"; import { appInfo } from "./appInfo"; +export const getApiDomain = () => { + return appInfo.apiDomain; +}; + +export const getWebsiteDomain = () => { + return appInfo.websiteDomain; +}; + export function initSuperTokensUI() { (window as any).supertokensUIInit("supertokensui", { appInfo, @@ -31,6 +39,12 @@ export function initSuperTokensUI() { (window as any).supertokensUITOTP.init(), (window as any).supertokensUISession.init(), ], + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, }); } diff --git a/boilerplate/fullstack/sveltekit/src/config/frontend/passwordless.ts b/boilerplate/fullstack/sveltekit/src/config/frontend/passwordless.ts index 4296df45..892d71b0 100644 --- a/boilerplate/fullstack/sveltekit/src/config/frontend/passwordless.ts +++ b/boilerplate/fullstack/sveltekit/src/config/frontend/passwordless.ts @@ -2,6 +2,14 @@ import SuperTokens from "supertokens-web-js"; import Session from "supertokens-web-js/recipe/session"; import { appInfo } from "./appInfo"; +export const getApiDomain = () => { + return appInfo.apiDomain; +}; + +export const getWebsiteDomain = () => { + return appInfo.websiteDomain; +}; + export function initSuperTokensUI() { (window as any).supertokensUIInit("supertokensui", { appInfo, @@ -11,6 +19,12 @@ export function initSuperTokensUI() { }), (window as any).supertokensUISession.init(), ], + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, }); } diff --git a/boilerplate/fullstack/sveltekit/src/config/frontend/thirdparty.ts b/boilerplate/fullstack/sveltekit/src/config/frontend/thirdparty.ts index ba9dd574..4cb00e23 100644 --- a/boilerplate/fullstack/sveltekit/src/config/frontend/thirdparty.ts +++ b/boilerplate/fullstack/sveltekit/src/config/frontend/thirdparty.ts @@ -2,6 +2,14 @@ import SuperTokens from "supertokens-web-js"; import Session from "supertokens-web-js/recipe/session"; import { appInfo } from "./appInfo"; +export const getApiDomain = () => { + return appInfo.apiDomain; +}; + +export const getWebsiteDomain = () => { + return appInfo.websiteDomain; +}; + export function initSuperTokensUI() { (window as any).supertokensUIInit("supertokensui", { appInfo, @@ -18,6 +26,12 @@ export function initSuperTokensUI() { }), (window as any).supertokensUISession.init(), ], + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, }); } diff --git a/boilerplate/fullstack/sveltekit/src/config/frontend/thirdpartyemailpassword.ts b/boilerplate/fullstack/sveltekit/src/config/frontend/thirdpartyemailpassword.ts index c82fbbe2..7f49d22d 100644 --- a/boilerplate/fullstack/sveltekit/src/config/frontend/thirdpartyemailpassword.ts +++ b/boilerplate/fullstack/sveltekit/src/config/frontend/thirdpartyemailpassword.ts @@ -2,6 +2,14 @@ import SuperTokens from "supertokens-web-js"; import Session from "supertokens-web-js/recipe/session"; import { appInfo } from "./appInfo"; +export const getApiDomain = () => { + return appInfo.apiDomain; +}; + +export const getWebsiteDomain = () => { + return appInfo.websiteDomain; +}; + export function initSuperTokensUI() { (window as any).supertokensUIInit("supertokensui", { appInfo, @@ -19,6 +27,12 @@ export function initSuperTokensUI() { }), (window as any).supertokensUISession.init(), ], + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, }); } diff --git a/boilerplate/fullstack/sveltekit/src/config/frontend/thirdpartypasswordless.ts b/boilerplate/fullstack/sveltekit/src/config/frontend/thirdpartypasswordless.ts index 31cc1e4f..eee227ba 100644 --- a/boilerplate/fullstack/sveltekit/src/config/frontend/thirdpartypasswordless.ts +++ b/boilerplate/fullstack/sveltekit/src/config/frontend/thirdpartypasswordless.ts @@ -2,6 +2,14 @@ import SuperTokens from "supertokens-web-js"; import Session from "supertokens-web-js/recipe/session"; import { appInfo } from "./appInfo"; +export const getApiDomain = () => { + return appInfo.apiDomain; +}; + +export const getWebsiteDomain = () => { + return appInfo.websiteDomain; +}; + export function initSuperTokensUI() { (window as any).supertokensUIInit("supertokensui", { appInfo, @@ -21,6 +29,12 @@ export function initSuperTokensUI() { }), (window as any).supertokensUISession.init(), ], + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return "/dashboard"; + } + return undefined; + }, }); } diff --git a/boilerplate/fullstack/sveltekit/src/routes/+layout.svelte b/boilerplate/fullstack/sveltekit/src/routes/+layout.svelte new file mode 100644 index 00000000..76995d90 --- /dev/null +++ b/boilerplate/fullstack/sveltekit/src/routes/+layout.svelte @@ -0,0 +1,57 @@ + + + + + + + SvelteKit SuperTokens demo + +
+
+ +
+
+ + + separator +
+ +
diff --git a/boilerplate/fullstack/sveltekit/src/routes/+page.svelte b/boilerplate/fullstack/sveltekit/src/routes/+page.svelte index 7bc76ecf..5d75f6e6 100644 --- a/boilerplate/fullstack/sveltekit/src/routes/+page.svelte +++ b/boilerplate/fullstack/sveltekit/src/routes/+page.svelte @@ -1,5 +1,5 @@ -
-
-

Hello

- - {#if session} -
- UserId: -

{userId}

- - -
- {:else} -
-

- Visit the - SuperTokens tutorial to learn how to build Auth under - a day. -

- +
+ SuperTokens + x + SvelteKit +
+
+
+

+ SuperTokens x SvelteKit
example project +

+
+ {#if session} +

+ You're signed in already,
check out the Dashboard! 👇 +

+ {:else} +

Sign-in to continue

+ {/if}
- {/if} -
-
+ +
+ - diff --git a/boilerplate/fullstack/sveltekit/src/routes/[...anything]/+server.ts b/boilerplate/fullstack/sveltekit/src/routes/[...anything]/+server.ts deleted file mode 100644 index e1409c91..00000000 --- a/boilerplate/fullstack/sveltekit/src/routes/[...anything]/+server.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { json } from "@sveltejs/kit"; -import { handleAuthAPIRequest } from "supertokens-node/custom"; -import { ensureSuperTokensInit } from "../../config/backend"; - -ensureSuperTokensInit(); - -const handleCall = handleAuthAPIRequest(); - -export async function fallback({ request }) { - try { - return await handleCall(request); - } catch (err) { - return json({ error: "Internal server error" }, { status: 500 }); - } -} diff --git a/boilerplate/fullstack/sveltekit/src/routes/api/ping/+server.ts b/boilerplate/fullstack/sveltekit/src/routes/api/ping/+server.ts new file mode 100644 index 00000000..036fd4c1 --- /dev/null +++ b/boilerplate/fullstack/sveltekit/src/routes/api/ping/+server.ts @@ -0,0 +1,8 @@ +export async function GET() { + return new Response(JSON.stringify({ message: "pong" }), { + status: 200, + headers: { + "Content-Type": "application/json", + }, + }); +} diff --git a/boilerplate/fullstack/sveltekit/src/routes/sessioninfo/+server.ts b/boilerplate/fullstack/sveltekit/src/routes/api/sessioninfo/+server.ts similarity index 100% rename from boilerplate/fullstack/sveltekit/src/routes/sessioninfo/+server.ts rename to boilerplate/fullstack/sveltekit/src/routes/api/sessioninfo/+server.ts diff --git a/boilerplate/fullstack/sveltekit/src/routes/auth/+page.svelte b/boilerplate/fullstack/sveltekit/src/routes/auth/+page.svelte index 5830b344..70d89086 100644 --- a/boilerplate/fullstack/sveltekit/src/routes/auth/+page.svelte +++ b/boilerplate/fullstack/sveltekit/src/routes/auth/+page.svelte @@ -14,9 +14,7 @@ document.body.appendChild(script); }; - loadScript( - "${jsdeliveryprebuiltuiurl}" - ); + loadScript("${jsdeliveryprebuiltuiurl}") }); diff --git a/boilerplate/fullstack/sveltekit/src/routes/auth/[...anything]/+page.svelte b/boilerplate/fullstack/sveltekit/src/routes/auth/[...anything]/+page.svelte index f528de88..0fa6d59c 100644 --- a/boilerplate/fullstack/sveltekit/src/routes/auth/[...anything]/+page.svelte +++ b/boilerplate/fullstack/sveltekit/src/routes/auth/[...anything]/+page.svelte @@ -14,9 +14,7 @@ document.body.appendChild(script); }; - loadScript( - "${jsdeliveryprebuiltuiurl}" - ); + loadScript("${jsdeliveryprebuiltuiurl}") }); diff --git a/boilerplate/fullstack/sveltekit/src/routes/dashboard/+page.svelte b/boilerplate/fullstack/sveltekit/src/routes/dashboard/+page.svelte new file mode 100644 index 00000000..33a539fd --- /dev/null +++ b/boilerplate/fullstack/sveltekit/src/routes/dashboard/+page.svelte @@ -0,0 +1,60 @@ + + +
+
+ Login successful + Login successful +
+
+
Your userID is:
+
+ {userId} +
+
+ + +
+
+
\ No newline at end of file diff --git a/boilerplate/fullstack/sveltekit/src/styles/app.css b/boilerplate/fullstack/sveltekit/src/styles/app.css new file mode 100644 index 00000000..d73bc9c1 --- /dev/null +++ b/boilerplate/fullstack/sveltekit/src/styles/app.css @@ -0,0 +1,179 @@ +/* Base styles */ +:root { + --primary-color: #ff9933; + --primary-hover: #ff8a15; + --success-color: #3eb655; + --success-bg: #e7ffed; + --border-color: #e0e0e0; +} + +@font-face { + font-family: Menlo; + src: url("./assets/fonts/MenloRegular.ttf"); +} + +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", + "Droid Sans", "Helvetica Neue", sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +* { + box-sizing: border-box; +} + +/* Layout */ +.app-container { + display: flex; + flex-direction: column; + width: 100%; + min-height: 100vh; + font-family: Rubik, sans-serif; +} + +.fill { + display: flex; + flex-direction: column; + justify-content: center; + flex: 1; +} + +/* Header */ +header { + border-bottom: 2px solid var(--border-color); + padding: 8px; +} + +header nav { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; +} + +header img { + width: 40px; +} + +header ul { + display: flex; + gap: 12px; + list-style: none; + margin: 0; + padding: 0; +} + +header a { + color: var(--primary-color); + font-weight: 600; + text-decoration: none; +} + +header a:hover { + color: var(--primary-hover); +} + +/* Home page */ +#home-container { + align-items: center; + min-height: calc(100vh - 58px); + background: url("./assets/images/background.png") center/cover; +} + +.logos { + display: flex; + align-items: center; + gap: 20px; + padding-bottom: 80px; +} + +.logos span { + font-size: 8rem; + font-weight: bold; +} + +.logos img { + height: 240px; + width: auto; +} + +/* Main content */ +.main-container { + box-shadow: 0 0 60px rgba(0, 0, 0, 0.16); + width: min(635px, calc(100% - 24px)); + border-radius: 16px; + margin-bottom: 159px; + background-color: white; +} + +.success-title { + display: flex; + justify-content: center; + align-items: center; + padding: 26px; + background-color: var(--success-bg); + color: var(--success-color); + font-size: 20px; + line-height: 1; +} + +.success-title img { + margin-right: 8px; +} + +.inner-content { + padding: 48px; + text-align: center; + display: flex; + flex-direction: column; + align-items: center; +} + +/* User ID display */ +#user-id { + position: relative; + width: min(430px, calc(100% - 30px)); + margin: 11px auto 23px; + padding: 14px 17px; + border-radius: 9px; + font-family: Menlo, monospace; + line-height: 1; + cursor: text; + border: 3px solid var(--primary-color); +} + +/* Buttons and navigation */ +.buttons { + display: flex; + gap: 4px; +} + +.dashboard-button { + padding: 8px 13px; + background: var(--primary-color); + border: 1px solid var(--primary-hover); + border-radius: 6px; + box-shadow: 0 3px 6px rgba(255, 153, 51, 0.16); + color: white; + font-weight: bold; + font-size: 16px; + cursor: pointer; + text-decoration: none; +} + +/* Footer */ +footer { + padding: 10px; +} + +footer a { + color: var(--primary-color); + font-weight: 600; + /* text-decoration: none; */ +} + +footer a:hover { + color: var(--primary-hover); +} diff --git a/boilerplate/fullstack/sveltekit/static/React.svg b/boilerplate/fullstack/sveltekit/static/React.svg new file mode 100644 index 00000000..8aefdab3 --- /dev/null +++ b/boilerplate/fullstack/sveltekit/static/React.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/boilerplate/fullstack/sveltekit/static/ST.svg b/boilerplate/fullstack/sveltekit/static/ST.svg new file mode 100644 index 00000000..30b435bd --- /dev/null +++ b/boilerplate/fullstack/sveltekit/static/ST.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/boilerplate/fullstack/sveltekit/static/assets/fonts/MenloRegular.ttf b/boilerplate/fullstack/sveltekit/static/assets/fonts/MenloRegular.ttf new file mode 100644 index 00000000..033dc6d2 Binary files /dev/null and b/boilerplate/fullstack/sveltekit/static/assets/fonts/MenloRegular.ttf differ diff --git a/boilerplate/fullstack/sveltekit/static/assets/images/arrow-right-icon.svg b/boilerplate/fullstack/sveltekit/static/assets/images/arrow-right-icon.svg new file mode 100644 index 00000000..95aa1fec --- /dev/null +++ b/boilerplate/fullstack/sveltekit/static/assets/images/arrow-right-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/boilerplate/fullstack/sveltekit/static/assets/images/background.png b/boilerplate/fullstack/sveltekit/static/assets/images/background.png new file mode 100644 index 00000000..2147c15c Binary files /dev/null and b/boilerplate/fullstack/sveltekit/static/assets/images/background.png differ diff --git a/boilerplate/fullstack/sveltekit/static/assets/images/blogs-icon.svg b/boilerplate/fullstack/sveltekit/static/assets/images/blogs-icon.svg new file mode 100644 index 00000000..a2fc9dd6 --- /dev/null +++ b/boilerplate/fullstack/sveltekit/static/assets/images/blogs-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/boilerplate/fullstack/sveltekit/static/assets/images/celebrate-icon.svg b/boilerplate/fullstack/sveltekit/static/assets/images/celebrate-icon.svg new file mode 100644 index 00000000..3b40b1ef --- /dev/null +++ b/boilerplate/fullstack/sveltekit/static/assets/images/celebrate-icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/boilerplate/fullstack/sveltekit/static/assets/images/guide-icon.svg b/boilerplate/fullstack/sveltekit/static/assets/images/guide-icon.svg new file mode 100644 index 00000000..bd85af72 --- /dev/null +++ b/boilerplate/fullstack/sveltekit/static/assets/images/guide-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/boilerplate/fullstack/sveltekit/static/assets/images/index.ts b/boilerplate/fullstack/sveltekit/static/assets/images/index.ts new file mode 100644 index 00000000..7adf036c --- /dev/null +++ b/boilerplate/fullstack/sveltekit/static/assets/images/index.ts @@ -0,0 +1,8 @@ +import SeparatorLine from "./separator-line.svg"; +import ArrowRight from "./arrow-right-icon.svg"; +import SignOutIcon from "./sign-out-icon.svg"; +import GuideIcon from "./guide-icon.svg"; +import BlogsIcon from "./blogs-icon.svg"; +import CelebrateIcon from "./celebrate-icon.svg"; + +export { SeparatorLine, ArrowRight, SignOutIcon, GuideIcon, BlogsIcon, CelebrateIcon }; diff --git a/boilerplate/fullstack/sveltekit/static/assets/images/separator-line.svg b/boilerplate/fullstack/sveltekit/static/assets/images/separator-line.svg new file mode 100644 index 00000000..7127a00d --- /dev/null +++ b/boilerplate/fullstack/sveltekit/static/assets/images/separator-line.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/boilerplate/fullstack/sveltekit/static/assets/images/sign-out-icon.svg b/boilerplate/fullstack/sveltekit/static/assets/images/sign-out-icon.svg new file mode 100644 index 00000000..6cc4f85f --- /dev/null +++ b/boilerplate/fullstack/sveltekit/static/assets/images/sign-out-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/boilerplate/fullstack/sveltekit/static/favicon.ico b/boilerplate/fullstack/sveltekit/static/favicon.ico new file mode 100644 index 00000000..a11777cc Binary files /dev/null and b/boilerplate/fullstack/sveltekit/static/favicon.ico differ diff --git a/boilerplate/fullstack/sveltekit/static/index.html b/boilerplate/fullstack/sveltekit/static/index.html new file mode 100644 index 00000000..6f1f7cb5 --- /dev/null +++ b/boilerplate/fullstack/sveltekit/static/index.html @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + React App + + + +
+ + diff --git a/boilerplate/fullstack/sveltekit/static/manifest.json b/boilerplate/fullstack/sveltekit/static/manifest.json new file mode 100644 index 00000000..f01493ff --- /dev/null +++ b/boilerplate/fullstack/sveltekit/static/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/boilerplate/fullstack/sveltekit/static/robots.txt b/boilerplate/fullstack/sveltekit/static/robots.txt new file mode 100644 index 00000000..e9e57dc4 --- /dev/null +++ b/boilerplate/fullstack/sveltekit/static/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/boilerplate/fullstack/sveltekit/static/svelte_logo.png b/boilerplate/fullstack/sveltekit/static/svelte_logo.png new file mode 100644 index 00000000..1959038c Binary files /dev/null and b/boilerplate/fullstack/sveltekit/static/svelte_logo.png differ diff --git a/lib/build/config.js b/lib/build/config.js index 3a0f824b..fdc633cd 100644 --- a/lib/build/config.js +++ b/lib/build/config.js @@ -173,6 +173,32 @@ export async function getFrontendOptions({ manager }) { setup: [`${manager} install`], }, }, + { + isFullStack: true, + value: "astro-react", + displayName: "Astro (using React)", + location: { + main: "fullstack/astro-react", + config: { + frontend: [ + { + configFiles: "/src/config/frontend", + finalConfig: "/src/config/frontend.ts", + }, + ], + backend: [ + { + configFiles: "/src/config/backend", + finalConfig: "/src/config/backend.ts", + }, + ], + }, + }, + script: { + run: [`${manager} run dev`], + setup: [`${manager} install`], + }, + }, { isFullStack: true, value: "sveltekit", @@ -253,7 +279,7 @@ export async function getFrontendOptions({ manager }) { value: "solid", displayName: "SolidJS", location: { - main: "frontend/solid-prebuilt", + main: "frontend/solid", config: [{ finalConfig: "/src/config.ts", configFiles: "/config" }], }, script: { diff --git a/lib/build/questionUtils.js b/lib/build/questionUtils.js index 3d892b1d..ec2ed827 100644 --- a/lib/build/questionUtils.js +++ b/lib/build/questionUtils.js @@ -78,6 +78,7 @@ export function shouldSkipBackendQuestion(answers, userFlags) { frontEndInFlags === "capacitor" || frontEndInFlags === "remix" || frontEndInFlags === "astro" || + frontEndInFlags === "astro-react" || frontEndInFlags === "sveltekit" || frontEndInFlags === "nuxtjs" ); @@ -87,6 +88,7 @@ export function shouldSkipBackendQuestion(answers, userFlags) { answers.frontend === "capacitor" || answers.frontend === "remix" || answers.frontend === "astro" || + answers.frontend === "astro-react" || answers.frontend === "sveltekit" || answers.frontend === "nuxtjs" ); diff --git a/lib/build/types.js b/lib/build/types.js index d3720f5f..90bbc5dc 100644 --- a/lib/build/types.js +++ b/lib/build/types.js @@ -27,6 +27,9 @@ export const allFrontends = [ { id: "astro", }, + { + id: "astro-react", + }, { id: "remix", }, diff --git a/lib/ts/config.ts b/lib/ts/config.ts index ebc2a2c1..f162435d 100644 --- a/lib/ts/config.ts +++ b/lib/ts/config.ts @@ -176,6 +176,32 @@ export async function getFrontendOptions({ manager }: UserFlags): Promise