8
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Zeit Now + Next.js のページにFirebase認証を導入した際、環境変数にハマったメモ

Last updated at Posted at 2020-04-17

概要

フォルダ構成

- .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)

デプロイ時の環境変数について

  • secretsに登録して、now.jsonで設定する( * ) ( ** ) ( *** )
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で動かしたメモ

8
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?