最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

next.js - Problem validating state in Discord OAuth2, NextJS - Stack Overflow

programmeradmin3浏览0评论

I am trying to create a Discord OAuth2 application from scratch. Everything has gone well except trying to implement state to prevent CSRF attacks. I don't know how to pass the state generated from the frontend to the backend API route to have it validated on the server side.

I tried to set up a cookie to store the value of the state value generated, but it's clearly not being passed to the backend.

Loginbutton.tsx

import React from 'react'
import { FaDiscord } from 'react-icons/fa6'
import styles from './button.module.css'

export default async function Loginbutton() {
    const state = crypto.randomUUID();

    // Store state in a cookie for verification later
    document.cookie = `discord_oauth_state=${state}; path=/; Secure; HttpOnly; SameSite=Lax`;

    // Take their email to log it into the database
    const url = `;response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fapi%2Fdiscord%2Fcallback&scope=identify+email+guilds.members.read&state=${state}`;

    return (
        <div>
            <a className={`${styles['button']} flex items-center rounded-[6px] py-2 w-fit space-x-3`} href={url} rel='noopener noreferrer' title='Sign In With Discord Button'>
                <FaDiscord className='mt-[2px]' size={22} />
                <span>Sign In With Discord</span>
            </a>
        </div>
    )
}

Server-side API route

'use strict'

import axios from 'axios';
import { eq } from 'drizzle-orm';
import { NextRequest, NextResponse } from "next/server";
import { db } from '@/db/config';
import { discordUsers } from '@/db/schema';
import { createSession } from '@/app/lib/sessions/discordSession';

export async function GET(req: NextRequest) {
    const { searchParams } = new URL(req.url);
    const code = searchParams.get('code');
    // const receivedState = searchParams.get('state');

    if (!code) {
        return NextResponse.json({ error: 'Code not found!' }, { status: 500 });
    }

    // Get stored state from cookie
    const storedState = (await cookies()).get('discord_oauth_state')?.value;
    if (!storedState || storedState !== receivedState) {
        return NextResponse.json({ error: 'Invalid state!' }, { status: 403 });
     }

    const discordURL = "/";

    const headers = {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Accept-Encoding': 'application/x-www-form-urlencoded'
    };

    const params = new URLSearchParams({
        client_id: process.env.CLIENT_ID!,
        client_secret: process.env.CLIENT_SECRET!,
        code: code as string,
        grant_type: 'authorization_code',
        redirect_uri: process.env.REDIRECT_URL!
    })


    // Token exchange block
    try {
        const response = await axios.post(`${discordURL}/oauth2/token`, params, { headers });
        const { access_token } = response.data;

        if (!access_token) {
            return NextResponse.json({ error: 'Access token not found!' }, { status: 500 });
        }

        // User Data block
        const userResponse = await axios.get(`${discordURL}/users/@me`, {
            headers: {
                Authorization: `Bearer ${access_token}`,
            }
        });
        const { id: discordID, username } = userResponse.data;

        // Check if the user exists
        const existingUser = await db
            .select()
            .from(discordUsers)
            .where(eq(discordUsers.discordID, discordID))
            .limit(1);

        if (existingUser.length > 0) {
            const user = existingUser[0];

            // Check for updates
            const isChanged =
                user.username !== username

            if (isChanged) {
                // Update user data if they exist
                await db
                    .update(discordUsers)
                    .set({
                        username,
                    })
                    .where(eq(discordUsers.discordID, discordID));
            }
        } else {

            // Insert new user, if record does not exist
            await db.insert(discordUsers).values({
                discordID,
                username
            });
            console.log("User inserted");
        }

        // return NextResponse.json({ message: "Complete", status: 200 });

        await createSession(discordID, username);
        return NextResponse.redirect(process.env.CLIENT_REDIRECT_URL!);

    } catch (error) {
        if (axios.isAxiosError(error)) {
            console.error(error.response?.data || error.message);
            return NextResponse.json({ error: error.response?.data, status: error.response?.status });
        }
    }
}
发布评论

评论列表(0)

  1. 暂无评论