@o3co/create-auth-provider 0.16.0 → 0.17.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/README.ja.md +66 -21
  2. package/README.md +76 -31
  3. package/dist/index.d.mts +33 -10
  4. package/dist/index.d.mts.map +1 -1
  5. package/dist/index.mjs +118 -59
  6. package/dist/internal/template-filter.d.mts +7 -10
  7. package/dist/internal/template-filter.d.mts.map +1 -1
  8. package/dist/internal/template-filter.mjs +7 -10
  9. package/package.json +6 -4
  10. package/templates/standalone/.dockerignore +15 -2
  11. package/templates/standalone/.env.example +59 -29
  12. package/templates/standalone/Dockerfile +6 -2
  13. package/templates/standalone/README.ja.md +324 -169
  14. package/templates/standalone/README.md +618 -208
  15. package/templates/standalone/config/application.conf +211 -352
  16. package/templates/standalone/config/clients.yaml +2 -0
  17. package/templates/standalone/config/clients.yaml.example +4 -0
  18. package/templates/standalone/config/development.conf +13 -4
  19. package/templates/standalone/config/mailpit.conf +8 -0
  20. package/templates/standalone/config/production.conf +5 -6
  21. package/templates/standalone/config/reference.conf +418 -0
  22. package/templates/standalone/docker-compose.mailpit.yml +36 -0
  23. package/templates/standalone/docker-compose.production.yml +63 -32
  24. package/templates/standalone/docker-compose.yml +13 -3
  25. package/templates/standalone/gitignore +1 -0
  26. package/templates/standalone/package.json +9 -6
  27. package/templates/standalone/src/__tests__/adapters.test.mts +419 -0
  28. package/templates/standalone/src/__tests__/admission-lifecycle.test.mts +87 -0
  29. package/templates/standalone/src/__tests__/all-modules-composition.fixture.mts +287 -198
  30. package/templates/standalone/src/__tests__/all-modules-composition.multi.test.mts +71 -35
  31. package/templates/standalone/src/__tests__/all-modules-composition.test.mts +148 -94
  32. package/templates/standalone/src/__tests__/audit-sink.test.mts +84 -81
  33. package/templates/standalone/src/__tests__/config-notices.test.mts +154 -0
  34. package/templates/standalone/src/__tests__/configPath.test.mts +13 -11
  35. package/templates/standalone/src/__tests__/core-federations.test.mts +249 -0
  36. package/templates/standalone/src/__tests__/core-sections.test.mts +190 -0
  37. package/templates/standalone/src/__tests__/device-code-store-client-module.test.mts +10 -15
  38. package/templates/standalone/src/__tests__/documented-env-overrides.test.mts +970 -288
  39. package/templates/standalone/src/__tests__/federation-grants-composition.test.mts +421 -86
  40. package/templates/standalone/src/__tests__/federation-grants-survive-logout.test.mts +87 -72
  41. package/templates/standalone/src/__tests__/federation-token-refresh-outage.test.mts +16 -8
  42. package/templates/standalone/src/__tests__/federation-types.test.mts +239 -0
  43. package/templates/standalone/src/__tests__/google-session.fixture.mts +97 -93
  44. package/templates/standalone/src/__tests__/host-routes.test.mts +4 -11
  45. package/templates/standalone/src/__tests__/library-reference-resolution.smoke.test.mts +1 -1
  46. package/templates/standalone/src/__tests__/library-references.fixture.mts +188 -0
  47. package/templates/standalone/src/__tests__/lifecycle-pending-close.test.mts +216 -0
  48. package/templates/standalone/src/__tests__/listen.test.mts +6 -13
  49. package/templates/standalone/src/__tests__/logger-projection.test.mts +1 -1
  50. package/templates/standalone/src/__tests__/mail-sender.test.mts +134 -0
  51. package/templates/standalone/src/__tests__/mailpit-compose.test.mts +326 -0
  52. package/templates/standalone/src/__tests__/metrics.test.mts +4 -9
  53. package/templates/standalone/src/__tests__/mfa-routes-budget.test.mts +122 -0
  54. package/templates/standalone/src/__tests__/mfa-switch.redis.test.mts +123 -0
  55. package/templates/standalone/src/__tests__/mfa-switch.test.mts +825 -0
  56. package/templates/standalone/src/__tests__/own-modules.test.mts +1486 -0
  57. package/templates/standalone/src/__tests__/post-logout-redirect.test.mts +10 -14
  58. package/templates/standalone/src/__tests__/redis-connection-error-log.test.mts +1 -5
  59. package/templates/standalone/src/__tests__/redis-rate-limiter-config-survives.test.mts +116 -81
  60. package/templates/standalone/src/__tests__/refresh-token-family-client-module.test.mts +25 -31
  61. package/templates/standalone/src/__tests__/replica-safety.test.mts +265 -146
  62. package/templates/standalone/src/__tests__/router-error-answers.test.mts +4 -7
  63. package/templates/standalone/src/__tests__/scaffold-assets.test.mts +278 -103
  64. package/templates/standalone/src/__tests__/sections.test.mts +480 -0
  65. package/templates/standalone/src/__tests__/session-lifecycle.test.mts +215 -0
  66. package/templates/standalone/src/__tests__/session-logout-invalidates-token.test.mts +69 -61
  67. package/templates/standalone/src/__tests__/session-logout-lifecycle.test.mts +130 -0
  68. package/templates/standalone/src/__tests__/session-requirements.test.mts +382 -0
  69. package/templates/standalone/src/__tests__/shipped-config-notices.test.mts +270 -0
  70. package/templates/standalone/src/__tests__/shutdown.test.mts +163 -96
  71. package/templates/standalone/src/__tests__/smoke.test.mts +176 -258
  72. package/templates/standalone/src/__tests__/store-credential.test.mts +88 -52
  73. package/templates/standalone/src/__tests__/supertest-loopback.test.mts +182 -11
  74. package/templates/standalone/src/__tests__/three-tier-resolution.test.mts +131 -102
  75. package/templates/standalone/src/__tests__/two-phase-config.test.mts +604 -0
  76. package/templates/standalone/src/__tests__/upstream-amr.acceptance.test.mts +382 -0
  77. package/templates/standalone/src/adapters.mts +162 -0
  78. package/templates/standalone/src/app.mts +65 -79
  79. package/templates/standalone/src/bootRefusal.mts +117 -0
  80. package/templates/standalone/src/buildModules.mts +220 -309
  81. package/templates/standalone/src/configPath.mts +449 -29
  82. package/templates/standalone/src/listen.mts +12 -18
  83. package/templates/standalone/src/logger.mts +31 -60
  84. package/templates/standalone/src/metrics.mts +24 -47
  85. package/templates/standalone/src/mfaSwitch.mts +285 -0
  86. package/templates/standalone/src/modules.mts +423 -629
  87. package/templates/standalone/src/rootRenames.mts +96 -0
  88. package/templates/standalone/src/routes.mts +8 -14
  89. package/templates/standalone/src/sections.mts +367 -0
  90. package/templates/standalone/src/shutdown.mts +45 -123
  91. package/templates/standalone/tests/index.test.js +6 -5
  92. package/templates/standalone/tests/package-lock.json +1615 -2223
  93. package/templates/standalone/tests/package.json +2 -2
  94. package/templates/standalone/tests/vitest.config.mjs +26 -0
  95. package/templates/standalone/vitest.config.mts +8 -7
  96. package/templates/standalone/vitest.supertest-loopback.mts +97 -69
  97. package/templates/versions.json +18 -15
  98. package/templates/standalone/src/__tests__/access-token-lifetime-alias.test.mts +0 -135
  99. package/templates/standalone/src/__tests__/google-federation-config-module.test.mts +0 -199
  100. package/templates/standalone/src/__tests__/oidc-federation-config-module.test.mts +0 -138
