Merge branch 'master' into feat-add-retries-to-webhooks

This commit is contained in:
Rodrigo Mansueli authored and GitHub committed 2024-02-15 13:19:45 -03:00
commit b0ff719c25
1162 files changed
+29125 -16975

No files matched your search

+58
View File
@@ -0,0 +1,58 @@
name: Autofix Linting Checks
on:
pull_request:
types:
- labeled
# Cancel old builds on new commit for same workflow + branch/PR
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
autofix:
runs-on: ubuntu-latest
permissions:
contents: write
if: ${{ github.event_name == 'pull_request' && (github.event.label.name == 'autofix') }}
steps:
- name: Calculate number of commits
run: echo "PR_FETCH_DEPTH=$(( ${{ github.event.pull_request.commits }} + 1 ))" >> "${GITHUB_ENV}"
- uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
sparse-checkout: apps
fetch-depth: ${{ env.PR_FETCH_DEPTH }}
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
# Installing all dependencies takes up to three minutes, hacking around to only installing prettier+deps
- name: Install required dependencies
run: |
rm package.json
rm package-lock.json
npm i prettier@4.0.0-alpha.8 prettier-plugin-sql-cst
- name: Run Prettier in fix mode
run: |
npx prettier --write 'apps/**/*.{js,jsx,ts,tsx,css,md,mdx,json}'
- name: Commit changes and push to existing branch
run: |
git checkout -- package.json
git checkout -- package-lock.json
git config --global user.name 'github-tidy-bot'
git config --global user.email 'github-tidy-bot@supabase.com'
if [[ `git status --porcelain` ]]; then
echo "[bot] Changes detected, committing."
echo "[bot] Running in non-squash mode."
git commit -am "ci: Autofix updates from GitHub workflow"
git push
else
echo "[bot] No changes detected, nothing to commit."
fi
+5 -4
View File
@@ -27,7 +27,7 @@ jobs:
run: |
rm package.json
rm package-lock.json
npm i prettier@2 prettier-plugin-sql-cst
npm i prettier@4.0.0-alpha.8 prettier-plugin-sql-cst
- name: Run prettier
run: |-
npx prettier -c 'apps/**/*.{js,jsx,ts,tsx,css,md,mdx,json}'
@@ -50,7 +50,7 @@ jobs:
run: |
rm package.json
rm package-lock.json
npm i prettier@2 prettier-plugin-sql-cst
npm i prettier@4.0.0-alpha.8 prettier-plugin-sql-cst
- name: Run prettier
run: |-
npx prettier -c 'i18n/**/*.{js,jsx,ts,tsx,css,md,mdx,json}'
@@ -63,13 +63,14 @@ jobs:
with:
sparse-checkout: |
apps/docs/pages
apps/docs/content
# Installing all dependencies takes up to three minutes, hacking around to only installing prettier+deps
- name: Download dependencies
run: |
rm package.json
rm package-lock.json
npm i prettier@2 prettier-plugin-sql-cst
npm i prettier@4.0.0-alpha.8 prettier-plugin-sql-cst
- name: Run prettier
run: |-
# Check mdx files which contain sql code blocks
grep -lr '```sql' apps/docs/pages/**/*.mdx | xargs npx prettier -c
grep -lr '```sql' apps/docs/{pages,content}/**/*.mdx | xargs npx prettier -c
+5
View File
@@ -38,6 +38,7 @@ jobs:
with:
sparse-checkout: |
apps/docs
apps/www/.env.local.example
supabase
- name: Setup node
@@ -54,6 +55,10 @@ jobs:
- name: Run migrations
run: npx supabase db push
# Need the miscellaneous use API, which is available publicly (by design) in www
- name: Copy environment variables
run: cp apps/www/.env.local.example apps/docs/.env
- name: Update embeddings
working-directory: ./apps/docs
if: ${{ !inputs.refresh }}
+7 -1
View File
@@ -7,5 +7,11 @@
"endOfLine": "lf",
"sqlKeywordCase": "lower",
"pluginSearchDirs": false,
"plugins": ["prettier-plugin-sql-cst"]
"plugins": ["prettier-plugin-sql-cst"],
"overrides": [
{
"files": "**/*.json",
"options": { "parser": "json" }
}
]
}
+1 -2
View File
@@ -39,7 +39,6 @@ To see how to Contribute, visit [Getting Started](./DEVELOPERS.md)
- [Email Support](https://supabase.com/docs/support#business-support). Best for: problems with your database or infrastructure.
- [Discord](https://discord.supabase.com). Best for: sharing your applications and hanging out with the community.
## How it works
Supabase is a combination of open source tools. We’re building the features of Firebase using enterprise-grade, open source products. If the tools and communities exist, with an MIT, Apache 2, or equivalent open license, we will use and support that tool. If the tool doesn't exist, we build and open source it ourselves. Supabase is not a 1-to-1 mapping of Firebase. Our aim is to give developers a Firebase-like developer experience using open source tools.
@@ -49,7 +48,7 @@ Supabase is a combination of open source tools. We’re building the features of
Supabase is a [hosted platform](https://supabase.com/dashboard). You can sign up and start using Supabase without installing anything.
You can also [self-host](https://supabase.com/docs/guides/hosting/overview) and [develop locally](https://supabase.com/docs/guides/local-development).
![Architecture](https://github.com/supabase/supabase/blob/master/apps/docs/public/img/supabase-architecture.png)
![Architecture](apps/docs/public/img/supabase-architecture.svg)
- [Postgres](https://www.postgresql.org/) is an object-relational database system with over 30 years of active development that has earned it a strong reputation for reliability, feature robustness, and performance.
- [Realtime](https://github.com/supabase/realtime) is an Elixir server that allows you to listen to PostgreSQL inserts, updates, and deletes using websockets. Realtime polls Postgres' built-in replication functionality for database changes, converts changes to JSON, then broadcasts the JSON over websockets to authorized clients.
@@ -138,8 +138,8 @@ const UserChat = ({ message, isLatest, times, run }: UserChatProps) => {
{replyDuration !== undefined
? ` with ${replyDuration}s response`
: isLoading
? ', generating response...'
: ''}
? ', generating response...'
: ''}
</p>
)}
</div>
+18 -9
View File
@@ -8,56 +8,64 @@
@font-face {
font-family: 'circular';
src: url(/fonts/custom/CustomFont-Book.woff2) format('woff2'),
src:
url(/fonts/custom/CustomFont-Book.woff2) format('woff2'),
url(/fonts/custom/CustomFont-Book.woff) format('woff');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'circular';
src: url(/fonts/custom/CustomFont-BookItalic.woff2) format('woff2'),
src:
url(/fonts/custom/CustomFont-BookItalic.woff2) format('woff2'),
url(/fonts/custom/CustomFont-BookItalic.woff) format('woff');
font-weight: 400;
font-style: italic;
}
@font-face {
font-family: 'circular';
src: url(/fonts/custom/CustomFont-Medium.woff2) format('woff2'),
src:
url(/fonts/custom/CustomFont-Medium.woff2) format('woff2'),
url(/fonts/custom/CustomFont-Medium.woff) format('woff');
font-weight: 500;
font-style: normal;
}
@font-face {
font-family: 'circular';
src: url(/fonts/custom/CustomFont-MediumItalic.woff2) format('woff2'),
src:
url(/fonts/custom/CustomFont-MediumItalic.woff2) format('woff2'),
url(/fonts/custom/CustomFont-MediumItalic.woff) format('woff');
font-weight: 500;
font-style: italic;
}
@font-face {
font-family: 'circular';
src: url(/fonts/custom/CustomFont-Bold.woff2) format('woff2'),
src:
url(/fonts/custom/CustomFont-Bold.woff2) format('woff2'),
url(/fonts/custom/CustomFont-Bold.woff) format('woff');
font-weight: 700;
font-style: 600;
}
@font-face {
font-family: 'circular';
src: url(/fonts/custom/CustomFont-BoldItalic.woff2) format('woff2'),
src:
url(/fonts/custom/CustomFont-BoldItalic.woff2) format('woff2'),
url(/fonts/custom/CustomFont-BoldItalic.woff) format('woff');
font-style: 600;
font-style: italic;
}
@font-face {
font-family: 'circular';
src: url(/fonts/custom/CustomFont-Black.woff2) format('woff2'),
src:
url(/fonts/custom/CustomFont-Black.woff2) format('woff2'),
url(/fonts/custom/CustomFont-Black.woff) format('woff');
font-weight: 800;
font-style: normal;
}
@font-face {
font-family: 'circular';
src: url(/fonts/custom/CustomFont-BlackItalic.woff2) format('woff2'),
src:
url(/fonts/custom/CustomFont-BlackItalic.woff2) format('woff2'),
url(/fonts/custom/CustomFont-BlackItalic.woff) format('woff');
font-weight: 800;
font-style: italic;
@@ -66,7 +74,8 @@
@font-face {
font-family: 'source code pro';
src: url('/fonts/source-code-pro/SourceCodePro-Regular.eot');
src: url('/fonts/source-code-pro/SourceCodePro-Regular.woff2') format('woff2'),
src:
url('/fonts/source-code-pro/SourceCodePro-Regular.woff2') format('woff2'),
url('/fonts/source-code-pro/SourceCodePro-Regular.woff') format('woff'),
url('/fonts/source-code-pro/SourceCodePro-Regular.ttf') format('truetype'),
url('/fonts/source-code-pro/SourceCodePro-Regular.svg#SourceCodePro-Regular') format('svg');
@@ -1,5 +1,5 @@
'use client'
import ThemeToggle from '@ui/components/ThemeProvider/ThemeToggle'
import { ThemeToggle } from 'ui-patterns/ThemeToggle'
export default function ThemeSwitcher() {
return <ThemeToggle />
+1 -1
View File
@@ -1,6 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
transpilePackages: ['ui'],
transpilePackages: ['ui', 'ui-patterns'],
webpack: (config, { dev, isServer, webpack, nextRuntime }) => {
config.module.rules.push({
test: /\.node$/,
+2 -1
View File
@@ -17,7 +17,7 @@
"common": "*",
"config": "*",
"dayjs": "^1.11.10",
"eslint-config-next": "^14.0.3",
"eslint-config-next": "^14.1.0",
"lodash": "^4.17.21",
"lucide-react": "^0.292.0",
"next": "^14.0.3",
@@ -27,6 +27,7 @@
"react-flow": "^1.0.3",
"sql-formatter": "^13.1.0",
"ui": "*",
"ui-patterns": "*",
"valtio": "^1.12.0",
"zod": "^3.22.4"
},
+1 -1
View File
@@ -5,8 +5,8 @@ export default config({
'./pages/**/*.{ts,tsx}',
'./components/**/*.{ts,tsx}',
'./app/**/*.{ts,tsx}',
'./src/**/*.{ts,tsx}',
'../../packages/ui/src/**/*.{tsx,ts,js}',
'../../packages/ui-patterns/**/*.{tsx,ts,js}',
],
theme: {
extend: {
+3 -3
View File
@@ -46,7 +46,7 @@ export interface Database {
columns: ['user_id']
referencedRelation: 'users'
referencedColumns: ['id']
}
},
]
}
responses_ai: {
@@ -92,7 +92,7 @@ export interface Database {
columns: ['user_id']
referencedRelation: 'users'
referencedColumns: ['id']
}
},
]
}
threads: {
@@ -132,7 +132,7 @@ export interface Database {
columns: ['user_id']
referencedRelation: 'users'
referencedColumns: ['id']
}
},
]
}
}
-4
View File
@@ -23,7 +23,3 @@ yarn-error.log*
**/*/generated/**/*
!**/*/generated/.gitkeep
!**/*/generated/**/.gitkeep
# Temporary to avoid storing a lot of duplicate Markdown
# before finalizing the codemod
/content
@@ -0,0 +1,11 @@
import { lazy, Suspense } from 'react'
const Generator = lazy(() => import('./AppleSecretGenerator'))
export function AppleSecretGenerator() {
return (
<Suspense>
<Generator />
</Suspense>
)
}
+1 -1
View File
@@ -1,5 +1,5 @@
import { IconPanel } from 'ui-patterns/IconPanel'
import providers from '../data/authProviders'
import { IconPanel } from 'ui'
import Link from 'next/link'
export default function AuthProviders({ type }: { type: string }) {
+2 -1
View File
@@ -1,7 +1,8 @@
import Link from 'next/link'
import React, { useState } from 'react'
import { extensions } from 'shared-data'
import { GlassPanel, IconX, Input } from 'ui'
import { IconX, Input } from 'ui'
import { GlassPanel } from 'ui-patterns/GlassPanel'
type Extension = {
name: string
+3 -2
View File
@@ -1,7 +1,8 @@
import Link from 'next/link'
import { IconBackground, IconPanel, IconPlay } from 'ui'
import { IconBackground, IconPlay } from 'ui'
import { useBreakpoint } from 'common'
import DocsCoverLogo from './DocsCoverLogo'
import { IconPanel } from 'ui-patterns/IconPanel'
const HomePageCover = (props) => {
const isXs = useBreakpoint(639)
@@ -74,7 +75,7 @@ const HomePageCover = (props) => {
<div className="md:max-w-xs shrink w-fit xl:max-w-none">
<div className="flex items-center gap-3 mb-3">
<IconBackground>
<IconPlay aria-hidden="true" className="text-brand w-4" strokeWidth={2} />
<IconPlay aria-hidden="true" className="text-brand-600 w-4" strokeWidth={2} />
</IconBackground>
<h2 className="text-2xl m-0 text-foreground">Getting Started</h2>
</div>
+2
View File
@@ -0,0 +1,2 @@
import { Mermaid } from 'mdx-mermaid/lib/Mermaid'
export default Mermaid
+11
View File
@@ -0,0 +1,11 @@
import { Suspense, lazy } from 'react'
const MermaidHeavy = lazy(() => import('./Mermaid'))
export function Mermaid(props) {
return (
<Suspense>
<MermaidHeavy {...props} />
</Suspense>
)
}
+14
View File
@@ -0,0 +1,14 @@
import { ReactNode } from 'react'
import { navDataForMdx } from './Navigation/NavigationMenu/NavigationMenu.constants'
export function NavData({
data,
children,
}: {
data: keyof typeof navDataForMdx
children: (navItems: (typeof navDataForMdx)[keyof typeof navDataForMdx]) => ReactNode
}) {
const navItems = navDataForMdx[data]
return children(navItems)
}
@@ -614,6 +614,10 @@ export const auth = {
url: undefined,
items: [
{ name: 'Overview', url: '/guides/auth/server-side/overview' },
{
name: 'Next.js guide',
url: '/guides/auth/server-side/nextjs',
},
{
name: 'Creating a client',
url: '/guides/auth/server-side/creating-a-client',
@@ -653,20 +657,20 @@ export const auth = {
items: [
{
name: 'Part One: JWTs',
url: '/learn/auth-deep-dive/auth-deep-dive-jwts',
url: '/guides/auth/auth-deep-dive/auth-deep-dive-jwts',
},
{
name: 'Part Two: Row Level Security',
url: '/learn/auth-deep-dive/auth-row-level-security',
url: '/guides/auth/auth-deep-dive/auth-row-level-security',
},
{
name: 'Part Three: Policies',
url: '/learn/auth-deep-dive/auth-policies',
url: '/guides/auth/auth-deep-dive/auth-policies',
},
{ name: 'Part Four: GoTrue', url: '/learn/auth-deep-dive/auth-gotrue' },
{ name: 'Part Four: GoTrue', url: '/guides/auth/auth-deep-dive/auth-gotrue' },
{
name: 'Part Five: Google OAuth',
url: '/learn/auth-deep-dive/auth-google-oauth',
url: '/guides/auth/auth-deep-dive/auth-google-oauth',
},
],
},
@@ -751,6 +755,10 @@ export const database: NavMenuConstant = {
url: '/guides/database/postgres/roles',
},
{ name: 'Managing secrets with Vault', url: '/guides/database/vault' },
{
name: 'Superuser Access and Unsupported Operations',
url: '/guides/database/postgres/roles-superuser',
},
],
},
{
@@ -938,6 +946,10 @@ export const database: NavMenuConstant = {
name: 'Print PostgreSQL Version',
url: '/guides/database/postgres/which-version-of-postgres',
},
{
name: 'Replicating from Supabase to External Postgres',
url: '/guides/database/postgres/setup-replication-external',
},
],
},
],
@@ -1622,6 +1634,10 @@ export const platform: NavMenuConstant = {
name: 'Enterprise Billing',
url: '/guides/platform/enterprise-billing',
},
{
name: 'Billing FAQ',
url: '/guides/platform/billing-faq',
},
],
},
{
@@ -2110,3 +2126,9 @@ export const references = [
],
},
]
export const navDataForMdx = {
nativeMobileLoginItems: NativeMobileLoginItems,
phoneLoginsItems: PhoneLoginsItems,
socialLoginItems: SocialLoginItems,
}
@@ -4,8 +4,7 @@ import Link from 'next/link'
import { FC } from 'react'
import { Button, IconCommand, IconGitHub, IconSearch, SearchButton } from 'ui'
import { useIsLoggedIn, useIsUserLoading } from 'common'
import ThemeToggle from '@ui/components/ThemeProvider/ThemeToggle'
import { ThemeToggle } from 'ui-patterns/ThemeToggle'
const TopNavBar: FC = () => {
const isLoggedIn = useIsLoggedIn()
@@ -64,8 +64,8 @@ export function ComboBox<Opt extends ComboBoxOption>({
{isLoading
? 'Loading...'
: options.length === 0
? `No ${name} found`
: selectedOptionDisplayName ?? `Select a ${name}...`}
? `No ${name} found`
: selectedOptionDisplayName ?? `Select a ${name}...`}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
@@ -98,14 +98,14 @@ function OrgProjectSelector() {
const stateSummary: ProjectOrgDataState = isUserLoading
? 'userLoading'
: !isLoggedIn
? 'loggedOut'
: anyIsPending
? 'loggedIn.dataPending'
: anyIsError
? 'loggedIn.dataError'
: projects?.length === 0
? 'loggedIn.dataSuccess.hasNoData'
: 'loggedIn.dataSuccess.hasData'
? 'loggedOut'
: anyIsPending
? 'loggedIn.dataPending'
: anyIsError
? 'loggedIn.dataError'
: projects?.length === 0
? 'loggedIn.dataSuccess.hasNoData'
: 'loggedIn.dataSuccess.hasData'
const formattedData: ComboBoxOption[] = useMemo(
() =>
@@ -194,16 +194,16 @@ function BranchSelector() {
const stateSummary: BranchesDataState = userLoading
? 'userLoading'
: !isLoggedIn
? 'loggedOut'
: !hasBranches
? 'loggedIn.noBranches'
: isPending
? 'loggedIn.branches.dataPending'
: isError
? 'loggedIn.branches.dataError'
: data.length === 0
? 'loggedIn.branches.dataSuccess.noData'
: 'loggedIn.branches.dataSuccess.hasData'
? 'loggedOut'
: !hasBranches
? 'loggedIn.noBranches'
: isPending
? 'loggedIn.branches.dataPending'
: isError
? 'loggedIn.branches.dataError'
: data.length === 0
? 'loggedIn.branches.dataSuccess.noData'
: 'loggedIn.branches.dataSuccess.hasData'
const formattedData: ComboBoxOption[] =
stateSummary !== 'loggedIn.branches.dataSuccess.hasData'
@@ -282,14 +282,14 @@ function VariableView({ variable, className }: { variable: Variable; className?:
const stateSummary: VariableDataState = isUserLoading
? 'userLoading'
: !isLoggedIn
? 'loggedOut'
: !ref
? 'loggedIn.noSelectedProject'
: isPending
? 'loggedIn.selectedProject.dataPending'
: isError
? 'loggedIn.selectedProject.dataError'
: 'loggedIn.selectedProject.dataSuccess'
? 'loggedOut'
: !ref
? 'loggedIn.noSelectedProject'
: isPending
? 'loggedIn.selectedProject.dataPending'
: isError
? 'loggedIn.selectedProject.dataError'
: 'loggedIn.selectedProject.dataSuccess'
let variableValue: string = null
if (stateSummary === 'loggedIn.selectedProject.dataSuccess') {
@@ -319,8 +319,8 @@ function VariableView({ variable, className }: { variable: Variable; className?:
stateSummary === 'loggedIn.selectedProject.dataPending'
? 'Loading...'
: stateSummary === 'loggedIn.selectedProject.dataSuccess'
? variableValue
: `YOUR ${prettyFormatVariable[variable].toUpperCase()}`
? variableValue
: `YOUR ${prettyFormatVariable[variable].toUpperCase()}`
}
/>
<CopyToClipboard text={variableValue ?? ''}>
@@ -8,6 +8,26 @@ export type Branch = BranchesData[number]
export type Variable = 'url' | 'anonKey'
function removeDoubleQuotes(str: string) {
return str.replaceAll('"', '')
}
function escapeDoubleQuotes(str: string) {
return str.replaceAll('"', '\\"')
}
function unescapeDoubleQuotes(str: string) {
/**
* Regex matches a backslash followed by a double quote, except when the
* backslash is escaped.
*
* Negative lookbehind:
* \\(?:\\\\)* -> An odd number of backslashes (means the next backslash is escaped)
* (?:^|[^\\]) -> Backslash group is at start or follows non-backslash
*/
return str.replace(/(?<!(?:^|[^\\])\\(?:\\\\)*)\\"/g, '"')
}
export const prettyFormatVariable: Record<Variable, string> = {
url: 'Project URL',
anonKey: 'Anon key',
@@ -18,15 +38,19 @@ export function toDisplayNameOrgProject(org: Org, project: Project) {
}
export function toOrgProjectValue(org: Org, project: Project) {
// @ts-ignore -- problem in OpenAPI spec -- project has ref property
return JSON.stringify([org.id, project.ref, toDisplayNameOrgProject(org, project)])
return escapeDoubleQuotes(
// @ts-ignore -- problem in OpenAPI spec -- project has ref property
JSON.stringify([org.id, project.ref, removeDoubleQuotes(toDisplayNameOrgProject(org, project))])
)
}
export function fromOrgProjectValue(
maybeOrgProject: string
): [string, string, string] | [null, null, null] {
try {
const data = JSON.parse(maybeOrgProject)
// not restoring the double quotes on the display name is fine because it's only used for
// command fuzzy search, not for exact/literal matching
const data = JSON.parse(unescapeDoubleQuotes(maybeOrgProject))
if (!Array.isArray(data) || data.length !== 3) {
throw Error("Shape of parsed JSON doesn't match form of org and project value")
}
@@ -37,12 +61,14 @@ export function fromOrgProjectValue(
}
export function toBranchValue(branch: Branch) {
return JSON.stringify([branch.id, branch.name])
return escapeDoubleQuotes(JSON.stringify([branch.id, removeDoubleQuotes(branch.name)]))
}
export function fromBranchValue(maybeBranch: string): [string, string] | [null, null] {
try {
const data = JSON.parse(maybeBranch)
// not restoring the double quotes on the branch name is fine because it's only used for
// command fuzzy search, not for exact/literal matching
const data = JSON.parse(unescapeDoubleQuotes(maybeBranch))
if (!Array.isArray(data) || data.length !== 2) {
throw Error("Shape of parsed JSON doesn't match form of branch value")
}
@@ -158,8 +158,8 @@ export default function RealtimeLimitsEstimater({}) {
{computeAddOn === 'micro'
? 'Micro'
: computeAddOn === 'small'
? 'Small to medium'
: 'Large to 16XL'}
? 'Small to medium'
: 'Large to 16XL'}
</h4>
<table className="table-auto">
<thead>
@@ -0,0 +1,11 @@
import { lazy, Suspense } from 'react'
const Estimator = lazy(() => import('./RealtimeLimitsEstimator'))
export function RealtimeLimitsEstimator() {
return (
<Suspense>
<Estimator />
</Suspense>
)
}
+104 -75
View File
@@ -1,80 +1,94 @@
/**
* This entire file could do with a cleanup and better code-splitting, but one thing at a time.
*/
// Basic UI things
import Link from 'next/link'
import { Alert, Button, CodeBlock, GlassPanel, markdownComponents, Tabs } from 'ui'
import StepHikeCompact from '~/components/StepHikeCompact'
import { Accordion, Admonition, Alert, Button, CodeBlock, markdownComponents, Tabs } from 'ui'
import { GlassPanel } from 'ui-patterns/GlassPanel'
import { IconPanel } from 'ui-patterns/IconPanel'
import { ThemeImage } from 'ui-patterns/ThemeImage'
// Common components
import { CH } from '@code-hike/mdx/components'
import StepHikeCompact from '~/components/StepHikeCompact'
import ButtonCard from './ButtonCard'
import { Heading } from './CustomHTMLElements'
// Reference guide specific
// [Charis] I think we can factor these out so they aren't in the bundle for absolutely everything
import CliGlobalFlagsHandler from '~/components/reference/enrichments/cli/CliGlobalFlagsHandler'
import RefSubLayout from '~/layouts/ref/RefSubLayout'
import RefHeaderSection from './reference/RefHeaderSection'
// Other components
import RefSubLayout from '~/layouts/ref/RefSubLayout'
import { Heading } from './CustomHTMLElements'
import AuthProviders from '~/components/AuthProviders'
import Options from '~/components/Options'
import Param from '~/components/Params'
import { ProjectConfigVariables } from './ProjectConfigVariables'
// Data wrappers
import { NavData } from './NavData'
// Partials
import DatabaseSetup from './MDX/database_setup.mdx'
import GetSessionWarning from './MDX/get_session_warning.mdx'
import HuggingFaceDeployment from './MDX/ai/quickstart_hf_deployment.mdx'
import MigrationWarnings from './MDX/migration_warnings.mdx'
import ProjectSetup from './MDX/project_setup.mdx'
import QuickstartIntro from './MDX/quickstart_intro.mdx'
import SocialProviderSettingsSupabase from './MDX/social_provider_settings_supabase.mdx'
import SocialProviderSetup from './MDX/social_provider_setup.mdx'
import StorageManagement from './MDX/storage_management.mdx'
import MigrationWarnings from './MDX/migration_warnings.mdx'
import GetSessionWarning from './MDX/get_session_warning.mdx'
import { CH } from '@code-hike/mdx/components'
import RefHeaderSection from './reference/RefHeaderSection'
import { ProjectConfigVariables } from './ProjectConfigVariables'
// Ref version specific
import CliGlobalFlagsHandler from '~/components/reference/enrichments/cli/CliGlobalFlagsHandler'
import Options from '~/components/Options'
import Param from '~/components/Params'
import { Admonition, ThemeImage } from 'ui'
// Icons
import {
IconMenuJavascript,
IconMenuHome,
IconMenuGettingStarted,
IconMenuDatabase,
IconMenuRestApis,
IconMenuAuth,
IconMenuEdgeFunctions,
IconMenuRealtime,
IconMenuStorage,
IconMenuPlatform,
IconMenuResources,
IconMenuSelfHosting,
IconMenuIntegrations,
IconMenuFlutter,
IconMenuPython,
IconMenuCsharp,
IconMenuSwift,
IconMenuKotlin,
IconMenuApi,
IconMenuAuth,
IconMenuCli,
IconMenuCsharp,
IconMenuDatabase,
IconMenuEdgeFunctions,
IconMenuFlutter,
IconMenuGettingStarted,
IconMenuHome,
IconMenuIntegrations,
IconMenuJavascript,
IconMenuKotlin,
IconMenuPlatform,
IconMenuPython,
IconMenuRealtime,
IconMenuResources,
IconMenuRestApis,
IconMenuSelfHosting,
IconMenuStorage,
IconMenuSwift,
} from './Navigation/NavigationMenu/HomeMenuIcons'
// Heavy/rare (lazy-loaded)
import { AppleSecretGenerator } from './AppleSecretGenerator'
import { Mermaid } from './Mermaid'
import { RealtimeLimitsEstimator } from './RealtimeLimitsEstimator'
const components = {
...markdownComponents,
Accordion,
Admonition,
Button,
ButtonCard,
CH,
CodeBlock,
GlassPanel,
Link,
QuickstartIntro,
DatabaseSetup,
ProjectSetup,
MigrationWarnings,
GetSessionWarning,
SocialProviderSetup,
SocialProviderSettingsSupabase,
StepHikeCompact,
ProjectConfigVariables,
StorageManagement,
Alert: (props: any) => (
<Alert {...props} className="not-prose">
{props.children}
</Alert>
),
Tabs: (props: any) => <Tabs wrappable {...props} />,
TabPanel: (props: any) => <Tabs.Panel {...props}>{props.children}</Tabs.Panel>,
AppleSecretGenerator,
AuthProviders,
Button,
ButtonCard,
CH,
CliGlobalFlagsHandler: () => <CliGlobalFlagsHandler />,
CodeBlock,
DatabaseSetup,
GetSessionWarning,
GlassPanel,
h2: (props: any) => (
<Heading tag="h2" {...props}>
{props.children}
@@ -90,32 +104,47 @@ const components = {
{props.children}
</Heading>
),
HuggingFaceDeployment,
IconMenuApi,
IconMenuAuth,
IconMenuCli,
IconMenuCsharp,
IconMenuDatabase,
IconMenuEdgeFunctions,
IconMenuFlutter,
IconMenuGettingStarted,
IconMenuHome,
IconMenuIntegrations,
IconMenuJavascript,
IconMenuKotlin,
IconMenuPlatform,
IconMenuPython,
IconMenuRealtime,
IconMenuResources,
IconMenuRestApis,
IconMenuSelfHosting,
IconMenuStorage,
IconMenuSwift,
IconPanel,
Image: (props: any) => <ThemeImage fill className="object-contain" {...props} />,
RefSubLayout,
RefHeaderSection: (props: any) => <RefHeaderSection {...props} />,
CliGlobalFlagsHandler: () => <CliGlobalFlagsHandler />,
Link,
Mermaid,
MigrationWarnings,
NavData,
Options,
Param,
IconMenuJavascript,
IconMenuHome,
IconMenuGettingStarted,
IconMenuDatabase,
IconMenuRestApis,
IconMenuAuth,
IconMenuEdgeFunctions,
IconMenuRealtime,
IconMenuStorage,
IconMenuPlatform,
IconMenuResources,
IconMenuSelfHosting,
IconMenuIntegrations,
IconMenuFlutter,
IconMenuPython,
IconMenuCsharp,
IconMenuKotlin,
IconMenuSwift,
IconMenuApi,
IconMenuCli,
ProjectConfigVariables,
ProjectSetup,
QuickstartIntro,
RealtimeLimitsEstimator,
RefHeaderSection: (props: any) => <RefHeaderSection {...props} />,
RefSubLayout,
SocialProviderSettingsSupabase,
SocialProviderSetup,
StepHikeCompact,
StorageManagement,
TabPanel: (props: any) => <Tabs.Panel {...props}>{props.children}</Tabs.Panel>,
Tabs: (props: any) => <Tabs wrappable {...props} />,
}
export default components
@@ -126,14 +126,14 @@ const RefFunctionSection: React.FC<IRefFunctionSection> = (props) => {
const codeBlockLang = example?.code?.startsWith('```js')
? 'js'
: example?.code?.startsWith('```ts')
? 'ts'
: example?.code?.startsWith('```dart')
? 'dart'
: example?.code?.startsWith('```c#')
? 'csharp'
: example?.code?.startsWith('```kotlin')
? 'kotlin'
: 'js'
? 'ts'
: example?.code?.startsWith('```dart')
? 'dart'
: example?.code?.startsWith('```c#')
? 'csharp'
: example?.code?.startsWith('```kotlin')
? 'kotlin'
: 'js'
// `
// import { createClient } from '@supabase/supabase-js'
@@ -1,12 +1,10 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'ai',
title: 'AI & Vectors',
description: 'The best vector database is the database you already have.',
subtitle: 'The best vector database is the database you already have.',
sidebar_label: 'Overview',
}
---
id: 'ai'
title: 'AI & Vectors'
description: 'The best vector database is the database you already have.'
subtitle: 'The best vector database is the database you already have.'
hideToc: true
---
Supabase provides an open source toolkit for developing AI applications using Postgres and pgvector. Use the Supabase client libraries to store, index, and query your vector embeddings at scale.
@@ -109,7 +107,26 @@ export const integrations = [
## Case studies
<div className="grid md:grid-cols-12 gap-4 not-prose">
{customers.map((x) => (
{[
{
name: 'Berri AI Boosts Productivity by Migrating from AWS RDS to Supabase with pgvector',
description:
'Learn how Berri AI overcame challenges with self-hosting their vector database on AWS RDS and successfully migrated to Supabase.',
href: 'https://supabase.com/customers/berriai',
},
{
name: 'Mendable switches from Pinecone to Supabase for PostgreSQL vector embeddings',
description:
'How Mendable boosts efficiency and accuracy of chat powered search for documentation using Supabase with pgvector',
href: 'https://supabase.com/customers/mendableai',
},
{
name: 'Markprompt: GDPR-Compliant AI Chatbots for Docs and Websites',
description:
"AI-powered chatbot platform, Markprompt, empowers developers to deliver efficient and GDPR-compliant prompt experiences on top of their content, by leveraging Supabase's secure and privacy-focused database and authentication solutions",
href: 'https://supabase.com/customers/markprompt',
},
].map((x) => (
<div className="col-span-4" key={x.href}>
<Link href={x.href} passHref>
<GlassPanel title={x.name}>{x.description}</GlassPanel>
@@ -117,28 +134,3 @@ export const integrations = [
</div>
))}
</div>
export const customers = [
{
name: 'Berri AI Boosts Productivity by Migrating from AWS RDS to Supabase with pgvector',
description:
'Learn how Berri AI overcame challenges with self-hosting their vector database on AWS RDS and successfully migrated to Supabase.',
href: 'https://supabase.com/customers/berriai',
},
{
name: 'Mendable switches from Pinecone to Supabase for PostgreSQL vector embeddings',
description:
'How Mendable boosts efficiency and accuracy of chat powered search for documentation using Supabase with pgvector',
href: 'https://supabase.com/customers/mendableai',
},
{
name: 'Markprompt: GDPR-Compliant AI Chatbots for Docs and Websites',
description:
"AI-powered chatbot platform, Markprompt, empowers developers to deliver efficient and GDPR-compliant prompt experiences on top of their content, by leveraging Supabase's secure and privacy-focused database and authentication solutions",
href: 'https://supabase.com/customers/markprompt',
},
]
export const Page = ({ children }) => <Layout meta={meta} children={children} hideToc={true} />
export default Page
@@ -1,14 +1,10 @@
import Layout from '~/layouts/DefaultGuideLayout'
import { Tabs } from 'ui'
export const TabPanel = Tabs.Panel
export const meta = {
id: 'ai-choosing-compute-addon',
title: 'Choosing your Compute Add-on',
description: 'Choosing the right Compute Add-on for your vector workload.',
subtitle: 'Choosing the right Compute Add-on for your vector workload.',
sidebar_label: 'Choosing Compute Add-on',
}
---
id: 'ai-choosing-compute-addon'
title: 'Choosing your Compute Add-on'
description: 'Choosing the right Compute Add-on for your vector workload.'
subtitle: 'Choosing the right Compute Add-on for your vector workload.'
sidebar_label: 'Choosing Compute Add-on'
---
You have two options for scaling your vector workload:
@@ -339,7 +335,3 @@ We follow techniques outlined in the [ANN Benchmarks](https://github.com/erikber
Each test is run for a minimum of 30-40 minutes. They include a series of experiments executed at different concurrency levels to measure the engine's performance under different load types. The results are then averaged.
As a general recommendation, we suggest using a concurrency level of 5 or more for most workloads and 30 or more for high-load workloads.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'ai-concepts',
title: 'Concepts',
description: 'Learn about embeddings within AI and vector applications.',
sidebar_label: 'Concepts',
}
---
id: 'ai-concepts'
title: 'Concepts'
description: 'Learn about embeddings within AI and vector applications.'
sidebar_label: 'Concepts'
---
Embeddings are core to many AI and vector applications. This guide covers these concepts. If you prefer to get started right away, see our guide on [Generating Embeddings](/docs/guides/ai/quickstarts/generate-text-embeddings).
@@ -61,7 +59,3 @@ Why is this useful? Once we have generated embeddings on multiple texts, it is t
## See also
- [Structured and Unstructured embeddings](/docs/guides/ai/structured-unstructured)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,12 +1,10 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'ai-engineering-for-scale',
title: 'Engineering for Scale',
description: 'Building an enterprise-grade vector architecture',
subtitle: 'Building an enterprise-grade vector architecture.',
sidebar_label: 'Engineering for Scale',
}
---
id: 'ai-engineering-for-scale'
title: 'Engineering for Scale'
description: 'Building an enterprise-grade vector architecture'
subtitle: 'Building an enterprise-grade vector architecture.'
sidebar_label: 'Engineering for Scale'
---
Content sources for vectors can be extremely large. As you grow you should run your Vector workloads across several secondary databases (sometimes called "pods"), which allows each collection to scale independently.
@@ -145,7 +143,3 @@ This diagram provides an example architecture that allows you to access the coll
}}
/>
</div>
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
import StepHikeCompact from '~/components/StepHikeCompact'
export const meta = {
title: 'Building ChatGPT plugins',
subtitle: 'Use Supabase as a Retrieval Store for your ChatGPT plugin.',
breadcrumb: 'AI Examples',
}
---
title: 'Building ChatGPT plugins'
subtitle: 'Use Supabase as a Retrieval Store for your ChatGPT plugin.'
breadcrumb: 'AI Examples'
---
ChatGPT recently released [Plugins](https://openai.com/blog/chatgpt-plugins) which help ChatGPT access up-to-date information, run computations, or use third-party services.
If you're building a plugin for ChatGPT, you'll probably want to answer questions from a specific source. We can solve this with “retrieval plugins”, which allow ChatGPT to access information from a database.
@@ -151,6 +148,3 @@ And after ChatGPT receives a response from the plugin it will answer your questi
- ChatGPT Retrieval Plugin: [github.com/openai/chatgpt-retrieval-plugin](https://github.com/openai/chatgpt-retrieval-plugin)
- ChatGPT Plugins: [official documentation](https://platform.openai.com/docs/plugins/introduction)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,12 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
import StepHikeCompact from '~/components/StepHikeCompact'
export const meta = {
title: 'Adding generative Q&A for your documentation',
subtitle:
'Learn how to build a ChatGPT-style doc search powered using our headless search toolkit.',
breadcrumb: 'AI Examples',
}
---
title: 'Adding generative Q&A for your documentation'
subtitle: 'Learn how to build a ChatGPT-style doc search powered using our headless search toolkit.'
breadcrumb: 'AI Examples'
---
Supabase provides a [Headless Search Toolkit](https://github.com/supabase/headless-vector-search) for adding "Generative Q&A" to your documentation. The toolkit is "headless", so that you can integrate it into your existing website and style it to match your website theme.
@@ -37,7 +33,7 @@ There are 3 steps to build similarity search inside your documentation:
### Prepare your database
To prepare, create a [new Supabase project](https://database.new) and store the database and API credentials, which you can find in the project [settings](https://supabase.com/dashboard/_/settings).
To prepare, create a [new Supabase project](https://database.new) and store the database and API credentials, which you can find in the project [settings](https://supabase.com/dashboard/project/_/settings).
Now we can use the [Headless Vector Search](https://github.com/supabase/headless-vector-search#set-up) instructions to set up the database:
@@ -119,6 +115,3 @@ const onSubmit = (e: Event) => {
- Read about how we built [ChatGPT for the Supabase Docs](https://supabase.com/blog/chatgpt-supabase-docs).
- Read the pgvector Docs for [Embeddings and vector similarity](/docs/guides/database/extensions/pgvector)
- See how to build something like this from scratch [using Next.js](/docs/guides/ai/examples/nextjs-vector-search).
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,14 +1,10 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
title: 'Generate image captions using Hugging Face',
description:
'Use the Hugging Face Inference API to make calls to 100,000+ Machine Learning models from Supabase Edge Functions.',
subtitle:
'Use the Hugging Face Inference API to make calls to 100,000+ Machine Learning models from Supabase Edge Functions.',
video: 'https://www.youtube.com/v/OgnYxRkxEUw',
tocVideo: 'OgnYxRkxEUw',
}
---
title: 'Generate image captions using Hugging Face'
description: 'Use the Hugging Face Inference API to make calls to 100,000+ Machine Learning models from Supabase Edge Functions.'
subtitle: 'Use the Hugging Face Inference API to make calls to 100,000+ Machine Learning models from Supabase Edge Functions.'
video: 'https://www.youtube.com/v/OgnYxRkxEUw'
tocVideo: 'OgnYxRkxEUw'
---
We can combine Hugging Face with [Supabase Storage](https://supabase.com/storage) and [Database Webhooks](https://supabase.com/docs/guides/database/webhooks) to automatically caption for any image we upload to a storage bucket.
@@ -93,7 +89,3 @@ serve(async (req) => {
return new Response('ok')
})
```
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'examples-image-search-python',
title: 'Image Search with OpenAI CLIP',
description: 'Implement image search with the OpenAI CLIP Model and Supabase Vector.',
subtitle: 'Implement image search with the OpenAI CLIP Model and Supabase Vector.',
}
---
id: 'examples-image-search-python'
title: 'Image Search with OpenAI CLIP'
description: 'Implement image search with the OpenAI CLIP Model and Supabase Vector.'
subtitle: 'Implement image search with the OpenAI CLIP Model and Supabase Vector.'
---
The [OpenAI CLIP Model](https://github.com/openai/CLIP) was trained on a variety of (image, text)-pairs. You can use the CLIP model for:
@@ -171,7 +169,3 @@ That's it, go ahead and test it out by running `poetry run search` and you will
## Conclusion
With just a couple of lines of Python you are able to implement image search as well as reverse image search using OpenAI's CLIP model and Supabase Vector.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,14 +1,10 @@
import Layout from '~/layouts/DefaultGuideLayout'
import StepHikeCompact from '~/components/StepHikeCompact'
export const meta = {
title: 'Vector search with Next.js and OpenAI',
subtitle:
'Learn how to build a ChatGPT-style doc search powered by Next.js, OpenAI, and Supabase.',
breadcrumb: 'AI Examples',
video: 'https://www.youtube.com/v/xmfNUCjszh4',
tocVideo: 'xmfNUCjszh4',
}
---
title: 'Vector search with Next.js and OpenAI'
subtitle: 'Learn how to build a ChatGPT-style doc search powered by Next.js, OpenAI, and Supabase.'
breadcrumb: 'AI Examples'
video: 'https://www.youtube.com/v/xmfNUCjszh4'
tocVideo: 'xmfNUCjszh4'
---
While our [Headless Vector search](/docs/guides/ai/examples/headless-vector-search) provides a toolkit for generative Q&A, in this tutorial we'll go more in-depth, build a custom ChatGPT-like search experience from the ground-up using Next.js. You will:
@@ -543,6 +539,3 @@ Want to learn more about the awesome tech that is powering this?
allowFullScreen
></iframe>
</div>
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,13 +1,11 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'examples-openai',
title: 'Generating OpenAI GPT3 completions',
description: 'Generate GPT text completions using OpenAI and Supabase Edge Functions.',
subtitle: 'Generate GPT text completions using OpenAI and Supabase Edge Functions.',
video: 'https://www.youtube-nocookie.com/v/29p8kIqyU_Y',
tocVideo: '29p8kIqyU_Y',
}
---
id: 'examples-openai'
title: 'Generating OpenAI GPT3 completions'
description: 'Generate GPT text completions using OpenAI and Supabase Edge Functions.'
subtitle: 'Generate GPT text completions using OpenAI and Supabase Edge Functions.'
video: 'https://www.youtube-nocookie.com/v/29p8kIqyU_Y'
tocVideo: '29p8kIqyU_Y'
---
OpenAI provides a [completions API](https://platform.openai.com/docs/api-reference/completions) that allows you to use their generative GPT models in your own applications.
@@ -37,7 +35,7 @@ We'll design the function to take your user's query (via POST request) and forwa
import OpenAI from 'https://deno.land/x/openai@v4.24.0/mod.ts'
Deno.serve(async (req) => {
const query = await req.json()
const { query } = await req.json()
const apiKey = Deno.env.get('OPENAI_API_KEY')
const openai = new OpenAI({
apiKey: apiKey,
@@ -112,7 +110,3 @@ If you're interesting in learning how to use this to build your own ChatGPT, rea
allowFullScreen
></iframe>
</div>
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,12 +1,10 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'ai-going-to-prod',
title: 'Going to Production',
description: 'Checklist for going to production with your AI application.',
subtitle: 'Going to production checklist for AI applications.',
sidebar_label: 'Going to Production',
}
---
id: 'ai-going-to-prod'
title: 'Going to Production'
description: 'Checklist for going to production with your AI application.'
subtitle: 'Going to production checklist for AI applications.'
sidebar_label: 'Going to Production'
---
This guide will help you to prepare your application for production. We'll provide actionable steps to help you scale your application, ensure that it is reliable, can handle the load, and provide optimal accuracy for your use case.
@@ -125,7 +123,3 @@ Or take a look at our [pgvector 0.5.0 performance](https://supabase.com/blog/inc
}}
/>
</div>
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,12 +1,10 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'ai-google-colab',
title: 'Google Colab',
description: 'Use Google Colab to manage your Supabase Vector store.',
subtitle: 'Use Google Colab to manage your Supabase Vector store.',
sidebar_label: 'Google Colab',
}
---
id: 'ai-google-colab'
title: 'Google Colab'
description: 'Use Google Colab to manage your Supabase Vector store.'
subtitle: 'Use Google Colab to manage your Supabase Vector store.'
sidebar_label: 'Google Colab'
---
<a
className="w-64"
@@ -39,7 +37,7 @@ pip install vecs
## Connect to your database
Find the Postgres pooler connection string for your Supabase project in the [database settings](https://supabase.com/dashboard/_/settings/database) of the dashboard. Copy the "URI" format, which should look something like `postgres://postgres.xxxx:password@xxxx.pooler.supabase.com:6543/postgres`
Find the Postgres pooler connection string for your Supabase project in the [database settings](https://supabase.com/dashboard/project/_/settings/database) of the dashboard. Copy the "URI" format, which should look something like `postgres://postgres.xxxx:password@xxxx.pooler.supabase.com:6543/postgres`
Create a new code block below the install block (`ctrl+m b`) and add the following code using the Postgres URI you copied above:
@@ -113,7 +111,3 @@ You can lean more about creating indexes in the [Vecs documentation](https://sup
## Resources
- Vecs API: [supabase.github.io/vecs/api](https://supabase.github.io/vecs/api)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'ai-hugging-face',
title: 'Hugging Face Inference API',
description: 'Learn how to integrate hugging face models with Supabase',
sidebar_label: 'Hugging Face',
}
---
id: 'ai-hugging-face'
title: 'Hugging Face Inference API'
description: 'Learn how to integrate hugging face models with Supabase'
sidebar_label: 'Hugging Face'
---
[Hugging Face](https://huggingface.co) is an open source hub for AI/ML models and tools. With over 100,000 machine learning models available, Hugging Face provides a great way to integrate specialized AI & ML tasks into your application.
@@ -170,7 +168,3 @@ Try running some other [AI tasks](#ai-tasks).
- Official [Hugging Face site](https://huggingface.co/).
- Official [Hugging Face JS docs](https://huggingface.co/docs/huggingface.js).
- [Generate image captions](/docs/guides/ai/examples/huggingface-image-captioning) using Hugging Face.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,13 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'ai-integration-llamaindex',
title:
'Learn how to integrate Supabase with LlamaIndex, a data framework for your LLM applications.',
subtitle:
'Learn how to integrate Supabase with LlamaIndex, a data framework for your LLM applications.',
breadcrumb: 'AI Integrations',
}
---
id: 'ai-integration-llamaindex'
title: 'Learn how to integrate Supabase with LlamaIndex, a data framework for your LLM applications.'
subtitle: 'Learn how to integrate Supabase with LlamaIndex, a data framework for your LLM applications.'
breadcrumb: 'AI Integrations'
---
This guide will walk you through a basic example using the LlamaIndex [SupabaseVectorStore](https://github.com/supabase/supabase/blob/master/examples/ai/llamaindex/llamaindex.ipynb).
@@ -46,7 +42,19 @@ DB_CONNECTION = "postgresql://<user>:<password>@<host>:<port>/<db_name>"
vx = vecs.create_client(DB_CONNECTION)
```
Replace the `DB_CONNECTION` with your own connection string for your database, which you set up in first step of this guide.
Replace the `DB_CONNECTION` with your own connection string for your database. You can find the Postgres connection string in the [Database Settings](https://supabase.com/dashboard/project/_/settings/database) of your Supabase project.
<Admonition type='note'>
SQLAlchemy requires the connection string to start with `postgresql://` (instead of `postgres://`). Don't forget to rename this after copying the string from the dashboard.
</Admonition>
<Admonition type='note'>
You must use the "connection pooling" string (domain ending in `*.pooler.supabase.com`) with Google Colab since Colab does not support IPv6.
</Admonition>
## Stepping through the notebook
@@ -60,7 +68,3 @@ You can view the inserted items in the [Table Editor](https://supabase.com/dashb
- Visit the LlamaIndex + SupabaseVectorStore [docs](https://gpt-index.readthedocs.io/en/latest/examples/vector_stores/SupabaseVectorIndexDemo.html)
- Visit the official LlamaIndex [repo](https://github.com/jerryjliu/llama_index/)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,12 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'ai-integration-roboflow',
title: 'Roboflow',
subtitle:
'Learn how to integrate Supabase with Roboflow, a tool for running fine-tuned and foundation vision models.',
breadcrumb: 'AI Integrations',
}
---
id: 'ai-integration-roboflow'
title: 'Roboflow'
subtitle: 'Learn how to integrate Supabase with Roboflow, a tool for running fine-tuned and foundation vision models.'
breadcrumb: 'AI Integrations'
---
In this guide, we will walk through two examples of using [Roboflow Inference](https://inference.roboflow.com) to run fine-tuned and foundation models. We will run inference and save predictions using an object detection model and [CLIP](https://github.com/openai/CLIP).
@@ -238,7 +235,3 @@ print(result[0])
- [Roboflow Inference documentation](https://inference.roboflow.com)
- [Roboflow Getting Started guide](https://blog.roboflow.com/getting-started-with-roboflow/)
- [How to Build a Semantic Image Search Engine with Supabase and OpenAI CLIP](https://blog.roboflow.com/how-to-use-semantic-search-supabase-openai-clip/)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,14 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
import { Tabs } from 'ui'
export const TabPanel = Tabs.Panel
export const meta = {
id: 'ai-lang-chain',
title: 'LangChain',
description:
'Learn how to integrate Supabase with LangChain, a popular framework for composing AI, Vectors, and embeddings',
sidebar_label: 'LangChain',
}
---
id: 'ai-lang-chain'
title: 'LangChain'
description: 'Learn how to integrate Supabase with LangChain, a popular framework for composing AI, Vectors, and embeddings'
sidebar_label: 'LangChain'
---
[LangChain](https://langchain.com/) is a popular framework for working with AI, Vectors, and embeddings. LangChain supports using Supabase as a [vector store](https://js.langchain.com/docs/modules/indexes/vector_stores/integrations/supabase), using the `pgvector` extension.
@@ -230,7 +225,3 @@ You can install the LangChain Hybrid Search function though our [database.dev pa
- Official [LangChain site](https://langchain.com/).
- Official [LangChain docs](https://js.langchain.com/docs/modules/indexes/vector_stores/integrations/supabase).
- Supabase [Hybrid Search](https://js.langchain.com/docs/modules/indexes/retrievers/supabase-hybrid).
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,18 +1,18 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'ai-python-clients',
title: 'Choosing a Client',
description: 'Learn how to manage vectors using Python',
sidebar_label: 'Choosing a Client',
}
---
id: 'ai-python-clients'
title: 'Choosing a Client'
description: 'Learn how to manage vectors using Python'
sidebar_label: 'Choosing a Client'
---
As described in [Structured & Unstructured Embeddings](/docs/guides/ai/structured-unstructured), AI workloads come in many forms.
For data science or ephemeral workloads, the [Supabase Vecs](https://supabase.github.io/vecs/) client gets you started quickly. All you need is a connection string and vecs handles setting up your database to store and query vectors with associated metadata.
<Admonition type="tip">
You can get your connection string from the [**Database Settings**](https://supabase.com/dashboard/project/_/settings/database) page in your dashboard. Make sure to check **Use connection pooling**, then copy the URI. Also, change the URI scheme from `postgres` to `postgresql`. `vecs` uses SQLAlchemy under the hood, which only supports `postgresql` as a dialect.
</Admonition>
For production python applications with version controlled migrations, we recommend adding first class vector support to your toolchain by [registering the vector type with your ORM](https://github.com/pgvector/pgvector-python). pgvector provides bindings for the most commonly used SQL drivers/libraries including Django, SQLAlchemy, SQLModel, psycopg, asyncpg and Peewee.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'ai-vecs-python-client',
title: 'Face similarity search',
subtitle: 'Identify the celebrities who look most similar to you using Supabase Vecs.',
breadcrumb: 'AI Quickstarts',
}
---
id: 'ai-vecs-python-client'
title: 'Face similarity search'
subtitle: 'Identify the celebrities who look most similar to you using Supabase Vecs.'
---
This guide will walk you through a ["Face Similarity Search"](https://github.com/supabase/supabase/blob/master/examples/ai/face_similarity.ipynb) example using Colab and Supabase Vecs. You will be able to identify the celebrities who look most similar to you (or any other person). You will:
@@ -43,7 +40,19 @@ DB_CONNECTION = "postgresql://<user>:<password>@<host>:<port>/<db_name>"
vx = vecs.create_client(DB_CONNECTION)
```
Replace the `DB_CONNECTION` with your own connection string for your database, which you set up in first step of this guide.
Replace the `DB_CONNECTION` with your own connection string for your database. You can find the Postgres connection string in the [Database Settings](https://supabase.com/dashboard/project/_/settings/database) of your Supabase project.
<Admonition type='note'>
SQLAlchemy requires the connection string to start with `postgresql://` (instead of `postgres://`). Don't forget to rename this after copying the string from the dashboard.
</Admonition>
<Admonition type='note'>
You must use the "connection pooling" string (domain ending in `*.pooler.supabase.com`) with Google Colab since Colab does not support IPv6.
</Admonition>
## Stepping through the notebook
@@ -56,7 +65,3 @@ You can view the inserted items in the [Table Editor](https://supabase.com/dashb
## Next steps
You can now start building your own applications with Vecs. Check our [examples](/docs/guides/ai#examples) for ideas.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'ai-generating-embeddings',
title: 'Generate Embeddings',
subtitle: 'Generate text embeddings using Edge Functions + Transformer.js.',
breadcrumb: 'AI Quickstarts',
}
---
id: 'ai-generating-embeddings'
title: 'Generate Embeddings'
subtitle: 'Generate text embeddings using Edge Functions + Transformer.js.'
breadcrumb: 'AI Quickstarts'
---
This guide will walk you through how to generate high quality text embeddings in [Edge Functions](/docs/guides/functions) using Transformers.js. Inference is performed directly in Edge Functions using open source Hugging Face models, so no external API is required.
@@ -205,7 +203,3 @@ Let's build an Edge Function that will accept an input string and generate an em
- Learn more about [embedding concepts](/docs/guides/ai/concepts)
- [Store your embeddings](/docs/guides/ai/vector-columns) in a database
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'ai-vecs-python-client',
title: 'Creating and managing collections',
subtitle: 'Connecting to your database with Colab.',
breadcrumb: 'AI Quickstarts',
}
---
id: 'ai-vecs-python-client'
title: 'Creating and managing collections'
subtitle: 'Connecting to your database with Colab.'
breadcrumb: 'AI Quickstarts'
---
This guide will walk you through a basic ["Hello World"](https://github.com/supabase/supabase/blob/master/examples/ai/vector_hello_world.ipynb) example using Colab and Supabase Vecs. You'll learn how to:
@@ -43,7 +41,19 @@ DB_CONNECTION = "postgresql://<user>:<password>@<host>:<port>/<db_name>"
vx = vecs.create_client(DB_CONNECTION)
```
Replace the `DB_CONNECTION` with your own connection string for your database, which you set up in first step of this guide.
Replace the `DB_CONNECTION` with your own connection string for your database. You can find the Postgres connection string in the [Database Settings](https://supabase.com/dashboard/project/_/settings/database) of your Supabase project.
<Admonition type='note'>
SQLAlchemy requires the connection string to start with `postgresql://` (instead of `postgres://`). Don't forget to rename this after copying the string from the dashboard.
</Admonition>
<Admonition type='note'>
You must use the "connection pooling" string (domain ending in `*.pooler.supabase.com`) with Google Colab since Colab does not support IPv6.
</Admonition>
## Stepping through the notebook
@@ -56,7 +66,3 @@ You can view the inserted items in the [Table Editor](https://supabase.com/dashb
## Next steps
You can now start building your own applications with Vecs. Check our [examples](/docs/guides/ai#examples) for ideas.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,12 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
import HuggingFaceDeployment from '~/components/MDX/ai/quickstart_hf_deployment.mdx'
export const meta = {
id: 'ai-vecs-python-client',
title: 'Semantic Text Deduplication',
subtitle: 'Finding duplicate movie reviews with Supabase Vecs.',
breadcrumb: 'AI Quickstarts',
}
---
id: 'ai-vecs-python-client'
title: 'Semantic Text Deduplication'
subtitle: 'Finding duplicate movie reviews with Supabase Vecs.'
breadcrumb: 'AI Quickstarts'
---
This guide will walk you through a ["Semantic Text Deduplication"](https://github.com/supabase/supabase/blob/master/examples/ai/semantic_text_deduplication.ipynb) example using Colab and Supabase Vecs. You'll learn how to find similar movie reviews using embeddings, and remove any that seem like duplicates. You will:
@@ -44,7 +41,19 @@ DB_CONNECTION = "postgresql://<user>:<password>@<host>:<port>/<db_name>"
vx = vecs.create_client(DB_CONNECTION)
```
Replace the `DB_CONNECTION` with your own connection string for your database, which you set up in first step of this guide.
Replace the `DB_CONNECTION` with your own connection string for your database. You can find the Postgres connection string in the [Database Settings](https://supabase.com/dashboard/project/_/settings/database) of your Supabase project.
<Admonition type='note'>
SQLAlchemy requires the connection string to start with `postgresql://` (instead of `postgres://`). Don't forget to rename this after copying the string from the dashboard.
</Admonition>
<Admonition type='note'>
You must use the "connection pooling" string (domain ending in `*.pooler.supabase.com`) with Google Colab since Colab does not support IPv6.
</Admonition>
## Stepping through the notebook
@@ -59,7 +68,3 @@ You can view the inserted items in the [Table Editor](https://supabase.com/dashb
## Next steps
You can now start building your own applications with Vecs. Check our [examples](/docs/guides/ai#examples) for ideas.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,12 +1,10 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'ai-rag-with-permissions',
title: 'RAG with Permissions',
subtitle: 'Fine-grain access control with Retrieval Augmented Generation.',
description: 'Implement fine-grain access control with retrieval augmented generation',
sidebar_label: 'RAG with Permissions',
}
---
id: 'ai-rag-with-permissions'
title: 'RAG with Permissions'
subtitle: 'Fine-grain access control with Retrieval Augmented Generation.'
description: 'Implement fine-grain access control with retrieval augmented generation'
sidebar_label: 'RAG with Permissions'
---
Since pgvector is built on top of Postgres, you can implement fine-grain access control on your vector database using [Row Level Security (RLS)](/docs/guides/database/postgres/row-level-security). This means you can restrict which documents are returned during a vector similarity search to users that have access to them. Supabase also supports [Foreign Data Wrappers (FDW)](/docs/guides/database/extensions/wrappers/overview) which means you can use an external database or data source to determine these permissions if your user data doesn't exist in Supabase.
@@ -272,7 +270,3 @@ order by document_sections.embedding <#> embedding;
There are endless approaches to this problem based on the complexities of each system. Luckily Postgres comes with all the primitives needed to provide access control in the way that works best for your project.
If the examples above didn't fit your use case or you need to adjust them slightly to better fit your existing system, feel free to reach out to [support](https://supabase.com/dashboard/support/new) and we'll be happy to assist you.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,14 +1,10 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'structured-unstructured-embeddings',
title: 'Structured and Unstructured',
description:
'Supabase is flexible enough to associate structured and unstructured metadata with embeddings.',
subtitle:
'Supabase is flexible enough to associate structured and unstructured metadata with embeddings.',
sidebar_label: 'Structured and unstructured embeddings',
}
---
id: 'structured-unstructured-embeddings'
title: 'Structured and Unstructured'
description: 'Supabase is flexible enough to associate structured and unstructured metadata with embeddings.'
subtitle: 'Supabase is flexible enough to associate structured and unstructured metadata with embeddings.'
sidebar_label: 'Structured and unstructured embeddings'
---
Most vector stores treat metadata associated with embeddings like NoSQL, unstructured data. Supabase is flexible enough to store unstructured and structured metadata.
@@ -108,7 +104,3 @@ Both approaches create a table where you can store your embeddings and some meta
- Unstructured metadata is best when fields are unknown/user-defined or when working with data interactively e.g. exploratory research
Both approaches are valid, and the one you should choose depends on your use-case.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,12 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
import StepHikeCompact from '~/components/StepHikeCompact'
export const meta = {
id: 'ai-vecs-python-client',
title: 'Python client',
subtitle: 'Manage unstructured vector stores in PostgreSQL.',
breadcrumb: 'AI Quickstarts',
}
---
id: 'ai-vecs-python-client'
title: 'Python client'
subtitle: 'Manage unstructured vector stores in PostgreSQL.'
breadcrumb: 'AI Quickstarts'
---
Supabase provides a Python client called [`vecs`](https://github.com/supabase/vecs) for managing unstructured vector stores. This client provides a set of useful tools for creating and querying collections in PostgreSQL using the [pgvector](/docs/guides/database/extensions/pgvector) extension.
@@ -85,7 +82,3 @@ For a more in-depth guide on `vecs` collections, see [API](/docs/guides/ai/pytho
- Official Vecs Documentation: https://supabase.github.io/vecs/api
- Source Code: https://github.com/supabase/vecs
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'ai-vector-columns',
title: 'Vector columns',
description: 'Learn how to use vectors within your own Postgres tables',
sidebar_label: 'Vector columns',
}
---
id: 'ai-vector-columns'
title: 'Vector columns'
description: 'Learn how to use vectors within your own Postgres tables'
sidebar_label: 'Vector columns'
---
Supabase offers a number of different ways to store and query vectors within Postgres. The SQL included in this guide is applicable for clients in all programming languages. If you are a Python user see your [Python client options](/docs/guides/ai/python-clients) after reading the `Learn` section.
@@ -166,7 +164,3 @@ Vectors and embeddings can be used for much more than search. Learn more about e
### Indexes
Once your vector table starts to grow, you will likely want to add an index to speed up queries. See [Vector indexes](/docs/guides/ai/vector-indexes) to learn how vector indexes work and how to create them.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'ai-vector-indexes',
title: 'Vector indexes',
description: 'Understanding vector indexes',
sidebar_label: 'Vector indexes',
}
---
id: 'ai-vector-indexes'
title: 'Vector indexes'
description: 'Understanding vector indexes'
sidebar_label: 'Vector indexes'
---
Once your vector table starts to grow, you will likely want to add an index to speed up queries. Without indexes, you'll be performing a sequential scan which can be a resource-intensive operation when you have many records.
@@ -33,7 +31,3 @@ Currently vectors with up to 2,000 dimensions can be indexed.
## Resources
Read more about indexing on `pgvector`'s [GitHub page](https://github.com/pgvector/pgvector#indexing).
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'ai-hnsw-indexes',
title: 'HNSW indexes',
description: 'Understanding HNSW indexes in pgvector',
sidebar_label: 'HNSW indexes',
}
---
id: 'ai-hnsw-indexes'
title: 'HNSW indexes'
description: 'Understanding HNSW indexes in pgvector'
sidebar_label: 'HNSW indexes'
---
HNSW is an algorithm for approximate nearest neighbor search. It is a frequently used index type that can improve performance when querying highly-dimensional vectors, like those representing embeddings.
@@ -92,7 +90,3 @@ Unlike IVFFlat indexes, you are safe to build an HNSW index immediately after th
## Resources
Read more about indexing on `pgvector`'s [GitHub page](https://github.com/pgvector/pgvector#indexing).
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'ai-ivf-indexes',
title: 'IVFFlat indexes',
description: 'Understanding IVFFlat indexes in pgvector',
sidebar_label: 'IVFFlat indexes',
}
---
id: 'ai-ivf-indexes'
title: 'IVFFlat indexes'
description: 'Understanding IVFFlat indexes in pgvector'
sidebar_label: 'IVFFlat indexes'
---
IVFFlat is a type of vector index for approximate nearest neighbor search. It is a frequently used index type that can improve performance when querying highly-dimensional vectors, like those representing embeddings.
@@ -96,7 +94,3 @@ One important note with IVF indexes is that nearest neighbor search is approxima
## Resources
Read more about indexing on `pgvector`'s [GitHub page](https://github.com/pgvector/pgvector#indexing).
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,13 +1,10 @@
import Layout from '~/layouts/DefaultGuideLayout'
import AuthProviders from '~/components/AuthProviders'
export const meta = {
id: 'auth',
title: 'Auth',
description: 'Use Supabase to Authenticate and Authorize your users.',
subtitle: 'Use Supabase to authenticate and authorize your users.',
video: 'https://www.youtube.com/v/6ow_jW4epf8',
}
---
id: 'auth'
title: 'Auth'
description: 'Use Supabase to Authenticate and Authorize your users.'
subtitle: 'Use Supabase to authenticate and authorize your users.'
video: 'https://www.youtube.com/v/6ow_jW4epf8'
---
There are two parts to every Auth system:
@@ -149,7 +146,3 @@ When users sign up, Supabase assigns them a unique ID. You can reference this ID
type="video/mp4"
/>
</video>
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-captcha',
title: 'Enable Captcha Protection',
description: 'Add Captcha Protection to your Supabase project',
video: 'https://www.youtube.com/v/em1cpOAXknM',
}
---
id: 'auth-captcha'
title: 'Enable Captcha Protection'
description: 'Add Captcha Protection to your Supabase project'
video: 'https://www.youtube.com/v/em1cpOAXknM'
---
Supabase provides you with the option of adding captcha to your sign-in, sign-up, and password reset forms. This keeps your website safe from bots and malicious scripts. Supabase authentication has support for [hCaptcha](https://www.hcaptcha.com/) and [Cloudflare Turnstile](https://www.cloudflare.com/products/turnstile/).
@@ -99,7 +97,9 @@ We will pass it the sitekey we copied from the hCaptcha website as a property al
```jsx
<HCaptcha
sitekey="your-sitekey"
onVerify={(token) => { setCaptchaToken(token) }}
onVerify={(token) => {
setCaptchaToken(token)
}}
/>
```
@@ -176,7 +176,9 @@ We will pass it the sitekey we copied from the Cloudflare website as a property
```jsx
<Turnstile
siteKey="your-sitekey"
onSuccess={(token) => { setCaptchaToken(token) }}
onSuccess={(token) => {
setCaptchaToken(token)
}}
/>
```
@@ -196,7 +198,3 @@ To test locally, you will need to add localhost to the domain whitelist as per t
</Tabs>
Run the application and you should now be provided with a captcha challenge.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-deep-dive-jwts',
title: 'Part One: JWTs',
description: 'Supabase Auth Deep Dive Part 1 - JWTs',
video: 'https://www.youtube.com/v/v3Exg5YpJvE',
}
---
id: 'auth-deep-dive-jwts'
title: 'Part One: JWTs'
description: 'Supabase Auth Deep Dive Part 1 - JWTs'
video: 'https://www.youtube.com/v/v3Exg5YpJvE'
---
### About
@@ -192,7 +190,3 @@ Now that you understand what JWTs are and where they're used in Supabase, you ca
- [Part Four: GoTrue](../../learn/auth-deep-dive/auth-gotrue)
- [Part Five: Google Oauth](../../learn/auth-deep-dive/auth-google-oauth)
- Sign up for Supabase: [supabase.com/dashboard](https://supabase.com/dashboard)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-google-oauth',
title: 'Part Five: Google OAuth',
description: 'Supabase Deep Dive Part 5: Google OAuth Provider',
video: 'https://www.youtube.com/v/_XM9ziOzWk4',
}
---
id: 'auth-google-oauth'
title: 'Part Five: Google OAuth'
description: 'Supabase Deep Dive Part 5: Google OAuth Provider'
video: 'https://www.youtube.com/v/_XM9ziOzWk4'
---
### About
@@ -105,7 +103,3 @@ For any support please get in touch at beta at [supabase.com](https://supabase.c
- Watch [Part Four: GoTrue](../../learn/auth-deep-dive/auth-gotrue)
{/* <!-- - Watch [Part Five: Google Oauth](../../learn/auth-deep-dive/auth-google-oauth) --> */}
- Sign up for Supabase: [supabase.com/dashboard](https://supabase.com/dashboard)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-gotrue',
title: 'Part Four: GoTrue',
description: 'Supabase Deep Dive Part 4: GoTrue Overview',
}
---
id: 'auth-gotrue'
title: 'Part Four: GoTrue'
description: 'Supabase Deep Dive Part 4: GoTrue Overview'
---
### About
@@ -79,7 +77,3 @@ In the next guide we'll be looking at how to setup external OAuth providers: Wat
{/* <!-- Watch [Part Four: GoTrue](../../learn/auth-deep-dive/auth-gotrue) --> */}
- Watch [Part Five: Google OAuth](../../learn/auth-deep-dive/auth-google-oauth)
- Sign up for Supabase: [supabase.com/dashboard](https://supabase.com/dashboard)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-policies',
title: 'Part Three: Policies',
description: 'Supabase Auth Deep Dive Part 3: User Based Access Policies',
video: 'https://www.youtube.com/v/0LvCOlELs5U',
}
---
id: 'auth-policies'
title: 'Part Three: Policies'
description: 'Supabase Auth Deep Dive Part 3: User Based Access Policies'
video: 'https://www.youtube.com/v/0LvCOlELs5U'
---
### About
@@ -186,7 +184,3 @@ You can get as creative as you like with these policies.
- Watch [Part Four: GoTrue](../../learn/auth-deep-dive/auth-gotrue)
- Watch [Part Five: Google Oauth](../../learn/auth-deep-dive/auth-google-oauth)
- Sign up for Supabase: [supabase.com/dashboard](https://supabase.com/dashboard)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-row-level-security',
title: 'Part Two: Row Level Security',
description: 'Supabase Auth Deep Dive Part Two - Row Level Security',
video: 'https://www.youtube.com/v/qY_iQ10IUhs',
}
---
id: 'auth-row-level-security'
title: 'Part Two: Row Level Security'
description: 'Supabase Auth Deep Dive Part Two - Row Level Security'
video: 'https://www.youtube.com/v/qY_iQ10IUhs'
---
### About
@@ -135,7 +133,3 @@ In the next guide we will look at using Policies in combination with User Accoun
- Watch [Part Four: GoTrue](../../learn/auth-deep-dive/auth-gotrue)
- Watch [Part Five: Google Oauth](../../learn/auth-deep-dive/auth-google-oauth)
- Sign up for Supabase: [supabase.com/dashboard](https://supabase.com/dashboard)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
title: 'Email Templates',
description: 'Learn how to manage the email templates in Supabase.',
subtitle: 'Learn how to manage the email templates in Supabase.',
}
---
title: 'Email Templates'
description: 'Learn how to manage the email templates in Supabase.'
subtitle: 'Learn how to manage the email templates in Supabase.'
---
You can customize the email messages used for the authentication flows. You can edit the following email templates:
@@ -18,14 +16,14 @@ You can customize the email messages used for the authentication flows. You can
The templating system provides the following variables for use:
| Name | Description |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `{{ .ConfirmationURL }}` | Contains the confirmation URL. For example, a signup confirmation URL would look like: `https://project-ref.supabase.co/auth/v1/verify?token={{ .TokenHash }}&type=signup&redirect_to=https://example.com/path` . |
| `{{ .Token }}` | Contains a 6-digit One-Time-Password (OTP) that can be used instead of the `{{. ConfirmationURL }}` . |
| `{{ .TokenHash }}` | Contains a hashed version of the `{{ .Token }}`. This is useful for constructing your own email link in the email template. |
| `{{ .SiteURL }}` | Contains your application's Site URL. This can be configured in your project's [authentication settings](/dashboard/project/_/auth/url-configuration). |
| `{{ .RedirectTo }}` | Contains the redirect URL passed when `signUp`, `signInWithOtp`, `signInWithOAuth`, `resetPasswordForEmail` or `inviteUserByEmail` is called. The redirect URL allow list can be configured in your project's [authentication settings](/dashboard/project/_/auth/url-configuration). |
| `{{ .Data }}` | Contains metadata from `auth.users.user_metadata`. Use this to personalize the email message.
| Name | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `{{ .ConfirmationURL }}` | Contains the confirmation URL. For example, a signup confirmation URL would look like: `https://project-ref.supabase.co/auth/v1/verify?token={{ .TokenHash }}&type=signup&redirect_to=https://example.com/path` . |
| `{{ .Token }}` | Contains a 6-digit One-Time-Password (OTP) that can be used instead of the `{{. ConfirmationURL }}` . |
| `{{ .TokenHash }}` | Contains a hashed version of the `{{ .Token }}`. This is useful for constructing your own email link in the email template. |
| `{{ .SiteURL }}` | Contains your application's Site URL. This can be configured in your project's [authentication settings](/dashboard/project/_/auth/url-configuration). |
| `{{ .RedirectTo }}` | Contains the redirect URL passed when `signUp`, `signInWithOtp`, `signInWithOAuth`, `resetPasswordForEmail` or `inviteUserByEmail` is called. The redirect URL allow list can be configured in your project's [authentication settings](/dashboard/project/_/auth/url-configuration). |
| `{{ .Data }}` | Contains metadata from `auth.users.user_metadata`. Use this to personalize the email message. |
## Limitations
@@ -59,19 +57,23 @@ If you intend to use [Server-side rendering](/docs/guides/auth/server-side-rende
You can customize the email link in the email template to redirect the user to a server-side endpoint successfully. For example:
```html
<a href="https://api.example.com/v1/authenticate?token_hash={{ .TokenHash }}&type=invite&redirect_to={{ .RedirectTo }}"
>Accept the invite
</a>
<a
href="https://api.example.com/v1/authenticate?token_hash={{ .TokenHash }}&type=invite&redirect_to={{ .RedirectTo }}"
>Accept the invite
</a>
```
When the user clicks on the link, the request will hit `https://api.example.com/v1/authenticate` and you can grab the `token_hash`, `type` and `redirect_to` query parameters from the URL. Then, you can call the [`verifyOtp`](/docs/reference/javascript/auth-verifyotp) method to get back an authenticated session before redirecting the user back to the client. Since the `verifyOtp` method makes a `POST` request to Supabase Auth to verify the user, the session will be returned in the response body, which can be read by the server. For example:
```js
const { token_hash, type } = Object.fromEntries(new URLSearchParams(window.location.search))
const { data: { session }, error } = await supabase.auth.verifyOtp({ token_hash, type })
const {
data: { session },
error,
} = await supabase.auth.verifyOtp({ token_hash, type })
// subsequently redirect the user back to the client using the redirect_to param
// ...
```
## Customization
@@ -98,7 +100,3 @@ Billy Team</p>
Billy Team</p>
{{ end }}
```
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-email',
title: 'Login With Email',
description: 'Use Supabase to Authenticate and Authorize your users using email.',
}
---
id: 'auth-email'
title: 'Login With Email'
description: 'Use Supabase to Authenticate and Authorize your users using email.'
---
Set up Email Logins for your Supabase application.
@@ -20,7 +18,8 @@ See the [self-hosting docs](/docs/guides/hosting/overview#configuration) for det
</Admonition>
## Sign up the user
## Sign up the user
<Tabs
scrollable
size="small"
@@ -38,8 +37,8 @@ async function signUpNewUser() {
email: 'example@email.com',
password: 'example-password',
options: {
emailRedirectTo: 'https://example.com/welcome'
}
emailRedirectTo: 'https://example.com/welcome',
},
})
}
```
@@ -92,7 +91,7 @@ When your user signs in, call [signInWithPassword()](/docs/reference/javascript/
async function signInWithEmail() {
const { data, error } = await supabase.auth.signInWithPassword({
email: 'example@email.com',
password: 'example-password'
password: 'example-password',
})
}
```
@@ -161,7 +160,6 @@ Future<void> signOut() async {
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
When your user signs out, call [logout()](/docs/reference/kotlin/auth-signout) to remove them from the browser session and any objects from localStorage:
```kotlin
@@ -179,7 +177,3 @@ suspend fun logout() {
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Supabase Flutter Client](https://github.com/supabase/supabase-flutter)
- [Supabase Kotlin Client](https://github.com/supabase-community/supabase-kt)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'index',
title: 'Auth Helpers',
description: 'Server-Side Auth guides and utilities for working with Supabase.',
sidebar_label: 'Overview',
}
---
id: 'index'
title: 'Auth Helpers'
description: 'Server-Side Auth guides and utilities for working with Supabase.'
sidebar_label: 'Overview'
---
<Admonition type="caution">
@@ -52,7 +50,3 @@ The Auth Helpers are in `beta`. They are usable in their current state, but it's
- [Source code](https://github.com/supabase/auth-helpers)
- [Known bugs and issues](https://github.com/supabase/auth-helpers/issues)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-ui',
title: 'Auth UI',
description: 'A prebuilt, customizable React component for authenticating users.',
}
---
id: 'auth-ui'
title: 'Auth UI'
description: 'A prebuilt, customizable React component for authenticating users.'
---
Auth UI is a pre-built React component for authenticating users.
It supports custom themes and extensible styles to match your brand and aesthetic.
@@ -472,7 +470,3 @@ Setting `showLinks` to `false` will hide the following links:
- Already have an account? Sign in
- Send a magic link email
- Forgot your password?
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'flutter-auth-ui',
title: 'Flutter Auth UI',
description: 'Prebuilt, customizable Flutter widgets for authenticating users.',
}
---
id: 'flutter-auth-ui'
title: 'Flutter Auth UI'
description: 'Prebuilt, customizable Flutter widgets for authenticating users.'
---
Flutter Auth UI is a Flutter package containing pre-built widgets for authenticating users.
It is unstyled and can match your brand and aesthetic.
@@ -124,7 +122,3 @@ SupaSocialsAuth(
### Theming
This package uses plain Flutter components allowing you to control the appearance of the components using your own theme.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,16 +1,13 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'nextjs-pages',
title: 'Supabase Auth with Next.js Pages Directory',
description:
'Authentication helpers for Next.js API routes, middleware, and SSR in the Pages Directory.',
sidebar_label: 'Next.js (pages)',
}
---
id: 'nextjs-pages'
title: 'Supabase Auth with Next.js Pages Directory'
description: 'Authentication helpers for Next.js API routes, middleware, and SSR in the Pages Directory.'
sidebar_label: 'Next.js (pages)'
---
<Admonition type="caution">
We generally recommend using the new `@supabase/ssr` package instead of `auth-helpers`. `@supabase/ssr` takes the core concepts of the Auth Helpers package and makes them available to any server language or framework. Check out the [migration doc](/docs/guides/auth/server-side/migrating-to-ssr-from-auth-helpers) to learn more.
The `auth-helpers` package has been replaced with the `@supabase/ssr` package. We recommend setting up Auth for your Next.js app with `@supabase/ssr` instead. See the [Next.js Server-Side Auth guide](/docs/guides/auth/server-side/nextjs?router=pages) to learn how.
</Admonition>
@@ -917,7 +914,3 @@ import { Database } from '../database.types'
const supabaseClient = useSupabaseClient<Database>()
```
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,16 +1,13 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'nextjs',
title: 'Supabase Auth with the Next.js App Router',
description:
'Authentication and Authorization helpers for creating an authenticated Supabase client with the Next.js 13 App Router.',
sidebar_label: 'Next.js',
}
---
id: 'nextjs'
title: 'Supabase Auth with the Next.js App Router'
description: 'Authentication and Authorization helpers for creating an authenticated Supabase client with the Next.js 13 App Router.'
sidebar_label: 'Next.js'
---
<Admonition type="caution">
We generally recommend using the new `@supabase/ssr` package instead of `auth-helpers`. `@supabase/ssr` takes the core concepts of the Auth Helpers package and makes them available to any server framework. Check out the [migration doc](/docs/guides/auth/server-side/migrating-to-ssr-from-auth-helpers) to learn more.
The `auth-helpers` package has been replaced with the `@supabase/ssr` package. We recommend setting up Auth for your Next.js app with `@supabase/ssr` instead. See the [Next.js Server-Side Auth guide](/docs/guides/auth/server-side/nextjs) to learn how.
</Admonition>
@@ -1315,7 +1312,3 @@ export default function() {
```
For an example of creating multiple Supabase clients, check [Singleton section](/docs/guides/auth/auth-helpers/nextjs#singleton) above.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'remix',
title: 'Supabase Auth with Remix',
description: 'Authentication helpers for loaders and actions in Remix.',
sidebar_label: 'Remix',
}
---
id: 'remix'
title: 'Supabase Auth with Remix'
description: 'Authentication helpers for loaders and actions in Remix.'
sidebar_label: 'Remix'
---
<Admonition type="caution">
@@ -784,7 +782,3 @@ supabaseClient.auth.signUp({
},
})
```
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'sveltekit',
title: 'Supabase Auth with SvelteKit',
description: 'Convenience helpers for implementing user authentication in SvelteKit.',
sidebar_label: 'SvelteKit',
}
---
id: 'sveltekit'
title: 'Supabase Auth with SvelteKit'
description: 'Convenience helpers for implementing user authentication in SvelteKit.'
sidebar_label: 'SvelteKit'
---
<Admonition type="caution">
@@ -1968,7 +1966,3 @@ export const GET: RequestHandler = withAuth(async ({ session, getSupabaseClient
- [Auth Helpers Source code](https://github.com/supabase/auth-helpers)
- [SvelteKit example](https://github.com/supabase/auth-helpers/tree/main/examples/sveltekit)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,12 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
import { CodeBlock, Admonition, Accordion } from 'ui'
import Image from 'next/image'
export const meta = {
id: 'auth-hooks',
title: 'Auth Hooks',
description: 'Use Supabase Postgres Functions to customize your authentication flow',
}
---
id: 'auth-hooks'
title: 'Auth Hooks'
description: 'Use Supabase Postgres Functions to customize your authentication flow'
---
Supabase allows you to use Postgres functions to alter the default Supabase Auth flow. Developers can use hooks to add custom behavior that's not supported natively.
@@ -35,12 +31,14 @@ A hook is a [Postgres Function](https://www.postgresql.org/docs/current/sql-crea
This function is invoked by Supabase Auth at its point in the flow by providing an event object. The object returned by the function gives instructions on how Supabase Auth should continue processing.
To access properties of the `event` argument, you can use the [JSON operators and functions](https://www.postgresql.org/docs/current/functions-json.html).
To access properties of the `event` argument, you can use the [JSON operators and functions](https://www.postgresql.org/docs/current/functions-json.html).
There are no restrictions as to what language can be used to write Auth Hooks. If [PL/pgSQL](https://www.postgresql.org/docs/current/plpgsql.html) is too difficult consider using the [plv8](/docs/guides/database/extensions/plv8) extension which lets you use JavaScript to define functions.
<Admonition type="caution">
If you're using the Supabase SQL Editor, there's an issue when using the `?` (*Does the string exist as a top-level key within the JSON value?*) operator. Use a direct connection to the database if you need to use it when defining a function.
If you're using the Supabase SQL Editor, there's an issue when using the `?` (*Does the string
exist as a top-level key within the JSON value?*) operator. Use a direct connection to the
database if you need to use it when defining a function.
</Admonition>
Here is an example hook signature:
@@ -73,6 +71,12 @@ grant execute
to supabase_auth_admin;
```
You also need to grant usage to `supabase_auth_admin`:
```sql
grant usage on schema public to supabase_auth_admin;
```
Also revoke permissions from the `authenticated` and `anon` roles to ensure the function is not accessible by Supabase Serverless APIs.
```sql
@@ -414,6 +418,7 @@ Next, store the API key of our email API provider:
```sql
select vault.create_secret('my_api_key', 'my_api_key_name', 'description_of_my_api_key');
```
Create the hook:
```sql
@@ -567,20 +572,20 @@ as $$
is_admin boolean;
begin
-- Check if the user is marked as admin in the profiles table
select is_admin into is_admin from profiles where user_id = event->>'user_id';
select is_admin into is_admin from profiles where user_id = event->>'user_id'::uuid;
-- Proceed only if the user is an admin
if is_admin then
claims := event->'claims';
-- Check if 'user_metadata' exists in claims
if jsonb_typeof(claims->'user_metadata') is null then
-- If 'user_metadata' does not exist, create an empty object
claims := jsonb_set(claims, '{user_metadata}', '{}');
-- Check if 'app_metadata' exists in claims
if jsonb_typeof(claims->'app_metadata') is null then
-- If 'app_metadata' does not exist, create an empty object
claims := jsonb_set(claims, '{app_metadata}', '{}');
end if;
-- Set a claim of 'admin'
claims := jsonb_set(claims, '{user_metadata, admin}', 'true');
claims := jsonb_set(claims, '{app_metadata, admin}', 'true');
-- Update the 'claims' object in the original event
event := jsonb_set(event, '{claims}', claims);
@@ -691,5 +696,20 @@ revoke execute
</TabPanel>
</Tabs>
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
## Local Development
Unlike the hosted platform, the local development dashboard does not have a user interface for linking a Hook. Instead, edit `config.toml` to link an Auth Hook and run it with your local setup. Modify the `auth.hook.<hook_name>` field and set `uri` to a value of `pg-functions://postgres/<schema>/<function_name>`
For instance, the `config.toml` for a Custom Access Token Hook which uses the function `public.custom_access_token_hook`:
```
...
[auth.hook.custom_access_token]
enabled = true
uri = "pg-functions://postgres/public/custom_access_token_hook"
...
```
Save your Auth Hook as a migration in order to version the Auth Hook and share it with other team members. Run [`supabase migration new`](https://supabase.com/docs/reference/cli/supabase-migration-new) to create a migration.
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-identity-linking',
title: 'Identity Linking',
description: 'Manage the identities associated with your user',
subtitle: 'Manage the identities associated with your user',
}
---
id: 'auth-identity-linking'
title: 'Identity Linking'
description: 'Manage the identities associated with your user'
subtitle: 'Manage the identities associated with your user'
---
## The user identity
@@ -20,9 +18,9 @@ The user identity object contains the following attributes:
| identity_data | `object` | The identity metadata. |
| identity_id | `string` | The unique id of the identity. |
| provider | `string` | The provider name. |
| created_at | `string` | The timestamp that the identity was created. |
| last_sign_in_at | `string` | The timestamp that the identity was last used to sign in. |
| updated_at | `string` | The timestamp that the identity was last updated. |
| created_at | `string` | The timestamp that the identity was created. |
| last_sign_in_at | `string` | The timestamp that the identity was last used to sign in. |
| updated_at | `string` | The timestamp that the identity was last updated. |
## Identity linking strategies
@@ -64,6 +62,7 @@ Supabase Auth allows a user to initiate identity linking with a different email
```kotlin
supabase.auth.linkIdentity(Google)
```
</TabPanel>
</Tabs>
@@ -110,9 +109,6 @@ val googleIdentity = identities.first { it.provider == "google" }
//unlink the google identity from the user
supabase.auth.unlinkIdentity(googleIdentity.identityId!!)
```
</TabPanel>
</Tabs>
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,13 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
import { Mermaid } from 'mdx-mermaid/lib/Mermaid'
export const meta = {
id: 'auth-mfa',
title: 'Multi-Factor Authentication',
description:
'Add an additional layer of security to your apps with Supabase Auth multi-factor authentication.',
}
---
id: 'auth-mfa'
title: 'Multi-Factor Authentication'
description: 'Add an additional layer of security to your apps with Supabase Auth multi-factor authentication.'
---
Multi-factor authentication (MFA), sometimes called two-factor authentication (2FA), adds an additional layer of security to your application by verifying their identity through additional verification steps.
@@ -117,7 +112,6 @@ Enrolling a factor for use with MFA takes three steps:
immediately becomes active for the user account. If not, you should repeat
steps 2 and 3.
#### Example: React
Below is an example that creates a new `EnrollMFA` component that illustrates the important pieces of the MFA enrollment flow.
@@ -223,7 +217,6 @@ An unenrollment flow provides a UI for users to manage and unenroll factors link
When a user unenrolls a factor, call `supabase.auth.mfa.unenroll()` with the ID of the factor. For example, call `supabase.auth.mfa.unenroll({factorId: "d30fd651-184e-4748-a928-0a4b9be1d429"})` to unenroll a factor with ID `d30fd651-184e-4748-a928-0a4b9be1d429`.
#### Example: React
Below is an example that creates a new `UnenrollMFA` component that illustrates the important pieces of the MFA enrollment flow. Note that users can only unenroll a factor after completing the enrollment flow and obtaining an `aal2` JWT claim. Here are some points of note:
@@ -266,26 +259,22 @@ export function UnenrollMFA() {
<>
{error && <div className="error">{error}</div>}
<tbody>
<tr>
<td>Factor ID</td>
<td>Friendly Name</td>
<td>Factor Status</td>
</tr>
{factors.map(factor => (
<tr>
<td>{factor.id}</td>
<td>{factor.friendly_name}</td>
<td>{factor.factor_type}</td>
<td>{factor.status}</td>
<td>Factor ID</td>
<td>Friendly Name</td>
<td>Factor Status</td>
</tr>
))}
{factors.map((factor) => (
<tr>
<td>{factor.id}</td>
<td>{factor.friendly_name}</td>
<td>{factor.factor_type}</td>
<td>{factor.status}</td>
</tr>
))}
</tbody>
<input
type="text"
value={verifyCode}
onChange={(e) => setFactorId(e.target.value.trim())}
/>
<button onClick={()=> supabase.auth.mfa.unenroll({factorId})}>Unenroll</button>
<input type="text" value={verifyCode} onChange={(e) => setFactorId(e.target.value.trim())} />
<button onClick={() => supabase.auth.mfa.unenroll({ factorId })}>Unenroll</button>
</>
)
}
@@ -581,6 +570,10 @@ TOTP is not going to be the only MFA factor Supabase Auth is going to support in
The TOTP QR code encodes a URI with the `otpauth` scheme. It was [initially introduced by Google Authenticator](https://github.com/google/google-authenticator/wiki/Key-Uri-Format) but is now universally accepted by all authenticator apps.
### How long is the TOTP code valid for?
In our TOTP implementation, each generated code remains valid for one interval, which spans 30 seconds. To account for minor time discrepancies, we allow for a one-interval clock skew. This ensures that users can successfully authenticate within this timeframe, even if there are slight variations in system clocks.
### How do I check _when_ a user went through MFA?
Access tokens issued by Supabase Auth contain an `amr` (Authentication Methods Reference) claim. It is an array of objects that indicate what authentication methods the user has used so far.
@@ -629,13 +622,10 @@ Currently recognized authentication methods are:
- `sso/saml` - any Single Sign On (SAML) method.
The following additional claims are available when using PKCE flow:
- `invite` - any sign in via an invitation.
- `magiclink` - any sign in via magic link. Excludes logins resulting from invocation of `signUp`.
- `email/signup` - any login resulting from an email signup.
- `email_change` - any login resulting from a change in email.
More authentication methods will be added over time as we increase the number of authentication methods supported by Supabase.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-smtp',
title: 'Configure a Custom SMTP',
description: 'Moving towards production: Configuring a custom SMTP provider',
}
---
id: 'auth-smtp'
title: 'Configure a Custom SMTP'
description: 'Moving towards production: Configuring a custom SMTP provider'
---
## Auth SMTP
@@ -38,6 +36,3 @@ You can use Supabase Auth with any major SMTP provider of your choosing. Some SM
- [Twilio SendGrid](https://docs.sendgrid.com/for-developers/sending-email/integrating-with-the-smtp-api)
- [AWS SES](https://docs.aws.amazon.com/ses/latest/dg/send-email-smtp.html)
- [Resend](https://resend.com/docs/dashboard/emails/introduction)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-user-management',
title: 'User Management',
description: 'Manage your users with Supabase Auth',
subtitle: 'Manage your users with Supabase Auth',
}
---
id: 'auth-user-management'
title: 'User Management'
description: 'Manage your users with Supabase Auth'
subtitle: 'Manage your users with Supabase Auth'
---
## The user object
@@ -81,7 +79,3 @@ Supabase Auth provides these [configuration options](/dashboard/project/_/settin
- **Confirm Email (Deprecated)**: Users will need to confirm their email address before signing in for the first time.
- Having **Confirm Email** disabled assumes that the user's email does not need to be verified in order to login and implicitly confirms the user's email in the database.
- If you previously relied on this config to autoconfirm a user's email address, you can switch to use **Allow unverified email sign in** instead. This new option allows the user to sign in with an unverified email which you can keep track of through the user object. It provides more versatility if you require your users to verify their email address in the future since you can structure your RLS policies to check the user's `email_verified` field.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'column-level-security',
title: 'Column Level Security',
description: 'Secure your data using Postgres Column Level Security.',
}
---
id: 'column-level-security'
title: 'Column Level Security'
description: 'Secure your data using Postgres Column Level Security.'
---
PostgreSQL's [Row Level Security (RLS)](https://www.postgresql.org/docs/current/ddl-rowsecurity.html) gives you granular control over who can access rows of data. However, it doesn't give you control over which columns they can access within rows. Sometimes you want to restrict access to specific columns in your database. Column Level Privileges allows you to do just that.
@@ -118,30 +116,28 @@ supabase migration new create_posts_table
<StepHikeCompact.Code>
```sql
create table
posts (
id bigint primary key generated always as identity,
user_id text,
title text,
content text,
created_at timestamptz default now()
updated_at timestamptz default now()
);
create table
posts (
id bigint primary key generated always as identity,
user_id text,
title text,
content text,
created_at timestamptz default now()
updated_at timestamptz default now()
);
-- Add row-level security
create a policy "Allow update for owners" on posts for
update
using (auth.uid () = user_id);
-- Add row-level security
create a policy "Allow update for owners" on posts for
update
using (auth.uid () = user_id);
-- Add column-level security
revoke
update
(title) on table public.posts
from
authenticated;
```
-- Add column-level security
revoke
update
(title) on table public.posts
from
authenticated;
```
</StepHikeCompact.Code>
@@ -149,10 +145,6 @@ authenticated;
</StepHikeCompact>
## Considerations when using column-level privileges
- If you turn off a column privilege you won't be able to use that column at all.
- All operations (insert, update, delete) as well as using `select *` will fail.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'redirect-urls',
title: 'Redirect URLs',
description: 'Set up redirect urls with Supabase Auth.',
subtitle: 'Set up redirect urls with Supabase Auth.',
}
---
id: 'redirect-urls'
title: 'Redirect URLs'
description: 'Set up redirect urls with Supabase Auth.'
subtitle: 'Set up redirect urls with Supabase Auth.'
---
## Overview
@@ -83,7 +81,3 @@ const { data, error } = await supabase.auth.signInWithOAuth({
For mobile applications you can use deep linking URIs. For example, for your `SITE_URL` you can specify something like `com.supabase://login-callback/` and for additional redirect URLs something like `com.supabase.staging://login-callback/` if needed.
Read more about deep linking and find code examples for different frameworks [here](/docs/guides/auth/native-mobile-deep-linking).
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -0,0 +1,6 @@
---
title: 'Enterprise Single Sign-On'
description: 'Learn about Single Sign-On support in Supabase Auth for enterprise applications'
---
Supabase Auth supports building enterprise applications that require Single Sign-On (SSO) authentication [with SAML 2.0](/docs/guides/auth/sso/auth-sso-saml).
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'managing-user-data',
title: 'Managing User Data',
description: 'Securing your user data with Row Level Security.',
}
---
id: 'managing-user-data'
title: 'Managing User Data'
description: 'Securing your user data with Row Level Security.'
---
For security purposes, the `auth` schema is not exposed on the auto-generated API.
@@ -241,7 +239,3 @@ create trigger on_auth_user_created
after insert on auth.users
for each row execute procedure public.handle_new_user();
```
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,13 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
import { IconPanel } from 'ui'
import Link from 'next/link'
import { NativeMobileLoginItems } from '~/components/Navigation/NavigationMenu/NavigationMenu.constants'
export const meta = {
title: 'Native Mobile Deep Linking',
description: 'Learn how to handle deep linking for OAuth and email based login.',
tocVideo: '8TZ6O1C8ujE',
}
---
title: 'Native Mobile Deep Linking'
description: 'Learn how to handle deep linking for OAuth and email based login.'
tocVideo: '8TZ6O1C8ujE'
---
In certain auth scenarios you will need to handle linking back into your application to finish the user's sign in.
@@ -115,6 +110,7 @@ In certain auth scenarios you will need to handle linking back into your applica
```
For the best user experience it is recommended to use universal links which require a more elaborate setup. You can find the detailed setup instructions in the [Expo docs](https://docs.expo.dev/guides/deep-linking/).
</TabPanel>
<TabPanel id="flutter" label="Flutter">
\*Currently supabase_flutter supports deep links on Android, iOS, Web, MacOS and Windows.
@@ -266,7 +262,7 @@ In certain auth scenarios you will need to handle linking back into your applica
...
```
At this point, you can register your own scheme.
At this point, you can register your own scheme.
On Windows, URL protocols are setup in the Windows registry.
This package won't do it for you.
@@ -306,6 +302,7 @@ In certain auth scenarios you will need to handle linking back into your applica
</TabPanel>
</Tabs>
</TabPanel>
<TabPanel id="swift" label="Swift">
@@ -344,10 +341,6 @@ In certain auth scenarios you will need to handle linking back into your applica
</dict>
</plist>
```
</TabPanel>
</Tabs>
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,12 +1,7 @@
import Layout from '~/layouts/DefaultGuideLayout'
import { IconPanel } from 'ui'
import Link from 'next/link'
import { NativeMobileLoginItems } from '~/components/Navigation/NavigationMenu/NavigationMenu.constants'
export const meta = {
title: 'Native Mobile Login',
description: 'Logging in with native mobile identity providers like Apple & Google.',
}
---
title: 'Native Mobile Login'
description: 'Logging in with native mobile identity providers like Apple & Google.'
---
Some native mobile operating systems, like iOS and Android, offer a built-in identity provider for convenient user authentication.
@@ -25,21 +20,28 @@ There are several reasons why you might want to add social login to your applica
## Set up a native social login with Supabase Auth
<div className="grid grid-cols-12 gap-10 not-prose py-8">
{NativeMobileLoginItems.map((item) => (
<Link href={`${item.url}`} key={item.name} passHref className="col-span-6 lg:col-span-4 xl:col-span-3">
<IconPanel
title={item.name}
span="col-span-6"
icon={item.icon}
isDarkMode={item.isDarkMode}
hasLightIcon={item.hasLightIcon}
>
{item.description}
</IconPanel>
</Link>
))}
<NavData data="nativeMobileLoginItems">
{(data) =>
data.map((item) => (
<Link
href={`${item.url}`}
key={item.name}
passHref
className="col-span-6 lg:col-span-4 xl:col-span-3"
>
<IconPanel
title={item.name}
span="col-span-6"
icon={item.icon}
isDarkMode={item.isDarkMode}
hasLightIcon={item.hasLightIcon}
>
{item.description}
</IconPanel>
</Link>
))
}
</NavData>
</div>
## Provider tokens
@@ -47,7 +49,3 @@ There are several reasons why you might want to add social login to your applica
Just like with Oauth, you will receive a copy of the provider token in case you need to use it further. For example, you can use the Google provider token to access Google APIs on behalf of your user.
Provider tokens are intentionally not stored in your project's database, however. This is because provider tokens give access to potentially sensitive user data in third-party systems. Different applications have different needs, and one application's OAuth scopes may be significantly more permissive than another. If you do want to use the provider token outside of the browser that completed the OAuth flow, you will have to send it manually to a secure server under your control.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,12 +1,7 @@
import Layout from '~/layouts/DefaultGuideLayout'
import { IconPanel, GlassPanel, IconMail } from 'ui'
import Link from 'next/link'
import { PhoneLoginsItems } from '~/components/Navigation/NavigationMenu/NavigationMenu.constants'
export const meta = {
title: 'Passwordless Login',
description: 'Sign in your users without passwords via magic link email, or one-time passwords sent via email, SMS, or WhatsApp.',
}
---
title: 'Passwordless Login'
description: 'Sign in your users without passwords via magic link email, or one-time passwords sent via email, SMS, or WhatsApp.'
---
Supabase supports various forms of passwordless authentication:
@@ -35,23 +30,22 @@ There are several reasons why you might want to add passwordless login to your a
Supabase supports [Phone Login](/docs/guides/auth/phone-login) (SMS & WhatsApp) with several communications platforms. Follow the guides below to set up a provider with Supabase Auth.
<div className="grid grid-cols-12 gap-10 not-prose py-8">
{PhoneLoginsItems.map((item) => (
<Link href={`${item.url}`} key={item.name} passHref className="col-span-4">
<IconPanel
title={item.name}
span="col-span-6"
icon={item.icon}
isDarkMode={item.isDarkMode}
hasLightIcon={item.hasLightIcon}
>
{item.linkDescription}
</IconPanel>
</Link>
))}
<div className="grid grid-cols-1 md:grid-cols-12 gap-10 not-prose py-8">
<NavData data="phoneLoginsItems">
{(data) =>
data.map((item) => (
<Link href={`${item.url}`} key={item.name} passHref className="col-span-4">
<IconPanel
title={item.name}
span="col-span-6"
icon={item.icon}
isDarkMode={item.isDarkMode}
hasLightIcon={item.hasLightIcon}
>
{item.linkDescription}
</IconPanel>
</Link>
))
}
</NavData>
</div>
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-email-otp',
title: 'Login With Email OTP',
description: 'Use Supabase to authenticate your users using one-time password emails.',
}
---
id: 'auth-email-otp'
title: 'Login With Email OTP'
description: 'Use Supabase to authenticate your users using one-time password emails.'
---
Email one-time passwords (OTP) are a form of passwordless login where users key in a six digit code sent to their email address to log in to their accounts. By default, a user can only request an OTP once every 60 seconds and they expire after 24 hours.
@@ -86,6 +84,15 @@ If the request was successful, you receive a response with `error: null` and a `
## Verify OTP to create session
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
queryGroup="language"
>
<TabPanel id="js" label="JavaScript">
Provide an input field for the user to key in the one-time code. To verify the code and complete the user's sign in, call [verifyOtp()](/docs/reference/javascript/auth-verifyotp) with their email address, the code, and `type: "email"`:
```js
@@ -99,6 +106,18 @@ const {
})
```
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
Provide an input field for the user to key in the one-time code. To verify the code and complete the user's sign in, call [verifyEmailOtp()](/docs/reference/kotlin/auth-verifyotp) with their email address, the code, and `OtpType.Email.EMAIL` as the type:
```kotlin
supabase.auth.verifyEmailOtp(type = OtpType.Email.EMAIL, email = "email", token = "151345")
```
</TabPanel>
</Tabs>
If successful the user will now be logged in and you should receive a valid session like:
```json
@@ -117,7 +136,3 @@ If successful the user will now be logged in and you should receive a valid sess
- [Phone SMS & WhatsApp OTP](/docs/guides/auth/phone-login)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Supabase Flutter Client](https://github.com/supabase/supabase-flutter)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-magic-link',
title: 'Login With Magic Link',
description: 'Use Supabase to authenticate your users using magic links.',
}
---
id: 'auth-magic-link'
title: 'Login With Magic Link'
description: 'Use Supabase to authenticate your users using magic links.'
---
Magic links are a form of passwordless logins where users click on a link sent to their email address to log in to their accounts. Magic links only work with email addresses and are one-time use only. By default, a user can only request a magic link once every 60 seconds and they expire after 24 hours.
@@ -140,7 +138,3 @@ suspend fun logout() {
- [Supabase Account - Free Plan OK](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Supabase Flutter Client](https://github.com/supabase/supabase-flutter)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
import { Accordion } from 'ui'
export const meta = {
id: 'auth-passwords',
title: 'Passwords',
description: 'How to work with passwords in Supabase Auth',
}
---
id: 'auth-passwords'
title: 'Passwords'
description: 'How to work with passwords in Supabase Auth'
---
Using passwords to authenticate users is a tried-and-tested method to give your users access to your application. Supabase Auth provides you with secure configuration options and uses best practices to store and verify your user's passwords.
@@ -52,28 +49,28 @@ Strong passwords are difficult to remember, so Supabase Auth provides you with A
To add a secure password reset flow to your application:
1. Build the **password reset page**:
- Should be publicly accessible and contain a form asking for the user's email address.
- Use the [`supabase.auth.resetPasswordForEmail`](/docs/reference/javascript/auth-resetpasswordforemail) API to request a password reset link for a user's email address.
- Specify the `redirectTo` parameter when calling this API to point to the URL of the **change password page.**
- Should be publicly accessible and contain a form asking for the user's email address.
- Use the [`supabase.auth.resetPasswordForEmail`](/docs/reference/javascript/auth-resetpasswordforemail) API to request a password reset link for a user's email address.
- Specify the `redirectTo` parameter when calling this API to point to the URL of the **change password page.**
2. Build the **password change page**:
- Should be accessible only to authenticated users.
- Add its URL to the allowed [Redirect URLs](/dashboard/project/_/auth/url-configuration) settings.
- Show a form or other prompt asking the user to choose a new password.
- Call the [`supabase.auth.updateUser`](/docs/reference/javascript/auth-updateuser) API to set a new password for the user.
- Should be accessible only to authenticated users.
- Add its URL to the allowed [Redirect URLs](/dashboard/project/_/auth/url-configuration) settings.
- Show a form or other prompt asking the user to choose a new password.
- Call the [`supabase.auth.updateUser`](/docs/reference/javascript/auth-updateuser) API to set a new password for the user.
The email link sent to your users works in the same way as [passwordless authentication](/docs/guides/auth/passwordless-login):
1. After a user visits the **reset password page**, they are sent a reset password link.
- If you use PKCE (default), this link only works on the device or browser where the original reset request was made. Display a message to the user to make sure they don't change devices or browsers.
- If you use the implicit grant flow, the link can be opened on any device.
- If you use PKCE (default), this link only works on the device or browser where the original reset request was made. Display a message to the user to make sure they don't change devices or browsers.
- If you use the implicit grant flow, the link can be opened on any device.
2. A user clicks the link. They are taken to Supabase Auth, which validates the link.
- If the link is valid, it redirects to the **change password page**, which you specified in the `redirectTo` parameter.
- If the **change password page's** URL is not properly registered in the [Redirect URLs](/dashboard/project/_/auth/url-configuration) configuration, the user is taken to the default Site URL page.
- If the link is valid, it redirects to the **change password page**, which you specified in the `redirectTo` parameter.
- If the **change password page's** URL is not properly registered in the [Redirect URLs](/dashboard/project/_/auth/url-configuration) configuration, the user is taken to the default Site URL page.
3. Supabase Auth redirects to the **change password page** including [session](/docs/guides/auth/sessions) information in the URL.
- If you used PKCE (default), the redirect contains the `code` query param.
- If you are not using official Supabase libraries, or have a custom setup, extract the `code` from the URL and call the [`supabase.auth.exchangeCodeForSession`](/docs/reference/javascript/auth-exchangecodeforsession) API.
- Official Supabase libraries handle code the code exchange for you.
- If you used the implicit flow, the redirect contains a URL fragment encoding the user's [session](/docs/guides/auth/sessions).
- If you used PKCE (default), the redirect contains the `code` query param.
- If you are not using official Supabase libraries, or have a custom setup, extract the `code` from the URL and call the [`supabase.auth.exchangeCodeForSession`](/docs/reference/javascript/auth-exchangecodeforsession) API.
- Official Supabase libraries handle code the code exchange for you.
- If you used the implicit flow, the redirect contains a URL fragment encoding the user's [session](/docs/guides/auth/sessions).
### Example: Request a password reset email
@@ -153,7 +150,3 @@ The hash is stored in the `encrypted_password` column of the `auth.users` table.
### How will strengthened password requirements affect current users?
Existing users can still sign in with their current password even if it doesn't meet the new, strengthened password requirements. However, if their password falls short of these updated standards, they will encounter a `WeakPasswordError` during the `signInWithPassword` process, explaining why it's considered weak. This change is also applicable to new users and existing users changing their passwords, ensuring everyone adheres to the enhanced security standards.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -0,0 +1,291 @@
---
title: 'Phone Login'
description: 'Learn about logging in to your platform using SMS one-time passwords.'
---
<Admonition type="tip">
Authenticating users via SMS can become expensive. Adjust your project's rate limits and [configure CAPTCHA](/docs/guides/auth/auth-captcha) to control the bill. Read more about this in the [Production Checklist](/docs/guides/platform/going-into-prod).
</Admonition>
Phone Login is a method of authentication that allows users to log in to a website or application without using a password. Instead of entering a password, the user provides another form of authentication through a one-time code sent via SMS.
## Benefits
There are several reasons why you might want to add phone login to your applications:
- **Improved user experience**: By eliminating the need for users to remember and enter complex passwords, phone login can make it easier and more convenient for users to log in to your application. This can improve the overall user experience and make it more enjoyable for users to interact with your application.
- **Increased security**: Phone login can improve the security of your application by reducing the risk of password-related security breaches, such as password reuse and weak passwords. By using alternative forms of authentication, such as one-time codes or biometric factors, you can make it more difficult for unauthorized users to access your application.
- **Reduced support burden**: Phone login can also help reduce the support burden for your team by eliminating the need to handle password recovery flows or deal with other password-related issues. This can free up your team to focus on other important tasks and improve the efficiency of your operation.
## Set up a provider with Supabase Auth
To use Phone Login, you first need to set up a Phone provider. Supabase supports Phone Login with several communications platforms. Follow the guides below to set up your provider.
<div className="grid grid-cols-1 md:grid-cols-12 gap-10 not-prose py-8">
<NavData data="phoneLoginsItems">
{(data) =>
data.map((item) => (
<Link href={`${item.url}`} key={item.name} passHref className="col-span-4">
<IconPanel
title={item.name}
span="col-span-6"
icon={item.icon}
isDarkMode={item.isDarkMode}
hasLightIcon={item.hasLightIcon}
>
{item.linkDescription}
</IconPanel>
</Link>
))
}
</NavData>
</div>
## Using Phone Login
You can use Phone Login to:
- [Sign up a user with phone number and password](/docs/guides/auth/phone-login#sign-up-a-user-with-phone-number-and-password)
- [Sign in a user with a One Time Password (OTP)](/docs/guides/auth/phone-login#sign-in-a-user-with-otp)
- [Update a user's phone number](/docs/guides/auth/phone-login#update-a-users-phone-number)
Each of these flows sends the user an SMS containing a six-digit PIN, which you must [verify](/docs/guides/auth/phone-login#verify-a-user) to complete the flow.
### Sign up a user with phone number and password
You can use a user's mobile phone number, instead of an email address, when they sign up with a password.
This practice is usually discouraged because phone networks often recycle mobile phone numbers. Anyone receiving a recycled phone number gets access to the original user's account. To mitigate this risk, [implement MFA](/docs/guides/auth/auth-mfa).
To sign up the user, call [`signUp()`](/docs/reference/javascript/auth-signup) with their phone number and password:
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
queryGroup="language"
>
<TabPanel id="js" label="JavaScript">
```js
const { data, error } = await supabase.auth.signUp({
phone: '+13334445555',
password: 'some-password',
})
```
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
```kotlin
supabase.auth.signUpWith(Phone) {
phone = "+13334445555"
password = "some-password"
}
```
</TabPanel>
<TabPanel id="http" label="HTTP">
```bash
curl -X POST 'https://cvwawazfelidkloqmbma.supabase.co/auth/v1/signup' \
-H "apikey: SUPABASE_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone": "+13334445555",
"password": "some-password"
}'
```
</TabPanel>
</Tabs>
The user receives an SMS with a 6-digit pin that you must [verify](/docs/guides/auth/phone-login#verify-a-user) within 60 seconds.
After their phone number is verified, the user can use their phone number and password to sign in with needing to reverify each time:
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
queryGroup="language"
>
<TabPanel id="js" label="JavaScript">
```js
const { user, error } = await supabase.auth.signInWithPassword({
phone: '+13334445555',
password: 'some-password',
})
```
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
```kotlin
supabase.auth.signInWith(Phone) {
phone = "+13334445555"
password = "some-password"
}
```
</TabPanel>
<TabPanel id="http" label="HTTP">
```bash
curl -X POST 'https://cvwawazfelidkloqmbma.supabase.co/auth/v1/token?grant_type=password' \
-H "apikey: SUPABASE_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone": "+13334445555",
"password": "some-password"
}'
```
</TabPanel>
</Tabs>
### Sign in a user with OTP
With OTP, a user can sign in without setting a password on their account. They need to verify their phone number each time they sign in.
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
queryGroup="language"
>
<TabPanel id="js" label="JavaScript">
```js
const { data, error } = await supabase.auth.signInWithOtp({
phone: '+13334445555',
})
```
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
```kotlin
supabase.auth.signInWith(OTP) {
phone = "+13334445555"
}
```
</TabPanel>
<TabPanel id="http" label="HTTP">
```bash
curl -X POST 'https://cvwawazfelidkloqmbma.supabase.co/auth/v1/otp' \
-H "apikey: SUPABASE_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone": "+13334445555"
}'
```
</TabPanel>
</Tabs>
The user receives an SMS with a 6-digit pin that you must [verify](/docs/guides/auth/phone-login#verify-a-user) within 60 seconds.
### Update a user's phone number
To update a user's phone number, the user must be logged in. Call [`updateUser()`](/docs/reference/javascript/auth-updateuser) with their phone number:
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
queryGroup="language"
>
<TabPanel id="js" label="JavaScript">
```js
const { data, error } = await supabase.auth.updateUser({
phone: '123456789',
})
```
</TabPanel>
</Tabs>
The user receives an SMS with a 6-digit pin that you must [verify](/docs/guides/auth/phone-login#verify-a-user) within 60 seconds.
### Verify a user
To verify the one-time password (OTP) sent to the user's phone number, call [`verifyOtp()`](/docs/reference/javascript/auth-verifyotp) with the phone number and OTP:
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
queryGroup="language"
>
<TabPanel id="js" label="JavaScript">
You should present a form to the user so they can input the 6 digit pin, then send it along with the phone number to `verifyOtp`:
```js
const {
data: { session },
error,
} = await supabase.auth.verifyOtp({
phone: '+13334445555',
token: '123456',
type: 'sms',
})
```
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
You should present a form to the user so they can input the 6 digit pin, then send it along with the phone number to `verifyPhoneOtp`:
```kotlin
supabase.auth.verifyPhoneOtp(
type = OtpType.Phone.SMS,
phone = "+13334445555",
token = "123456"
)
```
</TabPanel>
<TabPanel id="http" label="HTTP">
```bash
curl -X POST 'https://<PROJECT_REF>.supabase.co/auth/v1/verify' \
-H "apikey: <SUPABASE_KEY>" \
-H "Content-Type: application/json" \
-d '{
"type": "sms",
"phone": "+13334445555",
"token": "123456"
}'
```
</TabPanel>
</Tabs>
If successful the user will now be logged in and you should receive a valid session like:
```json
{
"access_token": "<ACCESS_TOKEN>",
"token_type": "bearer",
"expires_in": 3600,
"refresh_token": "<REFRESH_TOKEN>"
}
```
The access token can be sent in the Authorization header as a Bearer token for any CRUD operations on supabase-js. See our guide on [Row Level Security](/docs/guides/auth#row-level-security) for more info on restricting access on a user basis.
@@ -0,0 +1,69 @@
---
id: 'auth-messagebird'
title: 'Phone Auth with MessageBird'
description: 'How to set up and use Mobile OTP with MessageBird and Supabase.'
---
<Admonition type="tip">
Authenticating users via SMS can become expensive. Adjust your project's rate limits and [configure CAPTCHA](/docs/guides/auth/auth-captcha) to control the bill. Read more about this in the [Production Checklist](/docs/guides/platform/going-into-prod).
</Admonition>
## Prerequisites
You'll need:
- A MessageBird account (sign up [here](https://dashboard.messagebird.com/en/sign-up))
- A Supabase project (create one [here](https://supabase.com/dashboard))
- A mobile phone capable of receiving SMS
## Set up MessageBird as your SMS provider
Start by logging into your MessageBird account and verify the mobile number you'll be using to test with [here](https://dashboard.messagebird.com/en/getting-started/sms). This is the number that will be receiving the SMS OTPs.
![Verify your own phone number](/docs/img/guides/auth-messagebird/1.png)
![Get your API Keys](/docs/img/guides/auth-messagebird/2.png)
Navigate to the [dashboard settings](https://dashboard.messagebird.com/en/settings/sms) to set the default originator. The messagebird originator is the name or number from which the message is sent. For more information, you can refer to the messagebird article on choosing an originator [here](https://support.messagebird.com/hc/en-us/articles/115002628665-Choosing-an-originator)
![Set the default originator](/docs/img/guides/auth-messagebird/3.png)
You will need the following values to get started:
- Live API Key / Test API Key
- MessageBird originator
Now go to the [Auth > Providers](https://supabase.com/dashboard/project/_/auth/providers) page in the Supabase dashboard and select "Phone" from the Auth Providers list.
You should see an option to enable the Phone provider.
Toggle it on, and copy the 2 values over from the Messagebird dashboard. Click save.
<Admonition>
If you use the Test API Key, the OTP will not be delivered to the mobile number specified but messagebird will log the response in the dashboard. If the Live API Key is used instead, the OTP will be delivered and there will be a deduction in your free credits.
</Admonition>
Plugin MessageBird credentials
Now the backend should be setup, we can proceed to add our client-side code!
#### SMS custom template
The SMS message sent to a phone containing an OTP code can be customized. This is useful if you need to mention a brand name or display a website address.
Go to [Auth > Templates](https://supabase.com/dashboard/project/_/auth/templates) page in the Supabase dashboard.
Use the variable `.Code` in the template to display the code.
## Next steps
To implement Phone Login, see the docs on [using Phone Login](/docs/guides/auth/phone-login#using-phone-login).
## Resources
- [MessageBird Signup](https://dashboard.messagebird.com/en/sign-up)
- [Supabase Dashboard](https://supabase.com/dashboard)
@@ -0,0 +1,151 @@
---
id: 'auth-twilio'
title: 'Phone Auth with Twilio'
description: 'How to set up and use Mobile OTP with Twilio and Supabase.'
video: 'https://www.youtube.com/v/akScoPO01bc'
---
<Admonition type="tip">
Authenticating users via SMS can become expensive. Adjust your project's rate limits and [configure CAPTCHA](/docs/guides/auth/auth-captcha) to control the bill. Read more about this in the [Production Checklist](/docs/guides/platform/going-into-prod).
</Admonition>
## Preqrequisites
You'll need:
- Twilio account ([sign up](https://www.twilio.com/try-twilio))
- Supabase project (create one [here](https://supabase.com/dashboard))
- Mobile phone capable of receiving SMS
SMS Authentication can be done with either Twilio Verify or Twilio Programmable Messaging. [Twilio Verify](https://www.twilio.com/en-us/trusted-activation/verify) is a specialized OTP solution and is recommended for most developers that need over-the-phone authentication. Alternatively you can use [Twilio Programmable Messaging](https://www.twilio.com/docs/messaging) which offers generic SMS sending support.
## Twilio Verify
To set up Twilio Verify, you will need to:
1. Create a new [verification service](https://support.twilio.com/hc/en-us/articles/360033309133-Getting-Started-with-Twilio-Verify-V2) in the Twilio dashboard.
2. [Switch Phone Provider to Twilio Verify](https://supabase.com/dashboard/project/_/auth/providers)
3. Configure the Twilio Verify Service ID field using the Verification Service ID obtained in 1.
When using Twilio Verify, OTPs are generated by Twilio. This means that:
- Unlike other providers, the OTP expiry duration and message content fields are not configurable via the Supabase dashboard. Please head to Twilio Verify to configure these settings.
- The token remains the same during its validity period until the verification is successful. This means if your user make another request within that period, they will receive the same token.
- Twilio Verify has a separate set of rate limits that apply. Visit Twilio's [Rate Limit and Timeouts page](https://www.twilio.com/docs/verify/api/rate-limits-and-timeouts) to find out more.
<Admonition type="caution">
At this time, Twilio Verify is only supported on the `whatsapp` and `sms` channels.
</Admonition>
## Twilio (Programmable Messaging)
In this section, you'll set up Twilio as an SMS provider:
What you'll need:
- A Twilio account (sign up here: https://www.twilio.com/try-twilio)
- A Supabase project (create one here: https://supabase.com/dashboard)
- A mobile phone capable of receiving SMS
<div className="video-container">
<iframe
src="https://www.youtube-nocookie.com/embed/akScoPO01bc"
frameBorder="1"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
### Setting up your Twilio credentials
Start by logging into your Twilio account and starting a new project: https://www.twilio.com/console/projects/create
Give your project a name and verify the mobile number you'll be using to test with. This is the number that will be receiving the SMS OTPs.
![Name your twilio project](/docs/img/guides/auth-twilio/1.png)
![verify your own phone number](/docs/img/guides/auth-twilio/2.png)
Select 'SMS', 'Identity & Verification', and 'With code' as options on the welcome form.
![Form Fields](/docs/img/guides/auth-twilio/3.png)
When you're back on the [Twilio console screen](https://www.twilio.com/console), you need to scroll down and click 'Get a trial phone number' - this is the number that you'll be sending SMSs from.
![Get a trial phone number](/docs/img/guides/auth-twilio/4.png)
![Successful phone number](/docs/img/guides/auth-twilio/5.png)
You should now be able to see all three values you'll need to get started:
- Account SID
- Auth Token
- Sender Phone Number
![All the credentials you'll need](/docs/img/guides/auth-twilio/6.png)
Now go to the [Auth > Providers](https://supabase.com/dashboard/project/_/auth/providers) page in the Supabase dashboard and select "Phone" from the Auth Providers list.
You should see an option to enable the Phone provider.
Toggle it on, and copy the 3 values over from the Twilio dashboard. Click save.
Note: for "Twilio Message Service SID" you can use the Sender Phone Number generated above.
![Plug in Twilio credentials](/docs/img/guides/auth-twilio/8.png)
Now the backend should be setup, we can proceed to add our client-side code!
<Admonition type="note">
The `Twilio Content SID` field is specific to Twilio Programmable Messaging (WhatsApp) senders.
Disregard this field if you are only sending SMS messages. Refer to the section on WhatsApp OTP
Logins for further details.
</Admonition>
#### SMS custom template
The SMS message sent to a phone containing an OTP code can be customized. This is useful if you need to mention a brand name or display a website address.
Go to the [Auth > Providers](https://supabase.com/dashboard/project/_/auth/providers) page in the Supabase dashboard and select "Phone" from the Auth Providers list. Scroll to the very bottom of the "Phone" section to the "SMS Message" input - you can customize the SMS message here.
Use the variable `.Code` in the template to display the OTP code. Here's an example in the SMS template.
![example in the SMS template](/docs/img/guides/auth-twilio/9.png)
## WhatsApp OTP logins
In some cases, you may wish to use WhatsApp as a delivery channel instead. Here are some examples our users have cited:
- You want higher deliverability
- You wish for a secure channel
- Your users mostly use WhatsApp as a messaging platform
Twilio Verify can be used with WhatsApp OTPs with no additional configuration.
Complete the following steps to use WhatsApp OTP with Twilio Programmable Messaging:
1. Go through the [Twilio self sign up guide for WhatsApp](https://www.twilio.com/docs/whatsapp/self-sign-up).
2. Register a Twilio Content Template via the [API](https://www.twilio.com/docs/content/whatsappauthentication) and note the corresponding Twilio Content SID. You can also use the Template Builder on the Twilio dashboard to create a Content Template
![Twilio Content SID Image](/docs/img/guides/auth-twilio/twilio_content_sid.png)
3. Register the Twilio Content SID on the Supabase dashboard under Authentication > Providers > Phone > Twilio Content SID. Ensure you have Twilio selected as your phone provider.
<Admonition>
You may only use one Twilio Content SID at a time. Supabase Auth will use the Content Template over the `SMS Message` field when sending WhatsApp messages. Use Twilio Verify if you need to use more than one message template.
</Admonition>
## Next steps
To implement Phone Login, see the docs on [using Phone Login](/docs/guides/auth/phone-login#using-phone-login).
## Resources
- [Reasons to use Twilio Verify](https://www.twilio.com/blog/9-reasons-to-use-the-verify-api)
- [Twilio Verify Documentation](https://www.twilio.com/docs/verify)
- [Twilio Signup](https://www.twilio.com/try-twilio)
- [Supabase Dashboard](https://supabase.com/dashboard)
- [Supabase Row Level Security](/docs/guides/auth#row-level-security)
@@ -0,0 +1,66 @@
---
id: 'auth-vonage'
title: 'Phone Auth with Vonage'
description: 'How to set up and use Mobile OTP with Vonage and Supabase.'
---
<Admonition type="tip">
Authenticating users via SMS can become expensive. Adjust your project's rate limits and [configure CAPTCHA](/docs/guides/auth/auth-captcha) to control the bill. Read more about this in the [Production Checklist](/docs/guides/platform/going-into-prod).
</Admonition>
## Prerequisites
You'll need:
- A Vonage account (sign up here: https://dashboard.nexmo.com/sign-up)
- A Supabase project (create one here: https://supabase.com/dashboard)
- A mobile phone capable of receiving SMS
## Set up Vonage as an SMS provider
### Getting your Vonage credentials
Start by logging into your Vonage Dashboard at https://dashboard.nexmo.com/
You will see you API Key and API Secret here, which is actually all you need to get started.
In most countries, a phone number is actually optional and you can also use any Alphanumeric Sender ID of up to 11 characters length (8 for India) as a Sender ID (from). This means you do not need a number to test with in most cases.
To find out more about supported countries for Alphanumeric Sender ID, check this overview: https://help.nexmo.com/hc/en-us/articles/115011781468-SMS-Features-Overview-Outbound-only-
Hint: Some countries might need a Sender ID Registration to allow sending with an Alphanumeric Sender ID. You can find this information in the help article as well. If Alpha Sender IDs are not supported, you will need to buy a phone number.
### Getting a phone number (optional)
If you want a phone number to send SMS from, you can buy one from the Vonage Dashboard under Numbers > Buy Numbers (https://dashboard.nexmo.com/buy-numbers).
Select the country you want a number for. You will need a mobile phone number with SMS or SMS+Voice capability. After you have bought the number, you will be able to send SMS from it.
### Configure Supabase
Now go to the [Auth > Providers](https://supabase.com/dashboard/project/_/auth/providers) page in the Supabase dashboard and select "Phone" from the Auth Providers list.
You should see an option to enable the Phone provider.
Toggle it on, and copy the API key, API secret and phone number values over from the Vonage dashboard. Click save.
Now the backend should be setup, we can proceed to add our client-side code!
#### SMS custom template
The SMS message sent to a phone containing an OTP code can be customized. This is useful if you need to mention a brand name or display a website address.
Go to [Auth > Templates](https://supabase.com/dashboard/project/_/auth/templates) page in the Supabase dashboard.
Use the variable `.Code` in the template to display the code.
## Next steps
To implement Phone Login, see the docs on [using Phone Login](/docs/guides/auth/phone-login#using-phone-login).
## Resources
- [Vonage Signup](https://dashboard.nexmo.com/sign-up)
- [Supabase Dashboard](https://supabase.com/dashboard)
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
import StepHikeCompact from '~/components/StepHikeCompact'
export const meta = {
title: 'Use Supabase Auth with Next.js',
subtitle: 'Learn how to configure Supabase Auth for the Next.js App Router.',
breadcrumb: 'Auth Quickstarts',
}
---
title: 'Use Supabase Auth with Next.js'
subtitle: 'Learn how to configure Supabase Auth for the Next.js App Router.'
breadcrumb: 'Auth Quickstarts'
hideToc: true
---
<StepHikeCompact>
@@ -88,7 +86,3 @@ export const meta = {
</StepHikeCompact.Step>
</StepHikeCompact>
export const Page = ({ children }) => <Layout meta={meta} children={children} hideToc={true} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
import StepHikeCompact from '~/components/StepHikeCompact'
export const meta = {
title: 'Use Supabase Auth with React Native',
subtitle: 'Learn how to use Supabase Auth with React Native',
breadcrumb: 'Auth Quickstarts',
}
---
title: 'Use Supabase Auth with React Native'
subtitle: 'Learn how to use Supabase Auth with React Native'
breadcrumb: 'Auth Quickstarts'
hideToc: true
---
<StepHikeCompact>
@@ -268,7 +266,3 @@ export const meta = {
</StepHikeCompact.Step>
</StepHikeCompact>
export const Page = ({ children }) => <Layout meta={meta} children={children} hideToc={true} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
import StepHikeCompact from '~/components/StepHikeCompact'
export const meta = {
title: 'Use Supabase Auth with React',
subtitle: 'Learn how to use Supabase Auth with React.js.',
breadcrumb: 'Auth Quickstarts',
}
---
title: 'Use Supabase Auth with React'
subtitle: 'Learn how to use Supabase Auth with React.js.'
breadcrumb: 'Auth Quickstarts'
hideToc: true
---
<StepHikeCompact>
@@ -132,7 +130,3 @@ export const meta = {
</StepHikeCompact.Step>
</StepHikeCompact>
export const Page = ({ children }) => <Layout meta={meta} children={children} hideToc={true} />
export default Page
@@ -1,12 +1,10 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'row-level-security',
title: 'Row Level Security',
description: 'Using Row Level Security with Supabase Auth.',
subtitle: 'Using Row Level Security with Supabase Auth.',
tocVideo: 'Ow_Uzedfohk',
}
---
id: 'row-level-security'
title: 'Row Level Security'
description: 'Using Row Level Security with Supabase Auth.'
subtitle: 'Using Row Level Security with Supabase Auth.'
tocVideo: 'Ow_Uzedfohk'
---
[Postgres Row Level Security](/docs/guides/database/postgres/row-level-security) (RLS) is a feature of Postgres that allows you to control which users are permitted to perform SELECT/INSERT/UPDATE/DELETE statements on specific rows within tables and views. For example, you could restrict a `blog_post` table such that the current user is only allowed to UPDATE rows where their user id is set in the table's `author_id` column.
@@ -38,10 +36,11 @@ using ( auth.uid() = user_id );
```sql
select *
from todos
where auth.uid() = todos.user_id; -- Policy is implicitly added.
where auth.uid() = todos.user_id;
-- Policy is implicitly added.
```
## Authenticated and anonymous roles
## Authenticated and anonymous roles
Supabase Auth maps every request to one of the roles:
@@ -56,7 +55,7 @@ on profiles for select
to authenticated, anon
using ( true );
-- OR
-- OR
create policy "Public profiles are viewable only by authenticated users"
on profiles for select
@@ -64,7 +63,6 @@ to authenticated
using ( true );
```
## Helper functions
Supabase provides some helper functions that make it easier to write Policies.
@@ -82,8 +80,8 @@ Returns the JWT of the user making the request. Anything that you store in the u
The `auth.jwt()` function is extremely versatile. For example, if you store some team data inside `app_metadata`, you can use it to determine whether a particular user belongs to a team. For example, if this was an array of IDs:
```sql
create policy "User is in team"
```sql
create policy "User is in team"
on my_table
to authenticated
using ( team_id in (select auth.jwt() -> 'app_metadata' -> 'teams'));
@@ -91,7 +89,7 @@ using ( team_id in (select auth.jwt() -> 'app_metadata' -> 'teams'));
<Admonition type="caution">
Keep in mind that a JWT is not always "fresh". In the example above, even if you remove a user from a team and update the `app_metadata` field, that will not be reflected using `auth.jwt()` until the user's JWT is refreshed.
Keep in mind that a JWT is not always "fresh". In the example above, even if you remove a user from a team and update the `app_metadata` field, that will not be reflected using `auth.jwt()` until the user's JWT is refreshed.
Also, if you are using Cookies for Auth, then you must be mindful of the JWT size. Some browsers are limited to 4096 bytes for each cookie, and so the total size of your JWT should be small enough to fit inside this limitation.
@@ -125,7 +123,7 @@ using ( true );
If you want to use another authorization method for your applications that's also fine. Supabase is "just Postgres", so if your application works with Postgres, then it also works with Supabase. If you take this path, don't put your tables in the `public` schema - instead create a new schema for your tables and functions:
```sql
```sql
create schema private;
create table private.employees (
@@ -147,10 +145,9 @@ alter table profiles enable row level security;
This makes the tables inaccessible via the [APIs](/docs/guides/api).
## Usage
Row Level Security is extremely versatile, since it simply uses SQL to express access rules for your data.
Row Level Security is extremely versatile, since it simply uses SQL to express access rules for your data.
### Using functions
@@ -291,7 +288,3 @@ to authenticated using (
- [Testing your database](/docs/guides/database/testing)
- Community repo on testing RLS using [pgTAP and dbdev](https://github.com/usebasejump/supabase-test-helpers/tree/main)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
import { Accordion } from 'ui'
export const meta = {
id: 'server-side-rendering',
title: 'Server-Side Rendering',
description: 'Render pages with user information on the server.',
}
---
id: 'server-side-rendering'
title: 'Server-Side Rendering'
description: 'Render pages with user information on the server.'
---
Single-page apps with server-side rendering (SSR) is a popular way to optimize rendering performance and leverage advanced caching strategies.
@@ -68,7 +65,7 @@ Supabase Auth supports two authentication flows: **Implicit** and **PKCE**. The
<Admonition type="note">
Web browsers do not send the URL fragment to the server they're making the request to. Since you may not be hosting the single-page app on a server under your direct control (such as on GitHub Pages or other freemium hosting providers), we want to prevent hosting services from getting access to your user's authorization credentials by default.
Even if the server is under your direct control, `GET` requests and their full URLs are often logged. This approach also avoids leaking credentials in request or access logs. If you wish to obtain the `access_token` and `refresh_token` on a server, please consider using the PKCE flow.
</Admonition>
@@ -80,7 +77,7 @@ Supabase Auth supports two authentication flows: **Implicit** and **PKCE**. The
header={<span className="text-foreground font-bold">PKCE</span>}
id={`ssr-pkce-flow`}
>
When using the PKCE flow, a redirect URL will be returned with the following structure:
```
https://yourapp.com/...?code=<...>
@@ -90,7 +87,7 @@ Supabase Auth supports two authentication flows: **Implicit** and **PKCE**. The
<Admonition type="note">
For security purposes, the code has a validity of 5 minutes and can only be exchanged for an access token once. You will need to restart the authentication flow from scratch if you wish to obtain a new access token.
</Admonition>
As the flow is run server side, `localStorage` may not be available. You may configure the client library to use a custom storage adapter an alternate backing storage such as cookies by setting the `storage` option to an object with the following methods:
@@ -143,6 +140,7 @@ Supabase Auth supports two authentication flows: **Implicit** and **PKCE**. The
</Accordion.Item>
</div>
</Accordion>
## Bringing it together
@@ -240,7 +238,3 @@ Also be sure you set proper cache control headers. We recommend invalidating cac
### Which authentication flows have PKCE support?
At present, PKCE is supported on the Magic Link, OAuth, Sign Up, and Password Recovery routes. These correspond to the `signInWithOtp`, `signInWithOAuth`, `signUp`, and `resetPasswordForEmail` methods on the Supabase client library. When using PKCE with Phone and Email OTPs, there is no behavior change with respect to the implicit flow - an access token will be returned in the body when a request is successful.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
title: 'Creating a Supabase client for SSR',
description: 'Configure Supabase client to use Cookies',
sidebar_label: 'Creating a client',
}
---
title: 'Creating a Supabase client for SSR'
description: 'Configure Supabase client to use Cookies'
sidebar_label: 'Creating a client'
---
### Install Supabase packages
@@ -98,6 +96,12 @@ dotenv.config()
Creating a Supabase client with the `ssr` package automatically configures it to use cookies. This means your user's session is available throughout the entire Next.js stack - client, server, App Router, Pages Router. It just works!
<Admonition type="note">
For the client utility functions, it is recommended to create them in a separate `/utils/supabase` folder. That way, you can import them as `client` into any component or page that needs access to Supabase. For further information, you can look at the [Next.js Auth Guide](/docs/guides/auth/server-side/nextjs).
</Admonition>
<Tabs
scrollable
size="small"
@@ -236,7 +240,7 @@ export const config = {
* - favicon.ico (favicon file)
* Feel free to modify this pattern to include more paths.
*/
'/((?!_next/static|_next/image|favicon.ico).*)',
'/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
],
}
```
@@ -462,10 +466,10 @@ export const handle: Handle = async ({ event, resolve }) => {
* will replicate previous/standard behaviour (https://kit.svelte.dev/docs/types#public-types-cookies)
*/
set: (key, value, options) => {
event.cookies.set(key, value, { ...options, path: '' })
event.cookies.set(key, value, { ...options, path: '/' })
},
remove: (key, options) => {
event.cookies.delete(key, { ...options, path: '' })
event.cookies.delete(key, { ...options, path: '/' })
},
},
})
@@ -862,7 +866,3 @@ app.post("/hello-world", async function (req, res, next) {
- Implement [Authentication using Email and Password](/docs/guides/auth/server-side/email-based-auth-with-pkce-flow-for-ssr)
- Implement [Authentication using OAuth](/docs/guides/auth/server-side/oauth-with-pkce-flow-for-ssr)
- [Learn more about SSR](/docs/guides/auth/server-side-rendering)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,12 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
title: 'Email Auth with PKCE flow for SSR',
description:
'Learn how to configure email authentication in your server-side rendering (SSR) application to work with the PKCE flow.',
subtitle:
'Learn how to configure email authentication in your server-side rendering (SSR) application to work with the PKCE flow.',
}
---
title: 'Email Auth with PKCE flow for SSR'
description: 'Learn how to configure email authentication in your server-side rendering (SSR) application to work with the PKCE flow.'
subtitle: 'Learn how to configure email authentication in your server-side rendering (SSR) application to work with the PKCE flow.'
---
### Setting up SSR client
@@ -277,7 +273,7 @@ Let's update the URL in our email templates to point to our new confirmation end
<h2>Magic Link</h2>
<p>Follow this link to login:</p>
<p><a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=email">Log In</a></p>
<p><a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=magiclink">Log In</a></p>
```
**Change Email Address template**
@@ -306,7 +302,3 @@ Let's update the URL in our email templates to point to our new confirmation end
>
</p>
```
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
Loaded 100 of 1162 files, more files were not shown because too many files have changed in this diff. Show more