Overview

This is a note about using Drupal OAuth with NextAuth.js.

Behavior

Access the app created with Next.js and press the “Sign in” button.

If you are not logged into Drupal, you will be redirected to the login screen.

If you are already logged in, an authorization button is displayed. Click to authorize.

The login information is displayed.

Drupal-Side Setup

Module Installation

Install the following module.

https://www.drupal.org/project/simple_oauth

I installed the latest version available at the time of writing.

composer require 'drupal/simple_oauth:^6.0@beta'

Generating Keys for Token Encryption

Generate a key pair and save them outside the document root for security.

openssl genrsa -out private.key 2048
openssl rsa -in private.key -pubout > public.key

Setting Key Paths

Set the key paths in the admin panel:

/admin/config/people/simple_oauth

If running Drupal on Amazon Lightsail, you may need to change the file owner as follows.

sudo chown daemon:daemon private.key

Clients

Access /admin/config/services/consumer.

The default_consumer is already created, so edit it.

Verifying with Postman

Enter the following settings. Assume Drupal is set up at https://drupal.example.org.

When you click “Get New Access Token”, as shown in the behavior section at the beginning, you are redirected to the Drupal screen. After authorization, you are redirected to:

https://oauth.pstmn.io/v1/callback?code=...

Using from Next.js

You can check the source code here:

https://github.com/nakamura196/drupal_oauth_app

There may be room for improvement, but I was able to verify functionality with the following configuration.

export const authOptions = {
  // debug: true, // Enable next-auth debug mode
  providers: [
    {
      id: "drupal",
      name: "Drupal",
      type: "oauth",
      clientId: process.env.DRUPAL_CLIENT_ID,
      clientSecret: process.env.DRUPAL_CLIENT_SECRET,
      authorization: {
        url: process.env.DRUPAL_AUTH_URL,
        params: {
          scope: process.env.DRUPAL_SCOPE,
          response_type: "code",
          redirect_uri: `${process.env.NEXTAUTH_URL}/api/auth/callback/drupal`, // Build redirect URI from environment variable
        },
      },
      token: {
        async request(context) {
          const body = new URLSearchParams({
            client_id: process.env.DRUPAL_CLIENT_ID, // Explicitly add client_id
            client_secret: process.env.DRUPAL_CLIENT_SECRET,
            code: context.params.code, // Authorization code
            grant_type: "authorization_code",
            redirect_uri: `${process.env.NEXTAUTH_URL}/api/auth/callback/drupal`,
          });

          const res = await fetch(process.env.DRUPAL_TOKEN_URL, {
            method: "POST",
            headers: {
              "Content-Type": "application/x-www-form-urlencoded",
            },
            body,
          });

          const json = await res.json(); // Parse the response body once

          if (!res.ok) {
            throw new Error(`Token request failed: ${res.statusText}`);
          }

          return {
            tokens: json
          }
        }
      },
      profile(profile) {
        return {
          id: profile.sub, // Use "sub" as the unique user ID
          name: profile.name || profile.preferred_username || "Unknown User", // Set name priority
          email: profile.email || "No Email Provided", // Fallback when email is not available
          image: profile.profile || null, // Use profile URL as image (adjust as needed)
        }
      },
    },
  ],
  callbacks: {
    async session({ session, token }) {
      // Add necessary information from token to session
      session.accessToken = token.accessToken;
      session.user.id = token.id;
      return session;
    },

    async jwt({ token, account, user }) {
      if (account) {
        token.accessToken = account.access_token;
      }
      if (user) {
        token.id = user.id; // Save user ID from profile to token
      }
      return token;
    },
  },
};

Summary

I hope this serves as a helpful reference for using Drupal OAuth.