I want to be able to authenticate users with Oauth and protect the API endpoints with Basic authentication. How can I achieve this? My currently configuration doesn't work, unless I remove the Basic auth part and exclude api routes from authentication/authorization. In that case, Oauth authentication alone works perfectly.
auth.config.ts
import type { NextAuthConfig, Session } from 'next-auth'; import Google from "next-auth/providers/google" import { SupabaseAdapter } from "@auth/supabase-adapter" import { NextRequest } from 'next/server'; export const authConfig = { pages: { signIn: '/login' }, callbacks: { authorized({ auth, request: { nextUrl }} : {auth: null | Session, request: NextRequest}) { const isLoggedIn = !!auth?.user; const isOnHomePage = nextUrl.pathname === '/'; if (isOnHomePage) { if (isLoggedIn) { return true; } return false; // Redirect unauthenticated users to login page } else if (isLoggedIn) { return Response.redirect(new URL('/', nextUrl)) } return true; } }, providers: [Google], adapter: SupabaseAdapter({ url: process.env.SUPABASE_URL!, secret: process.env.SUPABASE_SERVICE_ROLE_KEY!, }) } satisfies NextAuthConfig; auth.ts
import NextAuth from "next-auth"; import { authConfig } from './auth.config'; export const { handlers, signIn, signOut, auth } = NextAuth(authConfig) middleware.ts
import NextAuth from 'next-auth'; import { authConfig } from './auth.config'; import { NextResponse } from 'next/server'; const { auth } = NextAuth(authConfig); export default auth((req) => { const url = new URL(req.url); // Explicitly skip NextAuth API routes and session endpoint if (url.pathname.startsWith('/api/auth/') || url.pathname === '/api/session') { return NextResponse.next(); } if (url.pathname.startsWith('/api/')) { const authHeader = req.headers.get('authorization') ?? ''; if (!authHeader.startsWith('Basic ')) { return new NextResponse('Unauthorized', { status: 401, headers: { 'WWW-Authenticate': 'Basic realm="Restricted"' }, }); } const base64 = authHeader.slice(6); let decoded = ''; try { if (typeof Buffer !== 'undefined' && typeof Buffer.from === 'function') { decoded = Buffer.from(base64, 'base64').toString('utf-8'); } else if (typeof atob === 'function') { decoded = atob(base64); } else { return new NextResponse('Unauthorized', { status: 401, headers: { 'WWW-Authenticate': 'Basic realm="Restricted"' }, }); } } catch (err) { return new NextResponse('Unauthorized', { status: 401, headers: { 'WWW-Authenticate': 'Basic realm="Restricted"' }, }); } const [username, password] = decoded.split(':'); const validUsername = process.env.API_BASIC_AUTH_USER; const validPassword = process.env.API_BASIC_AUTH_PASS; if (!validUsername || !validPassword || username !== validUsername || password !== validPassword) { return new NextResponse('Unauthorized', { status: 401, headers: { 'WWW-Authenticate': 'Basic realm="Restricted"' }, }); } // Basic auth passed — continue to API handler return NextResponse.next(); } // Non-API routes: proceed (NextAuth will handle redirects/auth pages) return NextResponse.next(); }); export const config = { // apply middleware to API routes and app pages; do NOT use negated matcher entries matcher: [ '/api/:path*', '/((?!_next/static|_next/image|.*\\.png$).*)' ], }; I get the followin error in the browser console:
providers.tsx:36 ClientFetchError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON. Read more at https://errors.authjs.dev#autherror at fetchData (client.js:39:22) at async getSession (react.js:97:21) at async SessionProvider.useEffect [as _getSession] (react.js:253:43)