[{"data":1,"prerenderedAt":1437},["ShallowReactive",2],{"page-/post/nuxt/nuxt-auth-quick-start":3,"surrounding-page":1428},{"id":4,"title":5,"author":6,"body":7,"date":1417,"description":1418,"extension":1419,"group":6,"lastmod":1420,"meta":1421,"navigation":814,"path":1422,"rawbody":1423,"seo":1424,"showTitle":6,"stem":1425,"tags":1426,"versions":6,"__hash__":1427},"content/post/nuxt/nuxt-auth-quick-start.md","Nuxt4 中使用 NuxtAuth 实现 Github 登录",null,{"type":8,"value":9,"toc":1411},"minimark",[10,30,43,51,54,61,64,72,78,82,89,99,134,137,155,198,208,256,259,447,454,458,471,642,649,656,705,720,727,730,741,752,758,779,894,910,920,982,998,1011,1014,1038,1041,1058,1061,1067,1080,1093,1099,1103,1113,1120,1126,1135,1148,1229,1277,1286,1291,1296,1302,1319,1326,1333,1336,1344,1347,1369,1375,1385,1390,1393,1402,1407],[11,12,13,14,18,19,22,23,26,27],"p",{},"大家都知道 ",[15,16,17],"code",{},"Next"," 有个 ",[15,20,21],{},"NextAuth"," 非常好用，其实 ",[15,24,25],{},"Nuxt"," 也有配套的 ",[15,28,29],{},"Auth Module",[11,31,32,33,36,37,39,40,42],{},"而且 ",[15,34,35],{},"NuxtAuth"," 还依赖于 ",[15,38,21],{},"，这为 ",[15,41,25],{}," 生态提供了非常多可靠性和便利性。",[11,44,45,47,48,50],{},[15,46,21],{}," 里能用的，在 ",[15,49,35],{}," 里同样支持",[11,52,53],{},"并且还针对Nuxt 提供了特定功能，比如登录、注销、身份验证中间件和插件等",[11,55,56,57,60],{},"使用 NuxtAuth 时需要注意，它包装了  ",[15,58,59],{},"next-auth@4.21.1"," ，因为更高的版本中更改了包导出。",[11,62,63],{},"所以使用时，安装指定版本就可以了。",[11,65,66,67,71],{},"这里我先以 ",[68,69,70],"strong",{},"Github登录"," 为例演示其用法",[11,73,74],{},[75,76,77],"em",{},"本系列在博客站上会持续更新，直到覆盖NuxtAuth的所有用法",[79,80,81],"h2",{"id":81},"初始化和安装依赖",[11,83,84,85,88],{},"我在一个 ",[15,86,87],{},"layer"," 中演示其使用方法，因为这部分功能相对独立，使用的地方继承就行。",[11,90,91,98],{},[92,93,97],"a",{"href":94,"rel":95},"https://nuxt.new/",[96],"nofollow","新建","项目",[100,101,106],"pre",{"className":102,"code":103,"language":104,"meta":105,"style":105},"language-shell shiki shiki-themes github-light","npx nuxi init -t layer zz-auth-layer\n","shell","",[15,107,108],{"__ignoreMap":105},[109,110,113,117,121,124,128,131],"span",{"class":111,"line":112},"line",1,[109,114,116],{"class":115},"s7eDp","npx",[109,118,120],{"class":119},"sYBdl"," nuxi",[109,122,123],{"class":119}," init",[109,125,127],{"class":126},"sYu0t"," -t",[109,129,130],{"class":119}," layer",[109,132,133],{"class":119}," zz-auth-layer\n",[11,135,136],{},"然后来到项目根目录下，安装依赖",[11,138,139,140,143,144,147,148,151,152,154],{},"注：这里有两种 ",[15,141,142],{},"Provider"," 可选，一个是 ",[15,145,146],{},"authjs(next-auth)"," ，一个是 ",[15,149,150],{},"local"," ，所谓 ",[15,153,150],{}," 就是你已经自己写好了一套登录逻辑，他可以帮你接管一下。",[100,156,158],{"className":102,"code":157,"language":104,"meta":105,"style":105},"pnpm exec nuxi module add sidebase-auth\n// 因为我们使用github登录，所以要安装这个包\npnpm add next-auth@4.21.1\n",[15,159,160,179,188],{"__ignoreMap":105},[109,161,162,165,168,170,173,176],{"class":111,"line":112},[109,163,164],{"class":115},"pnpm",[109,166,167],{"class":119}," exec",[109,169,120],{"class":119},[109,171,172],{"class":119}," module",[109,174,175],{"class":119}," add",[109,177,178],{"class":119}," sidebase-auth\n",[109,180,182,185],{"class":111,"line":181},2,[109,183,184],{"class":115},"//",[109,186,187],{"class":119}," 因为我们使用github登录，所以要安装这个包\n",[109,189,191,193,195],{"class":111,"line":190},3,[109,192,164],{"class":115},[109,194,175],{"class":119},[109,196,197],{"class":119}," next-auth@4.21.1\n",[11,199,200,201,204,205,207],{},"给此项目开启 ",[15,202,203],{},"Nuxt4"," 的特性，",[15,206,203],{}," 的目录结构更合理，更适合拓展。",[100,209,213],{"className":210,"code":211,"language":212,"meta":105,"style":105},"language-typescript shiki shiki-themes github-light","export default defineNuxtConfig({\n    future: {\n        compatibilityVersion: 4\n    },\n})\n","typescript",[15,214,215,231,236,244,250],{"__ignoreMap":105},[109,216,217,221,224,227],{"class":111,"line":112},[109,218,220],{"class":219},"sD7c4","export",[109,222,223],{"class":219}," default",[109,225,226],{"class":115}," defineNuxtConfig",[109,228,230],{"class":229},"sgsFI","({\n",[109,232,233],{"class":111,"line":181},[109,234,235],{"class":229},"    future: {\n",[109,237,238,241],{"class":111,"line":190},[109,239,240],{"class":229},"        compatibilityVersion: ",[109,242,243],{"class":126},"4\n",[109,245,247],{"class":111,"line":246},4,[109,248,249],{"class":229},"    },\n",[109,251,253],{"class":111,"line":252},5,[109,254,255],{"class":229},"})\n",[11,257,258],{},"然后把根目录下的内容改造成 v4 的结构",[100,260,262],{"className":102,"code":261,"language":104,"meta":105,"style":105},".\n├── .editorconfig\n├── .env\n├── .gitignore\n├── .npmignore\n├── .npmrc\n├── .nuxtrc\n├── README.md\n├── app\n│   ├── app.config.ts\n│   ├── app.vue\n│   └── components\n│       └── AuthView.vue\n├── nuxt.config.ts\n├── package.json\n├── pnpm-lock.yaml\n├── server\n│   ├── api\n│   │   └── auth\n│   └── tsconfig.json\n└── tsconfig.json\n",[15,263,264,269,277,284,291,298,306,314,322,330,342,352,363,374,382,390,398,406,416,429,439],{"__ignoreMap":105},[109,265,266],{"class":111,"line":112},[109,267,268],{"class":126},".\n",[109,270,271,274],{"class":111,"line":181},[109,272,273],{"class":115},"├──",[109,275,276],{"class":119}," .editorconfig\n",[109,278,279,281],{"class":111,"line":190},[109,280,273],{"class":115},[109,282,283],{"class":119}," .env\n",[109,285,286,288],{"class":111,"line":246},[109,287,273],{"class":115},[109,289,290],{"class":119}," .gitignore\n",[109,292,293,295],{"class":111,"line":252},[109,294,273],{"class":115},[109,296,297],{"class":119}," .npmignore\n",[109,299,301,303],{"class":111,"line":300},6,[109,302,273],{"class":115},[109,304,305],{"class":119}," .npmrc\n",[109,307,309,311],{"class":111,"line":308},7,[109,310,273],{"class":115},[109,312,313],{"class":119}," .nuxtrc\n",[109,315,317,319],{"class":111,"line":316},8,[109,318,273],{"class":115},[109,320,321],{"class":119}," README.md\n",[109,323,325,327],{"class":111,"line":324},9,[109,326,273],{"class":115},[109,328,329],{"class":119}," app\n",[109,331,333,336,339],{"class":111,"line":332},10,[109,334,335],{"class":115},"│",[109,337,338],{"class":119},"   ├──",[109,340,341],{"class":119}," app.config.ts\n",[109,343,345,347,349],{"class":111,"line":344},11,[109,346,335],{"class":115},[109,348,338],{"class":119},[109,350,351],{"class":119}," app.vue\n",[109,353,355,357,360],{"class":111,"line":354},12,[109,356,335],{"class":115},[109,358,359],{"class":119},"   └──",[109,361,362],{"class":119}," components\n",[109,364,366,368,371],{"class":111,"line":365},13,[109,367,335],{"class":115},[109,369,370],{"class":119},"       └──",[109,372,373],{"class":119}," AuthView.vue\n",[109,375,377,379],{"class":111,"line":376},14,[109,378,273],{"class":115},[109,380,381],{"class":119}," nuxt.config.ts\n",[109,383,385,387],{"class":111,"line":384},15,[109,386,273],{"class":115},[109,388,389],{"class":119}," package.json\n",[109,391,393,395],{"class":111,"line":392},16,[109,394,273],{"class":115},[109,396,397],{"class":119}," pnpm-lock.yaml\n",[109,399,401,403],{"class":111,"line":400},17,[109,402,273],{"class":115},[109,404,405],{"class":119}," server\n",[109,407,409,411,413],{"class":111,"line":408},18,[109,410,335],{"class":115},[109,412,338],{"class":119},[109,414,415],{"class":119}," api\n",[109,417,419,421,424,426],{"class":111,"line":418},19,[109,420,335],{"class":115},[109,422,423],{"class":119},"   │",[109,425,359],{"class":119},[109,427,428],{"class":119}," auth\n",[109,430,432,434,436],{"class":111,"line":431},20,[109,433,335],{"class":115},[109,435,359],{"class":119},[109,437,438],{"class":119}," tsconfig.json\n",[109,440,442,445],{"class":111,"line":441},21,[109,443,444],{"class":115},"└──",[109,446,438],{"class":119},[11,448,449,450,453],{},"组件 ",[15,451,452],{},"AuthView"," 里什么也没有，随便写点东西就行",[79,455,457],{"id":456},"配置-nuxtauth","配置 NuxtAuth",[11,459,460,461,464,465,470],{},"然后再把刚才安装的 ",[15,462,463],{},"nuxt-auth"," 配置一下。这里的配置内容，来源于",[92,466,469],{"href":467,"rel":468},"https://auth.sidebase.io/guide/application-side/configuration",[96],"官方文档","。",[100,472,474],{"className":210,"code":473,"language":212,"meta":105,"style":105},"export default defineNuxtConfig({\n    future: {\n        compatibilityVersion: 4\n    },\n    auth: {\n        isEnabled: true,\n        disableServerSideAuth: false,\n        originEnvKey: 'NUXT_AUTH_ORIGIN',\n        // baseURL: 'http://localhost:3000/api/auth',\n        provider: {\n          type: 'authjs',\n          trustHost: false,\n          defaultProvider: 'github',\n          addDefaultCallbackUrl: true,\n        },\n        sessionRefresh: {\n          enablePeriodically: 1000 * 60 * 5, // 5 分钟刷新一次\n          enableOnWindowFocus: true,\n        }\n    },\n})\n",[15,475,476,486,490,496,500,505,516,526,536,542,547,557,566,576,585,590,595,620,629,634,638],{"__ignoreMap":105},[109,477,478,480,482,484],{"class":111,"line":112},[109,479,220],{"class":219},[109,481,223],{"class":219},[109,483,226],{"class":115},[109,485,230],{"class":229},[109,487,488],{"class":111,"line":181},[109,489,235],{"class":229},[109,491,492,494],{"class":111,"line":190},[109,493,240],{"class":229},[109,495,243],{"class":126},[109,497,498],{"class":111,"line":246},[109,499,249],{"class":229},[109,501,502],{"class":111,"line":252},[109,503,504],{"class":229},"    auth: {\n",[109,506,507,510,513],{"class":111,"line":300},[109,508,509],{"class":229},"        isEnabled: ",[109,511,512],{"class":126},"true",[109,514,515],{"class":229},",\n",[109,517,518,521,524],{"class":111,"line":308},[109,519,520],{"class":229},"        disableServerSideAuth: ",[109,522,523],{"class":126},"false",[109,525,515],{"class":229},[109,527,528,531,534],{"class":111,"line":316},[109,529,530],{"class":229},"        originEnvKey: ",[109,532,533],{"class":119},"'NUXT_AUTH_ORIGIN'",[109,535,515],{"class":229},[109,537,538],{"class":111,"line":324},[109,539,541],{"class":540},"sAwPA","        // baseURL: 'http://localhost:3000/api/auth',\n",[109,543,544],{"class":111,"line":332},[109,545,546],{"class":229},"        provider: {\n",[109,548,549,552,555],{"class":111,"line":344},[109,550,551],{"class":229},"          type: ",[109,553,554],{"class":119},"'authjs'",[109,556,515],{"class":229},[109,558,559,562,564],{"class":111,"line":354},[109,560,561],{"class":229},"          trustHost: ",[109,563,523],{"class":126},[109,565,515],{"class":229},[109,567,568,571,574],{"class":111,"line":365},[109,569,570],{"class":229},"          defaultProvider: ",[109,572,573],{"class":119},"'github'",[109,575,515],{"class":229},[109,577,578,581,583],{"class":111,"line":376},[109,579,580],{"class":229},"          addDefaultCallbackUrl: ",[109,582,512],{"class":126},[109,584,515],{"class":229},[109,586,587],{"class":111,"line":384},[109,588,589],{"class":229},"        },\n",[109,591,592],{"class":111,"line":392},[109,593,594],{"class":229},"        sessionRefresh: {\n",[109,596,597,600,603,606,609,611,614,617],{"class":111,"line":400},[109,598,599],{"class":229},"          enablePeriodically: ",[109,601,602],{"class":126},"1000",[109,604,605],{"class":219}," *",[109,607,608],{"class":126}," 60",[109,610,605],{"class":219},[109,612,613],{"class":126}," 5",[109,615,616],{"class":229},", ",[109,618,619],{"class":540},"// 5 分钟刷新一次\n",[109,621,622,625,627],{"class":111,"line":408},[109,623,624],{"class":229},"          enableOnWindowFocus: ",[109,626,512],{"class":126},[109,628,515],{"class":229},[109,630,631],{"class":111,"line":418},[109,632,633],{"class":229},"        }\n",[109,635,636],{"class":111,"line":431},[109,637,249],{"class":229},[109,639,640],{"class":111,"line":441},[109,641,255],{"class":229},[11,643,644,645,648],{},"如果你此时是按照官方文档一步步的来的，那这里的 ",[15,646,647],{},"provider"," 就是后面配上的",[11,650,651,652,655],{},"然后在 ",[15,653,654],{},"app.config.ts"," 里增加一些信息",[100,657,659],{"className":210,"code":658,"language":212,"meta":105,"style":105},"export default defineAppConfig({\n  authLayer: {\n    name: 'Hello from Auth layer (playground)',\n    enabled: true,\n  }\n})\n\n",[15,660,661,672,677,687,696,701],{"__ignoreMap":105},[109,662,663,665,667,670],{"class":111,"line":112},[109,664,220],{"class":219},[109,666,223],{"class":219},[109,668,669],{"class":115}," defineAppConfig",[109,671,230],{"class":229},[109,673,674],{"class":111,"line":181},[109,675,676],{"class":229},"  authLayer: {\n",[109,678,679,682,685],{"class":111,"line":190},[109,680,681],{"class":229},"    name: ",[109,683,684],{"class":119},"'Hello from Auth layer (playground)'",[109,686,515],{"class":229},[109,688,689,692,694],{"class":111,"line":246},[109,690,691],{"class":229},"    enabled: ",[109,693,512],{"class":126},[109,695,515],{"class":229},[109,697,698],{"class":111,"line":252},[109,699,700],{"class":229},"  }\n",[109,702,703],{"class":111,"line":300},[109,704,255],{"class":229},[11,706,707,708,711,712,715,716,719],{},"当这个 ",[15,709,710],{},"auth-layer"," 层被其他模块 ",[15,713,714],{},"extend"," 时，这个 ",[15,717,718],{},"authLayer"," 对象就会被合并过去",[11,721,722,723,726],{},"所以也可以直接在其他模块中使用 ",[15,724,725],{},"useAppConfig().authLayer?.enabled"," 来判断当前是否开启了鉴权的层",[11,728,729],{},"然后再来配置环境变量",[11,731,732,733,736,737,740],{},"刚才已经配置了一个 ",[15,734,735],{},"originEnvKey"," 为 ",[15,738,739],{},"NUXT_AUTH_ORIGIN"," ，所以需要在 .env 中增加这个环境变量",[100,742,746],{"className":743,"code":744,"language":745,"meta":105,"style":105},"language-env shiki shiki-themes github-light","NUXT_AUTH_ORIGIN=http://localhost:3000/api/auth\n","env",[15,747,748],{"__ignoreMap":105},[109,749,750],{"class":111,"line":112},[109,751,744],{},[11,753,754,755],{},"此时配置的这个路径，需要我们自己定义出来，所以先新增这个接口",[15,756,757],{},"server/api/auth/[...].ts",[11,759,760,761,764,765,768,769,772,773,775,776],{},"这个接口是一个 ",[15,762,763],{},"NuxtAuthHandler"," ，是从 ",[15,766,767],{},"NextAuthHandler"," 基础上改编来的，我们需要在这个 ",[15,770,771],{},"Handler"," 中定义我们的 ",[15,774,142],{},"，也就是 ",[15,777,778],{},"Github",[100,780,782],{"className":210,"code":781,"language":212,"meta":105,"style":105},"import GithubProvider from 'next-auth/providers/github'\nimport { NuxtAuthHandler } from '#auth'\n\nexport default NuxtAuthHandler({\n  // A secret string you define, to ensure correct encryption\n  secret: 'your-secret-here',\n  providers: [\n    // @ts-expect-error Use .default here for it to work during SSR.\n    GithubProvider.default({\n      clientId: 'your-client-id',\n      clientSecret: 'your-client-secret'\n    })\n  ]\n})\n",[15,783,784,798,810,816,827,832,842,847,852,862,872,880,885,890],{"__ignoreMap":105},[109,785,786,789,792,795],{"class":111,"line":112},[109,787,788],{"class":219},"import",[109,790,791],{"class":229}," GithubProvider ",[109,793,794],{"class":219},"from",[109,796,797],{"class":119}," 'next-auth/providers/github'\n",[109,799,800,802,805,807],{"class":111,"line":181},[109,801,788],{"class":219},[109,803,804],{"class":229}," { NuxtAuthHandler } ",[109,806,794],{"class":219},[109,808,809],{"class":119}," '#auth'\n",[109,811,812],{"class":111,"line":190},[109,813,815],{"emptyLinePlaceholder":814},true,"\n",[109,817,818,820,822,825],{"class":111,"line":246},[109,819,220],{"class":219},[109,821,223],{"class":219},[109,823,824],{"class":115}," NuxtAuthHandler",[109,826,230],{"class":229},[109,828,829],{"class":111,"line":252},[109,830,831],{"class":540},"  // A secret string you define, to ensure correct encryption\n",[109,833,834,837,840],{"class":111,"line":300},[109,835,836],{"class":229},"  secret: ",[109,838,839],{"class":119},"'your-secret-here'",[109,841,515],{"class":229},[109,843,844],{"class":111,"line":308},[109,845,846],{"class":229},"  providers: [\n",[109,848,849],{"class":111,"line":316},[109,850,851],{"class":540},"    // @ts-expect-error Use .default here for it to work during SSR.\n",[109,853,854,857,860],{"class":111,"line":324},[109,855,856],{"class":229},"    GithubProvider.",[109,858,859],{"class":115},"default",[109,861,230],{"class":229},[109,863,864,867,870],{"class":111,"line":332},[109,865,866],{"class":229},"      clientId: ",[109,868,869],{"class":119},"'your-client-id'",[109,871,515],{"class":229},[109,873,874,877],{"class":111,"line":344},[109,875,876],{"class":229},"      clientSecret: ",[109,878,879],{"class":119},"'your-client-secret'\n",[109,881,882],{"class":111,"line":354},[109,883,884],{"class":229},"    })\n",[109,886,887],{"class":111,"line":365},[109,888,889],{"class":229},"  ]\n",[109,891,892],{"class":111,"line":376},[109,893,255],{"class":229},[11,895,896,897,900,901,903,904,907,908],{},"定义好后需要再定义一下 ",[15,898,899],{},"secret","，以及",[15,902,778],{}," 需要的 ",[68,905,906],{},"id"," 和 ",[68,909,899],{},[11,911,912,913,916,917],{},"先在 ",[15,914,915],{},"nuxt.config.ts"," 中定义 ",[15,918,919],{},"runtimeConfig",[100,921,923],{"className":210,"code":922,"language":212,"meta":105,"style":105},"runtimeConfig: {\n    authSecret: 'your_secret',\n    authOrigin: 'your_secret',\n    githubClientId: 'your_secret',\n    githubClientSecret: 'your_secret',\n  }\n",[15,924,925,932,945,956,967,978],{"__ignoreMap":105},[109,926,927,929],{"class":111,"line":112},[109,928,919],{"class":115},[109,930,931],{"class":229},": {\n",[109,933,934,937,940,943],{"class":111,"line":181},[109,935,936],{"class":115},"    authSecret",[109,938,939],{"class":229},": ",[109,941,942],{"class":119},"'your_secret'",[109,944,515],{"class":229},[109,946,947,950,952,954],{"class":111,"line":190},[109,948,949],{"class":115},"    authOrigin",[109,951,939],{"class":229},[109,953,942],{"class":119},[109,955,515],{"class":229},[109,957,958,961,963,965],{"class":111,"line":246},[109,959,960],{"class":115},"    githubClientId",[109,962,939],{"class":229},[109,964,942],{"class":119},[109,966,515],{"class":229},[109,968,969,972,974,976],{"class":111,"line":252},[109,970,971],{"class":115},"    githubClientSecret",[109,973,939],{"class":229},[109,975,942],{"class":119},[109,977,515],{"class":229},[109,979,980],{"class":111,"line":300},[109,981,700],{"class":229},[11,983,984,986,987,990,991,986,994,997],{},[15,985,919],{}," 里的 ",[15,988,989],{},"authSecret"," 会被 ",[15,992,993],{},".env",[15,995,996],{},"NUXT_AUTH_SECRET"," 所覆盖",[11,999,1000,1001,1003,1004,1006,1007,1010],{},"所以我们把真实的 ",[15,1002,989],{}," 写在 ",[15,1005,993],{}," ,然后在 ",[15,1008,1009],{},"git"," 中忽略即可",[11,1012,1013],{},"此时已经定义好了需要的四个环境变量",[100,1015,1017],{"className":743,"code":1016,"language":745,"meta":105,"style":105},"NUXT_AUTH_ORIGIN=http://localhost:3000/api/auth\nNUXT_AUTH_SECTRET=123131231231\nNUXT_GITHUB_CLIENT_ID=xxxxx\nNUXT_GITHUB_CLIENT_SECRET=xxxxxx\n",[15,1018,1019,1023,1028,1033],{"__ignoreMap":105},[109,1020,1021],{"class":111,"line":112},[109,1022,744],{},[109,1024,1025],{"class":111,"line":181},[109,1026,1027],{},"NUXT_AUTH_SECTRET=123131231231\n",[109,1029,1030],{"class":111,"line":190},[109,1031,1032],{},"NUXT_GITHUB_CLIENT_ID=xxxxx\n",[109,1034,1035],{"class":111,"line":246},[109,1036,1037],{},"NUXT_GITHUB_CLIENT_SECRET=xxxxxx\n",[11,1039,1040],{},"但是 Github 的 id 和 secret 还需要自己去申请",[11,1042,1043,1044,1047,1048,1051,1052,1051,1055],{},"先打开 Github ，登录后，点击",[68,1045,1046],{},"头像"," -> Settings -> 左侧菜单拉到最下边的 ",[68,1049,1050],{},"developer settings"," -> ",[68,1053,1054],{},"OAuth Apps",[68,1056,1057],{},"New OAuth App",[11,1059,1060],{},"然后填写如下表单即可",[11,1062,1063],{},[1064,1065],"img",{"alt":105,"src":1066},"https://img.zzao.club/article/202502241606759.png",[11,1068,1069,1070,1072,1073,1075,1076,1079],{},"因为请求 ",[15,1071,778],{}," 之后，",[15,1074,778],{}," 需要再跳过来，需要 ",[15,1077,1078],{},"callback URL"," 需要填你本地项目的一个页面地址",[11,1081,1082,1083],{},"等待上线后，",[68,1084,1085,1086,1089,1090,1092],{},"再把此处的",[15,1087,1088],{},"OAuth App"," 以及 ",[15,1091,993],{},"（看部署方式而定） 里配置的本地路径改为线上路径",[11,1094,1095,1096,1098],{},"注册后就会给你一个 id 和 secret ，把它写在 ",[15,1097,993],{}," 里",[79,1100,1102],{"id":1101},"在-playground-中使用","在 Playground 中使用",[11,1104,1105,1106,1108,1109,1112],{},"配置好后，我们就可以使用 ",[15,1107,35],{}," 提供的 ",[15,1110,1111],{},"composable"," 来管理鉴权的逻辑了",[11,1114,1115,1116,1119],{},"为什么不在根目录下直接写，而是在 ",[15,1117,1118],{},".playground"," 中写呢？",[11,1121,1122,1123,1125],{},"因为根目录下相当于一个独立的npm包，是要给其他项目使用的，而",[15,1124,1118],{},"就相当于是模拟了其他项目",[11,1127,1128,1129,1131,1132,1134],{},"所以在 ",[15,1130,87],{}," 部分，只需要完成通用的配置、页面、接口即可，具体使用时都在 ",[15,1133,1118],{}," 里操作",[11,1136,1137,1140,1141,1144,1145,1147],{},[15,1138,1139],{},"playground"," 里已经写好了 ",[15,1142,1143],{},"extends: ['..']","，所以我们直接新建一个页面来测试一下如何使用 ",[15,1146,778],{}," 登录",[100,1149,1151],{"className":210,"code":1150,"language":212,"meta":105,"style":105},"const {\n  status,\n  data,\n  lastRefreshedAt,\n  getCsrfToken,\n  getProviders,\n  getSession,\n  signIn,\n  signOut\n} = useAuth()\n",[15,1152,1153,1161,1168,1175,1182,1189,1196,1203,1210,1215],{"__ignoreMap":105},[109,1154,1155,1158],{"class":111,"line":112},[109,1156,1157],{"class":219},"const",[109,1159,1160],{"class":229}," {\n",[109,1162,1163,1166],{"class":111,"line":181},[109,1164,1165],{"class":126},"  status",[109,1167,515],{"class":229},[109,1169,1170,1173],{"class":111,"line":190},[109,1171,1172],{"class":126},"  data",[109,1174,515],{"class":229},[109,1176,1177,1180],{"class":111,"line":246},[109,1178,1179],{"class":126},"  lastRefreshedAt",[109,1181,515],{"class":229},[109,1183,1184,1187],{"class":111,"line":252},[109,1185,1186],{"class":126},"  getCsrfToken",[109,1188,515],{"class":229},[109,1190,1191,1194],{"class":111,"line":300},[109,1192,1193],{"class":126},"  getProviders",[109,1195,515],{"class":229},[109,1197,1198,1201],{"class":111,"line":308},[109,1199,1200],{"class":126},"  getSession",[109,1202,515],{"class":229},[109,1204,1205,1208],{"class":111,"line":316},[109,1206,1207],{"class":126},"  signIn",[109,1209,515],{"class":229},[109,1211,1212],{"class":111,"line":324},[109,1213,1214],{"class":126},"  signOut\n",[109,1216,1217,1220,1223,1226],{"class":111,"line":332},[109,1218,1219],{"class":229},"} ",[109,1221,1222],{"class":219},"=",[109,1224,1225],{"class":115}," useAuth",[109,1227,1228],{"class":229},"()\n",[100,1230,1234],{"className":1231,"code":1232,"language":1233,"meta":105,"style":105},"language-vue shiki shiki-themes github-light","\u003Cbutton @click=\"signIn('github')\"> 使用 Github 登录\u003C/button>\n","vue",[15,1235,1236],{"__ignoreMap":105},[109,1237,1238,1241,1245,1248,1251,1253,1256,1259,1262,1264,1267,1269,1272,1274],{"class":111,"line":112},[109,1239,1240],{"class":229},"\u003C",[109,1242,1244],{"class":1243},"shJU0","button",[109,1246,1247],{"class":229}," @",[109,1249,1250],{"class":115},"click",[109,1252,1222],{"class":229},[109,1254,1255],{"class":119},"\"",[109,1257,1258],{"class":115},"signIn",[109,1260,1261],{"class":229},"(",[109,1263,573],{"class":119},[109,1265,1266],{"class":229},")",[109,1268,1255],{"class":119},[109,1270,1271],{"class":229},"> 使用 Github 登录\u003C/",[109,1273,1244],{"class":1243},[109,1275,1276],{"class":229},">\n",[11,1278,1279,1280,1282,1283,1285],{},"当点击按钮时，就会跳转到 ",[15,1281,778],{},"，授权登录后就能拿到 ",[15,1284,778],{}," 提供的用户信息",[11,1287,1288],{},[68,1289,1290],{},"在此之后的逻辑就和Github无关了",[11,1292,1293,1295],{},[15,1294,35],{}," 会帮我们做一整套的逻辑，我们只需要拿到用户信息后，和自行注册的用户做一个关联即可",[11,1297,1298,1301],{},[15,1299,1300],{},"status"," 表示当前登录状态",[11,1303,1304,1307,1308,1311,1312,1314,1315,1318],{},[15,1305,1306],{},"data"," 表示当前会话中的数据，这个 ",[15,1309,1310],{},"sessionData"," 也可以在 ",[15,1313,763],{}," 的 ",[15,1316,1317],{},"callback"," 中插入一些用户信息",[11,1320,1321,1322,1325],{},"登出时，使用 ",[15,1323,1324],{},"signOut"," 即可",[11,1327,1328,1329,1332],{},"如果是使用自己写的逻辑，也就是 ",[15,1330,1331],{},"local Provider"," ，还可以配置 refreshToken 的接口等等",[79,1334,1335],{"id":1335},"部署",[11,1337,1338,1339,1341,1342],{},"部署时，只需要注意一下 ",[15,1340,993],{}," 文件，因为 Nuxt 打包后不会再动态读取 ",[15,1343,993],{},[11,1345,1346],{},"所以这些环境变量如何配置，和你使用的部署工具有关",[11,1348,1349,1350,1353,1354,1357,1358,1361,1362,1365,1366],{},"比如你用的 ",[15,1351,1352],{},"pm2","，在 ",[15,1355,1356],{},"ecosystem.config.cjs","，就可以配置 ",[15,1359,1360],{},"NODE_ENV","，但是我不推荐这样搞，因为如果代码要放在 ",[15,1363,1364],{},"github"," 上，就不要在任何一次提交中包含敏感的 ",[15,1367,1368],{},"token",[11,1370,1371,1372,1374],{},"除非你的 ",[15,1373,1356],{}," 不在你的项目里，而是直接写在服务器上对应的文件夹里，每次打包时只覆盖 Nuxt 的部分",[11,1376,1377,1378,1381,1382,1384],{},"如果是用 ",[15,1379,1380],{},"Gitea"," 来自动化部署，就在 ",[15,1383,1380],{}," 的仓库设置中配置变量",[11,1386,1387],{},[1064,1388],{"alt":105,"src":1389},"https://img.zzao.club/article/202502241606761.png",[11,1391,1392],{},"正常情况只需要配置一次，因为正式环境没有需要经常改变的变量",[11,1394,1395,1396,1398,1399],{},"以上就是如何使用 ",[15,1397,778],{}," 的小 ",[15,1400,1401],{},"Demo",[11,1403,1404],{},[68,1405,1406],{},"未完待续",[1408,1409,1410],"style",{},"html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .shJU0, html code.shiki .shJU0{--shiki-default:#22863A}",{"title":105,"searchDepth":181,"depth":181,"links":1412},[1413,1414,1415,1416],{"id":81,"depth":181,"text":81},{"id":456,"depth":181,"text":457},{"id":1101,"depth":181,"text":1102},{"id":1335,"depth":181,"text":1335},"2025-02-21T00:00:00.000Z","NuxtAuth 使用指南，快速实现Github登录","md","2025-08-19T00:00:00.000Z",{},"/post/nuxt/nuxt-auth-quick-start","---\ntitle: Nuxt4 中使用 NuxtAuth 实现 Github 登录\ndate: 2025-02-21\nlastmod: 2025-08-19\ntags: [\"Nuxt\"]\ndescription: NuxtAuth 使用指南，快速实现Github登录\n---\n\n大家都知道 `Next` 有个 `NextAuth` 非常好用，其实 `Nuxt` 也有配套的 `Auth Module`\n\n而且 `NuxtAuth` 还依赖于 `NextAuth`，这为 `Nuxt` 生态提供了非常多可靠性和便利性。\n\n`NextAuth` 里能用的，在 `NuxtAuth` 里同样支持\n\n并且还针对Nuxt 提供了特定功能，比如登录、注销、身份验证中间件和插件等\n\n使用 NuxtAuth 时需要注意，它包装了  `next-auth@4.21.1` ，因为更高的版本中更改了包导出。\n\n所以使用时，安装指定版本就可以了。\n\n这里我先以 **Github登录** 为例演示其用法\n\n_本系列在博客站上会持续更新，直到覆盖NuxtAuth的所有用法_\n## 初始化和安装依赖\n\n我在一个 `layer` 中演示其使用方法，因为这部分功能相对独立，使用的地方继承就行。\n\n[新建](https://nuxt.new/)项目\n\n```shell\nnpx nuxi init -t layer zz-auth-layer\n```\n\n然后来到项目根目录下，安装依赖\n\n注：这里有两种 `Provider` 可选，一个是 `authjs(next-auth)` ，一个是 `local` ，所谓 `local` 就是你已经自己写好了一套登录逻辑，他可以帮你接管一下。\n\n```shell\npnpm exec nuxi module add sidebase-auth\n// 因为我们使用github登录，所以要安装这个包\npnpm add next-auth@4.21.1\n```\n\n给此项目开启 `Nuxt4` 的特性，`Nuxt4` 的目录结构更合理，更适合拓展。\n\n```typescript\nexport default defineNuxtConfig({\n\tfuture: {\n\t    compatibilityVersion: 4\n\t},\n})\n```\n\n然后把根目录下的内容改造成 v4 的结构\n\n```shell\n.\n├── .editorconfig\n├── .env\n├── .gitignore\n├── .npmignore\n├── .npmrc\n├── .nuxtrc\n├── README.md\n├── app\n│   ├── app.config.ts\n│   ├── app.vue\n│   └── components\n│       └── AuthView.vue\n├── nuxt.config.ts\n├── package.json\n├── pnpm-lock.yaml\n├── server\n│   ├── api\n│   │   └── auth\n│   └── tsconfig.json\n└── tsconfig.json\n```\n\n组件 `AuthView` 里什么也没有，随便写点东西就行\n\n## 配置 NuxtAuth\n\n然后再把刚才安装的 `nuxt-auth` 配置一下。这里的配置内容，来源于[官方文档](https://auth.sidebase.io/guide/application-side/configuration)。\n\n```typescript\nexport default defineNuxtConfig({\n\tfuture: {\n\t    compatibilityVersion: 4\n\t},\n\tauth: {\n\t    isEnabled: true,\n\t    disableServerSideAuth: false,\n\t    originEnvKey: 'NUXT_AUTH_ORIGIN',\n\t    // baseURL: 'http://localhost:3000/api/auth',\n\t    provider: {\n\t      type: 'authjs',\n\t      trustHost: false,\n\t      defaultProvider: 'github',\n\t      addDefaultCallbackUrl: true,\n\t    },\n\t    sessionRefresh: {\n\t      enablePeriodically: 1000 * 60 * 5, // 5 分钟刷新一次\n\t      enableOnWindowFocus: true,\n\t    }\n\t},\n})\n```\n\n如果你此时是按照官方文档一步步的来的，那这里的 `provider` 就是后面配上的\n\n然后在 `app.config.ts` 里增加一些信息\n\n```typescript\nexport default defineAppConfig({\n  authLayer: {\n    name: 'Hello from Auth layer (playground)',\n    enabled: true,\n  }\n})\n\n```\n\n当这个 `auth-layer` 层被其他模块 `extend` 时，这个 `authLayer` 对象就会被合并过去\n\n所以也可以直接在其他模块中使用 `useAppConfig().authLayer?.enabled` 来判断当前是否开启了鉴权的层\n\n然后再来配置环境变量\n\n刚才已经配置了一个 `originEnvKey` 为 `NUXT_AUTH_ORIGIN` ，所以需要在 .env 中增加这个环境变量\n\n```env\nNUXT_AUTH_ORIGIN=http://localhost:3000/api/auth\n```\n\n此时配置的这个路径，需要我们自己定义出来，所以先新增这个接口`server/api/auth/[...].ts`\n\n这个接口是一个 `NuxtAuthHandler` ，是从 `NextAuthHandler` 基础上改编来的，我们需要在这个 `Handler` 中定义我们的 `Provider`，也就是 `Github`\n\n```typescript\nimport GithubProvider from 'next-auth/providers/github'\nimport { NuxtAuthHandler } from '#auth'\n\nexport default NuxtAuthHandler({\n  // A secret string you define, to ensure correct encryption\n  secret: 'your-secret-here',\n  providers: [\n    // @ts-expect-error Use .default here for it to work during SSR.\n    GithubProvider.default({\n      clientId: 'your-client-id',\n      clientSecret: 'your-client-secret'\n    })\n  ]\n})\n```\n\n定义好后需要再定义一下 `secret`，以及`Github` 需要的 **id** 和 **secret**\n\n先在 `nuxt.config.ts` 中定义 `runtimeConfig`\n\n```typescript\nruntimeConfig: {\n    authSecret: 'your_secret',\n    authOrigin: 'your_secret',\n    githubClientId: 'your_secret',\n    githubClientSecret: 'your_secret',\n  }\n```\n\n`runtimeConfig` 里的 `authSecret` 会被 `.env` 里的 `NUXT_AUTH_SECRET` 所覆盖\n\n所以我们把真实的 `authSecret` 写在 `.env` ,然后在 `git` 中忽略即可\n\n此时已经定义好了需要的四个环境变量\n\n```env\nNUXT_AUTH_ORIGIN=http://localhost:3000/api/auth\nNUXT_AUTH_SECTRET=123131231231\nNUXT_GITHUB_CLIENT_ID=xxxxx\nNUXT_GITHUB_CLIENT_SECRET=xxxxxx\n```\n\n但是 Github 的 id 和 secret 还需要自己去申请\n\n先打开 Github ，登录后，点击**头像** -> Settings -> 左侧菜单拉到最下边的 **developer settings** -> **OAuth Apps** -> **New OAuth App** \n\n然后填写如下表单即可\n\n![](https://img.zzao.club/article/202502241606759.png)\n\n因为请求 `Github` 之后，`Github` 需要再跳过来，需要 `callback URL` 需要填你本地项目的一个页面地址\n\n等待上线后，**再把此处的`OAuth App` 以及 `.env`（看部署方式而定） 里配置的本地路径改为线上路径**\n\n注册后就会给你一个 id 和 secret ，把它写在 `.env` 里\n\n\n## 在 Playground 中使用\n\n配置好后，我们就可以使用 `NuxtAuth` 提供的 `composable` 来管理鉴权的逻辑了\n\n为什么不在根目录下直接写，而是在 `.playground` 中写呢？\n\n因为根目录下相当于一个独立的npm包，是要给其他项目使用的，而`.playground`就相当于是模拟了其他项目\n\n所以在 `layer` 部分，只需要完成通用的配置、页面、接口即可，具体使用时都在 `.playground` 里操作\n\n`playground` 里已经写好了 `extends: ['..']`，所以我们直接新建一个页面来测试一下如何使用 `Github` 登录\n\n```typescript\nconst {\n  status,\n  data,\n  lastRefreshedAt,\n  getCsrfToken,\n  getProviders,\n  getSession,\n  signIn,\n  signOut\n} = useAuth()\n```\n\n```vue\n\u003Cbutton @click=\"signIn('github')\"> 使用 Github 登录\u003C/button>\n```\n\n当点击按钮时，就会跳转到 `Github`，授权登录后就能拿到 `Github` 提供的用户信息\n\n**在此之后的逻辑就和Github无关了**\n\n`NuxtAuth` 会帮我们做一整套的逻辑，我们只需要拿到用户信息后，和自行注册的用户做一个关联即可\n\n`status` 表示当前登录状态\n\n`data` 表示当前会话中的数据，这个 `sessionData` 也可以在 `NuxtAuthHandler` 的 `callback` 中插入一些用户信息\n\n登出时，使用 `signOut` 即可\n\n如果是使用自己写的逻辑，也就是 `local Provider` ，还可以配置 refreshToken 的接口等等\n\n## 部署\n\n部署时，只需要注意一下 `.env` 文件，因为 Nuxt 打包后不会再动态读取 `.env` \n\n所以这些环境变量如何配置，和你使用的部署工具有关\n\n比如你用的 `pm2`，在 `ecosystem.config.cjs`，就可以配置 `NODE_ENV`，但是我不推荐这样搞，因为如果代码要放在 `github` 上，就不要在任何一次提交中包含敏感的 `token` \n\n除非你的 `ecosystem.config.cjs` 不在你的项目里，而是直接写在服务器上对应的文件夹里，每次打包时只覆盖 Nuxt 的部分\n\n如果是用 `Gitea` 来自动化部署，就在 `Gitea` 的仓库设置中配置变量\n\n![](https://img.zzao.club/article/202502241606761.png)\n\n正常情况只需要配置一次，因为正式环境没有需要经常改变的变量\n\n以上就是如何使用 `Github` 的小 `Demo`\n\n**未完待续**",{"title":5,"description":1418},"post/nuxt/nuxt-auth-quick-start",[25],"BYDJh8-eVp_-tYDbQvWkesOU7O2SlePQJHiU-CP-T4g",[1429,1433],{"title":1430,"path":1431,"stem":1432},"OpenClaw 安装入门（Windows）","/post/zzao/openclaw/openclaw-install-windows","post/zzao/openclaw/openclaw-install-windows",{"title":1434,"path":1435,"stem":1436},"假设你是AI，你的Skill应该是什么样的","/post/zzao/ai-skill-structure","post/zzao/ai-skill-structure",1779005086088]