├── .gitignore ├── .npmrc ├── README.md ├── magic-link ├── Magic_Link_Otp_SignIn_Flow_SSR.excalidraw ├── Magic_Link_Otp_SignIn_Flow_SSR.excalidraw.png ├── README.md ├── auth-ui │ └── react │ │ ├── .env.example │ │ ├── .gitignore │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── pnpm-lock.yaml │ │ ├── public │ │ └── vite.svg │ │ ├── src │ │ ├── App.tsx │ │ ├── app.css │ │ ├── assets │ │ │ └── react.svg │ │ ├── components │ │ │ ├── Alert.tsx │ │ │ ├── AppLayout.tsx │ │ │ ├── AuthLayout.tsx │ │ │ └── InputErrorMessage.tsx │ │ ├── lib │ │ │ ├── AuthProvider.tsx │ │ │ ├── db.ts │ │ │ ├── utils.ts │ │ │ ├── validationRules.ts │ │ │ └── validationSchema.ts │ │ ├── main.tsx │ │ ├── routes │ │ │ ├── account │ │ │ │ ├── index.tsx │ │ │ │ └── update-email.tsx │ │ │ ├── auth │ │ │ │ ├── signin.tsx │ │ │ │ └── verify-token.tsx │ │ │ └── index.tsx │ │ ├── tailwind.css │ │ └── vite-env.d.ts │ │ ├── supabase │ │ ├── .gitignore │ │ ├── config.toml │ │ └── seed.sql │ │ ├── tailwind.config.cjs │ │ ├── tests │ │ ├── signin-flow.spec.ts │ │ └── utils.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.node.json │ │ └── vite.config.ts ├── express │ ├── .env.example │ ├── .gitignore │ ├── README.md │ ├── app.js │ ├── bin │ │ └── www │ ├── lib │ │ ├── supabase.js │ │ ├── utils.js │ │ ├── validationRules.js │ │ └── validationSchema.js │ ├── package.json │ ├── playwright.config.ts │ ├── pnpm-lock.yaml │ ├── public │ │ ├── images │ │ │ └── vite.svg │ │ └── stylesheets │ │ │ ├── app.css │ │ │ ├── style.css │ │ │ └── tailwind.css │ ├── routes │ │ ├── account.js │ │ ├── auth.js │ │ └── index.js │ ├── supabase │ │ ├── .gitignore │ │ ├── auth │ │ │ └── email │ │ │ │ ├── confirmation.html │ │ │ │ ├── email-change.html │ │ │ │ ├── magic-link.html │ │ │ │ └── recovery.html │ │ ├── config.toml │ │ └── seed.sql │ ├── tailwind.config.js │ ├── tests │ │ ├── signin-flow.spec.ts │ │ └── utils.ts │ └── views │ │ ├── _partials.pug │ │ ├── app │ │ ├── account │ │ │ ├── index.pug │ │ │ └── update-email.pug │ │ └── layout.pug │ │ ├── auth │ │ ├── layout.pug │ │ ├── signin.pug │ │ └── verify-token.pug │ │ ├── error.pug │ │ ├── index.pug │ │ └── layout.pug ├── nextjs-pages │ ├── .env.example │ ├── .github │ │ └── workflows │ │ │ └── playwright.yml │ ├── .gitignore │ ├── README.md │ ├── components │ │ ├── Alert.tsx │ │ ├── AppLayout.tsx │ │ ├── AuthLayout.tsx │ │ └── InputErrorMessage.tsx │ ├── lib │ │ ├── utils.ts │ │ ├── validationRules.ts │ │ └── validationSchema.ts │ ├── middleware.ts │ ├── next.config.js │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ ├── _document.tsx │ │ ├── account │ │ │ ├── index.tsx │ │ │ └── update-email.tsx │ │ ├── api │ │ │ └── auth │ │ │ │ ├── callback.ts │ │ │ │ ├── confirm.ts │ │ │ │ └── signout.ts │ │ ├── auth │ │ │ ├── index.tsx │ │ │ ├── signin.tsx │ │ │ └── verify-token.tsx │ │ └── index.tsx │ ├── playwright.config.ts │ ├── pnpm-lock.yaml │ ├── public │ │ ├── favicon.ico │ │ ├── next.svg │ │ ├── thirteen.svg │ │ └── vercel.svg │ ├── styles │ │ └── tailwind.css │ ├── supabase │ │ ├── .gitignore │ │ ├── auth │ │ │ └── email │ │ │ │ ├── confirmation.html │ │ │ │ ├── email-change.html │ │ │ │ ├── magic-link.html │ │ │ │ └── recovery.html │ │ ├── config.toml │ │ └── seed.sql │ ├── tailwind.config.js │ ├── tests │ │ ├── signin-flow.spec.ts │ │ └── utils.ts │ └── tsconfig.json ├── nextjs │ ├── .env.example │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── app │ │ ├── (app) │ │ │ ├── account │ │ │ │ ├── page.tsx │ │ │ │ └── update-email │ │ │ │ │ ├── email-form.tsx │ │ │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ ├── auth │ │ │ ├── callback │ │ │ │ └── route.ts │ │ │ ├── confirm │ │ │ │ └── route.ts │ │ │ ├── layout.tsx │ │ │ ├── signin │ │ │ │ ├── magiclinkform.tsx │ │ │ │ └── page.tsx │ │ │ ├── signout │ │ │ │ └── route.ts │ │ │ └── verify-token │ │ │ │ ├── page.tsx │ │ │ │ └── verifytokenform.tsx │ │ ├── favicon.ico │ │ ├── globals.css │ │ └── layout.tsx │ ├── components │ │ ├── Alert.tsx │ │ └── InputErrorMessage.tsx │ ├── lib │ │ ├── supabase-server.ts │ │ ├── utils.ts │ │ ├── validationRules.ts │ │ └── validationSchema.ts │ ├── middleware.ts │ ├── next.config.js │ ├── package.json │ ├── playwright.config.ts │ ├── pnpm-lock.yaml │ ├── postcss.config.js │ ├── public │ │ ├── next.svg │ │ └── vercel.svg │ ├── supabase │ │ ├── .gitignore │ │ ├── auth │ │ │ └── email │ │ │ │ ├── confirmation.html │ │ │ │ ├── email-change.html │ │ │ │ ├── magic-link.html │ │ │ │ └── recovery.html │ │ ├── config.toml │ │ └── seed.sql │ ├── tailwind.config.js │ ├── tests │ │ ├── signin-flow.spec.ts │ │ └── utils.ts │ └── tsconfig.json ├── nuxt │ ├── .env.example │ ├── .gitignore │ ├── .npmrc │ ├── README.md │ ├── app.vue │ ├── components │ │ ├── Alert.vue │ │ └── InputErrorMessage.vue │ ├── layouts │ │ ├── auth.vue │ │ └── default.vue │ ├── lib │ │ ├── utils.ts │ │ ├── validationRules.ts │ │ └── validationSchema.ts │ ├── middleware │ │ └── auth.ts │ ├── nuxt.config.ts │ ├── package.json │ ├── pages │ │ ├── account │ │ │ ├── index.vue │ │ │ └── update-email.vue │ │ ├── auth │ │ │ ├── confirm.vue │ │ │ ├── signin.vue │ │ │ └── verify-token.vue │ │ └── index.vue │ ├── playwright.config.ts │ ├── pnpm-lock.yaml │ ├── public │ │ └── favicon.ico │ ├── server │ │ └── tsconfig.json │ ├── supabase │ │ ├── .gitignore │ │ ├── config.toml │ │ └── seed.sql │ ├── tailwind.config.js │ ├── tests │ │ ├── signin-flow.spec.ts │ │ └── utils.ts │ └── tsconfig.json ├── react │ ├── .env.example │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package.json │ ├── playwright.config.ts │ ├── pnpm-lock.yaml │ ├── public │ │ └── vite.svg │ ├── src │ │ ├── App.tsx │ │ ├── app.css │ │ ├── assets │ │ │ └── react.svg │ │ ├── components │ │ │ ├── Alert.tsx │ │ │ ├── AppLayout.tsx │ │ │ ├── AuthLayout.tsx │ │ │ └── InputErrorMessage.tsx │ │ ├── lib │ │ │ ├── AuthProvider.tsx │ │ │ ├── db.ts │ │ │ ├── utils.ts │ │ │ ├── validationRules.ts │ │ │ └── validationSchema.ts │ │ ├── main.tsx │ │ ├── routes │ │ │ ├── account │ │ │ │ ├── index.tsx │ │ │ │ └── update-email.tsx │ │ │ ├── auth │ │ │ │ ├── signin.tsx │ │ │ │ └── verify-token.tsx │ │ │ └── index.tsx │ │ ├── tailwind.css │ │ └── vite-env.d.ts │ ├── supabase │ │ ├── .gitignore │ │ ├── config.toml │ │ └── seed.sql │ ├── tailwind.config.cjs │ ├── tests │ │ ├── signin-flow.spec.ts │ │ └── utils.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts └── sveltekit │ ├── .env.example │ ├── .eslintignore │ ├── .eslintrc.cjs │ ├── .github │ └── workflows │ │ └── playwright.yml │ ├── .gitignore │ ├── .npmrc │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── package.json │ ├── playwright.config.ts │ ├── pnpm-lock.yaml │ ├── src │ ├── app.d.ts │ ├── app.html │ ├── hooks.server.ts │ ├── lib │ │ ├── Alert.svelte │ │ ├── InputErrorMessage.svelte │ │ ├── LoadingRelative.svelte │ │ ├── schema.ts │ │ ├── utils.ts │ │ ├── validationRules.ts │ │ └── validationSchema.ts │ ├── routes │ │ ├── (app) │ │ │ ├── +layout.svelte │ │ │ ├── +page.server.ts │ │ │ ├── +page.svelte │ │ │ └── account │ │ │ │ ├── +layout.server.ts │ │ │ │ ├── +page.svelte │ │ │ │ └── update-email │ │ │ │ ├── +page.server.ts │ │ │ │ └── +page.svelte │ │ ├── (auth) │ │ │ ├── +layout.server.ts │ │ │ ├── +layout.svelte │ │ │ └── auth │ │ │ │ ├── +page.ts │ │ │ │ ├── callback │ │ │ │ └── +server.ts │ │ │ │ ├── confirm │ │ │ │ └── +server.ts │ │ │ │ ├── signin │ │ │ │ ├── +page.server.ts │ │ │ │ └── +page.svelte │ │ │ │ ├── signout │ │ │ │ └── +page.server.ts │ │ │ │ └── verify-token │ │ │ │ ├── +page.server.ts │ │ │ │ └── +page.svelte │ │ ├── +layout.server.ts │ │ ├── +layout.svelte │ │ └── +layout.ts │ └── tailwind.css │ ├── static │ ├── app.css │ └── favicon.png │ ├── supabase │ ├── .gitignore │ ├── auth │ │ └── email │ │ │ ├── confirmation.html │ │ │ ├── email-change.html │ │ │ ├── magic-link.html │ │ │ └── recovery.html │ ├── config.toml │ └── seed.sql │ ├── svelte.config.js │ ├── tailwind.config.cjs │ ├── tests │ ├── signin-flow.spec.ts │ └── utils.ts │ ├── tsconfig.json │ └── vite.config.ts ├── oauth-flow ├── OAuth_SignIn_Flow_SSR.excalidraw ├── OAuth_SignIn_Flow_SSR.excalidraw.png ├── README.md ├── nextjs-pages │ ├── .env.example │ ├── .gitignore │ ├── README.md │ ├── components │ │ ├── Alert.tsx │ │ ├── AppLayout.tsx │ │ ├── AuthLayout.tsx │ │ └── InputErrorMessage.tsx │ ├── lib │ │ ├── utils.ts │ │ ├── validationRules.ts │ │ └── validationSchema.ts │ ├── next.config.js │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ ├── _document.tsx │ │ ├── account │ │ │ ├── index.tsx │ │ │ └── update-email.tsx │ │ ├── api │ │ │ ├── auth │ │ │ │ ├── [provider].ts │ │ │ │ └── callback.ts │ │ │ └── hello.ts │ │ ├── auth │ │ │ ├── index.tsx │ │ │ ├── signin.tsx │ │ │ └── signout.tsx │ │ └── index.tsx │ ├── pnpm-lock.yaml │ ├── public │ │ ├── favicon.ico │ │ ├── next.svg │ │ ├── thirteen.svg │ │ └── vercel.svg │ ├── styles │ │ └── tailwind.css │ ├── supabase │ │ ├── .gitignore │ │ ├── config.toml │ │ └── seed.sql │ ├── tailwind.config.js │ └── tsconfig.json ├── nextjs │ ├── .env.example │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── app │ │ ├── (app) │ │ │ ├── account │ │ │ │ ├── page.tsx │ │ │ │ └── update-email │ │ │ │ │ ├── email-form.tsx │ │ │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ ├── auth │ │ │ ├── [slug] │ │ │ │ └── route.ts │ │ │ ├── callback │ │ │ │ └── route.ts │ │ │ ├── layout.tsx │ │ │ ├── signin │ │ │ │ ├── oauth-form.tsx │ │ │ │ └── page.tsx │ │ │ ├── signout │ │ │ │ └── route.ts │ │ │ └── verify-token │ │ │ │ ├── page.tsx │ │ │ │ └── verifytokenform.tsx │ │ ├── favicon.ico │ │ ├── globals.css │ │ ├── layout.tsx │ │ └── supabase-provider.tsx │ ├── components │ │ ├── Alert.tsx │ │ └── InputErrorMessage.tsx │ ├── lib │ │ ├── supabase-server.ts │ │ ├── utils.ts │ │ ├── validationRules.ts │ │ └── validationSchema.ts │ ├── middleware.ts │ ├── next.config.js │ ├── package.json │ ├── pnpm-lock.yaml │ ├── postcss.config.js │ ├── public │ │ ├── next.svg │ │ └── vercel.svg │ ├── supabase │ │ ├── .gitignore │ │ ├── config.toml │ │ └── seed.sql │ ├── tailwind.config.js │ └── tsconfig.json ├── nuxt │ ├── .env.example │ ├── .gitignore │ ├── .npmrc │ ├── README.md │ ├── app.vue │ ├── components │ │ ├── Alert.vue │ │ └── InputErrorMessage.vue │ ├── layouts │ │ ├── auth.vue │ │ └── default.vue │ ├── lib │ │ ├── utils.ts │ │ ├── validationRules.ts │ │ └── validationSchema.ts │ ├── middleware │ │ └── auth.ts │ ├── nuxt.config.ts │ ├── package.json │ ├── pages │ │ ├── account │ │ │ └── index.vue │ │ ├── auth │ │ │ ├── confirm.vue │ │ │ └── signin.vue │ │ └── index.vue │ ├── pnpm-lock.yaml │ ├── public │ │ └── favicon.ico │ ├── server │ │ └── tsconfig.json │ ├── supabase │ │ ├── .gitignore │ │ ├── config.toml │ │ └── seed.sql │ ├── tailwind.config.js │ └── tsconfig.json ├── react │ ├── .env.example │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package.json │ ├── pnpm-lock.yaml │ ├── public │ │ └── vite.svg │ ├── src │ │ ├── App.tsx │ │ ├── app.css │ │ ├── assets │ │ │ └── react.svg │ │ ├── components │ │ │ ├── Alert.tsx │ │ │ ├── AppLayout.tsx │ │ │ ├── AuthLayout.tsx │ │ │ └── InputErrorMessage.tsx │ │ ├── lib │ │ │ ├── AuthProvider.tsx │ │ │ ├── db.ts │ │ │ ├── utils.ts │ │ │ ├── validationRules.ts │ │ │ └── validationSchema.ts │ │ ├── main.tsx │ │ ├── routes │ │ │ ├── account │ │ │ │ └── index.tsx │ │ │ ├── auth │ │ │ │ └── signin.tsx │ │ │ └── index.tsx │ │ ├── tailwind.css │ │ └── vite-env.d.ts │ ├── supabase │ │ ├── .gitignore │ │ ├── config.toml │ │ └── seed.sql │ ├── tailwind.config.cjs │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts └── sveltekit │ ├── .env.example │ ├── .eslintignore │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── .npmrc │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── package.json │ ├── pnpm-lock.yaml │ ├── src │ ├── app.d.ts │ ├── app.html │ ├── hooks.server.ts │ ├── lib │ │ ├── Alert.svelte │ │ ├── InputErrorMessage.svelte │ │ ├── LoadingRelative.svelte │ │ ├── icons │ │ │ ├── Azure.svelte │ │ │ ├── GitHub.svelte │ │ │ ├── Google.svelte │ │ │ └── Slack.svelte │ │ ├── utils.ts │ │ ├── validationRules.ts │ │ └── validationSchema.ts │ ├── routes │ │ ├── (app) │ │ │ ├── +layout.svelte │ │ │ ├── +page.server.ts │ │ │ ├── +page.svelte │ │ │ ├── account │ │ │ │ ├── +layout.server.ts │ │ │ │ ├── +page.svelte │ │ │ │ └── update-email │ │ │ │ │ ├── +page.server.ts │ │ │ │ │ └── +page.svelte │ │ │ └── logging-in │ │ │ │ ├── +layout@.svelte │ │ │ │ └── +page.svelte │ │ ├── (auth) │ │ │ ├── +layout.server.ts │ │ │ ├── +layout.svelte │ │ │ └── auth │ │ │ │ ├── +page.ts │ │ │ │ ├── callback │ │ │ │ └── +server.ts │ │ │ │ ├── signin │ │ │ │ ├── +page.server.ts │ │ │ │ └── +page.svelte │ │ │ │ └── signout │ │ │ │ └── +page.server.ts │ │ ├── +layout.server.ts │ │ ├── +layout.svelte │ │ └── +layout.ts │ └── tailwind.css │ ├── static │ ├── app.css │ └── favicon.png │ ├── supabase │ ├── .gitignore │ ├── config.toml │ └── seed.sql │ ├── svelte.config.js │ ├── tailwind.config.cjs │ ├── tsconfig.json │ └── vite.config.ts ├── package.json ├── pnpm-lock.yaml ├── reset-flow ├── README.md ├── Reset_Password_Flow_SSR.excalidraw ├── Reset_Password_Flow_SSR.excalidraw.png ├── auth-ui │ ├── nextjs │ │ ├── .env.example │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── README.md │ │ ├── app │ │ │ ├── (app) │ │ │ │ ├── account │ │ │ │ │ ├── page.tsx │ │ │ │ │ ├── update-email │ │ │ │ │ │ ├── email-form.tsx │ │ │ │ │ │ └── page.tsx │ │ │ │ │ └── update-password │ │ │ │ │ │ ├── page.tsx │ │ │ │ │ │ └── password-form.tsx │ │ │ │ ├── layout.tsx │ │ │ │ └── page.tsx │ │ │ ├── auth │ │ │ │ ├── callback │ │ │ │ │ └── route.ts │ │ │ │ ├── confirm │ │ │ │ │ └── route.ts │ │ │ │ ├── forgotpassword │ │ │ │ │ ├── forgot-password-form.tsx │ │ │ │ │ └── page.tsx │ │ │ │ ├── layout.tsx │ │ │ │ ├── signin │ │ │ │ │ ├── page.tsx │ │ │ │ │ └── sign-in-form.tsx │ │ │ │ ├── signout │ │ │ │ │ └── route.ts │ │ │ │ └── signup │ │ │ │ │ ├── page.tsx │ │ │ │ │ └── sign-up-form.tsx │ │ │ ├── favicon.ico │ │ │ ├── formStyle.ts │ │ │ ├── globals.css │ │ │ └── layout.tsx │ │ ├── components │ │ │ ├── Alert.tsx │ │ │ └── InputErrorMessage.tsx │ │ ├── lib │ │ │ ├── supabase-server.ts │ │ │ ├── utils.ts │ │ │ ├── validationRules.ts │ │ │ └── validationSchema.ts │ │ ├── middleware.ts │ │ ├── next.config.js │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── pnpm-lock.yaml │ │ ├── postcss.config.js │ │ ├── public │ │ │ ├── next.svg │ │ │ └── vercel.svg │ │ ├── supabase │ │ │ ├── .gitignore │ │ │ ├── auth │ │ │ │ └── email │ │ │ │ │ ├── confirmation.html │ │ │ │ │ ├── email-change.html │ │ │ │ │ ├── magic-link.html │ │ │ │ │ └── recovery.html │ │ │ ├── config.toml │ │ │ └── seed.sql │ │ ├── tailwind.config.js │ │ ├── tests │ │ │ ├── password-reset-flow.spec.ts │ │ │ ├── signup-flow.spec.ts │ │ │ └── utils.ts │ │ └── tsconfig.json │ └── react │ │ ├── .env.example │ │ ├── .gitignore │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── pnpm-lock.yaml │ │ ├── public │ │ └── vite.svg │ │ ├── src │ │ ├── App.tsx │ │ ├── app.css │ │ ├── assets │ │ │ └── react.svg │ │ ├── components │ │ │ ├── Alert.tsx │ │ │ ├── AppLayout.tsx │ │ │ ├── AuthLayout.tsx │ │ │ └── InputErrorMessage.tsx │ │ ├── lib │ │ │ ├── AuthProvider.tsx │ │ │ ├── db.ts │ │ │ ├── utils.ts │ │ │ ├── validationRules.ts │ │ │ └── validationSchema.ts │ │ ├── main.tsx │ │ ├── routes │ │ │ ├── account │ │ │ │ ├── index.tsx │ │ │ │ ├── update-email.tsx │ │ │ │ └── update-password.tsx │ │ │ ├── auth │ │ │ │ ├── forgotpassword.tsx │ │ │ │ ├── signin.tsx │ │ │ │ └── signup.tsx │ │ │ └── index.tsx │ │ ├── tailwind.css │ │ └── vite-env.d.ts │ │ ├── supabase │ │ ├── .gitignore │ │ ├── config.toml │ │ └── seed.sql │ │ ├── tailwind.config.cjs │ │ ├── tests │ │ ├── password-reset-flow.spec.ts │ │ ├── signup-flow.spec.ts │ │ └── utils.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.node.json │ │ └── vite.config.ts ├── express │ ├── .env.example │ ├── .gitignore │ ├── README.md │ ├── app.js │ ├── bin │ │ └── www │ ├── lib │ │ ├── supabase.js │ │ ├── utils.js │ │ ├── validationRules.js │ │ └── validationSchema.js │ ├── package.json │ ├── playwright.config.ts │ ├── pnpm-lock.yaml │ ├── public │ │ ├── images │ │ │ └── vite.svg │ │ └── stylesheets │ │ │ ├── app.css │ │ │ ├── style.css │ │ │ └── tailwind.css │ ├── routes │ │ ├── account.js │ │ ├── auth.js │ │ └── index.js │ ├── supabase │ │ ├── .gitignore │ │ ├── auth │ │ │ └── email │ │ │ │ ├── confirmation.html │ │ │ │ ├── email-change.html │ │ │ │ ├── magic-link.html │ │ │ │ └── recovery.html │ │ ├── config.toml │ │ └── seed.sql │ ├── tailwind.config.js │ ├── tests │ │ ├── password-reset-flow.spec.ts │ │ ├── signup-flow.spec.ts │ │ └── utils.ts │ └── views │ │ ├── _partials.pug │ │ ├── app │ │ ├── account │ │ │ ├── index.pug │ │ │ ├── update-email.pug │ │ │ └── update-password.pug │ │ └── layout.pug │ │ ├── auth │ │ ├── forgotpassword.pug │ │ ├── layout.pug │ │ ├── signin.pug │ │ └── signup.pug │ │ ├── error.pug │ │ ├── index.pug │ │ └── layout.pug ├── nextjs-pages │ ├── .env.example │ ├── .github │ │ └── workflows │ │ │ └── playwright.yml │ ├── .gitignore │ ├── README.md │ ├── components │ │ ├── Alert.tsx │ │ ├── AppLayout.tsx │ │ ├── AuthLayout.tsx │ │ └── InputErrorMessage.tsx │ ├── lib │ │ ├── utils.ts │ │ ├── validationRules.ts │ │ └── validationSchema.ts │ ├── next.config.js │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ ├── _document.tsx │ │ ├── account │ │ │ ├── index.tsx │ │ │ ├── update-email.tsx │ │ │ └── update-password.tsx │ │ ├── api │ │ │ └── auth │ │ │ │ ├── callback.ts │ │ │ │ ├── confirm.ts │ │ │ │ └── signout.ts │ │ ├── auth │ │ │ ├── forgotpassword.tsx │ │ │ ├── index.tsx │ │ │ ├── signin.tsx │ │ │ └── signup.tsx │ │ └── index.tsx │ ├── playwright.config.ts │ ├── pnpm-lock.yaml │ ├── public │ │ ├── favicon.ico │ │ ├── next.svg │ │ ├── thirteen.svg │ │ └── vercel.svg │ ├── styles │ │ └── tailwind.css │ ├── supabase │ │ ├── .gitignore │ │ ├── auth │ │ │ └── email │ │ │ │ ├── confirmation.html │ │ │ │ ├── email-change.html │ │ │ │ ├── magic-link.html │ │ │ │ └── recovery.html │ │ ├── config.toml │ │ └── seed.sql │ ├── tailwind.config.js │ ├── tests │ │ ├── password-reset-flow.spec.ts │ │ ├── signup-flow.spec.ts │ │ └── utils.ts │ └── tsconfig.json ├── nextjs │ ├── .env.example │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── app │ │ ├── (app) │ │ │ ├── account │ │ │ │ ├── page.tsx │ │ │ │ ├── update-email │ │ │ │ │ ├── email-form.tsx │ │ │ │ │ └── page.tsx │ │ │ │ └── update-password │ │ │ │ │ ├── page.tsx │ │ │ │ │ └── password-form.tsx │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ ├── auth │ │ │ ├── confirm │ │ │ │ └── route.ts │ │ │ ├── forgotpassword │ │ │ │ ├── forgot-password-form.tsx │ │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ ├── signin │ │ │ │ ├── page.tsx │ │ │ │ └── sign-in-form.tsx │ │ │ ├── signout │ │ │ │ └── route.ts │ │ │ └── signup │ │ │ │ ├── page.tsx │ │ │ │ └── sign-up-form.tsx │ │ ├── favicon.ico │ │ ├── globals.css │ │ └── layout.tsx │ ├── components │ │ ├── Alert.tsx │ │ └── InputErrorMessage.tsx │ ├── lib │ │ ├── supabase-server.ts │ │ ├── utils.ts │ │ ├── validationRules.ts │ │ └── validationSchema.ts │ ├── middleware.ts │ ├── next.config.js │ ├── package.json │ ├── playwright.config.ts │ ├── pnpm-lock.yaml │ ├── postcss.config.js │ ├── public │ │ ├── next.svg │ │ └── vercel.svg │ ├── supabase │ │ ├── .gitignore │ │ ├── auth │ │ │ └── email │ │ │ │ ├── confirmation.html │ │ │ │ ├── email-change.html │ │ │ │ ├── invite.html │ │ │ │ ├── magic-link.html │ │ │ │ └── recovery.html │ │ ├── config.toml │ │ └── seed.sql │ ├── tailwind.config.js │ ├── tests │ │ ├── password-reset-flow.spec.ts │ │ ├── signup-flow.spec.ts │ │ └── utils.ts │ └── tsconfig.json ├── nuxt │ ├── .env.example │ ├── .gitignore │ ├── .npmrc │ ├── README.md │ ├── app.vue │ ├── components │ │ ├── Alert.vue │ │ └── InputErrorMessage.vue │ ├── layouts │ │ ├── auth.vue │ │ └── default.vue │ ├── lib │ │ ├── utils.ts │ │ ├── validationRules.ts │ │ └── validationSchema.ts │ ├── middleware │ │ └── auth.ts │ ├── nuxt.config.ts │ ├── package.json │ ├── pages │ │ ├── account │ │ │ ├── index.vue │ │ │ ├── update-email.vue │ │ │ └── update-password.vue │ │ ├── auth │ │ │ ├── confirm.vue │ │ │ ├── forgotpassword.vue │ │ │ ├── signin.vue │ │ │ └── signup.vue │ │ └── index.vue │ ├── playwright.config.ts │ ├── pnpm-lock.yaml │ ├── public │ │ └── favicon.ico │ ├── server │ │ └── tsconfig.json │ ├── supabase │ │ ├── .gitignore │ │ ├── config.toml │ │ └── seed.sql │ ├── tailwind.config.js │ ├── tests │ │ ├── password-reset-flow.spec.ts │ │ ├── signup-flow.spec.ts │ │ └── utils.ts │ └── tsconfig.json ├── react │ ├── .env.example │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package.json │ ├── playwright.config.ts │ ├── pnpm-lock.yaml │ ├── public │ │ └── vite.svg │ ├── src │ │ ├── App.tsx │ │ ├── app.css │ │ ├── assets │ │ │ └── react.svg │ │ ├── components │ │ │ ├── Alert.tsx │ │ │ ├── AppLayout.tsx │ │ │ ├── AuthLayout.tsx │ │ │ └── InputErrorMessage.tsx │ │ ├── lib │ │ │ ├── AuthProvider.tsx │ │ │ ├── db.ts │ │ │ ├── utils.ts │ │ │ ├── validationRules.ts │ │ │ └── validationSchema.ts │ │ ├── main.tsx │ │ ├── routes │ │ │ ├── account │ │ │ │ ├── index.tsx │ │ │ │ ├── update-email.tsx │ │ │ │ └── update-password.tsx │ │ │ ├── auth │ │ │ │ ├── forgotpassword.tsx │ │ │ │ ├── signin.tsx │ │ │ │ └── signup.tsx │ │ │ └── index.tsx │ │ ├── tailwind.css │ │ └── vite-env.d.ts │ ├── supabase │ │ ├── .gitignore │ │ ├── config.toml │ │ └── seed.sql │ ├── tailwind.config.cjs │ ├── tests │ │ ├── password-reset-flow.spec.ts │ │ ├── signup-flow.spec.ts │ │ └── utils.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts └── sveltekit │ ├── .env.example │ ├── .eslintignore │ ├── .eslintrc.cjs │ ├── .github │ └── workflows │ │ └── playwright.yml │ ├── .gitignore │ ├── .npmrc │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── package.json │ ├── playwright.config.ts │ ├── pnpm-lock.yaml │ ├── src │ ├── app.d.ts │ ├── app.html │ ├── hooks.server.ts │ ├── lib │ │ ├── Alert.svelte │ │ ├── InputErrorMessage.svelte │ │ ├── LoadingRelative.svelte │ │ ├── utils.ts │ │ ├── validationRules.ts │ │ └── validationSchema.ts │ ├── routes │ │ ├── (app) │ │ │ ├── +layout.svelte │ │ │ ├── +page.server.ts │ │ │ ├── +page.svelte │ │ │ ├── account │ │ │ │ ├── +layout.server.ts │ │ │ │ ├── +page.server.ts │ │ │ │ ├── +page.svelte │ │ │ │ ├── update-email │ │ │ │ │ ├── +page.server.ts │ │ │ │ │ └── +page.svelte │ │ │ │ └── update-password │ │ │ │ │ ├── +page.server.ts │ │ │ │ │ └── +page.svelte │ │ │ └── logging-in │ │ │ │ ├── +layout@.svelte │ │ │ │ └── +page.svelte │ │ ├── (auth) │ │ │ ├── +layout.server.ts │ │ │ ├── +layout.svelte │ │ │ └── auth │ │ │ │ ├── +page.ts │ │ │ │ ├── callback │ │ │ │ └── +server.ts │ │ │ │ ├── confirm │ │ │ │ └── +server.ts │ │ │ │ ├── forgotpassword │ │ │ │ ├── +page.server.ts │ │ │ │ └── +page.svelte │ │ │ │ ├── signin │ │ │ │ ├── +page.server.ts │ │ │ │ └── +page.svelte │ │ │ │ ├── signout │ │ │ │ └── +page.server.ts │ │ │ │ └── signup │ │ │ │ ├── +page.server.ts │ │ │ │ └── +page.svelte │ │ ├── +layout.server.ts │ │ ├── +layout.svelte │ │ └── +layout.ts │ └── tailwind.css │ ├── static │ ├── app.css │ └── favicon.png │ ├── supabase │ ├── .gitignore │ ├── auth │ │ └── email │ │ │ ├── confirmation.html │ │ │ ├── email-change.html │ │ │ ├── magic-link.html │ │ │ └── recovery.html │ ├── config.toml │ └── seed.sql │ ├── svelte.config.js │ ├── tailwind.config.cjs │ ├── tests │ ├── password-reset-flow.spec.ts │ ├── signup-flow.spec.ts │ └── utils.ts │ ├── tsconfig.json │ └── vite.config.ts └── user-profile ├── nextjs-pages ├── .env.example ├── .github │ └── workflows │ │ └── playwright.yml ├── .gitignore ├── README.md ├── components │ ├── Alert.tsx │ ├── AppLayout.tsx │ ├── AuthLayout.tsx │ └── InputErrorMessage.tsx ├── lib │ ├── invalidateNextRouterCache.ts │ ├── schema.ts │ ├── utils.ts │ ├── validationRules.ts │ └── validationSchema.ts ├── middleware.ts ├── next.config.js ├── package.json ├── pages │ ├── [slug].tsx │ ├── _app.tsx │ ├── _document.tsx │ ├── account │ │ ├── index.tsx │ │ ├── update-email.tsx │ │ ├── update-password.tsx │ │ └── update.tsx │ ├── api │ │ ├── auth │ │ │ ├── callback.ts │ │ │ └── confirm.ts │ │ └── hello.ts │ ├── auth │ │ ├── forgotpassword.tsx │ │ ├── index.tsx │ │ ├── signin.tsx │ │ ├── signout.tsx │ │ └── signup.tsx │ └── index.tsx ├── playwright.config.ts ├── pnpm-lock.yaml ├── public │ ├── favicon.ico │ ├── next.svg │ ├── thirteen.svg │ └── vercel.svg ├── styles │ └── tailwind.css ├── supabase │ ├── .gitignore │ ├── auth │ │ └── email │ │ │ ├── confirmation.html │ │ │ ├── email-change.html │ │ │ ├── magic-link.html │ │ │ └── recovery.html │ ├── config.toml │ ├── migrations │ │ ├── 20230217190748_add_slugify_function.sql │ │ └── 20230217193007_add_profiles_table.sql │ ├── seed.sql │ └── tests │ │ └── database │ │ ├── anons.test.sql │ │ └── profiles.test.sql ├── tailwind.config.js ├── tests │ ├── user-profile-flow.spec.ts │ └── utils.ts └── tsconfig.json ├── nextjs ├── .env.example ├── .eslintrc.json ├── .gitignore ├── README.md ├── app │ ├── (app) │ │ ├── account │ │ │ ├── page.tsx │ │ │ ├── update-email │ │ │ │ ├── email-form.tsx │ │ │ │ └── page.tsx │ │ │ ├── update-password │ │ │ │ ├── page.tsx │ │ │ │ └── password-form.tsx │ │ │ └── update │ │ │ │ ├── page.tsx │ │ │ │ └── update-form.tsx │ │ ├── layout.tsx │ │ └── page.tsx │ ├── [slug] │ │ └── page.tsx │ ├── auth │ │ ├── callback │ │ │ └── route.ts │ │ ├── confirm │ │ │ └── route.ts │ │ ├── forgotpassword │ │ │ ├── forgot-password-form.tsx │ │ │ └── page.tsx │ │ ├── layout.tsx │ │ ├── signin │ │ │ ├── page.tsx │ │ │ └── sign-in-form.tsx │ │ ├── signout │ │ │ └── route.ts │ │ └── signup │ │ │ ├── page.tsx │ │ │ └── sign-up-form.tsx │ ├── favicon.ico │ ├── global.d.ts │ ├── globals.css │ └── layout.tsx ├── components │ ├── Alert.tsx │ └── InputErrorMessage.tsx ├── lib │ ├── schema.ts │ ├── supabase-server.ts │ ├── utils.ts │ ├── validationRules.ts │ └── validationSchema.ts ├── middleware.ts ├── next.config.js ├── package.json ├── playwright.config.ts ├── pnpm-lock.yaml ├── postcss.config.js ├── public │ ├── next.svg │ └── vercel.svg ├── supabase │ ├── .gitignore │ ├── auth │ │ └── email │ │ │ ├── confirmation.html │ │ │ ├── email-change.html │ │ │ ├── magic-link.html │ │ │ └── recovery.html │ ├── config.toml │ ├── migrations │ │ ├── 20230217190748_add_slugify_function.sql │ │ └── 20230217193007_add_profiles_table.sql │ ├── seed.sql │ └── tests │ │ └── database │ │ ├── anons.test.sql │ │ └── profiles.test.sql ├── tailwind.config.js ├── tests │ ├── user-profile-flow.spec.ts │ └── utils.ts └── tsconfig.json ├── nuxt ├── .env.example ├── .gitignore ├── .npmrc ├── README.md ├── app.vue ├── components │ ├── Alert.vue │ └── InputErrorMessage.vue ├── global.d.ts ├── layouts │ ├── auth.vue │ └── default.vue ├── lib │ ├── schema.ts │ ├── utils.ts │ ├── validationRules.ts │ └── validationSchema.ts ├── middleware │ └── auth.ts ├── nuxt.config.ts ├── package.json ├── pages │ ├── account │ │ ├── index.vue │ │ ├── update-email.vue │ │ ├── update-password.vue │ │ └── update.vue │ ├── auth │ │ ├── confirm.vue │ │ ├── forgotpassword.vue │ │ ├── signin.vue │ │ └── signup.vue │ ├── index.vue │ └── u │ │ └── [slug] │ │ └── index.vue ├── playwright.config.ts ├── pnpm-lock.yaml ├── public │ └── favicon.ico ├── server │ └── tsconfig.json ├── supabase │ ├── .gitignore │ ├── config.toml │ ├── migrations │ │ ├── 20230217190748_add_slugify_function.sql │ │ └── 20230217193007_add_profiles_table.sql │ ├── seed.sql │ └── tests │ │ └── database │ │ ├── anons.test.sql │ │ └── profiles.test.sql ├── tailwind.config.js ├── tests │ ├── user-profile-flow.spec.ts │ └── utils.ts └── tsconfig.json ├── react ├── .env.example ├── .gitignore ├── README.md ├── index.html ├── package.json ├── playwright.config.ts ├── pnpm-lock.yaml ├── public │ └── vite.svg ├── src │ ├── App.tsx │ ├── app.css │ ├── assets │ │ └── react.svg │ ├── components │ │ ├── Alert.tsx │ │ ├── AppLayout.tsx │ │ ├── AuthLayout.tsx │ │ └── InputErrorMessage.tsx │ ├── global.d.ts │ ├── lib │ │ ├── AuthProvider.tsx │ │ ├── db.ts │ │ ├── schema.ts │ │ ├── useProfile.ts │ │ ├── utils.ts │ │ ├── validationRules.ts │ │ └── validationSchema.ts │ ├── main.tsx │ ├── routes │ │ ├── account │ │ │ ├── index.tsx │ │ │ ├── update-email.tsx │ │ │ ├── update-password.tsx │ │ │ └── update.tsx │ │ ├── auth │ │ │ ├── forgotpassword.tsx │ │ │ ├── signin.tsx │ │ │ └── signup.tsx │ │ ├── index.tsx │ │ └── u │ │ │ └── profile.tsx │ ├── tailwind.css │ └── vite-env.d.ts ├── supabase │ ├── .gitignore │ ├── config.toml │ ├── migrations │ │ ├── 20230217190748_add_slugify_function.sql │ │ └── 20230217193007_add_profiles_table.sql │ ├── seed.sql │ └── tests │ │ └── database │ │ ├── anons.test.sql │ │ └── profiles.test.sql ├── tailwind.config.cjs ├── tests │ ├── user-profile-flow.spec.ts │ └── utils.ts ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts └── sveltekit ├── .env.example ├── .eslintignore ├── .eslintrc.cjs ├── .github └── workflows │ └── playwright.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc ├── README.md ├── package.json ├── playwright.config.ts ├── pnpm-lock.yaml ├── src ├── app.d.ts ├── app.html ├── hooks.server.ts ├── lib │ ├── Alert.svelte │ ├── InputErrorMessage.svelte │ ├── LoadingRelative.svelte │ ├── schema.ts │ ├── utils.ts │ ├── validationRules.ts │ └── validationSchema.ts ├── routes │ ├── (app) │ │ ├── +layout.server.ts │ │ ├── +layout.svelte │ │ ├── +page.server.ts │ │ ├── +page.svelte │ │ └── account │ │ │ ├── +page.server.ts │ │ │ ├── +page.svelte │ │ │ ├── update-email │ │ │ ├── +page.server.ts │ │ │ └── +page.svelte │ │ │ ├── update-password │ │ │ ├── +page.server.ts │ │ │ └── +page.svelte │ │ │ └── update │ │ │ ├── +page.server.ts │ │ │ └── +page.svelte │ ├── (auth) │ │ ├── +layout.server.ts │ │ ├── +layout.svelte │ │ └── auth │ │ │ ├── +page.ts │ │ │ ├── callback │ │ │ └── +server.ts │ │ │ ├── confirm │ │ │ └── +server.ts │ │ │ ├── forgotpassword │ │ │ ├── +page.server.ts │ │ │ └── +page.svelte │ │ │ ├── signin │ │ │ ├── +page.server.ts │ │ │ └── +page.svelte │ │ │ ├── signout │ │ │ └── +page.server.ts │ │ │ └── signup │ │ │ ├── +page.server.ts │ │ │ └── +page.svelte │ ├── +layout.server.ts │ ├── +layout.svelte │ ├── +layout.ts │ └── u │ │ └── [slug] │ │ ├── +layout@.svelte │ │ ├── +page.server.ts │ │ └── +page.svelte └── tailwind.css ├── static ├── app.css └── favicon.png ├── supabase ├── .gitignore ├── auth │ └── email │ │ ├── confirmation.html │ │ ├── email-change.html │ │ ├── magic-link.html │ │ └── recovery.html ├── config.toml ├── migrations │ ├── 20230217190748_add_slugify_function.sql │ └── 20230217193007_add_profiles_table.sql ├── seed.sql └── tests │ └── database │ ├── anons.test.sql │ └── profiles.test.sql ├── svelte.config.js ├── tailwind.config.cjs ├── tests ├── user-profile-flow.spec.ts └── utils.ts ├── tsconfig.json └── vite.config.ts /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | build 14 | playwright-report 15 | *.local 16 | 17 | # Editor directories and files 18 | .vscode/* 19 | !.vscode/extensions.json 20 | .idea 21 | .DS_Store 22 | *.suo 23 | *.ntvs* 24 | *.njsproj 25 | *.sln 26 | *.sw? 27 | 28 | 29 | # Vite 30 | .vite+ 31 | 32 | # Environment Variables 33 | .env 34 | .env.* 35 | !.env.example 36 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | auto-install-peers=true -------------------------------------------------------------------------------- /magic-link/Magic_Link_Otp_SignIn_Flow_SSR.excalidraw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/magic-link/Magic_Link_Otp_SignIn_Flow_SSR.excalidraw.png -------------------------------------------------------------------------------- /magic-link/README.md: -------------------------------------------------------------------------------- 1 | # Magic Link and OTP flow 2 | 3 | These are Supabase projects showing how to do magic link and otp login. 4 | 5 | These projects make use of: 6 | 7 | - [Supabase Auth Helpers](https://supabase.com/docs/guides/auth/auth-helpers) 8 | - [Zod](https://zod.dev/) Schema Validation library 9 | - [DaisyUI](https://daisyui.com/) 10 | - [tailwindcss](https://tailwindcss.com/) 11 | - [Playwright](https://playwright.dev/) e2e testing 12 | 13 | ## Magic Link Otp SignIn Flow in Server-side rendering (SSR) environment 14 | ![Magic Link Otp SignIn Flow](Magic_Link_Otp_SignIn_Flow_SSR.excalidraw.png) 15 | -------------------------------------------------------------------------------- /magic-link/auth-ui/react/.env.example: -------------------------------------------------------------------------------- 1 | VITE_SUPABASE_URL=http://localhost:54321 2 | VITE_SUPABASE_ANON_KEY= -------------------------------------------------------------------------------- /magic-link/auth-ui/react/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /magic-link/auth-ui/react/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Magic Link + Otp Flow 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /magic-link/auth-ui/react/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@playwright/test"; 2 | 3 | export default defineConfig({ 4 | webServer: { 5 | command: "npm run build && npm run preview", 6 | port: 4173, 7 | reuseExistingServer: true, 8 | }, 9 | testDir: "./tests", 10 | reporter: "list", 11 | 12 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 13 | forbidOnly: !!process.env.CI, 14 | /* Retry on CI only */ 15 | retries: process.env.CI ? 2 : 0, 16 | /* Opt out of parallel tests on CI. */ 17 | workers: process.env.CI ? 1 : undefined, 18 | }); 19 | -------------------------------------------------------------------------------- /magic-link/auth-ui/react/src/components/Alert.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | className: string; 5 | children: ReactNode; 6 | }; 7 | 8 | export default function Alert({ className, children }: Props) { 9 | return ( 10 |
11 |
12 | {children} 13 |
14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /magic-link/auth-ui/react/src/components/InputErrorMessage.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | children: ReactNode; 5 | }; 6 | 7 | export default function InputErrorMessage({ children }: Props) { 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /magic-link/auth-ui/react/src/lib/db.ts: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | 3 | export const supabase = createClient( 4 | import.meta.env.VITE_SUPABASE_URL, 5 | import.meta.env.VITE_SUPABASE_ANON_KEY 6 | ); 7 | -------------------------------------------------------------------------------- /magic-link/auth-ui/react/src/lib/validationRules.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const required = (name: string) => z.string().min(1, `${name} is required`); 4 | export const email = (name = 'Email') => required(name).email(`${name} is not valid`); 5 | export const password = (number = 5, name = 'Password') => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | -------------------------------------------------------------------------------- /magic-link/auth-ui/react/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import { BrowserRouter } from "react-router-dom"; 4 | import App from "./App"; 5 | import "./app.css"; 6 | import { AuthProvider } from "./lib/AuthProvider"; 7 | import { supabase } from "./lib/db"; 8 | 9 | ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /magic-link/auth-ui/react/src/routes/index.tsx: -------------------------------------------------------------------------------- 1 | import { useAuth } from "@/lib/AuthProvider"; 2 | 3 | export default function Home() { 4 | const { user } = useAuth(); 5 | 6 | return ( 7 | <> 8 |

Welcome {user?.email}

9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /magic-link/auth-ui/react/src/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /magic-link/auth-ui/react/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | interface ImportMetaEnv { 4 | readonly VITE_SUPABASE_URL: string; 5 | readonly VITE_SUPABASE_ANON_KEY: string; 6 | } 7 | 8 | interface ImportMeta { 9 | readonly env: ImportMetaEnv; 10 | } 11 | -------------------------------------------------------------------------------- /magic-link/auth-ui/react/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | config.local.toml -------------------------------------------------------------------------------- /magic-link/auth-ui/react/supabase/seed.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/magic-link/auth-ui/react/supabase/seed.sql -------------------------------------------------------------------------------- /magic-link/auth-ui/react/tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./src/**/*.{html,js,jsx,tsx,ts}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [require("daisyui")], 8 | 9 | daisyui: { 10 | themes: ["winter"], 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /magic-link/auth-ui/react/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true, 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /magic-link/auth-ui/react/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | import path from "path"; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react()], 8 | resolve: { 9 | alias: { 10 | "@": path.resolve(__dirname, "./src"), 11 | }, 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /magic-link/express/.env.example: -------------------------------------------------------------------------------- 1 | SUPABASE_URL=http://localhost:54321 2 | SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 3 | APP_BASE_URL=http://localhost:3000 -------------------------------------------------------------------------------- /magic-link/express/.gitignore: -------------------------------------------------------------------------------- 1 | # local env files 2 | .env 3 | .env.* 4 | !.env.example -------------------------------------------------------------------------------- /magic-link/express/lib/validationRules.js: -------------------------------------------------------------------------------- 1 | const z = require("zod").z; 2 | 3 | const required = (name) => z.string().min(1, `${name} is required`); 4 | const email = (name = "Email") => required(name).email(`${name} is not valid`); 5 | const password = (number = 5, name = "Password") => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | 8 | module.exports = { 9 | required, 10 | email, 11 | password, 12 | }; 13 | -------------------------------------------------------------------------------- /magic-link/express/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@playwright/test"; 2 | 3 | export default defineConfig({ 4 | webServer: { 5 | command: "npm run build && npm run start", 6 | port: 3000, 7 | reuseExistingServer: true, 8 | }, 9 | testDir: "./tests", 10 | reporter: "list", 11 | 12 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 13 | forbidOnly: !!process.env.CI, 14 | /* Retry on CI only */ 15 | retries: process.env.CI ? 2 : 0, 16 | /* Opt out of parallel tests on CI. */ 17 | workers: process.env.CI ? 1 : undefined, 18 | }); 19 | -------------------------------------------------------------------------------- /magic-link/express/public/stylesheets/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /magic-link/express/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require("express"); 2 | var router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get("/", function (req, res, next) { 6 | const session = res.locals.session; 7 | res.render("index", { user: session?.user }); 8 | }); 9 | 10 | module.exports = router; 11 | -------------------------------------------------------------------------------- /magic-link/express/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | -------------------------------------------------------------------------------- /magic-link/express/supabase/auth/email/email-change.html: -------------------------------------------------------------------------------- 1 |

Confirm Change of Email

2 | 3 |

4 | Follow this link to confirm the update of your email from {{ .Email }} to {{ .NewEmail }}: 5 |

6 |

Change Email

7 | 8 |

Or use the token {{ .Token }}

-------------------------------------------------------------------------------- /magic-link/express/supabase/auth/email/magic-link.html: -------------------------------------------------------------------------------- 1 |

Magic Link

2 | 3 |

Follow this link to login:

4 |

Log In

5 | 6 |

or alternatively, visit the verify token page and enter the token code: {{ .Token }}

7 | 8 |

The link will remain valid for 24 hours or until it is used, whichever comes first. If you need to request another link, please click here.

-------------------------------------------------------------------------------- /magic-link/express/supabase/seed.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/magic-link/express/supabase/seed.sql -------------------------------------------------------------------------------- /magic-link/express/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./views/**/*.{html,pug}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [require("daisyui")], 8 | 9 | daisyui: { 10 | themes: ["winter"], 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /magic-link/express/views/_partials.pug: -------------------------------------------------------------------------------- 1 | mixin alert(content) 2 | div(class=`alert shadow-lg rounded-none`, class!=attributes.class) 3 | div 4 | span= content 5 | 6 | mixin input_error(content) 7 | span(class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1")= content -------------------------------------------------------------------------------- /magic-link/express/views/app/account/index.pug: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | 3 | block main 4 | div(class='w-11/12 p-12 px-6 py-10 rounded-lg sm:w-8/12 md:w-6/12 lg:w-5/12 2xl:w-3/12 sm:px-10 sm:py-6') 5 | h2(class="font-semibold text-4xl mb-4") Account 6 | p.font-medium.mb-10 Hi #{user.email}, you can update your email or password from here 7 | ul.divide-y-2.divide-gray-200 8 | li(class='flex justify-between hover:bg-blue-600 hover:text-blue-200') 9 | a(href='/account/update-email', class="block w-full p-3") Update email 10 | -------------------------------------------------------------------------------- /magic-link/express/views/app/layout.pug: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | 3 | block content 4 | main(data-theme='winter') 5 | .flex.flex-col.h-screen 6 | .navbar.border-b.border-gray-300.py-8.px-4 7 | .flex-1 8 | h1.font-semibold 9 | a(href='/') Magic Link + Otp Flow 10 | .flex-none.space-x-10 11 | a(class='btn btn-outline no-animation' href='/account') Account 12 | form(class='block' action='/auth/signout' method='post') 13 | button(type='submit') Sign out 14 | .grid.place-items-center.my-20.mx-2 15 | block main 16 | -------------------------------------------------------------------------------- /magic-link/express/views/auth/layout.pug: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | 3 | block content 4 | main(data-theme='winter') 5 | .flex.flex-col.h-screen 6 | div(class='header border-b border-gray-300 py-8 px-4 mx-8 text-center') 7 | h1.font-semibold Magic Link + Otp Flow 8 | div(class='grid place-items-center my-20 mx-2 sm:my-auto') 9 | block main 10 | -------------------------------------------------------------------------------- /magic-link/express/views/error.pug: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.stack} 7 | -------------------------------------------------------------------------------- /magic-link/express/views/index.pug: -------------------------------------------------------------------------------- 1 | extends app/layout 2 | 3 | block main 4 | h1 Welcome #{user.email} 5 | -------------------------------------------------------------------------------- /magic-link/express/views/layout.pug: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title= `${title ? title + ' | ' : ''}Magic Link + Otp Flow` 5 | link(rel='icon', type='image/svg+xml', href='/images/vite.svg') 6 | link(rel='stylesheet', href='/stylesheets/style.css') 7 | body 8 | block content 9 | -------------------------------------------------------------------------------- /magic-link/nextjs-pages/.env.example: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321 2 | NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 -------------------------------------------------------------------------------- /magic-link/nextjs-pages/components/Alert.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | className: string; 5 | children: ReactNode; 6 | }; 7 | 8 | export default function Alert({ className, children }: Props) { 9 | return ( 10 |
11 |
12 | {children} 13 |
14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /magic-link/nextjs-pages/components/InputErrorMessage.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | children: ReactNode; 5 | }; 6 | 7 | export default function InputErrorMessage({ children }: Props) { 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /magic-link/nextjs-pages/lib/validationRules.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const required = (name: string) => z.string().min(1, `${name} is required`); 4 | export const email = (name = 'Email') => required(name).email(`${name} is not valid`); 5 | export const password = (number = 5, name = 'Password') => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | -------------------------------------------------------------------------------- /magic-link/nextjs-pages/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | } 5 | 6 | module.exports = nextConfig 7 | -------------------------------------------------------------------------------- /magic-link/nextjs-pages/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import { Html, Head, Main, NextScript } from 'next/document' 2 | 3 | export default function Document() { 4 | return ( 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /magic-link/nextjs-pages/pages/api/auth/callback.ts: -------------------------------------------------------------------------------- 1 | import { NextApiRequest, NextApiResponse } from "next"; 2 | import { createPagesServerClient } from "@supabase/auth-helpers-nextjs"; 3 | 4 | export default async function handler( 5 | req: NextApiRequest, 6 | res: NextApiResponse 7 | ) { 8 | const code = req.query.code; 9 | 10 | if (typeof code === "string") { 11 | // Create authenticated Supabase Client 12 | const supabase = createPagesServerClient({ req, res }); 13 | await supabase.auth.exchangeCodeForSession(code); 14 | } 15 | 16 | res.redirect("/"); 17 | } 18 | -------------------------------------------------------------------------------- /magic-link/nextjs-pages/pages/auth/index.tsx: -------------------------------------------------------------------------------- 1 | import { GetServerSidePropsContext } from "next"; 2 | 3 | export default function Auth() {} 4 | 5 | export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { 6 | return { 7 | redirect: { 8 | destination: "/auth/signin", 9 | permanent: true, 10 | }, 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /magic-link/nextjs-pages/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@playwright/test"; 2 | 3 | export default defineConfig({ 4 | webServer: { 5 | command: "npm run build && npm run start", 6 | port: 3000, 7 | reuseExistingServer: true, 8 | }, 9 | testDir: "./tests", 10 | reporter: "list", 11 | 12 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 13 | forbidOnly: !!process.env.CI, 14 | /* Retry on CI only */ 15 | retries: process.env.CI ? 2 : 0, 16 | /* Opt out of parallel tests on CI. */ 17 | workers: process.env.CI ? 1 : undefined, 18 | }); 19 | -------------------------------------------------------------------------------- /magic-link/nextjs-pages/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/magic-link/nextjs-pages/public/favicon.ico -------------------------------------------------------------------------------- /magic-link/nextjs-pages/styles/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /magic-link/nextjs-pages/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | -------------------------------------------------------------------------------- /magic-link/nextjs-pages/supabase/auth/email/email-change.html: -------------------------------------------------------------------------------- 1 |

Confirm Change of Email

2 | 3 |

4 | Follow this link to confirm the update of your email from {{ .Email }} to {{ .NewEmail }}: 5 |

6 |

Change Email

7 | 8 |

Or use the token {{ .Token }}

-------------------------------------------------------------------------------- /magic-link/nextjs-pages/supabase/auth/email/magic-link.html: -------------------------------------------------------------------------------- 1 |

Magic Link

2 | 3 |

Follow this link to login:

4 |

Log In

5 | 6 |

or alternatively, visit the verify token page and enter the token code: {{ .Token }}

7 | 8 |

The link will remain valid for 24 hours or until it is used, whichever comes first. If you need to request another link, please click here.

-------------------------------------------------------------------------------- /magic-link/nextjs-pages/supabase/seed.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/magic-link/nextjs-pages/supabase/seed.sql -------------------------------------------------------------------------------- /magic-link/nextjs-pages/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./{pages,components}/**/*.{html,js,jsx,tsx,ts}'], 4 | theme: { 5 | extend: {} 6 | }, 7 | plugins: [require('daisyui')], 8 | 9 | daisyui: { 10 | themes: ['winter'] 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /magic-link/nextjs/.env.example: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321 2 | NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 -------------------------------------------------------------------------------- /magic-link/nextjs/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /magic-link/nextjs/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env 29 | .env.* 30 | !.env.example 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | -------------------------------------------------------------------------------- /magic-link/nextjs/app/(app)/account/update-email/page.tsx: -------------------------------------------------------------------------------- 1 | import { createServerClient } from "@/lib/supabase-server"; 2 | import EmailForm from "./email-form"; 3 | 4 | export const dynamic = "force-dynamic"; 5 | 6 | export default async function UpdateEmail() { 7 | const supabase = createServerClient(); 8 | 9 | const { 10 | data: { session }, 11 | } = await supabase.auth.getSession(); 12 | 13 | return ; 14 | } 15 | -------------------------------------------------------------------------------- /magic-link/nextjs/app/(app)/page.tsx: -------------------------------------------------------------------------------- 1 | import { createServerClient } from "@/lib/supabase-server"; 2 | 3 | export const dynamic = "force-dynamic"; 4 | 5 | export default async function Home() { 6 | const supabase = createServerClient(); 7 | 8 | const { 9 | data: { session }, 10 | } = await supabase.auth.getSession(); 11 | 12 | return

Welcome {session?.user.email}

; 13 | } 14 | -------------------------------------------------------------------------------- /magic-link/nextjs/app/auth/callback/route.ts: -------------------------------------------------------------------------------- 1 | import { createRouteHandlerClient } from "@supabase/auth-helpers-nextjs"; 2 | import { cookies } from "next/headers"; 3 | import { NextRequest, NextResponse } from "next/server"; 4 | 5 | export async function GET(req: NextRequest) { 6 | const { searchParams } = new URL(req.url); 7 | const code = searchParams.get("code"); 8 | 9 | if (code) { 10 | const supabase = createRouteHandlerClient({ cookies }); 11 | await supabase.auth.exchangeCodeForSession(code); 12 | } 13 | 14 | return NextResponse.redirect(new URL("/", req.url)); 15 | } 16 | -------------------------------------------------------------------------------- /magic-link/nextjs/app/auth/signin/page.tsx: -------------------------------------------------------------------------------- 1 | import MagicLinkForm from "./magiclinkform"; 2 | 3 | export default function SignIn() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /magic-link/nextjs/app/auth/verify-token/page.tsx: -------------------------------------------------------------------------------- 1 | import VerifyTokenForm from "./verifytokenform"; 2 | 3 | export default function VerifyToken() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /magic-link/nextjs/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/magic-link/nextjs/app/favicon.ico -------------------------------------------------------------------------------- /magic-link/nextjs/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import "./globals.css"; 2 | 3 | export const metadata = { 4 | title: "Magic Link + Otp Flow", 5 | description: "Generated by create next app", 6 | }; 7 | 8 | export default function RootLayout({ 9 | children, 10 | }: { 11 | children: React.ReactNode; 12 | }) { 13 | return ( 14 | 15 | {children} 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /magic-link/nextjs/components/Alert.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | className: string; 5 | children: ReactNode; 6 | }; 7 | 8 | export default function Alert({ className, children }: Props) { 9 | return ( 10 |
11 |
12 | {children} 13 |
14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /magic-link/nextjs/components/InputErrorMessage.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | children: ReactNode; 5 | }; 6 | 7 | export default function InputErrorMessage({ children }: Props) { 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /magic-link/nextjs/lib/supabase-server.ts: -------------------------------------------------------------------------------- 1 | import { cookies } from "next/headers"; 2 | import { createServerComponentClient } from "@supabase/auth-helpers-nextjs"; 3 | 4 | export const createServerClient = () => 5 | createServerComponentClient({ cookies }); 6 | -------------------------------------------------------------------------------- /magic-link/nextjs/lib/validationRules.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const required = (name: string) => z.string().min(1, `${name} is required`); 4 | export const email = (name = 'Email') => required(name).email(`${name} is not valid`); 5 | export const password = (number = 5, name = 'Password') => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | -------------------------------------------------------------------------------- /magic-link/nextjs/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | module.exports = nextConfig; 5 | -------------------------------------------------------------------------------- /magic-link/nextjs/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@playwright/test"; 2 | 3 | export default defineConfig({ 4 | webServer: { 5 | command: "npm run build && npm run start", 6 | port: 3000, 7 | reuseExistingServer: true, 8 | }, 9 | testDir: "./tests", 10 | reporter: "list", 11 | 12 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 13 | forbidOnly: !!process.env.CI, 14 | /* Retry on CI only */ 15 | retries: process.env.CI ? 2 : 0, 16 | /* Opt out of parallel tests on CI. */ 17 | workers: process.env.CI ? 1 : undefined, 18 | }); 19 | -------------------------------------------------------------------------------- /magic-link/nextjs/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /magic-link/nextjs/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | -------------------------------------------------------------------------------- /magic-link/nextjs/supabase/auth/email/email-change.html: -------------------------------------------------------------------------------- 1 |

Confirm Change of Email

2 | 3 |

4 | Follow this link to confirm the update of your email from {{ .Email }} to {{ .NewEmail }}: 5 |

6 |

Change Email

7 | 8 |

Or use the token {{ .Token }}

-------------------------------------------------------------------------------- /magic-link/nextjs/supabase/auth/email/magic-link.html: -------------------------------------------------------------------------------- 1 |

Magic Link

2 | 3 |

Follow this link to login:

4 |

Log In

5 | 6 |

or alternatively, visit the verify token page and enter the token code: {{ .Token }}

7 | 8 |

The link will remain valid for 24 hours or until it is used, whichever comes first. If you need to request another link, please click here.

-------------------------------------------------------------------------------- /magic-link/nextjs/supabase/seed.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/magic-link/nextjs/supabase/seed.sql -------------------------------------------------------------------------------- /magic-link/nuxt/.env.example: -------------------------------------------------------------------------------- 1 | SUPABASE_URL=http://localhost:54321 2 | SUPABASE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 -------------------------------------------------------------------------------- /magic-link/nuxt/.gitignore: -------------------------------------------------------------------------------- 1 | # Nuxt dev/build outputs 2 | .output 3 | .nuxt 4 | .nitro 5 | .cache 6 | dist 7 | 8 | # Node dependencies 9 | node_modules 10 | 11 | # Logs 12 | logs 13 | *.log 14 | 15 | # Misc 16 | .DS_Store 17 | .fleet 18 | .idea 19 | 20 | # Local env files 21 | .env 22 | .env.* 23 | !.env.example 24 | -------------------------------------------------------------------------------- /magic-link/nuxt/.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | strict-peer-dependencies=false 3 | -------------------------------------------------------------------------------- /magic-link/nuxt/app.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /magic-link/nuxt/components/Alert.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /magic-link/nuxt/components/InputErrorMessage.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /magic-link/nuxt/layouts/auth.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /magic-link/nuxt/lib/validationRules.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const required = (name: string) => z.string().min(1, `${name} is required`); 4 | export const email = (name = 'Email') => required(name).email(`${name} is not valid`); 5 | export const password = (number = 5, name = 'Password') => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | -------------------------------------------------------------------------------- /magic-link/nuxt/middleware/auth.ts: -------------------------------------------------------------------------------- 1 | export default defineNuxtRouteMiddleware((to, _from) => { 2 | const user = useSupabaseUser(); 3 | 4 | if (!user.value) { 5 | return navigateTo("/auth/signin"); 6 | } 7 | }); 8 | -------------------------------------------------------------------------------- /magic-link/nuxt/nuxt.config.ts: -------------------------------------------------------------------------------- 1 | // https://nuxt.com/docs/api/configuration/nuxt-config 2 | export default defineNuxtConfig({ 3 | devtools: { enabled: true }, 4 | modules: ["@nuxtjs/supabase", "@nuxtjs/tailwindcss"], 5 | runtimeConfig: { 6 | public: { 7 | baseUrl: process.env.BASE_URL || "http://localhost:3000", 8 | }, 9 | }, 10 | supabase: { 11 | redirectOptions: { 12 | login: "/auth/signin", 13 | callback: "/auth/confirm", 14 | exclude: ["/auth/*"], 15 | }, 16 | }, 17 | }); 18 | -------------------------------------------------------------------------------- /magic-link/nuxt/pages/auth/confirm.vue: -------------------------------------------------------------------------------- 1 | 13 | 16 | -------------------------------------------------------------------------------- /magic-link/nuxt/pages/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /magic-link/nuxt/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@playwright/test"; 2 | 3 | export default defineConfig({ 4 | webServer: { 5 | command: "npm run build && npm run preview", 6 | port: 3000, 7 | reuseExistingServer: true, 8 | }, 9 | testDir: "./tests", 10 | reporter: "list", 11 | 12 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 13 | forbidOnly: !!process.env.CI, 14 | /* Retry on CI only */ 15 | retries: process.env.CI ? 2 : 0, 16 | /* Opt out of parallel tests on CI. */ 17 | workers: process.env.CI ? 1 : undefined, 18 | }); 19 | -------------------------------------------------------------------------------- /magic-link/nuxt/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/magic-link/nuxt/public/favicon.ico -------------------------------------------------------------------------------- /magic-link/nuxt/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.nuxt/tsconfig.server.json" 3 | } 4 | -------------------------------------------------------------------------------- /magic-link/nuxt/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | -------------------------------------------------------------------------------- /magic-link/nuxt/supabase/seed.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/magic-link/nuxt/supabase/seed.sql -------------------------------------------------------------------------------- /magic-link/nuxt/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | plugins: [require("daisyui")], 4 | 5 | daisyui: { 6 | themes: ["winter"], 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /magic-link/nuxt/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | -------------------------------------------------------------------------------- /magic-link/react/.env.example: -------------------------------------------------------------------------------- 1 | VITE_SUPABASE_URL=http://localhost:54321 2 | VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 -------------------------------------------------------------------------------- /magic-link/react/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | # local env files 27 | .env 28 | .env.* 29 | !.env.example 30 | -------------------------------------------------------------------------------- /magic-link/react/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Magic Link + Otp Flow 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /magic-link/react/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@playwright/test"; 2 | 3 | export default defineConfig({ 4 | webServer: { 5 | command: "npm run build && npm run preview", 6 | port: 4173, 7 | reuseExistingServer: true, 8 | }, 9 | testDir: "./tests", 10 | reporter: "list", 11 | 12 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 13 | forbidOnly: !!process.env.CI, 14 | /* Retry on CI only */ 15 | retries: process.env.CI ? 2 : 0, 16 | /* Opt out of parallel tests on CI. */ 17 | workers: process.env.CI ? 1 : undefined, 18 | }); 19 | -------------------------------------------------------------------------------- /magic-link/react/src/components/Alert.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | className: string; 5 | children: ReactNode; 6 | }; 7 | 8 | export default function Alert({ className, children }: Props) { 9 | return ( 10 |
11 |
12 | {children} 13 |
14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /magic-link/react/src/components/InputErrorMessage.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | children: ReactNode; 5 | }; 6 | 7 | export default function InputErrorMessage({ children }: Props) { 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /magic-link/react/src/lib/db.ts: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | 3 | export const supabase = createClient( 4 | import.meta.env.VITE_SUPABASE_URL, 5 | import.meta.env.VITE_SUPABASE_ANON_KEY 6 | ); 7 | -------------------------------------------------------------------------------- /magic-link/react/src/lib/validationRules.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const required = (name: string) => z.string().min(1, `${name} is required`); 4 | export const email = (name = 'Email') => required(name).email(`${name} is not valid`); 5 | export const password = (number = 5, name = 'Password') => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | -------------------------------------------------------------------------------- /magic-link/react/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import { BrowserRouter } from "react-router-dom"; 4 | import App from "./App"; 5 | import "./app.css"; 6 | import { AuthProvider } from "./lib/AuthProvider"; 7 | import { supabase } from "./lib/db"; 8 | 9 | ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /magic-link/react/src/routes/index.tsx: -------------------------------------------------------------------------------- 1 | import { useAuth } from "@/lib/AuthProvider"; 2 | 3 | export default function Home() { 4 | const { user } = useAuth(); 5 | 6 | return ( 7 | <> 8 |

Welcome {user?.email}

9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /magic-link/react/src/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /magic-link/react/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | interface ImportMetaEnv { 4 | readonly VITE_SUPABASE_URL: string; 5 | readonly VITE_SUPABASE_ANON_KEY: string; 6 | } 7 | 8 | interface ImportMeta { 9 | readonly env: ImportMetaEnv; 10 | } 11 | -------------------------------------------------------------------------------- /magic-link/react/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | config.local.toml -------------------------------------------------------------------------------- /magic-link/react/supabase/seed.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/magic-link/react/supabase/seed.sql -------------------------------------------------------------------------------- /magic-link/react/tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./src/**/*.{html,js,jsx,tsx,ts}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [require("daisyui")], 8 | 9 | daisyui: { 10 | themes: ["winter"], 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /magic-link/react/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true, 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /magic-link/react/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | import path from "path"; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react()], 8 | resolve: { 9 | alias: { 10 | "@": path.resolve(__dirname, "./src"), 11 | }, 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /magic-link/sveltekit/.env.example: -------------------------------------------------------------------------------- 1 | PUBLIC_SUPABASE_URL=http://localhost:54321 2 | PUBLIC_SUPABASE_ANON_KEY= -------------------------------------------------------------------------------- /magic-link/sveltekit/.eslintignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /magic-link/sveltekit/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'], 5 | plugins: ['svelte3', '@typescript-eslint'], 6 | ignorePatterns: ['*.cjs'], 7 | overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }], 8 | settings: { 9 | 'svelte3/typescript': () => require('typescript') 10 | }, 11 | parserOptions: { 12 | sourceType: 'module', 13 | ecmaVersion: 2020 14 | }, 15 | env: { 16 | browser: true, 17 | es2017: true, 18 | node: true 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /magic-link/sveltekit/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | vite.config.js.timestamp-* 10 | vite.config.ts.timestamp-* 11 | playwright-report 12 | -------------------------------------------------------------------------------- /magic-link/sveltekit/.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /magic-link/sveltekit/.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /magic-link/sveltekit/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100, 6 | "plugins": ["prettier-plugin-svelte"], 7 | "pluginSearchDirs": ["."], 8 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] 9 | } 10 | -------------------------------------------------------------------------------- /magic-link/sveltekit/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import type { PlaywrightTestConfig } from '@playwright/test'; 2 | 3 | const config: PlaywrightTestConfig = { 4 | webServer: { 5 | command: 'npm run build && npm run preview -- --port 5173', 6 | port: 5173 7 | }, 8 | testDir: 'tests', 9 | reporter: 'list', 10 | 11 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 12 | forbidOnly: !!process.env.CI, 13 | /* Retry on CI only */ 14 | retries: process.env.CI ? 2 : 0, 15 | /* Opt out of parallel tests on CI. */ 16 | workers: process.env.CI ? 1 : undefined 17 | }; 18 | 19 | export default config; 20 | -------------------------------------------------------------------------------- /magic-link/sveltekit/src/app.d.ts: -------------------------------------------------------------------------------- 1 | import { SupabaseClient, Session } from '@supabase/supabase-js'; 2 | import { Database } from './lib/schema'; 3 | 4 | declare global { 5 | namespace App { 6 | interface Locals { 7 | supabase: SupabaseClient; 8 | getSession(): Promise; 9 | } 10 | interface PageData { 11 | session: Session | null; 12 | } 13 | // interface Error {} 14 | // interface Platform {} 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /magic-link/sveltekit/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | %sveltekit.head% 9 | 10 | 11 |
%sveltekit.body%
12 | 13 | 14 | -------------------------------------------------------------------------------- /magic-link/sveltekit/src/lib/Alert.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 | 8 |
9 | -------------------------------------------------------------------------------- /magic-link/sveltekit/src/lib/InputErrorMessage.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /magic-link/sveltekit/src/lib/validationRules.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const required = (name: string) => z.string().min(1, `${name} is required`); 4 | export const email = (name = 'Email') => required(name).email(`${name} is not valid`); 5 | export const password = (number = 5, name = 'Password') => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | -------------------------------------------------------------------------------- /magic-link/sveltekit/src/routes/(app)/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | 3 | export const load = async ({ locals: { getSession } }) => { 4 | const session = await getSession(); 5 | 6 | if (!session) { 7 | throw redirect(303, '/auth/signin'); 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /magic-link/sveltekit/src/routes/(app)/+page.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |

Welcome {data.session?.user?.email}

6 | -------------------------------------------------------------------------------- /magic-link/sveltekit/src/routes/(app)/account/+layout.server.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | 3 | export const load = async ({ locals: { getSession } }) => { 4 | const session = await getSession(); 5 | 6 | if (!session) { 7 | throw redirect(303, '/auth/signin'); 8 | } 9 | 10 | return { session }; 11 | }; 12 | -------------------------------------------------------------------------------- /magic-link/sveltekit/src/routes/(auth)/+layout.server.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | 3 | export const load = async ({ url, locals: { getSession } }) => { 4 | const session = await getSession(); 5 | 6 | // only allow the signout subpath when visiting the auth path 7 | if (url.pathname !== '/auth/signout' && session) { 8 | throw redirect(303, '/'); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /magic-link/sveltekit/src/routes/(auth)/+layout.svelte: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Magic Link + Otp Flow

5 |
6 |
7 | 8 |
9 |
10 |
11 | -------------------------------------------------------------------------------- /magic-link/sveltekit/src/routes/(auth)/auth/+page.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | 3 | export const load = async () => { 4 | throw redirect(303, '/auth/signin'); 5 | }; 6 | -------------------------------------------------------------------------------- /magic-link/sveltekit/src/routes/(auth)/auth/callback/+server.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | 3 | export const GET = async (event) => { 4 | const { 5 | url, 6 | locals: { supabase } 7 | } = event; 8 | const code = url.searchParams.get('code'); 9 | 10 | if (code) { 11 | await supabase.auth.exchangeCodeForSession(code); 12 | } 13 | 14 | throw redirect(303, '/'); 15 | }; 16 | -------------------------------------------------------------------------------- /magic-link/sveltekit/src/routes/(auth)/auth/confirm/+server.ts: -------------------------------------------------------------------------------- 1 | import type { EmailOtpType } from '@supabase/supabase-js'; 2 | import { redirect } from '@sveltejs/kit'; 3 | 4 | export const GET = async (event) => { 5 | const { 6 | url, 7 | locals: { supabase } 8 | } = event; 9 | const token_hash = url.searchParams.get('token_hash') as string; 10 | const type = url.searchParams.get('type') as EmailOtpType; 11 | const next = url.searchParams.get('next') ?? '/'; 12 | 13 | if (token_hash && type) { 14 | await supabase.auth.verifyOtp({ token_hash, type }); 15 | } 16 | 17 | throw redirect(303, `/${next.slice(1)}`); 18 | }; -------------------------------------------------------------------------------- /magic-link/sveltekit/src/routes/(auth)/auth/signout/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | 3 | export const actions = { 4 | default: async ({ locals: { supabase, getSession } }) => { 5 | const session = await getSession(); 6 | 7 | if (session) { 8 | await supabase.auth.signOut(); 9 | throw redirect(303, '/auth/signin'); 10 | } 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /magic-link/sveltekit/src/routes/+layout.server.ts: -------------------------------------------------------------------------------- 1 | // src/routes/+layout.server.ts 2 | export const load = async ({ locals: { getSession } }) => { 3 | return { 4 | session: await getSession() 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /magic-link/sveltekit/src/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /magic-link/sveltekit/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/magic-link/sveltekit/static/favicon.png -------------------------------------------------------------------------------- /magic-link/sveltekit/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | config.local.toml -------------------------------------------------------------------------------- /magic-link/sveltekit/supabase/auth/email/email-change.html: -------------------------------------------------------------------------------- 1 |

Confirm Change of Email

2 | 3 |

4 | Follow this link to confirm the update of your email from {{ .Email }} to {{ .NewEmail }}: 5 |

6 |

Change Email

7 | 8 |

Or use the token {{ .Token }}

-------------------------------------------------------------------------------- /magic-link/sveltekit/supabase/auth/email/magic-link.html: -------------------------------------------------------------------------------- 1 |

Magic Link

2 | 3 |

Follow this link to login:

4 |

Log In

5 | 6 |

or alternatively, visit the verify token page and enter the token code: {{ .Token }}

7 | 8 |

The link will remain valid for 24 hours or until it is used, whichever comes first. If you need to request another link, please click here.

-------------------------------------------------------------------------------- /magic-link/sveltekit/supabase/seed.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/magic-link/sveltekit/supabase/seed.sql -------------------------------------------------------------------------------- /magic-link/sveltekit/svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-auto'; 2 | import preprocess from 'svelte-preprocess'; 3 | 4 | /** @type {import('@sveltejs/kit').Config} */ 5 | const config = { 6 | // Consult https://github.com/sveltejs/svelte-preprocess 7 | // for more information about preprocessors 8 | preprocess: preprocess(), 9 | 10 | kit: { 11 | adapter: adapter() 12 | } 13 | }; 14 | 15 | export default config; 16 | -------------------------------------------------------------------------------- /magic-link/sveltekit/tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./src/**/*.{html,js,svelte,ts}'], 4 | theme: { 5 | extend: {} 6 | }, 7 | plugins: [require('daisyui')], 8 | 9 | daisyui: { 10 | themes: ['winter'] 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /magic-link/sveltekit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "checkJs": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true 12 | } 13 | // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias 14 | // 15 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes 16 | // from the referenced tsconfig.json - TypeScript does not merge them in 17 | } 18 | -------------------------------------------------------------------------------- /magic-link/sveltekit/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite'; 2 | import type { UserConfig } from 'vite'; 3 | 4 | const config: UserConfig = { 5 | plugins: [sveltekit()] 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /oauth-flow/OAuth_SignIn_Flow_SSR.excalidraw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/oauth-flow/OAuth_SignIn_Flow_SSR.excalidraw.png -------------------------------------------------------------------------------- /oauth-flow/README.md: -------------------------------------------------------------------------------- 1 | # OAuth Sign-in flow 2 | 3 | These are Supabase projects showing how to do OAuth sign-in. 4 | 5 | These projects make use of: 6 | 7 | - [Supabase Auth Helpers](https://supabase.com/docs/guides/auth/auth-helpers) 8 | - [Zod](https://zod.dev/) Schema Validation library 9 | - [DaisyUI](https://daisyui.com/) 10 | - [tailwindcss](https://tailwindcss.com/) 11 | 12 | ## OAuth SignIn Flow in Server-side rendering (SSR) environment 13 | ![OAuth SignIn Flow](OAuth_SignIn_Flow_SSR.excalidraw.png) 14 | -------------------------------------------------------------------------------- /oauth-flow/nextjs-pages/.env.example: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321 2 | NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 3 | GITHUB_CLIENT_ID= 4 | GITHUB_SECRET= 5 | GOOGLE_CLIENT_ID= 6 | GOOGLE_SECRET= 7 | AZURE_CLIENT_ID= 8 | AZURE_SECRET= -------------------------------------------------------------------------------- /oauth-flow/nextjs-pages/components/Alert.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | className: string; 5 | children: ReactNode; 6 | }; 7 | 8 | export default function Alert({ className, children }: Props) { 9 | return ( 10 |
11 |
12 | {children} 13 |
14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /oauth-flow/nextjs-pages/components/InputErrorMessage.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | children: ReactNode; 5 | }; 6 | 7 | export default function InputErrorMessage({ children }: Props) { 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /oauth-flow/nextjs-pages/lib/validationRules.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const required = (name: string) => z.string().min(1, `${name} is required`); 4 | export const email = (name = 'Email') => required(name).email(`${name} is not valid`); 5 | export const password = (number = 5, name = 'Password') => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | -------------------------------------------------------------------------------- /oauth-flow/nextjs-pages/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | } 5 | 6 | module.exports = nextConfig 7 | -------------------------------------------------------------------------------- /oauth-flow/nextjs-pages/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import { Html, Head, Main, NextScript } from 'next/document' 2 | 3 | export default function Document() { 4 | return ( 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /oauth-flow/nextjs-pages/pages/api/auth/callback.ts: -------------------------------------------------------------------------------- 1 | import { NextApiRequest, NextApiResponse } from "next"; 2 | import { createServerSupabaseClient } from "@supabase/auth-helpers-nextjs"; 3 | 4 | export default async function handler( 5 | req: NextApiRequest, 6 | res: NextApiResponse 7 | ) { 8 | // Create authenticated Supabase Client 9 | const supabase = createServerSupabaseClient({ req, res }); 10 | 11 | const code = req.query.code; 12 | 13 | if (typeof code === "string") { 14 | await supabase.auth.exchangeCodeForSession(code); 15 | } 16 | 17 | res.redirect("/"); 18 | } 19 | -------------------------------------------------------------------------------- /oauth-flow/nextjs-pages/pages/api/hello.ts: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | import type { NextApiRequest, NextApiResponse } from 'next' 3 | 4 | type Data = { 5 | name: string 6 | } 7 | 8 | export default function handler( 9 | req: NextApiRequest, 10 | res: NextApiResponse 11 | ) { 12 | res.status(200).json({ name: 'John Doe' }) 13 | } 14 | -------------------------------------------------------------------------------- /oauth-flow/nextjs-pages/pages/auth/index.tsx: -------------------------------------------------------------------------------- 1 | import { GetServerSidePropsContext } from "next"; 2 | 3 | export default function Auth() {} 4 | 5 | export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { 6 | return { 7 | redirect: { 8 | destination: "/auth/signin", 9 | permanent: true, 10 | }, 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /oauth-flow/nextjs-pages/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/oauth-flow/nextjs-pages/public/favicon.ico -------------------------------------------------------------------------------- /oauth-flow/nextjs-pages/styles/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /oauth-flow/nextjs-pages/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | config.local.toml -------------------------------------------------------------------------------- /oauth-flow/nextjs-pages/supabase/seed.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/oauth-flow/nextjs-pages/supabase/seed.sql -------------------------------------------------------------------------------- /oauth-flow/nextjs-pages/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./{pages,components}/**/*.{html,js,jsx,tsx,ts}'], 4 | theme: { 5 | extend: {} 6 | }, 7 | plugins: [require('daisyui')], 8 | 9 | daisyui: { 10 | themes: ['winter'] 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /oauth-flow/nextjs/.env.example: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321 2 | NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 3 | GITHUB_CLIENT_ID= 4 | GITHUB_SECRET= 5 | GOOGLE_CLIENT_ID= 6 | GOOGLE_SECRET= 7 | AZURE_CLIENT_ID= 8 | AZURE_SECRET= -------------------------------------------------------------------------------- /oauth-flow/nextjs/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /oauth-flow/nextjs/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env 29 | .env.* 30 | !.env.example 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | -------------------------------------------------------------------------------- /oauth-flow/nextjs/app/(app)/account/update-email/page.tsx: -------------------------------------------------------------------------------- 1 | import { createServerClient } from "@/lib/supabase-server"; 2 | import EmailForm from "./email-form"; 3 | 4 | export default async function UpdateEmail() { 5 | const supabase = createServerClient(); 6 | 7 | const { 8 | data: { session }, 9 | } = await supabase.auth.getSession(); 10 | 11 | return ; 12 | } 13 | -------------------------------------------------------------------------------- /oauth-flow/nextjs/app/(app)/page.tsx: -------------------------------------------------------------------------------- 1 | import { createServerClient } from "@/lib/supabase-server"; 2 | 3 | export default async function Home() { 4 | const supabase = createServerClient(); 5 | 6 | const { 7 | data: { session }, 8 | } = await supabase.auth.getSession(); 9 | 10 | return

Welcome {session?.user.email}

; 11 | } 12 | -------------------------------------------------------------------------------- /oauth-flow/nextjs/app/auth/callback/route.ts: -------------------------------------------------------------------------------- 1 | import { createRouteHandlerClient } from "@supabase/auth-helpers-nextjs"; 2 | import { cookies } from "next/headers"; 3 | import { NextRequest, NextResponse } from "next/server"; 4 | 5 | export async function GET(req: NextRequest) { 6 | const { searchParams } = new URL(req.url); 7 | const code = searchParams.get("code"); 8 | 9 | if (code) { 10 | const supabase = createRouteHandlerClient({ cookies }); 11 | await supabase.auth.exchangeCodeForSession(code); 12 | } 13 | 14 | return NextResponse.redirect(new URL("/", req.url)); 15 | } 16 | -------------------------------------------------------------------------------- /oauth-flow/nextjs/app/auth/verify-token/page.tsx: -------------------------------------------------------------------------------- 1 | import VerifyTokenForm from "./verifytokenform"; 2 | 3 | export default function VerifyToken() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /oauth-flow/nextjs/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/oauth-flow/nextjs/app/favicon.ico -------------------------------------------------------------------------------- /oauth-flow/nextjs/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import "./globals.css"; 2 | 3 | export const metadata = { 4 | title: "OAuth Flow", 5 | description: "Generated by create next app", 6 | }; 7 | 8 | export default function RootLayout({ 9 | children, 10 | }: { 11 | children: React.ReactNode; 12 | }) { 13 | return ( 14 | 15 | {children} 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /oauth-flow/nextjs/components/Alert.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | className: string; 5 | children: ReactNode; 6 | }; 7 | 8 | export default function Alert({ className, children }: Props) { 9 | return ( 10 |
11 |
12 | {children} 13 |
14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /oauth-flow/nextjs/components/InputErrorMessage.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | children: ReactNode; 5 | }; 6 | 7 | export default function InputErrorMessage({ children }: Props) { 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /oauth-flow/nextjs/lib/supabase-server.ts: -------------------------------------------------------------------------------- 1 | import { cookies } from "next/headers"; 2 | import { createServerComponentClient } from "@supabase/auth-helpers-nextjs"; 3 | 4 | export const createServerClient = () => 5 | createServerComponentClient({ cookies }); 6 | -------------------------------------------------------------------------------- /oauth-flow/nextjs/lib/validationRules.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const required = (name: string) => z.string().min(1, `${name} is required`); 4 | export const email = (name = 'Email') => required(name).email(`${name} is not valid`); 5 | export const password = (number = 5, name = 'Password') => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | -------------------------------------------------------------------------------- /oauth-flow/nextjs/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | module.exports = nextConfig; 5 | -------------------------------------------------------------------------------- /oauth-flow/nextjs/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /oauth-flow/nextjs/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | -------------------------------------------------------------------------------- /oauth-flow/nextjs/supabase/seed.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/oauth-flow/nextjs/supabase/seed.sql -------------------------------------------------------------------------------- /oauth-flow/nuxt/.env.example: -------------------------------------------------------------------------------- 1 | SUPABASE_URL=http://localhost:54321 2 | SUPABASE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 3 | GITHUB_CLIENT_ID= 4 | GITHUB_SECRET= 5 | GOOGLE_CLIENT_ID= 6 | GOOGLE_SECRET= 7 | AZURE_CLIENT_ID= 8 | AZURE_SECRET= -------------------------------------------------------------------------------- /oauth-flow/nuxt/.gitignore: -------------------------------------------------------------------------------- 1 | # Nuxt dev/build outputs 2 | .output 3 | .nuxt 4 | .nitro 5 | .cache 6 | dist 7 | 8 | # Node dependencies 9 | node_modules 10 | 11 | # Logs 12 | logs 13 | *.log 14 | 15 | # Misc 16 | .DS_Store 17 | .fleet 18 | .idea 19 | 20 | # Local env files 21 | .env 22 | .env.* 23 | !.env.example 24 | -------------------------------------------------------------------------------- /oauth-flow/nuxt/.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | strict-peer-dependencies=false 3 | -------------------------------------------------------------------------------- /oauth-flow/nuxt/app.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /oauth-flow/nuxt/components/Alert.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /oauth-flow/nuxt/components/InputErrorMessage.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /oauth-flow/nuxt/layouts/auth.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /oauth-flow/nuxt/lib/validationRules.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const required = (name: string) => z.string().min(1, `${name} is required`); 4 | export const email = (name = 'Email') => required(name).email(`${name} is not valid`); 5 | export const password = (number = 5, name = 'Password') => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | -------------------------------------------------------------------------------- /oauth-flow/nuxt/middleware/auth.ts: -------------------------------------------------------------------------------- 1 | export default defineNuxtRouteMiddleware((to, _from) => { 2 | const user = useSupabaseUser(); 3 | 4 | if (!user.value) { 5 | return navigateTo("/auth/signin"); 6 | } 7 | }); 8 | -------------------------------------------------------------------------------- /oauth-flow/nuxt/pages/account/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /oauth-flow/nuxt/pages/auth/confirm.vue: -------------------------------------------------------------------------------- 1 | 13 | 16 | -------------------------------------------------------------------------------- /oauth-flow/nuxt/pages/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /oauth-flow/nuxt/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/oauth-flow/nuxt/public/favicon.ico -------------------------------------------------------------------------------- /oauth-flow/nuxt/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.nuxt/tsconfig.server.json" 3 | } 4 | -------------------------------------------------------------------------------- /oauth-flow/nuxt/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | -------------------------------------------------------------------------------- /oauth-flow/nuxt/supabase/seed.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/oauth-flow/nuxt/supabase/seed.sql -------------------------------------------------------------------------------- /oauth-flow/nuxt/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | plugins: [require("daisyui")], 4 | 5 | daisyui: { 6 | themes: ["winter"], 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /oauth-flow/nuxt/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | -------------------------------------------------------------------------------- /oauth-flow/react/.env.example: -------------------------------------------------------------------------------- 1 | VITE_SUPABASE_URL=http://localhost:54321 2 | VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 3 | GITHUB_CLIENT_ID= 4 | GITHUB_SECRET= 5 | GOOGLE_CLIENT_ID= 6 | GOOGLE_SECRET= 7 | AZURE_CLIENT_ID= 8 | AZURE_SECRET= -------------------------------------------------------------------------------- /oauth-flow/react/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | # local env files 27 | .env 28 | .env.* 29 | !.env.example 30 | -------------------------------------------------------------------------------- /oauth-flow/react/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | OAuth Flow 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /oauth-flow/react/src/components/Alert.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | className: string; 5 | children: ReactNode; 6 | }; 7 | 8 | export default function Alert({ className, children }: Props) { 9 | return ( 10 |
11 |
12 | {children} 13 |
14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /oauth-flow/react/src/components/InputErrorMessage.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | children: ReactNode; 5 | }; 6 | 7 | export default function InputErrorMessage({ children }: Props) { 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /oauth-flow/react/src/lib/db.ts: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | 3 | export const supabase = createClient( 4 | import.meta.env.VITE_SUPABASE_URL, 5 | import.meta.env.VITE_SUPABASE_ANON_KEY, 6 | { 7 | auth: { 8 | flowType: "pkce", 9 | }, 10 | } 11 | ); 12 | -------------------------------------------------------------------------------- /oauth-flow/react/src/lib/validationRules.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const required = (name: string) => z.string().min(1, `${name} is required`); 4 | export const email = (name = 'Email') => required(name).email(`${name} is not valid`); 5 | export const password = (number = 5, name = 'Password') => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | -------------------------------------------------------------------------------- /oauth-flow/react/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import { BrowserRouter } from "react-router-dom"; 4 | import App from "./App"; 5 | import "./app.css"; 6 | import { AuthProvider } from "./lib/AuthProvider"; 7 | import { supabase } from "./lib/db"; 8 | 9 | ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /oauth-flow/react/src/routes/account/index.tsx: -------------------------------------------------------------------------------- 1 | import { useAuth } from "@/lib/AuthProvider"; 2 | 3 | export default function AccountIndex() { 4 | const { user } = useAuth(); 5 | 6 | return ( 7 | <> 8 |
9 |

Account

10 |

11 | Hi {user?.email}, you can update your email or password from here 12 |

13 |
14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /oauth-flow/react/src/routes/index.tsx: -------------------------------------------------------------------------------- 1 | import { useAuth } from "@/lib/AuthProvider"; 2 | 3 | export default function Home() { 4 | const { user } = useAuth(); 5 | 6 | return ( 7 | <> 8 |

Welcome {user?.email}

9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /oauth-flow/react/src/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /oauth-flow/react/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | interface ImportMetaEnv { 4 | readonly VITE_SUPABASE_URL: string; 5 | readonly VITE_SUPABASE_ANON_KEY: string; 6 | } 7 | 8 | interface ImportMeta { 9 | readonly env: ImportMetaEnv; 10 | } 11 | -------------------------------------------------------------------------------- /oauth-flow/react/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | config.local.toml -------------------------------------------------------------------------------- /oauth-flow/react/supabase/seed.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/oauth-flow/react/supabase/seed.sql -------------------------------------------------------------------------------- /oauth-flow/react/tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./src/**/*.{html,js,jsx,tsx,ts}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [require("daisyui")], 8 | 9 | daisyui: { 10 | themes: ["winter"], 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /oauth-flow/react/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true, 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /oauth-flow/react/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | import path from "path"; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react()], 8 | resolve: { 9 | alias: { 10 | "@": path.resolve(__dirname, "./src"), 11 | }, 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/.env.example: -------------------------------------------------------------------------------- 1 | PUBLIC_SUPABASE_URL=http://localhost:54321 2 | PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 3 | GITHUB_CLIENT_ID= 4 | GITHUB_SECRET= 5 | GOOGLE_CLIENT_ID= 6 | GOOGLE_SECRET= 7 | AZURE_CLIENT_ID= 8 | AZURE_SECRET= -------------------------------------------------------------------------------- /oauth-flow/sveltekit/.eslintignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'], 5 | plugins: ['svelte3', '@typescript-eslint'], 6 | ignorePatterns: ['*.cjs'], 7 | overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }], 8 | settings: { 9 | 'svelte3/typescript': () => require('typescript') 10 | }, 11 | parserOptions: { 12 | sourceType: 'module', 13 | ecmaVersion: 2020 14 | }, 15 | env: { 16 | browser: true, 17 | es2017: true, 18 | node: true 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | vite.config.js.timestamp-* 10 | vite.config.ts.timestamp-* 11 | playwright-report 12 | config.local.toml -------------------------------------------------------------------------------- /oauth-flow/sveltekit/.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100, 6 | "plugins": ["prettier-plugin-svelte"], 7 | "pluginSearchDirs": ["."], 8 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] 9 | } 10 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/src/app.d.ts: -------------------------------------------------------------------------------- 1 | import { SupabaseClient, Session } from '@supabase/supabase-js'; 2 | 3 | declare global { 4 | namespace App { 5 | interface Locals { 6 | supabase: SupabaseClient; 7 | getSession(): Promise; 8 | } 9 | interface PageData { 10 | session: Session | null; 11 | } 12 | // interface Error {} 13 | // interface Platform {} 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | %sveltekit.head% 9 | 10 | 11 |
%sveltekit.body%
12 | 13 | 14 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/src/lib/Alert.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 | 8 |
9 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/src/lib/InputErrorMessage.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/src/lib/validationRules.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const required = (name: string) => z.string().min(1, `${name} is required`); 4 | export const email = (name = 'Email') => required(name).email(`${name} is not valid`); 5 | export const password = (number = 5, name = 'Password') => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/src/routes/(app)/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | import type { PageServerLoad } from './$types'; 3 | 4 | export const load: PageServerLoad = async ({ locals: { getSession } }) => { 5 | const session = await getSession(); 6 | 7 | if (!session) { 8 | throw redirect(303, '/auth/signin'); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/src/routes/(app)/+page.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 |

Welcome {data.session?.user?.email}

8 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/src/routes/(app)/account/+layout.server.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | import type { LayoutServerLoad } from './$types'; 3 | 4 | export const load: LayoutServerLoad = async ({ locals: { getSession } }) => { 5 | const session = await getSession(); 6 | 7 | if (!session) { 8 | throw redirect(303, '/auth/signin'); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/src/routes/(app)/logging-in/+layout@.svelte: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/src/routes/(auth)/+layout.server.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | import type { LayoutServerLoad } from './$types'; 3 | 4 | export const load: LayoutServerLoad = async ({ url, locals: { getSession } }) => { 5 | const session = await getSession(); 6 | 7 | // only allow the signout subpath when visiting the auth path 8 | if (url.pathname !== '/auth/signout' && session) { 9 | throw redirect(303, '/'); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/src/routes/(auth)/+layout.svelte: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

OAuth Flow

5 |
6 |
7 | 8 |
9 |
10 |
11 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/src/routes/(auth)/auth/+page.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | import type { PageLoad } from './$types'; 3 | 4 | export const load: PageLoad = async () => { 5 | throw redirect(303, '/auth/signin'); 6 | }; 7 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/src/routes/(auth)/auth/callback/+server.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | 3 | export const GET = async ({ url, locals: { supabase } }) => { 4 | const code = url.searchParams.get('code'); 5 | 6 | if (code) { 7 | await supabase.auth.exchangeCodeForSession(code); 8 | } 9 | 10 | throw redirect(303, '/'); 11 | }; 12 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/src/routes/(auth)/auth/signout/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | 3 | export const actions = { 4 | default: async ({ locals: { supabase, getSession } }) => { 5 | const session = await getSession(); 6 | 7 | if (session) { 8 | await supabase.auth.signOut(); 9 | throw redirect(303, '/auth/signin'); 10 | } 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/src/routes/+layout.server.ts: -------------------------------------------------------------------------------- 1 | // src/routes/+layout.server.ts 2 | import type { LayoutServerLoad } from './$types'; 3 | 4 | export const load: LayoutServerLoad = async ({ locals: { getSession } }) => { 5 | return { 6 | session: await getSession() 7 | }; 8 | }; 9 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/src/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/oauth-flow/sveltekit/static/favicon.png -------------------------------------------------------------------------------- /oauth-flow/sveltekit/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | config.local.toml -------------------------------------------------------------------------------- /oauth-flow/sveltekit/supabase/seed.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/oauth-flow/sveltekit/supabase/seed.sql -------------------------------------------------------------------------------- /oauth-flow/sveltekit/svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-auto'; 2 | import preprocess from 'svelte-preprocess'; 3 | 4 | /** @type {import('@sveltejs/kit').Config} */ 5 | const config = { 6 | // Consult https://github.com/sveltejs/svelte-preprocess 7 | // for more information about preprocessors 8 | preprocess: preprocess(), 9 | 10 | kit: { 11 | adapter: adapter() 12 | } 13 | }; 14 | 15 | export default config; 16 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./src/**/*.{html,js,svelte,ts}'], 4 | theme: { 5 | extend: {} 6 | }, 7 | plugins: [require('daisyui')], 8 | 9 | daisyui: { 10 | themes: ['winter'] 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "checkJs": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true 12 | } 13 | // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias 14 | // 15 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes 16 | // from the referenced tsconfig.json - TypeScript does not merge them in 17 | } 18 | -------------------------------------------------------------------------------- /oauth-flow/sveltekit/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite'; 2 | import type { UserConfig } from 'vite'; 3 | 4 | const config: UserConfig = { 5 | plugins: [sveltekit()] 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /reset-flow/README.md: -------------------------------------------------------------------------------- 1 | # Reset Password and Change Email flow 2 | 3 | These are Supabase projects showing how to do password reset and email change. 4 | 5 | These projects make use of: 6 | 7 | - [Supabase Auth Helpers](https://supabase.com/docs/guides/auth/auth-helpers) 8 | - [Zod](https://zod.dev/) Schema Validation library 9 | - [DaisyUI](https://daisyui.com/) 10 | - [tailwindcss](https://tailwindcss.com/) 11 | - [Playwright](https://playwright.dev/) e2e testing 12 | 13 | ## Reset Password Flow in Server-side rendering (SSR) environment 14 | ![Password Reset Flow](Reset_Password_Flow_SSR.excalidraw.png) 15 | -------------------------------------------------------------------------------- /reset-flow/Reset_Password_Flow_SSR.excalidraw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/reset-flow/Reset_Password_Flow_SSR.excalidraw.png -------------------------------------------------------------------------------- /reset-flow/auth-ui/nextjs/.env.example: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321 2 | NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 -------------------------------------------------------------------------------- /reset-flow/auth-ui/nextjs/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/nextjs/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env 29 | .env.* 30 | !.env.example 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/nextjs/app/(app)/account/update-email/page.tsx: -------------------------------------------------------------------------------- 1 | import { createServerClient } from "@/lib/supabase-server"; 2 | import EmailForm from "./email-form"; 3 | 4 | export const dynamic = "force-dynamic"; 5 | 6 | export default async function UpdateEmail() { 7 | const supabase = createServerClient(); 8 | 9 | const { 10 | data: { session }, 11 | } = await supabase.auth.getSession(); 12 | 13 | return ; 14 | } 15 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/nextjs/app/(app)/account/update-password/page.tsx: -------------------------------------------------------------------------------- 1 | import { createServerClient } from "@/lib/supabase-server"; 2 | import PasswordForm from "./password-form"; 3 | 4 | export const dynamic = "force-dynamic"; 5 | 6 | export default async function UpdatePassword() { 7 | const supabase = createServerClient(); 8 | 9 | const { 10 | data: { session }, 11 | } = await supabase.auth.getSession(); 12 | 13 | return ; 14 | } 15 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/nextjs/app/(app)/page.tsx: -------------------------------------------------------------------------------- 1 | import { createServerClient } from "@/lib/supabase-server"; 2 | 3 | export const dynamic = "force-dynamic"; 4 | 5 | export default async function Home() { 6 | const supabase = createServerClient(); 7 | 8 | const { 9 | data: { session }, 10 | } = await supabase.auth.getSession(); 11 | 12 | return

Welcome {session?.user.email}

; 13 | } 14 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/nextjs/app/auth/forgotpassword/page.tsx: -------------------------------------------------------------------------------- 1 | import ForgotPasswordForm from "./forgot-password-form"; 2 | 3 | export default function ForgotPassword() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/nextjs/app/auth/signin/page.tsx: -------------------------------------------------------------------------------- 1 | import SignInForm from "./sign-in-form"; 2 | 3 | export default function SignIn() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/nextjs/app/auth/signup/page.tsx: -------------------------------------------------------------------------------- 1 | import SignUpForm from "./sign-up-form"; 2 | 3 | export default function SignIn() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/nextjs/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/reset-flow/auth-ui/nextjs/app/favicon.ico -------------------------------------------------------------------------------- /reset-flow/auth-ui/nextjs/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import "./globals.css"; 2 | 3 | export const metadata = { 4 | title: "Magic Link + Otp Flow", 5 | description: "Generated by create next app", 6 | }; 7 | 8 | export default function RootLayout({ 9 | children, 10 | }: { 11 | children: React.ReactNode; 12 | }) { 13 | return ( 14 | 15 | {children} 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/nextjs/components/Alert.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | className: string; 5 | children: ReactNode; 6 | }; 7 | 8 | export default function Alert({ className, children }: Props) { 9 | return ( 10 |
11 |
12 | {children} 13 |
14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/nextjs/components/InputErrorMessage.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | children: ReactNode; 5 | }; 6 | 7 | export default function InputErrorMessage({ children }: Props) { 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/nextjs/lib/supabase-server.ts: -------------------------------------------------------------------------------- 1 | import { cookies } from "next/headers"; 2 | import { createServerComponentClient } from "@supabase/auth-helpers-nextjs"; 3 | 4 | export const createServerClient = () => 5 | createServerComponentClient({ cookies }); 6 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/nextjs/lib/validationRules.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const required = (name: string) => z.string().min(1, `${name} is required`); 4 | export const email = (name = 'Email') => required(name).email(`${name} is not valid`); 5 | export const password = (number = 5, name = 'Password') => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/nextjs/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | module.exports = nextConfig; 5 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/nextjs/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@playwright/test"; 2 | 3 | export default defineConfig({ 4 | webServer: { 5 | command: "npm run build && npm run start", 6 | port: 3000, 7 | reuseExistingServer: true, 8 | }, 9 | testDir: "./tests", 10 | reporter: "list", 11 | 12 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 13 | forbidOnly: !!process.env.CI, 14 | /* Retry on CI only */ 15 | retries: process.env.CI ? 2 : 0, 16 | /* Opt out of parallel tests on CI. */ 17 | workers: process.env.CI ? 1 : undefined, 18 | }); 19 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/nextjs/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/nextjs/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/nextjs/supabase/auth/email/email-change.html: -------------------------------------------------------------------------------- 1 |

Confirm Change of Email

2 | 3 |

4 | Follow this link to confirm the update of your email from {{ .Email }} to {{ .NewEmail }}: 5 |

6 |

Change Email

7 | 8 |

Or use the token {{ .Token }}

-------------------------------------------------------------------------------- /reset-flow/auth-ui/nextjs/supabase/auth/email/magic-link.html: -------------------------------------------------------------------------------- 1 |

Magic Link

2 | 3 |

Follow this link to login:

4 |

Log In

5 | 6 |

The link will remain valid for 24 hours or until it is used, whichever comes first. If you need to request another link, please click here.

-------------------------------------------------------------------------------- /reset-flow/auth-ui/nextjs/supabase/seed.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/reset-flow/auth-ui/nextjs/supabase/seed.sql -------------------------------------------------------------------------------- /reset-flow/auth-ui/react/.env.example: -------------------------------------------------------------------------------- 1 | VITE_SUPABASE_URL=http://localhost:54321 2 | VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 -------------------------------------------------------------------------------- /reset-flow/auth-ui/react/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | # local env files 27 | .env 28 | .env.* 29 | !.env.example 30 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/react/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Reset Flow 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/react/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@playwright/test"; 2 | 3 | export default defineConfig({ 4 | webServer: { 5 | command: "npm run build && npm run preview", 6 | port: 4173, 7 | reuseExistingServer: true, 8 | }, 9 | testDir: "./tests", 10 | reporter: "list", 11 | 12 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 13 | forbidOnly: !!process.env.CI, 14 | /* Retry on CI only */ 15 | retries: process.env.CI ? 2 : 0, 16 | /* Opt out of parallel tests on CI. */ 17 | workers: process.env.CI ? 1 : undefined, 18 | }); 19 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/react/src/components/Alert.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | className: string; 5 | children: ReactNode; 6 | }; 7 | 8 | export default function Alert({ className, children }: Props) { 9 | return ( 10 |
11 |
12 | {children} 13 |
14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/react/src/components/InputErrorMessage.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | children: ReactNode; 5 | }; 6 | 7 | export default function InputErrorMessage({ children }: Props) { 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/react/src/lib/db.ts: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | 3 | export const supabase = createClient( 4 | import.meta.env.VITE_SUPABASE_URL, 5 | import.meta.env.VITE_SUPABASE_ANON_KEY 6 | ); 7 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/react/src/lib/validationRules.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const required = (name: string) => z.string().min(1, `${name} is required`); 4 | export const email = (name = 'Email') => required(name).email(`${name} is not valid`); 5 | export const password = (number = 5, name = 'Password') => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/react/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import { BrowserRouter } from "react-router-dom"; 4 | import App from "./App"; 5 | import "./app.css"; 6 | import { AuthProvider } from "./lib/AuthProvider"; 7 | import { supabase } from "./lib/db"; 8 | 9 | ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/react/src/routes/index.tsx: -------------------------------------------------------------------------------- 1 | import { useAuth } from "@/lib/AuthProvider"; 2 | 3 | export default function Home() { 4 | const { user } = useAuth(); 5 | 6 | return ( 7 | <> 8 |

Welcome {user?.email}

9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/react/src/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/react/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | interface ImportMetaEnv { 4 | readonly VITE_SUPABASE_URL: string; 5 | readonly VITE_SUPABASE_ANON_KEY: string; 6 | } 7 | 8 | interface ImportMeta { 9 | readonly env: ImportMetaEnv; 10 | } 11 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/react/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | config.local.toml -------------------------------------------------------------------------------- /reset-flow/auth-ui/react/supabase/seed.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/reset-flow/auth-ui/react/supabase/seed.sql -------------------------------------------------------------------------------- /reset-flow/auth-ui/react/tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./src/**/*.{html,js,jsx,tsx,ts}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [require("daisyui")], 8 | 9 | daisyui: { 10 | themes: ["winter"], 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/react/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true, 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /reset-flow/auth-ui/react/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | import path from "path"; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react()], 8 | resolve: { 9 | alias: { 10 | "@": path.resolve(__dirname, "./src"), 11 | }, 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /reset-flow/express/.env.example: -------------------------------------------------------------------------------- 1 | SUPABASE_URL=http://localhost:54321 2 | SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 -------------------------------------------------------------------------------- /reset-flow/express/.gitignore: -------------------------------------------------------------------------------- 1 | # local env files 2 | .env 3 | .env.* 4 | !.env.example -------------------------------------------------------------------------------- /reset-flow/express/lib/utils.js: -------------------------------------------------------------------------------- 1 | exports.formatError = (zodError) => { 2 | const formattedErrors = {}; 3 | zodError.errors.forEach((err) => { 4 | const k = err.path.pop(); 5 | if (formattedErrors[k] == null) { 6 | formattedErrors[k] = err.message; 7 | } 8 | }); 9 | return formattedErrors; 10 | }; 11 | 12 | exports.success = (message, data) => ({ 13 | success: true, 14 | message, 15 | ...data, 16 | }); 17 | 18 | exports.fault = (message, data) => ({ 19 | success: false, 20 | message, 21 | ...data, 22 | }); 23 | -------------------------------------------------------------------------------- /reset-flow/express/lib/validationRules.js: -------------------------------------------------------------------------------- 1 | const z = require("zod").z; 2 | 3 | const required = (name) => z.string().min(1, `${name} is required`); 4 | const email = (name = "Email") => required(name).email(`${name} is not valid`); 5 | const password = (number = 5, name = "Password") => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | 8 | module.exports = { 9 | required, 10 | email, 11 | password, 12 | }; 13 | -------------------------------------------------------------------------------- /reset-flow/express/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@playwright/test"; 2 | 3 | export default defineConfig({ 4 | webServer: { 5 | command: "npm run build && npm run start", 6 | port: 3000, 7 | reuseExistingServer: true, 8 | }, 9 | testDir: "./tests", 10 | reporter: "list", 11 | 12 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 13 | forbidOnly: !!process.env.CI, 14 | /* Retry on CI only */ 15 | retries: process.env.CI ? 2 : 0, 16 | /* Opt out of parallel tests on CI. */ 17 | workers: process.env.CI ? 1 : undefined, 18 | }); 19 | -------------------------------------------------------------------------------- /reset-flow/express/public/stylesheets/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /reset-flow/express/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require("express"); 2 | var router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get("/", function (req, res, next) { 6 | const session = res.locals.session; 7 | res.render("index", { user: session?.user }); 8 | }); 9 | 10 | module.exports = router; 11 | -------------------------------------------------------------------------------- /reset-flow/express/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | -------------------------------------------------------------------------------- /reset-flow/express/supabase/auth/email/email-change.html: -------------------------------------------------------------------------------- 1 |

Confirm Change of Email

2 | 3 |

4 | Follow this link to confirm the update of your email from {{ .Email }} to {{ .NewEmail }}: 5 |

6 |

Change Email

7 | 8 |

Or use the token {{ .Token }}

-------------------------------------------------------------------------------- /reset-flow/express/supabase/auth/email/magic-link.html: -------------------------------------------------------------------------------- 1 |

Magic Link

2 | 3 |

Follow this link to login:

4 |

Log In

5 | 6 |

or alternatively, visit the verify token page and enter the token code: {{ .Token }}

7 | 8 |

The link will remain valid for 24 hours or until it is used, whichever comes first. If you need to request another link, please click here.

-------------------------------------------------------------------------------- /reset-flow/express/supabase/seed.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/reset-flow/express/supabase/seed.sql -------------------------------------------------------------------------------- /reset-flow/express/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./views/**/*.{html,pug}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [require("daisyui")], 8 | 9 | daisyui: { 10 | themes: ["winter"], 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /reset-flow/express/views/_partials.pug: -------------------------------------------------------------------------------- 1 | mixin alert(content) 2 | div(class=`alert shadow-lg rounded-none`, class!=attributes.class) 3 | div 4 | span= content 5 | 6 | mixin input_error(content) 7 | span(class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1")= content -------------------------------------------------------------------------------- /reset-flow/express/views/app/layout.pug: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | 3 | block content 4 | main(data-theme='winter') 5 | .flex.flex-col.h-screen 6 | .navbar.border-b.border-gray-300.py-8.px-4 7 | .flex-1 8 | h1.font-semibold 9 | a(href='/') Reset Flow 10 | .flex-none.space-x-10 11 | a(class='btn btn-outline no-animation' href='/account') Account 12 | form(class='block' action='/auth/signout' method='post') 13 | button(type='submit') Sign out 14 | .grid.place-items-center.my-20.mx-2 15 | block main 16 | -------------------------------------------------------------------------------- /reset-flow/express/views/auth/layout.pug: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | 3 | block content 4 | main(data-theme='winter') 5 | .flex.flex-col.h-screen 6 | div(class='header border-b border-gray-300 py-8 px-4 mx-8 text-center') 7 | h1.font-semibold Reset Flow 8 | div(class='grid place-items-center my-20 mx-2 sm:my-auto') 9 | block main 10 | -------------------------------------------------------------------------------- /reset-flow/express/views/error.pug: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.stack} 7 | -------------------------------------------------------------------------------- /reset-flow/express/views/index.pug: -------------------------------------------------------------------------------- 1 | extends app/layout 2 | 3 | block main 4 | h1 Welcome #{user.email} 5 | -------------------------------------------------------------------------------- /reset-flow/express/views/layout.pug: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title= `${title ? title + ' | ' : ''}Reset Flow` 5 | link(rel='icon', type='image/svg+xml', href='/images/vite.svg') 6 | link(rel='stylesheet', href='/stylesheets/style.css') 7 | body 8 | block content 9 | -------------------------------------------------------------------------------- /reset-flow/nextjs-pages/.env.example: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321 2 | NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 -------------------------------------------------------------------------------- /reset-flow/nextjs-pages/components/Alert.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | className: string; 5 | children: ReactNode; 6 | }; 7 | 8 | export default function Alert({ className, children }: Props) { 9 | return ( 10 |
11 |
12 | {children} 13 |
14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /reset-flow/nextjs-pages/components/InputErrorMessage.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | children: ReactNode; 5 | }; 6 | 7 | export default function InputErrorMessage({ children }: Props) { 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /reset-flow/nextjs-pages/lib/validationRules.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const required = (name: string) => z.string().min(1, `${name} is required`); 4 | export const email = (name = 'Email') => required(name).email(`${name} is not valid`); 5 | export const password = (number = 5, name = 'Password') => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | -------------------------------------------------------------------------------- /reset-flow/nextjs-pages/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | } 5 | 6 | module.exports = nextConfig 7 | -------------------------------------------------------------------------------- /reset-flow/nextjs-pages/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import { Html, Head, Main, NextScript } from 'next/document' 2 | 3 | export default function Document() { 4 | return ( 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /reset-flow/nextjs-pages/pages/auth/index.tsx: -------------------------------------------------------------------------------- 1 | import { GetServerSidePropsContext } from "next"; 2 | 3 | export default function Auth() {} 4 | 5 | export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { 6 | return { 7 | redirect: { 8 | destination: "/auth/signin", 9 | permanent: true, 10 | }, 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /reset-flow/nextjs-pages/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@playwright/test"; 2 | 3 | export default defineConfig({ 4 | webServer: { 5 | command: "npm run build && npm run start", 6 | port: 3000, 7 | reuseExistingServer: true, 8 | }, 9 | testDir: "./tests", 10 | reporter: "list", 11 | 12 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 13 | forbidOnly: !!process.env.CI, 14 | /* Retry on CI only */ 15 | retries: process.env.CI ? 2 : 0, 16 | /* Opt out of parallel tests on CI. */ 17 | workers: process.env.CI ? 1 : undefined, 18 | }); 19 | -------------------------------------------------------------------------------- /reset-flow/nextjs-pages/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/reset-flow/nextjs-pages/public/favicon.ico -------------------------------------------------------------------------------- /reset-flow/nextjs-pages/styles/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /reset-flow/nextjs-pages/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | -------------------------------------------------------------------------------- /reset-flow/nextjs-pages/supabase/auth/email/email-change.html: -------------------------------------------------------------------------------- 1 |

Confirm Change of Email

2 | 3 |

4 | Follow this link to confirm the update of your email from {{ .Email }} to {{ .NewEmail }}: 5 |

6 |

Change Email

7 | 8 |

Or use the token {{ .Token }}

-------------------------------------------------------------------------------- /reset-flow/nextjs-pages/supabase/auth/email/magic-link.html: -------------------------------------------------------------------------------- 1 |

Magic Link

2 | 3 |

Follow this link to login:

4 |

Log In

5 | 6 |

or alternatively, visit the verify token page and enter the token code: {{ .Token }}

7 | 8 |

The link will remain valid for 24 hours or until it is used, whichever comes first. If you need to request another link, please click here.

-------------------------------------------------------------------------------- /reset-flow/nextjs-pages/supabase/seed.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/reset-flow/nextjs-pages/supabase/seed.sql -------------------------------------------------------------------------------- /reset-flow/nextjs-pages/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./{pages,components}/**/*.{html,js,jsx,tsx,ts}'], 4 | theme: { 5 | extend: {} 6 | }, 7 | plugins: [require('daisyui')], 8 | 9 | daisyui: { 10 | themes: ['winter'] 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /reset-flow/nextjs/.env.example: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321 2 | NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 -------------------------------------------------------------------------------- /reset-flow/nextjs/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /reset-flow/nextjs/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env 29 | .env.* 30 | !.env.example 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | -------------------------------------------------------------------------------- /reset-flow/nextjs/app/(app)/account/update-email/page.tsx: -------------------------------------------------------------------------------- 1 | import { createServerClient } from "@/lib/supabase-server"; 2 | import EmailForm from "./email-form"; 3 | 4 | export default async function UpdateEmail() { 5 | const supabase = createServerClient(); 6 | 7 | const { 8 | data: { session }, 9 | } = await supabase.auth.getSession(); 10 | 11 | return ; 12 | } 13 | -------------------------------------------------------------------------------- /reset-flow/nextjs/app/(app)/account/update-password/page.tsx: -------------------------------------------------------------------------------- 1 | import { createServerClient } from "@/lib/supabase-server"; 2 | import PasswordForm from "./password-form"; 3 | 4 | export default async function UpdatePassword() { 5 | const supabase = createServerClient(); 6 | 7 | const { 8 | data: { session }, 9 | } = await supabase.auth.getSession(); 10 | 11 | return ; 12 | } 13 | -------------------------------------------------------------------------------- /reset-flow/nextjs/app/(app)/page.tsx: -------------------------------------------------------------------------------- 1 | import { createServerClient } from "@/lib/supabase-server"; 2 | 3 | export default async function Home() { 4 | const supabase = createServerClient(); 5 | 6 | const { 7 | data: { session }, 8 | } = await supabase.auth.getSession(); 9 | 10 | return

Welcome {session?.user.email}

; 11 | } 12 | -------------------------------------------------------------------------------- /reset-flow/nextjs/app/auth/forgotpassword/page.tsx: -------------------------------------------------------------------------------- 1 | import ForgotPasswordForm from "./forgot-password-form"; 2 | 3 | export default function ForgotPassword() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /reset-flow/nextjs/app/auth/signin/page.tsx: -------------------------------------------------------------------------------- 1 | import SignInForm from "./sign-in-form"; 2 | 3 | export default function SignIn() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /reset-flow/nextjs/app/auth/signup/page.tsx: -------------------------------------------------------------------------------- 1 | import SignUpForm from "./sign-up-form"; 2 | 3 | export default function SignIn() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /reset-flow/nextjs/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/reset-flow/nextjs/app/favicon.ico -------------------------------------------------------------------------------- /reset-flow/nextjs/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import "./globals.css"; 2 | 3 | export const metadata = { 4 | title: "Magic Link + Otp Flow", 5 | description: "Generated by create next app", 6 | }; 7 | 8 | export default function RootLayout({ 9 | children, 10 | }: { 11 | children: React.ReactNode; 12 | }) { 13 | return ( 14 | 15 | {children} 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /reset-flow/nextjs/components/Alert.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | className: string; 5 | children: ReactNode; 6 | }; 7 | 8 | export default function Alert({ className, children }: Props) { 9 | return ( 10 |
11 |
12 | {children} 13 |
14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /reset-flow/nextjs/components/InputErrorMessage.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | children: ReactNode; 5 | }; 6 | 7 | export default function InputErrorMessage({ children }: Props) { 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /reset-flow/nextjs/lib/supabase-server.ts: -------------------------------------------------------------------------------- 1 | import { cookies } from "next/headers"; 2 | import { createServerComponentClient } from "@supabase/auth-helpers-nextjs"; 3 | 4 | export const createServerClient = () => { 5 | const cookieStore = cookies(); 6 | return createServerComponentClient({ cookies: () => cookieStore }); 7 | }; 8 | -------------------------------------------------------------------------------- /reset-flow/nextjs/lib/validationRules.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const required = (name: string) => z.string().min(1, `${name} is required`); 4 | export const email = (name = 'Email') => required(name).email(`${name} is not valid`); 5 | export const password = (number = 5, name = 'Password') => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | -------------------------------------------------------------------------------- /reset-flow/nextjs/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | module.exports = nextConfig; 5 | -------------------------------------------------------------------------------- /reset-flow/nextjs/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@playwright/test"; 2 | 3 | export default defineConfig({ 4 | webServer: { 5 | command: "npm run build && npm run start", 6 | port: 3000, 7 | reuseExistingServer: true, 8 | }, 9 | testDir: "./tests", 10 | reporter: "list", 11 | 12 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 13 | forbidOnly: !!process.env.CI, 14 | /* Retry on CI only */ 15 | retries: process.env.CI ? 2 : 0, 16 | /* Opt out of parallel tests on CI. */ 17 | workers: process.env.CI ? 1 : undefined, 18 | }); 19 | -------------------------------------------------------------------------------- /reset-flow/nextjs/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /reset-flow/nextjs/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | -------------------------------------------------------------------------------- /reset-flow/nextjs/supabase/auth/email/email-change.html: -------------------------------------------------------------------------------- 1 |

Confirm Change of Email

2 | 3 | {{ if eq .Email .SendingTo }} 4 |

We are writing to confirm you initiated an email change from {{ .Email }} to {{ .NewEmail }}

5 |

Change Email

6 | {{ else }} 7 |

Please confirm your email address ({{ .NewEmail }}) for this account.

8 |

Confirm Email Address

9 | {{end}} 10 | 11 |

Or use the token {{ .Token }}

-------------------------------------------------------------------------------- /reset-flow/nextjs/supabase/auth/email/invite.html: -------------------------------------------------------------------------------- 1 |

You have been invited

2 | 3 |

4 | You have been invited to create a user on {{ .SiteURL }}. Follow this link to 5 | accept the invite: 6 |

7 |

Accept the invite

8 | 9 |

The link will remain valid for 24 hours or until it is used, whichever comes first. If you need to request another link, please contact the website admin.

10 | -------------------------------------------------------------------------------- /reset-flow/nextjs/supabase/auth/email/magic-link.html: -------------------------------------------------------------------------------- 1 |

Magic Link

2 | 3 |

Follow this link to login:

4 |

Log In

5 | 6 |

or alternatively, visit the verify token page and enter the token code: {{ .Token }}

7 | 8 |

The link will remain valid for 24 hours or until it is used, whichever comes first. If you need to request another link, please click here.

-------------------------------------------------------------------------------- /reset-flow/nextjs/supabase/seed.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/reset-flow/nextjs/supabase/seed.sql -------------------------------------------------------------------------------- /reset-flow/nuxt/.env.example: -------------------------------------------------------------------------------- 1 | SUPABASE_URL=http://localhost:54321 2 | SUPABASE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 -------------------------------------------------------------------------------- /reset-flow/nuxt/.gitignore: -------------------------------------------------------------------------------- 1 | # Nuxt dev/build outputs 2 | .output 3 | .nuxt 4 | .nitro 5 | .cache 6 | dist 7 | 8 | # Node dependencies 9 | node_modules 10 | 11 | # Logs 12 | logs 13 | *.log 14 | 15 | # Misc 16 | .DS_Store 17 | .fleet 18 | .idea 19 | 20 | # Local env files 21 | .env 22 | .env.* 23 | !.env.example 24 | -------------------------------------------------------------------------------- /reset-flow/nuxt/.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | strict-peer-dependencies=false 3 | -------------------------------------------------------------------------------- /reset-flow/nuxt/app.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /reset-flow/nuxt/components/Alert.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /reset-flow/nuxt/components/InputErrorMessage.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /reset-flow/nuxt/layouts/auth.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /reset-flow/nuxt/lib/validationRules.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const required = (name: string) => z.string().min(1, `${name} is required`); 4 | export const email = (name = 'Email') => required(name).email(`${name} is not valid`); 5 | export const password = (number = 5, name = 'Password') => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | -------------------------------------------------------------------------------- /reset-flow/nuxt/middleware/auth.ts: -------------------------------------------------------------------------------- 1 | export default defineNuxtRouteMiddleware((to, _from) => { 2 | const user = useSupabaseUser(); 3 | 4 | if (!user.value) { 5 | return navigateTo("/auth/signin"); 6 | } 7 | }); 8 | -------------------------------------------------------------------------------- /reset-flow/nuxt/nuxt.config.ts: -------------------------------------------------------------------------------- 1 | // https://nuxt.com/docs/api/configuration/nuxt-config 2 | export default defineNuxtConfig({ 3 | devtools: { enabled: true }, 4 | modules: ["@nuxtjs/supabase", "@nuxtjs/tailwindcss"], 5 | runtimeConfig: { 6 | public: { 7 | baseUrl: process.env.BASE_URL || "http://localhost:3000", 8 | }, 9 | }, 10 | supabase: { 11 | redirectOptions: { 12 | login: "/auth/signin", 13 | callback: "/auth/confirm", 14 | exclude: ["/auth/*"], 15 | }, 16 | }, 17 | }); 18 | -------------------------------------------------------------------------------- /reset-flow/nuxt/pages/auth/confirm.vue: -------------------------------------------------------------------------------- 1 | 13 | 16 | -------------------------------------------------------------------------------- /reset-flow/nuxt/pages/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /reset-flow/nuxt/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@playwright/test"; 2 | 3 | export default defineConfig({ 4 | webServer: { 5 | command: "npm run build && npm run preview", 6 | port: 3000, 7 | reuseExistingServer: true, 8 | }, 9 | testDir: "./tests", 10 | reporter: "list", 11 | 12 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 13 | forbidOnly: !!process.env.CI, 14 | /* Retry on CI only */ 15 | retries: process.env.CI ? 2 : 0, 16 | /* Opt out of parallel tests on CI. */ 17 | workers: process.env.CI ? 1 : undefined, 18 | }); 19 | -------------------------------------------------------------------------------- /reset-flow/nuxt/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/reset-flow/nuxt/public/favicon.ico -------------------------------------------------------------------------------- /reset-flow/nuxt/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.nuxt/tsconfig.server.json" 3 | } 4 | -------------------------------------------------------------------------------- /reset-flow/nuxt/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | -------------------------------------------------------------------------------- /reset-flow/nuxt/supabase/seed.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/reset-flow/nuxt/supabase/seed.sql -------------------------------------------------------------------------------- /reset-flow/nuxt/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | plugins: [require("daisyui")], 4 | 5 | daisyui: { 6 | themes: ["winter"], 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /reset-flow/nuxt/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | -------------------------------------------------------------------------------- /reset-flow/react/.env.example: -------------------------------------------------------------------------------- 1 | VITE_SUPABASE_URL=http://localhost:54321 2 | VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 -------------------------------------------------------------------------------- /reset-flow/react/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | # local env files 27 | .env 28 | .env.* 29 | !.env.example 30 | -------------------------------------------------------------------------------- /reset-flow/react/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Reset Flow 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /reset-flow/react/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@playwright/test"; 2 | 3 | export default defineConfig({ 4 | webServer: { 5 | command: "npm run build && npm run preview", 6 | port: 4173, 7 | reuseExistingServer: true, 8 | }, 9 | testDir: "./tests", 10 | reporter: "list", 11 | 12 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 13 | forbidOnly: !!process.env.CI, 14 | /* Retry on CI only */ 15 | retries: process.env.CI ? 2 : 0, 16 | /* Opt out of parallel tests on CI. */ 17 | workers: process.env.CI ? 1 : undefined, 18 | }); 19 | -------------------------------------------------------------------------------- /reset-flow/react/src/components/Alert.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | className: string; 5 | children: ReactNode; 6 | }; 7 | 8 | export default function Alert({ className, children }: Props) { 9 | return ( 10 |
11 |
12 | {children} 13 |
14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /reset-flow/react/src/components/InputErrorMessage.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | children: ReactNode; 5 | }; 6 | 7 | export default function InputErrorMessage({ children }: Props) { 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /reset-flow/react/src/lib/db.ts: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | 3 | export const supabase = createClient( 4 | import.meta.env.VITE_SUPABASE_URL, 5 | import.meta.env.VITE_SUPABASE_ANON_KEY, 6 | { 7 | auth: { 8 | flowType: "pkce", 9 | }, 10 | } 11 | ); 12 | -------------------------------------------------------------------------------- /reset-flow/react/src/lib/validationRules.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const required = (name: string) => z.string().min(1, `${name} is required`); 4 | export const email = (name = 'Email') => required(name).email(`${name} is not valid`); 5 | export const password = (number = 5, name = 'Password') => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | -------------------------------------------------------------------------------- /reset-flow/react/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import { BrowserRouter } from "react-router-dom"; 4 | import App from "./App"; 5 | import "./app.css"; 6 | import { AuthProvider } from "./lib/AuthProvider"; 7 | import { supabase } from "./lib/db"; 8 | 9 | ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /reset-flow/react/src/routes/index.tsx: -------------------------------------------------------------------------------- 1 | import { useAuth } from "@/lib/AuthProvider"; 2 | 3 | export default function Home() { 4 | const { user } = useAuth(); 5 | 6 | return ( 7 | <> 8 |

Welcome {user?.email}

9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /reset-flow/react/src/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /reset-flow/react/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | interface ImportMetaEnv { 4 | readonly VITE_SUPABASE_URL: string; 5 | readonly VITE_SUPABASE_ANON_KEY: string; 6 | } 7 | 8 | interface ImportMeta { 9 | readonly env: ImportMetaEnv; 10 | } 11 | -------------------------------------------------------------------------------- /reset-flow/react/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | config.local.toml -------------------------------------------------------------------------------- /reset-flow/react/supabase/seed.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/reset-flow/react/supabase/seed.sql -------------------------------------------------------------------------------- /reset-flow/react/tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./src/**/*.{html,js,jsx,tsx,ts}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [require("daisyui")], 8 | 9 | daisyui: { 10 | themes: ["winter"], 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /reset-flow/react/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true, 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /reset-flow/react/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | import path from "path"; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react()], 8 | resolve: { 9 | alias: { 10 | "@": path.resolve(__dirname, "./src"), 11 | }, 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/.env.example: -------------------------------------------------------------------------------- 1 | PUBLIC_SUPABASE_URL=http://localhost:54321 2 | PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 -------------------------------------------------------------------------------- /reset-flow/sveltekit/.eslintignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'], 5 | plugins: ['svelte3', '@typescript-eslint'], 6 | ignorePatterns: ['*.cjs'], 7 | overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }], 8 | settings: { 9 | 'svelte3/typescript': () => require('typescript') 10 | }, 11 | parserOptions: { 12 | sourceType: 'module', 13 | ecmaVersion: 2020 14 | }, 15 | env: { 16 | browser: true, 17 | es2017: true, 18 | node: true 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | vite.config.js.timestamp-* 10 | vite.config.ts.timestamp-* 11 | playwright-report 12 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100, 6 | "plugins": ["prettier-plugin-svelte"], 7 | "pluginSearchDirs": ["."], 8 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] 9 | } 10 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/src/app.d.ts: -------------------------------------------------------------------------------- 1 | import { SupabaseClient, Session } from '@supabase/supabase-js'; 2 | 3 | declare global { 4 | namespace App { 5 | interface Locals { 6 | supabase: SupabaseClient; 7 | getSession(): Promise; 8 | } 9 | interface PageData { 10 | session: Session | null; 11 | } 12 | // interface Error {} 13 | // interface Platform {} 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | %sveltekit.head% 9 | 10 | 11 |
%sveltekit.body%
12 | 13 | 14 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/src/lib/Alert.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 | 8 |
9 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/src/lib/InputErrorMessage.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/src/lib/validationRules.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const required = (name: string) => z.string().min(1, `${name} is required`); 4 | export const email = (name = 'Email') => required(name).email(`${name} is not valid`); 5 | export const password = (number = 5, name = 'Password') => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/src/routes/(app)/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | import type { PageServerLoad } from './$types'; 3 | 4 | export const load: PageServerLoad = async ({ locals: { getSession } }) => { 5 | const session = await getSession(); 6 | 7 | if (!session) { 8 | throw redirect(303, '/auth/signin'); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/src/routes/(app)/+page.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 |

Welcome {data.session?.user?.email}

9 |

Visit kit.svelte.dev to read the documentation

10 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/src/routes/(app)/account/+layout.server.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | import type { LayoutServerLoad } from './$types'; 3 | 4 | export const load: LayoutServerLoad = async ({ locals: { getSession } }) => { 5 | const session = await getSession(); 6 | 7 | if (!session) { 8 | throw redirect(303, '/auth/signin'); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/src/routes/(app)/logging-in/+layout@.svelte: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/src/routes/(auth)/+layout.server.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | import type { LayoutServerLoad } from './$types'; 3 | 4 | export const load: LayoutServerLoad = async ({ url, locals: { getSession } }) => { 5 | const session = await getSession(); 6 | 7 | // only allow the signout subpath when visiting the auth path 8 | if (url.pathname !== '/auth/signout' && session) { 9 | throw redirect(303, '/'); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/src/routes/(auth)/+layout.svelte: -------------------------------------------------------------------------------- 1 | 2 | Reset Flow 3 | 4 | 5 |
6 |
7 |
8 |

Reset Flow

9 |
10 |
11 | 12 |
13 |
14 |
15 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/src/routes/(auth)/auth/+page.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | import type { PageLoad } from './$types'; 3 | 4 | export const load: PageLoad = async () => { 5 | throw redirect(303, '/auth/signin'); 6 | }; 7 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/src/routes/(auth)/auth/callback/+server.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | 3 | export const GET = async ({ url, locals: { supabase } }) => { 4 | const code = url.searchParams.get('code'); 5 | const next = url.searchParams.get('next') ?? '/'; 6 | 7 | if (code) { 8 | await supabase.auth.exchangeCodeForSession(code); 9 | } 10 | 11 | throw redirect(303, next); 12 | }; 13 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/src/routes/(auth)/auth/signout/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | 3 | export const actions = { 4 | default: async ({ locals: { supabase, getSession } }) => { 5 | const session = await getSession(); 6 | 7 | if (session) { 8 | await supabase.auth.signOut(); 9 | throw redirect(303, '/auth/signin'); 10 | } 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/src/routes/+layout.server.ts: -------------------------------------------------------------------------------- 1 | // src/routes/+layout.server.ts 2 | import type { LayoutServerLoad } from './$types'; 3 | 4 | export const load: LayoutServerLoad = async ({ locals: { getSession } }) => { 5 | return { 6 | session: await getSession() 7 | }; 8 | }; 9 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/src/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/reset-flow/sveltekit/static/favicon.png -------------------------------------------------------------------------------- /reset-flow/sveltekit/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | config.local.toml -------------------------------------------------------------------------------- /reset-flow/sveltekit/supabase/auth/email/email-change.html: -------------------------------------------------------------------------------- 1 |

Confirm Change of Email

2 | 3 |

4 | Follow this link to confirm the update of your email from {{ .Email }} to {{ .NewEmail }}: 5 |

6 |

Change Email

7 | 8 |

Or use the token {{ .Token }}

-------------------------------------------------------------------------------- /reset-flow/sveltekit/supabase/auth/email/magic-link.html: -------------------------------------------------------------------------------- 1 |

Magic Link

2 | 3 |

Follow this link to login:

4 |

Log In

5 | 6 |

or alternatively, visit the verify token page and enter the token code: {{ .Token }}

7 | 8 |

The link will remain valid for 24 hours or until it is used, whichever comes first. If you need to request another link, please click here.

-------------------------------------------------------------------------------- /reset-flow/sveltekit/supabase/seed.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/reset-flow/sveltekit/supabase/seed.sql -------------------------------------------------------------------------------- /reset-flow/sveltekit/svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-auto'; 2 | import preprocess from 'svelte-preprocess'; 3 | 4 | /** @type {import('@sveltejs/kit').Config} */ 5 | const config = { 6 | // Consult https://github.com/sveltejs/svelte-preprocess 7 | // for more information about preprocessors 8 | preprocess: preprocess(), 9 | 10 | kit: { 11 | adapter: adapter() 12 | } 13 | }; 14 | 15 | export default config; 16 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./src/**/*.{html,js,svelte,ts}'], 4 | theme: { 5 | extend: {} 6 | }, 7 | plugins: [require('daisyui')], 8 | 9 | daisyui: { 10 | themes: ['winter'] 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /reset-flow/sveltekit/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite'; 2 | import type { UserConfig } from 'vite'; 3 | 4 | const config: UserConfig = { 5 | plugins: [sveltekit()] 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /user-profile/nextjs-pages/.env.example: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321 2 | NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 -------------------------------------------------------------------------------- /user-profile/nextjs-pages/components/Alert.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | className: string; 5 | children: ReactNode; 6 | }; 7 | 8 | export default function Alert({ className, children }: Props) { 9 | return ( 10 |
11 |
12 | {children} 13 |
14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /user-profile/nextjs-pages/components/InputErrorMessage.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | children: ReactNode; 5 | }; 6 | 7 | export default function InputErrorMessage({ children }: Props) { 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /user-profile/nextjs-pages/lib/validationRules.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const required = (name: string) => z.string().min(1, `${name} is required`); 4 | export const email = (name = 'Email') => required(name).email(`${name} is not valid`); 5 | export const password = (number = 5, name = 'Password') => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | -------------------------------------------------------------------------------- /user-profile/nextjs-pages/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | } 5 | 6 | module.exports = nextConfig 7 | -------------------------------------------------------------------------------- /user-profile/nextjs-pages/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import { Html, Head, Main, NextScript } from 'next/document' 2 | 3 | export default function Document() { 4 | return ( 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /user-profile/nextjs-pages/pages/api/hello.ts: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | import type { NextApiRequest, NextApiResponse } from 'next' 3 | 4 | type Data = { 5 | name: string 6 | } 7 | 8 | export default function handler( 9 | req: NextApiRequest, 10 | res: NextApiResponse 11 | ) { 12 | res.status(200).json({ name: 'John Doe' }) 13 | } 14 | -------------------------------------------------------------------------------- /user-profile/nextjs-pages/pages/auth/index.tsx: -------------------------------------------------------------------------------- 1 | import { GetServerSidePropsContext } from "next"; 2 | 3 | export default function Auth() {} 4 | 5 | export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { 6 | return { 7 | redirect: { 8 | destination: "/auth/signin", 9 | permanent: true, 10 | }, 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /user-profile/nextjs-pages/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@playwright/test"; 2 | 3 | export default defineConfig({ 4 | webServer: { 5 | command: "npm run build && npm run start", 6 | port: 3000, 7 | reuseExistingServer: true, 8 | }, 9 | testDir: "./tests", 10 | reporter: "list", 11 | 12 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 13 | forbidOnly: !!process.env.CI, 14 | /* Retry on CI only */ 15 | retries: process.env.CI ? 2 : 0, 16 | /* Opt out of parallel tests on CI. */ 17 | workers: process.env.CI ? 1 : undefined, 18 | }); 19 | -------------------------------------------------------------------------------- /user-profile/nextjs-pages/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/user-profile/nextjs-pages/public/favicon.ico -------------------------------------------------------------------------------- /user-profile/nextjs-pages/styles/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /user-profile/nextjs-pages/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | config.local.toml -------------------------------------------------------------------------------- /user-profile/nextjs-pages/supabase/auth/email/email-change.html: -------------------------------------------------------------------------------- 1 |

Confirm Change of Email

2 | 3 |

4 | Follow this link to confirm the update of your email from {{ .Email }} to {{ .NewEmail }}: 5 |

6 |

Change Email

7 | 8 |

Or use the token {{ .Token }}

-------------------------------------------------------------------------------- /user-profile/nextjs-pages/supabase/auth/email/magic-link.html: -------------------------------------------------------------------------------- 1 |

Magic Link

2 | 3 |

Follow this link to login:

4 |

Log In

5 | 6 |

or alternatively, visit the verify token page and enter the token code: {{ .Token }}

7 | 8 |

The link will remain valid for 24 hours or until it is used, whichever comes first. If you need to request another link, please click here.

-------------------------------------------------------------------------------- /user-profile/nextjs-pages/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./{pages,components}/**/*.{html,js,jsx,tsx,ts}'], 4 | theme: { 5 | extend: {} 6 | }, 7 | plugins: [require('daisyui')], 8 | 9 | daisyui: { 10 | themes: ['winter'] 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /user-profile/nextjs/.env.example: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321 2 | NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 -------------------------------------------------------------------------------- /user-profile/nextjs/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /user-profile/nextjs/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env 29 | .env.* 30 | !.env.example 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | -------------------------------------------------------------------------------- /user-profile/nextjs/app/(app)/account/update-email/page.tsx: -------------------------------------------------------------------------------- 1 | import { createServerClient } from "@/lib/supabase-server"; 2 | import EmailForm from "./email-form"; 3 | 4 | export const dynamic = "force-dynamic"; 5 | 6 | export default async function UpdateEmail() { 7 | const supabase = createServerClient(); 8 | 9 | const { 10 | data: { session }, 11 | } = await supabase.auth.getSession(); 12 | 13 | return ; 14 | } 15 | -------------------------------------------------------------------------------- /user-profile/nextjs/app/(app)/account/update-password/page.tsx: -------------------------------------------------------------------------------- 1 | import { createServerClient } from "@/lib/supabase-server"; 2 | import PasswordForm from "./password-form"; 3 | 4 | export const dynamic = "force-dynamic"; 5 | 6 | export default async function UpdatePassword() { 7 | const supabase = createServerClient(); 8 | 9 | const { 10 | data: { session }, 11 | } = await supabase.auth.getSession(); 12 | 13 | return ; 14 | } 15 | -------------------------------------------------------------------------------- /user-profile/nextjs/app/(app)/account/update/page.tsx: -------------------------------------------------------------------------------- 1 | import { createServerClient } from "@/lib/supabase-server"; 2 | import UpdateForm from "./update-form"; 3 | import { getProfile } from "@/lib/utils"; 4 | 5 | export const dynamic = "force-dynamic"; 6 | 7 | export default async function Update() { 8 | const supabase = createServerClient(); 9 | 10 | const { profile, session } = await getProfile(supabase); 11 | 12 | return ; 13 | } 14 | -------------------------------------------------------------------------------- /user-profile/nextjs/app/auth/forgotpassword/page.tsx: -------------------------------------------------------------------------------- 1 | import ForgotPasswordForm from "./forgot-password-form"; 2 | 3 | export default function ForgotPassword() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /user-profile/nextjs/app/auth/signin/page.tsx: -------------------------------------------------------------------------------- 1 | import SignInForm from "./sign-in-form"; 2 | 3 | export default function SignIn() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /user-profile/nextjs/app/auth/signup/page.tsx: -------------------------------------------------------------------------------- 1 | import SignUpForm from "./sign-up-form"; 2 | 3 | export default function SignIn() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /user-profile/nextjs/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/user-profile/nextjs/app/favicon.ico -------------------------------------------------------------------------------- /user-profile/nextjs/app/global.d.ts: -------------------------------------------------------------------------------- 1 | import type { Database as DB } from "@/lib/schema"; 2 | 3 | declare global { 4 | type Database = DB; 5 | type ProfileInfo = DB["public"]["Tables"]["profiles_info"]["Row"]; 6 | type Profiles = DB["public"]["Tables"]["profiles"]["Row"]; 7 | type Profile = Profiles & { 8 | profiles_info: ProfileInfo | ProfileInfo[] | null; 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /user-profile/nextjs/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import "./globals.css"; 2 | 3 | export const metadata = { 4 | title: "User Profile", 5 | description: "Generated by create next app", 6 | }; 7 | 8 | export default function RootLayout({ 9 | children, 10 | }: { 11 | children: React.ReactNode; 12 | }) { 13 | return ( 14 | 15 | {children} 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /user-profile/nextjs/components/Alert.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | className: string; 5 | children: ReactNode; 6 | }; 7 | 8 | export default function Alert({ className, children }: Props) { 9 | return ( 10 |
11 |
12 | {children} 13 |
14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /user-profile/nextjs/components/InputErrorMessage.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | children: ReactNode; 5 | }; 6 | 7 | export default function InputErrorMessage({ children }: Props) { 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /user-profile/nextjs/lib/supabase-server.ts: -------------------------------------------------------------------------------- 1 | import { cookies } from "next/headers"; 2 | import { createServerComponentClient } from "@supabase/auth-helpers-nextjs"; 3 | 4 | export const createServerClient = () => 5 | createServerComponentClient({ cookies }); 6 | -------------------------------------------------------------------------------- /user-profile/nextjs/lib/validationRules.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const required = (name: string) => z.string().min(1, `${name} is required`); 4 | export const email = (name = 'Email') => required(name).email(`${name} is not valid`); 5 | export const password = (number = 5, name = 'Password') => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | -------------------------------------------------------------------------------- /user-profile/nextjs/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | module.exports = nextConfig; 5 | -------------------------------------------------------------------------------- /user-profile/nextjs/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@playwright/test"; 2 | 3 | export default defineConfig({ 4 | webServer: { 5 | command: "npm run build && npm run start", 6 | port: 3000, 7 | reuseExistingServer: true, 8 | }, 9 | testDir: "./tests", 10 | reporter: "list", 11 | 12 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 13 | forbidOnly: !!process.env.CI, 14 | /* Retry on CI only */ 15 | retries: process.env.CI ? 2 : 0, 16 | /* Opt out of parallel tests on CI. */ 17 | workers: process.env.CI ? 1 : undefined, 18 | }); 19 | -------------------------------------------------------------------------------- /user-profile/nextjs/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /user-profile/nextjs/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | -------------------------------------------------------------------------------- /user-profile/nextjs/supabase/auth/email/email-change.html: -------------------------------------------------------------------------------- 1 |

Confirm Change of Email

2 | 3 |

4 | Follow this link to confirm the update of your email from {{ .Email }} to {{ .NewEmail }}: 5 |

6 |

Change Email

7 | 8 |

Or use the token {{ .Token }}

-------------------------------------------------------------------------------- /user-profile/nextjs/supabase/auth/email/magic-link.html: -------------------------------------------------------------------------------- 1 |

Magic Link

2 | 3 |

Follow this link to login:

4 |

Log In

5 | 6 |

or alternatively, visit the verify token page and enter the token code: {{ .Token }}

7 | 8 |

The link will remain valid for 24 hours or until it is used, whichever comes first. If you need to request another link, please click here.

-------------------------------------------------------------------------------- /user-profile/nuxt/.env.example: -------------------------------------------------------------------------------- 1 | SUPABASE_URL=http://localhost:54321 2 | SUPABASE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 -------------------------------------------------------------------------------- /user-profile/nuxt/.gitignore: -------------------------------------------------------------------------------- 1 | # Nuxt dev/build outputs 2 | .output 3 | .nuxt 4 | .nitro 5 | .cache 6 | dist 7 | 8 | # Node dependencies 9 | node_modules 10 | 11 | # Logs 12 | logs 13 | *.log 14 | 15 | # Misc 16 | .DS_Store 17 | .fleet 18 | .idea 19 | 20 | # Local env files 21 | .env 22 | .env.* 23 | !.env.example 24 | -------------------------------------------------------------------------------- /user-profile/nuxt/.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | strict-peer-dependencies=false 3 | -------------------------------------------------------------------------------- /user-profile/nuxt/app.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /user-profile/nuxt/components/Alert.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /user-profile/nuxt/components/InputErrorMessage.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /user-profile/nuxt/global.d.ts: -------------------------------------------------------------------------------- 1 | import type { Database as DB } from "~/lib/schema"; 2 | 3 | declare global { 4 | type Database = DB; 5 | type ProfileInfo = DB["public"]["Tables"]["profiles_info"]["Row"]; 6 | type Profiles = DB["public"]["Tables"]["profiles"]["Row"]; 7 | type Profile = Profiles & { 8 | profiles_info: ProfileInfo | ProfileInfo[] | null; 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /user-profile/nuxt/layouts/auth.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /user-profile/nuxt/lib/validationRules.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const required = (name: string) => z.string().min(1, `${name} is required`); 4 | export const email = (name = 'Email') => required(name).email(`${name} is not valid`); 5 | export const password = (number = 5, name = 'Password') => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | -------------------------------------------------------------------------------- /user-profile/nuxt/middleware/auth.ts: -------------------------------------------------------------------------------- 1 | import { getProfile } from "~/lib/utils"; 2 | 3 | export default defineNuxtRouteMiddleware(async (to, _from) => { 4 | const supabase = useSupabaseClient(); 5 | 6 | // get profile and session 7 | const { profile, session } = await getProfile(supabase); 8 | 9 | if (!session) { 10 | return navigateTo("/auth/signin"); 11 | } 12 | 13 | if ( 14 | !to.path.startsWith("/account/update") && 15 | profile && 16 | profile.display_name == null 17 | ) { 18 | return navigateTo("/account/update"); 19 | } 20 | }); 21 | -------------------------------------------------------------------------------- /user-profile/nuxt/pages/auth/confirm.vue: -------------------------------------------------------------------------------- 1 | 13 | 16 | -------------------------------------------------------------------------------- /user-profile/nuxt/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@playwright/test"; 2 | 3 | export default defineConfig({ 4 | webServer: { 5 | command: "npm run build && npm run preview", 6 | port: 3000, 7 | reuseExistingServer: true, 8 | }, 9 | testDir: "./tests", 10 | reporter: "list", 11 | 12 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 13 | forbidOnly: !!process.env.CI, 14 | /* Retry on CI only */ 15 | retries: process.env.CI ? 2 : 0, 16 | /* Opt out of parallel tests on CI. */ 17 | workers: process.env.CI ? 1 : undefined, 18 | }); 19 | -------------------------------------------------------------------------------- /user-profile/nuxt/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/user-profile/nuxt/public/favicon.ico -------------------------------------------------------------------------------- /user-profile/nuxt/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.nuxt/tsconfig.server.json" 3 | } 4 | -------------------------------------------------------------------------------- /user-profile/nuxt/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | -------------------------------------------------------------------------------- /user-profile/nuxt/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | plugins: [require("daisyui")], 4 | 5 | daisyui: { 6 | themes: ["winter"], 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /user-profile/nuxt/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | -------------------------------------------------------------------------------- /user-profile/react/.env.example: -------------------------------------------------------------------------------- 1 | VITE_SUPABASE_URL=http://localhost:54321 2 | VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 -------------------------------------------------------------------------------- /user-profile/react/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | # local env files 27 | .env 28 | .env.* 29 | !.env.example 30 | -------------------------------------------------------------------------------- /user-profile/react/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | User Profile 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /user-profile/react/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@playwright/test"; 2 | 3 | export default defineConfig({ 4 | webServer: { 5 | command: "npm run build && npm run preview", 6 | port: 4173, 7 | reuseExistingServer: true, 8 | }, 9 | testDir: "./tests", 10 | reporter: "list", 11 | 12 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 13 | forbidOnly: !!process.env.CI, 14 | /* Retry on CI only */ 15 | retries: process.env.CI ? 2 : 0, 16 | /* Opt out of parallel tests on CI. */ 17 | workers: process.env.CI ? 1 : undefined, 18 | }); 19 | -------------------------------------------------------------------------------- /user-profile/react/src/components/Alert.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | className: string; 5 | children: ReactNode; 6 | }; 7 | 8 | export default function Alert({ className, children }: Props) { 9 | return ( 10 |
11 |
12 | {children} 13 |
14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /user-profile/react/src/components/InputErrorMessage.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | type Props = { 4 | children: ReactNode; 5 | }; 6 | 7 | export default function InputErrorMessage({ children }: Props) { 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /user-profile/react/src/global.d.ts: -------------------------------------------------------------------------------- 1 | import type { Database as DB } from "./lib/schema"; 2 | 3 | declare global { 4 | type Database = DB; 5 | type ProfileInfo = DB["public"]["Tables"]["profiles_info"]["Row"]; 6 | type Profiles = DB["public"]["Tables"]["profiles"]["Row"]; 7 | type Profile = Profiles & { 8 | profiles_info: ProfileInfo | ProfileInfo[] | null; 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /user-profile/react/src/lib/db.ts: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | 3 | export const supabase = createClient( 4 | import.meta.env.VITE_SUPABASE_URL, 5 | import.meta.env.VITE_SUPABASE_ANON_KEY 6 | ); 7 | -------------------------------------------------------------------------------- /user-profile/react/src/lib/validationRules.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const required = (name: string) => z.string().min(1, `${name} is required`); 4 | export const email = (name = 'Email') => required(name).email(`${name} is not valid`); 5 | export const password = (number = 5, name = 'Password') => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | -------------------------------------------------------------------------------- /user-profile/react/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App"; 4 | import "./app.css"; 5 | import { AuthProvider } from "./lib/AuthProvider"; 6 | import { supabase } from "./lib/db"; 7 | 8 | ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( 9 | 10 | 11 | 12 | 13 | 14 | ); 15 | -------------------------------------------------------------------------------- /user-profile/react/src/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /user-profile/react/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | interface ImportMetaEnv { 4 | readonly VITE_SUPABASE_URL: string; 5 | readonly VITE_SUPABASE_ANON_KEY: string; 6 | } 7 | 8 | interface ImportMeta { 9 | readonly env: ImportMetaEnv; 10 | } 11 | -------------------------------------------------------------------------------- /user-profile/react/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | config.local.toml -------------------------------------------------------------------------------- /user-profile/react/tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./src/**/*.{html,js,jsx,tsx,ts}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [require("daisyui")], 8 | 9 | daisyui: { 10 | themes: ["winter"], 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /user-profile/react/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true, 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /user-profile/react/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | import path from "path"; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react()], 8 | resolve: { 9 | alias: { 10 | "@": path.resolve(__dirname, "./src"), 11 | }, 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /user-profile/sveltekit/.env.example: -------------------------------------------------------------------------------- 1 | PUBLIC_SUPABASE_URL=http://localhost:54321 2 | PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 -------------------------------------------------------------------------------- /user-profile/sveltekit/.eslintignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /user-profile/sveltekit/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'], 5 | plugins: ['svelte3', '@typescript-eslint'], 6 | ignorePatterns: ['*.cjs'], 7 | overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }], 8 | settings: { 9 | 'svelte3/typescript': () => require('typescript') 10 | }, 11 | parserOptions: { 12 | sourceType: 'module', 13 | ecmaVersion: 2020 14 | }, 15 | env: { 16 | browser: true, 17 | es2017: true, 18 | node: true 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /user-profile/sveltekit/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | vite.config.js.timestamp-* 10 | vite.config.ts.timestamp-* 11 | playwright-report 12 | -------------------------------------------------------------------------------- /user-profile/sveltekit/.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /user-profile/sveltekit/.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /user-profile/sveltekit/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100, 6 | "plugins": ["prettier-plugin-svelte"], 7 | "pluginSearchDirs": ["."], 8 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] 9 | } 10 | -------------------------------------------------------------------------------- /user-profile/sveltekit/src/app.d.ts: -------------------------------------------------------------------------------- 1 | import { SupabaseClient, Session } from '@supabase/supabase-js'; 2 | import { Database } from './lib/schema'; 3 | 4 | declare global { 5 | namespace App { 6 | interface Locals { 7 | supabase: SupabaseClient; 8 | getSession(): Promise; 9 | } 10 | interface PageData { 11 | session: Session | null; 12 | } 13 | // interface Error {} 14 | // interface Platform {} 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /user-profile/sveltekit/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | %sveltekit.head% 9 | 10 | 11 |
%sveltekit.body%
12 | 13 | 14 | -------------------------------------------------------------------------------- /user-profile/sveltekit/src/lib/Alert.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 | 8 |
9 | -------------------------------------------------------------------------------- /user-profile/sveltekit/src/lib/InputErrorMessage.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /user-profile/sveltekit/src/lib/validationRules.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const required = (name: string) => z.string().min(1, `${name} is required`); 4 | export const email = (name = 'Email') => required(name).email(`${name} is not valid`); 5 | export const password = (number = 5, name = 'Password') => 6 | required(name).min(number, `${name} must be at least ${number} characters`); 7 | -------------------------------------------------------------------------------- /user-profile/sveltekit/src/routes/(app)/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | import type { PageServerLoad } from './$types'; 3 | 4 | export const load: PageServerLoad = async ({ locals: { getSession } }) => { 5 | const session = await getSession(); 6 | 7 | if (!session) { 8 | throw redirect(307, '/auth/signin'); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /user-profile/sveltekit/src/routes/(auth)/+layout.server.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | import type { LayoutServerLoad } from './$types'; 3 | 4 | export const load: LayoutServerLoad = async ({ url, locals: { getSession } }) => { 5 | const session = await getSession(); 6 | 7 | // only allow the signout subpath when visiting the auth path 8 | if (url.pathname !== '/auth/signout' && session) { 9 | throw redirect(303, '/'); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /user-profile/sveltekit/src/routes/(auth)/+layout.svelte: -------------------------------------------------------------------------------- 1 | 2 | User Profile 3 | 4 | 5 |
6 |
7 |
8 |

User Profile

9 |
10 |
11 | 12 |
13 |
14 |
15 | -------------------------------------------------------------------------------- /user-profile/sveltekit/src/routes/(auth)/auth/+page.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | import type { PageLoad } from './$types'; 3 | 4 | export const load: PageLoad = async () => { 5 | throw redirect(303, '/auth/signin'); 6 | }; 7 | -------------------------------------------------------------------------------- /user-profile/sveltekit/src/routes/(auth)/auth/callback/+server.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | 3 | export const GET = async ({ url, locals: { supabase } }) => { 4 | const code = url.searchParams.get('code'); 5 | const next = url.searchParams.get('next') ?? '/'; 6 | 7 | if (code) { 8 | await supabase.auth.exchangeCodeForSession(code); 9 | } 10 | 11 | throw redirect(303, next); 12 | }; 13 | -------------------------------------------------------------------------------- /user-profile/sveltekit/src/routes/(auth)/auth/signout/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | 3 | export const actions = { 4 | default: async ({ locals: { supabase, getSession } }) => { 5 | const session = await getSession(); 6 | 7 | if (session) { 8 | await supabase.auth.signOut(); 9 | throw redirect(303, '/auth/signin'); 10 | } 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /user-profile/sveltekit/src/routes/+layout.server.ts: -------------------------------------------------------------------------------- 1 | // src/routes/+layout.server.ts 2 | import type { LayoutServerLoad } from './$types'; 3 | 4 | export const load: LayoutServerLoad = async ({ locals: { getSession } }) => { 5 | return { 6 | session: await getSession() 7 | }; 8 | }; 9 | -------------------------------------------------------------------------------- /user-profile/sveltekit/src/routes/u/[slug]/+layout@.svelte: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /user-profile/sveltekit/src/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /user-profile/sveltekit/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-by-example/a40be7fe6e18c69764fdf8f4eb3422db636505c3/user-profile/sveltekit/static/favicon.png -------------------------------------------------------------------------------- /user-profile/sveltekit/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | config.local.toml -------------------------------------------------------------------------------- /user-profile/sveltekit/supabase/auth/email/email-change.html: -------------------------------------------------------------------------------- 1 |

Confirm Change of Email

2 | 3 |

4 | Follow this link to confirm the update of your email from {{ .Email }} to {{ .NewEmail }}: 5 |

6 |

Change Email

7 | 8 |

Or use the token {{ .Token }}

-------------------------------------------------------------------------------- /user-profile/sveltekit/supabase/auth/email/magic-link.html: -------------------------------------------------------------------------------- 1 |

Magic Link

2 | 3 |

Follow this link to login:

4 |

Log In

5 | 6 |

or alternatively, visit the verify token page and enter the token code: {{ .Token }}

7 | 8 |

The link will remain valid for 24 hours or until it is used, whichever comes first. If you need to request another link, please click here.

-------------------------------------------------------------------------------- /user-profile/sveltekit/svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-auto'; 2 | import preprocess from 'svelte-preprocess'; 3 | 4 | /** @type {import('@sveltejs/kit').Config} */ 5 | const config = { 6 | // Consult https://github.com/sveltejs/svelte-preprocess 7 | // for more information about preprocessors 8 | preprocess: preprocess(), 9 | 10 | kit: { 11 | adapter: adapter() 12 | } 13 | }; 14 | 15 | export default config; 16 | -------------------------------------------------------------------------------- /user-profile/sveltekit/tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./src/**/*.{html,js,svelte,ts}'], 4 | theme: { 5 | extend: {} 6 | }, 7 | plugins: [require('daisyui')], 8 | 9 | daisyui: { 10 | themes: ['winter'] 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /user-profile/sveltekit/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite'; 2 | import type { UserConfig } from 'vite'; 3 | 4 | const config: UserConfig = { 5 | plugins: [sveltekit()] 6 | }; 7 | 8 | export default config; 9 | --------------------------------------------------------------------------------