You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Notion API OAuth出现invalid_client错误:Postman正常但应用内请求失败

问题根因

你遇到的{ error: 'invalid_client' } 401错误核心是axios请求参数格式错误:你将请求体、请求头配置都错误塞入了axios.post的第二个参数,导致Notion服务端无法正确读取Authorization请求头,自然无法校验客户端身份。

另外还存在3个可能触发异常的隐患点:

  • 授权跳转时redirect_uri使用encodeURI编码,特殊字符转义不完整,建议改用encodeURIComponent
  • 回调接口中redirect_uri硬编码,和授权阶段使用的环境变量配置可能存在不一致
  • 未添加Notion强制要求的Notion-Version请求头,部分场景会触发校验失败

修正后代码

import axios from "axios";
import { Request, Response } from "express";

router
.get("/notion/authorize", async (req : Request, res: Response) => { 
    // 修正redirect_uri编码方式
    const redirectURL = `${process.env.NOTION_AUTH_URL}?owner=user&client_id=${process.env.NOTION_CLIENT_ID}&response_type=code&redirect_uri=${encodeURIComponent(process.env.NOTION_REDIRECT_URI)}`
    res.redirect(redirectURL)
})
.get("/notion/callback", async (req: Request<any,any, any, {code : string } >, res: Response) => {
    try {
        const { code } = req.query;
        if(!code) {
            throw new Error("Code is required from notion")
        }
        // 修正axios参数结构:第二个参数为请求体,第三个参数为请求配置
        const response = await axios.post(
            'https://api.notion.com/v1/oauth/token',
            // 直接传请求体对象,axios会自动序列化为JSON,无需手动stringify
            {
                grant_type: "authorization_code",
                code: code,
                redirect_uri: process.env.NOTION_REDIRECT_URI
            },
            {
                headers: {
                    "Authorization": `Basic ${Buffer.from(`${process.env.NOTION_CLIENT_ID}:${process.env.NOTION_CLIENT_SECRET}`).toString('base64')}`,
                    "Content-Type": "application/json",
                    // 添加Notion要求的版本头,可替换为你使用的API版本
                    "Notion-Version": "2022-06-28"
                }
            }
        )
        const { access_token, bot_id, workspace_id, workspace_name } = response.data;
        
        // 后续业务代码

    }catch(err) {
        if(axios.isAxiosError(err)) {
            console.log(err.response?.data || err.message, err.response?.status)
        }
        return res.status(400).json({message: `用户注册失败 ${err}`})
    }
})

可选校验步骤

如果修改后仍有问题,可以打印Buffer.from(${process.env.NOTION_CLIENT_ID}:${process.env.NOTION_CLIENT_SECRET}).toString('base64')的结果,和Postman中请求的Authorization请求头值做对比,确认环境变量没有多空格、换行符等隐藏字符。

内容的提问来源于stack exchange,提问作者Lakshya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 12:45:02