DEV Community

Cover image for How to select oauth scopes in next-auth / authjs
Dennis kinuthia
Dennis kinuthia

Posted on

How to select oauth scopes in next-auth / authjs

You can select the scopes you want to request from GitHub/your oauthprovider in the login page, and then the scopes will be passed to the OAuth provider.

firstly look up th eoauth scopes allowed for example the github ones are oauth scopes documentation

then we can do it in 2 ways

  • globally in your nextauth client
import NextAuth from "next-auth";
import GitHub from "next-auth/providers/github";

export const githubScopes = ["user", "repo", "delete_repo"] as const;

export const { handlers, signIn, signOut, auth } = NextAuth({
  pages: {
    signIn: "/auth",
  },
  providers: [
    GitHub({
      authorization: {
        params: {
          scope: githubScopes.join(" "),
        },
      },
    }),
  ],
  debug: true,
  callbacks: {
    authorized: async ({ auth }) => {
      // Logged in users are authenticated, otherwise redirect to login page
      return !!auth;
    },
  },
});
Enter fullscreen mode Exit fullscreen mode
  • in the login page > useful when you want to conditionally select the scopes >[!NOTE] > use the signin for client side login , and make sure the component has a "use client" directive
"use client";
import { signIn } from "next-auth/react";
import { useState } from "react";
import { FaGithub } from "react-icons/fa";

interface OauthButtonProps {}

export function OauthButton({}: OauthButtonProps) {
  const [selectedScopes, setSelectedScopes] = useState<string[]>([]);

  const scopeOptions = [
    {
      id: "user",
      label: "User Profile",
      description: "Read/Write access to profile info",
    },
    {
      id: "repo",
      label: "Repository Access",
      description: "Full control of public and private repositories",
    },
    {
      id: "delete_repo",
      label: "Delete Repositories",
      description: "Ability to delete repositories",
    },
  ];

  const handleScopeToggle = (scope: string) => {
    setSelectedScopes((prev) =>
      prev.includes(scope) ? prev.filter((s) => s !== scope) : [...prev, scope]
    );
  };

  return (
    <div className="w-full flex flex-col justify-center items-center gap-5">
      <div className=" flex flex-col justify-center items-center gap-2">
        {selectedScopes.length === 0 && (
          <div role="alert" className="alert alert-info alert-outline">
            <span className="text-xs text-center">
              if no scopes are selected, 
              the default read-only access to public information will be used
            </span>
          </div>
        )}
        {scopeOptions.map((scope) => (
          <label
            key={scope.id}
            className="flex w-full bg-primary/10 items-start space-x-3 p-3 rounded-lg hover:bg-base-300 transition-colors">
            <input
              type="checkbox"
              className="checkbox checkbox-accent mt-1 border border-accent"
              checked={selectedScopes.includes(scope.id)}
              onChange={() => handleScopeToggle(scope.id)}
            />
            <div className="flex flex-col">
              <span className="font-medium">{scope.label}</span>
              <span className="text-sm text-base-content/70">{scope.description}</span>
            </div>
          </label>
        ))}
      </div>
      <button
        className="btn btn-wide btn-accent w-full"
        onClick={() =>
          signIn(
            "github",
            {},
            {
              scope: selectedScopes.join(" ") || undefined,
            }
          )
        }>
        <FaGithub className="h-5 w-5" />
        Sign in with GitHub
      </button>
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

[!NOTE]
The scopes should be a space separated string , eg "repo user delete_repo"

Top comments (0)