mirror of
https://github.com/supabase/supabase.git
synced 2026-10-07 02:15:05 +03:00
931 files changed
+121919
-212537
No files matched your search
@@ -119,8 +119,8 @@ Our approach for client libraries is modular. Each sub-library is a standalone i
|
||||
<td><a href="https://github.com/supabase-community/functions-csharp" target="_blank" rel="noopener noreferrer">functions-csharp</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Dart (Flutter)</td>
|
||||
<td><a href="https://github.com/supabase/supabase-dart" target="_blank" rel="noopener noreferrer">supabase-dart</a></td>
|
||||
<td>Flutter</td>
|
||||
<td><a href="https://github.com/supabase/supabase-flutter" target="_blank" rel="noopener noreferrer">supabase-flutter</a></td>
|
||||
<td><a href="https://github.com/supabase/postgrest-dart" target="_blank" rel="noopener noreferrer">postgrest-dart</a></td>
|
||||
<td><a href="https://github.com/supabase/gotrue-dart" target="_blank" rel="noopener noreferrer">gotrue-dart</a></td>
|
||||
<td><a href="https://github.com/supabase/realtime-dart" target="_blank" rel="noopener noreferrer">realtime-dart</a></td>
|
||||
|
||||
@@ -7,24 +7,9 @@ slug: initializing
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
## Dart
|
||||
|
||||
You can initialize a new Supabase client using the `SupabaseClient()` method.
|
||||
|
||||
The Supabase client is your entrypoint to the rest of the Supabase functionality
|
||||
and is the easiest way to interact with everything we offer within the Supabase ecosystem.
|
||||
|
||||
## Flutter
|
||||
|
||||
For `supabase_flutter`, you will be using the static `initialize()` method on `Supabase` class.
|
||||
|
||||
## Examples
|
||||
|
||||
### Dart `SupabaseClient()`
|
||||
|
||||
```dart
|
||||
final supabase = SupabaseClient('https://xyzcompany.supabase.co', 'public-anon-key');
|
||||
```
|
||||
For `supabase-flutter`, you will be using the static `initialize()` method on `Supabase` class.
|
||||
|
||||
### Flutter `initialize()`
|
||||
|
||||
@@ -34,3 +19,25 @@ Future<void> main() async {
|
||||
runApp(MyApp());
|
||||
}
|
||||
```
|
||||
|
||||
### Access `SupabaseClient` instance
|
||||
|
||||
Once you initialize Supabase in your `main()` method, you can access the `SupabaseClient` instance from anywhere in your app.
|
||||
|
||||
```dart
|
||||
final supabase = Supabase.instance.client;
|
||||
```
|
||||
|
||||
## Other Dart Projects
|
||||
|
||||
You can initialize a new Supabase client using the `SupabaseClient()` method.
|
||||
|
||||
The Supabase client is your entrypoint to the rest of the Supabase functionality
|
||||
and is the easiest way to interact with everything we offer within the Supabase ecosystem.
|
||||
|
||||
### Dart `SupabaseClient()`
|
||||
|
||||
```dart
|
||||
final supabase = SupabaseClient('https://xyzcompany.supabase.co', 'public-anon-key');
|
||||
```
|
||||
|
||||
@@ -7,25 +7,18 @@ slug: installing
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
## Dart
|
||||
|
||||
Dart libraries are built and supported by the community.
|
||||
|
||||
```bash
|
||||
dart pub add supabase
|
||||
```
|
||||
|
||||
Find the source code on [GitHub](https://github.com/supabase/supabase-dart).
|
||||
|
||||
## Flutter
|
||||
|
||||
For Flutter project, you can use [supabase_flutter](https://github.com/supabase/supabase-flutter).
|
||||
For Flutter project, you can use [supabase_flutter](https://pub.dev/packages/supabase_flutter) package.
|
||||
|
||||
```bash
|
||||
flutter pub add supabase_flutter
|
||||
```
|
||||
|
||||
`supabase_flutter` plugin uses `supabase` plugin internally, and it adds some Flutter specific functionality such as handling deeplinks coming back from magic link verifications.
|
||||
If you are creating a Flutter application, we recommend using `supabase_flutter` instead of `supabase`.
|
||||
## Other Dart projects
|
||||
|
||||
For the most part `supabase_flutter` shares the same API as `supabase` with few exceptions such as initialization or OAuth sign in.
|
||||
For non-Flutter projects using Dart, such as server-side Dart or Angular-Dart, you can use the [supabase](https://pub.dev/packages/supabase) package.
|
||||
|
||||
```bash
|
||||
dart pub add supabase
|
||||
```
|
||||
@@ -1,22 +1,14 @@
|
||||
---
|
||||
id: intro
|
||||
slug: /
|
||||
sidebar_label: Supabase Dart Library
|
||||
sidebar_label: Supabase Flutter Library
|
||||
---
|
||||
|
||||
# Supabase Dart Library
|
||||
# Supabase Flutter Library
|
||||
|
||||
:::note
|
||||
This reference documents every object and method available in Supabase's isomorphic Flutter library, [`supabase-flutter`](https://pub.dev/packages/supabase_flutter).
|
||||
|
||||
You're viewing the Supabase docs for a developer preview version.
|
||||
|
||||
Refer to the `v0` docs for the previous release.
|
||||
|
||||
:::
|
||||
|
||||
This reference documents every object and method available in Supabase's isomorphic Dart library, `supabase-dart`.
|
||||
|
||||
You can use the `supabase-dart` library to:
|
||||
You can use the `supabase-flutter` library to:
|
||||
|
||||
- interact with your Postgres database
|
||||
- listen to database changes
|
||||
@@ -24,7 +16,12 @@ You can use the `supabase-dart` library to:
|
||||
- build login and user management functionality
|
||||
- manage large files
|
||||
|
||||
## For non-Flutter projects
|
||||
|
||||
We also have [supabase-dart](https://github.com/supabase-community/supabase-dart) for non-Flutter Dart projects, such as server-side Dart or Angular-Dart.
|
||||
supabase-dart shares most of the APIs with supabase-flutter without being dependent on Flutter so that you can use Supabase anywhere you can run Dart!
|
||||
|
||||
## Additional Links
|
||||
|
||||
- Source Code: [github.com/supabase/supabase-dart](https://github.com/supabase/supabase-dart)
|
||||
- Source Code: [github.com/supabase/supabase-flutter](https://github.com/supabase/supabase-flutter)
|
||||
- [Known bugs and issues](https://github.com/supabase/supabase-flutter/issues)
|
||||
@@ -6,6 +6,11 @@ sidebar_label: Supabase Dart Library
|
||||
|
||||
# Supabase Dart Library
|
||||
|
||||
:::caution
|
||||
You're viewing the docs for an older version of the `supabase-flutter` library.
|
||||
:::
|
||||
|
||||
|
||||
This reference documents every object and method available in Supabase's isomorphic Dart library, `supabase-dart`.
|
||||
|
||||
You can use the `supabase-dart` library to:
|
||||
|
||||
@@ -10,7 +10,7 @@ pagination_next: null
|
||||
import ThemedImage from '@theme/ThemedImage'
|
||||
import AngularLogo from '@site/static/img/libraries/angular-icon.svg'
|
||||
import ExpoLogo from '@site/static/img/libraries/expo-icon.svg'
|
||||
import DartLogo from '@site/static/img/libraries/dart-icon.svg'
|
||||
import FlutterLogo from '@site/static/img/libraries/flutter-icon.svg'
|
||||
import JavascriptLogo from '@site/static/img/libraries/javascript-icon.svg'
|
||||
import NextjsDarkLogo from '@site/static/img/libraries/nextjs-dark-icon.svg'
|
||||
import NextjsLightLogo from '@site/static/img/libraries/nextjs-light-icon.svg'
|
||||
@@ -35,7 +35,7 @@ const frameworks = [
|
||||
logo: ExpoLogo,
|
||||
href: 'https://github.com/supabase/examples/tree/main/supabase-js-v1/todo-list/expo-todo-list',
|
||||
},
|
||||
{ name: 'Flutter', logo: DartLogo, href: '/guides/with-flutter' },
|
||||
{ name: 'Flutter', logo: FlutterLogo, href: '/guides/with-flutter' },
|
||||
{
|
||||
name: 'JavaScript',
|
||||
logo: JavascriptLogo,
|
||||
|
||||
@@ -2,38 +2,155 @@
|
||||
id: architecture
|
||||
title: Architecture
|
||||
description: 'Supabase design and architecture'
|
||||
# hide_table_of_contents: true
|
||||
---
|
||||
|
||||
Supabase is open source. Wherever possible, we use and support existing tools rather than developing from scratch.
|
||||
We choose open source tools which are scalable and we make them simple to use.
|
||||
Supabase is open source. We choose open source tools which are scalable and make them simple to use.
|
||||
|
||||
Supabase is not a 1-to-1 mapping of Firebase. While we are building many of the features that Firebase offers, we are not going about it the same way:
|
||||
our technological choices are quite different; everything we use is open source; and wherever possible, we use and support existing tools rather than developing from scratch.
|
||||
|
||||
Most notably, we use Postgres rather than a NoSQL store. This choice was deliberate. We believe that no other database offers the functionality required to compete with Firebase,
|
||||
while maintaining the scalability required to go beyond it.
|
||||
|
||||
## Architecture
|
||||
|
||||
Each Supabase project consists of several tools:
|
||||
|
||||

|
||||
|
||||
Supabase is not a 1-to-1 mapping of Firebase. While we are building many of the features that Firebase offers, we are not going about it the same way.
|
||||
### PostgreSQL (Database)
|
||||
|
||||
Our technological choices are quite different from Firebase. Everything we use is open source. Wherever possible, we use and support existing tools rather than developing from scratch.
|
||||
PostgreSQL is the core of Supabase. We do not abstract the PostgreSQL database — you can access it and use it with full privileges. We simply provide tools which makes PostgreSQL as easy to use as Firebase.
|
||||
|
||||
Most notably, we use Postgres rather than a NoSQL store. This choice was deliberate. We believe that no other database offers the scalability and functionality required to compete with Firebase.
|
||||
- Official Docs: [postgresql.org/docs](https://www.postgresql.org/docs/current/index.html)
|
||||
- Source code: [github.com/postgres/postgres](https://github.com/postgres/postgres) (mirror)
|
||||
- License: [PostgreSQL Licence](https://www.postgresql.org/about/licence/)
|
||||
- Language: C
|
||||
|
||||
## Feature Status
|
||||
### Studio (Dashboard)
|
||||
|
||||
| Product | Feature | Stage | Docs |
|
||||
| -------------------------- | ---------------------- | ----- | ------------------------------------------------ |
|
||||
| Database | Postgres | GA | [Link](/docs/guides/database) |
|
||||
| Database | Webhooks | Alpha | |
|
||||
| Database | Point in time Recovery | Alpha | |
|
||||
| Realtime | Postgres CDC | Beta | [Link](/docs/guides/realtime/postgres-cdc) |
|
||||
| Realtime | Broadcast | Alpha | [Link](/docs/guides/realtime#broadcast) |
|
||||
| Realtime | Presence | Alpha | [Link](/docs/guides/realtime#presence) |
|
||||
| Storage | | Beta | [Link](/docs/guides/storage) |
|
||||
| Storage | CDN | Beta | [Link](/docs/guides/storage-cdn) |
|
||||
| Edge Functions | | Beta | [Link](/docs/guides/functions) |
|
||||
| Auth | OAuth Providers | Beta | [Link](/docs/guides/auth/auth-apple) |
|
||||
| Auth | Passwordless | Beta | [Link](/guides/auth/auth-magic-link) |
|
||||
| Auth | Next.js Auth Helpers | Alpha | [Link](/docs/guides/auth/auth-helpers/nextjs) |
|
||||
| Auth | SvelteKit Auth Helpers | Alpha | [Link](/docs/guides/auth/auth-helpers/sveltekit) |
|
||||
| Public API | | Beta | [Link](/docs/reference/api) |
|
||||
| CLI | | Beta | [Link](/docs/guides/cli) |
|
||||
| Client Library: JavaScript | | GA | [Link](/docs/reference/javascript) |
|
||||
| Client Library: Dart | | Beta | [Link](/docs/reference/dart) |
|
||||
An open source Dashboard for managing your database and services.
|
||||
|
||||
- Official Docs: [Supabase docs](/docs)
|
||||
- Source code: [github.com/supabase/supabase](https://github.com/supabase/supabase/tree/master/studio)
|
||||
- License: [Apache 2](https://github.com/supabase/supabase/blob/master/LICENSE)
|
||||
- Language: TypeScript
|
||||
|
||||
### GoTrue (Auth)
|
||||
|
||||
A JWT-based API for managing users and issuing access tokens. This integrates with PostgreSQL's Row Level Security and the API servers.
|
||||
|
||||
- Official Docs: [Supabase Auth reference docs](/docs/reference/auth)
|
||||
- Source code: [github.com/supabase/gotrue](https://github.com/supabase/gotrue)
|
||||
- License: [MIT](https://github.com/supabase/gotrue/blob/master/LICENSE)
|
||||
- Language: Go
|
||||
|
||||
### PostgREST (API)
|
||||
|
||||
A standalone web server that turns your PostgreSQL database directly into a RESTful API.
|
||||
We use this with our [pg_graphql](https://github.com/supabase/pg_graphql) extension to provide a GraphQL API.
|
||||
|
||||
- Official Docs: [postgrest.org](https://postgrest.org/)
|
||||
- Source code: [github.com/PostgREST/postgrest](https://github.com/PostgREST/postgrest)
|
||||
- License: [MIT](https://github.com/PostgREST/postgrest/blob/main/LICENSE)
|
||||
- Language: Haskel
|
||||
|
||||
### Realtime (API & multiplayer)
|
||||
|
||||
A scalable websocket engine for managing user Presence, broadcasting messages, and streaming database changes.
|
||||
|
||||
- Official Docs: [Supabase Realtime docs](/docs/guides/realtime)
|
||||
- Source code: [github.com/supabase/realtime](https://github.com/supabase/realtime)
|
||||
- License: [Apache 2](https://github.com/supabase/realtime/blob/master/LICENSE)
|
||||
- Language: Elixir
|
||||
|
||||
### Storage API (large file storage)
|
||||
|
||||
An S3-compatible object storage service that stores metadata in Postgres.
|
||||
|
||||
- Official Docs: [Supabase Storage reference docs](/docs/reference/storage)
|
||||
- Source code: [github.com/supabase/storage-api](https://github.com/supabase/storage-api)
|
||||
- License: [Apache 2.0](https://github.com/supabase/storage-api/blob/master/LICENSE)
|
||||
- Language: NodeJS / TypeScript
|
||||
|
||||
### Deno (Edge Functions)
|
||||
|
||||
A modern runtime for JavaScript and TypeScript.
|
||||
|
||||
- Official Docs: [deno.land](https://deno.land/)
|
||||
- Source code: [github.com/denoland/deno](https://github.com/denoland/deno)
|
||||
- License: [MIT](https://github.com/denoland/deno/blob/main/LICENSE.md)
|
||||
- Language: TypeScript / Rust
|
||||
|
||||
### postgres-meta (Database management)
|
||||
|
||||
A RESTful API for managing your Postgres. Fetch tables, add roles, and run queries.
|
||||
|
||||
- Official Docs: [supabase.github.io/postgres-meta](https://supabase.github.io/postgres-meta/)
|
||||
- Source code: [github.com/supabase/postgres-meta](https://github.com/supabase/postgres-meta)
|
||||
- License: [Apache 2.0](https://github.com/supabase/postgres-meta/blob/master/LICENSE)
|
||||
- Language: NodeJS / TypeScript
|
||||
|
||||
### PgBouncer
|
||||
|
||||
A lightweight connection pooler for PostgreSQL. This is useful for connecting to Postgres when using Serverless functions.
|
||||
|
||||
- Official Docs: [pgbouncer.org](http://www.pgbouncer.org/)
|
||||
- Source code: [pgbouncer/pgbouncer](https://github.com/pgbouncer/pgbouncer)
|
||||
- License: [ISC](https://github.com/pgbouncer/pgbouncer/blob/master/COPYRIGHT)
|
||||
- Language: C
|
||||
|
||||
### Kong (API Gateway)
|
||||
|
||||
A cloud-native API gateway, built on top of Nginx.
|
||||
|
||||
- Official Docs: [docs.konghq.com](https://docs.konghq.com/)
|
||||
- Source code: [github.com/kong/kong](https://github.com/kong/kong)
|
||||
- License: [Apache 2.0](https://github.com/Kong/kong/blob/master/LICENSE)
|
||||
- Language: Lua
|
||||
|
||||
## Product Principles
|
||||
|
||||
It is our goal to provide an architecture that any large-scale company would design for themselves,
|
||||
and then provide tooling around that architecture that is easy-to-use for indie-developers and small teams.
|
||||
|
||||
We use a series of principles to ensure that scalability and usability are never mutually exclusive:
|
||||
|
||||
### Everything works in isolation
|
||||
|
||||
Each system must work as a standalone tool with as few moving parts as possible.
|
||||
The litmus test for this is: "Can a user run this product with nothing but a Postgres database?"
|
||||
|
||||
### Everything is integrated
|
||||
|
||||
Supabase is composable. Even though every product works in isolation, each product on the platform needs to 10x the other products.
|
||||
For integration, each tool should expose an API and Webhooks.
|
||||
|
||||
### Everything is extensible
|
||||
|
||||
We're deliberate about adding a new tool, and prefer instead to extend an existing one.
|
||||
This is the opposite of many cloud providers whose product offering expands into niche use-cases. We provide _primitives_ for developers, which allow them to achieve any goal.
|
||||
Less, but better.
|
||||
|
||||
### Everything is portable
|
||||
|
||||
To avoid lock-in, we make it easy to migrate in and out. Our cloud offering is compatible with our self-hosted product.
|
||||
We use existing standards to increase portability (like pg_dump an CSV files). If a new standard emerges which competes with a "Supabase" approach, we will deprecate the approach in favor of the standard.
|
||||
This forces us compete on experience. We aim to be the best Postgres hosting service.
|
||||
|
||||
### Play the long game
|
||||
|
||||
We sacrifice short-term wins for long-term gains. For example, it is tempting to run a fork of Postgres with additional functionality which only our customers need.
|
||||
Instead, we prefer to support efforts to upstream missing functionality so that the entire community benefits. This has the additional benefit of ensuring portability and longevity.
|
||||
|
||||
### Build for developers
|
||||
|
||||
"Developers" are a specific profile of user: they are _builders_.
|
||||
When assessing impact as a function of effort, developers have a large efficiency due to the type of products and systems they can build.
|
||||
As the profile of a developer changes over time, Supabase will continue to evolve the product to fit this evolving profile.
|
||||
|
||||
### Support existing tools
|
||||
|
||||
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.
|
||||
@@ -33,6 +33,6 @@ We only support PostgreSQL. It's unlikely we'll ever move away from Postgres; ho
|
||||
|
||||
### Do you have a library for `[some other language]`?
|
||||
|
||||
We officially support [JavaScript](/docs/reference/javascript/installing) and [Dart](/docs/reference/dart/installing).
|
||||
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).
|
||||
@@ -65,6 +65,10 @@ This will allow your users to use your custom Azure login page when logging in.
|
||||
|
||||
## Add login code to your client app
|
||||
|
||||
:::tip
|
||||
Supabase Auth requires that Azure returns a valid email address. Therefore you must request the `email` scope in the `signIn` method above.
|
||||
:::
|
||||
|
||||
When your user signs in, call [signInWithOAuth()](/docs/reference/javascript/auth-signinwithoauth) with `azure` as the `provider`:
|
||||
|
||||
```js
|
||||
|
||||
@@ -12,7 +12,7 @@ import TabItem from '@theme/TabItem'
|
||||
Setting up Email logins for your Supabase application.
|
||||
|
||||
- Add Email authenticator to your [Supabase Project](https://app.supabase.com)
|
||||
- Add the login code to your application - [JavaScript](https://github.com/supabase/supabase-js) | [Dart](https://github.com/supabase/supabase-dart)
|
||||
- Add the login code to your application - [JavaScript](https://github.com/supabase/supabase-js) | [Flutter](https://github.com/supabase/supabase-flutter)
|
||||
|
||||
## Configure email settings
|
||||
|
||||
@@ -55,11 +55,11 @@ async function signInWithEmail() {
|
||||
|
||||
<TabItem value="dart">
|
||||
|
||||
When your user signs in, call [signIn()](/docs/reference/dart/auth-signin) with their email address and password:
|
||||
When your user signs in, call [signInWithPassword()](/docs/reference/dart/auth-signinwithpassword) with their email address and password:
|
||||
|
||||
```dart
|
||||
Future<void> signInWithEmail() async {
|
||||
await supabase.auth.signIn(
|
||||
final AuthResponse res = await supabase.auth.signInWithPassword(
|
||||
email: 'example@email.com',
|
||||
password: 'example-password'
|
||||
);
|
||||
@@ -106,4 +106,4 @@ Future<void> signOut() async {
|
||||
|
||||
- [Supabase Account - Free Tier OK](https://supabase.com)
|
||||
- [Supabase JS Client](https://github.com/supabase/supabase-js)
|
||||
- [Supabase Dart Client](https://github.com/supabase/supabase-dart)
|
||||
- [Supabase Flutter Client](https://github.com/supabase/supabase-flutter)
|
||||
@@ -15,7 +15,7 @@ Magic links only work with email addresses. By default, a user can only request
|
||||
Setting up Magic Link logins for your Supabase application.
|
||||
|
||||
- Add Magic Link authenticator to your [Supabase Project](https://app.supabase.com)
|
||||
- Add the login code to your application - [JavaScript](https://github.com/supabase/supabase-js) | [Dart](https://github.com/supabase/supabase-dart)
|
||||
- Add the login code to your application - [JavaScript](https://github.com/supabase/supabase-js) | [Flutter](https://github.com/supabase/supabase-flutter)
|
||||
|
||||
## Add Magic Link into your Supabase Project
|
||||
|
||||
@@ -50,11 +50,11 @@ async function signInWithEmail() {
|
||||
|
||||
<TabItem value="dart">
|
||||
|
||||
When your user signs in, call [signIn()](/docs/reference/dart/auth-signin) with their email address:
|
||||
When your user signs in, call [signIn()](/docs/reference/dart/auth-signinwithotp) with their email address:
|
||||
|
||||
```dart
|
||||
Future<void> signInWithEmail() async {
|
||||
await supabase.auth.signIn(email: 'example@email.com');
|
||||
final AuthResponse res = await supabase.auth.signinwithotp(email: 'example@email.com');
|
||||
}
|
||||
```
|
||||
|
||||
@@ -98,4 +98,4 @@ Future<void> signOut() async {
|
||||
|
||||
- [Supabase Account - Free Tier OK](https://supabase.com)
|
||||
- [Supabase JS Client](https://github.com/supabase/supabase-js)
|
||||
- [Supabase Dart Client](https://github.com/supabase/supabase-dart)
|
||||
- [Supabase Flutter Client](https://github.com/supabase/supabase-flutter)
|
||||
@@ -0,0 +1,202 @@
|
||||
---
|
||||
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.
|
||||
|
||||
Supabase Auth supports server-side rendering when you need access to user
|
||||
information, or your server needs to authorize API requests on behalf of your
|
||||
user to render content.
|
||||
|
||||
When a user authenticates with Supabase Auth, two pieces of information are
|
||||
issued by the server:
|
||||
|
||||
1. **Access token** in the form of a JWT.
|
||||
2. **Refresh token** which is a randomly generated string.
|
||||
|
||||
Most Supabase projects have their auth server listening on
|
||||
`<project-ref>.supabase.co/auth/v1`, thus the access token and refresh token are
|
||||
set as `sb-access-token` and `sb-refresh-token` cookies on the
|
||||
`<project-ref>.supabase.co` domain.
|
||||
|
||||
:::note
|
||||
These cookie names are for internal Supabase use only and may change without
|
||||
warning. They are included in this guide for illustration purposes only.
|
||||
:::
|
||||
|
||||
Web browsers limit access to cookies across domains, consistent with the
|
||||
[Same-Origin Policy
|
||||
(SOP)](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy).
|
||||
|
||||
Your web application cannot access these cookies,
|
||||
nor will these cookies be sent to your application's server.
|
||||
|
||||
## Understanding the authentication flow
|
||||
|
||||
When you call one of the `signIn` methods, the client library running in the
|
||||
browser sends the request to the Supabase Auth server. The Auth server determines
|
||||
whether to verify a phone number, email and password combination, a Magic Link,
|
||||
or use a social login (if you have any setup in your project).
|
||||
|
||||
Upon successful verification of the identity of the user, the Supabase Auth
|
||||
server redirects the user back to your single-page app.
|
||||
|
||||
:::tip
|
||||
You can configure [redirects URLs](https://app.supabase.com/project/_/auth/settings) in the Supabase Dashboard. You can use wildcard match patterns
|
||||
like `*` and `**` to allow redirects to different forms of URLs.
|
||||
:::
|
||||
|
||||
These redirect URLs have the following structure:
|
||||
|
||||
```
|
||||
https://yourapp.com/...#access_token=<...>&refresh_token=<...>&...
|
||||
```
|
||||
|
||||
The first access and refresh tokens after a successful verification are
|
||||
contained in the URL fragment (anything after the `#` sign) of the redirect
|
||||
location. This is intentional and not configurable.
|
||||
|
||||
The client libraries are designed to listen for this type of URL, extract
|
||||
the access token, refresh token and some extra information from it, and finally
|
||||
persist it in local storage for further use by the library and your app.
|
||||
|
||||
:::info
|
||||
Web browsers do not send the URL fragment to the server they're making the
|
||||
request to. Since you may not be hosting the single-page app on a server under
|
||||
your direct control (such as on GitHub Pages or other freemium hosting
|
||||
providers), we want to prevent hosting services from getting access to your
|
||||
user's authorization credentials by default. Even if the server is under your
|
||||
direct control, `GET` requests and their full URLs are often logged. This
|
||||
approach also avoids leaking credentials in request or access logs.
|
||||
:::
|
||||
|
||||
## Bringing it together
|
||||
|
||||
As seen from the authentication flow, the initial request after successful
|
||||
login made by the browser to your app's server after user login **does not
|
||||
contain any information about the user**. This is because first the client-side
|
||||
JavaScript library must run before it makes the access and refresh token
|
||||
available to your server.
|
||||
|
||||
It is very important to make sure that the redirect route right after login
|
||||
works without any server-side rendering. Other routes requiring authorization
|
||||
do not have the same limitation, provided you send the access and refresh
|
||||
tokens to your server.
|
||||
|
||||
This is traditionally done by setting cookies. Here's an example you
|
||||
can add to the root of your application:
|
||||
|
||||
```typescript
|
||||
supabase.auth.onAuthStateChange((event, session) => {
|
||||
if (event === "SIGNED_OUT" || event === "USER_DELETED") {
|
||||
// delete cookies on sign out
|
||||
const expires = new Date(0).toUTCString();
|
||||
document.cookie = `my-access-token=; path=/; expires=${expires}; SameSite=Lax; secure`;
|
||||
document.cookie = `my-refresh-token=; path=/; expires=${expires}; SameSite=Lax; secure`;
|
||||
} else if (event === "SIGNED_IN" || event === "TOKEN_REFRESHED") {
|
||||
const maxAge = 100 * 365 * 24 * 60 * 60; // 100 years, never expires
|
||||
document.cookie = `my-access-token=${session.access_token}; path=/; max-age=${maxAge}; SameSite=Lax; secure`;
|
||||
document.cookie = `my-refresh-token=${session.refresh_token}; path=/; max-age=${maxAge}; SameSite=Lax; secure`;
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
This uses the standard
|
||||
[`document.cookie` API](https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie)
|
||||
to set cookies on all paths of your app's domain. All subsequent requests
|
||||
made by the browser to your app's server include the `my-access-token` and
|
||||
`my-refresh-token` cookies (the names of the cookies and additional
|
||||
parameters can be changed).
|
||||
|
||||
In your server-side rendering code you can now access user and session
|
||||
information:
|
||||
|
||||
|
||||
```typescript
|
||||
const refreshToken = req.cookies['my-refresh-token'];
|
||||
const accessToken = req.cookies['my-access-token'];
|
||||
|
||||
if (refreshToken && accessToken) {
|
||||
await supabase.auth.setSession({
|
||||
refresh_token: refreshToken,
|
||||
access_token: accessToken
|
||||
});
|
||||
} else {
|
||||
// make sure you handle this case!
|
||||
throw new Error("User is not authenticated.")
|
||||
}
|
||||
|
||||
// returns user information
|
||||
await supabase.auth.getUser();
|
||||
```
|
||||
|
||||
Use `setSession({ access_token, refresh_token })` instead of
|
||||
`setSession(refreshToken)` or `getUser(accessToken)` as refresh tokens or access tokens alone do not properly identify a user session.
|
||||
|
||||
Access tokens are valid only for a short amount of time.
|
||||
|
||||
Even though refresh tokens are long-lived, there is no guarantee that a user
|
||||
has an active session. They may have logged out and your application failed to
|
||||
remove the `my-refresh-token` cookie, or some other failure occurred that left
|
||||
a stale refresh token in the browser. Furthermore, a refresh token can only be
|
||||
used a few seconds after it was first used. Only use a refresh token if the
|
||||
access token is about to expire, which will avoid the introduction of difficult
|
||||
to diagnose logout bugs in your app.
|
||||
|
||||
A good practice is to handle unauthorized errors by deferring rendering the
|
||||
page in the browser instead of in the server. Some user information is
|
||||
contained in the access token though, so in certain cases, you may be able to
|
||||
use this potentially stale information to render a page.
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
### How do I make the cookies `HttpOnly`?
|
||||
|
||||
This is not necessary. Both the access token and refresh token are designed to
|
||||
be passed around to different components in your application. The browser-based
|
||||
side of your application needs access to the refresh token to properly maintain
|
||||
a browser session anyway.
|
||||
|
||||
### My server is getting invalid refresh token errors. What's going on?
|
||||
|
||||
It is likely that the refresh token sent from the browser to your server is
|
||||
stale. Make sure the `onAuthStateChange` listener callback is free of bugs and
|
||||
is registered relatively early in your application's lifetime.
|
||||
|
||||
When you receive this error on the server-side, try to defer
|
||||
rendering to the browser where the client library can access an up-to-date
|
||||
refresh token and present the user with a better experience.
|
||||
|
||||
### Should I set a shorter `Max-Age` parameter on the cookies?
|
||||
|
||||
The `Max-Age` or `Expires` cookie parameters only control whether the browser
|
||||
sends the value to the server. Since a refresh token represents the
|
||||
long-lived authentication session of the user on that browser, setting a short
|
||||
`Max-Age` or `Expires` parameter on the cookies only results in a degraded
|
||||
user experience.
|
||||
|
||||
The only way to ensure that a user has logged out or their session has ended
|
||||
is to get the user's details with `getUser()`.
|
||||
|
||||
### What should I use for the `SameSite` property?
|
||||
|
||||
Make sure you [understand the behavior of the property in different
|
||||
situations](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite)
|
||||
as some properties can degrade the user experience.
|
||||
|
||||
A good default is to use `Lax` which sends cookies when users are
|
||||
navigating to your site. Cookies typically require the `Secure` attribute,
|
||||
which only sends them over HTTPS. However, this can be a problem when
|
||||
developing on `localhost`.
|
||||
|
||||
### Can I use server-side rendering with a CDN or cache?
|
||||
|
||||
Yes, but you need to be careful to include at least the refresh token cookie
|
||||
value in the cache key. Otherwise you may be accidentally serving pages with
|
||||
data belonging to different users!
|
||||
|
||||
Also be sure you set proper cache control headers. We recommend invalidating
|
||||
cache keys every hour or less.
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
id: custom-domains
|
||||
title: Custom Domains
|
||||
description: Configuring a Custom Domain for your Supabase project.
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
:::note
|
||||
|
||||
Custom Domains are currently in beta, and are being slowly made available to projects.
|
||||
|
||||
:::
|
||||
|
||||
## Overview
|
||||
|
||||
Custom domains allow you to present a branded experience to your users. You can also use a subdomain for the purposes of this guide.
|
||||
|
||||
Before you start, here are some prerequisites:
|
||||
|
||||
- [Install the Supabase CLI](/docs/guides/cli) 1.10.1+
|
||||
- Ensure that you've configured the CLI for your account
|
||||
- A Supabase project (create a new one if you don't already have one)
|
||||
- Ensure that you've subscribed to the Custom Domains add-on
|
||||
|
||||
## Configure a Custom Domain
|
||||
|
||||
Setting up a custom domain (`api.example.com`) for your Supabase project (ref `foobarbaz`, with assigned endpoints at `foobarbaz.supabase.co`) involves a few steps.
|
||||
|
||||
1) Set up a CNAME record for `api.example.com`, resolving to `foobarbaz.supabase.co.`, with as low a TTL as possible.
|
||||
|
||||
2) Use the `create` subcommand of the CLI to initiate the process:
|
||||
|
||||
```bash
|
||||
supabase custom-hostname create --project-ref foobarbaz --custom-hostname api.example.com --experimental
|
||||
```
|
||||
|
||||
3) The output of the `create` command includes two TXT records you will need to set up, in order to verify your control over the domain in question, and for us to issue SSL certificates for it. For example:
|
||||
|
||||
```
|
||||
[...]
|
||||
Required outstanding validation records:
|
||||
_cf-custom-hostname.api.example.com TXT -> 46BBC14D-D50A-409C-8DB5-F862CF5BA660
|
||||
api.example.com TXT -> ca3-F1HvR9i938OgVwpCFwi1jTsbhe1hvT0Ic3efPY3Q
|
||||
```
|
||||
|
||||
Set up both records as instructed, and then use the `reverify` command for the Supabase Platform to verify the records:
|
||||
|
||||
```bash
|
||||
supabase custom-hostname reverify --project-ref foobarbaz --experimental
|
||||
```
|
||||
|
||||
You might need to wait a few minutes before your updated DNS records get propagated, especially if the older records were using a high TTL. Upon successful verification, you will be able to proceed to the final activation step:
|
||||
|
||||
```
|
||||
Custom hostname configuration complete, and ready for activation.
|
||||
```
|
||||
|
||||
4) The final activation step reconfigures your project to start serving traffic on your custom domain (`api.example.com`). The auth service, in particular, will no longer work with the original URL (`foobarbaz.supabase.co`). As such, it is recommended that you schedule a downtime window of 20-30 minutes, depending on the complexity of your project, to update all the services that need to know about your custom domain:
|
||||
|
||||
- any client code (e.g., frontends, mobile apps)
|
||||
- any OAuth providers (e.g., google, github)
|
||||
|
||||
Additionally, update the DNS configuration for `api.example.com` to once more use a CNAME record that resolves to `foobarbaz.supabase.co`. Finally, you can use the `activate` subcommand to reconfigure your project:
|
||||
|
||||
```bash
|
||||
supabase custom-hostname activate --project-ref foobarbaz --experimental
|
||||
```
|
||||
@@ -316,7 +316,7 @@ GRANT EXECUTE ON FUNCTION hello_world TO service_role;
|
||||
|
||||
## Resources
|
||||
|
||||
- Official Client libraries: [JavaScript](../../reference/javascript/rpc) and [Dart](../../reference/dart/rpc)
|
||||
- Official Client libraries: [JavaScript](../../reference/javascript/rpc) and [Flutter](../../reference/dart/rpc)
|
||||
- Community client libraries: [github.com/supabase-community](https://github.com/supabase-community)
|
||||
- PostgreSQL Official Docs: [Chapter 9. Functions and Operators](https://www.postgresql.org/docs/current/functions.html)
|
||||
- PostgreSQL Reference: [CREATE FUNCTION](https://www.postgresql.org/docs/9.1/sql-createfunction.html)
|
||||
|
||||
@@ -80,7 +80,7 @@ Build a basic Todo List with Supabase and your favorite frontend framework:
|
||||
### Libraries
|
||||
|
||||
- D (in development): [GitHub](https://github.com/csharpdf/dupabase)
|
||||
- Dart (in development): [GitHub](https://github.com/supabase/supabase-dart)
|
||||
- Flutter (in development): [GitHub](https://github.com/supabase/supabase-flutter)
|
||||
- Python (in development): [GitHub](https://github.com/supabase/supabase-py)
|
||||
- C# (in development): [GitHub](https://github.com/supabase/supabase-csharp)
|
||||
- Kotlin (in development): [GitHub](https://github.com/supabase-community/postgrest-kt)
|
||||
|
||||
Executable
+171
@@ -0,0 +1,171 @@
|
||||
---
|
||||
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).
|
||||
|
||||
### Admin 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` |
|
||||
| Admin API | | `beta` |
|
||||
| CLI | | `beta` |
|
||||
| Client Library: JavaScript | | `GA` |
|
||||
| Client Library: Dart | | `beta` |
|
||||
@@ -0,0 +1,149 @@
|
||||
|
||||
id: keyri
|
||||
title: 'Keyri'
|
||||
description: 'QR authentication for an easy and flexible biometric solution across all platforms.'
|
||||
---
|
||||
|
||||
Keyri can be used to incorporate sign-in-with-QR functionality into your Supabase app, allowing users to scan a QR code on your web app with your mobile app and be instantly logged into the web app without having to input any credentials.
|
||||
|
||||
Configuration is split into Web and Mobile components. On web, the Keyri QR Widget needs to be installed along with an event listener, and in your mobile app, install the Keyri SDK and pass into it the user's refresh token when sign-in-with-QR is initiated. When the refresh token lands in your web app, it's passed into Supabase's `setSession()` method.
|
||||
|
||||
# Sign up for Keyri
|
||||
|
||||
First make a free account on the Keyri dashboard (<https://app.keyri.com>). On Add Your Application, set a name and input the domain on which your app will eventually be deployed. You can create multiple application in Keyri to account for your development, staging, and production environments
|
||||
|
||||

|
||||
|
||||
Note your application key from the Keys and Credentials section - this will be used in the Mobile portion of the implementation
|
||||
|
||||

|
||||
|
||||
# Web
|
||||
|
||||
[](http://www.youtube.com/watch?v=jrjrcpc2PFQ "Supabase & Keyri | Web Integration Overview")
|
||||
|
||||
For your web app, first download KeyriQR.html (available [here](https://raw.githubusercontent.com/Keyri-Co/library-keyri-connect/main/KeyriQR.html)) and save it to a public directory.
|
||||
|
||||
Next, embed KeyriQR.html in your login page as an iFrame within the desired div. This serves as the widget that displays the dynamic QR code and connects with the Keyri API.
|
||||
|
||||
```html
|
||||
<div>
|
||||
<iframe
|
||||
title='KeyriQR'
|
||||
src='/KeyriQR.html'
|
||||
id='qr-frame'
|
||||
height='300'
|
||||
width='300'
|
||||
scrolling='no'
|
||||
style={{ border: 'solid 5px white' }}
|
||||
></iframe>
|
||||
</div>
|
||||
```
|
||||
|
||||
Next, for the same login view, set up an event listener to pick up the session token that the iFrame emits when the QR code is scanned by your app.
|
||||
|
||||
```javascript
|
||||
useEffect(() => {
|
||||
window.addEventListener('message', async (evt) => {
|
||||
if (evt.data.keyri && evt.data.data && document.location.origin == evt.origin) {
|
||||
const { data } = evt;
|
||||
if (!data.error) {
|
||||
let refresh_token = JSON.parse(data.data).refreshToken;
|
||||
await handleQrLogin(refresh_token);
|
||||
} else if (data.error) {
|
||||
console.log(`Keyri error: ${data.message}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
That's it!
|
||||
|
||||
# Mobile
|
||||
|
||||
[](http://www.youtube.com/watch?v=oGMsSKyh6tc "Supabase & Keyri | Mobile Integration Overview")
|
||||
|
||||
### Install Flutter
|
||||
|
||||
First, install the Flutter SDK, found at flutter.dev
|
||||
|
||||
Make sure to add Flutter to your PATH, for example: 
|
||||
|
||||
```shell
|
||||
export PATH="$PATH:`pwd`/flutter/bin"
|
||||
```
|
||||
|
||||
### Apple - initial setup
|
||||
|
||||
Download the latest version of Xcode from the Mac App Store. Make sure the Xcode provided simulator is using a 64-bit device (iPhone 5s or later). You can check the device by viewing the settings in the simulator’s **Hardware > Device** or **File > Open Simulator** menus.
|
||||
|
||||
### Android - initial setup
|
||||
|
||||
Download the latest version of [Android Studio](https://developer.android.com/studio). Install Android SDK and needed emulator(s).
|
||||
|
||||
### Create Project
|
||||
|
||||
Run this command in your terminal/shell at the desired location for your new project
|
||||
|
||||
```shell
|
||||
$ flutter create my_app
|
||||
```
|
||||
|
||||
You can then CD into the new directory, and run the test app with 
|
||||
|
||||
```shell
|
||||
flutter run
|
||||
```
|
||||
|
||||
This is a good test - if things are configured correctly so far you should see the default Flutter test app deployed.
|
||||
|
||||
### Add dependencies (Keyri and Supabase)
|
||||
|
||||
Open your Pubspec.yaml file, which should be at the top level directory in your new project
|
||||
|
||||
Add Keyri and Supabase under **dependencies**
|
||||
|
||||

|
||||
|
||||
One can now access Supabase and Keyri sdks in their Flutter code
|
||||
|
||||
### Utilize the two together
|
||||
|
||||
1. Make a request to Supabase to authenticate the user
|
||||
|
||||
2. Parse the response to extract the token
|
||||
|
||||
3. Authenticate using Keyri
|
||||
1. Below, we show how to utilize the EasyKeyriAuth function, which takes the user through scanning the code, creating the session, displaying the confirmation screen, and finalizing the payload transmission
|
||||
- Note - you can find your App Key in the Keyri Developer Portal
|
||||
|
||||
2. Alternatively, intermediate functions in the Keyri SDK, discussed in the mobile docs, can provide control over displaying a custom QR Scanner and/or Confirmation screen
|
||||
|
||||
```kotlin
|
||||
// Sign in user with email and password
|
||||
// Alternatively one can utilize the Supabase API to accomplish the same thing
|
||||
final response = await client.auth.signIn(email: 'email', password: 'password');
|
||||
|
||||
if (response.error != null) {
|
||||
// Error
|
||||
print('Error: ${response.error?.message}');
|
||||
} else {
|
||||
// Success
|
||||
final session = response.data;
|
||||
|
||||
// This is the payload that needs to be send through Keyri
|
||||
final refreshToken = session.refreshToken
|
||||
|
||||
// EasyKeyriAuth guides the user through scanning and parsing the QR, confirming the session, and configuring the payload
|
||||
// One can also use the initiateQRSession method to use the Keyri Scanner with a custom Confirmation screen
|
||||
// Or the ProcessLink method if you have your own scanner or are using deep linking
|
||||
await keyri
|
||||
.easyKeyriAuth([App Key],
|
||||
'{"refreshToken":"$refreshToken"}', [email])
|
||||
.then((authResult) => _onAuthResult(authResult))
|
||||
.catchError((error, stackTrace) => _onError(error));
|
||||
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
@@ -26,7 +26,7 @@ Reference documentation for the official Supabase client libraries, APIs, and to
|
||||
</div>
|
||||
{/* DART */}
|
||||
<div class="col col--6">
|
||||
<ButtonCard icon={useBaseUrl("/img/icons/dart-icon.svg")} class="card" to={useBaseUrl("/reference/dart")} style={{ height: '100%' }} title="Dart" description="Dart and Flutter documentation."/>
|
||||
<ButtonCard icon={useBaseUrl("/img/icons/flutter-icon.svg")} class="card" to={useBaseUrl("/reference/dart")} style={{ height: '100%' }} title="Flutter" description="Flutter and Dart documentation."/>
|
||||
</div>
|
||||
{/* CLI */}
|
||||
<div class="col col--6">
|
||||
|
||||
@@ -90,11 +90,10 @@ const config = {
|
||||
breadcrumbs: false,
|
||||
editUrl:
|
||||
'https://github.com/supabase/supabase/edit/master/apps/reference/',
|
||||
lastVersion: 'v0',
|
||||
lastVersion: 'current',
|
||||
versions: {
|
||||
current: {
|
||||
label: 'v1-dev',
|
||||
path: '/next',
|
||||
label: 'v1',
|
||||
},
|
||||
v0: {
|
||||
label: 'v0',
|
||||
|
||||
@@ -4,7 +4,7 @@ const sidebars = {
|
||||
type: 'category',
|
||||
label: 'Overview',
|
||||
collapsed: true,
|
||||
items: ['about', 'architecture', 'guides/examples'],
|
||||
items: ['about', 'guides/features', 'architecture', 'guides/examples'],
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
@@ -80,6 +80,7 @@ const sidebars = {
|
||||
'guides/auth/row-level-security',
|
||||
'guides/auth/managing-user-data',
|
||||
'guides/auth/auth-captcha',
|
||||
'guides/auth/server-side-rendering',
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -224,6 +225,7 @@ const sidebars = {
|
||||
'guides/integrations/auth0',
|
||||
'guides/integrations/authsignal',
|
||||
'guides/integrations/clerk',
|
||||
'guides/integrations/keyri',
|
||||
'guides/integrations/stytch',
|
||||
'guides/integrations/supertokens',
|
||||
],
|
||||
@@ -277,7 +279,7 @@ const sidebars = {
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
label: 'Supabase Dart Library',
|
||||
label: 'Supabase Flutter Library',
|
||||
href: '/reference/dart',
|
||||
},
|
||||
{ type: 'link', label: 'Supabase CLI', href: '/reference/cli' },
|
||||
|
||||
@@ -14,10 +14,14 @@ const sidebars = {
|
||||
label: 'Auth',
|
||||
items: [
|
||||
'generated/auth-signup',
|
||||
'generated/auth-signin',
|
||||
'generated/auth-signinwithprovider',
|
||||
'generated/auth-signinwithpassword',
|
||||
'generated/auth-signinwithotp',
|
||||
'generated/auth-signinwithoauth',
|
||||
'generated/auth-signout',
|
||||
'generated/auth-update',
|
||||
'generated/auth-verifyotp',
|
||||
'generated/auth-currentsession',
|
||||
'generated/auth-currentuser',
|
||||
'generated/auth-updateuser',
|
||||
'generated/auth-onauthstatechange',
|
||||
'generated/reset-password-email',
|
||||
],
|
||||
|
||||
@@ -34,8 +34,8 @@ const headerNames = {
|
||||
icon: 'javascript-icon',
|
||||
},
|
||||
dart: {
|
||||
name: 'Dart',
|
||||
icon: 'dart-icon',
|
||||
name: 'Flutter',
|
||||
icon: 'flutter-icon',
|
||||
},
|
||||
'auth-helpers': {
|
||||
name: 'Auth Helpers',
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="64"
|
||||
height="64"
|
||||
>
|
||||
<g transform="matrix(.061615 0 0 .061615 -1.430818 -1.2754)">
|
||||
<defs>
|
||||
<path
|
||||
id="A"
|
||||
d="M959.4 500L679.8 779.7l279.6 279.7H639.9L360.2 779.7 639.9 500h319.5zM639.9 20.7L120.6 540l159.8 159.8 679-679.1H639.9z"
|
||||
/>
|
||||
</defs>
|
||||
<clipPath id="B"><use xlink:href="#A" /></clipPath>
|
||||
<g clip-path="url(#B)">
|
||||
<path d="M360.3 779.7L520 939.5 959.4 500H639.9z" fill="#39cefd" />
|
||||
</g>
|
||||
<defs>
|
||||
<path
|
||||
id="C"
|
||||
d="M959.4 500L679.8 779.7l279.6 279.7H639.9L360.2 779.7 639.9 500h319.5zM639.9 20.7L120.6 540l159.8 159.8 679-679.1H639.9z"
|
||||
/>
|
||||
</defs>
|
||||
<clipPath id="D"><use xlink:href="#C" /></clipPath>
|
||||
<path
|
||||
clip-path="url(#D)"
|
||||
d="M639.9 20.7h319.5l-679 679.1L120.6 540z"
|
||||
fill="#39cefd"
|
||||
/>
|
||||
<defs>
|
||||
<path
|
||||
id="E"
|
||||
d="M959.4 500L679.8 779.7l279.6 279.7H639.9L360.2 779.7 639.9 500h319.5zM639.9 20.7L120.6 540l159.8 159.8 679-679.1H639.9z"
|
||||
/>
|
||||
</defs>
|
||||
<clipPath id="F"><use xlink:href="#E" /></clipPath>
|
||||
<path
|
||||
clip-path="url(#F)"
|
||||
d="M520 939.5l119.9 119.8h319.5L679.8 779.7z"
|
||||
fill="#03569b"
|
||||
/>
|
||||
<defs>
|
||||
<path
|
||||
id="G"
|
||||
d="M959.4 500L679.8 779.7l279.6 279.7H639.9L360.2 779.7 639.9 500h319.5zM639.9 20.7L120.6 540l159.8 159.8 679-679.1H639.9z"
|
||||
/>
|
||||
</defs>
|
||||
<clipPath id="H"><use xlink:href="#G" /></clipPath>
|
||||
<linearGradient
|
||||
id="I"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="566.635"
|
||||
y1="970.339"
|
||||
x2="685.65"
|
||||
y2="851.324"
|
||||
>
|
||||
<stop offset="0" stop-color="#1a237e" stop-opacity=".4" />
|
||||
<stop offset="1" stop-color="#1a237e" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<path
|
||||
clip-path="url(#H)"
|
||||
d="M757 857.4l-77.2-77.7L520 939.5z"
|
||||
fill="url(#I)"
|
||||
/>
|
||||
<defs>
|
||||
<path
|
||||
id="J"
|
||||
d="M959.4 500L679.8 779.7l279.6 279.7H639.9L360.2 779.7 639.9 500h319.5zM639.9 20.7L120.6 540l159.8 159.8 679-679.1H639.9z"
|
||||
/>
|
||||
</defs>
|
||||
<clipPath id="K"><use xlink:href="#J" /></clipPath>
|
||||
<g clip-path="url(#K)">
|
||||
<path
|
||||
d="M360.282 779.645L520.086 619.84 679.9 779.645 520.086 939.45z"
|
||||
fill="#16b9fd"
|
||||
/>
|
||||
</g>
|
||||
<radialGradient
|
||||
id="L"
|
||||
cx="7824.659"
|
||||
cy="-2855.979"
|
||||
r="5082.889"
|
||||
gradientTransform="matrix(0.25,0,0,-0.25,-1812,-622.5)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0" stop-color="#fff" stop-opacity=".1" />
|
||||
<stop offset="1" stop-color="#fff" stop-opacity="0" />
|
||||
</radialGradient>
|
||||
<path
|
||||
d="M959.4 500L679.8 779.7l279.6 279.7H639.9L360.2 779.7 639.9 500h319.5zM639.9 20.7L120.6 540l159.8 159.8 679-679.1H639.9z"
|
||||
fill="url(#L)"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
@@ -0,0 +1,94 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="64"
|
||||
height="64"
|
||||
>
|
||||
<g transform="matrix(.061615 0 0 .061615 -1.430818 -1.2754)">
|
||||
<defs>
|
||||
<path
|
||||
id="A"
|
||||
d="M959.4 500L679.8 779.7l279.6 279.7H639.9L360.2 779.7 639.9 500h319.5zM639.9 20.7L120.6 540l159.8 159.8 679-679.1H639.9z"
|
||||
/>
|
||||
</defs>
|
||||
<clipPath id="B"><use xlink:href="#A" /></clipPath>
|
||||
<g clip-path="url(#B)">
|
||||
<path d="M360.3 779.7L520 939.5 959.4 500H639.9z" fill="#39cefd" />
|
||||
</g>
|
||||
<defs>
|
||||
<path
|
||||
id="C"
|
||||
d="M959.4 500L679.8 779.7l279.6 279.7H639.9L360.2 779.7 639.9 500h319.5zM639.9 20.7L120.6 540l159.8 159.8 679-679.1H639.9z"
|
||||
/>
|
||||
</defs>
|
||||
<clipPath id="D"><use xlink:href="#C" /></clipPath>
|
||||
<path
|
||||
clip-path="url(#D)"
|
||||
d="M639.9 20.7h319.5l-679 679.1L120.6 540z"
|
||||
fill="#39cefd"
|
||||
/>
|
||||
<defs>
|
||||
<path
|
||||
id="E"
|
||||
d="M959.4 500L679.8 779.7l279.6 279.7H639.9L360.2 779.7 639.9 500h319.5zM639.9 20.7L120.6 540l159.8 159.8 679-679.1H639.9z"
|
||||
/>
|
||||
</defs>
|
||||
<clipPath id="F"><use xlink:href="#E" /></clipPath>
|
||||
<path
|
||||
clip-path="url(#F)"
|
||||
d="M520 939.5l119.9 119.8h319.5L679.8 779.7z"
|
||||
fill="#03569b"
|
||||
/>
|
||||
<defs>
|
||||
<path
|
||||
id="G"
|
||||
d="M959.4 500L679.8 779.7l279.6 279.7H639.9L360.2 779.7 639.9 500h319.5zM639.9 20.7L120.6 540l159.8 159.8 679-679.1H639.9z"
|
||||
/>
|
||||
</defs>
|
||||
<clipPath id="H"><use xlink:href="#G" /></clipPath>
|
||||
<linearGradient
|
||||
id="I"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="566.635"
|
||||
y1="970.339"
|
||||
x2="685.65"
|
||||
y2="851.324"
|
||||
>
|
||||
<stop offset="0" stop-color="#1a237e" stop-opacity=".4" />
|
||||
<stop offset="1" stop-color="#1a237e" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<path
|
||||
clip-path="url(#H)"
|
||||
d="M757 857.4l-77.2-77.7L520 939.5z"
|
||||
fill="url(#I)"
|
||||
/>
|
||||
<defs>
|
||||
<path
|
||||
id="J"
|
||||
d="M959.4 500L679.8 779.7l279.6 279.7H639.9L360.2 779.7 639.9 500h319.5zM639.9 20.7L120.6 540l159.8 159.8 679-679.1H639.9z"
|
||||
/>
|
||||
</defs>
|
||||
<clipPath id="K"><use xlink:href="#J" /></clipPath>
|
||||
<g clip-path="url(#K)">
|
||||
<path
|
||||
d="M360.282 779.645L520.086 619.84 679.9 779.645 520.086 939.45z"
|
||||
fill="#16b9fd"
|
||||
/>
|
||||
</g>
|
||||
<radialGradient
|
||||
id="L"
|
||||
cx="7824.659"
|
||||
cy="-2855.979"
|
||||
r="5082.889"
|
||||
gradientTransform="matrix(0.25,0,0,-0.25,-1812,-622.5)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0" stop-color="#fff" stop-opacity=".1" />
|
||||
<stop offset="1" stop-color="#fff" stop-opacity="0" />
|
||||
</radialGradient>
|
||||
<path
|
||||
d="M959.4 500L679.8 779.7l279.6 279.7H639.9L360.2 779.7 639.9 500h319.5zM639.9 20.7L120.6 540l159.8 159.8 679-679.1H639.9z"
|
||||
fill="url(#L)"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
@@ -43,7 +43,7 @@ Our approach for client libraries is modular. Each sub-library is a standalone i
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Dart (Flutter)</td>
|
||||
<td>Flutter</td>
|
||||
<td><a href="https://github.com/supabase/supabase-dart" target="_blank" rel="noopener noreferrer">supabase-dart</a></td>
|
||||
<td><a href="https://github.com/supabase/postgrest-dart" target="_blank" rel="noopener noreferrer">postgrest-dart</a></td>
|
||||
<td><a href="https://github.com/supabase/gotrue-dart" target="_blank" rel="noopener noreferrer">gotrue-lang</a></td>
|
||||
|
||||
@@ -43,7 +43,7 @@ Our approach for client libraries is modular. Each sub-library is a standalone i
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Dart (Flutter)</td>
|
||||
<td>Flutter</td>
|
||||
<td><a href="https://github.com/supabase/supabase-dart" target="_blank" rel="noopener noreferrer">supabase-dart</a></td>
|
||||
<td><a href="https://github.com/supabase/postgrest-dart" target="_blank" rel="noopener noreferrer">postgrest-dart</a></td>
|
||||
<td><a href="https://github.com/supabase/gotrue-dart" target="_blank" rel="noopener noreferrer">gotrue-lang</a></td>
|
||||
|
||||
@@ -16,7 +16,6 @@ cd supabase
|
||||
npm install
|
||||
npm run dev
|
||||
|
||||
|
||||
# visit website
|
||||
http://localhost:3000
|
||||
```
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Supabase Examples Archive
|
||||
|
||||
## supabase-js v1
|
||||
|
||||
You can find the supabase-js v1 examples at [github.com/supabase/examples-archive](https://github.com/supabase/examples-archive).
|
||||
@@ -1,3 +0,0 @@
|
||||
dist/*
|
||||
|
||||
.vercel
|
||||
@@ -1,14 +0,0 @@
|
||||
# Vanilla-js Auth Example
|
||||
|
||||
Live Example: [https://auth-vanilla-js.vercel.app/](https://auth-vanilla-js.vercel.app/)
|
||||
|
||||
How to sign up and login using supabase and supabase-js using HTML and JavaScript only
|
||||
|
||||
<img width="558" alt="image" src="https://user-images.githubusercontent.com/458736/88377414-b6fb4180-cdd1-11ea-8061-103ec4577b7b.png">
|
||||
|
||||
### running
|
||||
|
||||
`npm run dev` (requires npx to be installed)
|
||||
|
||||
if you want to make changes without restarting the server run this in a different terminal window:
|
||||
`npm run watch`
|
||||
@@ -1,16 +0,0 @@
|
||||
body {
|
||||
/* margin: 0; */
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
|
||||
'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
background-color: rgb(250, 217, 246);
|
||||
}
|
||||
|
||||
label {
|
||||
margin: 0.5em;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin: 1em;
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@1"></script>
|
||||
<script src="./index.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="./index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class='container'>
|
||||
<div class='section'>
|
||||
<h1>Supabase Auth Example</h1>
|
||||
</div>
|
||||
<div class='section'>
|
||||
<a href="https://github.com/supabase/supabase/tree/master/examples/auth/javascript-auth">View the code on GitHub</a>
|
||||
</div>
|
||||
<div class='section'>
|
||||
<form id='sign-up'>
|
||||
<h3>Sign Up</h3>
|
||||
<label>Email</label><input type='email' name='email' />
|
||||
<label>Password</label><input type='password' name='password' />
|
||||
<input type='submit'>
|
||||
</form>
|
||||
</div>
|
||||
<div class='section'>
|
||||
<form id='log-in'>
|
||||
<h3>Log In</h3>
|
||||
<label>Email</label><input type='email' name='email' />
|
||||
<label>Password</label><input type='password' name='password' />
|
||||
<input type='submit'>
|
||||
</form>
|
||||
</div>
|
||||
<div class='section'>
|
||||
<form id='validate'>
|
||||
<h3>Access Token</h3>
|
||||
<input readonly=readonly type='text' id='access-token' /> <small>Default expiry is 60 minutes</small>
|
||||
<h3>Refresh Token</h3>
|
||||
<input readonly=readonly type='text' id='refresh-token' /> <small>Supabase-js will use this to automatically fetch a new accessToken for you every 60 mins whilst the client is running</small>
|
||||
</form>
|
||||
</div>
|
||||
<div class='section'>
|
||||
<h3>Fetch User Details</h3>
|
||||
<button id='user-button'>Fetch</button>
|
||||
</div>
|
||||
<div class='section'>
|
||||
<h3>Logout</h3>
|
||||
<button id='logout-button'>Logout</button>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,79 +0,0 @@
|
||||
var SUPABASE_URL = 'https://ernhobnpmmupjnmxpfbt.supabase.co'
|
||||
var SUPABASE_KEY =
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlhdCI6MTYxMzA5ODU0NCwiZXhwIjoxOTI4Njc0NTQ0fQ.Z9bRrfaL2oGhSuyBckFcdcnRIJDelWJ1II98OnEtLO0'
|
||||
|
||||
var supabase = supabase.createClient(SUPABASE_URL, SUPABASE_KEY)
|
||||
window.userToken = null
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function (event) {
|
||||
var signUpForm = document.querySelector('#sign-up')
|
||||
signUpForm.onsubmit = signUpSubmitted.bind(signUpForm)
|
||||
|
||||
var logInForm = document.querySelector('#log-in')
|
||||
logInForm.onsubmit = logInSubmitted.bind(logInForm)
|
||||
|
||||
var userDetailsButton = document.querySelector('#user-button')
|
||||
userDetailsButton.onclick = fetchUserDetails.bind(userDetailsButton)
|
||||
|
||||
var logoutButton = document.querySelector('#logout-button')
|
||||
logoutButton.onclick = logoutSubmitted.bind(logoutButton)
|
||||
})
|
||||
|
||||
const signUpSubmitted = (event) => {
|
||||
event.preventDefault()
|
||||
const email = event.target[0].value
|
||||
const password = event.target[1].value
|
||||
|
||||
supabase.auth
|
||||
.signUp({ email, password })
|
||||
.then((response) => {
|
||||
response.error ? alert(response.error.message) : setToken(response)
|
||||
})
|
||||
.catch((err) => {
|
||||
alert(err)
|
||||
})
|
||||
}
|
||||
|
||||
const logInSubmitted = (event) => {
|
||||
event.preventDefault()
|
||||
const email = event.target[0].value
|
||||
const password = event.target[1].value
|
||||
|
||||
supabase.auth
|
||||
.signIn({ email, password })
|
||||
.then((response) => {
|
||||
response.error ? alert(response.error.message) : setToken(response)
|
||||
})
|
||||
.catch((err) => {
|
||||
alert(err.response.text)
|
||||
})
|
||||
}
|
||||
|
||||
const fetchUserDetails = () => {
|
||||
alert(JSON.stringify(supabase.auth.user()))
|
||||
}
|
||||
|
||||
const logoutSubmitted = (event) => {
|
||||
event.preventDefault()
|
||||
|
||||
supabase.auth
|
||||
.signOut()
|
||||
.then((_response) => {
|
||||
document.querySelector('#access-token').value = ''
|
||||
document.querySelector('#refresh-token').value = ''
|
||||
alert('Logout successful')
|
||||
})
|
||||
.catch((err) => {
|
||||
alert(err.response.text)
|
||||
})
|
||||
}
|
||||
|
||||
function setToken(response) {
|
||||
if (response.user.confirmation_sent_at && !response?.session?.access_token) {
|
||||
alert('Confirmation Email Sent')
|
||||
} else {
|
||||
document.querySelector('#access-token').value = response.session.access_token
|
||||
document.querySelector('#refresh-token').value = response.session.refresh_token
|
||||
alert('Logged in as ' + response.user.email)
|
||||
}
|
||||
}
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"name": "supabase-auth-vanilla-js",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 1
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"name": "supabase-auth-vanilla-js",
|
||||
"version": "0.0.1",
|
||||
"description": "basic auth example for supabase with js",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev": "npx light-server -s ."
|
||||
},
|
||||
"author": "ant@supabase.io",
|
||||
"license": "MIT"
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 46 KiB |
@@ -1,3 +0,0 @@
|
||||
# Update these with your Supabase details from your project settings > API
|
||||
NEXT_PUBLIC_SUPABASE_URL=
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=
|
||||
@@ -1,35 +0,0 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/
|
||||
.env
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 2,
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<img src="./.assets/Screen.png">
|
||||
|
||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
|
||||
|
||||
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.
|
||||
|
||||
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
@@ -1,12 +0,0 @@
|
||||
import { default as HeadContainer } from 'next/head'
|
||||
|
||||
const Head = () => {
|
||||
return (
|
||||
<HeadContainer>
|
||||
<title>Supabase Example</title>
|
||||
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
|
||||
</HeadContainer>
|
||||
)
|
||||
}
|
||||
|
||||
export default Head
|
||||
@@ -1,87 +0,0 @@
|
||||
import React from 'react'
|
||||
import AuthUser from '../../hooks/authUser'
|
||||
import { Disclosure } from '@headlessui/react'
|
||||
import { MenuIcon, XIcon } from '@heroicons/react/outline'
|
||||
import MenuLogado from './menuLogado'
|
||||
import MenuNotLogado from './menuNotLogado'
|
||||
import Navigation from './navigation'
|
||||
import classNames from '../../utils/classsesNames'
|
||||
|
||||
export default function Header() {
|
||||
return (
|
||||
<Disclosure as="nav" className="bg-gray-800">
|
||||
{({ open }) => (
|
||||
<>
|
||||
<div className="max-w-7xl mx-auto px-2 sm:px-6 lg:px-8">
|
||||
<div className="relative flex items-center justify-between h-16">
|
||||
<div className="absolute inset-y-0 left-0 flex items-center sm:hidden">
|
||||
{/* Mobile menu button*/}
|
||||
<Disclosure.Button className="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white">
|
||||
<span className="sr-only">Open main menu</span>
|
||||
{open ? (
|
||||
<XIcon className="block h-6 w-6" aria-hidden="true" />
|
||||
) : (
|
||||
<MenuIcon className="block h-6 w-6" aria-hidden="true" />
|
||||
)}
|
||||
</Disclosure.Button>
|
||||
</div>
|
||||
<div className="flex-1 flex items-center justify-center sm:items-stretch sm:justify-start">
|
||||
<div className="flex-shrink-0 flex items-center text-white">
|
||||
<img
|
||||
className="h-8 w-auto"
|
||||
src="https://supabase.com/images/logo-dark.png"
|
||||
alt="supabase"
|
||||
/>
|
||||
</div>
|
||||
<div className="hidden sm:block sm:ml-6">
|
||||
<div className="flex space-x-4">
|
||||
{Navigation.map((item) => (
|
||||
<a
|
||||
key={item.name}
|
||||
href={item.href}
|
||||
className={classNames(
|
||||
item.current
|
||||
? 'bg-gray-900 text-white'
|
||||
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
||||
'px-3 py-2 rounded-md text-sm font-medium'
|
||||
)}
|
||||
aria-current={item.current ? 'page' : undefined}
|
||||
>
|
||||
{item.name}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0">
|
||||
{/** notifications */}
|
||||
|
||||
{AuthUser() ? <MenuLogado /> : <MenuNotLogado />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Disclosure.Panel className="sm:hidden">
|
||||
<div className="px-2 pt-2 pb-3 space-y-1">
|
||||
{Navigation.map((item) => (
|
||||
<a
|
||||
key={item.name}
|
||||
href={item.href}
|
||||
className={classNames(
|
||||
item.current
|
||||
? 'bg-gray-900 text-white'
|
||||
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
||||
'block px-3 py-2 rounded-md text-base font-medium'
|
||||
)}
|
||||
aria-current={item.current ? 'page' : undefined}
|
||||
>
|
||||
{item.name}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</Disclosure.Panel>
|
||||
</>
|
||||
)}
|
||||
</Disclosure>
|
||||
)
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
import { Fragment } from 'react'
|
||||
import { Menu, Transition } from '@headlessui/react'
|
||||
import { UserCircleIcon } from '@heroicons/react/outline'
|
||||
import classNames from '../../utils/classsesNames'
|
||||
|
||||
import { SignOut } from '../../hooks/authUser'
|
||||
|
||||
const MenuLogado = () => (
|
||||
<Menu as="div" className="ml-3 relative">
|
||||
{({ open }) => (
|
||||
<>
|
||||
<div>
|
||||
<Menu.Button className="bg-gray-800 flex text-sm rounded-full focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-white">
|
||||
<span className="sr-only">Open user menu</span>
|
||||
<UserCircleIcon className="h-8 w-8 text-white" />
|
||||
</Menu.Button>
|
||||
</div>
|
||||
<Transition
|
||||
show={open}
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items
|
||||
static
|
||||
className="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none"
|
||||
>
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
<a
|
||||
href="/profile"
|
||||
className={classNames(
|
||||
active ? 'bg-gray-100' : '',
|
||||
'block px-4 py-2 text-sm text-gray-700'
|
||||
)}
|
||||
>
|
||||
Your Profile
|
||||
</a>
|
||||
)}
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
<a
|
||||
href="/#"
|
||||
className={classNames(
|
||||
active ? 'bg-gray-100' : '',
|
||||
'block px-4 py-2 text-sm text-gray-700'
|
||||
)}
|
||||
>
|
||||
Settings
|
||||
</a>
|
||||
)}
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<button onClick={() => SignOut()} className="block px-4 py-2 text-sm text-gray-700">
|
||||
Sign out
|
||||
</button>
|
||||
</Menu.Item>
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
</>
|
||||
)}
|
||||
</Menu>
|
||||
)
|
||||
|
||||
export default MenuLogado
|
||||
@@ -1,11 +0,0 @@
|
||||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
const MenuNotLogado = () => (
|
||||
<div className="flex space-x-4">
|
||||
<Link href="/auth">
|
||||
<a className="text-white hover:bg-gray-700 px-3 py-2 rounded-md text-sm font-medium">LOGIN</a>
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
|
||||
export default MenuNotLogado
|
||||
@@ -1,7 +0,0 @@
|
||||
const Navigation = [
|
||||
{ name: 'Home', href: '/', current: true },
|
||||
{ name: 'Jobs', href: '#', current: false },
|
||||
{ name: 'Developers', href: '#', current: false },
|
||||
]
|
||||
|
||||
export default Navigation
|
||||
@@ -1,71 +0,0 @@
|
||||
import { useEffect, useState, createContext, useContext } from 'react'
|
||||
import { supabase } from '../utils/initSupabase'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
export const SignOut = async () => {
|
||||
await supabase.auth.signOut()
|
||||
}
|
||||
|
||||
export const RequireAuth = () => {
|
||||
const { user } = useUser()
|
||||
const router = useRouter()
|
||||
|
||||
useEffect(() => {
|
||||
if (!user) {
|
||||
router.push('/auth')
|
||||
}
|
||||
}, [user, router])
|
||||
}
|
||||
|
||||
export const AuthRedirect = () => {
|
||||
const { user } = useUser()
|
||||
const router = useRouter()
|
||||
|
||||
useEffect(() => {
|
||||
if (user) {
|
||||
router.push('/profile')
|
||||
}
|
||||
}, [user, router])
|
||||
}
|
||||
|
||||
export const UserContext = createContext()
|
||||
|
||||
export const UserContextProvider = (props) => {
|
||||
const [session, setSession] = useState(false)
|
||||
const [user, setUser] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const session = supabase.auth.session()
|
||||
setSession(session)
|
||||
setUser(session?.user ?? false)
|
||||
const { data: authListener } = supabase.auth.onAuthStateChange(async (event, session) => {
|
||||
setSession(session)
|
||||
setUser(session?.user ?? false)
|
||||
})
|
||||
|
||||
return () => {
|
||||
authListener.unsubscribe()
|
||||
}
|
||||
}, [])
|
||||
|
||||
const value = {
|
||||
session,
|
||||
user,
|
||||
}
|
||||
return <UserContext.Provider value={value} {...props} />
|
||||
}
|
||||
|
||||
export const useUser = () => {
|
||||
const context = useContext(UserContext)
|
||||
if (context === undefined) {
|
||||
throw new Error(`useUser must be used within a UserContextProvider.`)
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
const AuthUser = () => {
|
||||
const { user } = useUser()
|
||||
return user
|
||||
}
|
||||
|
||||
export default AuthUser
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"name": "nextjs-auth-tailwind",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"format": "prettier --write './**/*.{js,jsx,ts,tsx,css,md,json}' --config ./.prettierrc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^1.2.0",
|
||||
"@heroicons/react": "^1.0.1",
|
||||
"@supabase/supabase-js": "^1.11.15",
|
||||
"@supabase/ui": "^0.26.1",
|
||||
"@tailwindcss/typography": "^0.4.0",
|
||||
"next": "12.0.9",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.2.5",
|
||||
"postcss": "^8.3.0",
|
||||
"prettier": "^2.3.0",
|
||||
"tailwindcss": "^2.1.2"
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import React from 'react'
|
||||
import { UserContextProvider } from '../hooks/authUser'
|
||||
|
||||
import 'tailwindcss/tailwind.css'
|
||||
import '../styles/globals.css'
|
||||
|
||||
export default function MyApp({ Component, pageProps }) {
|
||||
return (
|
||||
<main className={'dark'}>
|
||||
<UserContextProvider>
|
||||
<Component {...pageProps} />
|
||||
</UserContextProvider>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import React from 'react'
|
||||
import { AuthRedirect } from '../hooks/authUser'
|
||||
import { supabase } from '../utils/initSupabase'
|
||||
import { Auth, Card, Typography, Space } from '@supabase/ui'
|
||||
|
||||
import Head from '../components/Head'
|
||||
import Header from '../components/Header'
|
||||
|
||||
const AuthPage = () => {
|
||||
AuthRedirect()
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head />
|
||||
<Header />
|
||||
<div className="authcontainer">
|
||||
<Card>
|
||||
<Space direction="vertical" size={8}>
|
||||
<div>
|
||||
<Typography.Title level={3}>Welcome</Typography.Title>
|
||||
</div>
|
||||
<Auth
|
||||
supabaseClient={supabase}
|
||||
providers={['google', 'github']}
|
||||
view={'sign_in'}
|
||||
socialLayout="horizontal"
|
||||
socialButtonSize="xlarge"
|
||||
/>
|
||||
</Space>
|
||||
</Card>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default AuthPage
|
||||
@@ -1,44 +0,0 @@
|
||||
import Header from '../components/Header'
|
||||
import Head from '../components/Head'
|
||||
|
||||
const Index = () => {
|
||||
return (
|
||||
<>
|
||||
<Head />
|
||||
|
||||
<Header />
|
||||
<main className="mt-10 mx-auto max-w-7xl px-4 sm:mt-12 sm:px-6 md:mt-16 lg:mt-20 lg:px-8 xl:mt-28">
|
||||
<div className="sm:text-center lg:text-left">
|
||||
<h1 className="text-4xl tracking-tight font-extrabold text-white sm:text-5xl md:text-6xl">
|
||||
<span className="block xl:inline">The Open Source</span>{' '}
|
||||
<span className="block text-green-400 xl:inline">Firebase Alternative</span>
|
||||
</h1>
|
||||
<p className="mt-3 text-base text-white sm:mt-5 sm:text-lg sm:max-w-xl sm:mx-auto md:mt-5 md:text-xl lg:mx-0">
|
||||
Create a backend in less than 2 minutes. Start your project with a Postgres Database,
|
||||
Authentication, instant APIs, Realtime subscriptions and Storage.
|
||||
</p>
|
||||
<div className="mt-5 sm:mt-8 sm:flex sm:justify-center lg:justify-start">
|
||||
<div className="rounded-md shadow">
|
||||
<a
|
||||
href="#"
|
||||
className="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-white bg-green-400 hover:bg-green-600 md:py-4 md:text-lg md:px-10"
|
||||
>
|
||||
Get started
|
||||
</a>
|
||||
</div>
|
||||
<div className="mt-3 sm:mt-0 sm:ml-3">
|
||||
<a
|
||||
href="#"
|
||||
className="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-green-400 bg-indigo-100 hover:bg-indigo-200 md:py-4 md:text-lg md:px-10"
|
||||
>
|
||||
Live demo
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Index
|
||||
@@ -1,32 +0,0 @@
|
||||
import React from 'react'
|
||||
import { useUser, RequireAuth } from '../hooks/authUser'
|
||||
|
||||
import { Card, Typography, Space } from '@supabase/ui'
|
||||
import Header from '../components/Header'
|
||||
|
||||
export default function Profile() {
|
||||
RequireAuth()
|
||||
|
||||
const { user } = useUser()
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
{user && (
|
||||
<div style={{ maxWidth: '620px', margin: '96px auto' }}>
|
||||
<Card>
|
||||
<Space direction="vertical" size={6}>
|
||||
<Typography.Text>you're signed in</Typography.Text>
|
||||
<Typography.Text strong>Email: {user.email}</Typography.Text>
|
||||
<Typography.Text type="success">User data:</Typography.Text>
|
||||
|
||||
<Typography.Text>
|
||||
<pre>{JSON.stringify(user, null, 2)}</pre>
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
@@ -1,4 +0,0 @@
|
||||
<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,29 +0,0 @@
|
||||
body {
|
||||
background: #181818;
|
||||
}
|
||||
|
||||
/*
|
||||
* Auth
|
||||
*/
|
||||
|
||||
.authcontainer {
|
||||
max-width: 420px;
|
||||
margin: 96px auto;
|
||||
}
|
||||
|
||||
.authcontainer a {
|
||||
color: #8b5cf6 !important;
|
||||
}
|
||||
|
||||
.authcontainer button[type='submit'] {
|
||||
background: #8b5cf6 !important;
|
||||
}
|
||||
|
||||
.authcontainer input:focus {
|
||||
border-color: #8b5cf6 !important;
|
||||
}
|
||||
|
||||
.authcontainer .sbui-typography-text-danger {
|
||||
top: 3px !important;
|
||||
position: absolute !important;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
module.exports = {
|
||||
purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
|
||||
darkMode: false, // or 'media' or 'class'
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
variants: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [require('@tailwindcss/typography')],
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
export default classNames
|
||||
@@ -1,6 +0,0 @@
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
|
||||
export const supabase = createClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
|
||||
)
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,3 +0,0 @@
|
||||
# Update these with your Supabase details from your project settings > API
|
||||
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
|
||||
@@ -1,34 +0,0 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
@@ -1,5 +0,0 @@
|
||||
# Example: Supabase authentication client- and server-side (API routes), and SSR with auth cookie.
|
||||
|
||||
This example shows how to use Supabase auth both on the client and server in both [API routes](https://nextjs.org/docs/api-routes/introduction) and when using [server side rendering (SSR)](https://nextjs.org/docs/basic-features/pages#server-side-rendering).
|
||||
|
||||
This example is the same as [nextjs-with-supabase-auth](https://github.com/supabase/supabase/tree/master/examples/nextjs-with-supabase-auth) however it does not use the @supabase/ui Auth component.
|
||||
@@ -1,209 +0,0 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
function Auth(props) {
|
||||
const { supabaseClient, authView, setAuthView } = props
|
||||
|
||||
const [email, setEmail] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
const [message, setMessage] = useState('')
|
||||
const [error, setError] = useState('')
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const handleSignIn = async (e) => {
|
||||
e.preventDefault()
|
||||
setError('')
|
||||
setLoading(true)
|
||||
|
||||
const { error: signInError } = await supabaseClient.auth.signIn({
|
||||
email,
|
||||
password,
|
||||
})
|
||||
if (signInError) setError(signInError.message)
|
||||
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
const handleSignUp = async (e) => {
|
||||
e.preventDefault()
|
||||
setError('')
|
||||
setLoading(true)
|
||||
|
||||
const { error: signUpError } = await supabaseClient.auth.signUp({
|
||||
email,
|
||||
password,
|
||||
})
|
||||
if (signUpError) setError(signUpError.message)
|
||||
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
const handlePasswordReset = async (e) => {
|
||||
e.preventDefault()
|
||||
setError('')
|
||||
setMessage('')
|
||||
setLoading(true)
|
||||
const { error } = await supabaseClient.auth.api.resetPasswordForEmail(email)
|
||||
if (error) setError(error.message)
|
||||
else setMessage('Check your email for the password reset link')
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
const handleMagicLinkSignIn = async (e) => {
|
||||
e.preventDefault()
|
||||
setError('')
|
||||
setMessage('')
|
||||
setLoading(true)
|
||||
const { error } = await supabaseClient.auth.signIn({ email })
|
||||
if (error) setError(error.message)
|
||||
else setMessage('Check your email for the magic link')
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{loading && <h3>Loading..</h3>}
|
||||
{error && <div style={{ color: 'red' }}>{error}</div>}
|
||||
{message && <div style={{ color: 'green' }}>{message}</div>}
|
||||
{authView === 'sign_in' ? (
|
||||
<>
|
||||
<h4>Sign in</h4>
|
||||
<form onSubmit={(e) => handleSignIn(e)}>
|
||||
<label htmlFor="sign-in__email">Email</label>
|
||||
<input
|
||||
id="sign-in__email"
|
||||
label="Email address"
|
||||
autoComplete="email"
|
||||
placeholder="Type in your email address"
|
||||
defaultValue={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
<label htmlFor="sign-in__password">Password</label>
|
||||
<input
|
||||
id="sign-in__password"
|
||||
label="Password"
|
||||
type="password"
|
||||
defaultValue={password}
|
||||
autoComplete="current-password"
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
<button type="submit">SignIn</button>
|
||||
</form>
|
||||
<hr />
|
||||
<a onClick={() => setAuthView('sign_up')}>Don't have an account? Sign up</a>
|
||||
<a onClick={() => setAuthView('forgotten_password')}>Forgot my password</a>
|
||||
<hr />
|
||||
<a onClick={() => setAuthView('magic_link')}>Send magic link email</a>
|
||||
</>
|
||||
) : authView === 'sign_up' ? (
|
||||
<>
|
||||
<h4>Sign up</h4>
|
||||
<form onSubmit={(e) => handleSignUp(e)}>
|
||||
<label htmlFor="sign-up__email">Email</label>
|
||||
<input
|
||||
id="sign-up__email"
|
||||
label="Email address"
|
||||
autoComplete="email"
|
||||
placeholder="Type in your email address"
|
||||
defaultValue={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
<label htmlFor="sign-up__password">Password</label>
|
||||
<input
|
||||
id="sign-up__password"
|
||||
label="Password"
|
||||
type="password"
|
||||
defaultValue={password}
|
||||
autoComplete="current-password"
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
<button type="submit">SignUp</button>
|
||||
</form>
|
||||
<hr />
|
||||
<a onClick={() => setAuthView('sign_in')}>Already have an account, Sign in</a>
|
||||
<a onClick={() => setAuthView('forgotten_password')}>Forgot my password</a>
|
||||
<hr />
|
||||
<a onClick={() => setAuthView('magic_link')}>Send magic link email</a>
|
||||
</>
|
||||
) : authView === 'forgotten_password' ? (
|
||||
<>
|
||||
<h4>Forgotten password</h4>
|
||||
<form onSubmit={handlePasswordReset}>
|
||||
<label htmlFor="forgotten_password__email">Email</label>
|
||||
<input
|
||||
id="forgotten_password__email"
|
||||
label="Email address"
|
||||
autoComplete="email"
|
||||
placeholder="Type in your email address"
|
||||
defaultValue={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
<button type="submit">Send reset password instructions</button>
|
||||
</form>
|
||||
<hr />
|
||||
<a onClick={() => setAuthView('sign_up')}>Don't have an account? Sign up</a>
|
||||
<a onClick={() => setAuthView('sign_in')}>Already have an account, Sign in</a>
|
||||
<hr />
|
||||
<a onClick={() => setAuthView('magic_link')}>Send magic link email</a>
|
||||
</>
|
||||
) : authView === 'magic_link' ? (
|
||||
<>
|
||||
<h4>Magic link</h4>
|
||||
<form onSubmit={handleMagicLinkSignIn}>
|
||||
<input
|
||||
label="Email address"
|
||||
autoComplete="email"
|
||||
placeholder="Type in your email address"
|
||||
defaultValue={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
<button type="submit">Send magic link</button>
|
||||
</form>
|
||||
<hr />
|
||||
<a onClick={() => setAuthView('sign_up')}>Don't have an account? Sign up</a>
|
||||
<a onClick={() => setAuthView('sign_in')}>Already have an account, Sign in</a>
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function UpdatePassword({ supabaseClient }) {
|
||||
const [password, setPassword] = useState('')
|
||||
const [error, setError] = useState('')
|
||||
const [message, setMessage] = useState('')
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const handlePasswordReset = async (e) => {
|
||||
e.preventDefault()
|
||||
setError('')
|
||||
setMessage('')
|
||||
setLoading(true)
|
||||
const { error } = await supabaseClient.auth.update({ password })
|
||||
if (error) setError(error.message)
|
||||
else setMessage('Your password has been updated')
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{loading && <h3>Loading..</h3>}
|
||||
{error && <div style={{ color: 'red' }}>{error}</div>}
|
||||
{message && <div style={{ color: 'green' }}>{message}</div>}
|
||||
<h4>Set a new password</h4>
|
||||
<form onSubmit={handlePasswordReset}>
|
||||
<input
|
||||
label="New password"
|
||||
placeholder="Enter your new password"
|
||||
type="password"
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
<button block size="large" htmlType="submit">
|
||||
Update password
|
||||
</button>
|
||||
</form>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Auth.UpdatePassword = UpdatePassword
|
||||
export default Auth
|
||||
@@ -1,38 +0,0 @@
|
||||
import React, { useEffect, useState, createContext, useContext } from 'react'
|
||||
|
||||
const UserContext = createContext({ user: null, session: null })
|
||||
|
||||
export const UserContextProvider = (props) => {
|
||||
const { supabaseClient } = props
|
||||
const [session, setSession] = useState(null)
|
||||
const [user, setUser] = useState(null)
|
||||
|
||||
useEffect(() => {
|
||||
const session = supabaseClient.auth.session()
|
||||
setSession(session)
|
||||
setUser(session?.user ?? null)
|
||||
const { data: authListener } = supabaseClient.auth.onAuthStateChange(async (event, session) => {
|
||||
setSession(session)
|
||||
setUser(session?.user ?? null)
|
||||
})
|
||||
|
||||
return () => {
|
||||
authListener.unsubscribe()
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
const value = {
|
||||
session,
|
||||
user,
|
||||
}
|
||||
return <UserContext.Provider value={value} {...props} />
|
||||
}
|
||||
|
||||
export const useUser = () => {
|
||||
const context = useContext(UserContext)
|
||||
if (context === undefined) {
|
||||
throw new Error(`useUser must be used within a UserContextProvider.`)
|
||||
}
|
||||
return context
|
||||
}
|
||||
-5571
File diff suppressed because it is too large.
Load diff
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "with-supabase-auth",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"@supabase/supabase-js": "^1.2.1",
|
||||
"next": "12.0.9",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"swr": "0.2.3"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { UserContextProvider } from '../lib/UserContext'
|
||||
import { supabase } from '../utils/initSupabase'
|
||||
import './../style.css'
|
||||
|
||||
export default function MyApp({ Component, pageProps }) {
|
||||
return (
|
||||
<main>
|
||||
<UserContextProvider supabaseClient={supabase}>
|
||||
<Component {...pageProps} />
|
||||
</UserContextProvider>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* NOTE: this file is only needed if you're doing SSR (getServerSideProps)!
|
||||
*/
|
||||
import { supabase } from '../../utils/initSupabase'
|
||||
|
||||
export default function handler(req, res) {
|
||||
supabase.auth.api.setAuthCookie(req, res)
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { supabase } from '../../utils/initSupabase'
|
||||
|
||||
// Example of how to verify and get user data server-side.
|
||||
const getUser = async (req, res) => {
|
||||
const token = req.headers.token
|
||||
|
||||
const { data: user, error } = await supabase.auth.api.getUser(token)
|
||||
|
||||
if (error) return res.status(401).json({ error: error.message })
|
||||
return res.status(200).json(user)
|
||||
}
|
||||
|
||||
export default getUser
|
||||
@@ -1,95 +0,0 @@
|
||||
import Link from 'next/link'
|
||||
import useSWR from 'swr'
|
||||
import { supabase } from '../utils/initSupabase'
|
||||
import { useEffect, useState } from 'react'
|
||||
import Auth from './../components/Auth'
|
||||
import { useUser } from '../lib/UserContext'
|
||||
|
||||
const fetcher = (url, token) =>
|
||||
fetch(url, {
|
||||
method: 'GET',
|
||||
headers: new Headers({ 'Content-Type': 'application/json', token }),
|
||||
credentials: 'same-origin',
|
||||
}).then((res) => res.json())
|
||||
|
||||
const Index = () => {
|
||||
const { user, session } = useUser()
|
||||
const { data, error } = useSWR(session ? ['/api/getUser', session.access_token] : null, fetcher)
|
||||
const [authView, setAuthView] = useState('sign_in')
|
||||
|
||||
useEffect(() => {
|
||||
const { data: authListener } = supabase.auth.onAuthStateChange((event, session) => {
|
||||
if (event === 'PASSWORD_RECOVERY') setAuthView('update_password')
|
||||
if (event === 'USER_UPDATED') setTimeout(() => setAuthView('sign_in'), 1000)
|
||||
// Send session to /api/auth route to set the auth cookie.
|
||||
// NOTE: this is only needed if you're doing SSR (getServerSideProps)!
|
||||
fetch('/api/auth', {
|
||||
method: 'POST',
|
||||
headers: new Headers({ 'Content-Type': 'application/json' }),
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({ event, session }),
|
||||
}).then((res) => res.json())
|
||||
})
|
||||
|
||||
return () => {
|
||||
authListener.unsubscribe()
|
||||
}
|
||||
}, [])
|
||||
|
||||
const View = () => {
|
||||
if (!user)
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<img src="https://app.supabase.com/img/supabase-dark.svg" width="96" />
|
||||
<h2>
|
||||
Supabase Auth <br />
|
||||
with NextJS SSR
|
||||
</h2>
|
||||
</div>
|
||||
<Auth supabaseClient={supabase} authView={authView} setAuthView={setAuthView} />
|
||||
</>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
{authView === 'update_password' && <Auth.UpdatePassword supabaseClient={supabase} />}
|
||||
{user && (
|
||||
<>
|
||||
<h4>You're signed in</h4>
|
||||
<h5>Email: {user.email}</h5>
|
||||
|
||||
<button type="outline" onClick={() => supabase.auth.signOut()}>
|
||||
Log out
|
||||
</button>
|
||||
<hr />
|
||||
{error && <div style={{ color: 'red' }}>Failed to fetch user!</div>}
|
||||
{data && !error ? (
|
||||
<>
|
||||
<div style={{ color: 'green' }}>
|
||||
User data retrieved server-side (in API route):
|
||||
</div>
|
||||
|
||||
<pre>{JSON.stringify(data, null, 2)}</pre>
|
||||
</>
|
||||
) : (
|
||||
<div>Loading...</div>
|
||||
)}
|
||||
|
||||
<Link href="/profile">
|
||||
<a>SSR example with getServerSideProps</a>
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ maxWidth: '520px', margin: '96px auto' }}>
|
||||
<View />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Index
|
||||
@@ -1,37 +0,0 @@
|
||||
import Link from 'next/link'
|
||||
import { supabase } from '../utils/initSupabase'
|
||||
|
||||
export default function Profile({ user }) {
|
||||
return (
|
||||
<div style={{ maxWidth: '520px', margin: '96px auto' }}>
|
||||
<h4>You're signed in</h4>
|
||||
<h5>Email: {user.email}</h5>
|
||||
<hr />
|
||||
|
||||
<div style={{ color: 'green' }}>
|
||||
User data retrieved server-side (from Cookie in getServerSideProps):
|
||||
</div>
|
||||
<div>
|
||||
<pre>{JSON.stringify(user, null, 2)}</pre>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Link href="/">
|
||||
<a>Static example with useSWR</a>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getServerSideProps({ req }) {
|
||||
const { user } = await supabase.auth.api.getUserByCookie(req)
|
||||
|
||||
if (!user) {
|
||||
// If no user, redirect to index.
|
||||
return { props: {}, redirect: { destination: '/', permanent: false } }
|
||||
}
|
||||
|
||||
// If there is a user, return it.
|
||||
return { props: { user } }
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
body {
|
||||
font-family: Helvetica, Arial, Sans-Serif;
|
||||
}
|
||||
|
||||
a {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
input {
|
||||
display: block;
|
||||
width: 320px;
|
||||
font-size: 16px;
|
||||
padding: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-bottom: 32px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: black;
|
||||
color: white;
|
||||
padding: 16px;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
|
||||
export const supabase = createClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
|
||||
)
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,2 +0,0 @@
|
||||
SUPABASE_SERVICE_KEY="{SERVICE_KEY}"
|
||||
SUPABASE_URL="https://{YOUR_INSTANCE_NAME}.supabase.co"
|
||||
@@ -1,6 +0,0 @@
|
||||
node_modules
|
||||
|
||||
/.cache
|
||||
/build
|
||||
/public/build
|
||||
.env
|
||||
@@ -1,53 +0,0 @@
|
||||
# Remix Auth - Supabase Strategy with redirectTo
|
||||
|
||||
Authentication using `signInWithEmail` handling redirectTo.
|
||||
|
||||
## Setup
|
||||
|
||||
1. Copy `.env.example` to create a new file `.env`:
|
||||
|
||||
```sh
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
2. Go to https://app.supabase.com/project/{PROJECT}/api?page=auth to find your secrets
|
||||
3. Add your `SUPABASE_URL` and `SUPABASE_SERVICE_ROLE` in `.env`
|
||||
|
||||
```env
|
||||
SUPABASE_SERVICE_KEY="{SERVICE_KEY}"
|
||||
SUPABASE_URL="https://{YOUR_INSTANCE_NAME}.supabase.co"
|
||||
```
|
||||
|
||||
## Using the Remix Auth & SupabaseStrategy 🚀
|
||||
|
||||
SupabaseStrategy provides `checkSession` working like Remix Auth `isAuthenticated` but handles token refresh
|
||||
|
||||
You must use `checkSession` instead of `isAuthenticated`
|
||||
|
||||
## Example
|
||||
|
||||
This is using Remix Auth, `remix-auth-supabase` and `supabase-js` packages.
|
||||
|
||||
> Thanks to Remix, we can securely use server only authentication with `supabase.auth.api.signInWithEmail`
|
||||
>
|
||||
> This function should only be called on a server (`loader` or `action` functions).
|
||||
>
|
||||
> **⚠️ Never expose your `service_role` key in the browser**
|
||||
|
||||
The `/login` route renders a form with a email and password input. After a submit it runs some validations and store `user` object, `access_token` and `refresh_token` in the session.
|
||||
|
||||
The `/private` routes redirects the user to `/login` if it's not logged-in, or shows the user email and a logout form if it's logged-in.
|
||||
|
||||
The `/private/profile` routes redirects the user to `/login?redirectTo=/private/profile` if it's not logged-in, or shows the user email and a logout form if it's logged-in.
|
||||
|
||||
If the user go to `/private/profile` and is not logged-in, it'll be redirected here after login success.
|
||||
|
||||
**Handle refreshing of tokens** (if expired) or redirects to `/login` if it fails
|
||||
|
||||
More use cases can be found on [Remix Auth Supabase - Use cases](https://github.com/mitchelvanbever/remix-auth-supabase#using-the-authenticator--strategy-)
|
||||
|
||||
## Related Links
|
||||
|
||||
- [Remix Auth](https://github.com/sergiodxa/remix-auth)
|
||||
- [Remix Auth Supabase](https://github.com/mitchelvanbever/remix-auth-supabase)
|
||||
- [supabase-js](https://github.com/supabase/supabase-js)
|
||||
@@ -1,53 +0,0 @@
|
||||
import { createCookieSessionStorage } from 'remix'
|
||||
import { Authenticator, AuthorizationError } from 'remix-auth'
|
||||
import { SupabaseStrategy } from 'remix-auth-supabase'
|
||||
import { supabaseClient } from '~/supabase'
|
||||
import type { Session } from '~/supabase'
|
||||
|
||||
export const sessionStorage = createCookieSessionStorage({
|
||||
cookie: {
|
||||
name: 'sb',
|
||||
httpOnly: true,
|
||||
path: '/',
|
||||
sameSite: 'lax',
|
||||
secrets: ['s3cr3t'], // This should be an env variable
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
},
|
||||
})
|
||||
|
||||
export const supabaseStrategy = new SupabaseStrategy(
|
||||
{
|
||||
supabaseClient,
|
||||
sessionStorage,
|
||||
sessionKey: 'sb:session',
|
||||
sessionErrorKey: 'sb:error',
|
||||
},
|
||||
async ({ req, supabaseClient }) => {
|
||||
const form = await req.formData()
|
||||
const email = form?.get('email')
|
||||
const password = form?.get('password')
|
||||
|
||||
if (!email) throw new AuthorizationError('Email is required')
|
||||
if (typeof email !== 'string') throw new AuthorizationError('Email must be a string')
|
||||
|
||||
if (!password) throw new AuthorizationError('Password is required')
|
||||
if (typeof password !== 'string') throw new AuthorizationError('Password must be a string')
|
||||
|
||||
return supabaseClient.auth.api
|
||||
.signInWithEmail(email, password)
|
||||
.then(({ data, error }): Session => {
|
||||
if (error || !data) {
|
||||
throw new AuthorizationError(error?.message ?? 'No user session found')
|
||||
}
|
||||
|
||||
return data
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
export const authenticator = new Authenticator<Session>(sessionStorage, {
|
||||
sessionKey: supabaseStrategy.sessionKey,
|
||||
sessionErrorKey: supabaseStrategy.sessionErrorKey,
|
||||
})
|
||||
|
||||
authenticator.use(supabaseStrategy)
|
||||
@@ -1,4 +0,0 @@
|
||||
import { hydrate } from 'react-dom'
|
||||
import { RemixBrowser } from 'remix'
|
||||
|
||||
hydrate(<RemixBrowser />, document)
|
||||
@@ -1,19 +0,0 @@
|
||||
import { renderToString } from 'react-dom/server'
|
||||
import { RemixServer } from 'remix'
|
||||
import type { EntryContext } from 'remix'
|
||||
|
||||
export default function handleRequest(
|
||||
request: Request,
|
||||
responseStatusCode: number,
|
||||
responseHeaders: Headers,
|
||||
remixContext: EntryContext
|
||||
) {
|
||||
const markup = renderToString(<RemixServer context={remixContext} url={request.url} />)
|
||||
|
||||
responseHeaders.set('Content-Type', 'text/html')
|
||||
|
||||
return new Response('<!DOCTYPE html>' + markup, {
|
||||
status: responseStatusCode,
|
||||
headers: responseHeaders,
|
||||
})
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration } from 'remix'
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<Meta />
|
||||
<Links />
|
||||
</head>
|
||||
<body>
|
||||
<Outlet />
|
||||
<ScrollRestoration />
|
||||
<Scripts />
|
||||
{process.env.NODE_ENV === 'development' && <LiveReload />}
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import { Link } from 'remix'
|
||||
|
||||
export default function Index() {
|
||||
return (
|
||||
<>
|
||||
<h1>Index page</h1>
|
||||
<ul>
|
||||
<li>
|
||||
<Link to="/private">Go to private page</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/private/profile">Go to private/profile page</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/login">Go to login page</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
import type { ActionFunction, LoaderFunction } from 'remix'
|
||||
import { Form, json, useLoaderData, useSearchParams } from 'remix'
|
||||
import { authenticator, sessionStorage, supabaseStrategy } from '~/auth.server'
|
||||
|
||||
type LoaderData = {
|
||||
error: { message: string } | null
|
||||
}
|
||||
|
||||
export const action: ActionFunction = async ({ request }) => {
|
||||
const data = await request.clone().formData()
|
||||
const redirectTo = (data.get('redirectTo') ?? '/private') as string
|
||||
|
||||
await authenticator.authenticate('sb', request, {
|
||||
successRedirect: redirectTo,
|
||||
failureRedirect: '/login',
|
||||
})
|
||||
}
|
||||
|
||||
export const loader: LoaderFunction = async ({ request }) => {
|
||||
const redirectTo = new URL(request.url).searchParams.get('redirectTo') ?? '/private'
|
||||
|
||||
await supabaseStrategy.checkSession(request, {
|
||||
successRedirect: redirectTo,
|
||||
})
|
||||
|
||||
const session = await sessionStorage.getSession(request.headers.get('Cookie'))
|
||||
|
||||
const error = session.get(authenticator.sessionErrorKey) as LoaderData['error']
|
||||
|
||||
return json<LoaderData>({ error })
|
||||
}
|
||||
|
||||
export default function Screen() {
|
||||
const [searchParams] = useSearchParams()
|
||||
const { error } = useLoaderData<LoaderData>()
|
||||
|
||||
return (
|
||||
<Form method="post">
|
||||
{error && <div>{error.message}</div>}
|
||||
<input
|
||||
name="redirectTo"
|
||||
value={searchParams.get('redirectTo') ?? undefined}
|
||||
hidden
|
||||
readOnly
|
||||
/>
|
||||
<div>
|
||||
<label htmlFor="email">Email</label>
|
||||
<input type="email" name="email" id="email" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="password">Password</label>
|
||||
<input type="password" name="password" id="password" />
|
||||
</div>
|
||||
|
||||
<button>Log In</button>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import type { ActionFunction, LoaderFunction } from 'remix'
|
||||
import { Form, json, useLoaderData } from 'remix'
|
||||
import { authenticator, supabaseStrategy } from '~/auth.server'
|
||||
|
||||
type LoaderData = { email?: string; id?: string }
|
||||
|
||||
export const action: ActionFunction = async ({ request }) => {
|
||||
await authenticator.logout(request, { redirectTo: '/login' })
|
||||
}
|
||||
|
||||
export const loader: LoaderFunction = async ({ request }) => {
|
||||
const session = await supabaseStrategy.checkSession(request, {
|
||||
failureRedirect: '/login?redirectTo=/private/profile',
|
||||
})
|
||||
|
||||
return json<LoaderData>({ email: session.user?.email, id: session.user?.id })
|
||||
}
|
||||
|
||||
export default function Screen() {
|
||||
const { email, id } = useLoaderData<LoaderData>()
|
||||
return (
|
||||
<>
|
||||
<h1>Hello {email}</h1>
|
||||
<h2>Welcome in Private Profile</h2>
|
||||
<h3>Your user id is {id}</h3>
|
||||
|
||||
<Form method="post">
|
||||
<button>Log Out</button>
|
||||
</Form>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import type { ActionFunction, LoaderFunction } from 'remix'
|
||||
import { Form, json, useLoaderData } from 'remix'
|
||||
import { authenticator, supabaseStrategy } from '~/auth.server'
|
||||
|
||||
type LoaderData = { email?: string }
|
||||
|
||||
export const action: ActionFunction = async ({ request }) => {
|
||||
await authenticator.logout(request, { redirectTo: '/login' })
|
||||
}
|
||||
|
||||
export const loader: LoaderFunction = async ({ request }) => {
|
||||
const session = await supabaseStrategy.checkSession(request, {
|
||||
failureRedirect: '/login',
|
||||
})
|
||||
|
||||
return json<LoaderData>({ email: session.user?.email })
|
||||
}
|
||||
|
||||
export default function Screen() {
|
||||
const { email } = useLoaderData<LoaderData>()
|
||||
return (
|
||||
<>
|
||||
<h1>Hello {email}</h1>
|
||||
<h2>Welcome in Private index</h2>
|
||||
|
||||
<Form method="post">
|
||||
<button>Log Out</button>
|
||||
</Form>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
import type { Session } from '@supabase/supabase-js'
|
||||
|
||||
declare global {
|
||||
namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
SUPABASE_URL: string
|
||||
SUPABASE_SERVICE_KEY: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!process.env.SUPABASE_URL) {
|
||||
throw new Error('SUPABASE_URL is required')
|
||||
}
|
||||
|
||||
if (!process.env.SUPABASE_SERVICE_KEY) {
|
||||
throw new Error('SUPABASE_SERVICE_KEY is required')
|
||||
}
|
||||
|
||||
// Supabase options example (build your own :))
|
||||
// https://supabase.com/docs/reference/javascript/initializing#with-additional-parameters
|
||||
|
||||
// const supabaseOptions = {
|
||||
// fetch, // see ⚠️ cloudflare
|
||||
// schema: "public",
|
||||
// persistSession: true,
|
||||
// autoRefreshToken: true,
|
||||
// detectSessionInUrl: true,
|
||||
// headers: { "x-application-name": "{my-site-name}" }
|
||||
// };
|
||||
|
||||
// ⚠️ cloudflare needs you define fetch option : https://github.com/supabase/supabase-js#custom-fetch-implementation
|
||||
// Use Remix fetch polyfill for node (See https://remix.run/docs/en/v1/other-api/node)
|
||||
|
||||
export const supabaseClient = createClient(
|
||||
process.env.SUPABASE_URL,
|
||||
process.env.SUPABASE_SERVICE_KEY
|
||||
// supabaseOptions
|
||||
)
|
||||
|
||||
export { Session }
|
||||
-9907
File diff suppressed because it is too large.
Load diff
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "remix-app",
|
||||
"description": "",
|
||||
"license": "",
|
||||
"scripts": {
|
||||
"build": "remix build",
|
||||
"dev": "node -r dotenv/config node_modules/.bin/remix dev",
|
||||
"postinstall": "remix setup node",
|
||||
"start": "remix-serve build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@remix-run/react": "^1.1.1",
|
||||
"@remix-run/serve": "^1.5.0",
|
||||
"@supabase/supabase-js": "^1.29.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"remix": "^1.1.1",
|
||||
"remix-auth": "^3.2.1",
|
||||
"remix-auth-supabase": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@remix-run/dev": "^1.1.1",
|
||||
"@types/react": "^17.0.38",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"dotenv": "^10.0.0",
|
||||
"prettier": "^2.5.1",
|
||||
"typescript": "^4.5.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"sideEffects": false
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* @type {import('@remix-run/dev/config').AppConfig}
|
||||
*/
|
||||
module.exports = {
|
||||
appDirectory: 'app',
|
||||
browserBuildDirectory: 'public/build',
|
||||
publicPath: '/build/',
|
||||
serverBuildDirectory: 'build',
|
||||
devServerPort: 8002,
|
||||
}
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
/// <reference types="@remix-run/dev" />
|
||||
/// <reference types="@remix-run/node/globals" />
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"compilerOptions": {
|
||||
"lib": ["DOM", "DOM.Iterable", "ES2021"],
|
||||
"esModuleInterop": true,
|
||||
"jsx": "react-jsx",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"target": "ES2019",
|
||||
"strict": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": ["./app/*"]
|
||||
},
|
||||
|
||||
// Remix takes care of building everything in `remix build`.
|
||||
"noEmit": true
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@supabase/auth-ui-react": "^0.2.1",
|
||||
"@supabase/supabase-js": "^2.0.0-rc.12",
|
||||
"@supabase/supabase-js": "^2.0.0",
|
||||
"@testing-library/jest-dom": "^5.16.3",
|
||||
"@testing-library/react": "^12.1.4",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// This enables autocomplete, go to definition, etc.
|
||||
|
||||
import { serve } from 'https://deno.land/std@0.131.0/http/server.ts'
|
||||
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.0.0-rc.12'
|
||||
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.0.0'
|
||||
import { corsHeaders } from '../_shared/cors.ts'
|
||||
|
||||
console.log(`Function "get-tshirt-competition" up and running!`)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// This enables autocomplete, go to definition, etc.
|
||||
|
||||
import { serve } from 'https://deno.land/std@0.131.0/http/server.ts'
|
||||
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.0.0-rc.12'
|
||||
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.0.0'
|
||||
import { corsHeaders } from '../_shared/cors.ts'
|
||||
|
||||
console.log(`Function "select-from-table-with-auth-rls" up and running!`)
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
// https://deno.land/manual/getting_started/setup_your_environment
|
||||
// This enables autocomplete, go to definition, etc.
|
||||
|
||||
import { serve } from 'https://deno.land/std@0.131.0/http/server.ts'
|
||||
import { serve } from 'https://deno.land/std@0.132.0/http/server.ts'
|
||||
|
||||
// esm.sh is used to compile stripe-node to be compatible with ES modules.
|
||||
import Stripe from 'https://esm.sh/stripe@9.6.0?target=deno&no-check'
|
||||
import Stripe from 'https://esm.sh/stripe@10.13.0?target=deno&deno-std=0.132.0'
|
||||
|
||||
const stripe = Stripe(Deno.env.get('STRIPE_API_KEY'), {
|
||||
// This is needed to use the Fetch API rather than relying on the Node http
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# Update these with your Supabase details from your project settings > API
|
||||
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
|
||||
@@ -1,34 +0,0 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
@@ -1,9 +0,0 @@
|
||||
# Example: Supabase authentication client- and server-side (API routes), and SSR with auth cookie.
|
||||
|
||||
This example shows how to use Supabase auth both on the client and server in both [API routes](https://nextjs.org/docs/api-routes/introduction) and when using [server side rendering (SSR)](https://nextjs.org/docs/basic-features/pages#server-side-rendering). This example uses helper functions that can be viewed at [supabase auth helpers repository](https://github.com/supabase-community/supabase-auth-helpers).
|
||||
|
||||
## Deploy with Vercel
|
||||
|
||||
The Vercel deployment will guide you through creating a Supabase account and project. After installation of the Supabase integration, all relevant environment variables will be set up so that the project is usable immediately after deployment 🚀
|
||||
|
||||
[](https://vercel.com/new/git/external?repository-url=https%3A%2F%2Fgithub.com%2Fsupabase%2Fsupabase%2Ftree%2Fmaster%2Fexamples%2Fnextjs-with-supabase-auth&project-name=nextjs-with-supabase-auth&repository-name=nextjs-with-supabase-auth&integration-ids=oac_jUduyjQgOyzev1fjrW83NYOv)
|
||||
-1243
File diff suppressed because it is too large.
Load diff
Loaded 100 of 931 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user