Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/auth via supabase #203

Merged
merged 29 commits into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
aad4073
feat(supabase auth): First working supabase auth
ff6347 Mar 16, 2023
8313f91
refactor(api v3): Move files and functions to allow v3 api
ff6347 Mar 16, 2023
39f8040
docs(auth): Add link to discussion about jwt verification
ff6347 Mar 17, 2023
3e7c53a
chore: Remove unused routes
ff6347 Mar 17, 2023
b1d0af7
chore(housekeeping): Remove dead routes
ff6347 Mar 17, 2023
1482cca
chore(housekeeping): Remove dead routes
ff6347 Mar 17, 2023
985470f
chore(housekeeping): Remove dead routes
ff6347 Mar 17, 2023
3f922a2
test: token verification
ff6347 Mar 17, 2023
3bebf69
feat(v2/3): Allow handle v2 and v3 requests
ff6347 Mar 17, 2023
0666b28
chore: Rename test function
ff6347 Mar 17, 2023
40f543d
chore(Housekeeping): Remove logging
ff6347 Mar 17, 2023
ea83659
test(supabase): Add tess for authenticated GET routes
ff6347 Mar 17, 2023
840ba09
chore: remove unused import
ff6347 Mar 17, 2023
d7159ef
test(v3): Add tests for delete and post
ff6347 Mar 17, 2023
2bb17e6
Merge branch 'staging' into feat/auth-via-supabase
ff6347 Mar 17, 2023
54837cb
docs(v3); Update docs to reflect v3
ff6347 Mar 17, 2023
2964d4c
Merge branch 'feat/auth-via-supabase' of github.com:technologiestiftu…
ff6347 Mar 17, 2023
e137749
test: Update route listing snapshot
ff6347 Mar 17, 2023
f35b956
fix(validation): Remove v3 for validation from route
ff6347 Mar 21, 2023
d189dbd
feat(user name): Update trees_watered on profile change
ff6347 Mar 21, 2023
c1d26b4
feat(user data): Remove personal data on delete
ff6347 Mar 21, 2023
3f6e846
feat(users fk): Add forein key constraint to profiles
ff6347 Mar 21, 2023
3b9b003
docs(delete account)
ff6347 Mar 21, 2023
523fe1a
feat(account): Allow users to remove their data
ff6347 Mar 21, 2023
9d15e46
fix(update): Change RLS to allow updates of username
ff6347 Mar 22, 2023
de8104b
Merge branch 'staging' into feat/auth-via-supabase
ff6347 Mar 22, 2023
e8f5fd1
test: Remove additional params added from vercel
ff6347 Mar 22, 2023
d17855c
Merge branch 'feat/auth-via-supabase' of github.com:technologiestiftu…
ff6347 Mar 22, 2023
7473577
docs: Remove leftover from conflict
ff6347 Mar 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 106 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
- [W.I.P. API Migration](#wip-api-migration)
- [Prerequisites](#prerequisites)
- [Setup](#setup)
- [Supabase (local)](#supabase-local)
- [Environments and Variables](#environments-and-variables)
- [Auth0](#auth0)
- [Auth0 (deprecated)](#auth0-deprecated)
- [Vercel](#vercel)
- [Vercel Environment Variables](#vercel-environment-variables)
- [API Routes](#api-routes)
Expand All @@ -15,6 +16,11 @@
- [Migrations and Types](#migrations-and-types)
- [Deployment](#deployment)
- [Radolan Harvester](#radolan-harvester)
- [API Routes](#api-routes-1)
- [API Authorization](#api-authorization-1)
- [Supabase](#supabase-1)
- [Auth0 (deprecated)](#auth0-deprecated-1)
- [Tests](#tests-1)
- [Contributors ✨](#contributors-)
- [Credits](#credits)

Expand All @@ -28,18 +34,20 @@ Built with Typescript connects to Supabase and (still) Auth0.com, runs on vercel

![](./docs/wip.png)

We are in the process of migrating the API fully to supabase. These docs are not up to date yet.
We are in the process of migrating the API fully to supabase. These docs might have some missing information.

## Prerequisites

- [Vercel.com](https://vercel.com) Account
- [Supabase](https://supabase.com) Account
- Supabase CLI install with brew `brew install supabase/tap/supabase`
- [Docker](https://www.docker.com/) Dependency for Supabase
- [Auth0.com](https://auth0.com) Account
- (deprecated) [Auth0.com](https://auth0.com) Account

## Setup

### Supabase (local)

```bash
git clone git@github.com:technologiestiftung/giessdenkiez-de-postgres-api.git
cd ./giessdenkiez-de-postgres-api
Expand Down Expand Up @@ -74,9 +82,9 @@ In the example code above the Postgres database Postgrest API are run locally. Y

Again. Be a smart developer, read https://12factor.net/config, https://github.com/motdotla/dotenv#should-i-have-multiple-env-files and never ever touch production with your local code!

### Auth0
### Auth0 (deprecated)

**!Hint: We are working on replacing Auth0 with Supabase. This is not yet implemented.**
**!Hint: We still support using Auth0 in this API but will eventually remove it. Using Supabase is preferred.**

Setup your auth0.com account and create a new API. Get your `jwksUri`, `issuer`, `audience`, `client_id` and `client_secret` values and add them to the `.env` file as well. The values for `client_id` and `client_secret` are only needed if you want to run local integration tests and use tools like rest-client, Postman, Insomnia or Paw to obtain a token. This is explained later in this document.

Expand Down Expand Up @@ -109,8 +117,6 @@ To let these variables take effect you need to deploy your application once more
vercel --prod
```

<!-- Congrats. Your API should be up and running. You might need to request tokens for the your endpoints that need authentication. See the auth0.com docs for more info. -->

## API Routes

There are 3 main routes `/get`, `/post` and `/delete`.
Expand Down Expand Up @@ -223,6 +229,99 @@ INSERT INTO "public"."radolan_harvester" ("id", "collection_date", "start_date",

This process is actually a little blackbox we need to solve.

## API Routes

There are 3 main routes `/get`, `/post` and `/delete`.

On the `/get` route all actions are controlled by passing URL params. On the `/post` and `/delete` route you will have to work with additional POST bodies. For example to fetch a specific tree run the following command.

```bash
curl --request GET \
--url 'http://localhost:3000/get/byid&id=_123456789' \

```

You can see all the available routes in the [docs/api.http](./docs/api.http) file with all their needed `URLSearchParams` and JSON bodies or by inspecting the JSON Schema that is returned when you do a request to the `/get`, `/post` or `/delete` route.

Currently we have these routes (for routes that still use auth0 remove the v3 prefix)

| `/v3/get` | `/v3/post` | `/v3/delete` |
| -------------------- | ---------- | ------------ |
| `/byid` | `/adopt` | `/unadopt` |
| `/treesbyids` | `/water` | `/unwater` |
| `/adopted` | | |
| `/istreeadopted` | | |
| `/wateredandadopted` | | |
| `/lastwatered` | | |
| `/wateredbyuser` | | |

### API Authorization

#### Supabase

Some of the requests need a authorized user. You can create a new user using email password via the Supabase API.

```bash
curl --request POST \
--url http://localhost:54321/auth/v1/signup \
--header 'apikey: <SUPABASE_ANON_KEY>' \
--header 'content-type: application/json' \
--data '{"email": "someone@email.com","password": "1234567890"}'
```

This will give you in development already an aceess token. In production you will need to confirm your email address first.

A login can be done like this:

```bash
curl --request POST \
--url 'http://localhost:54321/auth/v1/token?grant_type=password' \
--header 'apikey: <SUPABASE_ANON_KEY>' \
--header 'content-type: application/json' \
--data '{"email": "someone@email.com","password": "1234567890"}'
```

See the [docs/api.http](./docs/api.http) file for more examples or take a look into the API documentation in your local supabase instance under http://localhost:54323/project/default/api?page=users

#### Auth0 (deprecated)

Some of the request will need an authorization header. You can obtain a token by making a request to your auth0 token issuer.

```bash
curl --request POST \
--url https://your-tenant.eu.auth0.com/oauth/token \
--header 'content-type: application/json' \
--data '{"client_id": "<YOUR CLIENT ID>","client_secret": "<YOUR CLIENT SECRET>","audience": "<YOUR AUDIENCE>","grant_type": "client_credentials"}'
# fill in the <VALUS> fields
```

This will respond with an `access_token`. Use it to make authenticated requests.

```bash
curl --request POST \
--url http://localhost:3000/post \
--header 'authorization: Bearer <ACCESS_TOKEN>' \
--header 'content-type: application/json' \
--data '{"queryType":"adopt","tree_id":"_01","uuid": "auth0|123"}'
```

Take a look into [docs/api.http](./docs/api.http). The requests in this file can be run with the VSCode extension [REST Client](https://marketplace.visualstudio.com/items?itemName=humao.rest-client).

## Tests

Locally you will need supabase running and a `.env` file with the right values in it.

```bash
cd giessdenkiez-de-postgres-api
supabase start
# Once the backaned is up and running, run the tests
# Make sure to you habe your .env file setup right
# with all the values from `supabase status` and your API from Auth0.com
npm test
```

On CI the Supabase is started automagically. See [.github/workflows/tests.yml](.github/workflows/tests.yml) you still need an API on Auth0.com

## Contributors ✨

Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
Expand Down Expand Up @@ -277,5 +376,3 @@ This project follows the [all-contributors](https://github.com/all-contributors/

[gdk-supabase]: https://github.com/technologiestiftung/giessdenkiez-de-supabase/
[supabase]: https://supabase.com/

<!-- bump -->
24 changes: 18 additions & 6 deletions __test-utils/postgres.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,24 @@ export async function truncateTreesAdopted() {
export async function createWateredTrees() {
const sql = postgres(url);
await sql`
INSERT INTO trees_watered (uuid, tree_id, amount, timestamp)
VALUES
('test', '_2100294b1f', 1, '2023-01-01 00:00:00'),
('test', '_2100294b1f', 1, '2023-01-01 00:00:00'),
('test', '_2100186c08', 1, '2023-01-01 00:00:00'),
('test', '_2100186c08', 1, '2023-01-01 00:00:00');
INSERT INTO trees_watered (uuid, amount, timestamp, username, tree_id)
SELECT
md5(random()::text),
random() * 10,
NOW() - (random() * INTERVAL '7 days'),
md5(random()::text),
id
FROM
trees
ORDER BY
random()
LIMIT 10;
`;
sql.end();
}

export async function deleteSupabaseUser(email: string): Promise<void> {
const sql = postgres(url);
await sql`DELETE FROM auth.users WHERE email = ${email}`;
sql.end();
}
57 changes: 55 additions & 2 deletions __test-utils/req-test-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ const issuer = process.env.issuer || "";
const client_id = process.env.client_id || "";
const client_secret = process.env.client_secret || "";
const audience = process.env.audience || "";

export async function requestTestToken() {
const SUPABASE_URL = process.env.SUPABASE_URL || "http://localhost:54321";
const SUPABASE_ANON_KEY = process.env.SUPABASE_ANON_KEY || "";
export async function requestAuth0TestToken() {
const response = await fetch(`${issuer}oauth/token`, {
method: "POST",
headers: {
Expand All @@ -23,3 +24,55 @@ export async function requestTestToken() {
const json = await response.json();
return json.access_token;
}

export async function requestSupabaseTestToken(
email: string,
password: string
) {
const response = await fetch(
`${SUPABASE_URL}/auth/v1/token?grant_type=password`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
apikey: SUPABASE_ANON_KEY,
},
body: JSON.stringify({
email,
password,
}),
}
);
if (!response.ok) {
const json = await response.text();
throw new Error(`Could not get test token, ${json}`);
}
const json = (await response.json()) as {
access_token: string;
user: { id: string };
};
return json.access_token;
}

export async function createSupabaseUser(email: string, password: string) {
const response = await fetch(`${SUPABASE_URL}/auth/v1/signup`, {
method: "POST",
headers: {
"Content-Type": "application/json",
apikey: SUPABASE_ANON_KEY,
},
body: JSON.stringify({
email,
password,
}),
});
if (!response.ok) {
const json = await response.text();
throw new Error(`Could not create test user, ${json}`);
}
const json = (await response.json()) as {
access_token: string;
user: { id: string };
};
return json.access_token;
}
Loading