2段階認証
ログインのときに、パスワードに加えて、もう1つの手段で本人を確かめる仕組みです。
手段
| 手段 | 値 | 強さ | 説明 |
|---|---|---|---|
| パスキー | PASSKEY | 強い | 端末の指紋・顔認証・PIN、またはセキュリティキー(WebAuthn)。なりすましのサイトでは使えず、いちばん安全。1人で何個でも登録できる |
| 認証アプリ | TOTP | 強い | Google Authenticator などに表示される6桁のコード |
| メールのコード | EMAIL | 弱い | 登録しているメールアドレスに届く6桁のコード(10分・5回まで)。メールを見られると破られる |
| 予備のコード | RECOVERY_CODE | 強い | 最初の手段を登録したときに8個作る。1回ずつ使える。端末をなくしたとき用 |
どれか1つでも登録していれば「2段階認証を使っている」ことになります。
どの手段を使うか
- 登録するのは本人: 個人の設定で、使う手段を好きなだけ登録します(パスキーをおすすめとして先頭に出します)。
- ログインでは、登録しているうちいちばん強いものを先に出す: パスキー → 認証アプリ → メールのコードの順。「別の方法を使う」で、登録しているほかの手段や予備のコードに切り替えられます。パスキーを使えないブラウザでは、パスキーを出しません。
- 画面が認める手段に絞る: 画面ごとに「メールのコードでもよいか」を決めます。認めない画面では、メールのコードで確かめただけでは入れません(パスキーか認証アプリを求めます)。
- 管理画面: 認めない(
LASE_TWO_FACTOR_ADMIN_ALLOW_EMAIL) - パートナー画面: 認める(
LASE_TWO_FACTOR_PARTNER_ALLOW_EMAIL) - 組織の画面: その組織の管理者が「組織の設定 → セキュリティ」で選ぶ(
tenants.two_factor_allow_email。既定は認める)
- 管理画面: 認めない(
- 信頼できるデバイスなら省く: 確かめるときに「このデバイスを信頼する」を選ぶと、そのブラウザでは一定の日数(既定30日)2段階目を省きます。ただし、そのとき使った手段を画面が認めているときだけです(メールのコードで信頼したデバイスでも、管理画面では省きません)。
考え方
- 登録と確認は人(users)が持つ: 認証アプリの鍵・予備のコードは
usersに(暗号化して)、パスキーはpasskeysに、信頼できるデバイスはtrusted_devicesに持ちます。どの組織・画面でも同じものを使います。2段階認証を使っている人には、ログインのたびに2段階目を求めます。 - 求めるかどうかは画面が決める: 管理画面は設定で求めます(既定で求める)。パートナー画面は設定で選びます。組織の画面は、その組織の管理者が選びます。
- 確かめるのは画面に入るところ:
authorize:admin・authorize:partner・authorize:tenantの直後に、その画面が求めているのに、このセッションで(その画面が認める手段で)まだ確かめていなければ403(code: TWO_FACTOR_REQUIRED。allowEmail付き)を返します。画面は2段階認証の画面(two-factor)へ移り、使っている人には確かめることを、使っていない人には登録を求めます。 - 特定の人だけを免除はしない: 通れない人のために、予備のコードと、運営による解除(管理画面のユーザーの詳細)を用意します。
- 手段を変えるには本人の確認が要る: 手段を足すのと予備のコードの作り直しは、このセッションで2段階認証を確かめてから。手段を消すのとやめるのは、パスワードで確かめてから。最後の手段を消すと、予備のコードと信頼できるデバイスも消えます。
確かめたこと(と、その手段)はセッションに残します。ルートドメインで確かめた結果は、組織へのログイン用トークン(TenantLoginTokenService)で組織のサブドメイン側のセッションへ引き継ぎます。「ログイン状態を保持する」で戻ってきたセッションは確かめていない扱いなので、求める画面では改めて確かめます(信頼できるデバイスなら省きます)。管理者の代理ログインは、管理者が確かめた手段を引き継ぎます。
信頼できるデバイス
クッキー lase_trusted_device にランダムな値を入れ、DB にはそのハッシュだけを持ちます。クッキーは SESSION_DOMAIN に付けるので、組織のサブドメインでも使えます。個人の設定で一覧を見て、1つずつ、またはすべて取り消せます。
パスキー
lbuchs/webauthn で確かめます。パスキーを使うドメイン(RP ID)は LASE_TWO_FACTOR_PASSKEY_RP_ID(省略すると APP_DOMAIN)です。組織のサブドメインでも同じパスキーを使えるよう、親のドメインにしてください。送られてきた origin は、そのドメインかそのサブドメインの https だけを認めます(localhost は http でも可)。
設定
| 設定 | 環境変数 | 既定 | 説明 |
|---|---|---|---|
lase.two_factor.require_for_admin | LASE_TWO_FACTOR_REQUIRE_ADMIN | true | 管理画面で求める |
lase.two_factor.admin_allow_email | LASE_TWO_FACTOR_ADMIN_ALLOW_EMAIL | false | 管理画面でメールのコードを認める |
lase.two_factor.require_for_partner | LASE_TWO_FACTOR_REQUIRE_PARTNER | false | パートナー画面で求める |
lase.two_factor.partner_allow_email | LASE_TWO_FACTOR_PARTNER_ALLOW_EMAIL | true | パートナー画面でメールのコードを認める |
lase.two_factor.issuer | LASE_TWO_FACTOR_ISSUER | アプリの名前 | 認証アプリに出す発行元・パスキーの名前 |
lase.two_factor.passkey_rp_id | LASE_TWO_FACTOR_PASSKEY_RP_ID | APP_DOMAIN | パスキーを使うドメイン |
lase.two_factor.recovery_codes | 8 | 予備のコードの数 | |
lase.two_factor.email_code_minutes | 10 | メールのコードの有効な時間(分) | |
lase.two_factor.email_code_attempts | 5 | メールのコードを試せる回数 | |
lase.two_factor.trusted_device_days | LASE_TWO_FACTOR_TRUSTED_DEVICE_DAYS | 30 | 信頼できるデバイスで2段階目を省く日数(0 で信頼できるデバイスを使わない) |
総当たりを防ぐため、コードを確かめる・メールを送る・パスワードを確かめる API は、人ごと・IP ごとに1分あたり lase.auth.rate_limit_per_minute 回までにしています(throttle:lase-two-factor)。一度使った認証アプリのコードは、有効な間でも使えません。
画面
| 画面 | ルート | 説明 |
|---|---|---|
| ログイン | (公開画面) | 2段階認証を使っている人は、パスワードのあとに2段階目を確かめる(いちばん強い手段を先に出す) |
| 個人の設定 → 2段階認証 | account.two-factor | 手段の一覧・足す・消す(パスワード)・予備のコードの作り直し・信頼できるデバイスの取り消し・やめる |
| 2段階認証が必要 | two-factor(組織 /two-factor・管理 /admin/two-factor・パートナー /partner/two-factor) | 求める画面に入ろうとしたときに出す。確かめたら、もとの画面へ戻る。メールのコードを認めない画面(strong=1)では、メールのコードだけの人に、メールのコードで確かめてからパスキーか認証アプリの登録を求める |
| 組織の設定 → セキュリティ | settings.security | 「所属する人に2段階認証を求める」「メールのコードを認める」。自分がその設定で使える手段を持っているときだけ求められる(締め出されないように) |
| 管理画面 → ユーザーの詳細 | 2段階認証を使っているかと、解除(端末をなくした人のため) |
部品は TwoFactorChallenge(確かめる)・TwoFactorSetup(手段を足す)・TwoFactorRecoveryCodes(予備のコードを見せる)です。two-factor のルートは AuthRoutes(組織)・AdminRoutes・PartnerRoutes が持ちます。API が TWO_FACTOR_REQUIRED を返すと、useApi が自動でこの画面へ移します(onTwoFactorRequired(path, strong) で変えられます)。
API
ログイン
| メソッド | パス | 説明 |
|---|---|---|
| POST | /api/auth/login | 2段階認証を使っている人には {"twoFactor": true, "methods": [...], "email": "伏せたアドレス", "trustedDeviceDays": 30} を返し、まだログインさせない(信頼できるデバイスなら、そのままログインさせる) |
| POST | /api/auth/two-factor-challenge | ログインの2段階目(method と code・recoveryCode・credential のどれか、rememberDevice)。パスワードを確かめてから10分の間だけ |
| POST | /api/auth/two-factor-challenge/email | ログインの2段階目のメールのコードを送る |
| POST | /api/auth/two-factor-challenge/passkey-options | ログインの2段階目のパスキーの準備 |
自分の2段階認証(どの画面でも同じ)
| メソッド | パス | 説明 |
|---|---|---|
| GET | /api/auth/two-factor | 自分の状態(enabled・methods・totp・email・passkeys・recoveryCodesRemaining・verified・verifiedMethod・trustedDevices・trustedDeviceDays) |
| POST | /api/auth/two-factor/verify | このセッションで確かめる(ログインの2段階目と同じ入力) |
| POST | /api/auth/two-factor/email/send | 確かめるためのメールのコードを送る |
| POST | /api/auth/two-factor/passkeys/authentication-options | 確かめるためのパスキーの準備 |
| POST | /api/auth/two-factor/totp | 認証アプリの登録を始める(secret・otpauthUrl・qrCodeSvg) |
| POST | /api/auth/two-factor/totp/confirm | コードで確かめて使い始める |
| DELETE | /api/auth/two-factor/totp | 認証アプリをやめる(password) |
| POST | /api/auth/two-factor/email | メールのコードを使い始める準備(確かめるコードを送る) |
| POST | /api/auth/two-factor/email/confirm | 届いたコードで使い始める |
| DELETE | /api/auth/two-factor/email | メールのコードをやめる(password) |
| POST | /api/auth/two-factor/passkeys/registration-options | パスキーの登録の準備 |
| POST | /api/auth/two-factor/passkeys | パスキーを登録する(name・credential) |
| DELETE | /api/auth/two-factor/passkeys/{id} | パスキーを消す(password) |
| POST | /api/auth/two-factor/recovery-codes | 予備のコードを作り直す(このセッションで確かめたあと) |
| DELETE | /api/auth/two-factor/trusted-devices[/{id}] | 信頼できるデバイスを取り消す(省くと、すべて) |
| DELETE | /api/auth/two-factor | すべてやめる(password) |
手段を使い始める API は、最初の手段なら予備のコード(recoveryCodes)も返します。手段を足す API は、すでに2段階認証を使っている人なら、このセッションで確かめてから呼びます。
組織・運営
| メソッド | パス | 説明 |
|---|---|---|
| PUT | /api/tenant/tenant/two-factor-requirement | 組織が求めるか・メールのコードを認めるか(required・allowEmail。tenant:write) |
| DELETE | /api/admin/users/{id}/two-factor | 運営が解除する(すべての手段・予備のコード・信頼できるデバイスを消す) |
操作ログには、使い始め・やめた(手段ごと)・予備のコードの使用・運営による解除を残します。