はじめに
Cloudflare Workers + Hono で LINEログインを実装しているアプリを、Docker Compose 上の wrangler dev で動かしていました。
以前はログインできていたのですが、あるタイミングから LINEログインの callback で 500 になるようになりました。
画面に出ていたレスポンスはこれです。
{
"message": "サーバーで問題が発生しました"
}
問題
LINEログインの認証画面までは問題なく進めていました。
落ちていたのは、LINE から戻ってきた callback の中で code を access token に交換する箇所です。
await fetch('https://api.line.me/oauth2/v2.1/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code,
redirect_uri: config.redirectUri,
client_id: config.channelId,
client_secret: config.channelSecret,
code_verifier: codeVerifier,
}),
})
API コンテナのログを見ると、次のエラーが出ていました。
failed: TLS peer's certificate is not trusted;
reason = unable to get local issuer certificate
LINE の callback URL や Channel Secret の設定ミスではなく、Docker コンテナ内で LINE API への HTTPS 通信に失敗していました。
原因は、API コンテナに CA 証明書が入っていなかったことです。
今回の API コンテナは node:22-bookworm-slim を使っていました。
docker-compose exec api ls -l /etc/ssl/certs/ca-certificates.crt
No such file or directory
解決方法
API 用の Dockerfile を作り、ca-certificates を入れました。
FROM node:22-bookworm-slim
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/*
docker-compose.yml では、API サービスをこの Dockerfile から build するようにします。
services:
api:
build:
context: .
dockerfile: Dockerfile.api
working_dir: /app
ports:
- "8787:8787"
あわせて、LINE API への fetch() 自体が失敗したときに原因を追いやすいように、通信部分を少しラップしました。
async function fetchLine(input: RequestInfo | URL, init?: RequestInit) {
try {
return await fetch(input, init)
} catch (error) {
console.error('LINE OAuth request failed', error)
throw new HTTPException(502, { message: 'LINEとの通信に失敗しました' })
}
}
おわりに
今回のエラーは、LINEログインの実装や callback URL が間違っていたわけではなく、ローカル Docker 環境の CA 証明書不足が原因でした。
ブラウザで LINEログイン画面までは進めるのに callback だけ 500 になる場合、callback の中で外部 HTTPS 通信している箇所を見ると切り分けしやすいです。
特に node:*slim 系のイメージで wrangler dev を動かしている場合は、コンテナ内に CA 証明書があるか確認しておくとよさそうです。