drf-authjs-db-auth 0.1.0
DRF authentication for Auth.js with database adapter
Requires Python: >=3.13
Keywords: authentication, authjs, django, djangorestframework

# DRF authentication for Authjs with database adapter

This app is used to integrate [Authjs](https://authjs.dev) authentication with a
Django + DRF project. It can create the DB to be used as with the AuthJs adapter and
provides an authentication class based on that. Instead of reauthenticating users
with sessions or JWT, it matches the Authjs token sent in the `Authorization`
header with the contents of the database used by Authjs.

## Usage

### Frontend

In the frontend, use Authjs with a database adapter. In this example, we'll be
using the `pg` adapter for PostgreSQL.

```javascript
import NextAuth from "next-auth";
import { Pool } from "pg";
import MyProvider from "next-auth/providers/myprovider"; // Example: google

const pool = new Pool({
  host: process.env.DATABASE_HOST,
  user: process.env.DATABASE_USER,
  password: process.env.DATABASE_PASSWORD,
  database: process.env.DATABASE_NAME,
})

export const { auth, handlers, signIn, signOut } = NextAuth({
 providers: [MyProvider],
});
```

You'll also need to send the session token which each request to the backend.

```javascript
import { auth } from "@/auth";

const session = await auth();

const data = await fetch("/api/some-protected-endpoint", {
  headers: {
    "Authorization": `Bearer ${session.token}`,
  },
});
```

### Backend

Since we need to adapt the table schema to match authjs's, we recommend using a
separate database for the Authjs tables. In the settings.py file, configure a
new database connection for Authjs. Be sure to use the same values as the ones
passed to the frontend.

```python
# settings.py

import os

...

DATABASES = {
    ...
    "authjs": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": os.getenv("AUTHJS_DATABASE_NAME"),
        "USER": os.getenv("AUTHJS_DATABASE_USER"),
        "PASSWORD": os.getenv("AUTHJS_DATABASE_PASSWORD"),
        "HOST": os.getenv("AUTHJS_DATABASE_HOST"),
        "PORT": os.getenv("AUTHJS_DATABASE_PORT"),
    },
}
```

Configure this app to use the created database connection.

```python
# settings.py

...

AUTHJS_DATABASE = "authjs"
```

Put this package in your `INSTALLED_APPS`:

```python
# settings.py
INSTALLED_APPS = [
    ...
    "drf_authjs_db_auth",
    ...
]
```

And run migrations to create the necessary tables (be sure that the database
exists beforehand, if not using `sqlite`):

```
python manage.py migrate drf_authjs_db_auth --database authjs
python manage.py migrate drf_authjs_db_auth --fake # (avoid warnings about unnapplied migrations)
```

You can fake both migrations if the database already exists (though we do
introduce an extra text column `username` in the `user` table, make sure it is
present).

Finally, set up the rest framework authentication backend to use
Authjs authentication:

```python
# settings.py

REST_FRAMEWORK = {
    ...
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "drf_authjs_db_auth.authentication.AuthJSAuthentication",
        ...
    ],
    ...
}
```

### Getting user data from the backend

If you need additional user information from the backend, you can create a
custom user seralizer and read it with `authjs`'s session callback. Example user
serializer and view:

```python
from rest_framework import serializers
from django.contrib.auth import get_user_model

UserModel = get_user_model()

class UserDetailsSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserModel
        fields = ('pk', 'username', 'email', 'first_name', 'last_name')
        read_only_fields = ('email', 'pk')

```

```python
from rest_framework.generics import RetrieveAPIView
from rest_framework.permissions import IsAuthenticated

class UserDetailsView(RetrieveAPIView):
    serializer_class = UserDetailsSerializer
    permission_classes = (IsAuthenticated,)

    def get_object(self):
        return self.request.user
```

For more elaborate examples, check `dj_rest_auth`'s [user
serializer](https://github.com/iMerica/dj-rest-auth/blob/master/dj_rest_auth/serializers.py)
and [user
view](https://github.com/iMerica/dj-rest-auth/blob/master/dj_rest_auth/views.py).

Then, in the frontend, you can append the user data to the session in the
`NextAuth` session callback:

```javascript
import { auth } from "@/auth";

export const { auth, handlers, signIn, signOut } = NextAuth({
  ...,

  callbacks: {
    async session({ session }) {
      if (session && !session.user?.username) {
        const data = await getUserData(session); // Fetch from your user details endpoint
        session.user.username = data.username;
        session.user.pk = data.pk;
      }

      return session;
    },
  },
})
```

## Contributing

* Sync packages with `uv sync --all-packages`;
* Use `mypy` to type check;
* MR to the [repository](https://gitlab.com/joaoseckler/drf-authjs-db-auth).

## Needs doing

1. Adding an example frontend project (maybe using authjs's [example
   repository](https://github.com/nextauthjs/next-auth-example) for NextJS)
2. Test behavior and document for different database adapters (Prisma
   * SQLite for example).