概要
- Zeit Now + Next.jsの開発環境ができた(*1)(*2)ので、認証を試してみた。
- ▲🔥 Getting started with Next.js, ZEIT Now, and Firebaseの記事を参考に作成
- firebaseの認証は以前に行った(*3)ものを参考にして構築したので割愛。
- Twitter認証で使用するため、
/infos/agreement
と/infos/privacy-policy
のページを作成
- Twitter認証で使用するため、
- 環境変数まわりでハマったため、そのあたりのメモ
フォルダ構成
- .env # 環境変数設定ファイル
- .env.build
- now.json # now設定ファイル
- next.config.js # next設定ファイル
- api
- login.ts # セッション用API (cookieを使用。トークンをデコードしてユーザ情報を返却)
- logout.ts # セッション終了API
- src
- pages
- index.tsx # トップページ
- _document.tsx # テンプレート
- auth
- login.tsx # ログインページ
- mypage
- index.tsx # ログイン時のみ閲覧可能なページ
- infos
- agreement.tsx # 利用規約
- privacy-policy.tsx # プライバシーポリシー
- utils
- auth
- firebaseAdmin.ts # トークン検証(API用)
- firebaseSessionHandler # セッション用APIを叩く
- hooks.ts # コンテクスト作成用
- initFirebase.ts # firebase初期化
- logout.ts # ログアウト処理
- user.ts # ユーザ。firebaseの認証ユーザから必要な情報のみを抽出
- middleware
- commonMiddleware.ts # middlewareとして登録(API用)
- cookieSession.ts # セッションを追加する(API用)
- cookiSessionRefresh.ts # cookie初期化(API用)
- pageWrappers
- withAuthUser.tsx # サーバ側・クライアント側それぞれでユーザ情報を取得
- withAuthUserInfo.tsx # コンテクスト作成関数を呼び出して、コンテクストをNextのコンポーネントに追加
環境変数について
- ローカルの開発環境と、デプロイ先の環境で設定方法が違ってけっこうハマった
- デプロイ先の環境変数が設定できていないときに出力されたエラーは以下。
Error occurred prerendering page "/auth/login". Read more: https://err.sh/next.js/prerender-error:
Error: Failed to render serverless page
> Build error occurred
Error: Export encountered errors
at _default (/zeit/2856d89b/node_modules/next/dist/export/index.js:19:1086)
at runMicrotasks (<anonymous>)
at processTicksAndRejections (internal/process/task_queues.js:94:5)
at async build (/zeit/2856d89b/node_modules/next/dist/build/index.js:36:218)
ローカル開発環境での環境変数について
.envファイルに記述、next.config.jsファイルでnextに適用する。
.env
FIREBASE_API_KEY=xXxXxX
FIREBASE_AUTH_DOMAIN=XXX.firebaseapp.com
FIREBASE_PROJECT_ID=XXX
# == Firebase admin keys (from serviceAccount-staging.json) ==
FIREBASE_CLIENT_EMAIL=firebase-adminsdk-■■■■@■■■■■■■■.iam.gserviceaccount.com
FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n■■■■■■■■\n-----END PRIVATE KEY-----\n"
# Cookie
SESSION_SECRET=secretHOGEHOGE
SESSION_SECRET_PREVIOUS=secretFUGAFUGA
next.config.js
/* eslint-disable @typescript-eslint/no-var-requires */
const { resolve } = require('path')
const withOffline = require('next-offline')
+ require('dotenv').config()
const nextConfig = {
generateInDevMode: true,
workboxOpts: {
swDest: 'static/service-worker.js',
// 省略
},
+ // .envファイルから読み込んだ環境変数をnextで使用できるように定義
+ env: {
+ FIREBASE_API_KEY: process.env.FIREBASE_API_KEY,
+ FIREBASE_AUTH_DOMAIN: process.env.FIREBASE_AUTH_DOMAIN,
+ FIREBASE_PROJECT_ID: process.env.FIREBASE_PROJECT_ID,
+ FIREBASE_CLIENT_EMAIL: process.env.FIREBASE_CLIENT_EMAIL,
+ FIREBASE_PRIVATE_KEY: process.env.FIREBASE_PRIVATE_KEY,
+ SESSION_SECRET: process.env.SESSION_SECRET,
+ SESSION_SECRET_PREVIOUS: process.env.SESSION_SECRET_PREVIOUS,
+ },
}
// PWA に対応
module.exports = withOffline(nextConfig)
デプロイ時の環境変数について
now.json
{
"version": 2,
"routes": [
{
"src": "^/service-worker.js$",
"dest": "/_next/static/service-worker.js",
"headers": {
"cache-control": "public, max-age=43200, immutable",
"Service-Worker-Allowed": "/"
}
}
],
"functions": {
"api/pdf.ts": {
"includeFiles": "fonts/**"
}
},
"env": {
"FIREBASE_API_KEY": "@firebase-api-key",
"FIREBASE_AUTH_DOMAIN": "@firebase-auth-domain",
"FIREBASE_PROJECT_ID": "@firebase-project-id",
"FIREBASE_CLIENT_EMAIL": "@firebase-client-email",
"FIREBASE_PRIVATE_KEY": "@firebase-private-key",
"SESSION_SECRET": "@session-secret",
"SESSION_SECRET_PREVIOUS": "@session-secret-previous"
},
"build": {
"env": {
"FIREBASE_API_KEY": "@firebase-api-key",
"FIREBASE_AUTH_DOMAIN": "@firebase-auth-domain",
"FIREBASE_PROJECT_ID": "@firebase-project-id",
"FIREBASE_CLIENT_EMAIL": "@firebase-client-email",
"FIREBASE_PRIVATE_KEY": "@firebase-private-key",
"SESSION_SECRET": "@session-secret",
"SESSION_SECRET_PREVIOUS": "@session-secret-previous"
}
}
}
- secretsへの登録はコマンドを使用する
now secrets add firebase-api-key xXxXxX
now secrets add firebase-auth-domain XXX.firebaseapp.com
now secrets add firebase-project-id XXX
now secrets add firebase-client-email firebase-adminsdk-■■■■@■■■■■■■■.iam.gserviceaccount.com
now secrets add firebase-private-key -- "-----BEGIN PRIVATE KEY-----\n■■■■■■■■\n-----END PRIVATE KEY-----\n"
now secrets add session-secret secretHOGEHOGE
now secrets add session-secret-previous secretFUGAFUGA
- 秘密鍵の登録には、
--
オプションを使わないと以下のようなエラーとなる
Now CLI 18.0.0
Error! Invalid number of arguments. Usage: `now secret add <name> <value>`
If your secret has spaces or starts with '-', make sure to terminate command options with double dash and wrap it in quotes. Example:
$ now secret add -- "test"
設定したsecretsの確認
now secrets ls
コマンドで登録したsecretsを確認できる。
root@6ad82688728c:/app# now secrets ls
Now CLI 18.0.0
x secrets found under xxx[527ms]
name created
firebase-private-key 2h ago
session-secret 3h ago
session-secret-previous 14h ago
firebase-client-email 14h ago
firebase-project-id 14h ago
firebase-auth-domain 14h ago
firebase-api-key 14h ago
秘密鍵の改行コードについて
-
now secrets add
で登録した秘密鍵は、\n
が\\n
として登録されてしまう。- ローカルで
.env
ファイルから読み込んだときには発生しない状況。本番だけなのでハマった。
- ローカルで
- 使用時には、以下のように
FIREBASE_PRIVATE_KEY.replace(/\\n/g, '\n')
で置換する必要があった。- 改行込みで環境変数に登録できないものか。。。
import * as admin from 'firebase-admin'
export const verifyIdToken = (token: string) => {
if (!admin.apps.length) {
const cert = {
projectId: process.env.FIREBASE_PROJECT_ID,
clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
// zeit now の環境変数だと\nが\\nにエスケープされてしまっているので元に戻す
privateKey: process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, '\n'),
}
admin.initializeApp({
credential: admin.credential.cert(cert),
})
}
return admin
.auth()
.verifyIdToken(token)
.catch((error) => {
throw error
})
}
環境
- Windows 10 Home
- virtualbox 6.1.6
- Vagrant 2.2.7
- ubuntu 18.04 LTS
- docker-ce Docker version 19.03.5, build 633a0ea838
- docker-compose version 1.25.3, build d4d1b42b
- node 13.13
- now-cli: 18.0
- next: 9.3.4
- react: 16.13.1
- react-dom: 16.13.1
- @reduxjs/toolkit: 1.3.4
- react-redux: 7.2.0
- firebase: 7.14.0
- firebase-admin: 8.10.0
- react-firebaseui: 4.1.0
- typescript: 3.8.3
参考
[RS256] JWTでRSA秘密鍵を環境変数で処理したい [Javascript]
Zeit Nowの具体的なTips集
▲🔥 Getting started with Next.js, ZEIT Now, and Firebase
Next.js プロジェクトに Firebase Authentication を組み込み
firebase
next example
React で Firebase Authentication を使う
Next.js, Material-UI v4-alpha And React Hook With Firebase Hosting example
react-redux-firebase
Storing complex secrets
Docker で zeit nowのデプロイ環境とNext.jsの開発環境を作ったメモ
PdfMakeを使ったPDF作成APIをZeit Nowで動かしたメモ