🛡️

Remix on Vercel で無料で Basic 認証を全体にかける方法

2022/08/26に公開

これはなに

Remix で作ったプロトタイプを Vercel にデプロイして全ルートを対象に Basic 認証をかけたくていろいろ試行錯誤したので、その備忘録になります。

結論

  1. app/entry.server.tsxWWW-Authenticate: Basic のHTTPヘッダを返すように設定

  2. app/root.tsx に定義した loader で認証確認しRoot component で結果に応じて出し分ける

ポイントは 1. と 2. に分けてやることです。 root.tsx の headers で WWW-Authenticate を返すようにしてる記事もあったのですが、それだと実際の Response Header にヘッダが反映されず、動作しません。

デモ

https://remix-basic-auth-vercel.vercel.app/

ユーザ名: admin
パスワード: password

ソースコード

https://github.com/coji/remix-basic-auth-vercel

entry.server.tsx で WWW-Authenticate ヘッダを返す設定

entry.server.tsx
import type { EntryContext } from '@remix-run/node'
import { RemixServer } from '@remix-run/react'
import { renderToString } from 'react-dom/server'

export default function handleRequest(
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  remixContext: EntryContext
) {
  const markup = renderToString(
    <RemixServer context={remixContext} url={request.url} />
  )

  responseHeaders.set('Content-Type', 'text/html')
  responseHeaders.set('WWW-Authenticate', 'basic') // Basic認証のヘッダを全レスポンスで返す

  return new Response('<!DOCTYPE html>' + markup, {
    status: responseStatusCode,
    headers: responseHeaders,
  })
}

root.tsx の loader での認証確認処理。

root.tsx
const isAuthorized = (request: Request) => {
  const header = request.headers.get('Authorization')
  if (!header) return false
  const base64 = header.replace('Basic ', '')
  const [username, password] = Buffer.from(base64, 'base64')
    .toString()
    .split(':')
  return username === 'admin' && password === 'password'
}

export const loader = ({ request }: LoaderArgs) => {
  if (isAuthorized(request)) {
    return json({ authorized: true })
  } else {
    return json({ authorized: false }, { status: 401 })
  }
}

root.tsx の root コンポーネントで結果に応じて出し分け

root.tsx
export default function App() {
  const { authorized } = useLoaderData<typeof loader>()
  if (!authorized) {
    return <>Authorization Required</>
  }

  return (
    <html lang="en">
      <head>
        <Meta />
        <Links />
      </head>
      <body>
        <Outlet />
        <ScrollRestoration />
        <Scripts />
        <LiveReload />
      </body>
    </html>
  )
}

remix たのしい〜。
東京でミートアップをしたいな〜

Discussion