-
-
Notifications
You must be signed in to change notification settings - Fork 3.2k
feat: Add NodeLoc OAuth login support #2698
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughAdds NodeLoc OAuth2 integration: new backend handlers and user model fields, frontend OAuth/UI/routes, system settings, env constants, docs, and CI image name updates; includes token/user endpoint config and version bump. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Frontend
participant Backend
participant NodeLoc
participant Database
User->>Frontend: Click NodeLoc OAuth button
Frontend->>Frontend: prepare state (consent check)
Frontend->>NodeLoc: Redirect to /oauth/authorize (client_id, state, redirect_uri)
NodeLoc->>User: Prompt consent
User->>NodeLoc: Approve
NodeLoc->>Frontend: Redirect to /oauth/nodeloc?code=&state=
Frontend->>Backend: GET /oauth/nodeloc?code=&state=
Backend->>Backend: validate state
Backend->>NodeLoc: POST /oauth-provider/token (code, client_secret)
NodeLoc-->>Backend: access_token
Backend->>NodeLoc: GET /oauth-provider/userinfo (access_token)
NodeLoc-->>Backend: user info (id, username, trust_level,...)
alt binding (user session exists)
Backend->>Database: update user.NodeLocId
else login/registration
Backend->>Database: find by NodeLocId or create user (ensure username uniqueness, check trust level)
end
Backend->>Backend: setupLogin/session
Backend-->>Frontend: redirect to token/success
Frontend->>Frontend: store token & navigate
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
model/option.go (1)
32-44: Add missing NodeLoc (and LinuxDO) credential defaults toInitOptionMap.NodeLocClientId, NodeLocClientSecret, and NodeLocMinimumTrustLevel keys are not initialized in
InitOptionMap, even though NodeLocOAuthEnabled is seeded at line 42. This causes these keys to be absent from the options API until the first database save. The same issue affects LinuxDO credentials. Follow the existing pattern used for GitHub, Telegram, and WeChat by adding empty string/default value initializations:common.OptionMap["GitHubClientId"] = "" common.OptionMap["GitHubClientSecret"] = "" +common.OptionMap["LinuxDOClientId"] = "" +common.OptionMap["LinuxDOClientSecret"] = "" +common.OptionMap["LinuxDOMinimumTrustLevel"] = strconv.Itoa(common.LinuxDOMinimumTrustLevel) +common.OptionMap["NodeLocClientId"] = "" +common.OptionMap["NodeLocClientSecret"] = "" +common.OptionMap["NodeLocMinimumTrustLevel"] = strconv.Itoa(common.NodeLocMinimumTrustLevel) common.OptionMap["TelegramBotToken"] = ""web/src/components/auth/OAuth2Callback.jsx (1)
86-91: Inconsistent navigation target on missing authorization code.When the authorization code is missing (line 88-90), the user is redirected to
/console/personal, but other error scenarios (lines 53, 78) redirect to/login. This inconsistency could confuse users.🔧 Suggested fix for consistent navigation
// 参数缺失直接返回 if (!code) { showError(t('未获取到授权码')); - navigate('/console/personal'); + navigate('/login'); return; }web/src/components/auth/RegisterForm.jsx (1)
662-667:status.nodeloc_oauthmissing from conditional.The conditional that determines whether to show "other register options" doesn't include
status.nodeloc_oauth. If NodeLoc OAuth is the only enabled OAuth provider, users won't see the button to switch back from email registration.Proposed fix
{(status.github_oauth || status.discord_oauth || status.oidc_enabled || status.wechat_login || status.linuxdo_oauth || + status.nodeloc_oauth || status.telegram_oauth) && (web/src/components/auth/LoginForm.jsx (1)
797-802:status.nodeloc_oauthmissing from conditional.Similar to
RegisterForm.jsx, the "other login options" conditional doesn't includestatus.nodeloc_oauth. This should be added for consistency with line 938.Proposed fix
{(status.github_oauth || status.discord_oauth || status.oidc_enabled || status.wechat_login || status.linuxdo_oauth || + status.nodeloc_oauth || status.telegram_oauth) && (
🤖 Fix all issues with AI agents
In `@controller/nodeloc.go`:
- Around line 178-185: The state validation currently uses an unsafe type
assertion on session.Get("oauth_state"); change it to perform a safe comma-ok
type assertion (e.g., val, ok := session.Get("oauth_state").(string)), verify ok
is true and val is non-empty before comparing to the query `state`, and if the
assertion fails or values mismatch return the same forbidden JSON response;
update the comparison in the handler where `state` and
`session.Get("oauth_state")` are used to reference the safely-asserted variable
(so no direct .(string) assertion remains).
- Around line 58-60: The code uses session := sessions.Default(c); id :=
session.Get("id"); user.Id = id.(int) which can panic if id is nil or not an
int; change this to guard the value by checking for nil and using a safe type
assertion (idInt, ok := id.(int)) and handle the failure (e.g., return an
error/abort the request, set a safe default, or log and respond) before
assigning to user.Id; update any related logic in the handler that assumes a
valid user.Id so it doesn't proceed on missing/invalid session id.
- Around line 233-236: The current username suffix logic using
model.IsUsernameExist and model.GetMaxUserId is racy because GetMaxUserId() and
the subsequent user.Insert are not atomic; replace this with an atomic
conflict-safe approach: enforce a unique DB constraint on username and implement
insert-with-conflict-retry (catch unique-violation on user.Insert and retry with
a new suffix), or derive the suffix from the NodeLoc account ID
(nodelocUser.Username + "_" + nodelocUser.ID) or a UUID/timestamp so uniqueness
is guaranteed; update the code paths that call model.IsUsernameExist,
model.GetMaxUserId, and user.Insert to remove the non-atomic check-and-create
and instead perform the conflict-handling retry or deterministic-suffix logic.
- Around line 142-151: The response from client.Do(req) in the NodeLoc user
lookup is not checked for HTTP success before decoding into NodeLocUser; update
the code in the function performing client.Do(req) to verify res2.StatusCode is
a 2xx success (e.g., == http.StatusOK) before calling
json.NewDecoder(res2.Body).Decode(&nodelocUser); if the status is non-2xx, read
the response body (or at least include res2.StatusCode) and return a descriptive
error that includes the status code and any error payload instead of attempting
to decode into NodeLocUser.
In `@docs/nodeloc-oauth.md`:
- Around line 15-18: Update the markdown snippets in docs/nodeloc-oauth.md to
use fenced code blocks with a language specifier (e.g., ```text) for the URL
examples and replace bare URLs in prose with angle-bracketed links (e.g.,
<https://...>) so they are not treated as plain bare URLs; specifically change
the inline blocks containing
"https://www.nodeloc.com/oauth-provider/applications" and the authorize URL
block (client_id/redirect_uri/response_type...) to ```text fences, and update
occurrences like "https://docs.nodeloc.com/api-reference/introduction" to use
<...> angle-bracket notation; apply the same changes to the other occurrences
noted (lines around 60-67 and 108-110).
In `@web/package.json`:
- Line 13: Remove the unused "antd" dependency from web/package.json (delete the
"antd": "^6.2.0" entry), then update the lockfile by running your package
manager install (npm/yarn/pnpm) so the lockfile is regenerated; also run a quick
project-wide search for any stray "antd" imports/usages to ensure nothing breaks
and run the build/tests to verify no references remain.
In `@web/src/components/common/logo/NodeLocIcon.jsx`:
- Around line 38-41: In the NodeLocIcon component update the SVG stop attributes
to JSX camelCase: replace the two occurrences of stop-color with stopColor
inside the <linearGradient id="paint0_linear_1_6"> block (the <stop> elements)
so the JSX uses stopColor="#009966" and stopColor="#FF9933" respectively.
🧹 Nitpick comments (12)
controller/misc.go (1)
59-73: Formatting issue degrades readability.Multiple key-value pairs are concatenated on single lines (e.g., lines 59 and 61), which appears to be an unintended merge artifact. This makes the code harder to maintain and review.
♻️ Suggested formatting fix
- "linuxdo_minimum_trust_level": common.LinuxDOMinimumTrustLevel, "nodeloc_oauth": common.NodeLocOAuthEnabled, - "nodeloc_client_id": common.NodeLocClientId, - "nodeloc_minimum_trust_level": common.NodeLocMinimumTrustLevel, "telegram_oauth": common.TelegramOAuthEnabled, - "telegram_bot_name": common.TelegramBotName, + "linuxdo_minimum_trust_level": common.LinuxDOMinimumTrustLevel, + "nodeloc_oauth": common.NodeLocOAuthEnabled, + "nodeloc_client_id": common.NodeLocClientId, + "nodeloc_minimum_trust_level": common.NodeLocMinimumTrustLevel, + "telegram_oauth": common.TelegramOAuthEnabled, + "telegram_bot_name": common.TelegramBotName,controller/nodeloc.go (3)
107-112: Consider a longer timeout for external OAuth requests.5-second timeout may be too aggressive for OAuth token exchange, especially under network latency. Consider 10-15 seconds.
- client := http.Client{Timeout: 5 * time.Second} + client := http.Client{Timeout: 10 * time.Second}
81-84: Unused parameterc *gin.Contextin helper function.The
getNodeLocUserInfoByCodefunction receivesc *gin.Contextbut never uses it. Remove the unused parameter.-func getNodeLocUserInfoByCode(code string, c *gin.Context) (*NodeLocUser, error) { +func getNodeLocUserInfoByCode(code string) (*NodeLocUser, error) {And update callers at lines 40 and 202:
- nodelocUser, err := getNodeLocUserInfoByCode(code, c) + nodelocUser, err := getNodeLocUserInfoByCode(code)
242-246: Silent error swallowing for invalid affCode.When
GetUserIdByAffCodefails, the error is discarded with_. Consider logging the error for debugging purposes.affCode := session.Get("aff") inviterId := 0 if affCode != nil { - inviterId, _ = model.GetUserIdByAffCode(affCode.(string)) + var err error + inviterId, err = model.GetUserIdByAffCode(affCode.(string)) + if err != nil { + common.SysLog(fmt.Sprintf("Invalid aff code during NodeLoc registration: %s", affCode)) + } }web/src/components/settings/personal/cards/AccountManagement.jsx (1)
456-493: Consider using the NodeLocIcon component instead of inline SVG.A
NodeLocIconcomponent already exists inweb/src/components/common/logo/NodeLocIcon.jsx. Using it would improve consistency and maintainability.♻️ Proposed refactor
Add import:
+import NodeLocIcon from '../../../common/logo/NodeLocIcon';Replace the inline SVG:
- <div className='w-10 h-10 rounded-full bg-blue-100 dark:bg-blue-900 flex items-center justify-center mr-3 flex-shrink-0'> - <svg - width='20' - height='20' - viewBox='0 0 24 24' - className='text-blue-600 dark:text-blue-300' - > - <circle cx='12' cy='7' r='2.5' fill='currentColor' /> - ... - </svg> - </div> + <div className='w-10 h-10 rounded-full bg-slate-100 dark:bg-slate-700 flex items-center justify-center mr-3 flex-shrink-0'> + <NodeLocIcon size={20} /> + </div>Note: Also consider using
bg-slate-100for visual consistency with other binding cards.web/src/components/common/logo/NodeLocIcon.jsx (1)
23-49: CustomIcon is recreated on every render.Defining
CustomIconinsideNodeLocIconmeans a new function reference is created on each render, which may cause unnecessary re-renders of theIconcomponent.♻️ Proposed refactor - extract CustomIcon
+const CustomIcon = (props) => ( + <svg + className='icon' + viewBox='0 0 102 64' + version='1.1' + xmlns='http://www.w3.org/2000/svg' + width='1em' + height='1em' + {...props} + > + <g id='nodeloc_icon' data-name='nodeloc_icon'> + <path d="M51.504 19.32..." fill="url(`#paint0_linear_1_6`)"/> + <defs> + <linearGradient id="paint0_linear_1_6" x1="-6" y1="35" x2="104" y2="35" gradientUnits="userSpaceOnUse"> + <stop stop-color="#009966"/> + <stop offset="0.790909" stop-color="#FF9933"/> + </linearGradient> + </defs> + </g> + </svg> +); const NodeLocIcon = (props) => { - function CustomIcon() { - return ( - <svg ... {...props}> - ... - </svg> - ); - } - - return <Icon svg={<CustomIcon />} {...props} />; + return <Icon svg={<CustomIcon />} {...props} />; };web/src/components/auth/RegisterForm.jsx (2)
311-318: Missing user agreement/privacy policy consent check.Unlike
LoginForm.jsx(lines 363-368), this handler doesn't verifyagreedToTermsbefore initiating OAuth. Users could bypass consent by using NodeLoc OAuth registration. For consistency with LoginForm behavior, consider adding the consent check.Proposed fix
const handleNodeLocClick = () => { + if ((hasUserAgreement || hasPrivacyPolicy) && !agreedToTerms) { + showInfo(t('请先阅读并同意用户协议和隐私政策')); + return; + } setNodelocLoading(true); try { onNodeLocOAuthClicked(status.nodeloc_client_id, { shouldLogout: true }); } finally { setTimeout(() => setNodelocLoading(false), 3000); } };
467-474: Inlinecolorstyle won't affect gradient-filled SVG.The
NodeLocIconSVG uses a gradient fill (fill="url(#paint0_linear_1_6)"), so the inlinecolor: '#2563eb'style will have no visual effect. Either remove the unused style prop or update the icon component to support color customization viacurrentColor.web/src/components/auth/LoginForm.jsx (1)
574-581: Same note: inlinecolorstyle won't affect gradient-filled SVG.As noted for
RegisterForm.jsx, theNodeLocIconuses gradient fill, so thiscolorstyle has no effect.web/src/components/settings/SystemSetting.jsx (3)
1581-1630: Missing redirect URL Banner for NodeLoc OAuth configuration.Other OAuth configuration sections (GitHub at lines 1467-1470, LinuxDO at lines 1544-1547) include a
Bannershowing the callback/redirect URL. This helps users configure the OAuth app correctly. Consider adding a similar banner here.Proposed fix
<Card> <Form.Section text={t('配置 NodeLoc OAuth App')}> <Text> {t( '用以支持通过 NodeLoc 进行登录注册,' )} <a href='https://docs.nodeloc.com/api-reference/introduction' target='_blank' rel='noreferrer' style={{ display: 'inline-block', marginLeft: 4, marginRight: 4, }} > {t('点击查看详细文档')} </a> </Text> + <Banner + type='info' + description={`${t('回调 URL 填')} ${inputs.ServerAddress ? inputs.ServerAddress : t('网站地址')}/oauth/nodeloc`} + style={{ marginBottom: 20, marginTop: 16 }} + /> <Row
125-125: Unused state variablenodeLocOAuthEnabled.This state is set in
handleCheckboxChange(line 722) but never read. The same applies tolinuxDOOAuthEnabled(line 124). If these aren't needed for future functionality, consider removing them.
1619-1623: Missing i18n translation wrappers.The label and placeholder for
NodeLocMinimumTrustLevelare not wrapped int()for internationalization. Note: the same issue exists in the LinuxDO section (lines 1569-1572).Proposed fix
<Form.Input field='NodeLocMinimumTrustLevel' - label='NodeLoc Minimum Trust Level' - placeholder='允许注册的最低信任等级' + label={t('NodeLoc Minimum Trust Level')} + placeholder={t('允许注册的最低信任等级')} />
| 访问 NodeLoc 论坛创建 OAuth 应用: | ||
| ``` | ||
| https://www.nodeloc.com/oauth-provider/applications | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add code-fence languages and avoid bare URLs.
This aligns with markdown linting and improves readability.
✅ Suggested doc fixes
-```
+```text
https://www.nodeloc.com/oauth-provider/applications@@
-
https://www.nodeloc.com/oauth-provider/authorize? client_id=YOUR_CLIENT_ID& redirect_uri=YOUR_REDIRECT_URI& response_type=code& scope=openid%20profile& state=RANDOM_STATE
@@
-- NodeLoc OAuth 文档: https://docs.nodeloc.com/api-reference/introduction
+- NodeLoc OAuth 文档: https://docs.nodeloc.com/api-reference/introduction
</details>
Also applies to: 60-67, 108-110
<details>
<summary>🧰 Tools</summary>
<details>
<summary>🪛 markdownlint-cli2 (0.18.1)</summary>
16-16: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
</details>
</details>
<details>
<summary>🤖 Prompt for AI Agents</summary>
In @docs/nodeloc-oauth.md around lines 15 - 18, Update the markdown snippets in
docs/nodeloc-oauth.md to use fenced code blocks with a language specifier (e.g.,
angle-bracketed links (e.g., <https://...>) so they are not treated as plain
bare URLs; specifically change the inline blocks containing
"https://www.nodeloc.com/oauth-provider/applications" and the authorize URL
block (client_id/redirect_uri/response_type...) to ```text fences, and update
occurrences like "https://docs.nodeloc.com/api-reference/introduction" to use
<...> angle-bracket notation; apply the same changes to the other occurrences
noted (lines around 60-67 and 108-110).
- Add safe type assertions to prevent panics (session ID, oauth_state, affCode) - Add HTTP status code validation for token and userinfo endpoints - Fix username race condition using timestamp+NodeLocID combination - Add docstrings for Go functions (NodeLocUser, NodeLocBind, getNodeLocUserInfoByCode, NodeLocOAuth) - Add NodeLoc config defaults in InitOptionMap (ClientId, ClientSecret, MinimumTrustLevel) - Fix OAuth2Callback error redirect to /login (was /console/personal) - Add status.nodeloc_oauth to LoginForm and RegisterForm display conditions - Fix SVG attributes in NodeLocIcon (stop-color -> stopColor) - Remove unused antd dependency from package.json Co-Authored-By: Claude Opus 4.5 <[email protected]>
Co-Authored-By: Claude Opus 4.5 <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@web/src/components/auth/OAuth2Callback.jsx`:
- Around line 82-90: The useEffect currently only validates the authorization
code; also validate the OAuth2 state parameter before contacting the backend: in
OAuth2Callback.jsx inside the useEffect (where code and state are read), add a
guard for a missing or empty state (similar to the existing !code block) that
calls showError(...) with a localized message, navigates back to '/login' via
navigate('/login'), and returns early to avoid sending an incomplete payload to
the backend; ensure this check runs before any backend exchange call.
- Around line 68-74: The retry currently in sendCode(code, state, retry) retries
for all errors; change it to only retry when the error is a network error (i.e.,
no HTTP response was received) by checking that error.response is undefined/null
before performing the exponential/backoff retry; keep the existing MAX_RETRIES
logic, wait/backoff behavior ((retry + 1) * 2000) and recursive call to
sendCode(code, state, retry + 1), but immediately rethrow or propagate the error
when error.response exists (HTTP 4xx/5xx) so those failures do not trigger
retries.
🧹 Nitpick comments (1)
controller/nodeloc.go (1)
100-104: Consider adding timeout context for external HTTP calls.The HTTP client has a 5-second timeout which is good, but for production resilience, consider using
context.WithTimeoutderived from the request context (c.Request.Context()) to properly cancel requests if the client disconnects.
| } catch (error) { | ||
| // 网络错误才重试 | ||
| if (retry < MAX_RETRIES) { | ||
| // 递增的退避等待 | ||
| await new Promise((resolve) => setTimeout(resolve, (retry + 1) * 2000)); | ||
| return sendCode(code, state, retry + 1); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
cat -n web/src/components/auth/OAuth2Callback.jsx | head -100Repository: QuantumNous/new-api
Length of output: 3490
🏁 Script executed:
# Check the broader context around the sendCode function
sed -n '40,85p' web/src/components/auth/OAuth2Callback.jsxRepository: QuantumNous/new-api
Length of output: 1280
🌐 Web query:
Axios error detection network error vs HTTP response error handling
💡 Result:
In Axios, you generally distinguish HTTP response errors (server replied with a status code) from network / no-response errors (browser/node never got an HTTP response) by checking error.response vs error.request. Axios documents this exact branching pattern. [1]
import axios from "axios";
try {
const res = await axios.get("/api");
// success (2xx by default)
} catch (error) {
if (!axios.isAxiosError(error)) throw error;
if (error.response) {
// HTTP response received (non-2xx by default)
const { status, data, headers } = error.response;
// handle status-based logic here (401/403/429/500/etc)
} else if (error.request) {
// Request was sent but NO response was received
// Often shows: message "Network Error", code "ERR_NETWORK"
// Causes include connectivity/DNS, CORS blocking, blocked/mixed-content, etc. [1][2]
} else {
// Something happened setting up the request
// error.message / error.config
}
}Key points:
- Non-2xx is an error by default and is handled via
error.response.status,error.response.data, etc. [1] - “Network Error” /
code: "ERR_NETWORK"typically means no HTTP response was available to Axios (soerror.responseis missing). [1][2] - If you want to handle certain “error” status codes (or all status codes) without throwing, use
validateStatus. [1]
const res = await axios.get("/api", {
validateStatus: (status) => status < 500, // example
});If you’re seeing ERR_NETWORK but expect an HTTP status (e.g., 429) in the browser, it usually indicates the response is being prevented from reaching JS (commonly CORS / browser-layer blocking), so Axios can’t populate error.response. This kind of symptom is commonly reported. [3]
Sources: Axios error handling docs [1], explanation of Axios “Network Error” meaning [2], community report of 429 surfacing as ERR_NETWORK [3].
Limit retries to actual network errors
The current retry logic repeats all errors, including 4xx/5xx HTTP responses from the OAuth endpoint, which causes unnecessary delays for invalid codes or authorization failures. The comment "网络错误才重试" is not enforced. Gate retries on network-only errors (check for error.response absence, which indicates no HTTP response was received).
Proposed fix
} catch (error) {
- // 网络错误才重试
- if (retry < MAX_RETRIES) {
+ // 网络错误才重试(无 HTTP 响应)
+ const isNetworkError =
+ error?.response == null;
+ if (isNetworkError && retry < MAX_RETRIES) {
// 递增的退避等待
await new Promise((resolve) => setTimeout(resolve, (retry + 1) * 2000));
return sendCode(code, state, retry + 1);
}🤖 Prompt for AI Agents
In `@web/src/components/auth/OAuth2Callback.jsx` around lines 68 - 74, The retry
currently in sendCode(code, state, retry) retries for all errors; change it to
only retry when the error is a network error (i.e., no HTTP response was
received) by checking that error.response is undefined/null before performing
the exponential/backoff retry; keep the existing MAX_RETRIES logic, wait/backoff
behavior ((retry + 1) * 2000) and recursive call to sendCode(code, state, retry
+ 1), but immediately rethrow or propagate the error when error.response exists
(HTTP 4xx/5xx) so those failures do not trigger retries.
| useEffect(() => { | ||
| const code = searchParams.get('code'); | ||
| const state = searchParams.get('state'); | ||
|
|
||
| // 参数缺失直接返回 | ||
| // 参数缺失直接返回登录页 | ||
| if (!code) { | ||
| showError(t('未获取到授权码')); | ||
| navigate('/console/personal'); | ||
| navigate('/login'); | ||
| return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validate state before calling the backend
state can be missing; guard it to avoid sending an incomplete OAuth payload and to show a clearer error early.
✅ Proposed fix
- if (!code) {
+ if (!code || !state) {
showError(t('未获取到授权码'));
navigate('/login');
return;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| useEffect(() => { | |
| const code = searchParams.get('code'); | |
| const state = searchParams.get('state'); | |
| // 参数缺失直接返回 | |
| // 参数缺失直接返回登录页 | |
| if (!code) { | |
| showError(t('未获取到授权码')); | |
| navigate('/console/personal'); | |
| navigate('/login'); | |
| return; | |
| useEffect(() => { | |
| const code = searchParams.get('code'); | |
| const state = searchParams.get('state'); | |
| // 参数缺失直接返回登录页 | |
| if (!code || !state) { | |
| showError(t('未获取到授权码')); | |
| navigate('/login'); | |
| return; |
🤖 Prompt for AI Agents
In `@web/src/components/auth/OAuth2Callback.jsx` around lines 82 - 90, The
useEffect currently only validates the authorization code; also validate the
OAuth2 state parameter before contacting the backend: in OAuth2Callback.jsx
inside the useEffect (where code and state are read), add a guard for a missing
or empty state (similar to the existing !code block) that calls showError(...)
with a localized message, navigates back to '/login' via navigate('/login'), and
returns early to avoid sending an incomplete payload to the backend; ensure this
check runs before any backend exchange call.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Summary
新增 NodeLoc OAuth 登录支持,允许用户通过 NodeLoc 账号直接登录或注册。
Changes
Backend
controller/nodeloc.go- NodeLoc OAuth 核心逻辑model/user.go- 新增nodeloc_id字段及相关查询方法model/option.go- 新增 NodeLoc 相关配置项controller/misc.go- 在 status API 中返回 NodeLoc OAuth 状态controller/option.go- 新增 NodeLoc 配置项处理router/api-router.go- 新增 NodeLoc OAuth 路由common/constants.go- 新增 NodeLoc 相关常量Frontend
web/src/components/auth/LoginForm.jsx- 登录页添加 NodeLoc 登录按钮web/src/components/auth/RegisterForm.jsx- 注册页添加 NodeLoc 登录按钮web/src/components/auth/OAuth2Callback.jsx- 优化 OAuth 回调处理,业务错误不再重试web/src/components/settings/SystemSetting.jsx- 系统设置中添加 NodeLoc OAuth 配置web/src/components/settings/personal/cards/AccountManagement.jsx- 个人设置中支持绑定/解绑 NodeLocweb/src/components/common/logo/NodeLocIcon.jsx- 新增 NodeLoc 图标组件web/src/helpers/api.js- 新增onNodeLocOAuthClicked方法web/src/App.jsx- 新增/oauth/nodeloc路由Documentation
docs/nodeloc-oauth.md- NodeLoc OAuth 配置文档Features
Configuration
在系统设置中配置以下选项:
Test Plan
Summary by CodeRabbit
Release Notes - v1.0.3
New Features
Documentation
Chores
✏️ Tip: You can customize this high-level summary in your review settings.