Password
+diff --git a/.github/workflows/publish_image.yml b/.github/workflows/publish_image.yml
index 84c8b8a88c2..6106dad6b42 100644
--- a/.github/workflows/publish_image.yml
+++ b/.github/workflows/publish_image.yml
@@ -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 }}
diff --git a/DEVELOPERS.md b/DEVELOPERS.md
index e427b76ed6f..c9e45c9d5e9 100644
--- a/DEVELOPERS.md
+++ b/DEVELOPERS.md
@@ -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
diff --git a/apps/docs/pages/guides/auth.mdx b/apps/docs/pages/guides/auth.mdx
index 93384ee69cf..75e514e5b7e 100644
--- a/apps/docs/pages/guides/auth.mdx
+++ b/apps/docs/pages/guides/auth.mdx
@@ -51,6 +51,43 @@ You can enable third-party providers with the click of a button by navigating to

+### 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).
diff --git a/apps/docs/pages/guides/auth/server-side-rendering.mdx b/apps/docs/pages/guides/auth/server-side-rendering.mdx
index e11d61cafc8..635407cd986 100644
--- a/apps/docs/pages/guides/auth/server-side-rendering.mdx
+++ b/apps/docs/pages/guides/auth/server-side-rendering.mdx
@@ -50,7 +50,7 @@ server redirects the user back to your single-page app.
{JSON.stringify(posts, null, 2)}
+}
+```
+
+Server Components support async/await by default, and suspend the rendering of the component until the data has been fetched. This means we don’t need to handle error or loading states in our component, keeping our rendering logic clean.
+
+> To learn more about displaying `loading` and `error` states, check out [the documentation](https://beta.nextjs.org/docs/api-reference/file-conventions/loading).
+
+Let’s modify this component to render out a collection of `` components, that navigate to a dedicated page for each post.
+
+```tsx title="app/static/page.tsx"
+import Link from 'next/link'
+import supabase from '../../utils/supabase'
+
+export default async function Posts() {
+ const { data: posts } = await supabase.from('posts').select('id, title')
+
+ if (!posts) {
+ return No posts found.
+ } + + return posts.map((post) => ( ++ {post.title} +
+ )) +} +``` + +> Since we are only using `id` and `title` in our component, we can scope our query down to only return these two columns for each post. + +Let’s create a dynamic route to handle displaying an individual post. Create a new file at `app/static/[id]/page.tsx` and populate with the following: + +```tsx title="app/static/[id]/page.tsx" +import supabase from '../../../utils/supabase' +import { notFound } from 'next/navigation' + +export default async function Post({ params: { id } }: { params: { id: string } }) { + const { data } = await supabase.from('posts').select().match({ id }).single() + + if (!data) { + notFound() + } + + return{JSON.stringify(data, null, 2)}
+}
+```
+
+Currently, this page is generated on-demand and then cached. This means the first person who visits the page will need to wait for the server to get the post data from Supabase. This won’t take long at all, because Supabase is Supa awesome! But, we can still make this _slightly_ more efficient by telling Next.js a finite collection of paths that we want to generate at build time.
+
+We do this by exporting out a `generateStaticParams` function from our dynamic page.
+
+```tsx title="app/static/[id]/page.tsx"
+export async function generateStaticParams() {
+ const { data: posts } = await supabase.from('posts').select('id')
+
+ return posts?.map(({ id }) => ({
+ id,
+ }))
+}
+```
+
+> This is similar to `getStaticPaths` in a pages component. Learn more [here](https://beta.nextjs.org/docs/data-fetching/generating-static-params).
+
+The full component should look something like this:
+
+```tsx title="app/static/[id]/page.tsx"
+import supabase from '../../../utils/supabase'
+import { notFound } from 'next/navigation'
+
+export async function generateStaticParams() {
+ const { data: posts } = await supabase.from('posts').select('id')
+
+ return posts?.map(({ id }) => ({
+ id,
+ }))
+}
+
+export default async function Post({ params: { id } }: { params: { id: string } }) {
+ const { data: post } = await supabase.from('posts').select().match({ id }).single()
+
+ if (!post) {
+ notFound()
+ }
+
+ return {JSON.stringify(post, null, 2)}
+}
+```
+
+Awesome! We now have a Supa snappy blog! The user never needs to wait for data to be fetched. All pages are statically generated at build time, and cached at CDN nodes close to our users! 🎉
+
+Unfortunately, this means any changes we make in Supabase — adding, updating or deleting posts etc — will not be reflected in our blog. If we want to refresh this data on a regular basis, we need to tell Next.js when to _revalidate_.
+
+# Static with Revalidation
+
+By exporting a `revalidate` variable from our component, we can specify how many seconds we consider this data to be “fresh”.
+
+```tsx
+export const revalidate = 60
+```
+
+> This is similar to returning a `revalidate` key from the `getStaticProps` function in a component from the `pages` directory.
+
+So, for 60 seconds Next.js will continue to respond with the static version of our page. After 60 seconds, it will fetch fresh data from Supabase and generate a new static page. However, there is no downtime while this happens, as the previous static page will continue to be served until the “fresh” one has been successfully generated.
+
+The `Posts` component should now look like this:
+
+```tsx title="app/static-with-revalidate/page.tsx"
+import Link from 'next/link'
+import supabase from '../../utils/supabase'
+
+export const revalidate = 60
+
+export default async function Posts() {
+ const { data: posts } = await supabase.from('posts').select('id, title')
+
+ if (!posts) {
+ return No posts found.
+ } + + return posts.map((post) => ( ++ {post.title} +
+ )) +} +``` + +And the `Post` component should look like this: + +```tsx title="app/static-with-revalidate/[id]/page.tsx" +import supabase from '../../../utils/supabase' +import { notFound } from 'next/navigation' + +export const revalidate = 60 + +export async function generateStaticParams() { + const { data: posts } = await supabase.from('posts').select('id') + + return posts?.map(({ id }) => ({ + id, + })) +} + +export default async function Post({ params: { id } }: { params: { id: string } }) { + const { data: post } = await supabase.from('posts').select().match({ id }).single() + + if (!post) { + notFound() + } + + return{JSON.stringify(post, null, 2)}
+}
+```
+
+We now get all the benefits of static — users not waiting around while data is fetched at request time — but we also get the benefits of dynamic data, as it is being refreshed on a regular basis.
+
+Very cool! 😎
+
+# Dynamic
+
+If we want fresh data to be fetched on every single request, we can simply set our `revalidate` value to `0`.
+
+**Posts Component**
+
+```tsx title="app/server-rendered/page.tsx"
+import Link from 'next/link'
+import supabase from '../../utils/supabase'
+
+export const revalidate = 0
+
+export default async function Posts() {
+ const { data: posts } = await supabase.from('posts').select('id, title')
+
+ if (!posts) {
+ return No posts found.
+ } + + return posts.map((post) => ( ++ {post.title} +
+ )) +} +``` + +**Single Post Component** + +```tsx title="app/server-rendered/[id]/page.tsx" +import supabase from '../../../utils/supabase' +import { notFound } from 'next/navigation' + +export const revalidate = 0 + +export async function generateStaticParams() { + const { data: posts } = await supabase.from('posts').select('id') + + return posts?.map(({ id }) => ({ + id, + })) +} + +export default async function Post({ params: { id } }: { params: { id: string } }) { + const { data: post } = await supabase.from('posts').select().match({ id }).single() + + if (!post) { + notFound() + } + + return{JSON.stringify(post, null, 2)}
+}
+```
+
+This is similar to exporting a `getServerSideProps` function from a component in the `pages` directory.
+
+All this server stuff is great, but what if you want to use Supabase client-side? 🤔
+
+# Client-side
+
+There are many use-cases where you need to use Supabase client-side:
+
+1. Authentication
+
+ Supabase Auth does a bunch of stuff behind the scenes — handling 3rd party OAuth flows, for example. This will break if you try to sign users in and out on the server.
+
+2. Realtime
+
+ Supabase manages the awesome power of websockets on your behalf — something that is not yet solved in this serverless world.
+
+3. You prefer it
+
+ There is nothing wrong with this! You do you!
+
+To use Supabase client-side, we need to tell Next.js that this is a [Client Component](https://beta.nextjs.org/docs/rendering/server-and-client-components#client-components). We do this by specifying the `use client` directive at the top of our component. This opts into a similar flow to the `pages` directory — the component is rendered on the server and hydrated client-side.
+
+> The React team is [working on an awesome new hook called "use"](https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md#example-use-in-client-components-and-hooks), which will drastically simplify fetching data client-side, but for now, we still need to rely on the combination of `useState` and `useEffect`.
+
+Let’s implement client-side data fetching.
+
+```tsx title="app/client-side/page.tsx"
+'use client'
+
+import { useEffect, useState } from 'react'
+import supabase from '../../utils/supabase'
+
+export default function ClientPosts() {
+ const [isLoading, setIsLoading] = useState(true)
+ const [posts, setPosts] = useStateLoading
:{JSON.stringify(posts, null, 2)}
+}
+```
+
+> Again, check out [how to generate types](https://supabase.com/docs/reference/javascript/typescript-support) to add proper typing for Post.
+
+But now we have loading spinners! Yuck!
+
+# Realtime
+
+Realtime allows us to subscribe to changes in Supabase — inserted, updated or deleted posts — and update our UI dynamically. In order to receive realtime events, we need to [enable replication](https://app.supabase.com/project/_/database/replication) on the posts table.
+
+Let’s merge the two previous concepts and fetch the initial state of our posts in a Server Component, and then render a Client Component to do client-y things — like subscribe to changes in the DB and update the UI dynamically:
+
+**Server Component**
+
+```tsx title="app/realtime/page.tsx"
+import supabase from '../../utils/supabase'
+import RealtimePosts from './realtime-posts'
+
+export const revalidate = 0
+
+export default async function Realtime() {
+ const { data } = await supabase.from('posts').select('*')
+ return {JSON.stringify(posts, null, 2)}
+}
+```
+
+> `useEffect` is used to subscribe to changes to the `serverPosts` prop. Without this, our component would not display fresh server-side results when the parent component is re-rendered, only on the first render.
+
+This is a great pattern for fetching initial data server-side and subscribing to realtime changes client-side. This will likely be replaced by the `use` hook once it is stable with Next.js — it also uses suspense to _suspend_ the rendering of a component while fetching data, and cleans up those loading and error states.
+
+# Conclusion
+
+Next.js 13 Server Components are awesome! Suspense is awesome! Async components are awesome!
+
+The combination of these concepts allow us to think about data fetching and caching as separate concerns, rather than specifying completely different data fetching functions like `getStaticProps` and `getServerSideProps`. If our caching requirements for a component change, we simply update the caching value, rather than refactoring our data fetching logic.
+
+Additionally, by allowing any component in the tree to be either a server or client component — that is responsible for its own data and suspends rendering until it is ready — drastically simplifies our code, and provides much more flexible patterns for creating maintainable applications as complexity grows.
+
+# More Next.js 13 Resources
+
+- [Next.js Quickstart](https://supabase.com/docs/guides/with-nextjs)
+- [Next.js 13 Beta Docs](https://beta.nextjs.org/docs)
+- [Creating an authenticated Supabase client in Next.js 13 Server Component](https://github.com/supabase/auth-helpers/tree/main/examples/nextjs-server-components)
+- [Data fetching and caching with Next.js 13 - Supabase Happy Hour #26](https://www.youtube.com/watch?v=QH0P5xZt5wY)
diff --git a/apps/www/public/images/blog/2022-11-17-supabase-nextjs-13/nextjs-supabase.jpg b/apps/www/public/images/blog/2022-11-17-supabase-nextjs-13/nextjs-supabase.jpg
new file mode 100644
index 00000000000..c5209939a3b
Binary files /dev/null and b/apps/www/public/images/blog/2022-11-17-supabase-nextjs-13/nextjs-supabase.jpg differ
diff --git a/docker/.env.example b/docker/.env.example
index aa0ee2f2148..2469e01fa6b 100644
--- a/docker/.env.example
+++ b/docker/.env.example
@@ -74,4 +74,4 @@ STUDIO_DEFAULT_ORGANIZATION=Default Organization
STUDIO_DEFAULT_PROJECT=Default Project
STUDIO_PORT=3000
-PUBLIC_REST_URL=http://localhost:8000/rest/v1/ # replace if you intend to use Studio outside of localhost
+SUPABASE_PUBLIC_URL=https://localhost:8443 # replace if you intend to use Studio outside of localhost
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index 96fbeb101b5..82e500c45ed 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -21,7 +21,9 @@ services:
DEFAULT_PROJECT: ${STUDIO_DEFAULT_PROJECT}
SUPABASE_URL: http://kong:8000
- SUPABASE_REST_URL: ${PUBLIC_REST_URL}
+ SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL}
+ # Kept for backwards compatibility with studio:0.22.08
+ SUPABASE_REST_URL: ${API_EXTERNAL_URL}/rest/v1/
SUPABASE_ANON_KEY: ${ANON_KEY}
SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY}
@@ -169,7 +171,7 @@ services:
# Comment out everything below this point if you are using an external Postgres database
db:
container_name: supabase-db
- image: supabase/postgres:14.1.0.82
+ image: supabase/postgres:14.1.0.89
healthcheck:
test: pg_isready -U postgres -h localhost
interval: 5s
diff --git a/examples/user-management/flutter-user-management/README.md b/examples/user-management/flutter-user-management/README.md
index 13d06cbf26e..38f7169d129 100644
--- a/examples/user-management/flutter-user-management/README.md
+++ b/examples/user-management/flutter-user-management/README.md
@@ -7,7 +7,7 @@ This repo will demonstrate how to:
- store and retrieve data with [Supabase database](https://supabase.io/docs/guides/database)
- store image files in [Supabase storage](https://supabase.io/docs/guides/storage)
-
+
## Getting Started
diff --git a/spec/supabase_js_v2.yml b/spec/supabase_js_v2.yml
index 40d534300f0..942fcaa80a5 100644
--- a/spec/supabase_js_v2.yml
+++ b/spec/supabase_js_v2.yml
@@ -214,8 +214,8 @@ pages:
- name: Sign in using a third-party provider with redirect
isSpotlight: false
description: |
- When the third-party provider successfully authenticates the user, the provider will redirect the user to the URL specified in the `redirectTo` parameter. This parameter defaults to the [`SITE_URL`](https://supabase.com/docs/reference/auth/config#site_url). It does not redirect the user immediately after invoking this method.
- You can modify the `SITE_URL` or add additional redirect urls in [your project](https://app.supabase.com/project/_/auth/settings).
+ 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](/docs/guides/auth#redirect-urls-and-wildcards) to support preview URLs from providers like Netlify and Vercel.
js: |
```js
const { data, error } = await supabase.auth.signInWithOAuth({
diff --git a/studio/.env b/studio/.env
index 478baf5dc20..d872e21ab40 100644
--- a/studio/.env
+++ b/studio/.env
@@ -5,7 +5,12 @@ DEFAULT_ORGANIZATION_NAME=Default Organization
DEFAULT_PROJECT_NAME=Default Project
SUPABASE_URL=http://localhost:8000
-SUPABASE_REST_URL=http://localhost:8000/rest/v1/
+SUPABASE_PUBLIC_URL=https://localhost:8443
SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE
SUPABASE_SERVICE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q
+# TODO: evaluate these variables at runtime instead
+# https://nextjs.org/docs/basic-features/environment-variables#loading-environment-variables
+NEXT_PUBLIC_SITE_URL=http://localhost:3000
+NEXT_PUBLIC_GOTRUE_URL=$SUPABASE_URL/auth/v1
+NEXT_PUBLIC_HCAPTCHA_SITE_KEY=10000000-ffff-ffff-ffff-000000000001
diff --git a/studio/components/interfaces/Auth/AutoSchemaForm.tsx b/studio/components/interfaces/Auth/AutoSchemaForm.tsx
index 36fc149755d..d553fcf588d 100644
--- a/studio/components/interfaces/Auth/AutoSchemaForm.tsx
+++ b/studio/components/interfaces/Auth/AutoSchemaForm.tsx
@@ -4,7 +4,7 @@ import { boolean, number, object, string } from 'yup'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { Button, Form, Input, IconEye, IconEyeOff, InputNumber, Toggle } from 'ui'
-import { useStore, checkPermissions } from 'hooks'
+import { useStore, checkPermissions, useFlag } from 'hooks'
import {
FormActions,
FormHeader,
@@ -21,6 +21,7 @@ const AutoSchemaForm = observer(() => {
const formId = 'auth-config-general-form'
const [hidden, setHidden] = useState(true)
+ const showMfaSso = useFlag('mfaSso')
const canUpdateConfig = checkPermissions(PermissionAction.UPDATE, 'custom_config_gotrue')
const INITIAL_VALUES = {
@@ -187,18 +188,20 @@ const AutoSchemaForm = observer(() => {
)}
- Password
+