Conversation
Basic Auth handler for Next.js proxy.ts. Reads credentials from BASIC_AUTH_USER/PASSWORD env vars and controls Vercel environments via BASIC_AUTH_TARGET (all | production). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Walkthrough新しいPNPMワークスペースを設定し、Vercelデプロイメント向けのBasic認証ミドルウェアパッケージ Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/next-basic-auth-proxy/src/index.ts`:
- Around line 46-53: The Authorization parsing is insufficient: first validate
the scheme by ensuring the incoming authorization header starts with the "Basic
" scheme (case-insensitive) before extracting authValue, then when decoding
(atob(authValue)) split on the first ':' only (not split all colons) so
passwords containing ':' are preserved; update the logic around
authorization/authValue and the atob(...) handling and still call unauthorized()
when the scheme/decoded parts are missing or malformed, comparing the parsed
user and password against authUser and authPassword.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (7)
packages/next-basic-auth-proxy/.gitignorepackages/next-basic-auth-proxy/README.mdpackages/next-basic-auth-proxy/package.jsonpackages/next-basic-auth-proxy/src/index.tspackages/next-basic-auth-proxy/tsconfig.jsonpackages/next-basic-auth-proxy/tsup.config.tspnpm-workspace.yaml
| const authValue = authorization.split(" ")[1]; | ||
| if (authValue === undefined) { | ||
| return unauthorized(); | ||
| } | ||
|
|
||
| try { | ||
| const [user, password] = atob(authValue).split(":"); | ||
| if (user !== authUser || password !== authPassword) { |
There was a problem hiding this comment.
Authorizationヘッダー解析が不完全です(認証失敗の誤判定リスク)。
Basic スキームの明示検証がなく、さらに split(":") のため : を含むパスワードで正しい認証情報でも失敗します。ヘッダーは scheme を確認し、復号後は最初の : だけで分割してください。
修正案
- const authValue = authorization.split(" ")[1];
- if (authValue === undefined) {
+ const [scheme, encoded] = authorization.split(" ", 2);
+ if (!scheme || scheme.toLowerCase() !== "basic" || !encoded) {
return unauthorized();
}
try {
- const [user, password] = atob(authValue).split(":");
+ const decoded = atob(encoded);
+ const separatorIndex = decoded.indexOf(":");
+ if (separatorIndex < 0) {
+ return unauthorized();
+ }
+ const user = decoded.slice(0, separatorIndex);
+ const password = decoded.slice(separatorIndex + 1);
if (user !== authUser || password !== authPassword) {
return unauthorized();
}📝 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.
| const authValue = authorization.split(" ")[1]; | |
| if (authValue === undefined) { | |
| return unauthorized(); | |
| } | |
| try { | |
| const [user, password] = atob(authValue).split(":"); | |
| if (user !== authUser || password !== authPassword) { | |
| const [scheme, encoded] = authorization.split(" ", 2); | |
| if (!scheme || scheme.toLowerCase() !== "basic" || !encoded) { | |
| return unauthorized(); | |
| } | |
| try { | |
| const decoded = atob(encoded); | |
| const separatorIndex = decoded.indexOf(":"); | |
| if (separatorIndex < 0) { | |
| return unauthorized(); | |
| } | |
| const user = decoded.slice(0, separatorIndex); | |
| const password = decoded.slice(separatorIndex + 1); | |
| if (user !== authUser || password !== authPassword) { |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/next-basic-auth-proxy/src/index.ts` around lines 46 - 53, The
Authorization parsing is insufficient: first validate the scheme by ensuring the
incoming authorization header starts with the "Basic " scheme (case-insensitive)
before extracting authValue, then when decoding (atob(authValue)) split on the
first ':' only (not split all colons) so passwords containing ':' are preserved;
update the logic around authorization/authValue and the atob(...) handling and
still call unauthorized() when the scheme/decoded parts are missing or
malformed, comparing the parsed user and password against authUser and
authPassword.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/vercel-basic-auth/src/index.ts`:
- Around line 56-63: The Authorization header parsing is brittle: ensure the
header uses the Basic scheme and extract only the base64 payload, then decode
and split credentials on the first ':' only. Replace the current
authorization.split(" ")[1] logic with a check that
authorization.trim().startsWith("Basic "), obtain the substring after the first
space as authValue, decode with atob(authValue), and split the decoded string
using the first indexOf(':') to separate username and password (so passwords
containing ':' are preserved); keep the existing unauthorized() call when
validation fails and validate against authUsername/authPassword as before.
- Around line 20-25: Add runtime validation for the BasicAuthOptions inputs by
checking that authUsername and authPassword are non-empty strings (e.g., typeof
=== "string" and .trim().length > 0) at the start of the exported
initializer/middleware that receives { username: authUsername, password:
authPassword, vercelEnvTarget, dev }. If either value is missing/empty/invalid,
throw a clear Error (or fail-fast) describing which credential is invalid so
misconfiguration fails early; keep vercelEnvTarget and dev handling unchanged.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (6)
packages/vercel-basic-auth/.gitignorepackages/vercel-basic-auth/README.mdpackages/vercel-basic-auth/package.jsonpackages/vercel-basic-auth/src/index.tspackages/vercel-basic-auth/tsconfig.jsonpackages/vercel-basic-auth/tsup.config.ts
✅ Files skipped from review due to trivial changes (2)
- packages/vercel-basic-auth/README.md
- packages/vercel-basic-auth/.gitignore
| { | ||
| username: authUsername, | ||
| password: authPassword, | ||
| vercelEnvTarget = "only-production", | ||
| dev = false, | ||
| }: BasicAuthOptions, |
There was a problem hiding this comment.
認証情報オプションのランタイム検証を追加してください。
username / password は必須仕様ですが、現在は空文字や不正値を実行時に弾いていません。設定ミスを早期失敗させた方が安全です。
🔧 提案差分
export function basicAuth(
request: Request,
{
username: authUsername,
password: authPassword,
vercelEnvTarget = "only-production",
dev = false,
}: BasicAuthOptions,
): Response | null {
+ if (!authUsername || !authPassword) {
+ throw new TypeError("basicAuth: username and password are required");
+ }
+
function unauthorized() {
return new Response("Auth required", {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/vercel-basic-auth/src/index.ts` around lines 20 - 25, Add runtime
validation for the BasicAuthOptions inputs by checking that authUsername and
authPassword are non-empty strings (e.g., typeof === "string" and .trim().length
> 0) at the start of the exported initializer/middleware that receives {
username: authUsername, password: authPassword, vercelEnvTarget, dev }. If
either value is missing/empty/invalid, throw a clear Error (or fail-fast)
describing which credential is invalid so misconfiguration fails early; keep
vercelEnvTarget and dev handling unchanged.
| const authValue = authorization.split(" ")[1]; | ||
| if (authValue === undefined) { | ||
| return unauthorized(); | ||
| } | ||
|
|
||
| try { | ||
| const [username, password] = atob(authValue).split(":"); | ||
| if (username !== authUsername || password !== authPassword) { |
There was a problem hiding this comment.
Authorization ヘッダのパースが脆く、正当な認証情報を誤判定します。
現状は split(" ") / split(":") 依存のため、Basic スキーム未検証かつ password に : を含むケースを正しく扱えません。Basic <base64> を厳密に検証し、最初の : でのみ分割してください。
🔧 提案差分
- const authValue = authorization.split(" ")[1];
- if (authValue === undefined) {
+ const matched = authorization.match(/^Basic\s+(.+)$/i);
+ if (!matched) {
return unauthorized();
}
try {
- const [username, password] = atob(authValue).split(":");
+ const decoded = atob(matched[1]);
+ const separatorIndex = decoded.indexOf(":");
+ if (separatorIndex < 0) {
+ return unauthorized();
+ }
+ const username = decoded.slice(0, separatorIndex);
+ const password = decoded.slice(separatorIndex + 1);
if (username !== authUsername || password !== authPassword) {
return unauthorized();
}
} catch {📝 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.
| const authValue = authorization.split(" ")[1]; | |
| if (authValue === undefined) { | |
| return unauthorized(); | |
| } | |
| try { | |
| const [username, password] = atob(authValue).split(":"); | |
| if (username !== authUsername || password !== authPassword) { | |
| const matched = authorization.match(/^Basic\s+(.+)$/i); | |
| if (!matched) { | |
| return unauthorized(); | |
| } | |
| try { | |
| const decoded = atob(matched[1]); | |
| const separatorIndex = decoded.indexOf(":"); | |
| if (separatorIndex < 0) { | |
| return unauthorized(); | |
| } | |
| const username = decoded.slice(0, separatorIndex); | |
| const password = decoded.slice(separatorIndex + 1); | |
| if (username !== authUsername || password !== authPassword) { | |
| return unauthorized(); | |
| } | |
| } catch { |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/vercel-basic-auth/src/index.ts` around lines 56 - 63, The
Authorization header parsing is brittle: ensure the header uses the Basic scheme
and extract only the base64 payload, then decode and split credentials on the
first ':' only. Replace the current authorization.split(" ")[1] logic with a
check that authorization.trim().startsWith("Basic "), obtain the substring after
the first space as authValue, decode with atob(authValue), and split the decoded
string using the first indexOf(':') to separate username and password (so
passwords containing ':' are preserved); keep the existing unauthorized() call
when validation fails and validate against authUsername/authPassword as before.
Summary
Closes #4
以下を参考にライブラリ化 (skills の存在は public になっても良い)
https://github.com/plainbrew/skills/blob/a5987b1/skills/nextjs-proxy-basic-auth/SKILL.md
https://github.com/plainbrew/skills/blob/e3e39d0/skills/storybook-basic-auth/SKILL.md
pnpm-workspace.yamlを追加packages/vercel-basic-auth(@plainbrew/vercel-basic-auth) パッケージを追加basicAuthをエクスポートnext-utils 最初の pkg ではあるが、Next.js には依存せず、もっぱら Vercel hosting 用
その代わり Next.js でも Storybook でも利用できる
Next.js の持ち主だし、ヨシ!
使い方
オプション
usernamestringpasswordstringvercelEnvTargetstring'only-production'devbooleanfalseNODE_ENV=developmentでも Basic 認証を適用するかvercelEnvTargetonly-productionalldisabled🤖 Generated with Claude Code
Summary by CodeRabbit
リリースノート