package/README.ja.md CHANGED
@@ -1,35 +1,49 @@
1
1
  # @o3co/create-auth-provider
2
2
 
3
- 最終更新: 2026-09-24
3
+ 最終更新: 2026-10-03
4
4
 
5
- auth.provider 用の CLI スキャフォルダーです。内包する standalone テンプレートから新しいサーバープロジェクトを生成します。
5
+ auth.provider 用の CLI スキャフォルダーです。内包するテンプレートの 1 つから新しいサーバープロジェクトを生成します。
6
6
 
7
7
  ## 責務と役割
8
8
 
9
- **役割。** リポジトリ内のテンプレート
10
- [`templates/standalone`](../templates/standalone) を、独立した新しいプロジェクトに
11
- 変換する `npx` の入口です。運用者のマシン上で一度だけ実行されます。生成された
9
+ **役割。** リポジトリ内の [`templates/`](../templates) にあるテンプレート
10
+ (それぞれがデプロイ可能なコンポジションルート)の 1 つを、独立した新しい
11
+ プロジェクトに変換する `npx` の入口です。運用者のマシン上で一度だけ実行されます。生成された
12
12
  プロジェクトはこのパッケージを import せず、このパッケージも `packages/*` の
13
13
  ライブラリを一切 import しません。
14
14
 
15
- **担うもの。** プロジェクト名とディレクトリ名の検証、テンプレートのコピー、生成された
15
+ **担うもの。** プロジェクト名・ディレクトリ名・テンプレート名の検証、選んだテンプレートのコピー、生成された
16
16
  `package.json` の書き換え(名前、`workspace:*` → 公開バージョン)、プロジェクトの
17
17
  `pnpm-workspace.yaml` の書き出し、そして一度きりの `pnpm-lock.yaml` の解決。
18
18
 
19
19
  **担わないもの。** 生成されるプロジェクトの中身 — ソース、設定、Dockerfile、
20
- テスト — はテンプレートのものです(このパッケージではなく `templates/standalone` を
21
- 編集してください)。実行時の振る舞いは `@o3co/auth-provider-core` と、テンプレートが
20
+ テスト — はテンプレートのものです(このパッケージではなく `templates/<name>` を
21
+ 編集してください)。どのテンプレートがあり、それぞれ何のためにあるかは
22
+ [コンポジションテンプレートの ADR](../packages/core/docs/adr/2026-09-29-composition-templates.md)
23
+ のものです。実行時の振る舞いは `@o3co/auth-provider-core` と、テンプレートが
22
24
  依存するライブラリのものです。
23
25
 
24
26
  **別パッケージである理由。** `bin` 付きで単独公開されるため、provider をインストール
