{"id":569,"date":"2026-06-15T03:06:17","date_gmt":"2026-06-15T03:06:17","guid":{"rendered":"https:\/\/hyc.eshachem.com\/program\/?page_id=569"},"modified":"2026-06-15T10:21:12","modified_gmt":"2026-06-15T10:21:12","slug":"sso-%e5%96%ae%e4%b8%80%e7%99%bb%e5%85%a5%e6%8a%80%e8%a1%93%e6%96%87%e4%bb%b6-next-js","status":"publish","type":"page","link":"https:\/\/hyc.eshachem.com\/program\/birc\/sso-%e5%96%ae%e4%b8%80%e7%99%bb%e5%85%a5%e6%8a%80%e8%a1%93%e6%96%87%e4%bb%b6-next-js\/","title":{"rendered":"Next.js &#8211; SSO in BIRC"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">\u672c\u6587\u4ef6\u900f\u904e\u5546\u696d\u667a\u6167\u7814\u7a76\u4e2d\u5fc3 \u2013 \u667a\u6167\u6821\u5712:\u6d3b\u52d5\u7cfb\u7d71\u4f86\u8aaa\u660e<strong>\u5546\u667a\u4e2d\u5fc3\u7684SSO\u67b6\u69cb<\/strong>\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u5546\u667a\u4e2d\u5fc3\u7684SSO\u70ba\u81ea\u67b6\u4f3a\u670d\u5668\uff0c<strong>\u975e<\/strong>Google SSO\uff0c\u4f46\u539f\u7406\u5927\u540c\u5c0f\u7570\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u8a72\u6587\u7ae0\u6240\u8ff0\u7a0b\u5f0f\u78bc\u7686\u63d0\u4f9b\u65bc\u6587\u7ae0\u6700\u4e0b\u65b9GitHub\u4e2d\u3002<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">\u7248\u672c\uff1a0.1.0<br>\u6700\u5f8c\u66f4\u65b0\u6642\u9593\uff1a06\/10\/2026<br>\u6700\u5f8c\u66f4\u65b0\u4eba\u54e1\uff1a\u9673\u6cd3\u6bd3<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\" id=\"%e8%a8%ad%e8%a8%88%e5%8e%9f%e5%89%87\">\u8a2d\u8a08\u539f\u5247<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u539f\u5247<\/th><th>\u8aaa\u660e<\/th><\/tr><\/thead><tbody><tr><td><strong>\u524d\u7aef\u4e0d\u6301\u6709 Client Secret<\/strong><\/td><td>\u524d\u7aef<strong>\u7d55\u4e0d<\/strong>\u76f4\u63a5\u547c\u53eb SSO <code>\/sso\/verify-code<\/code><\/td><\/tr><tr><td><strong>\u6388\u6b0a\u78bc\u50c5\u7528\u65bc\u4ea4\u63db<\/strong><\/td><td>SSO \u56de\u50b3\u7684 <code>code<\/code> \u70ba\u4e00\u6b21\u6027\u6388\u6b0a\u78bc\uff0c\u61c9\u7acb\u5373\u8f49\u4ea4\u5f8c\u7aef\u4ea4\u63db JWT<\/td><\/tr><tr><td><strong>JWT \u4f5c\u70ba\u5f8c\u7e8c\u8a8d\u8b49<\/strong><\/td><td>\u767b\u5165\u5b8c\u6210\u5f8c\uff0c\u6240\u6709\u53d7\u4fdd\u8b77 API \u4ee5 <code>Authorization: Bearer {JWT}<\/code> \u5b58\u53d6<\/td><\/tr><tr><td><strong>\u5168\u7ad9\u56de\u8abf\u8655\u7406<\/strong><\/td><td>SSO \u53ef\u56de\u8df3\u81f3\u4efb\u610f\u9801\u9762\uff0c<code>AuthProvider<\/code> \u7d71\u4e00\u6514\u622a <code>?code=<\/code> \u53c3\u6578<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\" id=\"%e7%b3%bb%e7%b5%b1%e8%a7%92%e8%89%b2\">\u7cfb\u7d71\u89d2\u8272<\/h2>\n\n\n\n<div class=\"wp-block-merpress-mermaidjs diagram-source-mermaid\"><pre class=\"mermaid\">flowchart LR\n    User[\u4f7f\u7528\u8005]\n    FE[Campus Activity \u524d\u7aef]\n    SSO[SSO \u767b\u5165\u4e2d\u5fc3]\n    BE[Campus Activity \u5f8c\u7aef]\n\n    User --&gt;|\u9ede\u64ca\u767b\u5165| FE\n    FE --&gt;|GET \/sso\/prelogin| SSO\n    SSO --&gt;|302 ?code=| FE\n    FE --&gt;|POST \/auth-tokens\/exchange| BE\n    BE --&gt;|X-Auth-Token JWT| FE\n    FE --&gt;|Bearer JWT| BE<\/pre><\/div>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u89d2\u8272<\/th><th>\u8077\u8cac<\/th><\/tr><\/thead><tbody><tr><td><strong>SSO \u767b\u5165\u4e2d\u5fc3<\/strong><\/td><td>Google \u7b49 IdP \u767b\u5165\u3001\u6838\u767c\u4e00\u6b21\u6027\u6388\u6b0a\u78bc<\/td><\/tr><tr><td><strong>\u524d\u7aef<\/strong><\/td><td>\u5c0e\u5411 SSO\u3001\u63a5\u6536 <code>code<\/code>\u3001\u5411\u5f8c\u7aef\u4ea4\u63db JWT\u3001\u5132\u5b58\u4e26\u9644\u5e36 JWT \u547c\u53eb API<\/td><\/tr><tr><td><strong>\u5f8c\u7aef<\/strong><\/td><td>\u4ee5 Client Secret \u9a57\u8b49 <code>code<\/code>\u3001\u5efa\u7acb\/\u66f4\u65b0\u4f7f\u7528\u8005\u3001\u6838\u767c JWT<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\" id=\"%e6%95%b4%e9%ab%94%e6%b5%81%e7%a8%8b\">\u6574\u9ad4\u6d41\u7a0b<\/h2>\n\n\n\n<div class=\"wp-block-merpress-mermaidjs diagram-source-mermaid\"><pre class=\"mermaid\">sequenceDiagram\n    participant User as \u4f7f\u7528\u8005\n    participant Navbar as Navbar \/ AuthContext\n    participant SSO as SSO \u767b\u5165\u4e2d\u5fc3\n    participant BE as Campus Activity \u5f8c\u7aef\n\n    User-&gt;&gt;Navbar: \u9ede\u64ca\u53f3\u4e0a\u89d2\u300c\u767b\u5165\u300d\n    Navbar-&gt;&gt;SSO: GET \/sso\/prelogin?redirect_url={\u76ee\u524d\u9801\u9762\u7db2\u5740}\n    SSO-&gt;&gt;User: \u986f\u793a Google \u767b\u5165\u9801\n    User-&gt;&gt;SSO: \u5b8c\u6210\u8eab\u5206\u9a57\u8b49\n    SSO-&gt;&gt;Navbar: 302 \u8df3\u8f49 redirect_url?code={\u6388\u6b0a\u78bc}\n    Navbar-&gt;&gt;BE: POST \/auth-tokens\/exchange&lt;br\/&gt;Header: X-Client-Token: {code}\n    BE-&gt;&gt;SSO: POST \/sso\/verify-code\uff08\u50c5\u5f8c\u7aef\uff09\n    SSO--&gt;&gt;BE: { email, name, picture }\n    BE--&gt;&gt;Navbar: 200 OK&lt;br\/&gt;Header: X-Auth-Token: {JWT}\n    Navbar-&gt;&gt;Navbar: \u89e3\u6790 JWT\u3001\u5132\u5b58 authToken\u3001\u66f4\u65b0 UI\n    User-&gt;&gt;Navbar: \u53f3\u4e0a\u89d2\u986f\u793a\u982d\u50cf\uff08Google \u7167\u7247\u6216\u540d\u7a31\u9996\u5b57\uff09\n    User-&gt;&gt;Navbar: \u9ede\u64ca\u982d\u50cf\u9032\u5165 \/profile<\/pre><\/div>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\" id=\"%e7%92%b0%e5%a2%83%e8%ae%8a%e6%95%b8\">\u74b0\u5883\u8b8a\u6578<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u8b8a\u6578<\/th><th>\u5fc5\u586b<\/th><th>\u8aaa\u660e<\/th><th>\u7bc4\u4f8b<\/th><\/tr><\/thead><tbody><tr><td><code>NEXT_PUBLIC_API_URL<\/code><\/td><td>\u662f<\/td><td>\u5f8c\u7aef API Base URL\uff08\u542b <code>\/api<\/code> \u524d\u7db4\uff09<\/td><td><code>https:\/\/api.example.com\/api<\/code><\/td><\/tr><tr><td><code>NEXT_PUBLIC_SSO_BASE_URL<\/code><\/td><td>\u5426<\/td><td>SSO \u767b\u5165\u4e2d\u5fc3 Base URL<\/td><td><code>https:\/\/sso.ntubimdbirc.tw<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><code>.env.local<\/code> \u7bc4\u4f8b\uff1a<\/strong><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">NEXT_PUBLIC_API_URL=http:<span class=\"hljs-comment\">\/\/localhost:8080\/api<\/span>\nNEXT_PUBLIC_SSO_BASE_URL=https:<span class=\"hljs-comment\">\/\/sso.ntubimdbirc.tw<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\" id=\"%e5%89%8d%e7%ab%af%e5%af%a6%e4%bd%9c\">\u524d\u7aef\u5be6\u4f5c<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"%e6%aa%94%e6%a1%88%e8%81%b7%e8%b2%ac\">\u6a94\u6848\u8077\u8cac<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u6a94\u6848<\/th><th>\u8077\u8cac<\/th><\/tr><\/thead><tbody><tr><td><code>lib\/sso.ts<\/code><\/td><td>SSO \u5c0e\u5411 URL\u3001\u5411\u5f8c\u7aef\u4ea4\u63db JWT\u3001JWT \u89e3\u6790\u3001Token \u5132\u5b58<\/td><\/tr><tr><td><code>lib\/api.ts<\/code><\/td><td>\u9644\u5e36 <code>Authorization: Bearer<\/code> \u7684 API \u8acb\u6c42\u5c01\u88dd<\/td><\/tr><tr><td><code>lib\/hooks\/AuthContext.tsx<\/code><\/td><td>\u5168\u7ad9\u8a8d\u8b49\u72c0\u614b\u3001SSO \u56de\u8abf\u8655\u7406\u3001\u540c\u6b65 EventsContext \u4f7f\u7528\u8005<\/td><\/tr><tr><td><code>components\/layout\/Navbar.tsx<\/code><\/td><td>\u672a\u767b\u5165\u986f\u793a\u300c\u767b\u5165\u300d\u3001\u5df2\u767b\u5165\u986f\u793a\u982d\u50cf\u4e26\u9023\u7d50 <code>\/profile<\/code><\/td><\/tr><tr><td><code>app\/layout.tsx<\/code><\/td><td>\u639b\u8f09 <code>AuthProvider<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"%e7%99%bc%e8%b5%b7%e7%99%bb%e5%85%a5\">\u767c\u8d77\u767b\u5165<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u4f7f\u7528\u8005\u9ede\u64ca Navbar\u300c\u767b\u5165\u300d\u6642\uff0c\u547c\u53eb <code>startSsoLogin()<\/code>\uff1a<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> redirectUrl = <span class=\"hljs-built_in\">window<\/span>.location.origin + pathname;\n<span class=\"hljs-built_in\">window<\/span>.location.href = buildSsoLoginUrl(redirectUrl);\n<span class=\"hljs-comment\">\/\/ \u2192 https:\/\/sso.ntubimdbirc.tw\/sso\/prelogin?redirect_url={encodeURIComponent(redirectUrl)}<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">\u5be6\u4f5c\u4f4d\u7f6e\uff1a<code>lib\/sso.ts<\/code> \u2192 <code>buildSsoLoginUrl()<\/code><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"%e8%99%95%e7%90%86-sso-%e5%9b%9e%e8%aa%bf\">\u8655\u7406 SSO \u56de\u8abf<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><code>AuthProvider<\/code> \u76e3\u807d URL \u67e5\u8a62\u53c3\u6578\uff1a<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u53c3\u6578<\/th><th>\u8655\u7406\u65b9\u5f0f<\/th><\/tr><\/thead><tbody><tr><td><code>?error=...<\/code><\/td><td>\u986f\u793a\u932f\u8aa4 Toast\uff0c\u6e05\u9664 URL \u53c3\u6578<\/td><\/tr><tr><td><code>?code=...<\/code><\/td><td>\u547c\u53eb\u5f8c\u7aef\u4ea4\u63db JWT\uff0c\u6210\u529f\u5f8c <code>router.replace(pathname)<\/code> \u6e05\u9664 code<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ lib\/hooks\/AuthContext.tsx\uff08\u6458\u8981\uff09<\/span>\n<span class=\"hljs-keyword\">const<\/span> code = searchParams.get(<span class=\"hljs-string\">'code'<\/span>);\n<span class=\"hljs-keyword\">if<\/span> (code) {\n  <span class=\"hljs-keyword\">const<\/span> { token, user } = <span class=\"hljs-keyword\">await<\/span> exchangeCodeForToken(code);\n  saveAuthToken(token);\n  router.replace(pathname); <span class=\"hljs-comment\">\/\/ \u6e05\u9664 URL \u4e2d\u7684 code<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\" id=\"%e5%90%91%e5%be%8c%e7%ab%af%e4%ba%a4%e6%8f%9b-jwt\">\u5411\u5f8c\u7aef\u4ea4\u63db JWT<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ lib\/sso.ts<\/span>\n<span class=\"hljs-keyword\">const<\/span> response = <span class=\"hljs-keyword\">await<\/span> fetch(<span class=\"hljs-string\">`<span class=\"hljs-subst\">${NEXT_PUBLIC_API_URL}<\/span>\/auth-tokens\/exchange`<\/span>, {\n  <span class=\"hljs-attr\">method<\/span>: <span class=\"hljs-string\">'POST'<\/span>,\n  <span class=\"hljs-attr\">headers<\/span>: {\n    <span class=\"hljs-string\">'X-Client-Token'<\/span>: code,\n  },\n});\n\n<span class=\"hljs-keyword\">const<\/span> jwt = response.headers.get(<span class=\"hljs-string\">'X-Auth-Token'<\/span>);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u9805\u76ee<\/th><th>\u8aaa\u660e<\/th><\/tr><\/thead><tbody><tr><td>\u7aef\u9ede<\/td><td><code>POST {NEXT_PUBLIC_API_URL}\/auth-tokens\/exchange<\/code><\/td><\/tr><tr><td>\u8acb\u6c42 Header<\/td><td><code>X-Client-Token: {SSO \u6388\u6b0a\u78bc}<\/code><\/td><\/tr><tr><td>\u6210\u529f\u56de\u61c9 Header<\/td><td><code>X-Auth-Token: {JWT}<\/code><\/td><\/tr><tr><td>\u8a8d\u8b49\u9700\u6c42<\/td><td>\u4e0d\u9700\u8981 Bearer Token\uff08\u516c\u958b\u7aef\u9ede\uff09<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>CORS\uff1a<\/strong> \u5f8c\u7aef\u5df2\u5c07 <code>X-Auth-Token<\/code> \u52a0\u5165 <code>exposedHeaders<\/code>\uff0c\u524d\u7aef\u624d\u80fd\u5f9e Response Header \u8b80\u53d6 JWT\u3002<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"jwt-%e5%84%b2%e5%ad%98%e8%88%87%e8%a7%a3%e6%9e%90\">JWT \u5132\u5b58\u8207\u89e3\u6790<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u767b\u5165\u6210\u529f\u5f8c\uff0cJWT \u5132\u5b58\u65bc <code>localStorage<\/code>\uff1a<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">localStorage.setItem(<span class=\"hljs-string\">'authToken'<\/span>, jwt);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">\u4f7f\u7528\u8005\u8cc7\u8a0a\u5f9e JWT Payload \u89e3\u6790\uff08\u4e0d\u9700\u53e6\u5916\u5132\u5b58\u4f7f\u7528\u8005\u7269\u4ef6\uff09\uff1a<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>JWT Claim<\/th><th>\u524d\u7aef\u7528\u9014<\/th><\/tr><\/thead><tbody><tr><td><code>sub<\/code><\/td><td>\u5b78\u6821\u4fe1\u7bb1\uff08<code>email<\/code>\uff09<\/td><\/tr><tr><td><code>id<\/code><\/td><td>\u672c\u5730\u4f7f\u7528\u8005 ID<\/td><\/tr><tr><td><code>name<\/code><\/td><td>\u986f\u793a\u540d\u7a31<\/td><\/tr><tr><td><code>picture<\/code><\/td><td>Google \u982d\u50cf URL<\/td><\/tr><tr><td><code>role<\/code><\/td><td>\u89d2\u8272\uff08\u5982 <code>ROLE_TEACHER_STUDENT<\/code>\uff09<\/td><\/tr><tr><td><code>exp<\/code><\/td><td>\u904e\u671f\u6642\u9593\uff0c\u7528\u65bc\u5224\u65b7\u662f\u5426\u9700\u91cd\u65b0\u767b\u5165<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">\u5be6\u4f5c\u4f4d\u7f6e\uff1a<code>lib\/sso.ts<\/code> \u2192 <code>parseJwtPayload()<\/code>\u3001<code>authUserFromJwt()<\/code>\u3001<code>isTokenExpired()<\/code><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"%e5%91%bc%e5%8f%ab%e5%8f%97%e4%bf%9d%e8%ad%b7-api\">\u547c\u53eb\u53d7\u4fdd\u8b77 API<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u4f7f\u7528 <code>lib\/api.ts<\/code> \u5c01\u88dd\uff1a<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> { apiJson, apiFetch } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'@\/lib\/api'<\/span>;\n\n<span class=\"hljs-comment\">\/\/ GET \u7bc4\u4f8b<\/span>\n<span class=\"hljs-keyword\">const<\/span> events = <span class=\"hljs-keyword\">await<\/span> apiJson&lt;Event[]&gt;(<span class=\"hljs-string\">'\/events'<\/span>);\n\n<span class=\"hljs-comment\">\/\/ \u81ea\u8a02\u8acb\u6c42<\/span>\n<span class=\"hljs-keyword\">const<\/span> response = <span class=\"hljs-keyword\">await<\/span> apiFetch(<span class=\"hljs-string\">'\/events'<\/span>, { <span class=\"hljs-attr\">method<\/span>: <span class=\"hljs-string\">'GET'<\/span> });<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><code>apiFetch<\/code> \u6703\u81ea\u52d5\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u5f9e <code>localStorage<\/code> \u8b80\u53d6 <code>authToken<\/code><\/li>\n\n\n\n<li>\u9644\u52a0 <code>Authorization: Bearer {JWT}<\/code><\/li>\n\n\n\n<li>\u6536\u5230 <code>401<\/code> \u6642\u6e05\u9664 Token \u4e26\u62cb\u51fa <code>ApiError<\/code><\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\" id=\"ui-%e8%a1%8c%e7%82%ba\">UI \u884c\u70ba<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Navbar \u53f3\u4e0a\u89d2<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u72c0\u614b<\/th><th>\u986f\u793a<\/th><th>\u884c\u70ba<\/th><\/tr><\/thead><tbody><tr><td>\u672a\u767b\u5165<\/td><td>\u300c\u767b\u5165\u300d\u6309\u9215<\/td><td>\u89f8\u767c <code>startSsoLogin()<\/code><\/td><\/tr><tr><td>\u5df2\u767b\u5165<\/td><td>\u5713\u5f62\u982d\u50cf<\/td><td>\u6709 <code>picture<\/code> \u986f\u793a Google \u7167\u7247\uff0c\u5426\u5247\u986f\u793a\u540d\u7a31\u9996\u5b57<\/td><\/tr><tr><td>\u5df2\u767b\u5165\u9ede\u64ca\u982d\u50cf<\/td><td>\u2014<\/td><td>\u5c0e\u5411 <code>\/profile<\/code> \u500b\u4eba\u9801<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">\u624b\u6a5f\u7248 Overlay \u9078\u55ae\u4ea6\u6709\u5c0d\u61c9\u7684\u767b\u5165\u6309\u9215\u8207\u982d\u50cf\u9023\u7d50\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u500b\u4eba\u9801\u540c\u6b65<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u767b\u5165\u6210\u529f\u5f8c\uff0c<code>AuthContext<\/code> \u6703\u5c07 JWT \u4e2d\u7684\u4f7f\u7528\u8005\u8cc7\u6599\u540c\u6b65\u81f3 <code>EventsContext<\/code>\uff0c\u4f9b <code>\/profile<\/code> \u7b49\u9801\u9762\u4f7f\u7528\uff1a<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ role \u5c0d\u61c9<\/span>\nROLE_ADMIN \/ ROLE_ACTIVITY_ADMIN \/ ROLE_VENUE_ADMIN \u2192 <span class=\"hljs-string\">'admin'<\/span>\n\u5176\u9918 \u2192 <span class=\"hljs-string\">'user'<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\" id=\"%e9%8c%af%e8%aa%a4%e8%99%95%e7%90%86\">\u932f\u8aa4\u8655\u7406<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>SSO \u56de\u8abf\u932f\u8aa4<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">SSO \u767b\u5165\u5931\u6557\u6642\uff0c\u6703\u4ee5 <code>?error={\u4ee3\u78bc}<\/code> \u8df3\u8f49\u56de\u524d\u7aef\u3002<code>AuthContext<\/code> \u4ee5 Toast \u63d0\u793a\u4f7f\u7528\u8005\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u5f8c\u7aef\u4ea4\u63db\u932f\u8aa4<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u5f8c\u7aef\u56de\u61c9\u683c\u5f0f\uff08\u53c3\u8003\u5f8c\u7aef\u6587\u4ef6\uff09\uff1a<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\">{\n  <span class=\"hljs-attr\">\"errorCode\"<\/span>: <span class=\"hljs-string\">\"SSO_ERROR\"<\/span>,\n  <span class=\"hljs-attr\">\"message\"<\/span>: <span class=\"hljs-string\">\"unauthorized\"<\/span>,\n  <span class=\"hljs-attr\">\"status\"<\/span>: <span class=\"hljs-number\">401<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>message<\/th><th>\u524d\u7aef\u5efa\u8b70\u8655\u7406<\/th><\/tr><\/thead><tbody><tr><td><code>missing_code<\/code><\/td><td>\u63d0\u793a\u300c\u767b\u5165\u8cc7\u8a0a\u4e0d\u5b8c\u6574\uff0c\u8acb\u91cd\u8a66\u300d<\/td><\/tr><tr><td><code>unauthorized<\/code><\/td><td>\u63d0\u793a\u300c\u6388\u6b0a\u78bc\u5df2\u904e\u671f\uff0c\u8acb\u91cd\u65b0\u767b\u5165\u300d<\/td><\/tr><tr><td><code>error_login<\/code><\/td><td>\u63d0\u793a\u300cSSO \u670d\u52d9\u66ab\u6642\u7121\u6cd5\u9023\u7dda\u300d<\/td><\/tr><tr><td>\u5176\u4ed6<\/td><td>\u986f\u793a <code>message<\/code> \u6216\u901a\u7528\u300c\u767b\u5165\u5931\u6557\u300d<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>JWT \u904e\u671f<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u9801\u9762\u8f09\u5165\u6642\uff1a<code>getStoredAuth()<\/code> \u6aa2\u67e5 <code>exp<\/code>\uff0c\u904e\u671f\u5247\u8996\u70ba\u672a\u767b\u5165<\/li>\n\n\n\n<li>API \u8acb\u6c42\u6642\uff1a<code>apiFetch<\/code> \u6536\u5230 <code>401<\/code> \u81ea\u52d5\u6e05\u9664 Token<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u5efa\u8b70\u5f8c\u7e8c\u5728 <code>401<\/code> \u6642\u81ea\u52d5\u89f8\u767c <code>startSsoLogin()<\/code> \u5f15\u5c0e\u91cd\u65b0\u767b\u5165\u3002<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\" id=\"%e5%ae%89%e5%85%a8%e6%80%a7%e8%80%83%e9%87%8f\">\u5b89\u5168\u6027\u8003\u91cf<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Client Secret \u4e0d\u5f97\u51fa\u73fe\u5728\u524d\u7aef\u7a0b\u5f0f\u78bc\u6216\u74b0\u5883\u8b8a\u6578\u4e2d\u3002<\/strong><\/li>\n\n\n\n<li><strong>\u6388\u6b0a\u78bc\u4e00\u6b21\u6027<\/strong>\uff1a\u53d6\u5f97 <code>code<\/code> \u5f8c\u61c9\u7acb\u5373\u4ea4\u63db\uff0c\u4e26\u4ee5 <code>router.replace<\/code> \u6e05\u9664 URL \u53c3\u6578\u3002<\/li>\n\n\n\n<li><strong>HTTPS<\/strong>\uff1a\u751f\u7522\u74b0\u5883 SSO \u8207 API \u901a\u8a0a\u5fc5\u9808\u4f7f\u7528 HTTPS\u3002<\/li>\n\n\n\n<li><strong>Token \u5132\u5b58<\/strong>\uff1a\u76ee\u524d\u4f7f\u7528 <code>localStorage<\/code> \u5132\u5b58 JWT\u3002\u82e5\u9700\u66f4\u9ad8\u5b89\u5168\u6027\uff0c\u53ef\u6539\u70ba <code>httpOnly<\/code> Cookie\uff08\u9700\u5f8c\u7aef\u914d\u5408 Set-Cookie\uff09\u3002<\/li>\n\n\n\n<li><strong>\u4e0d\u8981\u5728\u65e5\u8a8c\u4e2d\u8f38\u51fa JWT \u6216\u6388\u6b0a\u78bc\u3002<\/strong><\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\" id=\"%e6%9c%ac%e5%9c%b0%e6%b8%ac%e8%a9%a6\">\u672c\u5730\u6e2c\u8a66<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u555f\u52d5\u6b65\u9a5f<\/strong><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">npm<\/span> <span class=\"hljs-selector-tag\">install<\/span>\n# \u5efa\u7acb <span class=\"hljs-selector-class\">.env<\/span><span class=\"hljs-selector-class\">.local<\/span> \u4e26\u8a2d\u5b9a <span class=\"hljs-selector-tag\">NEXT_PUBLIC_API_URL<\/span>\n<span class=\"hljs-selector-tag\">npm<\/span> <span class=\"hljs-selector-tag\">run<\/span> <span class=\"hljs-selector-tag\">dev<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><strong>\u6e2c\u8a66\u6d41\u7a0b<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u78ba\u8a8d\u5f8c\u7aef\u5df2\u555f\u52d5\uff0c\u4e14 <code>POST \/api\/auth-tokens\/exchange<\/code> \u53ef\u6b63\u5e38\u904b\u4f5c<\/li>\n\n\n\n<li>\u958b\u555f\u524d\u7aef\uff08\u9810\u8a2d <code>http:\/\/localhost:3000<\/code>\uff09<\/li>\n\n\n\n<li>\u9ede\u64ca\u53f3\u4e0a\u89d2\u300c\u767b\u5165\u300d<\/li>\n\n\n\n<li>\u5b8c\u6210 Google SSO \u767b\u5165<\/li>\n\n\n\n<li>\u78ba\u8a8d\u8df3\u8f49\u56de\u524d\u7aef\u5f8c\u53f3\u4e0a\u89d2\u986f\u793a\u982d\u50cf<\/li>\n\n\n\n<li>\u9ede\u64ca\u982d\u50cf\u9032\u5165 <code>\/profile<\/code>\uff0c\u78ba\u8a8d\u540d\u7a31\u8207\u4fe1\u7bb1\u6b63\u78ba<\/li>\n\n\n\n<li>\u958b\u555f DevTools \u2192 Application \u2192 Local Storage\uff0c\u78ba\u8a8d\u5b58\u5728 <code>authToken<\/code><\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"%e5%b8%b8%e8%a6%8b%e5%95%8f%e9%a1%8c%e8%88%87%e8%a7%a3%e6%b3%95\">\u5e38\u898b\u554f\u984c\u8207\u89e3\u6cd5<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u554f\u984c<\/th><th>\u53ef\u80fd\u539f\u56e0<\/th><th>\u89e3\u6cd5<\/th><\/tr><\/thead><tbody><tr><td>\u767b\u5165\u5f8c\u6c92\u6709 JWT<\/td><td>CORS \u672a\u66b4\u9732 <code>X-Auth-Token<\/code><\/td><td>\u78ba\u8a8d\u5f8c\u7aef CORS <code>exposedHeaders<\/code><\/td><\/tr><tr><td><code>NEXT_PUBLIC_API_URL \u672a\u8a2d\u5b9a<\/code><\/td><td>\u7f3a\u5c11\u74b0\u5883\u8b8a\u6578<\/td><td>\u5efa\u7acb <code>.env.local<\/code><\/td><\/tr><tr><td>\u8df3\u8f49\u5f8c\u51fa\u73fe <code>unauthorized<\/code><\/td><td>code \u5df2\u4f7f\u7528\u6216\u904e\u671f<\/td><td>\u91cd\u65b0\u9ede\u64ca\u767b\u5165<\/td><\/tr><tr><td>\u982d\u50cf\u53ea\u986f\u793a\u9996\u5b57<\/td><td>JWT \u7121 <code>picture<\/code> claim<\/td><td>\u78ba\u8a8d SSO \u56de\u50b3\u8207\u5f8c\u7aef JWT \u7c3d\u767c\u908f\u8f2f<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\" id=\"%e5%8e%9f%e5%a7%8b%e7%a2%bc%e7%b4%a2%e5%bc%95\">\u539f\u59cb\u78bc\u7d22\u5f15<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u6a94\u6848<\/th><th>\u8077\u8cac<\/th><\/tr><\/thead><tbody><tr><td><code>lib\/sso.ts<\/code><\/td><td>SSO URL \u5efa\u69cb\u3001code \u4ea4\u63db\u3001JWT \u89e3\u6790\u3001Token \u5b58\u53d6<\/td><\/tr><tr><td><code>lib\/api.ts<\/code><\/td><td>\u9644\u5e36 Bearer Token \u7684 HTTP \u8acb\u6c42<\/td><\/tr><tr><td><code>lib\/hooks\/AuthContext.tsx<\/code><\/td><td>React Context \u8a8d\u8b49\u72c0\u614b\u8207\u56de\u8abf<\/td><\/tr><tr><td><code>components\/layout\/Navbar.tsx<\/code><\/td><td>\u767b\u5165\u6309\u9215\u8207\u4f7f\u7528\u8005\u982d\u50cf UI<\/td><\/tr><tr><td><code>app\/layout.tsx<\/code><\/td><td>Provider \u639b\u8f09\u9ede<\/td><\/tr><tr><td><code>docker-compose.prod.yml<\/code><\/td><td>\u751f\u7522\u74b0\u5883 <code>NEXT_PUBLIC_API_URL<\/code> \u6ce8\u5165<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\" id=\"%e8%88%87%e5%be%8c%e7%ab%af%e6%96%87%e4%bb%b6%e5%b0%8d%e7%85%a7\">\u8207\u5f8c\u7aef\u6587\u4ef6\u5c0d\u7167<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u5f8c\u7aef\u6587\u4ef6\u7ae0\u7bc0<\/th><th>\u524d\u7aef\u5c0d\u61c9\u5be6\u4f5c<\/th><\/tr><\/thead><tbody><tr><td>\u00a73 \u6574\u9ad4\u767b\u5165\u6d41\u7a0b<\/td><td><code>AuthContext<\/code> + <code>exchangeCodeForToken()<\/code><\/td><\/tr><tr><td>\u00a77.1 \u767c\u8d77\u767b\u5165<\/td><td><code>buildSsoLoginUrl()<\/code> + <code>startSsoLogin()<\/code><\/td><\/tr><tr><td>\u00a77.2 \u8655\u7406\u56de\u547c<\/td><td><code>AuthProvider<\/code> \u7684 <code>useSearchParams<\/code> effect<\/td><\/tr><tr><td>\u00a77.3 \u4ea4\u63db JWT<\/td><td><code>exchangeCodeForToken()<\/code><\/td><\/tr><tr><td>\u00a77.4 \u53d7\u4fdd\u8b77 API<\/td><td><code>apiFetch()<\/code> \/ <code>apiJson()<\/code><\/td><\/tr><tr><td>\u00a77.5 CORS<\/td><td>\u8b80\u53d6 <code>X-Auth-Token<\/code> Response Header<\/td><\/tr><tr><td>\u00a710 \u5b89\u5168\u6027<\/td><td>\u524d\u7aef\u4e0d\u547c\u53eb <code>\/sso\/verify-code<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>\u672c\u6587\u4ef6\u900f\u904e\u5546\u696d\u667a\u6167\u7814\u7a76\u4e2d\u5fc3 \u2013 \u667a\u6167\u6821\u5712:\u6d3b\u52d5\u7cfb\u7d71\u4f86\u8aaa\u660e\u5546\u667a\u4e2d\u5fc3\u7684SSO\u67b6\u69cb\u3002 \u5546\u667a\u4e2d\u5fc3\u7684SSO\u70ba\u81ea\u67b6\u4f3a\u670d\u5668\uff0c\u975eGoogle SSO\uff0c\u4f46\u539f\u7406\u5927\u540c\u5c0f\u7570\u3002 \u8a72\u6587\u7ae0\u6240\u8ff0\u7a0b\u5f0f\u78bc\u7686\u63d0\u4f9b\u65bc\u6587\u7ae0\u6700\u4e0b\u65b9GitHub\u4e2d\u3002 \u7248\u672c\uff1a0.1.0\u6700\u5f8c\u66f4\u65b0\u6642\u9593\uff1a06\/10\/2026\u6700\u5f8c\u66f4\u65b0\u4eba\u54e1\uff1a\u9673\u6cd3\u6bd3 \u8a2d\u8a08\u539f\u5247 \u539f\u5247 \u8aaa\u660e \u524d\u7aef\u4e0d\u6301\u6709 Client Secret \u524d\u7aef\u7d55\u4e0d\u76f4\u63a5\u547c\u53eb SSO \/sso\/verify-code \u6388\u6b0a\u78bc\u50c5\u7528\u65bc\u4ea4\u63db SSO \u56de\u50b3\u7684 code \u70ba\u4e00\u6b21\u6027\u6388\u6b0a\u78bc\uff0c\u61c9\u7acb\u5373\u8f49\u4ea4\u5f8c\u7aef\u4ea4\u63db JWT JWT \u4f5c\u70ba\u5f8c\u7e8c\u8a8d\u8b49 \u767b\u5165\u5b8c\u6210\u5f8c\uff0c\u6240\u6709\u53d7\u4fdd\u8b77 API \u4ee5 Authorization: Bearer {JWT} \u5b58\u53d6 \u5168\u7ad9\u56de\u8abf\u8655\u7406 SSO \u53ef\u56de\u8df3\u81f3\u4efb\u610f\u9801\u9762\uff0cAuthProvider \u7d71\u4e00\u6514\u622a ?code= \u53c3\u6578 \u7cfb\u7d71\u89d2\u8272 \u89d2\u8272 \u8077\u8cac SSO \u767b\u5165\u4e2d\u5fc3 Google \u7b49 IdP \u767b\u5165\u3001\u6838\u767c\u4e00\u6b21\u6027\u6388\u6b0a\u78bc \u524d\u7aef \u5c0e\u5411 SSO\u3001\u63a5\u6536 code\u3001\u5411\u5f8c\u7aef\u4ea4\u63db JWT\u3001\u5132\u5b58\u4e26\u9644\u5e36 JWT \u547c\u53eb API \u5f8c\u7aef [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"parent":579,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"footnotes":""},"class_list":["post-569","page","type-page","status-publish","hentry"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 4.9.10 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u672c\u6587\u4ef6\u900f\u904e\u5546\u696d\u667a\u6167\u7814\u7a76\u4e2d\u5fc3 \u2013 \u667a\u6167\u6821\u5712:\u6d3b\u52d5\u7cfb\u7d71\u4f86\u8aaa\u660e\u5546\u667a\u4e2d\u5fc3\u7684SSO\u67b6\u69cb\u3002 \u5546\u667a\u4e2d\u5fc3\u7684SSO\u70ba\u81ea\u67b6\u4f3a\u670d\u5668\uff0c\u975eGoogle SSO\uff0c\u4f46\u539f\u7406\u5927\u540c\u5c0f\u7570\u3002 \u8a72\u6587\u7ae0\u6240\u8ff0\u7a0b\u5f0f\u78bc\u7686\u63d0\u4f9b\u65bc\u6587\u7ae0\u6700\u4e0b\u65b9GitHub\u4e2d\u3002 \u7248\u672c\uff1a0.1.0\u6700\u5f8c\u66f4\u65b0\u6642\u9593\uff1a06\/10\/2026\u6700\u5f8c\u66f4\u65b0\u4eba\u54e1\uff1a\u9673\u6cd3\u6bd3 \u8a2d\u8a08\u539f\u5247 \u7cfb\u7d71\u89d2\u8272 flowchart LR User[\u4f7f\u7528\u8005] FE[Campus Activity \u524d\u7aef] SSO[SSO \u767b\u5165\u4e2d\u5fc3] BE[Campus Activity \u5f8c\u7aef] User --&gt;|\u9ede\u64ca\u767b\u5165| FE FE --&gt;|GET \/sso\/prelogin| SSO SSO --&gt;|302 ?code=| FE FE --&gt;|POST \/auth-tokens\/exchange| BE BE --&gt;|X-Auth-Token JWT| FE FE --&gt;|Bearer JWT| BE \u6574\u9ad4\u6d41\u7a0b sequenceDiagram participant User as \u4f7f\u7528\u8005 participant Navbar as Navbar \/ AuthContext\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"google-site-verification\" content=\"-324YR5tomynX5FeTW2lTMHIX0KmPBvpxjXpOhXwi1Q\" \/>\n\t<link rel=\"canonical\" href=\"https:\/\/hyc.eshachem.com\/program\/birc\/sso-%e5%96%ae%e4%b8%80%e7%99%bb%e5%85%a5%e6%8a%80%e8%a1%93%e6%96%87%e4%bb%b6-next-js\/\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 4.9.10\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"HyC Wiki - Technical articles\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Next.js \u2013 SSO in BIRC - HyC Wiki\" \/>\n\t\t<meta property=\"og:description\" content=\"\u672c\u6587\u4ef6\u900f\u904e\u5546\u696d\u667a\u6167\u7814\u7a76\u4e2d\u5fc3 \u2013 \u667a\u6167\u6821\u5712:\u6d3b\u52d5\u7cfb\u7d71\u4f86\u8aaa\u660e\u5546\u667a\u4e2d\u5fc3\u7684SSO\u67b6\u69cb\u3002 \u5546\u667a\u4e2d\u5fc3\u7684SSO\u70ba\u81ea\u67b6\u4f3a\u670d\u5668\uff0c\u975eGoogle SSO\uff0c\u4f46\u539f\u7406\u5927\u540c\u5c0f\u7570\u3002 \u8a72\u6587\u7ae0\u6240\u8ff0\u7a0b\u5f0f\u78bc\u7686\u63d0\u4f9b\u65bc\u6587\u7ae0\u6700\u4e0b\u65b9GitHub\u4e2d\u3002 \u7248\u672c\uff1a0.1.0\u6700\u5f8c\u66f4\u65b0\u6642\u9593\uff1a06\/10\/2026\u6700\u5f8c\u66f4\u65b0\u4eba\u54e1\uff1a\u9673\u6cd3\u6bd3 \u8a2d\u8a08\u539f\u5247 \u7cfb\u7d71\u89d2\u8272 flowchart LR User[\u4f7f\u7528\u8005] FE[Campus Activity \u524d\u7aef] SSO[SSO \u767b\u5165\u4e2d\u5fc3] BE[Campus Activity \u5f8c\u7aef] User --&gt;|\u9ede\u64ca\u767b\u5165| FE FE --&gt;|GET \/sso\/prelogin| SSO SSO --&gt;|302 ?code=| FE FE --&gt;|POST \/auth-tokens\/exchange| BE BE --&gt;|X-Auth-Token JWT| FE FE --&gt;|Bearer JWT| BE \u6574\u9ad4\u6d41\u7a0b sequenceDiagram participant User as \u4f7f\u7528\u8005 participant Navbar as Navbar \/ AuthContext\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/hyc.eshachem.com\/program\/birc\/sso-%e5%96%ae%e4%b8%80%e7%99%bb%e5%85%a5%e6%8a%80%e8%a1%93%e6%96%87%e4%bb%b6-next-js\/\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/hyc.eshachem.com\/program\/wp-content\/uploads\/2026\/06\/cropped-cropped-\u672a\u547d\u540d\u8a2d\u8a08-4.png\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/hyc.eshachem.com\/program\/wp-content\/uploads\/2026\/06\/cropped-cropped-\u672a\u547d\u540d\u8a2d\u8a08-4.png\" \/>\n\t\t<meta property=\"og:image:width\" content=\"64\" \/>\n\t\t<meta property=\"og:image:height\" content=\"64\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2026-06-15T03:06:17+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2026-06-15T10:21:12+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Next.js \u2013 SSO in BIRC - HyC Wiki\" \/>\n\t\t<meta name=\"twitter:description\" content=\"\u672c\u6587\u4ef6\u900f\u904e\u5546\u696d\u667a\u6167\u7814\u7a76\u4e2d\u5fc3 \u2013 \u667a\u6167\u6821\u5712:\u6d3b\u52d5\u7cfb\u7d71\u4f86\u8aaa\u660e\u5546\u667a\u4e2d\u5fc3\u7684SSO\u67b6\u69cb\u3002 \u5546\u667a\u4e2d\u5fc3\u7684SSO\u70ba\u81ea\u67b6\u4f3a\u670d\u5668\uff0c\u975eGoogle SSO\uff0c\u4f46\u539f\u7406\u5927\u540c\u5c0f\u7570\u3002 \u8a72\u6587\u7ae0\u6240\u8ff0\u7a0b\u5f0f\u78bc\u7686\u63d0\u4f9b\u65bc\u6587\u7ae0\u6700\u4e0b\u65b9GitHub\u4e2d\u3002 \u7248\u672c\uff1a0.1.0\u6700\u5f8c\u66f4\u65b0\u6642\u9593\uff1a06\/10\/2026\u6700\u5f8c\u66f4\u65b0\u4eba\u54e1\uff1a\u9673\u6cd3\u6bd3 \u8a2d\u8a08\u539f\u5247 \u7cfb\u7d71\u89d2\u8272 flowchart LR User[\u4f7f\u7528\u8005] FE[Campus Activity \u524d\u7aef] SSO[SSO \u767b\u5165\u4e2d\u5fc3] BE[Campus Activity \u5f8c\u7aef] User --&gt;|\u9ede\u64ca\u767b\u5165| FE FE --&gt;|GET \/sso\/prelogin| SSO SSO --&gt;|302 ?code=| FE FE --&gt;|POST \/auth-tokens\/exchange| BE BE --&gt;|X-Auth-Token JWT| FE FE --&gt;|Bearer JWT| BE \u6574\u9ad4\u6d41\u7a0b sequenceDiagram participant User as \u4f7f\u7528\u8005 participant Navbar as Navbar \/ AuthContext\" \/>\n\t\t<meta name=\"twitter:image\" content=\"https:\/\/hyc.eshachem.com\/program\/wp-content\/uploads\/2026\/06\/cropped-cropped-\u672a\u547d\u540d\u8a2d\u8a08-4.png\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/hyc.eshachem.com\\\/program\\\/birc\\\/sso-%e5%96%ae%e4%b8%80%e7%99%bb%e5%85%a5%e6%8a%80%e8%a1%93%e6%96%87%e4%bb%b6-next-js\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/hyc.eshachem.com\\\/program#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/hyc.eshachem.com\\\/program\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/hyc.eshachem.com\\\/program\\\/birc\\\/#listItem\",\"name\":\"\\u3010BIRC\\u3011\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/hyc.eshachem.com\\\/program\\\/birc\\\/#listItem\",\"position\":2,\"name\":\"\\u3010BIRC\\u3011\",\"item\":\"https:\\\/\\\/hyc.eshachem.com\\\/program\\\/birc\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/hyc.eshachem.com\\\/program\\\/birc\\\/sso-%e5%96%ae%e4%b8%80%e7%99%bb%e5%85%a5%e6%8a%80%e8%a1%93%e6%96%87%e4%bb%b6-next-js\\\/#listItem\",\"name\":\"Next.js &#8211; SSO in BIRC\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/hyc.eshachem.com\\\/program#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/hyc.eshachem.com\\\/program\\\/birc\\\/sso-%e5%96%ae%e4%b8%80%e7%99%bb%e5%85%a5%e6%8a%80%e8%a1%93%e6%96%87%e4%bb%b6-next-js\\\/#listItem\",\"position\":3,\"name\":\"Next.js &#8211; SSO in BIRC\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/hyc.eshachem.com\\\/program\\\/birc\\\/#listItem\",\"name\":\"\\u3010BIRC\\u3011\"}}]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/hyc.eshachem.com\\\/program\\\/#person\",\"name\":\"H.Y. Chen\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/hyc.eshachem.com\\\/program\\\/birc\\\/sso-%e5%96%ae%e4%b8%80%e7%99%bb%e5%85%a5%e6%8a%80%e8%a1%93%e6%96%87%e4%bb%b6-next-js\\\/#personImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/fc6e05e080c9ea570267ee9ed68c8b1841d745cee07c0331c5440646e0646603?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"H.Y. Chen\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/hyc.eshachem.com\\\/program\\\/birc\\\/sso-%e5%96%ae%e4%b8%80%e7%99%bb%e5%85%a5%e6%8a%80%e8%a1%93%e6%96%87%e4%bb%b6-next-js\\\/#webpage\",\"url\":\"https:\\\/\\\/hyc.eshachem.com\\\/program\\\/birc\\\/sso-%e5%96%ae%e4%b8%80%e7%99%bb%e5%85%a5%e6%8a%80%e8%a1%93%e6%96%87%e4%bb%b6-next-js\\\/\",\"name\":\"Next.js \\u2013 SSO in BIRC - HyC Wiki\",\"description\":\"\\u672c\\u6587\\u4ef6\\u900f\\u904e\\u5546\\u696d\\u667a\\u6167\\u7814\\u7a76\\u4e2d\\u5fc3 \\u2013 \\u667a\\u6167\\u6821\\u5712:\\u6d3b\\u52d5\\u7cfb\\u7d71\\u4f86\\u8aaa\\u660e\\u5546\\u667a\\u4e2d\\u5fc3\\u7684SSO\\u67b6\\u69cb\\u3002 \\u5546\\u667a\\u4e2d\\u5fc3\\u7684SSO\\u70ba\\u81ea\\u67b6\\u4f3a\\u670d\\u5668\\uff0c\\u975eGoogle SSO\\uff0c\\u4f46\\u539f\\u7406\\u5927\\u540c\\u5c0f\\u7570\\u3002 \\u8a72\\u6587\\u7ae0\\u6240\\u8ff0\\u7a0b\\u5f0f\\u78bc\\u7686\\u63d0\\u4f9b\\u65bc\\u6587\\u7ae0\\u6700\\u4e0b\\u65b9GitHub\\u4e2d\\u3002 \\u7248\\u672c\\uff1a0.1.0\\u6700\\u5f8c\\u66f4\\u65b0\\u6642\\u9593\\uff1a06\\\/10\\\/2026\\u6700\\u5f8c\\u66f4\\u65b0\\u4eba\\u54e1\\uff1a\\u9673\\u6cd3\\u6bd3 \\u8a2d\\u8a08\\u539f\\u5247 \\u7cfb\\u7d71\\u89d2\\u8272 flowchart LR User[\\u4f7f\\u7528\\u8005] FE[Campus Activity \\u524d\\u7aef] SSO[SSO \\u767b\\u5165\\u4e2d\\u5fc3] BE[Campus Activity \\u5f8c\\u7aef] User -->|\\u9ede\\u64ca\\u767b\\u5165| FE FE -->|GET \\\/sso\\\/prelogin| SSO SSO -->|302 ?code=| FE FE -->|POST \\\/auth-tokens\\\/exchange| BE BE -->|X-Auth-Token JWT| FE FE -->|Bearer JWT| BE \\u6574\\u9ad4\\u6d41\\u7a0b sequenceDiagram participant User as \\u4f7f\\u7528\\u8005 participant Navbar as Navbar \\\/ AuthContext\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/hyc.eshachem.com\\\/program\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/hyc.eshachem.com\\\/program\\\/birc\\\/sso-%e5%96%ae%e4%b8%80%e7%99%bb%e5%85%a5%e6%8a%80%e8%a1%93%e6%96%87%e4%bb%b6-next-js\\\/#breadcrumblist\"},\"datePublished\":\"2026-06-15T03:06:17+00:00\",\"dateModified\":\"2026-06-15T10:21:12+00:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/hyc.eshachem.com\\\/program\\\/#website\",\"url\":\"https:\\\/\\\/hyc.eshachem.com\\\/program\\\/\",\"name\":\"HyC Wiki\",\"description\":\"Technical articles\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/hyc.eshachem.com\\\/program\\\/#person\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Next.js \u2013 SSO in BIRC - HyC Wiki","description":"\u672c\u6587\u4ef6\u900f\u904e\u5546\u696d\u667a\u6167\u7814\u7a76\u4e2d\u5fc3 \u2013 \u667a\u6167\u6821\u5712:\u6d3b\u52d5\u7cfb\u7d71\u4f86\u8aaa\u660e\u5546\u667a\u4e2d\u5fc3\u7684SSO\u67b6\u69cb\u3002 \u5546\u667a\u4e2d\u5fc3\u7684SSO\u70ba\u81ea\u67b6\u4f3a\u670d\u5668\uff0c\u975eGoogle SSO\uff0c\u4f46\u539f\u7406\u5927\u540c\u5c0f\u7570\u3002 \u8a72\u6587\u7ae0\u6240\u8ff0\u7a0b\u5f0f\u78bc\u7686\u63d0\u4f9b\u65bc\u6587\u7ae0\u6700\u4e0b\u65b9GitHub\u4e2d\u3002 \u7248\u672c\uff1a0.1.0\u6700\u5f8c\u66f4\u65b0\u6642\u9593\uff1a06\/10\/2026\u6700\u5f8c\u66f4\u65b0\u4eba\u54e1\uff1a\u9673\u6cd3\u6bd3 \u8a2d\u8a08\u539f\u5247 \u7cfb\u7d71\u89d2\u8272 flowchart LR User[\u4f7f\u7528\u8005] FE[Campus Activity \u524d\u7aef] SSO[SSO \u767b\u5165\u4e2d\u5fc3] BE[Campus Activity \u5f8c\u7aef] User -->|\u9ede\u64ca\u767b\u5165| FE FE -->|GET \/sso\/prelogin| SSO SSO -->|302 ?code=| FE FE -->|POST \/auth-tokens\/exchange| BE BE -->|X-Auth-Token JWT| FE FE -->|Bearer JWT| BE \u6574\u9ad4\u6d41\u7a0b sequenceDiagram participant User as \u4f7f\u7528\u8005 participant Navbar as Navbar \/ AuthContext","canonical_url":"https:\/\/hyc.eshachem.com\/program\/birc\/sso-%e5%96%ae%e4%b8%80%e7%99%bb%e5%85%a5%e6%8a%80%e8%a1%93%e6%96%87%e4%bb%b6-next-js\/","robots":"max-image-preview:large","keywords":"","webmasterTools":{"google-site-verification":"-324YR5tomynX5FeTW2lTMHIX0KmPBvpxjXpOhXwi1Q","miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"BreadcrumbList","@id":"https:\/\/hyc.eshachem.com\/program\/birc\/sso-%e5%96%ae%e4%b8%80%e7%99%bb%e5%85%a5%e6%8a%80%e8%a1%93%e6%96%87%e4%bb%b6-next-js\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/hyc.eshachem.com\/program#listItem","position":1,"name":"Home","item":"https:\/\/hyc.eshachem.com\/program","nextItem":{"@type":"ListItem","@id":"https:\/\/hyc.eshachem.com\/program\/birc\/#listItem","name":"\u3010BIRC\u3011"}},{"@type":"ListItem","@id":"https:\/\/hyc.eshachem.com\/program\/birc\/#listItem","position":2,"name":"\u3010BIRC\u3011","item":"https:\/\/hyc.eshachem.com\/program\/birc\/","nextItem":{"@type":"ListItem","@id":"https:\/\/hyc.eshachem.com\/program\/birc\/sso-%e5%96%ae%e4%b8%80%e7%99%bb%e5%85%a5%e6%8a%80%e8%a1%93%e6%96%87%e4%bb%b6-next-js\/#listItem","name":"Next.js &#8211; SSO in BIRC"},"previousItem":{"@type":"ListItem","@id":"https:\/\/hyc.eshachem.com\/program#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/hyc.eshachem.com\/program\/birc\/sso-%e5%96%ae%e4%b8%80%e7%99%bb%e5%85%a5%e6%8a%80%e8%a1%93%e6%96%87%e4%bb%b6-next-js\/#listItem","position":3,"name":"Next.js &#8211; SSO in BIRC","previousItem":{"@type":"ListItem","@id":"https:\/\/hyc.eshachem.com\/program\/birc\/#listItem","name":"\u3010BIRC\u3011"}}]},{"@type":"Person","@id":"https:\/\/hyc.eshachem.com\/program\/#person","name":"H.Y. Chen","image":{"@type":"ImageObject","@id":"https:\/\/hyc.eshachem.com\/program\/birc\/sso-%e5%96%ae%e4%b8%80%e7%99%bb%e5%85%a5%e6%8a%80%e8%a1%93%e6%96%87%e4%bb%b6-next-js\/#personImage","url":"https:\/\/secure.gravatar.com\/avatar\/fc6e05e080c9ea570267ee9ed68c8b1841d745cee07c0331c5440646e0646603?s=96&d=mm&r=g","width":96,"height":96,"caption":"H.Y. Chen"}},{"@type":"WebPage","@id":"https:\/\/hyc.eshachem.com\/program\/birc\/sso-%e5%96%ae%e4%b8%80%e7%99%bb%e5%85%a5%e6%8a%80%e8%a1%93%e6%96%87%e4%bb%b6-next-js\/#webpage","url":"https:\/\/hyc.eshachem.com\/program\/birc\/sso-%e5%96%ae%e4%b8%80%e7%99%bb%e5%85%a5%e6%8a%80%e8%a1%93%e6%96%87%e4%bb%b6-next-js\/","name":"Next.js \u2013 SSO in BIRC - HyC Wiki","description":"\u672c\u6587\u4ef6\u900f\u904e\u5546\u696d\u667a\u6167\u7814\u7a76\u4e2d\u5fc3 \u2013 \u667a\u6167\u6821\u5712:\u6d3b\u52d5\u7cfb\u7d71\u4f86\u8aaa\u660e\u5546\u667a\u4e2d\u5fc3\u7684SSO\u67b6\u69cb\u3002 \u5546\u667a\u4e2d\u5fc3\u7684SSO\u70ba\u81ea\u67b6\u4f3a\u670d\u5668\uff0c\u975eGoogle SSO\uff0c\u4f46\u539f\u7406\u5927\u540c\u5c0f\u7570\u3002 \u8a72\u6587\u7ae0\u6240\u8ff0\u7a0b\u5f0f\u78bc\u7686\u63d0\u4f9b\u65bc\u6587\u7ae0\u6700\u4e0b\u65b9GitHub\u4e2d\u3002 \u7248\u672c\uff1a0.1.0\u6700\u5f8c\u66f4\u65b0\u6642\u9593\uff1a06\/10\/2026\u6700\u5f8c\u66f4\u65b0\u4eba\u54e1\uff1a\u9673\u6cd3\u6bd3 \u8a2d\u8a08\u539f\u5247 \u7cfb\u7d71\u89d2\u8272 flowchart LR User[\u4f7f\u7528\u8005] FE[Campus Activity \u524d\u7aef] SSO[SSO \u767b\u5165\u4e2d\u5fc3] BE[Campus Activity \u5f8c\u7aef] User -->|\u9ede\u64ca\u767b\u5165| FE FE -->|GET \/sso\/prelogin| SSO SSO -->|302 ?code=| FE FE -->|POST \/auth-tokens\/exchange| BE BE -->|X-Auth-Token JWT| FE FE -->|Bearer JWT| BE \u6574\u9ad4\u6d41\u7a0b sequenceDiagram participant User as \u4f7f\u7528\u8005 participant Navbar as Navbar \/ AuthContext","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/hyc.eshachem.com\/program\/#website"},"breadcrumb":{"@id":"https:\/\/hyc.eshachem.com\/program\/birc\/sso-%e5%96%ae%e4%b8%80%e7%99%bb%e5%85%a5%e6%8a%80%e8%a1%93%e6%96%87%e4%bb%b6-next-js\/#breadcrumblist"},"datePublished":"2026-06-15T03:06:17+00:00","dateModified":"2026-06-15T10:21:12+00:00"},{"@type":"WebSite","@id":"https:\/\/hyc.eshachem.com\/program\/#website","url":"https:\/\/hyc.eshachem.com\/program\/","name":"HyC Wiki","description":"Technical articles","inLanguage":"en-US","publisher":{"@id":"https:\/\/hyc.eshachem.com\/program\/#person"}}]},"og:locale":"en_US","og:site_name":"HyC Wiki - Technical articles","og:type":"article","og:title":"Next.js \u2013 SSO in BIRC - HyC Wiki","og:description":"\u672c\u6587\u4ef6\u900f\u904e\u5546\u696d\u667a\u6167\u7814\u7a76\u4e2d\u5fc3 \u2013 \u667a\u6167\u6821\u5712:\u6d3b\u52d5\u7cfb\u7d71\u4f86\u8aaa\u660e\u5546\u667a\u4e2d\u5fc3\u7684SSO\u67b6\u69cb\u3002 \u5546\u667a\u4e2d\u5fc3\u7684SSO\u70ba\u81ea\u67b6\u4f3a\u670d\u5668\uff0c\u975eGoogle SSO\uff0c\u4f46\u539f\u7406\u5927\u540c\u5c0f\u7570\u3002 \u8a72\u6587\u7ae0\u6240\u8ff0\u7a0b\u5f0f\u78bc\u7686\u63d0\u4f9b\u65bc\u6587\u7ae0\u6700\u4e0b\u65b9GitHub\u4e2d\u3002 \u7248\u672c\uff1a0.1.0\u6700\u5f8c\u66f4\u65b0\u6642\u9593\uff1a06\/10\/2026\u6700\u5f8c\u66f4\u65b0\u4eba\u54e1\uff1a\u9673\u6cd3\u6bd3 \u8a2d\u8a08\u539f\u5247 \u7cfb\u7d71\u89d2\u8272 flowchart LR User[\u4f7f\u7528\u8005] FE[Campus Activity \u524d\u7aef] SSO[SSO \u767b\u5165\u4e2d\u5fc3] BE[Campus Activity \u5f8c\u7aef] User --&gt;|\u9ede\u64ca\u767b\u5165| FE FE --&gt;|GET \/sso\/prelogin| SSO SSO --&gt;|302 ?code=| FE FE --&gt;|POST \/auth-tokens\/exchange| BE BE --&gt;|X-Auth-Token JWT| FE FE --&gt;|Bearer JWT| BE \u6574\u9ad4\u6d41\u7a0b sequenceDiagram participant User as \u4f7f\u7528\u8005 participant Navbar as Navbar \/ AuthContext","og:url":"https:\/\/hyc.eshachem.com\/program\/birc\/sso-%e5%96%ae%e4%b8%80%e7%99%bb%e5%85%a5%e6%8a%80%e8%a1%93%e6%96%87%e4%bb%b6-next-js\/","og:image":"https:\/\/hyc.eshachem.com\/program\/wp-content\/uploads\/2026\/06\/cropped-cropped-\u672a\u547d\u540d\u8a2d\u8a08-4.png","og:image:secure_url":"https:\/\/hyc.eshachem.com\/program\/wp-content\/uploads\/2026\/06\/cropped-cropped-\u672a\u547d\u540d\u8a2d\u8a08-4.png","og:image:width":64,"og:image:height":64,"article:published_time":"2026-06-15T03:06:17+00:00","article:modified_time":"2026-06-15T10:21:12+00:00","twitter:card":"summary_large_image","twitter:title":"Next.js \u2013 SSO in BIRC - HyC Wiki","twitter:description":"\u672c\u6587\u4ef6\u900f\u904e\u5546\u696d\u667a\u6167\u7814\u7a76\u4e2d\u5fc3 \u2013 \u667a\u6167\u6821\u5712:\u6d3b\u52d5\u7cfb\u7d71\u4f86\u8aaa\u660e\u5546\u667a\u4e2d\u5fc3\u7684SSO\u67b6\u69cb\u3002 \u5546\u667a\u4e2d\u5fc3\u7684SSO\u70ba\u81ea\u67b6\u4f3a\u670d\u5668\uff0c\u975eGoogle SSO\uff0c\u4f46\u539f\u7406\u5927\u540c\u5c0f\u7570\u3002 \u8a72\u6587\u7ae0\u6240\u8ff0\u7a0b\u5f0f\u78bc\u7686\u63d0\u4f9b\u65bc\u6587\u7ae0\u6700\u4e0b\u65b9GitHub\u4e2d\u3002 \u7248\u672c\uff1a0.1.0\u6700\u5f8c\u66f4\u65b0\u6642\u9593\uff1a06\/10\/2026\u6700\u5f8c\u66f4\u65b0\u4eba\u54e1\uff1a\u9673\u6cd3\u6bd3 \u8a2d\u8a08\u539f\u5247 \u7cfb\u7d71\u89d2\u8272 flowchart LR User[\u4f7f\u7528\u8005] FE[Campus Activity \u524d\u7aef] SSO[SSO \u767b\u5165\u4e2d\u5fc3] BE[Campus Activity \u5f8c\u7aef] User --&gt;|\u9ede\u64ca\u767b\u5165| FE FE --&gt;|GET \/sso\/prelogin| SSO SSO --&gt;|302 ?code=| FE FE --&gt;|POST \/auth-tokens\/exchange| BE BE --&gt;|X-Auth-Token JWT| FE FE --&gt;|Bearer JWT| BE \u6574\u9ad4\u6d41\u7a0b sequenceDiagram participant User as \u4f7f\u7528\u8005 participant Navbar as Navbar \/ AuthContext","twitter:image":"https:\/\/hyc.eshachem.com\/program\/wp-content\/uploads\/2026\/06\/cropped-cropped-\u672a\u547d\u540d\u8a2d\u8a08-4.png"},"aioseo_meta_data":{"post_id":"569","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_custom_url":null,"og_image_custom_fields":null,"og_image_url":null,"og_image_width":null,"og_image_height":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_image_url":null,"twitter_title":null,"twitter_description":null,"schema_type":"default","schema_type_options":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"limit_modified_date":false,"ai":null,"breadcrumb_settings":null,"seo_analyzer_scan_date":null,"created":"2026-07-28 14:25:28","updated":"2026-07-28 14:25:28"},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/hyc.eshachem.com\/program\" title=\"Home\">Home<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/hyc.eshachem.com\/program\/birc\/\" title=\"\u3010BIRC\u3011\">\u3010BIRC\u3011<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\tNext.js \u2013 SSO in BIRC\n\t\t<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"Home","link":"https:\/\/hyc.eshachem.com\/program"},{"label":"\u3010BIRC\u3011","link":"https:\/\/hyc.eshachem.com\/program\/birc\/"},{"label":"Next.js &#8211; SSO in BIRC","link":"https:\/\/hyc.eshachem.com\/program\/birc\/sso-%e5%96%ae%e4%b8%80%e7%99%bb%e5%85%a5%e6%8a%80%e8%a1%93%e6%96%87%e4%bb%b6-next-js\/"}],"_links":{"self":[{"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/pages\/569","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/comments?post=569"}],"version-history":[{"count":4,"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/pages\/569\/revisions"}],"predecessor-version":[{"id":608,"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/pages\/569\/revisions\/608"}],"up":[{"embeddable":true,"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/pages\/579"}],"wp:attachment":[{"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/media?parent=569"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}