Merge branch 'master' of github.com:supabase/supabase into feat/custom-domains

This commit is contained in:
Joshen Lim committed 2022-11-21 17:19:43 +08:00
commit 4ecc353659
1097 files changed
+61313 -80166

No files matched your search

+2 -1
View File
@@ -52,7 +52,8 @@ jobs:
- uses: docker/build-push-action@v3
with:
push: true
context: '{{defaultContext}}:studio'
context: '{{defaultContext}}'
file: studio/Dockerfile
target: production
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
+1 -1
View File
@@ -79,7 +79,7 @@ Then visit, and edit, any of the following sites:
#### Running sites individually
You can run any of the sites indiviudally by using the scope name. For example:
You can run any of the sites individually by using the scope name. For example:
```sh
npm run dev:www
+2 -2
View File
@@ -6,8 +6,8 @@ interface Props {
title: string
description?: string
to: string
icon?: string | ReactNode
children?: ReactNode
icon?: string | any
children?: any
layout?: 'vertical' | 'horizontal'
}
+35
View File
@@ -0,0 +1,35 @@
import React, { useState } from 'react'
import extensions from '../data/extensions.json'
export default function Extensions() {
const [filter, setFilter] = useState('')
return (
<>
<div className="mb-8 grid">
<label className="text-xs mb-2">Filter extensions</label>
<input
type="text"
className="border text-gray-200"
placeholder="Extension name"
onChange={(e) => setFilter(e.target.value)}
/>
</div>
<div className="grid sm:grid-cols-2 gap-4">
{extensions
.filter((x) => x.name.indexOf(filter) >= 0)
.map((extension) => (
<div className={' my-2 px-2'} key={extension.name}>
<div className="border rounded-sm p-4">
<h3 className="m-0">
<code className="text-sm">{extension.name}</code>
</h3>
<p className=" mt-4">
{extension.comment.charAt(0).toUpperCase() + extension.comment.slice(1)}
</p>
</div>
</div>
))}
</div>
</>
)
}
+38
View File
@@ -0,0 +1,38 @@
import Head from 'next/head'
import { useRouter } from 'next/router'
const Favicons = () => {
const { basePath } = useRouter()
return (
<Head>
<link
rel="apple-touch-icon"
sizes="180x180"
href={`${basePath}/favicon/apple-touch-icon.png`}
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href={`${basePath}/favicon/favicon-32x32.png`}
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href={`${basePath}/favicon/favicon-16x16.png`}
/>
<link rel="manifest" href={`${basePath}/favicon/site.webmanifest`} />
{/* <link rel="mask-icon" href={`${basePath}/favicon/safari-pinned-tab.svg`} color="#333333" /> */}
<link rel="shortcut icon" href={`${basePath}/favicon/favicon.ico`} />
<meta name="msapplication-TileColor" content="#1E1E1E" />
<meta name="msapplication-config" content={`${basePath}/favicon/browserconfig.xml`} />
<meta name="theme-color" content="#1E1E1E" />
<link rel="alternate" type="application/rss+xml" href={`${basePath}/feed.xml`} />
<link rel="icon" type="image/png" href={`${basePath}/favicon/favicon.ico`} />
<link rel="apple-touch-icon" href={`${basePath}/favicon/favicon.ico`} />
</Head>
)
}
export default Favicons
+1 -1
View File
@@ -29,7 +29,7 @@ const examples = [
},
{
name: 'Working with Supabase Storage',
description: 'An example on reading a file from Supbase Storage.',
description: 'An example on reading a file from Supabase Storage.',
href: 'https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/read-storage/index.ts',
},
{
@@ -0,0 +1,74 @@
import { FC } from 'react'
import { removeAnchor } from './CustomHTMLElements/CustomHTMLElements.utils'
const formatSlug = (slug: string) => {
// [Joshen] We will still provide support for headers declared like this:
// ## REST API {#rest-api-overview}
// At least for now, this was a docusaurus thing.
if (slug.includes('#')) return slug.split('#')[1]
return slug
}
const formatTOCHeader = (content: string) => {
let begin = false
const res = []
for (const x of content) {
if (x === '`') {
if (!begin) {
begin = true
res.push(`<code class="text-xs border rounded bg-scale-400 border-scale-500">`)
} else {
begin = false
res.push(`</code>`)
}
} else {
res.push(x)
}
}
return res.join('')
}
interface TOCHeader {
id: number
level: number
text: string
link: string
}
interface Props {
list: TOCHeader[]
}
const GuidesTableOfContents: FC<Props> = ({ list }) => {
return (
<ul className="toc-menu list-none pl-4 text-[0.8rem] grid gap-2 mt-12">
{list.map((item, i) => (
<li key={`${item.level}-${i}`} className={item.level === 3 ? 'ml-4' : ''}>
<a
href={`#${formatSlug(item.link)}`}
className="text-scale-1000 hover:text-brand-900 transition-colors"
dangerouslySetInnerHTML={{ __html: formatTOCHeader(removeAnchor(item.text)) }}
/>
</li>
))}
</ul>
// <ul className="toc-menu list-none pl-4 text-[0.8rem] grid gap-2 mt-12">
// {(toc.json as TOCHeader[])
// .filter((item) => item.lvl !== 1 && item.lvl <= 3)
// .map((item: any, i: number) => {
// return (
// <li key={i} id={item.lvl} style={{ marginLeft: `${(item.lvl - 2) * 1}rem` }}>
// <a
// href={`#${formatSlug(item.slug)}`}
// className="text-scale-1000 hover:text-brand-900 transition-colors"
// dangerouslySetInnerHTML={{ __html: formatTOCHeader(removeAnchor(item.content)) }}
// />
// </li>
// )
// })}
// </ul>
)
}
export default GuidesTableOfContents
+1 -1
View File
@@ -9,7 +9,7 @@ export default function LinkCard({
}: {
title: string
description: string
icon: ReactElement
icon: any
link: string
}) {
return (
+1 -1
View File
@@ -1,5 +1,5 @@
import { ReactElement } from 'react'
export default function LinkCardsWrapper({ children }: { children: ReactElement }) {
export default function LinkCardsWrapper({ children }: { children: any }) {
return <div className="flex w-full flex-col lg:flex-row lg:flex-wrap">{children}</div>
}
@@ -110,15 +110,17 @@ const AlgoliaSearch: FC<Props> = ({}) => {
: 'capitalize'
}`}
>
{item.category}
{item.version ? ` (${item.version})` : ''}:
<>
{item.category}
{item.version ? ` (${item.version})` : ''}:
</>
</p>
)}
<p>
<components.Highlight hit={item} attribute="title" />
</p>
</div>
<p className="aa-ItemContentSubtitle">{item.description}</p>
<p className="aa-ItemContentSubtitle">{item.description as string}</p>
</div>
</a>
)
@@ -1,3 +1,5 @@
// [Terry] Also need to look at getPageType() in ./lib/helpers
// to set a menu to a page
import SupabaseJsV1Nav from 'data/nav/supabase-js/v1'
import SupabaseJsV2Nav from 'data/nav/supabase-js/v2'
import SupabaseDartV0Nav from 'data/nav/supabase-dart/v0'
@@ -5,6 +7,7 @@ import SupabaseDartV1Nav from 'data/nav/supabase-dart/v1'
import SupabaseCLINav from 'data/nav/supabase-cli'
import SupabaseAPINav from 'data/nav/supabase-api'
import AuthServerNav from 'data/nav/auth-server'
import RealtimeServerNav from 'data/nav/realtime-server'
import StorageServerNav from 'data/nav/storage-server'
import { NavMenu, References } from './Navigation.types'
@@ -162,7 +165,7 @@ export const menuItems: NavMenu = {
{ name: 'Database Connections', url: '/guides/database/connecting-to-postgres', items: [] },
{ name: 'Tables and Data', url: '/guides/database/tables', items: [] },
{ name: 'Database Functions', url: '/guides/database/functions', items: [] },
{ name: 'Database Webhooks', url: '/guides/database/database-webhooks', items: [] },
{ name: 'Database Webhooks', url: '/guides/database/webhooks', items: [] },
{ name: 'Full Text Search', url: '/guides/database/full-text-search', items: [] },
{ name: 'Database Testing', url: '/guides/database/testing', items: [] },
{
@@ -243,6 +246,7 @@ export const menuItems: NavMenu = {
label: 'Platform',
items: [
{ name: 'Overview', url: '/guides/hosting/platform', items: [] },
{ name: 'Compute Add-ons', url: '/guides/platform/compute-add-ons', items: [] },
{ name: 'Custom Domains', url: '/guides/platform/custom-domains', items: [] },
{ name: 'Database Usage', url: '/guides/platform/database-usage', items: [] },
{ name: 'Logging', url: '/guides/platform/logs', items: [] },
@@ -315,6 +319,7 @@ export const menuItems: NavMenu = {
{ name: 'Directus', url: '/guides/integrations/directus', items: [] },
{ name: 'Draftbit', url: '/guides/integrations/draftbit', items: [] },
{ name: 'Plasmic', url: '/guides/integrations/plasmic', items: [] },
{ name: 'WeWeb', url: '/guides/integrations/weweb', items: [] },
],
},
],
@@ -335,6 +340,7 @@ export const menuItems: NavMenu = {
label: 'Self-hosting',
items: [
{ name: 'Auth Server', url: '/reference/auth', items: [] },
{ name: 'Realtime Server', url: '/reference/realtime', items: [] },
{ name: 'Storage Server', url: '/reference/storage', items: [] },
],
},
@@ -346,5 +352,6 @@ export const menuItems: NavMenu = {
'reference/cli': SupabaseCLINav,
'reference/api': SupabaseAPINav,
'reference/auth': AuthServerNav,
'reference/realtime': RealtimeServerNav,
'reference/storage': StorageServerNav,
}
+4
View File
@@ -1,3 +1,7 @@
// [Terry]
// Delete this after we've implemented GuidesTableofContents and moved all guides
// and rename GuidesTableofContents to TableOfContents
import { FC } from 'react'
import { getAnchor, removeAnchor } from './CustomHTMLElements/CustomHTMLElements.utils'
+4
View File
@@ -11,11 +11,13 @@ import JwtGenerator from './JwtGenerator'
import Frameworks from './Frameworks'
import AuthProviders from './AuthProviders'
import FunctionsExamples from './FunctionsExamples'
import Extensions from './Extensions'
// Other components
import { Heading } from './CustomHTMLElements'
import QuickstartIntro from './MDX/quickstart_intro.mdx'
import ProjectSetup from './MDX/project_setup.mdx'
import { Mermaid } from 'mdx-mermaid/lib/Mermaid'
const components = {
Admonition,
@@ -28,6 +30,8 @@ const components = {
JwtGenerator,
QuickstartIntro,
ProjectSetup,
Mermaid,
Extensions,
Alert: (props: any) => (
<Alert {...props} className="not-prose">
{props.children}
+1 -1
View File
@@ -86,7 +86,7 @@
},
{
"text": "Open Source",
"url": "/oss"
"url": "/docs/oss"
},
{
"text": "SupaSquad",
+12
View File
@@ -0,0 +1,12 @@
const Nav = [
{
label: 'Realtime',
items: [
{ name: 'Realtime Server', url: '/reference/realtime', items: [] },
{ name: 'Configuration', url: '/reference/realtime/config', items: [] },
{ name: 'Release Notes', url: '/reference/realtime/release-notes', items: [] },
],
},
]
export default Nav
@@ -21,6 +21,8 @@ const nonGeneratedReferencePages = [
'docs/reference/auth/release-notes',
'docs/reference/storage',
'docs/reference/storage/release-notes',
'docs/reference/realtime',
'docs/reference/realtime/release-notes',
]
export default nonGeneratedReferencePages
File renamed without changes.
-6
View File
@@ -1,6 +0,0 @@
---
title: '404'
description: 'Page not found'
---
# Page not found
-52
View File
@@ -1,52 +0,0 @@
---
id: aup
title: Acceptable Use Policy
---
`Last Modified: 15 April 2021`
This Acceptable Use Policy (this “Policy”) describes prohibited uses of the web services offered by Supabase, Inc. and its affiliates (the “Services”) and the website located at https://supabase.com (the “Supabase Site”). The examples described in this Policy are not exhaustive. We may modify this Policy at any time by posting a revised version on the Supabase Site. By using the Services or accessing the Supabase Site, you agree to the latest version of this Policy. If you violate the Policy or authorize or help others to do so, we may suspend or terminate your use of the Services.
## No Illegal, Harmful, or Offensive Use or Content
You may not use, or encourage, promote, facilitate or instruct others to use, the Services or Supabase Site for any illegal, harmful, fraudulent, infringing or offensive use, or to transmit, store, display, distribute or otherwise make available content that is illegal, harmful, fraudulent, infringing or offensive. Prohibited activities or content include:
- **Illegal, Harmful or Fraudulent Activities.** Any activities that are illegal, that violate the rights of others, or that may be harmful to others, our operations or reputation, including disseminating, promoting or facilitating child pornography, offering or disseminating fraudulent goods, services, schemes, or promotions, make-money-fast schemes, ponzi and pyramid schemes, phishing, or pharming.
- **Infringing Content.** Content that infringes or misappropriates the intellectual property or proprietary rights of others.
- **Offensive Content.** Content that is defamatory, obscene, abusive, invasive of privacy, or otherwise objectionable, including content that constitutes child pornography, relates to bestiality, or depicts non-consensual sex acts.
- **Harmful Content.** Content or other computer technology that may damage, interfere with, surreptitiously intercept, or expropriate any system, program, or data, including viruses, Trojan horses, worms, time bombs, or cancelbots.
- **Platform compliance.** Any activities that are deemed unacceptable by the platforms used by the Supabase Site and Services, including [AWS](https://aws.amazon.com/aup/).
## No Security Violations
You may not use the Services to violate the security or integrity of any network, computer or communications system, software application, or network or computing device (each, a “System”). Prohibited activities include:
- **Unauthorized Access.** Accessing or using any System without permission, including attempting to probe, scan, or test the vulnerability of a System or to breach any security or authentication measures used by a System.
- **Interception.** Monitoring of data or traffic on a System without permission.
- **Falsification of Origin.** Forging TCP-IP packet headers, e-mail headers, or any part of a message describing its origin or route. The legitimate use of aliases and anonymous remailers is not prohibited by this provision.
## No Network Abuse
You may not make network connections to any users, hosts, or networks unless you have permission to communicate with them. Prohibited activities include:
- **Monitoring or Crawling.** Monitoring or crawling of a System that impairs or disrupts the System being monitored or crawled.
- **Denial of Service (DoS).** Inundating a target with communications requests so the target either cannot respond to legitimate traffic or responds so slowly that it becomes ineffective.
- **Intentional Interference.** Interfering with the proper functioning of any System, including any deliberate attempt to overload a system by mail bombing, news bombing, broadcast attacks, or flooding techniques.
- **Operation of Certain Network Services.** Operating network services like open proxies, open mail relays, or open recursive domain name servers.
- **Avoiding System Restrictions.** Using manual or electronic means to avoid any use limitations placed on a System, such as access and storage restrictions.
## No E-Mail or Other Message Abuse
You will not distribute, publish, send, or facilitate the sending of unsolicited mass e-mail or other messages, promotions, advertising, or solicitations (like “spam”), including commercial advertising and informational announcements. You will not alter or obscure mail headers or assume a sender’s identity without the sender’s explicit permission. You will not collect replies to messages sent from another internet service provider if those messages violate this Policy or the acceptable use policy of that provider.
## Our Monitoring and Enforcement
We reserve the right, but do not assume the obligation, to investigate any violation of this Policy or misuse of the Services or Supabase Site. We may:
- investigate violations of this Policy or misuse of the Services or Supabase Site; or
- remove, disable access to, or modify any content or resource that violates this Policy or any other agreement we have with you for use of the Services or the Supabase Site.
We may report any activity that we suspect violates any law or regulation to appropriate law enforcement officials, regulators, or other appropriate third parties. Our reporting may include disclosing appropriate customer information. We also may cooperate with appropriate law enforcement agencies, regulators, or other appropriate third parties to help with the investigation and prosecution of illegal conduct by providing network and systems information related to alleged violations of this Policy.
Reporting of Violations of this Policy
If you become aware of any violation of this Policy, you will immediately notify us and provide us with assistance, as requested, to stop or remedy the violation. To report any violation of this Policy, please contact us at support@supabase.io.
-292
View File
@@ -1,292 +0,0 @@
---
id: privacy
title: Privacy Policy
---
`Last modified: 27 March 2021`
Thank you for your interest in Supabase, Inc., ("**_Supabase_**," "**_we_**", "**_our_**" or "**_us_**"). Supabase provides a suite of open source tools, stitched together to build a seamless developer experience. This Privacy Notice explains how information about you, that directly identifies you, or that makes you identifiable ("**_personal information_**") is collected, used and disclosed by Supabase in connection with our website at [supabase.com](https://supabase.com) (the "**_Site_**") and our services offered in connection with the Site (collectively with the Site, the "**_Service_**").
We may also provide you with additional privacy notices or disclosures where the scope of the inquiry, request, or personal information we require falls outside the scope of this Privacy Notice. In that case, the additional Privacy Notice or disclosures will govern how we may process the information you provide at that time. Please note that this Privacy Notice does not cover or apply to our processing of information about our employees or contractors.
This Policy explains how we use your personal information when we act as a data controller. As far as you use our Service as a natural person, we are the controller of your personal information. We are responsible for, and control, the processing of your personal information.
Wherever our customers use our Service to submit, manage, or otherwise use content relating to our customers’ end users ("**_Customer Data_**") during the provision of our Service, we have contractually committed ourselves to only process such information on behalf and under the instruction of the respective customer, who is the data controller. This Privacy Notice does not apply to such processing and we recommend you read the Privacy Notice of the respective customer, if their processing concerns your personal information.
## Region-specific Disclosures
- **California - Your California Privacy Rights:** If you are a California resident, California Civil Code Section 1798.83 permits you to request information regarding the disclosure of personal information to third parties for their direct marketing purposes during the immediately preceding calendar year. Note we do not share your personal information with third parties for their own marketing purposes.
- **Nevada:** Chapter 603A of the Nevada Revised Statutes permits a Nevada resident to opt out of future sales of certain covered information that a website operator has collected or will collect about the resident. Note we do not sell your personal information within the meaning of Chapter 603A. However, if you would still like to submit such a request, please contact us at support@supabase.io.
- **European Economic Area, United Kingdom or Switzerland:** If you are located in the European Economic Area ("**_EEA_**"), United Kingdom or Switzerland, or otherwise engage with Supabase’s European operations, please see the **Privacy Disclosures for the European Economic Area, United Kingdom and Switzerland** for additional European-specific privacy disclosures, including what constitutes your personal information, the lawful bases we rely on to process your personal information, how we use cookies when you access our Sites from the EEA, UK or Switzerland and your rights in respect of your personal information.
**Note for International Visitors:** Personal information may be transferred to, stored and processed in a country other than the one in which it was collected. For example, the Sites are primarily hosted in and provided from the United States. Please note the country to which personal data is transferred may not provide the same level of protection for personal information as the country from which it was transferred.
## 1. Information we collect and our use
We collect personal information in connection with your visits to and use of the Service. This collection includes information that you provide in connection with the Service, information from third parties, and information that is collected automatically such as through the use of cookies and other technologies.
### Information That You Provide
We collect personal information from you. The categories of information we collect can include:
- **_Registration information._** We collect personal and/or business information that you provide when you register for an account at the Site. This information may include your name, email address, GitHub username. We use this information to administer your account, provide you with the relevant services and information, communicate with you regarding your account, the Site and for customer support purposes.
- **_Information collected through the Use of the Service._** After registration, you may create, upload or transmit files, documents, videos, images, data or information as part of your use of the Service (collectively, "**_User Content_**"). User Content and any information contained in the User Content, including personal information you may have included, is stored and collected as part of the Service. You have full control of the information included in the User Content.
- **_Payment information._** If you make a purchase or payment on the Site, such as for a subscription, we collect transactional information provided in connection with your purchase or payment. Please note that we use third party payment processors, including Stripe, to process payments made to us. As such, we do not retain any personally identifiable financial information such as credit card numbers. Rather, all such information is provided directly by you to our third-party processor. The payment processor’s use of your personal information is governed by their privacy notice. To view Stripe’s privacy notice, please visit: <https://stripe.com/privacy>.
- **_Communications._** If you communicate with us through any paper or electronic form, we may collect your name, email address, mailing address, phone number, or any other personal information you choose to provide to us. We use this information to investigate and respond to your inquiries, and to communicate with you, to enhance the services we offer to our users and to manage and grow our organization. If you register for our newsletters or updates, we may communicate with you by email. To unsubscribe from promotional messages, please follow the instructions within our messages and review the **Control Over Your Information** section below. If you become a contributor, we may also collect your GitHub name and feature you on our website.
- **_Inquiries and Feedback._** If you contact us, we will collect the information that you provide us, such as your contact information and the contents of your communication with us.
You are free to choose which personal information you want to provide to us or whether you want to provide us with personal information at all. However, some information, such as your name, address, payment transaction information, and information on your requested Services may be necessary for the performance of our contractual obligations.
### Information from Third Party Sources
We may receive personal information about you from our business partners and service providers and combine this information with other data we collect from you. The third-parties may include website and service operators, payment processors, marketing partners, and shipping providers. The information may include contact information, demographic information, information about your communications and related activities, and information about your orders. We may use this information to administer and facilitate our services, your orders and our marketing activities.
- **_Single Sign-On._** We use single sign-on ("**SSO**") such as GitHub to allow a user to authenticate their account using one set of login information. We will have access to certain information from those third parties in accordance with the authorization procedures determined by those third parties, including, for example, your name, username, email address, language preference, and profile picture. We use this information to operate, maintain, and provide to you the features and functionality of the Service. We may also send you service-related emails or messages (e.g., account verification, purchase confirmation, customer support, changes or updates to features of the Site, technical and security notices).
- **_Social Media._** When you interact with our Site through various social media, such as when you click on the social media icon on the Site, follow us on a social media site, or post a comment to one of our pages, we may receive information from the social network such as your profile information, profile picture, gender, user name, user ID associated with your social media account, age range, language, country, and any other information you permit the social network to share with third parties. The data we receive is dependent upon your privacy settings with the social network. We use this information to operate, maintain, and provide to you the features and functionality of the Service, as well as to communicate directly with you, such as to send you email messages about products and services that may be of interest to you.
- **_Employment Applications._** If you apply for employment, we collect your contact and demographic information, educational and work history, employment interests, information obtained during interviews and any other information you choose to provide. We use the information provided to evaluate your candidacy for employment, to communicate with you during the application process and to facilitate the onboarding process.
- **_Information from Other Sources._** We may obtain information from other sources, including through third-party information providers, our shareholders, customers, or through transactions such as mergers and acquisitions. We may combine this information with other information we collect from or about you. In these cases, our Privacy Notice governs the handling of the combined personal information. We use this information to operate, maintain, and provide to you the features and functionality of the Service, as well as to communicate directly with you, such as to send you email messages about products and services that may be of interest to you.
### Other Uses of Personal Information
In addition to the uses described above, we may collect and use personal information for the following purposes:
- For our business activities, including to operate the Service and to provide you with the features and functionality of the Service;
- To communicate with you and respond to your requests, such as to respond to your questions, contact you about changes to the Service, and communicate about account related matters;
- For marketing and advertising purposes, such as to market to you or offer you with information and updates on our products or services we think that you may be interested in. While we may use your personal information in this manner, please note that we do not use User Content to serve you ads, and we will never share User Content with any third parties for marketing or advertising purposes, unless you have explicitly submitted it to us for that purpose;
- For analytics and research purposes;
- To enforce our **Terms of Service**, to resolve disputes, to carry out our obligations and enforce our rights, and to protect our business interests and the interests and rights of third parties;
- To comply with contractual and legal obligations and requirements;
- To fulfill any other purpose for which you provide personal information; and
- For any other lawful purpose, or other purpose that you consent to.
## 2. How we share personal information
We may share your personal information in the instances described below. For further information on your choices regarding your information, see **Control Over Your Information**.
- We may share your personal information with third-party service providers or business partners who help us deliver or improve our Site or services, or who perform services on our behalf, which are subject to reasonable confidentiality terms, and may include processing payments, providing web hosting services, or providing analytics.
- Third parties as required by law or subpoena or if we reasonably believe that such action is necessary to (a) comply with the law and the reasonable requests of law enforcement; (b) to enforce our **Terms of Service** or other agreements or to protect the security or integrity of the Supabase services, including to prevent harm or financial loss, or in connection with preventing fraud or illegal activity; and/or (c) to exercise or protect the rights, property, or personal safety of Supabase, our Customers, visitors, or others.
- We may share with other companies and brands owned or controlled by Supabase, and other companies owned by or under common ownership as Supabase. These companies will use your personal information in the same way as we can under this Privacy Notice.
- We may transfer any information we collect in the event we sell or transfer all or a portion of our business or assets (including any shares in the company) or any portion or combination of our products, services, businesses and/or assets. Should such a transaction occur (whether a divestiture, merger, acquisition, bankruptcy, dissolution, reorganization, liquidation, or similar transaction or proceeding), we will use reasonable efforts to ensure that any transferred information is treated in a manner consistent with this Privacy Notice.
- We may disclose your information publicly or with another third party with your prior authorization.
- With others in an aggregated or otherwise anonymized form that does not reasonably identify you directly as an individual.
## 3. Control over your information
### Email Communications
From time to time, we may send you emails regarding updates to our Service, products or services, notices about our organization, or information about products/services we offer (or promotional offers from third parties) that we think may be of interest to you. If you wish to unsubscribe from such emails, simply click the "unsubscribe link" provided at the bottom of the email communication. Note that you cannot unsubscribe from certain services-related email communications (e.g., account verification, confirmations of transactions, technical or legal notices).
### Modifying Account Information
If you have an online account with us, you have the ability to modify certain information in your account (e.g., your contact information) through the account options provided on the Site. If there is personal information in your User Content, you can use the features and functionality of the Service to edit or delete the personal information or User Content. Not all personal information is maintained in a format that you can access or change. If you would like to request access to, or correction or deletion of personal information, you may send your request to us at the email provided below. We will review your request and may require you to provide additional information to identify yourself, but we do not promise that we will be able to satisfy your request.
## 4. How We Use Cookies and Other Tracking Technology to Collect Information
We, and our third-party partners, automatically collect certain types of usage information when you visit our Site, read our emails, or otherwise engage with us.  We typically collect this information through a variety of tracking technologies, including cookies, web beacons, embedded scripts, location-identifying technologies, file information, and similar technology (collectively, "**tracking technologies**").
We, and our third-party partners, use tracking technologies to automatically collect usage and device information, such as:
- Information about your device and its software, such as your IP address, browser type, Internet service provider, device type/model/manufacturer, operating system, date and time stamp, and a unique ID that allows us to uniquely identify your browser or your account (including, for example, a persistent device identifier), and other such information.
- When you access our sites from a mobile device, we may collect unique identification numbers associated with your device or our mobile application mobile carrier, device type, model and manufacturer, mobile device operating system brand and model, and depending on your mobile device settings, we may be able to approximate a device’s location by analyzing other information, like an IP address.
- Information about the way you access and use our services, for example, the site from which you came and the site to which you are going when you leave our services, the pages you visit, the links you click, whether you open emails or click the links contained in emails, whether you access the services from multiple devices, and other actions you take on the Sites.
We use the data collected through tracking technologies to:  (a) remember information so that you will not have to re-enter it during your visit or the next time you visit the site; (b) provide custom content and information; (c) identify you across multiple devices; (d) provide and monitor the effectiveness of our services; (e) monitor aggregate metrics such as total number of visitors, traffic, usage, and demographic patterns on our Site; (f) diagnose or fix technology problems; and (g) to provide, plan for, and enhance our services.
**Note we do not engage in online targeted advertising.**
**Cookies and Other Tracking Technologies Opt-Out.** Depending on your browser or mobile device, you may be able to set your browser to delete or notify you of cookies and other tracking technology by actively managing the settings on your browser or mobile device.
If you would prefer not to accept cookies, most browsers will allow you to: (i) change your browser settings to notify you when you receive a cookie, which lets you choose whether or not to accept it; (ii) disable existing cookies; or (iii) set your browser to automatically reject cookies. Please note that doing so may negatively impact your experience using the sites, as some features and services on our sites may not work properly. Depending on your mobile device and operating system, you may not be able to delete or block all cookies. You may also set your e-mail options to prevent the automatic downloading of images that may contain technologies that would allow us to know whether you have accessed our e-mail and performed certain functions with it.
## 5. Data Retention and Security
We will retain your personal information for the length of time needed to fulfill the purposes outlined in this Privacy Notice, unless a longer retention period is required or permitted by law. We store data on servers in the U.S. or any other country in which Supabase or its affiliates, subsidiaries, agents or contractors maintain facilities. If you are located in the European Union or other regions with laws governing data collection and use that may differ from U.S. law, please note that your personal information may be transferred to a country and jurisdiction that does not have the same data protection laws as your jurisdiction. When you register for use with Supabase you have the option of where you store your information and we will not transfer it without providing information to you in advance.
Supabase cares about the security of your information and uses commercially reasonable physical, technical and organizational measures designed to preserve the integrity and security of all information we collect. However, no security system is impenetrable, and we cannot guarantee the security of our systems 100%. In the event that any information under our control is compromised as a result of a breach of security, we will take reasonable steps to investigate the situation and where appropriate, notify those individuals whose information may have been compromised and take other steps, in accordance with any applicable laws and regulations.
## 6. Links to Third-Party Websites and Services
For your convenience, our Site may provide links to third-party websites or services that we do not own or operate. We are not responsible for the practices employed by any websites or services linked to or from the services, including the information or content contained within them. Your browsing and interaction on any other website or service are subject to the applicable third party’s rules and policies, not ours. If you are using a third-party website or service, you do so at your own risk. We encourage you to review the privacy policies of any site or service before providing any personal information.
## 7. Children’s Privacy
Our services are not intended for children under the age of 13. We do not knowingly solicit or collect personal information from children under the age of 13. If we learn that any personal information has been collected inadvertently from a child under 13, we will delete the information as soon as possible. If you believe that we might have collected information from a child under 13, please contact us at privacy@supabase.io.
## 8. Changes to Privacy Notice
We reserve the right to change this Privacy Notice from time to time in our sole discretion. We will notify you about material changes in the way we treat personal data by sending a notice to the primary email address specified in your Supabase account and/or by placing a prominent notice on our Site. It is your responsibility to review this Privacy Notice periodically. When we do change the Privacy Notice, we will also revise the "last modified" date.
## 9. Contact Us
For additional inquiries about this Privacy Notice, please send us an email at privacy@supabase.io.
This Privacy Notice was last modified on 27th March 2021
## Privacy disclosures for the European economic area, United Kingdom, and Switzerland.
While we are primarily based in the United States, Supabase maintains operations in Europe and may direct our services to individuals located in the European Economic Area ("**_EEA_**"), United Kingdom and Switzerland, including through our Site [supabase.com](/) (collectively, our "**_European Services_**"). The following disclosures ("**_Privacy Disclosures_**") apply to our processing of personal data in connection with our European Services.
Supabase, Inc. is the data controller responsible for the processing of personal data in connection with our European Services. This means that we determine and are responsible for how your personal information is used.
**Personal Data:** When we use the term "personal data" in this section, we mean information relating to an identified or identifiable natural person.
### 1. Personal data we collect from you when you use the Supabase European Services, and how we use it.
We collect the categories of personal data that you voluntarily submit directly to us when you use the European Services, as set forth in our Privacy Notice under the section entitled **Information We Collect and Our Use**. The table at **Annex 1** sets out in detail the categories of personal data we collect about you and how we use that information when you use the European Services, as well as the legal basis which we rely on to process the personal information and recipients of that personal information.
### 2. Information we collect about you automatically.
We also automatically collect personal information indirectly about how you access and use the European Services, and information about the device you use to access the European Services. For example, we may collect:
(a) information about the features you use and the pages you view on the European Services;
(b) information about your device (such as your IP address, device identifier, device type, model and manufacturer); and
(c) information about your usage patterns (such as how often you use the Supabase European Services and your language settings).
We use this information to provide you the features and functionality of the European Services, to monitor and improve the European Services and to develop new services.
The table at **Annex 2** sets out further information about the categories of personal information we collect about you automatically and how we use that information. The table also lists the legal basis which we rely on to process the personal information and recipients of that personal information.
We may link or combine the personal information we collect about you and the information we collect automatically.
We may anonymise and aggregate any of the personal information we collect (so that it does not directly identify you). We may use anonymised information for purposes that include testing our IT systems, research, data analysis, improving the Supabase European Services. We may also share such anonymised and aggregated information with others.
### 3. How long will we store your personal information
We will usually store the personal information we collect about you for no longer than necessary for the purposes set out in Annex 1 and Annex 2, in accordance with our legal obligations and legitimate business interests.
The criteria used to determine the period for which personal information about you will be retained varies depending on the legal basis under which we process the personal information:
1. **Legitimate Interests.** Where we are processing personal information based on our legitimate interests, we generally will retain such information for a reasonable period of time based on the particular interest, taking into account the fundamental interests and the rights and freedoms of data subjects.
2. **Consent.** Where we are processing personal information based on your consent, we generally will retain the information until you withdraw your consent, or otherwise for the period of time necessary to fulfill the underlying agreement with you or provide you with the applicable service for which we process that personal information.
3. **Contract.** Where we are processing personal information based on contract, we generally will retain the information for the duration of the contract plus some additional limited period of time that is necessary to comply with law or that represents the statute of limitations for legal claims that could arise from the contractual relationship.
4. **Legal Obligation.** Where we are processing personal information based on a legal obligation, we generally will retain the information for the period of time necessary to fulfill the legal obligation.
5. **Legal Claim.** We may need to apply a "legal hold" that retains information beyond our typical retention period where we face threat of legal claim.  In that case, we will retain the information until the hold is removed, which typically means the claim or threat of claim has been resolved.
In all cases, in addition to the purposes and legal bases, we consider the amount, nature and sensitivity of the personal information, as well as the potential risk of harm from unauthorized use or disclosure of your personal information.
### 4. Recipients of Personal Information
In addition to the recipients listed in Annexes 1 and 2, we may also share your personal information with the following (as required in accordance with the uses set out in Annexes 1 and 2):
1. **Service providers and advisors**: we may share your personal information with third party vendors and other service providers that perform services for us or on our behalf, which may include providing professional services, such as legal and accounting services, mailing, email or chat services, fraud prevention, web hosting, or providing analytic services.
2. **Affiliates**. Other companies owned by or under common ownership as Supabase, including our subsidiaries (i.e., any organization we own or control) and our ultimate holding company (i.e., any organization that owns or controls us) and any subsidiaries it owns. These companies will use your personal information in the same way as we can under these Privacy Disclosures.
3. **Purchasers and third parties in connection with a business transaction**: your personal information may be disclosed to third parties in connection with a transaction, such as a merger, sale of assets or shares, reorganization, financing, change of control or acquisition of all or a portion of our business.
4. **Law enforcement, regulators and other parties for legal reasons**: we may share your personal information with third parties as required by law or if we reasonably believe that such action is necessary to (i) comply with the law and the reasonable requests of law enforcement; (ii) detect and investigate illegal activities and breaches of agreements, including our Terms; and/or (iii) exercise or protect the rights, property, or personal safety of Supabase, its users or others.
### 5. Marketing and Advertising
From time to time we may contact you with information about our services, including sending you marketing messages and asking for your feedback on our services. Most marketing messages we send will be by email. For some marketing messages, we may use personal information we collect about you to help us determine the most relevant marketing information to share with you.
We will only send you marketing messages if you have given us your consent to do so. You can withdraw your consent at a later date by clicking on the unsubscribe link at the bottom of our marketing emails or by updating your preferences via your account on the Site.
### 6. Storing and transferring your personal information
**Security**. We implement appropriate technical and organizational measures to protect your personal information against accidental or unlawful destruction, loss, change or damage. All personal information we collect will be stored by our cloud hosting provider on secure servers. We will never send you unsolicited emails or contact you by phone requesting credit or debit card information or national identification numbers.
**International Transfers of your Personal Information**. The personal information we collect may be transferred to and stored in countries outside of the jurisdiction you are in where we and our third party service providers have operations. If you are located in the EEA, United Kingdom or Switzerland, your personal information may be processed outside of those regions, including in the United States.
In the event of such a transfer, we ensure that: (i) the personal information is transferred to countries recognized as offering an equivalent level of protection; or (ii) the transfer is made pursuant to appropriate safeguards, such as standard data protection clauses adopted by the European Commission.
If you wish to enquire further about these safeguards used, please contact us using the details set out at the end of these Privacy Disclosures.
### 7. Profiling
We may analyze personal data we have collected about you to create a profile of your interests and send product updates. We may also use personal data about you to detect and reduce fraud.
### 8. Your rights in respect of your personal information
In accordance with applicable privacy law, you have the following rights in respect of your personal information that we hold:
1. **Right of access**. You have the right to obtain:
1. confirmation of whether, and where, we are processing your personal information;
2. information about the categories of personal information we are processing, the purposes for which we process your personal information and information as to how we determine applicable retention periods;
3. information about the categories of recipients with whom we may share your personal information; and
4. a copy of the personal information we hold about you.
2. **Right of portability**. You have the right, in certain circumstances, to receive a copy of the personal information you have provided to us in a structured, commonly used, machine-readable format that supports re-use, or to request the transfer of your personal data to another person.
3. **Right to rectification**. You have the right to obtain rectification of any inaccurate or incomplete personal information we hold about you without undue delay.
4. **Right to erasure**. You have the right, in some circumstances, to require us to erase your personal information without undue delay if the continued processing of that personal information is not justified.
5. **Right to restriction**. You have the right, in some circumstances, to require us to limit the purposes for which we process your personal information if the continued processing of the personal information in this way is not justified, such as where the accuracy of the personal information is contested by you.
6. **Right to withdraw consent**. There are certain circumstances where we require your consent to process your personal information. In these instances, and if you have provided consent, you have the right to withdraw your consent. If you withdraw your consent, this will not affect the lawfulness of our use of your personal information before your withdrawal.
**You also have the right to object to any processing based on our legitimate interests where there are grounds relating to your particular situation. There may be compelling reasons for continuing to process your personal information, and we will assess and inform you if that is the case. You can object to marketing activities for any reason.**
You also have the right to lodge a complaint to your local data protection authority. If you are based in the European Union, information about how to contact your local data protection authority is available [here](http://ec.europa.eu/justice/data-protection/bodies/authorities/index_en.htm). If you are based in the UK or Switzerland, your local data protection authorities are the UK Information Commissioner's Office (<https://ico.org.uk/global/contact-us/>) and the Swiss Federal Data Protection and Information Commissioner (<https://www.edoeb.admin.ch/edoeb/en/home/the-fdpic/contact/address.html>).
If you wish to exercise one of these rights, please contact us using the contact details at the end of these Privacy Disclosures.
Due to the confidential nature of data processing we may ask you to provide proof of identity when exercising the above rights. This can be done by providing a scanned copy of a valid identity document or a signed photocopy of a valid identity document.
### 9. Cookies and similar technologies used on our European Services
Our European Services uses cookies and similar technologies such as pixels and Local Storage Objects (LSOs) like HTML5 (together "**_cookies_**") to distinguish you from other users of our European Services. This helps us to provide you with a good experience when you browse our European Services and also allows us to monitor and analyse how you use and interact with our European Services so that we can continue to improve our European Services.
Cookies are pieces of code that allow for personalization of our European Services experience by saving your information such as user ID and other preferences. A cookie is a small data file that we transfer to your computer's hard disk for record-keeping purposes.
We use the following types of cookies:
1. **Strictly necessary cookies**. These are cookies that are required for the operation of our European Services. They include, for example, cookies that enable you to log into secure areas of our European Services.
Please see **Annex 3** for more information about the cookies we use on the European Services.
Most browsers also allow you to change your cookie settings to block certain cookies. Depending on your mobile device and operating system, you may not be able to delete or block all cookies. Please note that if you choose to refuse all cookies you may not be able to use the full functionality of our European Services. These settings will typically be found in the "options" or "preferences" menu of your browser. In order to understand these settings, the following links may be helpful, otherwise you should use the "Help" option in your browser for more details.
- [Cookie settings in Internet Explorer](https://support.microsoft.com/en-gb/help/278835/how-to-delete-cookie-files-in-internet-explorer)
- [Cookie settings in Firefox](http://support.mozilla.org/en-US/kb/cookies)
- [Cookie settings in Chrome](https://support.google.com/chrome/answer/95647?hl=en)
- [Cookies settings in Safari web](https://support.apple.com/en-gb/guide/safari/manage-cookies-and-website-data-sfri11471/mac) and [iOS](https://support.apple.com/en-gb/HT201265).
If you would like to find out more about cookies and other similar technologies, please visit [allaboutcookies.org](http://www.allaboutcookies.org).
Please note that deleting or blocking cookies may not be effective for all types of tracking technologies, such as Local Storage Objects (LSOs) like HTML5.
### 10. Tracking technologies used in our emails
Our emails may contain tracking pixels that identify if and when you have opened an email that we have sent you, how many times you have read it and whether you have clicked on any links in that email. This helps us measure the effectiveness of our marketing email campaigns, make the emails we send to you more relevant to your interests and to understand if you have opened and read any important administrative emails we might send you.
Most popular email clients will allow you to block these pixels by disabling certain external images in emails. You can do this through the settings on your email client – these generally give you the option of choosing whether emails will display "remote images", "remote content" or "images" by default.
Some browsers also give you the option of downloading and installing extensions that block pixels and other tracking technologies.
## Annex 1 – Personal information you provide to us
| **Category of Personal Information** | **How we may use the Personal Information** | **Legal Bases for Processing** | **Recipients of Personal Information** |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Contact information**, such as first name, last name and email address. | We may use this information to set up and authenticate your account on the Service. | The processing is necessary for the performance of a contract with you and to take steps prior to entering into a contract with you, namely our Terms of Service. | <p>We may share this information with the following service providers through the provision of the Service: Segment, Auth0, Stripe, Intercom, Hubspot, Mixpanel, Notion, Slack, Amazon Web Services, and BigQuery (Google Cloud). </p><p> </p> |
| | We may use this information to communicate with you, including sending service-related communications. | The processing is necessary for the performance of a contract with you, namely our Terms of Service. | |
| | We may use this information to deal with enquiries and complaints made by or about you relating to the Service. | The processing is necessary for our legitimate interests, namely administering the Service, and for communicating with you effectively to respond to your queries or complaints. | |
| | We may use this information in connection with providing you with marketing communications in accordance with your preferences. | We will only use your personal information in this way to the extent you have given us consent to do so. | |
| **Your registration / account information** such as your full name, email, and password. | <p>We may use this information to create your account on the Service.</p><p></p><p></p> | <p>The processing is necessary for the performance of a contract with you.</p><p></p><p></p> | <p>We may share this information with the following service providers through the provision of the Service: Segment, Auth0, Stripe, Amazon Web Services.</p><p></p> |
| | <p>We use this information to deal with enquiries and complaints made by or about you relating to the Service.</p><p></p> | The processing is necessary for our legitimate interests, namely for communicating with our members effectively to respond to any queries or complaints. | |
| **Payment transaction information.** When you make a purchase, we may collect information such as your billing address and other information such as date and time of your transaction. | <p>We may use this information to process your orders through the Service.</p><p></p> | The processing is necessary for the performance of a contract. | <p>We may share this information with the following service providers through the provision of the Service: Stripe, and Amazon Web Services.</p><p></p> |
| | We may use this information to verify your identity in connection with the detection and prevention of fraud or financial crime. | The processing is necessary for our and third partiers' legitimate interests, namely the detection and prevention of fraud and financial crime. | |
| **Approximate Location information.** When you visit our Service, we may collect information about your location. This information may be derived from WiFi positioning or your IP address. | We may use information to present the Service to you on your device. | The processing is necessary for performance of a contract with you. | <p>We may share this information with the following service providers through the provision of the Service: Sentry, BigQuery (Google Cloud), and Amazon Web Services.</p><p></p> |
| | We may use this information to localise features of the Service. | The processing is necessary for our legitimate interest, namely localising features of the Service and tailoring the Service so that it is more relevant to our users. | |
| | We may use this information to determine content that may be of interest to you. | The processing is necessary for our legitimate interests, namely tailoring the Service so that it is more relevant to you. | |
| **Chat, comments and opinions.** When you contact us directly, e.g. by email or phone we will record your comments and opinions. | We may use this information to address your questions, issues and concerns. | The processing is necessary for our legitimate interests, namely communicating with you and responding to queries, complaints and concerns. | <p>We may share this information with the following service providers through the provision of the Service: Intercom, Hubspot, Google Gsuite, and Slack.</p><p></p> |
| | We may use this information to improve the Service. | The processing is necessary for our legitimate interests (to develop and improve our service). | |
| **Information received from third parties, such as social networks.** If you interact with us through a social network, we may receive information from the social network such as your name, profile information, and any other information you permit the social network to share with third parties. We use single sign-on ("**_SSO_**") such as GitHub to allow a user to authenticate their account using one set of login information. The data we receive is dependent on your privacy settings with the social network. | We may use this information to reshare content created through the use of the Service | The processing is necessary for our legitimate interests (to develop our service and inform our marketing strategy) | We may share this information with the following service providers through the provision of the Service: Auth0 and Slack. |
| | <p>We may use this information to authenticate you and allow you to access the Service.</p><p></p> | The processing is necessary for the performance of a contract with you. | |
| **Your preferences**, such as preferences set for notifications, marketing communications, how the Service is displayed and the active functionalities on the Service. | <p>We use this information to provide notifications, send news, alerts and marketing communications and provide the Service in accordance with your choices.</p><p></p> | <p>The processing is necessary for our legitimate interest, namely ensuring the user receives the correct marketing and other communications, and that this is displayed in accordance with the user's preferences.</p><p></p> | We may share this information with the following service providers through the provision of the Service: Segment, Intercom and Hubspot. |
| | <p>We use this information to ensure that we comply with our legal obligation to send only those marketing communications to which you have consented. </p><p></p> | The processing is necessary for compliance with a legal obligation to which we are subject. | |
## Annex 2 – Personal information collected automatically
| **Category of personal information** | **How we may use it** | **Legal basis for the processing** | **Recipients of Personal Data** |
| :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Approximate location information.** Other than information you choose to provide to us, we do not collect information about your precise location. Your device’s IP address may however help us determine an approximate location. | We may use information you provide to us about your location to monitor and detect fraud or suspicious activity in relation to your Supabase account. | The processing is necessary for our legitimate interests, namely to protect our business and your account from fraud and other illegal activities. | <p>**Approximate location information:** We may share this information with the following service providers through the provision of the Service: Sentry, BigQuery (Google Cloud), and Amazon Web Services.</p><p></p><p></p><p></p><p>**Information about how you access and use the Service.** We may share this information with the following service providers through the provision of the Service: Segment, Mixpanel, Intercom, Hubspot, BigQuery (Google Cloud), and Amazon Web Services.</p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p>**Log files and information about your device.** We may share this information with the following service providers through the provision of the Service: Segment, Mixpanel, Intercom, Hubspot, Sentry, BigQuery (Google Cloud), and Amazon Web Services.</p> |
| | We may use this information to tailor how the Service is displayed to you (such as the language in which it is provided to you). | The processing is necessary for our legitimate interest, namely tailoring our service so that it is more relevant to our users. | |
| **Information about how you access and use the Service.** For example, how frequently you access the Service, the time you access the Service and how long you use it for, the approximate location that you access the Service from, the site from which you came and the site to which you are going when you leave our website, the website pages you visit, the links you click, whether you open emails or click the links contained in emails, whether you access the Service from multiple devices, and other actions you take on the Service. | <p>We may use information about how you use and connect to the Service to present the Service to you on your device.</p><p></p> | The processing is necessary for our legitimate interests, namely to tailor the Service to the user. | |
| | We may use this information to determine products and services that may be of interest to you for marketing purposes. | The processing is necessary for our legitimate interests, namely to inform our direct marketing. | |
| | <p>We may use this information to monitor and improve the Service and business, resolve issues and to inform the development of new products and services.</p><p></p> | The processing is necessary for our legitimate interests, namely to monitor and resolve issues with the Service and to improve the Service generally. | |
| **Log files and information about your device.** We also collect information about the tablet, smartphone or other electronic device you use to connect to the Service. This information can include details about the, operating systems, browsers and applications connected to the Service through the device and your IP address. | <p>We may use information about how you use and connect to the Service to present the Service to you on your device.</p><p></p> | The processing is necessary for our legitimate interests, namely to tailor the Service to the user. | |
| | <p>We may use this information to monitor and improve the Service and business, resolve issues and to inform the development of new products and services.</p><p></p> | The processing is necessary for our legitimate interests, namely to monitor and resolve issues with the Service and to improve the Service generally. | |
## Annex 3 - Cookies
| **Cookie Name** | **Type of cookie** | **How long does the cookie stay on my device?** | **Purpose of the cookie** |
| :----------------------------- | :----------------- | :---------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| <p>a0:session</p><p></p> | Strictly necessary | 1 Day | Authentication Purposes. |
| <p>a0:state</p><p></p> | Strictly necessary | 2 Hours | Authentication Purposes. |
| <p>youtube-nocookie</p><p></p> | Strictly necessary | Persistent | <p>Privacy-enhanced cookie, essential for embedded videos. [Link.](https://support.google.com/youtube/answer/171780)</p><p><blockquote><small>Privacy Enhanced Mode allows you to embed YouTube videos without using cookies that track viewing behavior. This means no activity is collected to personalize the viewing experience.</small></blockquote></p> |
-172
View File
@@ -1,172 +0,0 @@
---
id: features
title: Features
description: 'Supabase features'
---
This is a non-exhaustive list of features that Supabase provides for every project.
## Database
### Postgres Database
Every project is a full Postgres database. [Docs](/docs/guides/database).
### Database Extensions
Every database comes with a full set of Postgres extensions. [Docs](/docs/guides/database/extensions).
### Database Functions
Create custom database functions which you can call from the browser. [Docs](/docs/guides/database/functions).
### Database Triggers
Attach triggers to your tables to handle database changes. [Docs](/docs/guides/auth/managing-user-data#using-triggers).
### Database Webhooks
Send database changes to any external service using Webhooks. [Link](https://supabase.com/blog/supabase-functions-updates#database-webhooks-alpha).
### Database Backups
Projects are backed up daily with the option to upgrade to Point in Time recovery.
### Search
Build search functionality using Postgres Full Text Search. [Docs](/docs/guides/database/full-text-search).
### Secrets and encryption
Encrypt sensitive data and store secrets using our Postgres extension, Supabase Vault. [Link](https://supabase.com/blog/supabase-vault).
### Database migrations
Develop locally and push your changes to your production database using migrations. [Docs](/docs/guides/cli/local-development#database-migrations)
<br />
## Auth
### Email & Password Logins
Build email logins for your application or website. [Docs](/docs/guides/auth/auth-email).
### Magic Links
Build passwordless logins for your application or website.[Docs](/docs/guides/auth/auth-magic-link).
### Social Logins
Provide social logins - everything from Apple, to GitHub, to Slack. [Docs](/docs/guides/auth/auth-apple).
### Phone Logins
Provide phone logins using a 3rd-party SMS provider. [Docs](/docs/guides/auth/auth-twilio).
### Row Level Security
Control the data each user can access with Postgres Policies. [Docs](/docs/guides/auth/row-level-security).
### Serverside Auth Helpers
Helpers for implementing user authentication in popular frameworks like [Next.js](/docs/guides/auth/auth-helpers/nextjs) and [SvelteKit](/docs/guides/auth/auth-helpers/sveltekit)
### Auth UI Kit
Build login and registration pages with custom themes. [Docs](/docs/guides/auth/auth-helpers/auth-ui).
<br />
## APIs & Client libraries
### Auto-generated REST API
RESTful APIs are autogenerated from your database, without a single line of code. [Docs](/docs/guides/api#rest-api-overview).
### Auto-generated GraphQL API
Fast GraphQL APIs using our custom Postgres GraphQL extension. [Docs](/docs/guides/api#graphql-api-overview).
### Realtime Database changes
Receive your database changes through websockets. [Docs](/docs/guides/realtime/postgres-cdc).
### User Broadcasting
Send messages between connected users through websockets. [Docs](/docs/guides/realtime#broadcast).
### User Presence
Synchronize shared state across your users, including online status and typing indicators. [Docs](/docs/guides/realtime#presence).
### Client libraries
Official client libraries for [JavaScript](/docs/reference/javascript/) and [Dart](/docs/reference/dart).
Unofficial libraries [supported by the community](https://github.com/supabase-community#client-libraries).
<br />
## File Storage
### Large File storage
Supabase Storage makes it simple to store and serve large files. [Docs](/docs/guides/storage).
### Storage CDN
Cache large files using the Supabase CDN. [Docs](/docs/guides/storage-cdn).
<br />
## Edge Functions
### Deno Edge Functions
Globally distributed TypeScript functions to execute custom business logic. [Docs](/docs/guides/functions).
<br />
## Project Managment
### CLI
Use our CLI to develop your project locally and deploy to the Supabase Platform. [Docs](/docs/reference/cli).
### Management API
Manage your projects programmatically. [Docs](/docs/reference/api).
<br />
## Feature Status
Both Postgres and the Supabase Platform are production-ready. Some tools we offer on top of Postgres are still under development.
| Product | Feature | Stage |
| -------------------------- | ---------------------- | ------- |
| Database | Postgres | `GA` |
| Database | Triggers | `GA` |
| Database | Functions | `GA` |
| Database | Extensions | `GA` |
| Database | Full Text Search | `GA` |
| Database | Webhooks | `alpha` |
| Database | Point-in-Time Recovery | `alpha` |
| Database | Vault | `alpha` |
| Studio | | `GA` |
| Realtime | Postgres CDC | `beta` |
| Realtime | Broadcast | `alpha` |
| Realtime | Presence | `alpha` |
| Storage | Backend (S3) | `GA` |
| Storage | API | `beta` |
| Storage | CDN | `beta` |
| Edge Functions | | `beta` |
| Auth | OAuth Providers | `beta` |
| Auth | Passwordless | `beta` |
| Auth | Next.js Auth Helpers | `alpha` |
| Auth | SvelteKit Auth Helpers | `alpha` |
| Auth | Remix Auth Helpers | `alpha` |
| Management API | | `beta` |
| CLI | | `beta` |
| Client Library: JavaScript | | `GA` |
| Client Library: Dart | | `beta` |
@@ -1,27 +0,0 @@
---
id: timeouts
title: Timeouts
description: Timeouts and optimization
---
By default, Supabase limits the maximum statement execution time to _3 seconds_ for users accessing the API using the anon key, and _8 seconds_ for authenticated users. Additionally, all users are subject to a global limit of _2 minutes_. This serves as a backstop against resource exhaustion due to either poorly written queries, or abusive usage.
### Changing the default timeout
The timeout values were picked as a reasonable default for the majority of use-cases, but can be modified using the [`alter role`](https://www.postgresql.org/docs/current/sql-alterrole.html) statement:
```sql
alter role authenticated set statement_timeout = '15s';
```
You can also update the statement timeout for a session:
```sql
set statement_timeout to 60000; -- 1 minute in milliseconds
```
### Statement Optimization
All Supabase projects come with the [`pg_stat_statements`](https://www.postgresql.org/docs/current/pgstatstatements.html) extension installed, which tracks planning and execution statistics for all statements executed against it. These statistics can be used in order to diagnose the performance of your project.
This data can further be used in conjunction with the [`explain`](https://www.postgresql.org/docs/current/using-explain.html) functionality of Postgres to optimize your usage.
-172
View File
@@ -1,172 +0,0 @@
---
id: features
title: Features
description: 'Supabase features'
---
This is a non-exhaustive list of features that Supabase provides for every project.
## Database
### Postgres Database
Every project is a full Postgres database. [Docs](/docs/guides/database).
### Database Extensions
Every database comes with a full set of Postgres extensions. [Docs](/docs/guides/database/extensions).
### Database Functions
Create custom database functions which you can call from the browser. [Docs](/docs/guides/database/functions).
### Database Triggers
Attach triggers to your tables to handle database changes. [Docs](/docs/guides/auth/managing-user-data#using-triggers).
### Database Webhooks
Send database changes to any external service using Webhooks. [Link](https://supabase.com/blog/supabase-functions-updates#database-webhooks-alpha).
### Database Backups
Projects are backed up daily with the option to upgrade to Point in Time recovery.
### Search
Build search functionality using Postgres Full Text Search. [Docs](/docs/guides/database/full-text-search).
### Secrets and encryption
Encrypt sensitive data and store secrets using our Postgres extension, Supabase Vault. [Link](https://supabase.com/blog/supabase-vault).
### Database migrations
Develop locally and push your changes to your production database using migrations. [Docs](/docs/guides/cli/local-development#database-migrations)
<br />
## Auth
### Email & Password Logins
Build email logins for your application or website. [Docs](/docs/guides/auth/auth-email).
### Magic Links
Build passwordless logins for your application or website.[Docs](/docs/guides/auth/auth-magic-link).
### Social Logins
Provide social logins - everything from Apple, to GitHub, to Slack. [Docs](/docs/guides/auth/auth-apple).
### Phone Logins
Provide phone logins using a 3rd-party SMS provider. [Docs](/docs/guides/auth/auth-twilio).
### Row Level Security
Control the data each user can access with Postgres Policies. [Docs](/docs/guides/auth/row-level-security).
### Serverside Auth Helpers
Helpers for implementing user authentication in popular frameworks like [Next.js](/docs/guides/auth/auth-helpers/nextjs) and [SvelteKit](/docs/guides/auth/auth-helpers/sveltekit)
### Auth UI Kit
Build login and registration pages with custom themes. [Docs](/docs/guides/auth/auth-helpers/auth-ui).
<br />
## APIs & Client libraries
### Auto-generated REST API
RESTful APIs are autogenerated from your database, without a single line of code. [Docs](/docs/guides/api#rest-api-overview).
### Auto-generated GraphQL API
Fast GraphQL APIs using our custom Postgres GraphQL extension. [Docs](/docs/guides/api#graphql-api-overview).
### Realtime Database changes
Receive your database changes through websockets. [Docs](/docs/guides/realtime/postgres-cdc).
### User Broadcasting
Send messages between connected users through websockets. [Docs](/docs/guides/realtime#broadcast).
### User Presence
Synchronize shared state across your users, including online status and typing indicators. [Docs](/docs/guides/realtime#presence).
### Client libraries
Official client libraries for [JavaScript](/docs/reference/javascript/) and [Dart](/docs/reference/dart).
Unofficial libraries [supported by the community](https://github.com/supabase-community#client-libraries).
<br />
## File Storage
### Large File storage
Supabase Storage makes it simple to store and serve large files. [Docs](/docs/guides/storage).
### Storage CDN
Cache large files using the Supabase CDN. [Docs](/docs/guides/storage-cdn).
<br />
## Edge Functions
### Deno Edge Functions
Globally distributed TypeScript functions to execute custom business logic. [Docs](/docs/guides/functions).
<br />
## Project Management
### CLI
Use our CLI to develop your project locally and deploy to the Supabase Platform. [Docs](/docs/reference/cli).
### Management API
Manage your projects programmatically. [Docs](/docs/reference/api).
<br />
## Feature Status
Both Postgres and the Supabase Platform are production-ready. Some tools we offer on top of Postgres are still under development.
| Product | Feature | Stage |
| -------------------------- | ---------------------- | ------- |
| Database | Postgres | `GA` |
| Database | Triggers | `GA` |
| Database | Functions | `GA` |
| Database | Extensions | `GA` |
| Database | Full Text Search | `GA` |
| Database | Webhooks | `alpha` |
| Database | Point-in-Time Recovery | `alpha` |
| Database | Vault | `alpha` |
| Studio | | `GA` |
| Realtime | Postgres CDC | `GA` |
| Realtime | Broadcast | `beta` |
| Realtime | Presence | `beta` |
| Storage | Backend (S3) | `GA` |
| Storage | API | `beta` |
| Storage | CDN | `beta` |
| Edge Functions | | `beta` |
| Auth | OAuth Providers | `beta` |
| Auth | Passwordless | `beta` |
| Auth | Next.js Auth Helpers | `alpha` |
| Auth | SvelteKit Auth Helpers | `alpha` |
| Auth | Remix Auth Helpers | `alpha` |
| Management API | | `beta` |
| CLI | | `beta` |
| Client Library: JavaScript | | `GA` |
| Client Library: Dart | | `beta` |
@@ -1,36 +0,0 @@
---
id: cicd-workflow
title: CI / CD Workflow
description: How to deploy Supabase Edge Functions with a CI / CD pipeline.
---
As described in the Supabase CLI [Environments Guide](/docs/guides/cli/managing-environments), you can use the [`setup-cli` GitHub Action](https://github.com/marketplace/actions/supabase-cli-action) to run Supabase CLI commands in your GitHub Actions, for example to deploy a Supabase Edge Function:
```yaml
name: Deploy Function
on:
push:
branches:
- main
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
PROJECT_ID: zdtdtxajzydjqzuktnqx
steps:
- uses: actions/checkout@v3
- uses: supabase/setup-cli@v1
with:
version: 1.0.0
- run: supabase functions deploy your-function-name --project-ref $PROJECT_ID
```
See the [example on GitHub](https://github.com/supabase/supabase/blob/master/examples/edge-functions/.github/workflows/deploy.yaml).
@@ -1,11 +0,0 @@
---
id: examples
title: Examples
description: Useful Supabase Edge Functions Examples
---
You can find a list of useful [Edge Function Examples](https://github.com/supabase/supabase/tree/master/examples/edge-functions) in our GitHub repository.
<div className="container" style={{ padding: 0 }}>
<FunctionsExamples />
</div>
@@ -1,582 +0,0 @@
---
id: authsignal
title: 'Authsignal'
description: 'Add an MFA step after sign-in. Use Supabase Auth to sign in with email and password and Authsignal to initiate an Authenticator App challenge.'
---
This guide shows how to integrate [Authsignal](https://www.authsignal.com/) with [Next.js](https://nextjs.org/) and [Supabase](https://supabase.com/) in order to add an MFA step after sign-in.
The user flow is as follows:
1. The user enters their email and password to sign in
2. If the user has set up MFA, they're prompted to complete an MFA challenge (via Authenticator App) in order to complete sign-in
3. If the user has not set up MFA, they're signed in immediately and will see a button to set up MFA
The approach uses a temporary encrypted cookie to ensure that the Supabase auth cookies (`access_token` and `refresh_token`) are only set if the MFA challenge was successful. Session data is encrypted using [@hapi/iron](https://hapi.dev/family/iron).
The full code version of this example can be found [here](https://github.com/authsignal/supabase-example).
A live demo can be found [here](https://authsignal-supabase-example.vercel.app).
## How it works
1. A sign-in form posts email and password to the Next.js API route `/api/sign-in`
2. The `signIn` API route calls the Supabase client's `signInWithEmail` method and gets back a session object
3. The `signIn` API route then calls the Authsignal client's `track` method to determine if an MFA challenge is required
4. If a challenge is required, the `signIn` API route saves the session object in a temporary encrypted cookie and redirects to Authsignal
5. Once the challenge is completed, Authsignal redirects back to `/api/callback` which retrieves the session and sets the Supabase auth cookies
6. The `callback` API route then redirects to the index page which is protected with Supabase's `withPageAuth` wrapper around `getServerSideProps`
## Step 1: Configuring an Authsignal tenant
Go to the [Authsignal Portal](https://portal.authsignal.com) and create a new project and tenant.
You will also need to [enable at least one authenticator for your tenant](https://portal.authsignal.com/organisations/tenants/authenticators) - for example Authenticator Apps.
Finally, to configure the sign-in action to always challenge, go [here](https://portal.authsignal.com/actions/signIn/rules) and set the default action outcome to `CHALLENGE` and click save.
![Authsignal settings](https://raw.githubusercontent.com/authsignal/supabase-example/main/authsignal-settings.png)
## Step 2: Creating a Supabase project
From your [Supabase dashboard](https://app.supabase.com/), click `New project`.
Enter a `Name` for your Supabase project and enter or generate a secure `Database Password`, then click `Create new project`.
Once your project is created go to `Authentication -> Settings -> Auth Providers` and ensure `Enable Email provider` is checked and that `Confirm Email` is unchecked.
![Supabase settings](https://raw.githubusercontent.com/authsignal/supabase-example/main/supabase-settings.png)
## Step 3: Building a Next.js app
Create a new Next.js project:
```bash
npx create-next-app --typescript supabase-authsignal-example
cd supabase-authsignal-example
```
Create a `.env.local` file and enter the following values:
```
NEXT_PUBLIC_SUPABASE_URL=get-from-supabase-dashboard
NEXT_PUBLIC_SUPABASE_ANON_KEY=get-from-supabase-dashboard
AUTHSIGNAL_SECRET=get-from-authsignal-dashboard
TEMP_TOKEN_SECRET=this-is-a-secret-value-with-at-least-32-characters
```
Supabase values can be found under `Settings > API` for your project.
Authsignal values can be found under `Settings > API Keys` for your tenant.
`TEMP_TOKEN_SECRET` is used to encrypt the temporary cookie. Set it to a random 32 character length string.
Restart your Next.js development server to read in the new values from `.env.local`.
```bash
npm run dev
```
## Step 4: Installing dependencies
Install the Supabase client and Auth helpers for Next.js:
```
npm install @supabase/supabase-js @supabase/auth-helpers-nextjs
```
Install the Authsignal Node.js client:
```
npm install @authsignal/node
```
Finally install 2 packages to help encrypt and serialize session data in cookies:
```
npm install @hapi/iron cookie
npm install --save-dev @types/cookie
```
## Step 5: Initializing the Authsignal client
Add the following code to `/lib/authsignal.ts`:
```
import { Authsignal } from "@authsignal/node";
const secret = process.env.AUTHSIGNAL_SECRET;
if (!secret) {
throw new Error("AUTHSIGNAL_SECRET is undefined");
}
const redirectUrl = "http://localhost:3000/api/callback";
export const authsignal = new Authsignal({ secret, redirectUrl });
```
The `redirectUrl` here is a Next.js API route which Authsignal will redirect back to after an MFA challenge. We'll implement this below.
## Step 6: Managing session data in cookies
Next we will add some helper functions for managing cookies:
- `setTempCookie` encrypts and serializes the Supabase session data and sets it in a temporary cookie
- `getSessionFromTempCookie` decrypts and parses this session data back from the cookie
- `setAuthCookie` sets the Supabase auth cookies (`access_token` and `refresh_token`) and clears the temporary cookie
Add the following code to `/lib/cookies.ts`:
```
import Iron from "@hapi/iron";
import { Session } from "@supabase/supabase-js";
import { parse, serialize } from "cookie";
import { NextApiRequest, NextApiResponse } from "next";
export async function setTempCookie(session: Session, res: NextApiResponse) {
const token = await Iron.seal(session, TEMP_TOKEN_SECRET, Iron.defaults);
const cookie = serialize(TEMP_COOKIE, token, {
maxAge: session.expires_in,
httpOnly: true,
secure: process.env.NODE_ENV === "production",
path: "/",
sameSite: "lax",
});
res.setHeader("Set-Cookie", cookie);
}
export async function getSessionFromTempCookie(
req: NextApiRequest
): Promise<Session | undefined> {
const cookie = req.headers.cookie as string;
const cookies = parse(cookie ?? "");
const tempCookie = cookies[TEMP_COOKIE];
if (!tempCookie) {
return undefined;
}
const session = await Iron.unseal(
tempCookie,
TEMP_TOKEN_SECRET,
Iron.defaults
);
return session;
}
export function setAuthCookie(session: Session, res: NextApiResponse) {
const { access_token, refresh_token, expires_in } = session;
const authCookies = [
{ name: ACCESS_TOKEN_COOKIE, value: access_token },
refresh_token
? { name: REFRESH_TOKEN_COOKIE, value: refresh_token }
: undefined,
]
.filter(isDefined)
.map(({ name, value }) =>
serialize(name, value, {
maxAge: expires_in,
httpOnly: true,
secure: process.env.NODE_ENV === "production",
path: "/",
sameSite: "lax",
})
);
// Also clear the temp cookie
const updatedCookies = [
...authCookies,
serialize(TEMP_COOKIE, "", { maxAge: -1, path: "/" }),
];
res.setHeader("Set-Cookie", updatedCookies);
}
const isDefined = <T>(value: T | undefined): value is T => !!value;
const TEMP_TOKEN_SECRET = process.env.TEMP_TOKEN_SECRET!;
const TEMP_COOKIE = "as-mfa-cookie";
const ACCESS_TOKEN_COOKIE = "sb-access-token";
const REFRESH_TOKEN_COOKIE = "sb-refresh-token";
```
## Step 7: Building the UI
We will add some form components for signing in and signing up as well as a basic home page.
Add the following code to `/pages/sign-up.tsx`:
```
import Link from "next/link";
import { useRouter } from "next/router";
export default function SignUpPage() {
const router = useRouter();
return (
<main>
<form
onSubmit={async (e) => {
e.preventDefault();
const target = e.target as typeof e.target & {
email: { value: string };
password: { value: string };
};
const email = target.email.value;
const password = target.password.value;
await fetch("/api/sign-up", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
}).then((res) => res.json());
router.push("/");
}}
>
<label htmlFor="email">Email</label>
<input id="email" type="email" name="email" required />
<label htmlFor="password">Password</label>
<input id="password" type="password" name="password" required />
<button type="submit">Sign up</button>
</form>
<div>
{"Already have an account? "}
<Link href="sign-in">
<a>Sign in</a>
</Link>
</div>
</main>
);
}
```
Then add the following code to `/pages/sign-in.tsx`:
```
import Link from "next/link";
import { useRouter } from "next/router";
export default function SignInPage() {
const router = useRouter();
return (
<main>
<form
onSubmit={async (e) => {
e.preventDefault();
const target = e.target as typeof e.target & {
email: { value: string };
password: { value: string };
};
const email = target.email.value;
const password = target.password.value;
const { state, mfaUrl } = await fetch("/api/sign-in", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
}).then((res) => res.json());
if (state === "CHALLENGE_REQUIRED") {
window.location.href = mfaUrl;
} else {
router.push("/");
}
}}
>
<label htmlFor="email">Email</label>
<input id="email" type="email" name="email" required />
<label htmlFor="password">Password</label>
<input id="password" type="password" name="password" required />
<button type="submit">Sign in</button>
</form>
<div>
{"Don't have an account? "}
<Link href="sign-up">
<a>Sign up</a>
</Link>
</div>
</main>
);
}
```
Now we will use Supabase's `withPageAuth` wrapper around `getServerSideProps` to make the home page require authentication via SSR. Replace the existing code in `/pages/index.tsx` with the following:
```
import { getUser, User, withPageAuth } from "@supabase/auth-helpers-nextjs";
import { GetServerSideProps } from "next";
import { useRouter } from "next/router";
import { authsignal } from "../lib/authsignal";
interface Props {
user: User;
isEnrolled: boolean;
}
export const getServerSideProps: GetServerSideProps<Props> = withPageAuth({
redirectTo: "/sign-in",
async getServerSideProps(ctx) {
const { user } = await getUser(ctx);
const { isEnrolled } = await authsignal.getUser({userId: user.id});
return {
props: { user, isEnrolled },
};
},
});
export default function HomePage({ user, isEnrolled }: Props) {
const router = useRouter();
return (
<main>
<section>
<div> Signed in as: {user?.email}</div>
<button
onClick={async (e) => {
e.preventDefault();
const { mfaUrl } = await fetch("/api/mfa", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ isEnrolled }),
}).then((res) => res.json());
window.location.href = mfaUrl;
}}
>
{isEnrolled ? "Manage MFA settings" : "Set up MFA"}
</button>
<button onClick={() => router.push("/api/sign-out")}>Sign out</button>
</section>
</main>
);
}
```
Optional: To make things look a bit nicer, you can add the following to `/styles/globals.css`:
```
main {
min-height: 100vh;
display: flex;
flex: 1;
flex-direction: column;
justify-content: center;
align-items: center;
}
section,
form {
display: flex;
flex-direction: column;
min-width: 300px;
}
button {
cursor: pointer;
font-weight: 500;
line-height: 1;
border-radius: 6px;
border: none;
background-color: #24b47e;
color: #fff;
padding: 0 15px;
height: 40px;
margin: 10px 0;
transition: background-color 0.15s, color 0.15s;
}
input {
outline: none;
font-family: inherit;
font-weight: 400;
background-color: #fff;
border-radius: 6px;
color: #1d1d1d;
border: 1px solid #e8e8e8;
padding: 0 15px;
margin: 5px 0;
height: 40px;
}
a {
color: #24b47e;
cursor: pointer;
}
```
## Step 8: Adding the API routes
Now we'll replace the existing api routes in `/pages/api/` with 5 new routes:
- `/sign-in.ts`: handles signing in with Supabase and initiating the MFA challenge with Authsignal
- `/sign-up.ts`: handles signing up with Supabase
- `/sign-out.ts`: clears the Supabase auth cookies and signs the user out
- `/mfa.ts`: handles the user's attempt to set up MFA or to manage their existing MFA settings
- `/callback.ts`: handles completing the MFA challenge with Authsignal
Add the following code to `/pages/api/sign-in.ts`:
```
import { supabaseClient } from "@supabase/auth-helpers-nextjs";
import { NextApiRequest, NextApiResponse } from "next";
import { authsignal } from "../../lib/authsignal";
import { setAuthCookie, setTempCookie } from "../../lib/cookies";
export default async function signIn(
req: NextApiRequest,
res: NextApiResponse
) {
const { email, password } = req.body;
const { data, error } = await supabaseClient.auth.api.signInWithEmail(
email,
password
);
if (error || !data?.user) {
return res.send({ error });
}
const { state, url: mfaUrl } = await authsignal.track({
action: "signIn",
userId: data.user.id,
});
if (state === "CHALLENGE_REQUIRED") {
await setTempCookie(data, res);
} else {
setAuthCookie(data, res);
}
res.send({ state, mfaUrl });
}
```
Then to handle new sign-ups add the following to `/pages/api/sign-up.ts`:
```
import { supabaseClient } from "@supabase/auth-helpers-nextjs";
import { Session } from "@supabase/supabase-js";
import { NextApiRequest, NextApiResponse } from "next";
import { setAuthCookie } from "../../lib/cookies";
export default async function signUp(
req: NextApiRequest,
res: NextApiResponse
) {
const { email, password } = req.body;
const { data, error } = await supabaseClient.auth.api.signUpWithEmail(
email,
password
);
if (error || !isSession(data)) {
res.send({ error });
} else {
setAuthCookie(data, res);
res.send({ data });
}
}
const isSession = (data: any): data is Session => !!data?.access_token;
```
To clear the auth cookies on sign-out add the following to `/pages/api/sign-out.ts`:
```
import { supabaseClient } from "@supabase/auth-helpers-nextjs";
import { NextApiRequest, NextApiResponse } from "next";
export default async function signOut(
req: NextApiRequest,
res: NextApiResponse
) {
supabaseClient.auth.api.deleteAuthCookie(req, res, { redirectTo: "/sign-in" });
}
```
To handle the user's actions to set up MFA or manage their existing MFA settings, add the following to `/pages/api/mfa.ts`:
```
import { getUser, withApiAuth } from "@supabase/auth-helpers-nextjs";
import { NextApiRequest, NextApiResponse } from "next";
import { authsignal } from "../../lib/authsignal";
export default withApiAuth(async function mfa(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== "POST") {
return res.status(405).send({ message: "Only POST requests allowed" });
}
const { user } = await getUser({ req, res });
const { isEnrolled } = req.body;
const { url: mfaUrl } = await authsignal.track({
action: isEnrolled ? "manageSettings" : "enroll",
userId: user.id,
redirectToSettings: isEnrolled,
});
res.send({ mfaUrl });
});
```
Because the user should be authenticated with Supabase to set up or manage MFA, we can use Supabase's `withApiAuth` wrapper to protect this route.
The `redirectToSettings` param specifies whether the user should be redirected to the MFA page settings panel after a challenge, rather than redirecting them immediately back to the application.
Finally we need a route to handle the redirect back from Authsignal after an MFA challenge. Add the following to `/pages/api/callback.ts`:
```
import { NextApiRequest, NextApiResponse } from "next";
import { authsignal } from "../../lib/authsignal";
import { getSessionFromTempCookie, setAuthCookie } from "../../lib/cookies";
export default async function callback(
req: NextApiRequest,
res: NextApiResponse
) {
const token = req.query.token as string;
const { success } = await authsignal.validateChallenge({ token });
if (success) {
const session = await getSessionFromTempCookie(req);
if (session) {
setAuthCookie(session, res);
}
}
res.redirect("/");
}
```
That's it! You should now be able to sign up a new user and set up MFA.
Then if you sign out, you'll be prompted to complete an MFA challenge when signing back in again.
## Resources
- To learn more about Authsignal take a look at the [API Documentation](https://docs.authsignal.com/).
- You can customize the look and feel of the Authsignal Prebuilt MFA page [here](https://portal.authsignal.com/organisations/tenants/customizations).
@@ -1,90 +0,0 @@
---
id: performance
title: Performance Tuning
description: Getting the best results out of your Supabase project
---
The Supabase platform automatically optimizes your Postgres database to take advantage of the compute resources of the tier your project is on. However, these optimizations are based on assumptions about the type of workflow the project is being utilized for, and it is likely that better results can be obtained by tuning the database for your particular workflow.
## Optimizing the number of connections
By default, the number of connections allowed to Postgres and PgBouncer is configured based on the resources available to the database.
| Compute Add-on | Postgresql connections | PGBouncer connections |
| -------------- | ---------------------- | --------------------- |
| None | 60 | 200 |
| Small | 90 | 200 |
| Medium | 120 | 200 |
| Large | 160 | 300 |
| XL | 240 | 700 |
| 2XL | 380 | 1500 |
| 4XL | 480 | 3000 |
| 8XL | 490 | 6000 |
| 12XL | 500 | 9000 |
| 16XL | 500 | 12000 |
If the number of connections is insufficient, you will receive the following error upon connecting to the DB:
```shell
$ psql -U postgres -h ...
FATAL: remaining connection slots are reserved for non-replication superuser connections
```
In such a scenario, you can consider:
- [upgrading to a larger compute add-on](https://app.supabase.com/project/_/settings/billing)
- configuring your clients to use fewer connections
- manually configuring the database for a higher number of connections
### Configuring clients to use fewer connections
You can use the [pg_stat_activity](https://www.postgresql.org/docs/current/monitoring-stats.html#MONITORING-PG-STAT-ACTIVITY-VIEW) view to debug which clients are holding open connections on your DB. `pg_stat_activity` only exposes information on direct connections to the database. Information on the number of connections to pgbouncer is available [via the metrics endpoint](../platform/metrics).
Depending on the clients involved, you might be able to configure them to work with fewer connections (e.g. by imposing a limit on the maximum number of connections they're allowed to use), or shift specific workloads to connect via [pgbouncer](/docs/guides/database/connecting-to-postgres#connection-pool) instead. Transient workflows, which can quickly scale up and down in response to traffic (e.g. serverless functions), can especially benefit from using a connection pooler rather than connecting to the DB directly.
### Allowing higher number of connections
You can configure Postgres by executing the following statement, followed by a server restart:
```sql
alter system set max_connections = '<val-here>';
```
Note that [the default configuration used by the Supabase platform](https://github.com/supabase/supabase-admin-api/blob/master/optimizations/postgres.go) optimizes the database to maximize resource utilization, and as a result, you might also need to configure other options (e.g. `work_mem`, `shared_buffers`, `maintenance_work_mem`) in order to tune them towards your use-case, and to avoid causing instability in your database.
Once overridden, the Supabase platform will continue to respect your manually configured value (even if the add-on size is changed), unless the override is removed with the following statement, followed by a server restart:
```sql
alter system reset max_connections;
alter system reset <other-overridden-conf>;
...
```
Configuring the number of PgBouncer connections is not supported at this time.
## Examining Query Performance
Every Supabase project has [the pg_stat_statements extension](https://www.postgresql.org/docs/14/pgstatstatements.html) enabled by default. This extension records query execution performance details and is the best way to find queries that take the most time to execute. This information can be combined with the Postgres query plan analyzer to develop more efficient queries.
Obtaining information from pg_stat_statements:
```sql
select mean_exec_time + stddev_exec_time, * from pg_stat_statements order by 1 desc;
```
Using the query plan analyzer on your expensive queries:
```sql
explain analyze <query-statement-here>;
```
Be careful using `explain analyze` with `insert`/`update`/`delete` queries, because the query will actually run, and could have unintended side-effects.
Using the query plan analyzer to optimize your queries is a large topic, with a number of online resources available:
- [Official docs.](https://www.postgresql.org/docs/current/using-explain.html)
- [The Art of PostgreSQL.](https://theartofpostgresql.com/explain-plan-visualizer/)
- [Postgres Wiki.](https://wiki.postgresql.org/wiki/Using_EXPLAIN)
- [Enterprise DB.](https://www.enterprisedb.com/blog/postgresql-query-optimization-performance-tuning-with-explain-analyze)
You can pair the information available from `pg_stat_statements` with the detailed system metrics available [via your metrics endpoint](../platform/metrics) to better understand the behavior of your DB and the queries you're executing against it.
@@ -1,12 +0,0 @@
---
id: storage-sample
title: 'Storage Sample Doc'
description: Storage Sample Doc
---
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
## Description
Sample...
-27
View File
@@ -1,27 +0,0 @@
---
id: contributing
title: Contributing
description: Want to help?
---
## How to contribute
Supabase has many [open source repos](https://github.com/orgs/supabase/repositories). Some of the more popular ones are:
- [Supabase](https://github.com/supabase/supabase) - Our Dashboard, Websites, and example apps
- [Supabase-js](https://github.com/supabase/supabase-js) - A clientside library written in Typescript
- [Gotrue](https://github.com/supabase/gotrue) - Our auth server, written in Go
- [Realtime](https://github.com/supabase/realtime) - A server for streaming changes from Postgres, written in Elixir
- [browse all](https://github.com/orgs/supabase/repositories)
Want to contribute? Why not jump into our GitHub repo and:
- [Sponsor Supabase](https://github.com/sponsors/supabase).
- [Answer Discussions](https://github.com/supabase/supabase/discussions).
- Submit an issue to one of our [repos](https://github.com/orgs/supabase/repositories)
- Report a performance issue or a part of the documentation that you find confusing.
- Create a pull request in one of our [repos](https://github.com/orgs/supabase/repositories)
- [Translate our Readme](https://github.com/supabase/supabase/issues/1341).
- Try our products and give feedback.
- Spread the word if you like what we are doing.
-15
View File
@@ -1,15 +0,0 @@
---
id: introduction
title: Supabase Handbook
description: 'The Supabase public handbook and manifesto'
---
Supabase is thinking in public. Everything is online, no matter how raw the current state of things.
This is our public handbook and manifesto. We are completely opensource and this means that you, our users, are also our team members.
---
## Opensource
Wherever we can we will support existing opensource tools and libraries.
@@ -123,14 +123,14 @@ const { data } = await supabase.auth.signIn({
There is a new `channel()` method in the Realtime library, which will be used for our Multiplayer updates.
```ts
supabaseClient
supabase
.channel('any_string_you_want')
.on('presence', { event: 'track' }, (payload) => {
console.log(payload)
})
.subscribe()
supabaseClient
supabase
.channel('any_string_you_want')
.on(
'postgres_changes',
@@ -28,7 +28,7 @@ _Optionally_ if you are using custom configuration with `createClient` then foll
<TabPanel id="1.0x" label="Before">
```ts title=src/supabaseClient.ts
const supabaseClient = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
schema: 'custom',
persistSession: false,
})
@@ -38,7 +38,7 @@ const supabaseClient = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
<TabPanel id="2.0x" label="After">
```ts title=src/supabaseClient.ts
const supabaseClient = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
db: {
schema: 'custom',
},
@@ -1,11 +1,11 @@
---
slug: /
sidebar_position: 1
sidebar_label: Realtime Server
id: realtime
title: Supabase Realtime Server
sidebar_label: Supabase Realtime Server
---
# Supabase Realtime Server
Supabase Realtime is a server built with Elixir using the [Phoenix Framework](https://www.phoenixframework.org) that allows you to listen to changes in your PostgreSQL database via logical replication and then broadcast those changes via WebSockets.
There are two versions of this server: `Realtime` and `Realtime RLS`.
@@ -23,7 +23,7 @@ There are two versions of this server: `Realtime` and `Realtime RLS`.
3. converting the changes into JSON
4. broadcasting to authorized subscribers over WebSockets
### Why not just use PostgreSQL's `NOTIFY`?
## Why not just use PostgreSQL's `NOTIFY`?
A few reasons:
@@ -31,13 +31,13 @@ A few reasons:
2. `NOTIFY` has a payload limit of 8000 bytes and will fail for anything larger. The usual solution is to send an ID and then fetch the record, but that's heavy on the database.
3. `Realtime` server consumes two connections to the database, then you can connect many clients to this server. Easier on your database, and to scale up you just add additional `Realtime` servers.
### Benefits
## Benefits
1. The beauty of listening to the replication functionality is that you can make changes to your database from anywhere - your API, directly in the DB, via a console, etc. - and you will still receive the changes via WebSockets.
2. Decoupling. For example, if you want to send a new slack message every time someone makes a new purchase you might build that functionality directly into your API. This allows you to decouple your async functionality from your API.
3. This is built with Phoenix, an [extremely scalable Elixir framework](https://www.phoenixframework.org/blog/the-road-to-2-million-websocket-connections).
### Does this server guarantee delivery of every data change?
## Does this server guarantee delivery of every data change?
Not yet! Due to the following limitations:
@@ -52,7 +52,6 @@ Not yet! Due to the following limitations:
## Additional Links
- [Source Code](https://github.com/supabase/storage-api)
- [Known bugs and issues](https://github.com/supabase/storage-js/issues)
- [Storage Guides](https://supabase.com/docs/guides/storage)
- [OpenAPI Docs](https://supabase.github.io/storage-api/)
- [Source Code](https://github.com/supabase/realtime)
- [Known bugs and issues](https://github.com/supabase/realtime/issues)
- [Realtime Guides](https://supabase.com/docs/guides/realtime)
+1 -1
View File
@@ -24,5 +24,5 @@ Read [this post](https://supabase.com/blog/supabase-storage) on why we decided t
- [Source Code](https://github.com/supabase/storage-api)
- [Known bugs and issues](https://github.com/supabase/storage-js/issues)
- [Storage Guides](/guides/storage)
- [Storage Guides](/docs/guides/storage)
- [OpenAPI Docs](https://supabase.github.io/storage-api/)
@@ -22,11 +22,11 @@ toc_max_heading_level: 3
<%- parameter.description %>
<ul>
<li>Required: <code><%= parameter.required %></code></li>
<li>Default: <code><%- parameter?.default ? parameter?.default : 'None' %></code></li><% if(parameter?.links?.length){ %>
<ul class="method-list-group">
<li class="method-list-item-label flex items-center gap-4">Required: <code><%= parameter.required %></code></li>
<li class="method-list-item-label flex items-center gap-4">Default: <code><%- parameter?.default ? parameter?.default : 'None' %></code></li><% if(parameter?.links?.length){ %>
<li>
<span>See also:</span>
<h3>See also:</h3>
<ul><% parameter.links?.forEach(function(link){ %><li><a href="<%- link.link %>"><%- link.name %></a></li><% }); %></ul>
</li>
<% } %>
+85
View File
@@ -0,0 +1,85 @@
import { writeFileSync } from 'fs'
import { globby } from 'globby'
import prettier from 'prettier'
/*
* kudos to leerob from vercel
* https://leerob.io/blog/nextjs-sitemap-robots
*/
async function generate() {
const prettierConfig = await prettier.resolveConfig('./.prettierrc.js')
const guidesPages = await globby(['docs/*.mdx', 'docs/guides/**/*.mdx', 'docs/handbook/**/*.mdx'])
const rawReferencePages = await globby([
// guides
'docs/*.mdx',
'!docs/404.mdx',
'docs/guides/**/*.mdx',
'docs/handbook/**/*.mdx',
// reference
'docs/reference/*.mdx',
'docs/reference/javascript/*.mdx',
'docs/reference/javascript/generated/*.mdx',
'!docs/reference/javascript/v1.mdx', // ignore this
'docs/reference/dart/*.mdx',
'docs/reference/dart/generated/*.mdx',
'!docs/reference/dart/v0.mdx', // ignore this
'docs/reference/api/*.mdx',
'docs/reference/api/generated/*.mdx',
'docs/reference/cli/*.mdx',
'docs/reference/cli/generated/*.mdx',
// misc reference
'docs/reference/postgres/*.mdx',
'docs/reference/postgres/generated/*.mdx',
'docs/reference/realtime/*.mdx',
'docs/reference/realtime/generated/*.mdx',
'docs/reference/storage/*.mdx',
'docs/reference/storage/generated/*.mdx',
'docs/reference/auth/*.mdx',
'docs/reference/auth/generated/*.mdx',
])
const pages = rawReferencePages.map((x) => {
let string = x
string = string.replace('/generated', '')
string = string.replace('.mdx', '')
return string
})
// add static OSS page
pages.unshift('docs/oss')
// add static homepage
pages.unshift('docs')
const sitemap = `
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${pages
.map((path) => {
return `
<url>
<loc>${`https://supabase.com/${path}`}</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
`
})
.join('')}
</urlset>
`
const formatted = prettier.format(sitemap, {
...prettierConfig,
parser: 'html',
})
const sitemapFilePath = `public/sitemap.xml`
console.log(`Total of ${pages.length} pages in sitemap, located at /apps/docs/${sitemapFilePath}`)
// eslint-disable-next-line no-sync
writeFileSync(sitemapFilePath, formatted)
}
generate()
+20 -12
View File
@@ -1,5 +1,5 @@
import { useEffect, FC } from 'react'
import Head from 'next/head'
import { NextSeo } from 'next-seo'
import NavBar from '../components/Navigation/NavBar'
import SideBar from '../components/Navigation/SideBar'
import Footer from '../components/Footer'
@@ -29,16 +29,20 @@ const Layout: FC<Props> = ({ meta, children, toc, menuItems, currentPage }) => {
return (
<>
<Head>
<title>{meta?.title} | Supabase</title>
<meta name="description" content={meta?.description} />
<meta content="width=device-width, initial-scale=1" name="viewport" />
<link rel="icon" href="/docs/favicon.ico" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content={meta?.title} />
<meta property="og:description" content={meta?.description} />
<meta property="og:title" content={meta?.title} />
</Head>
<NextSeo
title={`${meta?.title} | Supabase`}
description={meta?.description ? meta?.description : meta?.title}
openGraph={{
title: meta?.title,
description: meta?.description,
url: `https://supabase.com/docs/${currentPage}`,
images: [
{
url: `https://supabase.com/docs/img/supabase-og-image.png`,
},
],
}}
/>
<main>
<NavBar currentPage={currentPage} />
@@ -52,7 +56,11 @@ const Layout: FC<Props> = ({ meta, children, toc, menuItems, currentPage }) => {
: 'col-span-12 lg:col-span-9'
} py-2 lg:py-4 px-2 lg:px-8 mx-auto`}
>
<article className="doc-content-container prose dark:prose-dark dark:bg-scale-200 width-full mt-8 2xl:max-w-[880px] ">
<article
className={`${
meta?.hide_table_of_contents || !hasTableOfContents ? 'xl:min-w-[880px]' : ''
} doc-content-container prose dark:prose-dark dark:bg-scale-200 width-full mt-8 2xl:max-w-[880px]`}
>
{children}
</article>
</div>
+112
View File
@@ -0,0 +1,112 @@
import { MDXProvider } from '@mdx-js/react'
import { useEffect, FC, useRef, useState } from 'react'
import { NextSeo } from 'next-seo'
import NavBar from '../components/Navigation/NavBar'
import SideBar from '../components/Navigation/SideBar'
import Footer from '../components/Footer'
import GuidesTableOfContents from '~/components/GuidesTableOfContents'
import { useRouter } from 'next/router'
import { getPageType } from '../lib/helpers'
import components from '~/components'
import { menuItems } from '../components/Navigation/Navigation.constants'
interface Props {
meta: { title: string; description?: string; hide_table_of_contents?: boolean }
children: any
toc?: any
currentPage: string
}
const Layout: FC<Props> = ({ meta, children }) => {
const { asPath } = useRouter()
const page = getPageType(asPath)
useEffect(() => {
const key = localStorage.getItem('supabaseDarkMode')
if (!key) {
// Default to dark mode if no preference config
document.documentElement.className = 'dark'
} else {
document.documentElement.className = key === 'true' ? 'dark' : ''
}
}, [])
const articleRef = useRef()
const [tocList, setTocList] = useState([])
useEffect(() => {
const articleEl = articleRef.current as HTMLElement
if (!articleRef.current) return
const headings = Array.from(articleEl.querySelectorAll('h2, h3'))
const newHeadings = headings
.filter((heading) => heading.id)
.map((heading) => {
const text = heading.textContent.replace('#', '')
const link = heading.querySelector('a').getAttribute('href')
const level = heading.tagName === 'H2' ? 2 : 3
return { text, link, level }
})
setTocList(newHeadings)
}, [])
const hasTableOfContents = tocList.length > 0
return (
<>
<NextSeo
title={`${meta?.title} | Supabase`}
description={meta?.description ? meta?.description : meta?.title}
openGraph={{
title: meta?.title,
description: meta?.description,
url: `https://supabase.com/docs${asPath}`,
images: [
{
url: `https://supabase.com/docs/img/supabase-og-image.png`,
},
],
}}
/>
<main>
<NavBar currentPage={page} />
<div className="flex w-full flex-row">
<SideBar menuItems={menuItems[page]} />
<div className="main-content-pane docs-width grid md:grid-cols-12 gap-4 justify-between p-4 pb-8 w-full">
<div
className={`${
meta?.hide_table_of_contents || !hasTableOfContents
? 'col-span-12 xl:col-start-2 xl:col-span-10 2xl:col-start-3 2xl:col-span-8'
: 'col-span-12 lg:col-span-9'
} py-2 lg:py-4 px-2 lg:px-8 mx-auto`}
>
<article
ref={articleRef}
className={`${
meta?.hide_table_of_contents || !hasTableOfContents ? 'xl:min-w-[880px]' : ''
} doc-content-container prose dark:prose-dark dark:bg-scale-200 width-full mt-8 2xl:max-w-[880px]`}
>
{meta?.title && <h1>{meta.title}</h1>}
<MDXProvider components={components}>{children}</MDXProvider>
</article>
</div>
{hasTableOfContents && !meta?.hide_table_of_contents && (
<div
className={[
'border-scale-400 dark:bg-scale-200 table-of-contents-height border-l',
'thin-scrollbar overflow-y-auto sticky hidden xl:block md:col-span-3 px-2',
].join(' ')}
>
<GuidesTableOfContents list={tocList} />
</div>
)}
</div>
</div>
<Footer />
</main>
</>
)
}
export default Layout
+2 -1
View File
@@ -23,6 +23,7 @@ export function getDocsBySlug(slug: string) {
(realSlug.includes('reference/cli/') && !nonGeneratedReferencePages.includes(realSlug)) ||
(realSlug.includes('reference/api/') && !nonGeneratedReferencePages.includes(realSlug)) ||
(realSlug.includes('reference/auth/') && !nonGeneratedReferencePages.includes(realSlug)) ||
(realSlug.includes('reference/realtime/') && !nonGeneratedReferencePages.includes(realSlug)) ||
(realSlug.includes('reference/storage/') && !nonGeneratedReferencePages.includes(realSlug))
? getPathToGeneratedDoc(realSlug)
: realSlug
@@ -44,7 +45,7 @@ export function getDocsBySlug(slug: string) {
// if no match, 404
if (!fs.existsSync(fullPath)) {
console.log(`\nfile ${fullPath} not found, redirect to 404\n`)
fullPath = join(docsDirectory, 'docs/404.mdx')
fullPath = join(docsDirectory, 'pages/404.mdx')
}
const fileContents = fs.readFileSync(fullPath, 'utf8')
+2
View File
@@ -20,6 +20,8 @@ export function getPageType(asPath: string) {
page = 'reference/cli'
} else if (asPath.includes('/reference/auth')) {
page = 'reference/auth'
} else if (asPath.includes('/reference/realtime')) {
page = 'reference/realtime'
} else if (asPath.includes('/reference/storage')) {
page = 'reference/storage'
} else if (asPath.includes('/reference')) {
+23 -27
View File
@@ -1,44 +1,19 @@
import nextMdx from '@next/mdx'
import bundleAnalyzer from '@next/bundle-analyzer'
import remarkGfm from 'remark-gfm'
import admonitions from 'remark-admonitions'
import rehypeSlug from 'rehype-slug'
import withTM from 'next-transpile-modules'
// import { remarkCodeHike } from '@code-hike/mdx'
// import theme from 'shiki/themes/dark-plus.json' assert { type: 'json' }
// import codeHikeTheme from './codeHikeTheme.js'
/**
* Rewrites and redirects are handled by
* apps/www nextjs config
*
* Do not add them in this config
*/
const withMDX = nextMdx({
extension: /\.mdx?$/,
options: {
remarkPlugins: [
// [
// remarkCodeHike,
// { theme: codeHikeTheme, autoImport: false },
// ],
remarkGfm,
[admonitions, {}],
],
remarkPlugins: [remarkGfm],
rehypePlugins: [rehypeSlug],
// This is required for `MDXProvider` component
providerImportSource: '@mdx-js/react',
// providerImportSource: '@mdx-js/react',
},
})
const withBundleAnalyzer = bundleAnalyzer({
enabled: process.env.ANALYZE === 'true',
})
// /** @type {NextConfig} */
const nextConfig = {
// Append the default value with md extensions
@@ -50,6 +25,27 @@ const nextConfig = {
dangerouslyAllowSVG: true,
domains: ['avatars.githubusercontent.com', 'github.com', 'user-images.githubusercontent.com'],
},
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'Strict-Transport-Security',
value: '',
},
{
key: 'X-Robots-Tag',
value: 'all',
},
{
key: 'X-Frame-Options',
value: 'DENY',
},
],
},
]
},
}
// next.config.js
+7 -2
View File
@@ -7,7 +7,8 @@
"build": "npm run generate:all && next build",
"start": "next start",
"lint": "next lint",
"postbuild": "node ./scripts/build-search.js",
"build:sitemap": "node ./internals/generate-sitemap.mjs",
"postbuild": "node ./scripts/build-search.js && node ./internals/generate-sitemap.mjs",
"generate:all": "npm-run-all --parallel gen:api gen:cli gen:gotrue gen:storage gen:supabase-dart:v0 gen:supabase-dart:v1 gen:supabase-js:v1 gen:supabase-js:v2",
"gen:api": "npm-run-all gen:api:usage",
"gen:api:usage": "ts-node ./generator/index.ts gen --type api --url https://api.supabase.com --input ../../spec/transforms/api_v0_openapi_deparsed.json --output ./docs/reference/api/generated/usage.mdx",
@@ -27,7 +28,7 @@
"gen:supabase-js:v1": "npm-run-all gen:supabase-js:v1:ref",
"gen:supabase-js:v1:ref": "ts-node ./generator/index.ts gen --type legacy --input ../../spec/supabase_js_v1.yml --output ./docs/reference/javascript/v1/generated",
"gen:supabase-js:v2": "npm-run-all gen:supabase-js:v2:ref",
"gen:supabase-js:v2:ref": "ts-node ./generator/index.ts gen --type legacy --input ../../spec/supabase_js_v2_temp.yml --output ./docs/reference/javascript/generated"
"gen:supabase-js:v2:ref": "ts-node ./generator/index.ts gen --type legacy --input ../../spec/supabase_js_v2.yml --output ./docs/reference/javascript/generated"
},
"dependencies": {
"@algolia/autocomplete-js": "^1.7.2",
@@ -49,9 +50,11 @@
"jsrsasign": "^10.5.26",
"lodash": "^4.17.21",
"markdown-toc": "^1.2.0",
"mdx-mermaid": "2.0.0-rc3",
"next": "12.1.0",
"next-compose-plugins": "^2.2.1",
"next-mdx-remote": "^3.0.8",
"next-seo": "^5.14.1",
"next-themes": "0.0.15",
"next-transpile-modules": "^9.0.0",
"octokit": "^2.0.10",
@@ -74,10 +77,12 @@
"config": "*",
"ejs": "^3.1.8",
"eslint": "8.9.0",
"globby": "^12.0.2",
"minimist": "^1.2.6",
"next-transpile-modules": "9.0.0",
"npm-run-all": "^4.1.5",
"openapi-types": "^12.0.2",
"prettier": "^2.2.1",
"sass": "^1.55.0",
"ts-node": "^10.9.1",
"tsconfig": "*",
+6
View File
@@ -0,0 +1,6 @@
export const meta = {
title: '404',
description: 'Page not found',
}
# Page not found
-1
View File
@@ -38,7 +38,6 @@ export default function Doc({ meta, content, toc }: Props) {
export async function getStaticProps({ params }: { params: { slug: string[] } }) {
let slug
if (params.slug.length > 1) {
slug = `docs/${params.slug.join('/')}`
} else {
+36 -5
View File
@@ -4,6 +4,8 @@ import type { AppProps } from 'next/app'
import { post } from 'lib/fetchWrappers'
import { ThemeProvider } from '../components/Providers'
import { SearchProvider } from '~/components/DocSearch'
import { DefaultSeo } from 'next-seo'
import Favicons from '~/components/Favicons'
import '../styles/main.scss?v=1.0.0'
import '../styles/docsearch.scss'
import '../styles/algolia-search.scss'
@@ -32,12 +34,41 @@ function MyApp({ Component, pageProps }: AppProps) {
}
}, [router.events])
const SITE_TITLE = 'Supabase Documentation'
const SITE_DESCRIPTION = 'The open source Firebase alternative.'
const { basePath } = useRouter()
return (
<ThemeProvider>
<SearchProvider>
<Component {...pageProps} />
</SearchProvider>
</ThemeProvider>
<>
<Favicons />
<DefaultSeo
title={SITE_TITLE}
description={SITE_DESCRIPTION}
openGraph={{
type: 'website',
url: 'https://supabase.com/docs',
site_name: SITE_TITLE,
images: [
{
url: `https://supabase.com${basePath}/img/supabase-og-image.png`,
width: 800,
height: 600,
alt: 'Supabase Og Image',
},
],
}}
twitter={{
handle: '@supabase',
site: '@supabase',
cardType: 'summary_large_image',
}}
/>
<ThemeProvider>
<SearchProvider>
<Component {...pageProps} />
</SearchProvider>
</ThemeProvider>
</>
)
}
@@ -1,8 +1,10 @@
---
id: architecture
title: Architecture
description: 'Supabase design and architecture'
---
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'architecture',
title: 'Architecture',
description: 'Supabase design and architecture',
}
Supabase is open source. We choose open source tools which are scalable and make them simple to use.
@@ -154,3 +156,7 @@ As the profile of a developer changes over time, Supabase will continue to evolv
Supabase supports existing tools and communities wherever possible. Supabase is more like a "community of communities" - each tool typically has its own community
which we work with.
Open source is something we approach [collaboratively](https://supabase.com/blog/supabase-series-b#giving-back): we employ maintainers, sponsor projects, invest in businesses, and develop our own open source tools.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,9 +1,10 @@
---
id: aup
title: Acceptable Use Policy
---
import Layout from '~/layouts/DefaultGuideLayout'
`Last Modified: 15 April 2021`
export const meta = {
title: 'Acceptable Use Policy',
}
_Last Modified: 15 April 2021_
This Acceptable Use Policy (this “Policy”) describes prohibited uses of the web services offered by Supabase, Inc. and its affiliates (the “Services”) and the website located at https://supabase.com (the “Supabase Site”). The examples described in this Policy are not exhaustive. We may modify this Policy at any time by posting a revised version on the Supabase Site. By using the Services or accessing the Supabase Site, you agree to the latest version of this Policy. If you violate the Policy or authorize or help others to do so, we may suspend or terminate your use of the Services.
@@ -50,3 +51,7 @@ We may report any activity that we suspect violates any law or regulation to app
Reporting of Violations of this Policy
If you become aware of any violation of this Policy, you will immediately notify us and provide us with assistance, as requested, to stop or remedy the violation. To report any violation of this Policy, please contact us at support@supabase.io.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,9 +1,10 @@
---
id: privacy
title: Privacy Policy
---
import Layout from '~/layouts/DefaultGuideLayout'
`Last modified: 27 March 2021`
export const meta = {
title: 'Privacy Policy',
}
_Last modified: 27 March 2021_
Thank you for your interest in Supabase, Inc., ("**_Supabase_**," "**_we_**", "**_our_**" or "**_us_**"). Supabase provides a suite of open source tools, stitched together to build a seamless developer experience. This Privacy Notice explains how information about you, that directly identifies you, or that makes you identifiable ("**_personal information_**") is collected, used and disclosed by Supabase in connection with our website at [supabase.com](https://supabase.com) (the "**_Site_**") and our services offered in connection with the Site (collectively with the Site, the "**_Service_**").
@@ -290,3 +291,7 @@ Some browsers also give you the option of downloading and installing extensions
| <p>a0:session</p><p></p> | Strictly necessary | 1 Day | Authentication Purposes. |
| <p>a0:state</p><p></p> | Strictly necessary | 2 Hours | Authentication Purposes. |
| <p>youtube-nocookie</p><p></p> | Strictly necessary | Persistent | <p>Privacy-enhanced cookie, essential for embedded videos. [Link.](https://support.google.com/youtube/answer/171780)</p><p><blockquote><small>Privacy Enhanced Mode allows you to embed YouTube videos without using cookies that track viewing behavior. This means no activity is collected to personalize the viewing experience.</small></blockquote></p> |
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,7 +1,8 @@
---
id: sla
title: Service Level Agreement
---
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
title: 'Service Level Agreement',
}
The following Service Level Agreement, which is incorporated into and forms part of the Subscription Agreement between Supabase, Inc. ("Supabase") and Customer (the "Agreement"), will apply to the Services for Enterprise Customers specified in an Order Form during the applicable Subscription Term:
@@ -86,3 +87,7 @@ An Information request about Supabase or feature request.
| 4. Low | 2 business days<br />Monday - Friday | 2 business days<br />Monday - Friday | 1 business day<br />Monday - Friday |
Business hours are from 6am to 6pm (local time), except where otherwise stated.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,9 +1,10 @@
---
id: terms
title: Terms of Service
---
import Layout from '~/layouts/DefaultGuideLayout'
`Last Modified: 20 November 2020`
export const meta = {
title: 'Terms of service',
}
_Last Modified: 20 November 2020_
These Customer Terms and Conditions (this "**Agreement**"), effective as of the date on which you click a button or check a box (or something similar) acknowledging your acceptance of this Agreement (the "**Effective Date**"), is by and between Supabase, Inc., a Delaware corporation with offices located at 970 Toa Payoh North #07-04, Singapore 318992 ("**Supabase**") and the entity on whose behalf the individual accepting this Agreement accepts this Agreement ("**Customer**"). The individual accepting this Agreement hereby represents and warrants that it is duly authorized by the entity on whose behalf it accepts this Agreement to so accept this Agreement. Supabase and Customer may be referred to herein collectively as the "**Parties**" or individually as a "**Party**." The Parties agree as follows:
@@ -233,3 +234,7 @@ Each Party acknowledges and agrees that a breach or threatened breach by such Pa
### 12. Publicity.
Supabase may identify Customer as a user of the Services and may use Customer's name, logo, and other trademarks in Supabase's customer list, press releases, blog posts, advertisements, and website (and all use thereof and goodwill arising therefrom shall inure to the sole and exclusive benefit of Customer). Otherwise, neither Party may use the name, logo, or other trademarks of the other Party for any purpose without the other Party's prior written approval.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,8 +1,10 @@
---
id: faq
title: FAQs
description: 'Most frequently asked questions regarding Supabase'
---
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'faq',
title: 'FAQs',
description: 'Most frequently asked questions regarding Supabase',
}
### Where do I find support?
@@ -36,3 +38,7 @@ We only support PostgreSQL. It's unlikely we'll ever move away from Postgres; ho
We officially support [JavaScript](/docs/reference/javascript/installing) and [Flutter](/docs/reference/dart/installing).
You can find community-supported libraries in our [GitHub Community](https://github.com/supabase-community), and you can also help us to identify the most popular languages by [voting for a new client library](https://github.com/supabase/supabase/discussions/5).
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,8 +1,10 @@
---
id: features
title: Features
description: 'Supabase features'
---
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'features',
title: 'Features',
description: 'Supabase features',
}
This is a non-exhaustive list of features that Supabase provides for every project.
@@ -170,3 +172,7 @@ Both Postgres and the Supabase Platform are production-ready. Some tools we offe
| CLI | | `beta` |
| Client Library: JavaScript | | `GA` |
| Client Library: Dart | | `beta` |
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,9 +1,11 @@
---
id: api
title: Serverless APIs
description: Auto-generating and Realtime APIs.
sidebar_label: Overview
---
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'api',
title: 'Serverless APIs',
description: 'Auto-generating and Realtime APIs.',
sidebar_label: 'Overview',
}
Supabase auto-generates three types of API directly from your database schema.
@@ -319,8 +321,8 @@ called `supabase_realtime`, and by managing this publication you can control whi
### Securing your Routes
Your API is designed to work with Postgres Row Level Security (RLS). If you use Supabase [Auth](/guides/auth), you can restrict data based on the logged-in user.
To control access to your data, you can use [Policies](/guides/auth#policies).
Your API is designed to work with Postgres Row Level Security (RLS). If you use Supabase [Auth](/docs/guides/auth), you can restrict data based on the logged-in user.
To control access to your data, you can use [Policies](/docs/guides/auth#policies).
When you create a table in Postgres, Row Level Security is disabled by default. To enable RLS:
<Tabs
@@ -349,6 +351,12 @@ alter table todos enable row level security;
Never expose the `service_role` key in a browser or anywhere where a user can see it. This Key is designed to bypass Row Level Security - so it should only be used on a private server.
A common use case for the `service_role` key is to run data analytics jobs on the backend. To support joins on user id, it is often useful to grant the service role read access to `auth.users` table.
```sql
grant select on table auth.users to service_role;
```
We have [partnered with GitHub](https://github.blog/changelog/2022-03-28-supabase-is-now-a-github-secret-scanning-partner/) to scan for Supabase `service_role` keys pushed to public repositories.
If they detect any keys with service_role privileges being pushed to GitHub, they will forward the API key to us, so that we can automatically revoke the detected secrets and notify you, protecting your data against malicious actors.
@@ -367,3 +375,7 @@ The expected value for `useconfig` should be:
```sql
['session_preload_libraries=supautils, safeupdate']
```
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,8 +1,10 @@
---
id: generating-types
title: 'Generating Types'
description: How to generate types for your API and Supabase libraries.
---
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'generating-types',
title: 'Generating Types',
description: 'How to generate types for your API and Supabase libraries.',
}
Supabase APIs are generated from your database, which means that we can use database introspection to generate type-safe API definitions.
@@ -105,3 +107,7 @@ Alternatively, you can use a community-supported GitHub action: [generate-supaba
## Resources
- [Generating Supabase types with GitHub Actions](https://blog.esteetey.dev/how-to-create-and-test-a-github-action-that-generates-types-from-supabase-database)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,9 +1,11 @@
---
id: auth
title: Auth
description: Use Supabase to Authenticate and Authorize your users.
sidebar_label: Overview
---
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth',
title: 'Auth',
description: 'Use Supabase to Authenticate and Authorize your users.',
sidebar_label: 'Overview',
}
## Overview
@@ -49,6 +51,43 @@ You can enable third-party providers with the click of a button by navigating to
![OAuth Logins.](/docs/img/supabase-oauth-logins.png)
### Redirect URLs and wildcards
When using third-party providers, the [Supabase client library](/docs/reference/javascript/auth-signinwithoauth#sign-in-using-a-third-party-provider-with-redirect) redirects the user to the provider. When the third-party provider successfully authenticates the user, the provider redirects the user to the Supabase Auth callback URL where they are further redirected to the URL specified in the `redirectTo` parameter. This parameter defaults to the [`SITE_URL`](/docs/reference/auth/config#site_url). You can modify the `SITE_URL` or [add additional redirect URLs](https://app.supabase.com/project/_/auth/settings).
You can use wildcard match patterns to support preview URLs from providers like Netlify and Vercel. See the [full list of supported patterns](https://pkg.go.dev/github.com/gobwas/glob#Compile).
#### Netlify preview URLs
For deployments with Netlify, set the `SITE_URL` to your official site URL. Add the following additional redirect URLs for local development and deployment previews:
- `http://localhost:3000/*/*`
- `https://**--my_org.netlify.app/*`
#### Vercel preview URLs
For deployments with Vercel, set the `SITE_URL` to your official site URL. Add the following additional redirect URLs for local development and deployment previews:
- `http://localhost:3000/*/*`
- `https://**vercel.app/*/*`
Vercel provides an environment variable for the URL of the deployment called `NEXT_PUBLIC_VERCEL_URL`. See the [Vercel docs](https://vercel.com/docs/concepts/projects/environment-variables#system-environment-variables) for more details. You can use this variable to dynamically redirect depending on the environment:
```js
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'github'
options: {
redirectTo: process.env.NEXT_PUBLIC_VERCEL_URL
? `https://${process.env.NEXT_PUBLIC_VERCEL_URL}`
: "http://localhost:3000"
}
}
```
#### Mobile deep linking URIs
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.
## Authorization
When you need granular authorization rules, nothing beats PostgreSQL's Row Level Security (RLS).
@@ -120,3 +159,7 @@ When users sign up, Supabase assigns them a unique ID. You can reference this ID
<video width="99%" muted playsInline controls="true">
<source src="/docs/videos/auth-zoom2.mp4" type="video/mp4" muted playsInline />
</video>
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,10 @@
---
id: auth-apple
title: 'Login with Apple'
description: Add Apple OAuth to your Supabase project
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'auth-apple',
title: 'Login with Apple',
description: 'Add Apple OAuth to your Supabase project',
}
To enable Apple Auth for your project, you need to set up an Apple OAuth application and add the application credentials to your Supabase Dashboard.
@@ -202,3 +201,7 @@ async function signout() {
- [Ruby](https://www.ruby-lang.org/en/) Docs.
- [ruby-jwt](https://github.com/jwt/ruby-jwt) library.
- Thanks to [Janak Amarasena](https://medium.com/@janakda) who did all the heavy lifting in [How to configure Sign In with Apple](https://medium.com/identity-beyond-borders/how-to-configure-sign-in-with-apple-77c61e336003).
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,10 @@
---
id: auth-azure
title: 'Login with Azure'
description: Add Azure OAuth to your Supabase project
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'auth-azure',
title: 'Login with Azure',
description: 'Add Azure OAuth to your Supabase project',
}
To enable Azure Auth for your project, you need to set up an Azure OAuth application and add the application credentials to your Supabase Dashboard.
@@ -98,3 +97,7 @@ async function signout() {
- [Azure Developer Account](https://portal.azure.com)
- [GitHub Discussion](https://github.com/supabase/gotrue/pull/54#issuecomment-757043573)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,10 @@
---
id: auth-bitbucket
title: 'Login with Bitbucket'
description: Add Bitbucket OAuth to your Supabase project
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'auth-bitbucket',
title: 'Login with Bitbucket',
description: 'Add Bitbucket OAuth to your Supabase project',
}
To enable Bitbucket Auth for your project, you need to set up a BitBucket OAuth application and add the application credentials to your Supabase Dashboard.
@@ -90,3 +89,7 @@ async function signout() {
- [Supabase Account - Free Tier OK](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Bitbucket Account](https://bitbucket.org)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,14 +1,22 @@
---
id: auth-captcha
title: 'Enable Captcha Protection'
description: Add Captcha Protection to your Supabase project
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'auth-captcha',
title: 'Enable Captcha Protection',
description: 'Add Captcha Protection to your Supabase project',
}
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/).
<div className="video-container">
<iframe
src="https://www.youtube-nocookie.com/embed/em1cpOAXknM"
frameBorder="1"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
## Sign up for hCaptcha
Go to the [hCaptcha](https://www.hcaptcha.com/) website and sign up for an account. On the welcome page, copy the **Sitekey** and **Secret key**.
@@ -109,3 +117,7 @@ captcha.current.resetCaptcha()
In order to test that this works locally we will need to use something like [ngrok](https://ngrok.com/) or add an entry to your hosts file. You can read more about this in the [hCaptcha docs](https://docs.hcaptcha.com/#local-development).
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,10 @@
---
id: auth-discord
title: 'Login with Discord'
description: Add Discord OAuth to your Supabase project
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'auth-discord',
title: 'Login with Discord',
description: 'Add Discord OAuth to your Supabase project',
}
To enable Discord Auth for your project, you need to set up a Discord Application and add the Application OAuth credentials to your Supabase Dashboard.
@@ -92,3 +91,7 @@ async function signout() {
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Discord Account](https://discord.com)
- [Discord Developer Portal](https://discord.com/developers)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,10 @@
---
id: auth-email
title: 'Login With Email'
description: Use Supabase to Authenticate and Authorize your users using email.
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'auth-email',
title: 'Login With Email',
description: 'Use Supabase to Authenticate and Authorize your users using email.',
}
## Overview
@@ -99,3 +98,7 @@ Future<void> signOut() async {
- [Supabase Account - Free Tier 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,10 @@
---
id: auth-facebook
title: 'Login with Facebook'
description: Add Facebook OAuth to your Supabase project
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'auth-facebook',
title: 'Login with Facebook',
description: 'Add Facebook OAuth to your Supabase project',
}
To enable Facebook Auth for your project, you need to set up a Facebook OAuth application and add the application credentials to your Supabase Dashboard.
@@ -107,3 +106,7 @@ async function signout() {
- [Supabase Account - Free Tier OK](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Facebook Developers Dashboard](https://developers.facebook.com/)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,10 @@
---
id: auth-github
title: 'Login with GitHub'
description: Add GitHub OAuth to your Supabase project
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'auth-github',
title: 'Login with GitHub',
description: 'Add GitHub OAuth to your Supabase project',
}
To enable GitHub Auth for your project, you need to set up a GitHub OAuth application and add the application credentials to your Supabase Dashboard.
@@ -100,3 +99,7 @@ async function signout() {
- [Supabase Account - Free Tier OK](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [GitHub Developer Settings](https://github.com/settings/developers)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,10 @@
---
id: auth-gitlab
title: 'Login with GitLab'
description: Add GitLab OAuth to your Supabase project
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'auth-gitlab',
title: 'Login with GitLab',
description: 'Add GitLab OAuth to your Supabase project',
}
To enable GitLab Auth for your project, you need to set up a GitLab OAuth application and add the application credentials to your Supabase Dashboard.
@@ -87,3 +86,7 @@ async function signout() {
- [Supabase Account - Free Tier OK](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [GitLab Account](https://gitlab.com)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,10 @@
---
id: auth-google
title: 'Login with Google'
description: Add Google OAuth to your Supabase project
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'auth-google',
title: 'Login with Google',
description: 'Add Google OAuth to your Supabase project',
}
To enable Google Auth for your project, you need to set up a Google OAuth application and add the application credentials to your Supabase Dashboard.
@@ -115,3 +114,7 @@ async function signout() {
- [Supabase Account - Free Tier OK](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Google Cloud Platform Console](https://console.cloud.google.com/home/dashboard)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,9 +1,11 @@
---
id: index
title: Auth Helpers
description: A collection of framework-specific Auth utilities for working with Supabase.
sidebar_label: Overview
---
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'index',
title: 'Auth Helpers',
description: 'A collection of framework-specific Auth utilities for working with Supabase.',
sidebar_label: 'Overview',
}
A collection of framework-specific Auth utilities for working with Supabase.
@@ -52,3 +54,7 @@ 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,8 +1,10 @@
---
id: auth-ui
title: Auth UI
description: A prebuilt, customizable React component for authenticating users.
---
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
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.
@@ -273,3 +275,7 @@ const App = () => (
/>
)
```
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,12 +1,11 @@
---
id: nextjs
title: Supabase Auth with Next.js
description: Authentication helpers for Next.js API routes, middleware, and SSR.
sidebar_label: 'Next.js'
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'nextjs',
title: 'Supabase Auth with Next.js',
description: 'Authentication helpers for Next.js API routes, middleware, and SSR.',
sidebar_label: 'Next.js',
}
This submodule provides convenience helpers for implementing user authentication in Next.js applications.
@@ -838,3 +837,7 @@ import { Database } from '../database.types'
const supabaseClient = useSupabaseClient<Database>()
```
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,12 +1,11 @@
---
id: remix
title: Supabase Auth with Remix
description: Authentication helpers for loaders and actions in Remix.
sidebar_label: 'Remix'
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'remix',
title: 'Supabase Auth with Remix',
description: 'Authentication helpers for loaders and actions in Remix.',
sidebar_label: 'Remix',
}
This submodule provides convenience helpers for implementing user authentication in Remix applications.
@@ -877,3 +876,7 @@ export default function ProtectedPage() {
return <pre>{JSON.stringify({ user, allRepos }, null, 2)}</pre>;
}
```
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,12 +1,11 @@
---
id: sveltekit
title: Supabase Auth with SvelteKit
description: Convenience helpers for implementing user authentication in SvelteKit.
sidebar_label: SvelteKit
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'sveltekit',
title: 'Supabase Auth with SvelteKit',
description: 'Convenience helpers for implementing user authentication in SvelteKit.',
sidebar_label: 'SvelteKit',
}
This submodule provides convenience helpers for implementing user authentication in [SvelteKit](https://kit.svelte.dev/) applications.
@@ -53,92 +52,82 @@ PUBLIC_SUPABASE_ANON_KEY=your-anon-key
Start off by creating a `db.ts` file inside of the `src/lib` directory and instantiate the `supabaseClient`.
```ts title=src/lib/db.ts
import { createClient } from '@supabase/supabase-js'
import { setupSupabaseHelpers } from '@supabase/auth-helpers-sveltekit'
import { dev } from '$app/environment'
import { createClient } from '@supabase/auth-helpers-sveltekit'
import { env } from '$env/dynamic/public'
// or use the static env
// import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public';
export const supabaseClient = createClient(env.PUBLIC_SUPABASE_URL, env.PUBLIC_SUPABASE_ANON_KEY, {
persistSession: false,
autoRefreshToken: false,
})
setupSupabaseHelpers({
supabaseClient,
cookieOptions: {
secure: !dev,
},
})
export const supabaseClient = createClient(env.PUBLIC_SUPABASE_URL, env.PUBLIC_SUPABASE_ANON_KEY)
```
### Initialize the client
To make sure the client is initialized on the server and the client, include this file in `src/hooks.server.js` and `src/hooks.client.js`:
Edit your `+layout.svelte` file and set up the client-side.
```ts
import '$lib/db'
```
### Synchronizing the page store
Edit your `+layout.svelte` file and set up the client side.
```html title=src/routes/+layout.svelte
<script lang="ts">
// make sure the supabase instance is initialized on the client
import '$lib/db'
import { startSupabaseSessionSync } from '@supabase/auth-helpers-sveltekit'
import { page } from '$app/stores'
import { invalidateAll } from '$app/navigation'
<script>
import { supabaseClient } from '$lib/db'
import { invalidate } from '$app/navigation'
import { onMount } from 'svelte'
// this sets up automatic token refreshing
startSupabaseSessionSync({
page,
handleRefresh: () => invalidateAll(),
onMount(() => {
const {
data: { subscription },
} = supabaseClient.auth.onAuthStateChange(() => {
invalidate('supabase:auth')
})
return () => {
subscription.unsubscribe()
}
})
</script>
<slot />
```
### Set up hooks
Every `PageLoad` or `LayoutLoad` using `getSupabase()` will update when `invalidate('supabase:auth')` is called.
The `hooks.server.ts` file is where the heavy lifting of this library happens:
```ts title=src/hooks.server.ts
// make sure the supabase instance is initialized on the server
import '$lib/db'
import { dev } from '$app/environment'
import { auth } from '@supabase/auth-helpers-sveltekit/server'
export const handle = auth()
// use the sequence helper if you have additional Handle methods
import { sequence } from '@sveltejs/kit/hooks'
export const handle = sequence(auth(), yourHandler)
```
There are three handle methods available:
- `callback()`:
This creates a handler for `/api/auth/callback`. The `client` forwards the session details here every time `onAuthStateChange` fires on the client-side. This is needed to set up the cookies for your application so that SSR works seamlessly.
- `session()`:
This parses the session from the cookie and populate it in locals
- `auth()`:
a shorthand for `sequence(callback(), session())` that uses both handlers
If some data is not updated on signin/signout you can fall back to `invalidateAll()`.
### Send session to client
In order to make the session available to the UI (e.g., pages and layouts), pass the session in the root layout load function:
To make the session available to the UI (pages, layouts), pass the session in the root layout server load function:
```ts title=src/routes/+layout.server.ts
import type { LayoutServerLoad } from './$types'
import { getServerSession } from '@supabase/auth-helpers-sveltekit'
export const load: LayoutServerLoad = async ({ locals }) => {
export const load: LayoutServerLoad = async (event) => {
return {
session: locals.session,
session: await getServerSession(event),
}
}
```
In addition you can create a layout load function if you are using `invalidate('supabase:auth')`:
```ts title=src/routes/+layout.ts
import type { LayoutLoad } from './$types'
import { getSupabase } from '@supabase/auth-helpers-sveltekit'
export const load: LayoutLoad = async (event) => {
const { session } = await getSupabase(event)
return { session }
}
```
This results in fewer server calls as the client manages the session on its own.
### Typings
In order to get the most out of TypeScript and IntelliSense, import the types into the `app.d.ts` type definition file that comes with your SvelteKit project.
In order to get the most out of TypeScript and it´s intellisense, you should import our types into the `app.d.ts` type definition file that comes with your SvelteKit project.
```ts title=src/app.d.ts
/// <reference types="@sveltejs/kit" />
@@ -147,58 +136,30 @@ In order to get the most out of TypeScript and IntelliSense, import the types in
// for information about these interfaces
// and what to do when importing types
declare namespace App {
interface Locals {
session: import('@supabase/auth-helpers-sveltekit').SupabaseSession
interface Supabase {
Database: import('./DatabaseDefinitions').Database
SchemaName: 'public'
}
// interface Locals {}
interface PageData {
session: import('@supabase/auth-helpers-sveltekit').SupabaseSession
session: import('@supabase/supabase-js').Session | null
}
// interface Error {}
// interface Platform {}
}
```
### Signing out
### Basic Setup
This library has provided a method to delete the session cookie.
Create a server only page with an action to delete the cookie
```ts title=src/routes/logout/+page.server.ts
import { deleteSession } from '@supabase/auth-helpers-sveltekit/server'
import { redirect } from '@sveltejs/kit'
import type { Actions } from './$types'
export const actions: Actions = {
async default({ cookies }) {
deleteSession(cookies)
throw redirect(303, '/')
},
}
```
Then make a **POST** request with a form to the server only page above.
```html
<script lang="ts">
import { enhanceAndInvalidate } from '@supabase/auth-helpers-sveltekit'
</script>
<form action="/logout" method="post" use:enhanceAndInvalidate>
<button type="submit">Sign out</button>
</form>
```
### Basic setup
You can now determine if a user is authenticated on the client-side by checking that the `user` object in `$page.data.session` is defined.
You can now determine if a user is authenticated on the client-side by checking that the `session` object in `$page.data` is defined.
```html title=src/routes/+page.svelte
<script>
import { page } from '$app/stores'
</script>
{#if !$page.data.session.user}
{#if !$page.data.session}
<h1>I am not logged in</h1>
{:else}
<h1>Welcome {$page.data.session.user.email}</h1>
@@ -208,7 +169,7 @@ You can now determine if a user is authenticated on the client-side by checking
## Client-side data fetching with RLS
For [row level security](/docs/learn/auth-deep-dive/auth-row-level-security) to work properly when fetching data client-side, you need to make sure to import the `{ supabaseClient }` from `$lib/db` and only run your query once the user is defined client-side in `$page.data.session`:
For [row level security](/docs/learn/auth-deep-dive/auth-row-level-security) to work properly when fetching data client-side, you need to make sure to import the `{ supabaseClient }` from `$lib/db` and only run your query once the session is defined client-side in `$page.data`:
```html
<script>
@@ -221,12 +182,12 @@ For [row level security](/docs/learn/auth-deep-dive/auth-row-level-security) to
loadedData = data
}
$: if ($page.data.session.user) {
$: if ($page.data.session) {
loadData()
}
</script>
{#if $page.data.session.user}
{#if $page.data.session}
<p>client-side data fetching with RLS</p>
<pre>{JSON.stringify(loadedData, null, 2)}</pre>
{/if}
@@ -246,47 +207,25 @@ For [row level security](/docs/learn/auth-deep-dive/auth-row-level-security) to
<pre>{JSON.stringify(user, null, 2)}</pre>
```
For [row level security](/docs/learn/auth-deep-dive/auth-row-level-security) to work in a server environment, you need to use the `withAuth` helper to check if the user is authenticated. The helper extends the event with `session` and `getSupabaseClient()`:
For [row level security](/docs/learn/auth-deep-dive/auth-row-level-security) to work in a server environment, you need to use the `getSupabase` helper to check if the user is authenticated. The helper requires the `event` and returns `session` and `supabaseClient`:
```ts title=src/routes/profile/+page.ts
import type { PageLoad } from './$types';
import { withAuth } from '@supabase/auth-helpers-sveltekit';
import { redirect } from '@sveltejs/kit';
import type { PageLoad } from './$types'
import { getSupabase } from '@supabase/auth-helpers-sveltekit'
import { redirect } from '@sveltejs/kit'
interface TestTable {
id: string;
created_at: string;
}
export const load: PageLoad = withAuth(async ({ getSupabaseClient, session }) => {
if (!session.user) {
throw redirect(303, '/');
export const load: PageLoad = async (event) => {
const { session, supabaseClient } = await getSupabase(event)
if (!session) {
throw redirect(303, '/')
}
const { data: tableData } = await getSupabaseClient()
.from<TestTable>('test')
.select('*');
const { data: tableData } = await supabaseClient.from('test').select('*')
return {
user: session.user,
tableData
};
);
```
**Caution:**
Always use the instance returned by `getSupabaseClient()` directly!
```ts
// Bad
const supabaseClient = getSupabaseClient()
await supabaseClient.from('table1').select()
await supabaseClient.from('table2').select()
// Good
await getSupabaseClient().from('table1').select()
await getSupabaseClient().from('table2').select()
tableData,
}
}
```
## Protecting API routes
@@ -294,41 +233,37 @@ await getSupabaseClient().from('table2').select()
Wrap an API Route to check that the user has a valid session. If they're not logged in the session is `null`.
```ts title=src/routes/api/protected-route/+server.ts
import type { RequestHandler } from './$types';
import { withAuth } from '@supabase/auth-helpers-sveltekit';
import { json, redirect } from '@sveltejs/kit';
import type { RequestHandler } from './$types'
import { getSupabase } from '@supabase/auth-helpers-sveltekit'
import { json, redirect } from '@sveltejs/kit'
interface TestTable {
id: string;
created_at: string;
}
export const GET: RequestHandler = withAuth(async ({ session, getSupabaseClient }) => {
if (!session.user) {
throw redirect(303, '/');
export const GET: RequestHandler = async (event) => {
const { session, supabaseClient } = await getSupabase(event)
if (!session) {
throw redirect(303, '/')
}
const { data } = await getSupabaseClient()
.from<TestTable>('test')
.select('*');
const { data } = await supabaseClient.from('test').select('*')
return json({ data });
);
return json({ data })
}
```
If you visit `/api/protected-route` without a valid session cookie, you will get a 303 response.
## Protecting Actions
Wrap an Action to check that the user has a valid session. If they're not logged in, the session is `null`.
Wrap an Action to check that the user has a valid session. If they're not logged in the session is `null`.
```ts title=src/routes/posts/+page.server.ts
import type { Actions } from './$types'
import { withAuth } from '@supabase/auth-helpers-sveltekit'
import { getSupabase } from '@supabase/auth-helpers-sveltekit'
import { error, invalid } from '@sveltejs/kit'
export const actions: Actions = {
createPost: withAuth(async ({ session, getSupabaseClient, request }) => {
if (!session.user) {
createPost: async (event) => {
const { request } = event
const { session, supabaseClient } = await getSupabase(event)
if (!session) {
// the user is not signed in
throw error(403, { message: 'Unauthorized' })
}
@@ -336,7 +271,7 @@ export const actions: Actions = {
const formData = await request.formData()
const content = formData.get('content')
const { error: createPostError, data: newPost } = await getSupabaseClient()
const { error: createPostError, data: newPost } = await supabaseClient
.from('posts')
.insert({ content })
@@ -348,7 +283,7 @@ export const actions: Actions = {
return {
newPost,
}
}),
},
}
```
@@ -356,29 +291,29 @@ If you try to submit a form with the action `?/createPost` without a valid sessi
## Saving and deleting the session
Use `saveSession` to save the session cookies:
```ts
import type { Actions } from './$types'
import { supabaseClient } from '$lib/db'
import { invalid, redirect } from '@sveltejs/kit'
import { saveSession } from '@supabase/auth-helpers-sveltekit/server'
import { getSupabase } from '@supabase/auth-helpers-sveltekit'
export const actions: Actions = {
async signin({ request, cookies, url }) {
signin: async (event) => {
const { request, cookies, url } = event
const { session, supabaseClient } = await getSupabase(event)
const formData = await request.formData()
const email = formData.get('email') as string
const password = formData.get('password') as string
const { data, error } = await supabaseClient.auth.api.signInWithEmail(email, password, {
redirectTo: `${url.origin}/logging-in`,
const { error } = await supabaseClient.auth.signInWithPassword({
email,
password,
})
if (error || !data) {
if (error?.status === 400) {
if (error) {
if (error instanceof AuthApiError && error.status === 400) {
return invalid(400, {
error: 'Invalid credentials',
error: 'Invalid credentials.',
values: {
email,
},
@@ -392,63 +327,392 @@ export const actions: Actions = {
})
}
saveSession(cookies, data)
throw redirect(303, '/dashboard')
},
}
```
Use `deleteSession` to delete the session cookies:
```ts
import type { Actions } from './$types'
import { deleteSession } from '@supabase/auth-helpers-sveltekit/server'
import { redirect } from '@sveltejs/kit'
export const actions: Actions = {
async logout({ cookies }) {
deleteSession(cookies)
signout: async (event) => {
const { supabaseClient } = await getSupabase(event)
await supabaseClient.auth.signOut()
throw redirect(303, '/')
},
}
```
## Custom session namespace
## Protecting multiple routes
If you want to use something other than `locals.session` and `$page.data.session`, update the types and create three helper functions:
To avoid writing the same auth logic in every single route you can use the handle hook to
protect multiple routes at once.
```ts title=src/app.d.ts
declare namespace App {
interface Locals {
mySupabaseSession: import('@supabase/auth-helpers-sveltekit').SupabaseSession
```ts title=src/hooks.server.ts
import type { RequestHandler } from './$types'
import { getSupabase } from '@supabase/auth-helpers-sveltekit'
import { redirect, error } from '@sveltejs/kit'
export const handle: Handle = async ({ event, resolve }) => {
// protect requests to all routes that start with /protected-routes
if (event.url.pathname.startsWith('/protected-routes')) {
const { session, supabaseClient } = await getSupabase(event)
if (!session) {
throw redirect(303, '/')
}
}
interface PageData {
mySupabaseSession: import('@supabase/auth-helpers-sveltekit').SupabaseSession
// protect POST requests to all routes that start with /protected-posts
if (event.url.pathname.startsWith('/protected-posts') && event.request.method === 'POST') {
const { session, supabaseClient } = await getSupabase(event)
if (!session) {
throw error(303, '/')
}
}
return resolve(event)
}
```
```ts title=src/hooks.server.ts
setupSupabaseServer({
## Migrate from 0.7.x to 0.8 {#migration}
### Set up the Supabase client {#migration-set-up-supabase-client}
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="older-0.7"
>
<TabPanel id="older-0.7" label="0.7.x">
```js title=src/lib/db.ts
import { createClient } from '@supabase/supabase-js'
import { setupSupabaseHelpers } from '@supabase/auth-helpers-sveltekit'
import { dev } from '$app/environment'
import { env } from '$env/dynamic/public'
// or use the static env
// import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public';
export const supabaseClient = createClient(env.PUBLIC_SUPABASE_URL, env.PUBLIC_SUPABASE_ANON_KEY, {
persistSession: false,
autoRefreshToken: false,
})
setupSupabaseHelpers({
supabaseClient,
cookieOptions: {
secure: !dev,
},
// --- change location within locals ---
getSessionFromLocals: (locals) => locals.mySupabaseSession,
setSessionToLocals: (locals, session) => (locals.mySupabaseSession = session),
})
```
```ts title=src/lib/db.ts
setupSupabaseClient({
supabaseClient,
// --- change location within pageData ---
getSessionFromPageData: (data) => data.mySupabaseSession,
</TabPanel>
<TabPanel id="0.8.0" label="0.8.0">
```js title=src/lib/db.ts
import { createClient } from '@supabase/auth-helpers-sveltekit'
import { env } from '$env/dynamic/public'
// or use the static env
// import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public';
export const supabaseClient = createClient(env.PUBLIC_SUPABASE_URL, env.PUBLIC_SUPABASE_ANON_KEY)
```
</TabPanel>
</Tabs>
### Initialize the client {#migration-initialize-client}
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="older-0.7"
>
<TabPanel id="older-0.7" label="0.7.x">
```html title=src/routes/+layout.svelte
<script lang="ts">
// make sure the supabase instance is initialized on the client
import '$lib/db'
import { startSupabaseSessionSync } from '@supabase/auth-helpers-sveltekit'
import { page } from '$app/stores'
import { invalidateAll } from '$app/navigation'
// this sets up automatic token refreshing
startSupabaseSessionSync({
page,
handleRefresh: () => invalidateAll(),
})
</script>
<slot />
```
</TabPanel>
<TabPanel id="0.8.0" label="0.8.0">
```html title=src/routes/+layout.svelte
<script>
import { supabaseClient } from '$lib/db'
import { invalidate } from '$app/navigation'
import { onMount } from 'svelte'
onMount(() => {
const {
data: { subscription },
} = supabaseClient.auth.onAuthStateChange(() => {
invalidate('supabase:auth')
})
return () => {
subscription.unsubscribe()
}
})
</script>
<slot />
```
</TabPanel>
</Tabs>
### Set up hooks {#migration-set-up-hooks}
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="older-0-7"
>
<TabPanel id="older-0-7" label="0.7.x">
```ts title=src/hooks.server.ts
// make sure the supabase instance is initialized on the server
import '$lib/db'
import { dev } from '$app/environment'
import { auth } from '@supabase/auth-helpers-sveltekit/server'
export const handle = auth()
```
**Optional** _if using additional handle methods_
```ts title=src/hooks.server.ts
// make sure the supabase instance is initialized on the server
import '$lib/db'
import { dev } from '$app/environment'
import { auth } from '@supabase/auth-helpers-sveltekit/server'
import { sequence } from '@sveltejs/kit/hooks'
export const handle = sequence(auth(), yourHandler)
```
</TabPanel>
<TabPanel id="0.8.0" label="0.8.0">
```ts title=src/hooks.server.ts
// make sure the supabase instance is initialized on the server
import '$lib/db'
```
```ts title=src/hooks.client.ts
// make sure the supabase instance is initialized on the client
import '$lib/db'
```
</TabPanel>
</Tabs>
### Typings {#migration-typings}
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="older-0-7"
>
<TabPanel id="older-0-7" label="0.7.x">
```ts title=src/app.d.ts
/// <reference types="@sveltejs/kit" />
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
// and what to do when importing types
declare namespace App {
interface Locals {
session: import('@supabase/auth-helpers-sveltekit').SupabaseSession
}
interface PageData {
session: import('@supabase/auth-helpers-sveltekit').SupabaseSession
}
// interface Error {}
// interface Platform {}
}
```
</TabPanel>
<TabPanel id="0.8.0" label="0.8.0">
```ts title=src/app.d.ts
/// <reference types="@sveltejs/kit" />
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
// and what to do when importing types
declare namespace App {
interface Supabase {
Database: import('./DatabaseDefinitions').Database
SchemaName: 'public'
}
// interface Locals {}
interface PageData {
session: import('@supabase/auth-helpers-sveltekit').SupabaseSession
}
// interface Error {}
// interface Platform {}
}
```
</TabPanel>
</Tabs>
### withPageAuth {#migration-with-page-auth}
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="older-0-7"
>
<TabPanel id="older-0-7" label="0.7.x">
```html title=src/routes/protected-route/+page.svelte
<script lang="ts">
import type { PageData } from './$types'
export let data: PageData
$: ({ tableData, user } = data)
</script>
<div>Protected content for {user.email}</div>
<p>server-side fetched data with RLS:</p>
<pre>{JSON.stringify(tableData, null, 2)}</pre>
<p>user:</p>
<pre>{JSON.stringify(user, null, 2)}</pre>
```
```ts title=src/routes/protected-route/+page.ts
import { withAuth } from '@supabase/auth-helpers-sveltekit'
import { redirect } from '@sveltejs/kit'
import type { PageLoad } from './$types'
export const load: PageLoad = withAuth(async ({ session, getSupabaseClient }) => {
if (!session.user) {
throw redirect(303, '/')
}
const { data: tableData } = await getSupabaseClient().from('test').select('*')
return { tableData, user: session.user }
})
```
## Migrate from 0.6.11 and below to 0.7.0 {#migration}
</TabPanel>
<TabPanel id="0.8.0" label="0.8.0">
```html title=src/routes/protected-route/+page.svelte
<script>
/** @type {import('./$types').PageData} */
export let data
$: ({ user, tableData } = data)
</script>
<div>Protected content for {user.email}</div>
<pre>{JSON.stringify(tableData, null, 2)}</pre>
<pre>{JSON.stringify(user, null, 2)}</pre>
```
```ts title=src/routes/protected-route/+page.ts
// src/routes/profile/+page.ts
import type { PageLoad } from './$types'
import { getSupabase } from '@supabase/auth-helpers-sveltekit'
import { redirect } from '@sveltejs/kit'
export const load: PageLoad = async (event) => {
const { session, supabaseClient } = await getSupabase(event)
if (!session) {
throw redirect(303, '/')
}
const { data: tableData } = await supabaseClient.from('test').select('*')
return {
user: session.user,
tableData,
}
}
```
</TabPanel>
</Tabs>
### withApiAuth {#migration-with-api-auth}
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="older-0-7"
>
<TabPanel id="older-0-7" label="0.7.x">
```ts title=src/routes/api/protected-route/+server.ts
import type { RequestHandler } from './$types'
import { withAuth } from '@supabase/auth-helpers-sveltekit'
import { json, redirect } from '@sveltejs/kit'
interface TestTable {
id: string
created_at: string
}
export const GET: RequestHandler = withAuth(async ({ session, getSupabaseClient }) => {
if (!session.user) {
throw redirect(303, '/')
}
const { data } = await getSupabaseClient().from<TestTable>('test').select('*')
return json({ data })
})
```
</TabPanel>
<TabPanel id="0.8.0" label="0.8.0">
```ts title=src/routes/api/protected-route/+server.ts
import type { RequestHandler } from './$types'
import { getSupabase } from '@supabase/auth-helpers-sveltekit'
import { json, redirect } from '@sveltejs/kit'
export const GET: RequestHandler = async (event) => {
const { session, supabaseClient } = await getSupabase(event)
if (!session) {
throw redirect(303, '/')
}
const { data } = await supabaseClient.from('test').select('*')
return json({ data })
}
```
</TabPanel>
</Tabs>
## Migrate from 0.6.11 and below to 0.7.0 {#migration-0-7}
There are numerous breaking changes in the latest 0.7.0 version of this library.
@@ -456,7 +720,7 @@ There are numerous breaking changes in the latest 0.7.0 version of this library.
The environment variable prefix is now `PUBLIC_` instead of `VITE_` (e.g., `VITE_SUPABASE_URL` is now `PUBLIC_SUPABASE_URL`).
### Set up the Supabase client {#migration-set-up-supabase-client}
### Set up the Supabase client {#migration-set-up-supabase-client-0-7}
<Tabs
scrollable
@@ -478,7 +742,7 @@ export { supabaseClient };
```
</TabPanel>
<TabPanel id="latest" label="0.7.0">
<TabPanel id="0.7.0" label="0.7.0">
```js title=src/lib/db.ts
import { createClient } from '@supabase/supabase-js'
@@ -505,7 +769,7 @@ setupSupabaseHelpers({
</TabPanel>
</Tabs>
### Initialize the client {#migration-initialize-client}
### Initialize the client {#migration-initialize-client-0-7}
<Tabs
scrollable
@@ -528,7 +792,7 @@ setupSupabaseHelpers({
```
</TabPanel>
<TabPanel id="latest" label="0.7.0">
<TabPanel id="0.7.0" label="0.7.0">
The `@supabase/auth-helpers-svelte` library is no longer required as the `@supabase/auth-helpers-sveltekit` library handles all the client-side code.
@@ -553,7 +817,7 @@ The `@supabase/auth-helpers-svelte` library is no longer required as the `@supab
</TabPanel>
</Tabs>
### Set up hooks {#migration-set-up-hooks}
### Set up hooks {#migration-set-up-hooks-0-7}
<Tabs
scrollable
@@ -581,7 +845,7 @@ export const getSession: GetSession = async (event) => {
```
</TabPanel>
<TabPanel id="latest" label="0.7.0">
<TabPanel id="0.7.0" label="0.7.0">
```ts title=src/hooks.server.ts
// make sure the supabase instance is initialized on the server
@@ -607,7 +871,7 @@ export const handle = sequence(auth(), yourHandler)
</TabPanel>
</Tabs>
### Typings {#migration-typings}
### Typings {#migration-typings-0-7}
<Tabs
scrollable
@@ -639,7 +903,7 @@ declare namespace App {
```
</TabPanel>
<TabPanel id="latest" label="0.7.0">
<TabPanel id="0.7.0" label="0.7.0">
```ts title=src/app.d.ts
/// <reference types="@sveltejs/kit" />
@@ -688,7 +952,7 @@ declare namespace App {
```
</TabPanel>
<TabPanel id="latest" label="0.7.0">
<TabPanel id="0.7.0" label="0.7.0">
```html title=src/routes/+page.svelte
<script>
@@ -747,7 +1011,7 @@ declare namespace App {
```
</TabPanel>
<TabPanel id="latest" label="0.7.0">
<TabPanel id="0.7.0" label="0.7.0">
```html title=src/routes/protected-route/+page.svelte
<script lang="ts">
@@ -818,7 +1082,7 @@ export const GET: RequestHandler<GetOutput> = async ({ locals, request }) =>
```
</TabPanel>
<TabPanel id="latest" label="0.7.0">
<TabPanel id="0.7.0" label="0.7.0">
```ts title=src/routes/api/protected-route/+server.ts
import type { RequestHandler } from './$types';
@@ -852,3 +1116,7 @@ export const GET: RequestHandler = withAuth(async ({ session, getSupabaseClient
- [SvelteKit example](https://github.com/supabase/auth-helpers/tree/main/examples/sveltekit)
- [SvelteKit Email/Password example](https://github.com/supabase/auth-helpers/tree/main/examples/sveltekit-email-password)
- [SvelteKit Magiclink example](https://github.com/supabase/auth-helpers/tree/main/examples/sveltekit-magic-link)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,10 @@
---
id: auth-keycloak
title: 'Login with Keycloak'
description: Add Keycloak OAuth to your Supabase project
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'auth-keycloak',
title: 'Login with Keycloak',
description: 'Add Keycloak OAuth to your Supabase project',
}
To enable Keycloak Auth for your project, you need to set up an Keycloak OAuth application and add the application credentials to your Supabase Dashboard.
@@ -68,6 +67,9 @@ When your user signs in, call [signInWithOAuth()](/docs/reference/javascript/aut
async function signInWithKeycloak() {
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'keycloak',
options: {
scopes: 'openid',
},
})
}
```
@@ -84,3 +86,7 @@ async function signout() {
- You can find the keycloak openid endpoint configuration under the realm settings.
![Keycloak OpenID Endpoint Configuration](/docs/img/guides/auth-keycloak/keycloak-openid-endpoint-config.png)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,10 @@
---
id: auth-linkedin
title: 'Login with LinkedIn'
description: Add LinkedIn OAuth to your Supabase project
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'auth-linkedin',
title: 'Login with LinkedIn',
description: 'Add LinkedIn OAuth to your Supabase project',
}
To enable LinkedIn Auth for your project, you need to set up a LinkedIn OAuth application and add the application credentials to your Supabase Dashboard.
@@ -86,3 +85,7 @@ async function signout() {
- [Supabase Account - Free Tier OK](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [LinkedIn Developer Dashboard](https://api.LinkedIn.com/apps)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,10 @@
---
id: auth-magic-link
title: 'Login With Magic Link'
description: Use Supabase to authenticate and authorize your users using magic links.
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'auth-magic-link',
title: 'Login With Magic Link',
description: 'Use Supabase to authenticate and authorize 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. By default, a user can only request a magic link once every 60 seconds.
@@ -91,3 +90,7 @@ Future<void> signOut() async {
- [Supabase Account - Free Tier 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,10 @@
---
id: auth-messagebird
title: Phone Auth with MessageBird
description: How to set up and use Mobile OTP with MessageBird and Supabase.
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'auth-messagebird',
title: 'Phone Auth with MessageBird',
description: 'How to set up and use Mobile OTP with MessageBird and Supabase.',
}
## Overview
@@ -281,3 +280,7 @@ The user does not have a password therefore will need to sign in via this method
- [MessageBird Signup](https://dashboard.messagebird.com/en/sign-up)
- [Supabase Dashboard](https://app.supabase.com)
- [Supabase Row Level Security](/docs/guides/auth#row-level-security)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,11 @@
---
id: auth-mfa
title: 'Multi-Factor Authentication'
description: Add an additional layer of security to your apps with Supabase Auth multi-factor authentication.
---
import Layout from '~/layouts/DefaultGuideLayout'
import { Mermaid } from 'mdx-mermaid/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.',
}
<Admonition type="note">
@@ -26,9 +27,9 @@ prone to malicious account takeovers. These can be prevented with
MFA because they require the user to provide proof of
both of these:
- Something they know.
- Something they know.
Password, or access to a social-login account.
- Something they have.
- Something they have.
Access to an authenticator app (a.k.a. TOTP), mobile phone or recovery code.
## Overview
@@ -39,9 +40,9 @@ authenticator app in the control of users.
Applications using MFA require two important flows:
1. **Enrollment flow.**
1. **Enrollment flow.**
This lets users set up and control MFA in your app.
2. **Authentication flow.**
2. **Authentication flow.**
This lets users sign in using any factors after the conventional login step.
Supabase Auth provides:
@@ -78,11 +79,11 @@ Level](https://pages.nist.gov/800-63-3-Implementation-Resources/63B/AAL/), a
standard measure about the assurance Supabase Auth has of the user's identity
for that particular session. There are two levels recognized today:
1. **Assurance Level 1: `aal1`**
1. **Assurance Level 1: `aal1`**
Means that the user's identity was verified using a conventional login method
such as email+password, magic link, one-time password, phone auth or social
login.
2. **Assurance Level 2: `aal2`**
2. **Assurance Level 2: `aal2`**
Means that the user's identity was additionally verified using at least one
second factor, such as a TOTP code.
@@ -95,15 +96,15 @@ for your application. JWTs without an `aal` claim are at the `aal1` level.
Adding MFA to your app involves these three steps:
1. **Add enrollment flow.**
1. **Add enrollment flow.**
You need to provide a UI within your app that your users will be able to set-up
MFA in. You can add this right after sign-up, or as part of a separate flow in
the settings portion of your app.
2. **Add challenge step to login.**
2. **Add challenge step to login.**
If a user has set-up MFA, your app's login flow needs to present a challenge
screen to the user asking them to prove they have access to the additional
factor.
3. **Enforce rules for MFA logins.**
3. **Enforce rules for MFA logins.**
Once your users have a way to enroll and log in with MFA, you need to enforce
authorization rules across your app: on the frontend, backend, API servers or
Row-Level Security policies.
@@ -113,12 +114,12 @@ Adding MFA to your app involves these three steps:
An enrollment flow provides a UI for users to set up additional authentication factors.
Most applications add the enrollment flow in two places within their app:
1. Right after login or sign up.
1. Right after login or sign up.
This lets users quickly set up MFA immediately after they log in or create an
account. We recommend encouraging all users to set up MFA if that makes sense
for your application. Many applications offer this as an opt-in step in an
effort to reduce onboarding friction.
2. From within a settings page.
2. From within a settings page.
Allows users to set up, disable or modify their MFA settings.
We recommend building one generic flow that you can reuse in both cases with
@@ -126,15 +127,15 @@ minor modifications.
Enrolling a factor for use with MFA takes three steps:
1. Call `supabase.auth.mfa.enroll()`.
1. Call `supabase.auth.mfa.enroll()`.
This method returns a QR code and a secret. Display the QR
code to the user and ask them to scan it with their authenticator application.
If they are unable to scan the QR code, show the secret in plain text which
they can type or paste into their authenticator app.
2. Calling the `supabase.auth.mfa.challenge()` API.
2. Calling the `supabase.auth.mfa.challenge()` API.
This prepares Supabase Auth to accept a verification code from the user
and returns a challenge ID.
3. Calling the `supabase.auth.mfa.verify()` API.
3. Calling the `supabase.auth.mfa.verify()` API.
This verifies that the user has indeed added the secret from step (1) into
their app and is working correctly. If the verification succeeds, the factor
immediately becomes active for the user account. If not, you should repeat
@@ -400,15 +401,15 @@ application's database, APIs and server-side rendering.
Depending on your application's needs, there are three ways you can choose to
enforce MFA.
1. **Enforce for all users (new and existing).**
Any user account will have to enroll MFA to continue using your app.
1. **Enforce for all users (new and existing).**
Any user account will have to enroll MFA to continue using your app.
The application will not allow access without going through MFA first.
2. **Enforce for new users only.**
2. **Enforce for new users only.**
Only new users will be forced to enroll MFA, while old users will be encouraged
to do so.
to do so.
The application will not allow access for new users without going through MFA
first.
3. **Enforce only for users that have opted-in.**
3. **Enforce only for users that have opted-in.**
Users that want MFA can enroll in it and the application will not allow access
without going through MFA first.
@@ -547,14 +548,14 @@ using Row Level Security policies will give you sufficient protection. In the
event that you have other APIs that you wish to protect, follow these general
guidelines:
1. **Use a good JWT verification and parsing library for your language.**
1. **Use a good JWT verification and parsing library for your language.**
This will let you securely parse JWTs and extract their claims.
2. **Retrieve the `aal` claim from the JWT and compare its value according to
your needs.**
your needs.**
If you've encountered an AAL level that can be increased, ask the user to
continue the login process instead of logging them out.
3. **Use the `https://<project-ref>.supabase.co/rest/v1/auth/factors` REST
endpoint to identify if the user has enrolled any MFA factors.**
endpoint to identify if the user has enrolled any MFA factors.**
Only `verified` factors should be acted upon.
## Frequently asked questions
@@ -627,3 +628,7 @@ Currently recognized methods are:
- `mfa/totp` - a TOTP additional factor.
This list will expand in the future.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,10 @@
---
id: auth-notion
title: 'Login with Notion'
description: Add Notion OAuth to your Supabase project
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'auth-notion',
title: 'Login with Notion',
description: 'Add Notion OAuth to your Supabase project',
}
To enable Notion Auth for your project, you need to set up a Notion Application and add the Application OAuth credentials to your Supabase Dashboard.
@@ -90,3 +89,7 @@ async function signout() {
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Notion Account](https://notion.so)
- [Notion Developer Portal](https://www.notion.so/my-integrations)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,10 @@
---
id: auth-slack
title: 'Login with Slack'
description: Add Slack OAuth to your Supabase project
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'auth-slack',
title: 'Login with Slack',
description: 'Add Slack OAuth to your Supabase project',
}
To enable Slack Auth for your project, you need to set up a Slack OAuth application and add the application credentials to your Supabase Dashboard.
@@ -99,3 +98,7 @@ async function signout() {
- [Supabase Account - Free Tier OK](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Slack Developer Dashboard](https://api.slack.com/apps)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,10 @@
---
id: auth-spotify
title: 'Login with Spotify'
description: Add Spotify OAuth to your Supabase project
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'auth-spotify',
title: 'Login with Spotify',
description: 'Add Spotify OAuth to your Supabase project',
}
To enable Spotify Auth for your project, you need to set up a Spotify OAuth application and add the application credentials to your Supabase Dashboard.
@@ -94,3 +93,7 @@ async function signout() {
- [Supabase Account - Free Tier OK](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Spotify Developer Dashboard](https://developer.spotify.com/dashboard/)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,10 @@
---
id: auth-twilio
title: Phone Auth with Twilio
description: How to set up and use Mobile OTP with Twilio and Supabase.
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'auth-twilio',
title: 'Phone Auth with Twilio',
description: 'How to set up and use Mobile OTP with Twilio and Supabase.',
}
## Overview
@@ -300,3 +299,7 @@ The user does not have a password therefore will need to sign in via this method
- [Twilio Signup](https://www.twilio.com/try-twilio)
- [Supabase Dashboard](https://app.supabase.com)
- [Supabase Row Level Security](/docs/guides/auth#row-level-security)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,10 @@
---
id: auth-twitch
title: 'Login with Twitch'
description: Add Twitch OAuth to your Supabase project
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'auth-twitch',
title: 'Login with Twitch',
description: 'Add Twitch OAuth to your Supabase project',
}
To enable Twitch Auth for your project, you need to set up a Twitch Application and add the Application OAuth credentials to your Supabase Dashboard.
@@ -106,3 +105,7 @@ async function signout() {
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Twitch Account](https://twitch.tv)
- [Twitch Developer Console](https://dev.twitch.tv/console)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,10 @@
---
id: auth-twitter
title: 'Login with Twitter'
description: Add Twitter OAuth to your Supabase project
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'auth-twitter',
title: 'Login with Twitter',
description: 'Add Twitter OAuth to your Supabase project',
}
To enable Twitter Auth for your project, you need to set up a Twitter OAuth application and add the application credentials to your Supabase Dashboard.
@@ -95,3 +94,7 @@ async function signout() {
- [Supabase Account - Free Tier OK](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Twitter Developer Dashboard](https://developer.twitter.com/en/portal/dashboard)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,10 @@
---
id: auth-vonage
title: Phone Auth with Vonage
description: How to set up and use Mobile OTP with Vonage and Supabase.
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'auth-vonage',
title: 'Phone Auth with Vonage',
description: 'How to set up and use Mobile OTP with Vonage and Supabase.',
}
## Overview
@@ -272,3 +271,7 @@ The user does not have a password therefore will need to sign in via this method
- [Vonage Signup](https://dashboard.nexmo.com/sign-up)
- [Supabase Dashboard](https://app.supabase.com)
- [Supabase Row Level Security](/docs/guides/auth#row-level-security)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,10 @@
---
id: auth-workos
title: 'Login with WorkOS'
description: Add WorkOS OAuth to your Supabase project
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'auth-workos',
title: 'Login with WorkOS',
description: 'Add WorkOS OAuth to your Supabase project',
}
To enable WorkOS Auth for your project, you need to set up WorkOS OAuth application and add the application credentials to your Supabase Dashboard.
@@ -88,3 +87,7 @@ async function signout() {
## Resources
- [WorkOS Documentation](https://workos.com/docs/sso/guide)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,10 @@
---
id: auth-zoom
title: 'Login with Zoom'
description: Add Zoom OAuth to your Supabase project
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'auth-zoom',
title: 'Login with Zoom',
description: 'Add Zoom OAuth to your Supabase project',
}
To enable Zoom Auth for your project, you need to set up a Zoom OAuth application and add the application credentials to your Supabase Dashboard.
@@ -95,3 +94,7 @@ async function signout() {
- [Supabase Account - Free Tier OK](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Zoom App Marketplace](https://marketplace.zoom.us/)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,8 +1,10 @@
---
id: managing-user-data
title: Managing User Data
description: Securing your user data with Row Level Security.
---
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.',
}
For security purposes, the `auth` schema is not exposed on the auto-generated API.
@@ -59,9 +61,7 @@ The nice thing about this pattern? We can now query this table via the API and w
```js
// This will return nothing while the user is logged out
const { data } = await supabase
.from('profiles')
.select('id, username, avatar_url, website')
const { data } = await supabase.from('profiles').select('id, username, avatar_url, website')
// After the user is logged in, this will only return
// the logged-in user's data - in this case a single row
@@ -105,3 +105,7 @@ 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,8 +1,10 @@
---
id: row-level-security
title: Row Level Security
description: Secure your data using Postgres Row Level Security.
---
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'row-level-security',
title: 'Row Level Security',
description: 'Secure your data using Postgres Row Level Security.',
}
When you need granular authorization rules, nothing beats PostgreSQL's [Row Level Security (RLS)](https://www.postgresql.org/docs/current/ddl-rowsecurity.html).
@@ -363,3 +365,7 @@ Deprecated. Use `auth.jwt() ->> 'email'` instead.
</Admonition>
Returns the email of the user making the request.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,8 +1,10 @@
---
id: server-side-rendering
title: Server-Side Rendering
description: Render pages with user information on the server.
---
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
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.
@@ -48,7 +50,7 @@ server redirects the user back to your single-page app.
<Admonition type="tip">
You can configure [redirects URLs](https://app.supabase.com/project/_/auth/url-configuration) in the Supabase Dashboard. You can use wildcard match patterns
You can configure [redirects URLs](https://app.supabase.com/project/_/auth/url-configuration) in the Supabase Dashboard. You can use [wildcard match patterns](/docs/guides/auth#redirect-urls-and-wildcards)
like `*` and `**` to allow redirects to different forms of URLs.
</Admonition>
@@ -205,3 +207,7 @@ data belonging to different users!
Also be sure you set proper cache control headers. We recommend invalidating
cache keys every hour or less.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,13 @@
---
id: cli
title: Supabase CLI
description: The Supabase CLI provides tools to develop your project locally and deploy to the Supabase Platform.
sidebar_label: Overview
toc_max_heading_level: 2
---
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'cli',
title: 'Supabase CLI',
description:
'The Supabase CLI provides tools to develop your project locally and deploy to the Supabase Platform.',
sidebar_label: 'Overview',
toc_max_heading_level: 2,
}
The Supabase CLI provides tools to develop your project locally and deploy to the Supabase Platform.
You can also use the CLI to manage your Supabase projects, handle database migrations and CI/CD workflows, and generate types directly from your database schema.
@@ -20,6 +23,7 @@ You can also use the CLI to manage your Supabase projects, handle database migra
<TabPanel id="npm" label="npm">
Install the CLI as dev dependency via [npm](https://www.npmjs.com/package/supabase):
```sh
npm install supabase --save-dev
```
@@ -112,3 +116,7 @@ brew upgrade supabase
- [Supabase CLI Reference](/docs/reference/cli/usage)
- [Local Development](/docs/guides/cli/local-development)
- [Managing Environments](/docs/guides/cli/managing-environments)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,14 +1,22 @@
---
id: local-development
title: Local Development
description: How to use Supabase on your local development machine.
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'local-development',
title: 'Local Development',
description: 'How to use Supabase on your local development machine.',
}
Learn how to use the Supabase CLI to develop your project locally and deploy to the Supabase Platform.
<div className="video-container">
<iframe
src="https://www.youtube-nocookie.com/embed/vyHyYpvjaks"
frameBorder="1"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
## Prerequisites
Make sure you have these installed on your local machine:
@@ -46,7 +54,7 @@ git init
supabase init
```
Make sure Docker is running. The [start](/docs/reference/cli/usage#supabase-start) command uses Docker to start the Supabase [services](/architecture).
Make sure Docker is running. The [start](/docs/reference/cli/usage#supabase-start) command uses Docker to start the Supabase [services](/docs/architecture).
This command may take a while to run if this is the first time using the CLI.
```bash
@@ -123,6 +131,15 @@ http://localhost:54321/auth/v1/ # Auth (GoTrue)
Database changes are managed through "migrations." Database migrations are a common way of tracking changes to your database over time.
<div className="video-container">
<iframe
src="https://www.youtube-nocookie.com/embed/Kx5nHBmIxyQ"
frameBorder="1"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
### Make database changes
For this guide, create a table called `employees`. In Supabase Studio, navigate to the **SQL Editor** page and run the following SQL command:
@@ -238,3 +255,7 @@ The local development environment is not as feature-complete as the Supabase Pla
```
```
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,10 @@
---
id: managing-environments
title: Managing Environments
description: How to deploy Supabase schema changes with a CI / CD pipeline.
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'managing-environments',
title: 'Managing Environments',
description: 'How to deploy Supabase schema changes with a CI / CD pipeline.',
}
## Overview
@@ -19,6 +18,15 @@ To get started:
- Create a [Supabase project](https://app.supabase.com) or use an existing one
- Initialize a local Git repository
<div className="video-container">
<iframe
src="https://www.youtube-nocookie.com/embed/rOLyOsBR1Uc"
frameBorder="1"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
## Set up a local environment
The first step is to set up your local repository with the Supabase CLI:
@@ -415,3 +423,7 @@ supabase db reset
In case [`reset`](/docs/reference/cli/usage#supabase-db-reset) fails, you can manually resolve conflicts by editing `<t+2>_dev_A.sql` file.
Once validated locally, commit your changes to Git and push to GitHub.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,9 +1,11 @@
---
id: database
title: Database
description: Use Supabase to manage your data.
sidebar_label: Overview
---
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'database',
title: 'Database',
description: 'Use Supabase to manage your data.',
sidebar_label: 'Overview',
}
Every Supabase project comes with a full [Postgres](https://www.postgresql.org/) database, a free and open source
database which is considered one of the world's most stable and advanced databases.
@@ -85,3 +87,7 @@ Read about resetting your database password [here](/docs/guides/database/managin
- Read more about [Postgres](/docs/postgres/server/about)
- Sign in: [app.supabase.com](https://app.supabase.com)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,10 @@
---
id: arrays
title: 'Working With Arrays'
description: How to use arrays in PostgreSQL and the Supabase API.
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'arrays',
title: 'Working With Arrays',
description: 'How to use arrays in PostgreSQL and the Supabase API.',
}
PostgreSQL supports flexible [array types](https://www.postgresql.org/docs/12/arrays.html). These arrays are also supported in the Supabase Dashboard and in the JavaScript API.
@@ -160,3 +159,7 @@ returns:
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Supabase Account - Free Tier OK](https://supabase.com)
- [PostgreSQL Arrays](https://www.postgresql.org/docs/12/arrays.html)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,8 +1,10 @@
---
id: connecting-to-postgres
title: 'Database Connections'
description: There are various ways to connect to your Postgres database.
---
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'connecting-to-postgres',
title: 'Database Connections',
description: 'There are various ways to connect to your Postgres database.',
}
Supabase provides several options for programmatically connecting to your Postgres database:
@@ -121,7 +123,7 @@ SSL enabled as illustrated below:
psql "sslmode=verify-full sslrootcert=$HOME/Downloads/prod-ca-2021.cer host=db.abcdefghijklm.supabase.co dbname=postgres user=postgres"
```
2. With `pgAdmin`
2. With `pgAdmin`
a. Register a new Postgres server
![Register a new postgres server.](/docs/img/guides/database/register-server-pgAdmin.png)
@@ -133,3 +135,7 @@ psql "sslmode=verify-full sslrootcert=$HOME/Downloads/prod-ca-2021.cer host=db.a
file-picker modal. Select the certificate you downloaded from your Supabase dashboard and save the server details. PgAdmin
should now be able to connect to your Postgres via SSL.
![Add Connection Info.](/docs/img/guides/database/add-ssl-config.png)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,12 +1,10 @@
---
id: extensions
title: Overview
description: Using Postgres extensions.
---
import Layout from '~/layouts/DefaultGuideLayout'
import ExtensionsComponent from '@site/src/components/Extensions'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'extensions',
title: 'Overview',
description: 'Using Postgres extensions.',
}
Extensions are exactly as they sound - they "extend" the database with functionality which isn't part of the Postgres core.
Supabase has pre-installed some of the most useful open source extensions.
@@ -56,4 +54,8 @@ Enabling some extensions with `create extension <extension-name> with schema ext
Supabase is pre-configured with over 50 extensions. You can also install your own SQL extensions directly in the database through our SQL editor.
<ExtensionsComponent />
<Extensions />
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,10 @@
---
id: http
title: 'http: RESTful Client'
description: An HTTP Client for PostgreSQL Functions.
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'http',
title: 'http: RESTful Client',
description: 'An HTTP Client for PostgreSQL Functions.',
}
The `http` extension allows you to call RESTful endpoints within Postgres.
@@ -114,3 +113,7 @@ from
## Resources
- Official [`http` GitHub Repository](https://github.com/pramsey/pgsql-http)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,11 @@
---
id: pgcron
title: 'pg_cron: Job Scheduling'
description: 'pgnet: a simple cron-based job scheduler for PostgreSQL that runs inside the database.'
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'pgcron',
title: 'pg_cron: Job Scheduling',
description:
'pgnet: a simple cron-based job scheduler for PostgreSQL that runs inside the database.',
}
The `pg_cron` extension is a simple cron-based job scheduler for PostgreSQL that runs inside the database.
@@ -96,3 +96,7 @@ SELECT cron.unschedule('nightly-vacuum');
## Resources
- [pg_cron GitHub Repository](https://github.com/citusdata/pg_cron)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,10 @@
---
id: pgnet
title: 'pg_net: Async Networking'
description: 'pg_net: an async networking extension for PostgreSQL.'
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'pgnet',
title: 'pg_net: Async Networking',
description: 'pg_net: an async networking extension for PostgreSQL.',
}
<Admonition type="caution">
@@ -251,3 +250,7 @@ Possible values for `net.http_response_result.status` are `('PENDING', 'SUCCESS'
- Source code: [github.com/supabase/pg_net](https://github.com/supabase/pg_net/)
- Official Docs: [supabase.github.io/pg_net](https://supabase.github.io/pg_net/)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,10 @@
---
id: pgtap
title: 'pgTAP: Unit Testing'
description: Unit testing in PostgreSQL.
---
import Layout from '~/layouts/DefaultGuideLayout'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
export const meta = {
id: 'pgtap',
title: 'pgTAP: Unit Testing',
description: 'Unit testing in PostgreSQL.',
}
`pgTAP` is a unit testing extension for PostgreSQL.
@@ -156,3 +155,7 @@ API:
## Resources
- Official [`pgTAP` documentation](https://pgtap.org/)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
Loaded 100 of 1097 files, more files were not shown because too many files have changed in this diff. Show more