25
27
  せずに `npx` で実行できます。公開 tarball にはモノレポが含まれないので、このパッケージは
26
- テンプレートのコピーと、固定するライブラリのバージョンを自前で持ちます
28
+ すべてのテンプレートのコピーと、それらが固定するライブラリのバージョンを自前で持ちます
27
29
  ([テンプレートの同梱方法](#テンプレートの同梱方法))。
28
30
 
29
31
  ## 使い方
30
32
 
31
33
  ```bash
32
- npx @o3co/create-auth-provider <project-name> [--dir <dir-name>] [--no-lockfile]
34
+ npx @o3co/create-auth-provider <project-name> [--template <name>] [--dir <dir-name>] [--no-lockfile] [--no-mfa]
35
+ ```
36
+
37
+ `--template` はコピーするテンプレートを指定します(デフォルトは `standalone`)。
38
+ テンプレートは [`templates/`](../templates) 配下で `package.json` を持つディレクトリ
39
+ (シンボリックリンクでも、ドットで始まる名前でもないもの)で、それ以外の名前は、
40
+ 使えるテンプレートの一覧を示して拒否されます。各テンプレートが
41
+ 何のためにあるかはそれぞれの README に、テンプレートの分け方 — 機能ごとではなく
42
+ コンポジションの形ごとに 1 つ — は
43
+ [コンポジションテンプレートの ADR](../packages/core/docs/adr/2026-09-29-composition-templates.md) にあります。
44
+
45
+ ```bash
46
+ npx @o3co/create-auth-provider my-auth-server --template standalone
33
47
  ```
34
48
 
35
49
  `<project-name>` はスコープ付き npm 名 (`@scope/pkg`) とスコープなしの名前 (`pkg`) のどちらでも指定できます。
@@ -59,6 +73,20 @@ cd provider
59
73
 
60
74
  `--no-lockfile` を付けると lockfile の生成(下記の手順 7)を省略します。
61
75
 
76
+ スキャフォールドは、すべてのパスワードログインに第二要素を求めます: テンプレートの
77
+ MFA スイッチ `mfaMode`(`MFA_MODE`)のデフォルトは `required` です。development では
78
+ ほかに何も要りません。それ以外では、MFA に要るもの(2 つのストアを Redis に、鍵、
79
+ SMTP リレー)を設定するか、`MFA_MODE=off` で MFA を無効にするまで、
80
+ プロジェクトの起動は拒否されます。MFA ページのデフォルトは `/mfa` で、プロジェクトが
81
+ そこでページを用意しなければ、すべてのパスワードログインはそこで止まります。
82
+ スイッチが何をインストールし、何を要するかはテンプレートの README にあります。
83
+
84
+ `--no-mfa` を付けると、代わりにスイッチをスキャフォールド上で off と書きます。
85
+ `config/application.conf` の末尾に `mfaMode = "off"` と
86
+ `mfaMode = ${?MFA_MODE}` をこの順に追記するので、後のテンプレートがデフォルトを
87
+ 変えてもプロジェクトは off のままで、`MFA_MODE` を設定すれば作り直さずに MFA を
88
+ 有効にできます。MFA パッケージは依存に残り、ほかには何も削除・変更しません。
89
+
62
90
  生成されるプロジェクトは pnpm のプロジェクトです。`Dockerfile` は
63
91
  `pnpm install --frozen-lockfile` でインストールし、ビルドの許可リストは
64
92
  `pnpm-workspace.yaml` にあります。
@@ -67,7 +95,7 @@ CLI は最後のメッセージで次に `pnpm run debug` を実行するよう
67
95
  スキャフォールドのデフォルトのままでは、これは起動しません: `.env` ファイルは
68
96
  読まず(読むのは compose ファイルだけ)、シェルの環境変数から issuer
69
97
  (`OAUTH_JWT_ISSUER`)、署名鍵のペア、セッションシークレット、ユーザーサービスの
70
- 2 つの URL(`CLIENT_USER_AUTHENTICATE_URL`、`CLIENT_USER_AUTHENTICATE_BY_TOKEN_URL`)、
98
+ 2 つの URL(`REPOSITORIES_USER_HTTP_AUTHENTICATE_URL`、`REPOSITORIES_USER_HTTP_AUTHENTICATE_BY_TOKEN_URL`)、
71
99
  そして `localhost:6379` の Redis を必要とします。コマンドは、プロジェクトに含まれる
72
100
  テンプレートの README の[使い方](../templates/standalone/README.ja.md#使い方)にあります。
73
101
 
@@ -75,8 +103,8 @@ CLI は最後のメッセージで次に `pnpm run debug` を実行するよう
75
103
 
76
104
  1. `<project-name>` を検証する([バリデーションルール](#バリデーションルール) 参照)。
77
105
  2. 生成先ディレクトリ名を決定する: `--dir <value>` が指定されていればその値、そうでなければスコープ付き名のパッケージ部分、最終的には入力値そのもの。
78
- 3. 生成先ディレクトリを `<cwd>/<dir-name>` として解決し、すでに存在する場合はエラーで終了する。
79
- 4. 内包するテンプレートを生成先ディレクトリにコピーし(`node_modules/` と `dist/` は除外)、`.gitignore` を復元する(npm は公開パッケージから `.gitignore` という名前のファイルを落とすため、tarball には `gitignore` として入っている)。
106
+ 3. `--template`(デフォルト `standalone`)が内包するテンプレートの名前であることを確かめ、生成先ディレクトリを `<cwd>/<dir-name>` として解決し、すでに存在する場合はエラーで終了する。
107
+ 4. 指定したテンプレートを生成先ディレクトリにコピーし(`node_modules/` と `dist/` は除外)、`.gitignore` を復元する(npm は公開パッケージから `.gitignore` という名前のファイルを落とすため、tarball には `gitignore` として入っている)。`--no-mfa` のときは、その `config/application.conf` に MFA スイッチの行を追記する。
80
108
  5. 生成されたディレクトリの `package.json` を書き換える:
81
109
  - `name` を `<project-name>` そのままに設定する(スコープを保持)。
82
110
  - `"private": true` は意図的に残す: スキャフォールドされた ID プロバイダーが誤って公開できてはならないため。本当に公開するつもりなら自分でこのフィールドを削除する。
@@ -100,12 +128,21 @@ lockfile をテンプレートに同梱することはできません: 手順 5
100
128
 
101
129
  パッケージの `prebuild` と `prepack` スクリプトが
102
130
  [`scripts/copy-templates.mjs`](scripts/copy-templates.mjs) を実行し、
103
- `templates/standalone` を `create-app/templates/standalone` にコピーし(git 管理外。
104
- `node_modules/` と `dist/` は除外)、`create-app/templates/versions.json` —
131
+ すべてのテンプレートを `create-app/templates/<name>` にコピーし(git 管理外。
132
+ `node_modules/` と `dist/` は除外。コピー先は丸ごと作り直すので、リポジトリから
133
+ 消したテンプレートは残らない)、`create-app/templates/versions.json` —
105
134
  公開される `@o3co/auth-provider-*` 全パッケージの現在のバージョン — を書き出します。
106
135
  tarball には両方が入り(`files: ["dist", "templates"]`)、`scaffold()` はそこから
107
136
  読みます。したがってスキャフォールドされるのは、このパッケージをビルドした時点の
108
137
  テンプレートで、同じビルド時点のライブラリバージョンに固定されます。
138
+ テンプレートとは何か — `templates/` 配下のディレクトリで、シンボリックリンクでも
139
+ ドットで始まる名前でもなく、`package.json` を持ち、小文字のケバブケースで名付けられた
140
+ もの — は [`scripts/templates.mjs`](scripts/templates.mjs) の 1 か所で定義され、
141
+ コピーと CI の全テンプレートのビルドの両方がそれを使います。スキャフォルダーの
142
+ `availableTemplates()` は同じ規則でコピーを読みます。
143
+ [`published-package.test.mts`](src/__tests__/published-package.test.mts) は
144
+ パッケージを pack し、tarball がリポジトリのテンプレートをすべて含み、それぞれが
145
+ そこからスキャフォールドできることを確かめます。
109
146
 
110
147
  CI は [`scripts/check-versions-json.mjs`](scripts/check-versions-json.mjs) を
111
148
  実行します。`packages/` 配下の公開パッケージが `copy-templates.mjs` のバージョン
@@ -124,15 +161,20 @@ CI は [`scripts/check-versions-json.mjs`](scripts/check-versions-json.mjs) を
124
161
 
125
162
  `--dir <value>` はスコープなしのパターンと同じ制約です。
126
163
 
164
+ `--template <name>` は内包するテンプレートの名前と完全に一致する必要があります。パスは
165
+ テンプレート名になりません。テンプレート名は小文字のケバブケース
166
+ `^[a-z0-9]+(?:-[a-z0-9]+)*$`(英小文字と数字の並びを 1 つのハイフンでつなぐ)で、
167
+ それ以外の名前のテンプレートはビルドが拒否します。
168
+
127
169
  ## 既知の制約
128
170
 
129
- - 内包されているテンプレートの `README.md` / `README.ja.md` の見出しは上流の `@o3co/auth-provider-standalone` のままです。スコープ付きでプロジェクトを生成した場合、この見出しは `package.json` の名前と一致しません。必要に応じて手動で修正してください。
171
+ - 内包されている各テンプレートの `README.md` / `README.ja.md` の見出しは上流の名前(たとえば `@o3co/auth-provider-standalone`)のままです。スコープ付きでプロジェクトを生成した場合、この見出しは `package.json` の名前と一致しません。必要に応じて手動で修正してください。
130
172
  - テンプレートの README は、このリポジトリ内を相対パス(`../../docs/…`、`../../packages/…`)でリンクしています。これはモノレポ内では解決しますが、横に `docs/` も `packages/` もないスキャフォールド先のプロジェクトでは解決しません。GitHub 上で参照してください。
131
173
 
132
174
  ## 生成される構造
133
175
 
134
- 生成されるプロジェクトは [`templates/standalone`](../templates/standalone) の
135
- 完全なコピー(`node_modules/` と `dist/` を除く)に、`pnpm-workspace.yaml`(手順 6)と、
176
+ 生成されるプロジェクトは、選んだテンプレート(たとえば
177
+ [`templates/standalone`](../templates/standalone))の完全なコピー(`node_modules/` と `dist/` を除く)に、`pnpm-workspace.yaml`(手順 6)と、
136
178
  `--no-lockfile` を指定せず lockfile の手順が失敗しなかった場合は `pnpm-lock.yaml`(手順 7)を加えたものです。どの
137
179
  ファイルがモジュール構成で、どれがホストプロセスで、スキャフォールドが何を
138
180
  持つかは、テンプレートの README に記述されています。
@@ -142,12 +184,15 @@ CI は [`scripts/check-versions-json.mjs`](scripts/check-versions-json.mjs) を
142
184
  モジュールは CLI を構成する関数を export しています。シグネチャは
143
185
  [`src/index.mts`](src/index.mts) にあります。
144
186
 
145
- - `scaffold(targetDir, projectName)` — 手順 4〜6。内包するテンプレートがない場合、または `workspace:*` 依存が `versions.json` に見つからない場合は例外を投げる。
187
+ - `scaffold(targetDir, projectName, template?, options?)` — 手順 4〜6。`template`(デフォルトは `DEFAULT_TEMPLATE` = `"standalone"`)からコピーする。`options.noMfa` は `--no-mfa` にあたる。コピーできないときは何も書かずに `templateRefusal` のメッセージで例外を投げる。`workspace:*` 依存が `versions.json` に見つからない場合も例外を投げる。
188
+ - `availableTemplates(templatesRoot?)` — 内包するテンプレートの名前(ソート済み)。
189
+ - `templateRefusal(template, templates)` — `template` からスキャフォールドできない理由(1 つも内包されていない、またはそのどれでもない)。できるときは `undefined`。
146
190
  - `generateLockfile(targetDir)` — 手順 7。例外を投げず、`{ ok: true, command }` か `{ ok: false, reason }` を返す。
147
- - `main()` — CLI 本体。`process.argv` を読み、不正な引数や既存のディレクトリに対しては非ゼロで終了する。
191
+ - `main()` — CLI 本体。`process.argv` を読み、不正な引数、未知のテンプレート、既存のディレクトリに対しては非ゼロで終了する。
148
192
  - `isValidProjectName(name)` / `isValidDirName(name)` — [バリデーションルール](#バリデーションルール)。
149
193
 
150
194
  ## 関連
151
195
 
152
- - [`@o3co/auth-provider-standalone`](../templates/standalone) — このツールが生成元とするテンプレート
196
+ - [`templates/`](../templates) — このツールが生成元とするテンプレート。[`@o3co/auth-provider-standalone`](../templates/standalone) がデフォルト
197
+ - [コンポジションテンプレートの ADR](../packages/core/docs/adr/2026-09-29-composition-templates.md) — テンプレートをこう分ける理由
153
198
  - [`@o3co/auth-provider-core`](../packages/core) — コアアプリケーションファクトリ
package/README.md CHANGED
@@ -1,35 +1,49 @@
1
1
  # @o3co/create-auth-provider
2
2
 
3
- Last updated: 2026-09-24
3
+ Last updated: 2026-10-03
4
4
 
5
- CLI scaffolder for auth.provider. Generates a new standalone server project from the built-in template.
5
+ CLI scaffolder for auth.provider. Generates a new server project from one of the built-in templates.
6
6
 
7
7
  ## Responsibility
8
8
 
9
- **Role.** The `npx` entry point that turns the in-repo template
10
- [`templates/standalone`](../templates/standalone) into a new, independent
11
- project. It runs once, on the operator's machine; nothing in the generated
9
+ **Role.** The `npx` entry point that turns one of the in-repo templates under
10
+ [`templates/`](../templates) — each a deployable composition root — into a new,
11
+ independent project. It runs once, on the operator's machine; nothing in the generated
12
12
  project imports it, and it imports none of the `packages/*` libraries.
13
13
 
14
- **Owns.** Project-name and directory validation, copying the template,
15
- rewriting the generated `package.json` (name, `workspace:*` → published
16
- versions), writing the project's `pnpm-workspace.yaml`, and the one-time
17
- `pnpm-lock.yaml` resolution.
14
+ **Owns.** Project-name, directory and template validation, copying the
15
+ chosen template, rewriting the generated `package.json` (name, `workspace:*`
16
+ → published versions), writing the project's `pnpm-workspace.yaml`, and the
17
+ one-time `pnpm-lock.yaml` resolution.
18
18
 
19
19
  **Does not own.** The content of the generated project — source, config,
20
- Dockerfile, tests — which is the template's (edit `templates/standalone`, not
21
- this package). Runtime behaviour belongs to `@o3co/auth-provider-core` and the
22
- libraries the template depends on.
20
+ Dockerfile, tests — which is the template's (edit `templates/<name>`, not
21
+ this package); and which templates there are, and what each is for — the
22
+ [composition templates ADR](../packages/core/docs/adr/2026-09-29-composition-templates.md).
23
+ Runtime behaviour belongs to `@o3co/auth-provider-core` and the libraries the
24
+ template depends on.
23
25
 
24
26
  **Why a separate package.** It is published on its own with a `bin`, so it can
25
27
  be run with `npx` without installing the provider. The monorepo is not in the
26
- published tarball, so the package carries its own copy of the template and of
27
- the library versions it pins ([How the template is bundled](#how-the-template-is-bundled)).
28
+ published tarball, so the package carries its own copy of every template and
29
+ of the library versions they pin ([How the templates are bundled](#how-the-templates-are-bundled)).
28
30
 
29
31
  ## Usage
30
32
 
31
33
  ```bash
32
- npx @o3co/create-auth-provider <project-name> [--dir <dir-name>] [--no-lockfile]
34
+ npx @o3co/create-auth-provider <project-name> [--template <name>] [--dir <dir-name>] [--no-lockfile] [--no-mfa]
35
+ ```
36
+
37
+ `--template` names the template to copy, `standalone` by default. The
38
+ templates are the directories under [`templates/`](../templates) that hold a
39
+ `package.json` (not a symbolic link, not dot-named), and the CLI refuses any
40
+ other name, listing the ones it has.
41
+ What each template is for is in its own README; how the set is drawn — a
42
+ template per composition shape, not per feature — is in the
43
+ [composition templates ADR](../packages/core/docs/adr/2026-09-29-composition-templates.md).
44
+
45
+ ```bash
46
+ npx @o3co/create-auth-provider my-auth-server --template standalone
33
47
  ```
34
48
 
35
49
  `<project-name>` may be either a scoped npm name (`@scope/pkg`) or an unscoped name (`pkg`).
@@ -59,6 +73,20 @@ cd provider
59
73
 
60
74
  `--no-lockfile` skips the lockfile step (step 7 below).
61
75
 
76
+ The scaffold requires a second factor at every password login: the
77
+ template's MFA switch, `mfaMode` (`MFA_MODE`), defaults to `required`. In
78
+ development that needs nothing more; outside it, the project's boot is
79
+ refused until it sets what MFA needs (its two stores on Redis, a key, an SMTP
80
+ relay) or turns MFA off with `MFA_MODE=off`. The MFA page defaults to `/mfa`,
81
+ which the project must serve, or every password login stops there. What the
82
+ switch installs and needs is in the template's README.
83
+
84
+ `--no-mfa` writes the switch off in the scaffold instead:
85
+ it appends `mfaMode = "off"` and then `mfaMode = ${?MFA_MODE}` to
86
+ `config/application.conf`, so the project stays off whatever default a later
87
+ template ships, and `MFA_MODE` still turns MFA on without re-scaffolding. The
88
+ MFA package stays a dependency; nothing else is removed or changed.
89
+
62
90
  The generated project is a pnpm project: its `Dockerfile` installs with
63
91
  `pnpm install --frozen-lockfile`, and its build allowlist lives in
64
92
  `pnpm-workspace.yaml`.
@@ -67,8 +95,8 @@ The CLI's closing message suggests `pnpm run debug` next. On the scaffold's
67
95
  defaults alone that does not boot: it reads no `.env` file (only the compose
68
96
  files do), and needs from the shell's environment an issuer
69
97
  (`OAUTH_JWT_ISSUER`), a signing key pair, a session secret, the two URLs of
70
- your user service (`CLIENT_USER_AUTHENTICATE_URL`,
71
- `CLIENT_USER_AUTHENTICATE_BY_TOKEN_URL`) and a Redis on `localhost:6379`. The
98
+ your user service (`REPOSITORIES_USER_HTTP_AUTHENTICATE_URL`,
99
+ `REPOSITORIES_USER_HTTP_AUTHENTICATE_BY_TOKEN_URL`) and a Redis on `localhost:6379`. The
72
100
  template's README, which the project carries, gives the commands under
73
101
  [Usage](../templates/standalone/README.md#usage).
74
102
 
@@ -76,8 +104,8 @@ template's README, which the project carries, gives the commands under
76
104
 
77
105
  1. Validates `<project-name>` (see [Validation Rules](#validation-rules)).
78
106
  2. Derives the target directory name: `--dir <value>` if given, else the unscoped part of a scoped name, else the name itself.
79
- 3. Resolves the target directory as `<cwd>/<dir-name>`, and errors if it already exists.
80
- 4. Copies the bundled template to the target directory, excluding `node_modules/` and `dist/`, and restores its `.gitignore` (the tarball carries it as `gitignore`, because npm drops a file named `.gitignore` from a published package).
107
+ 3. Checks that `--template` (default `standalone`) names a bundled template, and resolves the target directory as `<cwd>/<dir-name>`, erroring if it already exists.
108
+ 4. Copies the named template to the target directory, excluding `node_modules/` and `dist/`, and restores its `.gitignore` (the tarball carries it as `gitignore`, because npm drops a file named `.gitignore` from a published package). With `--no-mfa`, appends the MFA switch lines to its `config/application.conf`.
81
109
  5. Rewrites `package.json` in the generated directory:
82
110
  - Sets `name` to `<project-name>` verbatim (scope-preserving).
83
111
  - Keeps `"private": true` on purpose: a scaffolded identity provider should not be publishable by accident. Remove the field yourself if you really intend to publish.
@@ -96,16 +124,25 @@ it is resolved once, here, against the rewritten `package.json`. **Commit it**
96
124
  it is what makes `docker build` reproducible. If step 7 failed or was skipped,
97
125
  run `pnpm install` in the project once and commit the result.
98
126
 
99
- ## How the template is bundled
127
+ ## How the templates are bundled
100
128
 
101
129
  The package's `prebuild` and `prepack` scripts run
102
130
  [`scripts/copy-templates.mjs`](scripts/copy-templates.mjs), which copies
103
- `templates/standalone` into `create-app/templates/standalone` (git-ignored;
104
- `node_modules/` and `dist/` excluded) and writes `create-app/templates/versions.json`,
105
- the current version of every published `@o3co/auth-provider-*` package. The
106
- tarball ships both (`files: ["dist", "templates"]`), and `scaffold()` reads
107
- them from there. A scaffold is therefore the template as it was when this
108
- package was built, pinned to the library versions of that same build.
131
+ every template into `create-app/templates/<name>` (git-ignored; `node_modules/`
132
+ and `dist/` excluded; the destination is rebuilt whole, so a template removed
133
+ from the repository does not linger) and writes `create-app/templates/versions.json`, the current version
134
+ of every published `@o3co/auth-provider-*` package. The tarball ships both
135
+ (`files: ["dist", "templates"]`), and `scaffold()` reads them from there. A
136
+ scaffold is therefore the template as it was when this package was built,
137
+ pinned to the library versions of that same build.
138
+ What a template is — a directory under `templates/`, not a symbolic link and
139
+ not dot-named, holding a `package.json`, named in lowercase kebab-case — is
140
+ defined once, in [`scripts/templates.mjs`](scripts/templates.mjs), which the
141
+ copy and CI's build of every template both use; the scaffolder's
142
+ `availableTemplates()` reads the copy by the same rule.
143
+ [`published-package.test.mts`](src/__tests__/published-package.test.mts)
144
+ packs the package and holds the tarball to the repository: it must ship every
145
+ template, and each must scaffold from it.
109
146
 
110
147
  CI runs [`scripts/check-versions-json.mjs`](scripts/check-versions-json.mjs),
111
148
  which fails when a published package under `packages/` is missing from
@@ -124,14 +161,19 @@ Both forms must be non-empty, not `.` or `..`, and ≤ 214 characters.
124
161
 
125
162
  `--dir <value>` must match the unscoped pattern above (same constraints).
126
163
 
164
+ `--template <name>` must be the name of a bundled template, exactly; a path is
165
+ never one. A template's name is lowercase kebab-case,
166
+ `^[a-z0-9]+(?:-[a-z0-9]+)*$` (lowercase letters and digits, in parts joined by
167
+ single hyphens); the build refuses a template named otherwise.
168
+
127
169
  ## Known Limitations
128
170
 
129
- - The bundled template's `README.md` / `README.ja.md` carry the upstream title `@o3co/auth-provider-standalone`. When generating a scoped project, that title will not match your `package.json` name; edit it manually if it matters for your use case.
171
+ - Each bundled template's `README.md` / `README.ja.md` carry its upstream title, such as `@o3co/auth-provider-standalone`. When generating a scoped project, that title will not match your `package.json` name; edit it manually if it matters for your use case.
130
172
  - The template's README links into this repository with relative paths (`../../docs/…`, `../../packages/…`). Those resolve in the monorepo and not in a scaffolded project, where there is no `docs/` or `packages/` beside it; read them on GitHub instead.
131
173
 
132
174
  ## Generated Structure
133
175
 
134
- The generated project is a complete copy of
176
+ The generated project is a complete copy of the chosen template, such as
135
177
  [`templates/standalone`](../templates/standalone) (without `node_modules/` and
136
178
  `dist/`) plus `pnpm-workspace.yaml` (step 6) and, unless `--no-lockfile` was
137
179
  given or the lockfile step failed, `pnpm-lock.yaml` (step 7). The template's README describes its layout — which file is
@@ -142,12 +184,15 @@ the composition, which is the host process, and what the scaffold owns.
142
184
  The module exports the functions the CLI is built from; their signatures are
143
185
  in [`src/index.mts`](src/index.mts).
144
186
 
145
- - `scaffold(targetDir, projectName)` — steps 4–6. Throws if the bundled template is missing or a `workspace:*` dependency has no entry in `versions.json`.
187
+ - `scaffold(targetDir, projectName, template?, options?)` — steps 4–6, from `template` (default `DEFAULT_TEMPLATE`, `"standalone"`); `options.noMfa` is `--no-mfa`. Throws, before writing anything, with `templateRefusal`'s message; and throws if a `workspace:*` dependency has no entry in `versions.json`.
188
+ - `availableTemplates(templatesRoot?)` — the bundled templates' names, sorted.
189
+ - `templateRefusal(template, templates)` — why `template` cannot be scaffolded (none bundled, or not one of them), or `undefined`.
146
190
  - `generateLockfile(targetDir)` — step 7. Returns `{ ok: true, command }` or `{ ok: false, reason }` rather than throwing.
147
- - `main()` — the CLI: reads `process.argv`, and exits non-zero on an invalid argument or an existing directory.
191
+ - `main()` — the CLI: reads `process.argv`, and exits non-zero on an invalid argument, an unknown template or an existing directory.
148
192
  - `isValidProjectName(name)` / `isValidDirName(name)` — the [Validation Rules](#validation-rules).
149
193
 
150
194
  ## See Also
151
195
 
152
- - [`@o3co/auth-provider-standalone`](../templates/standalone) — The template this tool generates from
196
+ - [`templates/`](../templates) — The templates this tool generates from; [`@o3co/auth-provider-standalone`](../templates/standalone) is the default
197
+ - [Composition templates ADR](../packages/core/docs/adr/2026-09-29-composition-templates.md) — Why the templates are split as they are
153
198
  - [`@o3co/auth-provider-core`](../packages/core) — Core application factory
package/dist/index.d.mts CHANGED
@@ -1,6 +1,32 @@
1
+ /** The template a scaffold is made from when none is named. */
2
+ export declare const DEFAULT_TEMPLATE = "standalone";
3
+ /**
4
+ * The templates under `templatesRoot`: every directory — not a symbolic link,
5
+ * not dot-named — holding a `package.json`, sorted; none when the directory is
6
+ * missing. This is the rule `scripts/templates.mjs` copies templates by, read
7
+ * here from the copy, where `versions.json` sits beside them. A name is only
8
+ * ever looked up in this list, so nothing outside the templates directory can
9
+ * be named as a template.
10
+ */
11
+ export declare const availableTemplates: (templatesRoot?: string) => string[];
12
+ /**
13
+ * Why `template` cannot be scaffolded from `templates`, or `undefined` when it
14
+ * can: none is bundled (a checkout that never ran the prebuild script), or it
15
+ * is not one of them.
16
+ */
17
+ export declare const templateRefusal: (template: string, templates: readonly string[]) => string | undefined;
1
18
  export declare const isValidProjectName: (name: string) => boolean;
2
19
  export declare const isValidDirName: (name: string) => boolean;
3
- export declare const scaffold: (targetDir: string, projectName: string) => void;
20
+ /** What `scaffold()` changes in the copy beyond the name and the versions. */
21
+ export interface ScaffoldOptions {
22
+ /**
23
+ * Write the template's MFA switch off (`MFA_OFF_LINES`), so the scaffold
24
+ * stays off whatever default a later template ships; `MFA_MODE` still
25
+ * turns MFA on.
26
+ */
27
+ readonly noMfa?: boolean;
28
+ }
29
+ export declare const scaffold: (targetDir: string, projectName: string, template?: string, options?: ScaffoldOptions) => void;
4
30
  /** Outcome of the scaffold-time lockfile generation. */
5
31
  export type LockfileResult = {
6
32
  readonly ok: true;
@@ -10,15 +36,12 @@ export type LockfileResult = {
10
36
  readonly reason: string;
11
37
  };
12
38
  /**
13
- * Resolve the scaffolded project's dependency graph into `pnpm-lock.yaml`.
14
- *
15
- * This is what makes the template's `pnpm install --frozen-lockfile` build
16
- * possible: the lockfile cannot be shipped with the template, because the
17
- * dependency set it would pin does not exist until `scaffold` has replaced
18
- * every `workspace:*` with a published version (#289). It is therefore
19
- * resolved once, here, against the rewritten `package.json`. Best-effort by
20
- * design — it needs a package manager and a reachable registry — so failure
21
- * is a result, not a throw.
39
+ * Resolve the scaffolded project's dependency graph into `pnpm-lock.yaml`,
40
+ * which the template's `pnpm install --frozen-lockfile` build needs. The
41
+ * lockfile cannot ship with the template: the dependency set it pins exists
42
+ * only after `scaffold` has replaced every `workspace:*`. Best-effort (it needs
43
+ * a package manager and a reachable registry), so failure is a result, not a
44
+ * throw.
22
45
  */
23
46
  export declare const generateLockfile: (targetDir: string) => LockfileResult;
24
47
  export declare const main: () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.mts"],"names":[],"mappings":"AAoCA,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,KAAG,OAIjD,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,KAAG,OAI7C,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,WAAW,MAAM,EAAE,aAAa,MAAM,KAAG,IAyEjE,CAAC;AAEF,wDAAwD;AACxD,MAAM,MAAM,cAAc,GACvB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAqBnD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,GAAI,WAAW,MAAM,KAAG,cA6BpD,CAAC;AA0DF,eAAO,MAAM,IAAI,QAAO,IAmEvB,CAAC"}
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.mts"],"names":[],"mappings":"AAgCA,+DAA+D;AAC/D,eAAO,MAAM,gBAAgB,eAAe,CAAC;AAE7C;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,GAAI,gBAAe,MAAuB,KAAG,MAAM,EAWjF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAC3B,UAAU,MAAM,EAChB,WAAW,SAAS,MAAM,EAAE,KAC1B,MAAM,GAAG,SAQX,CAAC;AAcF,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,KAAG,OAIjD,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,KAAG,OAI7C,CAAC;AAEF,8EAA8E;AAC9E,MAAM,WAAW,eAAe;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CACzB;AAeD,eAAO,MAAM,QAAQ,GACpB,WAAW,MAAM,EACjB,aAAa,MAAM,EACnB,WAAU,MAAyB,EACnC,UAAS,eAAoB,KAC3B,IAwEF,CAAC;AAEF,wDAAwD;AACxD,MAAM,MAAM,cAAc,GACvB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAkBnD;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,GAAI,WAAW,MAAM,KAAG,cA6BpD,CAAC;AA4EF,eAAO,MAAM,IAAI,QAAO,IA6EvB,CAAC"}