sidebase nuxt auth凭证认证异常求助:数据库请求成功但会话未授权
Sidebase Nuxt Auth 认证异常排查
我在应用中使用sidebase nuxt auth组件,通过axios对接外部API完成认证流程。API请求无报错且返回成功结果,但sidebase auth认证出现异常:认证逻辑返回true的同时,页面跳转到授权错误页,且index.vue中useSession()返回的状态为未授权。
相关代码
index.vue(表单定义文件)
<template> <section class="agents-enter"> {{status}} <div class="container-fluid"> <div class="row"> <div class="col-12 col-lg-6 col-xl-7 d-flex flex-column"> <SectionTitle title="Партнерам" subtitle="Приглашаем агентов к сотрудничеству по продаже автобусных билетов" description="Описание о том, что это и как это работает" /> <!-- <img v-if="!isMobile()" class="image-section" alt="Агенты" src="/img/agents/agents.svg"> --> </div> <div class="col-12 col-lg-6 col-xl-5 d-flex justify-content-end"> <div class="login-form-wrapper"> <div class="login-form"> <h3 class="title-form text-center"> Вход для агентов </h3> <form @submit.prevent="signIn('credentials', { username: userStore.username, password: userStore.password })"> <!-- TODO добавить is-ok-bordered или is-error-bordered для валидации--> <div class="form-group"> <label for="login" class="form-label">Эл. почта</label> <div class="position-relative"> <input v-model="userStore.username" id="mail" type="text" class="form-control" name="mail" placeholder="Введите email"> <div class="form-icon-wrapper"> <IconsMail class="form-icon" color="#B5BDDB" /> </div> <div class="is-error-icon d-none"> <IconsMail class="form-icon" color="#fff" /> </div> </div> <div class="error-feedback-bordered d-none"> Неверная почта </div> </div> <div class="form-group"> <label for="passwordInputForm" class="form-label">Пароль</label> <div class="position-relative"> <input v-model="userStore.password" id="passwordInputForm" type="password" class="form-control" name="password" placeholder="Введите пароль"> <div class="form-icon-wrapper"> <IconsPassword class="form-icon" color="#B5BDDB" /> </div> <div class="is-error-icon d-none"> <IconsPassword class="form-icon" color="#fff" /> </div> </div> <div class="error-feedback-bordered d-none"> Неверный пароль </div> </div> <div class="d-grid mt-4"> <button :class="{'btn-disabled' : !userStore.username || !userStore.password}" type="submit" class="btn d-block"> Войти </button> </div> </form> <ForgotPasswordLink class="forgotPasswordLink" /> </div> </div> </div> </div> </div> </section> <ForgotPasswordModal /> </template> <script setup> import {useUserStore} from "~/stores/userStore"; const userStore = useUserStore() const { status, data, signIn, signOut } = useSession() </script> <script> export default { name: "index", } </script> <style scoped> </style>
认证逻辑文件(路径:server/api/auth/[...].ts)
// file: ~/server/api/auth/[...].ts import { NuxtAuthHandler } from '#auth' import CredentialsProvider from "next-auth/providers/credentials"; import axios from "axios"; export default NuxtAuthHandler({ secret: 'your-secret-here', session: { strategy: 'jwt' }, jwt: { // The maximum age of the NextAuth.js issued JWT in seconds. // Defaults to `session.maxAge`. maxAge: 60 * 60 * 24 * 30, }, providers: [ // @ts-expect-error You need to use .default here for it to work during SSR. May be fixed via Vite at some point CredentialsProvider.default({ // The name to display on the sign in form (e.g. 'Sign in with...') name: 'Credentials', // The credentials is used to generate a suitable form on the sign in page. // You can specify whatever fields you are expecting to be submitted. // e.g. domain, username, password, 2FA token, etc. // You can pass any HTML attribute to the <input> tag through the object. credentials: { username: { label: 'Username', type: 'text'}, password: { label: 'Password', type: 'password'} }, async authorize (credentials: any) { try { // You need to provide your own logic here that takes the credentials // submitted and returns either a object representing a user or value // that is false/null if the credentials are invalid. const usernameForm = credentials?.username const passwordForm = credentials?.password const config = { url: 'https://api3.evrotrans.net/auth/login', credentials: 'include', method: 'post', headers: { "Content-type": "application/json; charset=UTF-8" }, data: '{"login":"' + usernameForm +'","password":"' + passwordForm + '"}', } await axios.request(config).then((response) => { const user = {username: usernameForm, password: passwordForm, token: response.data.token} console.log(response.data.message) if (response.data.error == 0) { // Any object returned will be saved in `user` property of the JWT console.log('авторизован') return true } if (response.data.password == 'Incorrect login or password.') { console.error('неправильный логин или пароль') return null } }) } catch (e) { console.log(e) return e } } }) ], })
问题排查与修复
核心问题1:authorize函数无有效返回值
你在authorize里使用了axios.request().then(...)的回调写法,回调内的return仅作用于内部函数,外层authorize实际返回undefined,会被Nuxt Auth判定为认证失败。
核心问题2:返回true不符合要求
Nuxt Auth要求authorize函数在认证成功时返回用户对象,而非true。返回true会被视为无效用户信息,导致会话无法创建。
核心问题3:错误处理逻辑错误
catch块返回错误对象e,会被判定为认证失败,正确做法是返回null或false表示认证失败。
修复后的认证逻辑代码
// file: ~/server/api/auth/[...].ts import { NuxtAuthHandler } from '#auth' import CredentialsProvider from "next-auth/providers/credentials"; import axios from "axios"; export default NuxtAuthHandler({ secret: 'your-secret-here', session: { strategy: 'jwt' }, jwt: { maxAge: 60 * 60 * 24 * 30, }, providers: [ CredentialsProvider.default({ name: 'Credentials', credentials: { username: { label: 'Username', type: 'text'}, password: { label: 'Password', type: 'password'} }, async authorize (credentials: any) { try { const usernameForm = credentials?.username const passwordForm = credentials?.password // 改用await替代then回调,确保返回值传递到外层函数 const response = await axios.request({ url: 'https://api3.evrotrans.net/auth/login', credentials: 'include', method: 'post', headers: { "Content-type": "application/json; charset=UTF-8" }, data: JSON.stringify({ login: usernameForm, password: passwordForm }), // 用JSON.stringify避免字符串拼接风险 }) if (response.data.error === 0) { // 返回包含用户标识的对象,会被存入JWT return { id: usernameForm, username: usernameForm, token: response.data.token } } // 账号密码错误时返回null if (response.data.password === 'Incorrect login or password.') { console.error('неправильный логин или пароль') return null } // 其他未知情况返回null return null } catch (e) { console.error('认证请求出错:', e) // 认证失败必须返回null或false return null } } }) ], })
额外优化点
- 用
JSON.stringify替代手动拼接JSON字符串,避免语法错误和注入风险 - 错误日志改用
console.error,更符合调试规范 - 移除冗余注释,保持代码简洁
内容的提问来源于stack exchange,提问作者bobrov_site
相关产品推荐
相关产品推荐

