@o3co/create-auth-provider 0.4.1
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.
- package/README.ja.md +107 -0
- package/README.md +107 -0
- package/dist/__tests__/index.test.d.mts +2 -0
- package/dist/__tests__/index.test.d.mts.map +1 -0
- package/dist/__tests__/index.test.mjs +226 -0
- package/dist/cli.d.mts +3 -0
- package/dist/cli.d.mts.map +1 -0
- package/dist/cli.mjs +18 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +166 -0
- package/package.json +28 -0
- package/templates/standalone/.dockerignore +9 -0
- package/templates/standalone/.env.example +21 -0
- package/templates/standalone/Dockerfile +58 -0
- package/templates/standalone/Makefile +12 -0
- package/templates/standalone/README.ja.md +205 -0
- package/templates/standalone/README.md +205 -0
- package/templates/standalone/config/application.conf +132 -0
- package/templates/standalone/config/clients.yaml +2 -0
- package/templates/standalone/config/clients.yaml.example +14 -0
- package/templates/standalone/config/development.conf +8 -0
- package/templates/standalone/config/production.conf +10 -0
- package/templates/standalone/docker-compose.test.yml +16 -0
- package/templates/standalone/docker-compose.yml +26 -0
- package/templates/standalone/package.json +45 -0
- package/templates/standalone/src/__tests__/configPath.test.mts +50 -0
- package/templates/standalone/src/__tests__/smoke.test.mts +204 -0
- package/templates/standalone/src/app.mts +201 -0
- package/templates/standalone/src/configPath.mts +37 -0
- package/templates/standalone/src/logger.mts +18 -0
- package/templates/standalone/tests/Makefile +5 -0
- package/templates/standalone/tests/did-grant.test.js +193 -0
- package/templates/standalone/tests/index.test.js +218 -0
- package/templates/standalone/tests/package-lock.json +2225 -0
- package/templates/standalone/tests/package.json +17 -0
- package/templates/standalone/tsconfig.json +12 -0
- package/templates/standalone/vitest.config.mts +7 -0
- package/templates/versions.json +9 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Required
|
|
2
|
+
OAUTH_JWT_SECRET=
|
|
3
|
+
SESSION_SECRET=
|
|
4
|
+
|
|
5
|
+
# User repository
|
|
6
|
+
CLIENT_USER_AUTHENTICATE_URL=
|
|
7
|
+
CLIENT_USER_AUTHENTICATE_BY_TOKEN_URL=
|
|
8
|
+
|
|
9
|
+
# Redis (code repository + session storage)
|
|
10
|
+
# Use redis://redis:6379 when running via docker compose
|
|
11
|
+
CLIENT_CODE_ENDPOINT_URI=redis://redis:6379
|
|
12
|
+
SESSION_STORAGE_REDIS_URL=redis://redis:6379
|
|
13
|
+
|
|
14
|
+
# Development (override for local HTTP)
|
|
15
|
+
SESSION_SECURE=false
|
|
16
|
+
|
|
17
|
+
# Optional
|
|
18
|
+
# HTTP_PORT=3000
|
|
19
|
+
# OAUTH_JWT_ISSUER=
|
|
20
|
+
# OAUTH_ACCESS_TOKEN_EXPIRES_IN=3600
|
|
21
|
+
# OAUTH_REFRESH_TOKEN_EXPIRES_IN=86400
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
FROM node:24-alpine AS node-base
|
|
2
|
+
|
|
3
|
+
ENV HOME=/home/node
|
|
4
|
+
|
|
5
|
+
RUN apk add --no-cache tini \
|
|
6
|
+
&& npm install -g corepack --force \
|
|
7
|
+
&& corepack enable
|
|
8
|
+
|
|
9
|
+
WORKDIR /home/node
|
|
10
|
+
|
|
11
|
+
#############################################
|
|
12
|
+
FROM node-base AS deps
|
|
13
|
+
|
|
14
|
+
COPY package.json ./
|
|
15
|
+
|
|
16
|
+
RUN pnpm install
|
|
17
|
+
|
|
18
|
+
#############################################
|
|
19
|
+
FROM deps AS builder
|
|
20
|
+
|
|
21
|
+
COPY tsconfig.json ./
|
|
22
|
+
COPY config/ ./config/
|
|
23
|
+
COPY src/ ./src/
|
|
24
|
+
|
|
25
|
+
RUN pnpm run build
|
|
26
|
+
|
|
27
|
+
#############################################
|
|
28
|
+
FROM node-base AS runtime
|
|
29
|
+
|
|
30
|
+
ENV NODE_ENV=production
|
|
31
|
+
|
|
32
|
+
COPY --from=deps /home/node/package.json /home/node/pnpm-lock.yaml ./
|
|
33
|
+
|
|
34
|
+
RUN pnpm install --prod \
|
|
35
|
+
&& pnpm store prune
|
|
36
|
+
|
|
37
|
+
COPY --from=builder /home/node/dist/ ./dist/
|
|
38
|
+
COPY --from=builder /home/node/config/ ./config/
|
|
39
|
+
|
|
40
|
+
USER node
|
|
41
|
+
|
|
42
|
+
ENTRYPOINT ["tini", "--"]
|
|
43
|
+
CMD ["node", "dist/app.mjs"]
|
|
44
|
+
|
|
45
|
+
#############################################
|
|
46
|
+
FROM builder AS test
|
|
47
|
+
|
|
48
|
+
COPY vitest.config.mts ./
|
|
49
|
+
COPY tests/ ./tests/
|
|
50
|
+
|
|
51
|
+
CMD ["pnpm", "run", "test", "--", "--passWithNoTests"]
|
|
52
|
+
|
|
53
|
+
#############################################
|
|
54
|
+
FROM builder AS develop
|
|
55
|
+
|
|
56
|
+
USER node
|
|
57
|
+
|
|
58
|
+
CMD ["pnpm", "run", "debug"]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
IMAGE ?= auth-provider
|
|
2
|
+
|
|
3
|
+
.PHONY: dev test build
|
|
4
|
+
|
|
5
|
+
dev:
|
|
6
|
+
docker compose up --build
|
|
7
|
+
|
|
8
|
+
test:
|
|
9
|
+
docker compose -f docker-compose.test.yml up --build --abort-on-container-exit --exit-code-from test
|
|
10
|
+
|
|
11
|
+
build:
|
|
12
|
+
docker build --target runtime -t $(IMAGE) .
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# @o3co/auth-provider-standalone
|
|
2
|
+
|
|
3
|
+
auth.provider のデプロイ可能なサーバーテンプレートです。これは composition root であり、設定を読み込み、モジュールをロードし、Express サーバーを起動します。`@o3co/create-auth-provider` で生成されます。
|
|
4
|
+
|
|
5
|
+
## 使い方
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# 依存関係をインストール
|
|
9
|
+
pnpm install
|
|
10
|
+
|
|
11
|
+
# 環境変数ファイルをコピーして必須値を記入
|
|
12
|
+
cp .env.example .env
|
|
13
|
+
|
|
14
|
+
# 開発モードで起動(ホットリロード)
|
|
15
|
+
pnpm run debug
|
|
16
|
+
|
|
17
|
+
# ビルドして本番モードで起動
|
|
18
|
+
pnpm run build
|
|
19
|
+
pnpm start
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 設定
|
|
23
|
+
|
|
24
|
+
設定は `config/application.conf`(HOCON 形式)から読み込まれます。各値は対応する環境変数で上書きできます。
|
|
25
|
+
|
|
26
|
+
### 環境別コンフィグ overlay
|
|
27
|
+
|
|
28
|
+
`src/app.mts` は 2 層構成でコンフィグを読み込みます:
|
|
29
|
+
|
|
30
|
+
1. **`config/application.conf`** — アプリの基本デフォルト。
|
|
31
|
+
2. **`config/{ENV}.conf`** — 現在の環境の overlay。
|
|
32
|
+
`ENV = CONFIG_ENV || NODE_ENV || "development"` で決まります。
|
|
33
|
+
|
|
34
|
+
overlay の値は `application.conf` を上書きします。scaffold には
|
|
35
|
+
`development.conf` と `production.conf` が同梱されています。別の環境
|
|
36
|
+
(例: `staging`)を追加するときは `config/staging.conf` を作成し、
|
|
37
|
+
`CONFIG_ENV=staging` を設定してください。`{ENV}.conf` が存在しない場合は
|
|
38
|
+
起動時にエラーになります(タイポを silent に素通りさせず fail-fast します)。
|
|
39
|
+
|
|
40
|
+
### HTTP
|
|
41
|
+
|
|
42
|
+
| 変数 | デフォルト | 説明 |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| `HTTP_PORT` | `3000` | サーバーがリッスンするポート |
|
|
45
|
+
| `HTTP_TRUST_PROXY` | `false` | リバースプロキシの背後で動作する場合は `true` に設定 |
|
|
46
|
+
|
|
47
|
+
### OAuth JWT
|
|
48
|
+
|
|
49
|
+
| 変数 | デフォルト | 説明 |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| `OAUTH_JWT_ALGORITHM` | `HS256` | JWT 署名アルゴリズム(例: `RS256`、`ES256`) |
|
|
52
|
+
| `OAUTH_JWT_SECRET` | — | 署名シークレット(HMAC アルゴリズム用) |
|
|
53
|
+
| `OAUTH_JWT_ISSUER` | — | JWT の `iss` クレーム値 |
|
|
54
|
+
| `OAUTH_JWT_KID` | `v0` | JWT ヘッダーに含まれる key ID |
|
|
55
|
+
| `OAUTH_JWT_PRIVATE_KEY` | — | PEM エンコードされた秘密鍵(非対称アルゴリズム用) |
|
|
56
|
+
| `OAUTH_JWT_PRIVATE_KEY_PATH` | — | PEM 秘密鍵ファイルのパス |
|
|
57
|
+
| `OAUTH_JWT_PUBLIC_KEY` | — | PEM エンコードされた公開鍵 |
|
|
58
|
+
| `OAUTH_JWT_PUBLIC_KEY_PATH` | — | PEM 公開鍵ファイルのパス |
|
|
59
|
+
|
|
60
|
+
### トークン有効期限
|
|
61
|
+
|
|
62
|
+
| 変数 | デフォルト | 説明 |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| `OAUTH_ACCESS_TOKEN_EXPIRES_IN` | `3600` | アクセストークンの有効期間(秒) |
|
|
65
|
+
| `OAUTH_REFRESH_TOKEN_EXPIRES_IN` | `86400` | リフレッシュトークンの有効期間(秒) |
|
|
66
|
+
|
|
67
|
+
### グラントタイプ
|
|
68
|
+
|
|
69
|
+
| 変数 | デフォルト | 説明 |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| `OAUTH_GRANTS_SESSION_ENABLED` | `true` | session グラントタイプを有効化 |
|
|
72
|
+
| `OAUTH_GRANTS_AUTHORIZATION_ENABLED` | `true` | authorization code グラントタイプを有効化 |
|
|
73
|
+
| `OAUTH_GRANTS_REFRESH_TOKEN_ENABLED` | `true` | refresh token グラントタイプを有効化 |
|
|
74
|
+
|
|
75
|
+
### Session
|
|
76
|
+
|
|
77
|
+
| 変数 | デフォルト | 説明 |
|
|
78
|
+
|---|---|---|
|
|
79
|
+
| `SESSION_SECRET` | — | **必須。** セッション署名シークレット |
|
|
80
|
+
| `SESSION_MAX_AGE` | `3600000` | セッション Cookie の最大有効期間(ミリ秒) |
|
|
81
|
+
| `SESSION_SECURE` | `true` | セッション Cookie に `Secure` フラグを設定 |
|
|
82
|
+
| `SESSION_SAME_SITE` | `lax` | `SameSite` 属性(`lax`、`strict`、`none`) |
|
|
83
|
+
| `SESSION_DOMAIN` | — | Cookie ドメイン(デフォルト未設定) |
|
|
84
|
+
| `SESSION_STORAGE_TYPE` | `redis` | セッションストアのバックエンド: `redis` または `memory` |
|
|
85
|
+
| `SESSION_STORAGE_REDIS_URL` | `redis://localhost:6379` | セッションストア用 Redis 接続 URL |
|
|
86
|
+
| `SESSION_STORAGE_REDIS_PASSWORD` | — | セッションストア用 Redis パスワード |
|
|
87
|
+
|
|
88
|
+
### Google フェデレーション
|
|
89
|
+
|
|
90
|
+
| 変数 | デフォルト | 説明 |
|
|
91
|
+
|---|---|---|
|
|
92
|
+
| `FEDERATIONS_GOOGLE_ENABLED` | `false` | Google OAuth フェデレーションを有効化 |
|
|
93
|
+
| `FEDERATIONS_GOOGLE_CLIENT_ID` | — | Google OAuth クライアント ID |
|
|
94
|
+
| `FEDERATIONS_GOOGLE_CLIENT_SECRET` | — | Google OAuth クライアントシークレット |
|
|
95
|
+
| `FEDERATIONS_GOOGLE_CALLBACK_URL` | `http://localhost:3000/session/oauth/federation/google/callback` | Google OAuth コールバック URL |
|
|
96
|
+
|
|
97
|
+
### クライアントリポジトリ
|
|
98
|
+
|
|
99
|
+
| 変数 | デフォルト | 説明 |
|
|
100
|
+
|---|---|---|
|
|
101
|
+
| `CLIENT_TYPE` | `yaml` | クライアントストアのバックエンド: `yaml` |
|
|
102
|
+
| `CLIENT_PATH` | `./config/clients.yaml` | YAML クライアントレジストリのパス |
|
|
103
|
+
|
|
104
|
+
### ユーザーリポジトリ
|
|
105
|
+
|
|
106
|
+
| 変数 | デフォルト | 説明 |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| `CLIENT_USER_TYPE` | `http` | ユーザーリポジトリのバックエンド: `http` |
|
|
109
|
+
| `CLIENT_USER_AUTHENTICATE_URL` | — | パスワード認証用のユーザー認証 URL |
|
|
110
|
+
| `CLIENT_USER_AUTHENTICATE_BY_TOKEN_URL` | — | トークン認証用のユーザー認証 URL |
|
|
111
|
+
| `CLIENT_USER_TIMEOUT` | `5000` | HTTP リクエストタイムアウト(ミリ秒) |
|
|
112
|
+
|
|
113
|
+
### コードリポジトリ
|
|
114
|
+
|
|
115
|
+
| 変数 | デフォルト | 説明 |
|
|
116
|
+
|---|---|---|
|
|
117
|
+
| `CLIENT_CODE_TYPE` | `redis` | 認可コードストアのバックエンド: `redis` |
|
|
118
|
+
| `CLIENT_CODE_ENDPOINT_URI` | — | コードストア用 Redis 接続 URI |
|
|
119
|
+
| `CLIENT_CODE_PASSWORD` | — | コードストア用 Redis パスワード |
|
|
120
|
+
| `CLIENT_CODE_DEFAULT_EXPIRES_IN` | `600` | 認可コードのデフォルト有効期間(秒) |
|
|
121
|
+
|
|
122
|
+
### エンドポイント
|
|
123
|
+
|
|
124
|
+
| 変数 | デフォルト | 説明 |
|
|
125
|
+
|---|---|---|
|
|
126
|
+
| `ENDPOINTS_LOGIN_URL` | — | ログインページの URL(リダイレクト先) |
|
|
127
|
+
| `ENDPOINTS_CLIENT_URL` | — | クライアントアプリケーションの URL |
|
|
128
|
+
| `ENDPOINTS_AUTH_CALLBACK_URL` | — | 認可コールバックの URL |
|
|
129
|
+
|
|
130
|
+
## モジュール合成順序
|
|
131
|
+
|
|
132
|
+
モジュールは以下の順序で登録されます。各モジュールは独自のルートとグラントタイプを登録します。
|
|
133
|
+
|
|
134
|
+
1. **`oauthModule`** — OAuth 2.0 コアエンドポイント: `POST /oauth/token`、`POST /oauth/introspect`、`GET /oauth/authorize`
|
|
135
|
+
2. **`sessionModule`** — セッションベースのログイン・ログアウトおよびフェデレーション: `POST /session/login`、`POST /session/logout`、`GET/POST /session/oauth/federation/*`
|
|
136
|
+
3. **`oauthSessionModule`** — session グラントタイプ(有効なセッションをトークンに交換)
|
|
137
|
+
4. **`oauthAuthorizationModule`** — authorization code および refresh token グラントタイプ
|
|
138
|
+
|
|
139
|
+
## 組み込みルート
|
|
140
|
+
|
|
141
|
+
以下のルートはモジュールシステムの外部で登録されます。
|
|
142
|
+
|
|
143
|
+
| メソッド | パス | 説明 |
|
|
144
|
+
|---|---|---|
|
|
145
|
+
| `GET` | `/health` | ヘルスチェックエンドポイント |
|
|
146
|
+
| `GET` | `/.well-known/jwks.json` | トークン検証用公開鍵セット(JWKS) |
|
|
147
|
+
|
|
148
|
+
## Docker
|
|
149
|
+
|
|
150
|
+
Dockerfile はマルチステージビルドを使用しており、以下のターゲットがあります。
|
|
151
|
+
|
|
152
|
+
| ターゲット | 用途 |
|
|
153
|
+
|---|---|
|
|
154
|
+
| `runtime` | 本番イメージ(コンパイル済み JS のみ) |
|
|
155
|
+
| `develop` | ホットリロード付き開発イメージ(`pnpm run debug`) |
|
|
156
|
+
| `test` | テストランナーイメージ |
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
# 本番イメージをビルド
|
|
160
|
+
make build IMAGE=my-auth-provider
|
|
161
|
+
|
|
162
|
+
# docker compose で起動(開発用)
|
|
163
|
+
make dev
|
|
164
|
+
|
|
165
|
+
# Docker でテストを実行
|
|
166
|
+
make test
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
`docker-compose.yml` は認証サーバーと Redis コンテナをまとめて起動します。環境変数は `.env` で設定してください。
|
|
170
|
+
|
|
171
|
+
## カスタムモジュールの追加
|
|
172
|
+
|
|
173
|
+
カスタムモジュールを追加するには、`src/app.mts` でインポートし、`createApp` に渡す `modules` 配列に追加します。
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
import { myCustomModule } from "./modules/my-custom-module.mjs";
|
|
177
|
+
|
|
178
|
+
const { init, router, grantRegistry } = createApp(express, {
|
|
179
|
+
// ...
|
|
180
|
+
modules: [
|
|
181
|
+
oauthModule({ clientRepository, codeRepository, express }),
|
|
182
|
+
sessionModule({ userRepository, express }),
|
|
183
|
+
oauthSessionModule({ clientRepository }),
|
|
184
|
+
oauthAuthorizationModule({ codeRepository }),
|
|
185
|
+
myCustomModule, // ここに追加
|
|
186
|
+
],
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## npm スクリプト
|
|
191
|
+
|
|
192
|
+
| スクリプト | 説明 |
|
|
193
|
+
|---|---|
|
|
194
|
+
| `pnpm run build` | TypeScript を `dist/` にコンパイル |
|
|
195
|
+
| `pnpm start` | コンパイル済みサーバーを起動 |
|
|
196
|
+
| `pnpm run debug` | `tsx watch` でホットリロード起動(開発モード) |
|
|
197
|
+
| `pnpm test` | Vitest でテストを実行 |
|
|
198
|
+
|
|
199
|
+
## 関連
|
|
200
|
+
|
|
201
|
+
- [`@o3co/auth-provider-core`](../../packages/core) — アプリケーションファクトリと設定スキーマ
|
|
202
|
+
- [`@o3co/auth-provider-oauth`](../../packages/oauth) — OAuth モジュール
|
|
203
|
+
- [`@o3co/auth-provider-session`](../../packages/session) — Session モジュール
|
|
204
|
+
- [`@o3co/auth-provider-foundation`](../../packages/foundation) — 組み込みアダプター登録
|
|
205
|
+
- [`@o3co/create-auth-provider`](../../create-app) — このテンプレートを生成する CLI スキャフォルダー
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# @o3co/auth-provider-standalone
|
|
2
|
+
|
|
3
|
+
Deployable server template for auth.provider. This is the composition root — it reads config, loads modules, and starts the Express server. Generated by `@o3co/create-auth-provider`.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Install dependencies
|
|
9
|
+
pnpm install
|
|
10
|
+
|
|
11
|
+
# Copy environment file and fill in required values
|
|
12
|
+
cp .env.example .env
|
|
13
|
+
|
|
14
|
+
# Start in development mode (hot reload)
|
|
15
|
+
pnpm run debug
|
|
16
|
+
|
|
17
|
+
# Build and start in production mode
|
|
18
|
+
pnpm run build
|
|
19
|
+
pnpm start
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Configuration
|
|
23
|
+
|
|
24
|
+
Configuration is loaded from `config/application.conf` (HOCON format). Each value can be overridden with the corresponding environment variable.
|
|
25
|
+
|
|
26
|
+
### Environment-specific config overlay
|
|
27
|
+
|
|
28
|
+
`src/app.mts` loads configuration in two layers:
|
|
29
|
+
|
|
30
|
+
1. **`config/application.conf`** — base defaults for the app.
|
|
31
|
+
2. **`config/{ENV}.conf`** — overlay for the current environment, where
|
|
32
|
+
`ENV = CONFIG_ENV || NODE_ENV || "development"`.
|
|
33
|
+
|
|
34
|
+
Values in the overlay take precedence over `application.conf`. The scaffold
|
|
35
|
+
ships with `development.conf` and `production.conf`. To add another
|
|
36
|
+
environment (e.g. `staging`), create `config/staging.conf` and set
|
|
37
|
+
`CONFIG_ENV=staging`. A missing `{ENV}.conf` is a boot-time error — typos
|
|
38
|
+
fail fast rather than silently falling back to defaults.
|
|
39
|
+
|
|
40
|
+
### HTTP
|
|
41
|
+
|
|
42
|
+
| Variable | Default | Description |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| `HTTP_PORT` | `3000` | Port the server listens on |
|
|
45
|
+
| `HTTP_TRUST_PROXY` | `false` | Set to `true` when running behind a reverse proxy |
|
|
46
|
+
|
|
47
|
+
### OAuth JWT
|
|
48
|
+
|
|
49
|
+
| Variable | Default | Description |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| `OAUTH_JWT_ALGORITHM` | `HS256` | JWT signing algorithm (e.g. `RS256`, `ES256`) |
|
|
52
|
+
| `OAUTH_JWT_SECRET` | — | Signing secret (HMAC algorithms) |
|
|
53
|
+
| `OAUTH_JWT_ISSUER` | — | JWT `iss` claim value |
|
|
54
|
+
| `OAUTH_JWT_KID` | `v0` | Key ID included in the JWT header |
|
|
55
|
+
| `OAUTH_JWT_PRIVATE_KEY` | — | PEM-encoded private key (asymmetric algorithms) |
|
|
56
|
+
| `OAUTH_JWT_PRIVATE_KEY_PATH` | — | Path to PEM private key file |
|
|
57
|
+
| `OAUTH_JWT_PUBLIC_KEY` | — | PEM-encoded public key |
|
|
58
|
+
| `OAUTH_JWT_PUBLIC_KEY_PATH` | — | Path to PEM public key file |
|
|
59
|
+
|
|
60
|
+
### Token Expiry
|
|
61
|
+
|
|
62
|
+
| Variable | Default | Description |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| `OAUTH_ACCESS_TOKEN_EXPIRES_IN` | `3600` | Access token lifetime in seconds |
|
|
65
|
+
| `OAUTH_REFRESH_TOKEN_EXPIRES_IN` | `86400` | Refresh token lifetime in seconds |
|
|
66
|
+
|
|
67
|
+
### Grant Types
|
|
68
|
+
|
|
69
|
+
| Variable | Default | Description |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| `OAUTH_GRANTS_SESSION_ENABLED` | `true` | Enable the session grant type |
|
|
72
|
+
| `OAUTH_GRANTS_AUTHORIZATION_ENABLED` | `true` | Enable the authorization code grant type |
|
|
73
|
+
| `OAUTH_GRANTS_REFRESH_TOKEN_ENABLED` | `true` | Enable the refresh token grant type |
|
|
74
|
+
|
|
75
|
+
### Session
|
|
76
|
+
|
|
77
|
+
| Variable | Default | Description |
|
|
78
|
+
|---|---|---|
|
|
79
|
+
| `SESSION_SECRET` | — | **Required.** Session signing secret |
|
|
80
|
+
| `SESSION_MAX_AGE` | `3600000` | Session cookie max age in milliseconds |
|
|
81
|
+
| `SESSION_SECURE` | `true` | Set `Secure` flag on session cookie |
|
|
82
|
+
| `SESSION_SAME_SITE` | `lax` | `SameSite` attribute (`lax`, `strict`, `none`) |
|
|
83
|
+
| `SESSION_DOMAIN` | — | Cookie domain (unset by default) |
|
|
84
|
+
| `SESSION_STORAGE_TYPE` | `redis` | Session store backend: `redis` or `memory` |
|
|
85
|
+
| `SESSION_STORAGE_REDIS_URL` | `redis://localhost:6379` | Redis connection URL for session storage |
|
|
86
|
+
| `SESSION_STORAGE_REDIS_PASSWORD` | — | Redis password for session storage |
|
|
87
|
+
|
|
88
|
+
### Google Federation
|
|
89
|
+
|
|
90
|
+
| Variable | Default | Description |
|
|
91
|
+
|---|---|---|
|
|
92
|
+
| `FEDERATIONS_GOOGLE_ENABLED` | `false` | Enable Google OAuth federation |
|
|
93
|
+
| `FEDERATIONS_GOOGLE_CLIENT_ID` | — | Google OAuth client ID |
|
|
94
|
+
| `FEDERATIONS_GOOGLE_CLIENT_SECRET` | — | Google OAuth client secret |
|
|
95
|
+
| `FEDERATIONS_GOOGLE_CALLBACK_URL` | `http://localhost:3000/session/oauth/federation/google/callback` | Google OAuth callback URL |
|
|
96
|
+
|
|
97
|
+
### Client Repository
|
|
98
|
+
|
|
99
|
+
| Variable | Default | Description |
|
|
100
|
+
|---|---|---|
|
|
101
|
+
| `CLIENT_TYPE` | `yaml` | Client store backend: `yaml` |
|
|
102
|
+
| `CLIENT_PATH` | `./config/clients.yaml` | Path to the YAML client registry |
|
|
103
|
+
|
|
104
|
+
### User Repository
|
|
105
|
+
|
|
106
|
+
| Variable | Default | Description |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| `CLIENT_USER_TYPE` | `http` | User repository backend: `http` |
|
|
109
|
+
| `CLIENT_USER_AUTHENTICATE_URL` | — | URL for password-based user authentication |
|
|
110
|
+
| `CLIENT_USER_AUTHENTICATE_BY_TOKEN_URL` | — | URL for token-based user authentication |
|
|
111
|
+
| `CLIENT_USER_TIMEOUT` | `5000` | HTTP request timeout in milliseconds |
|
|
112
|
+
|
|
113
|
+
### Code Repository
|
|
114
|
+
|
|
115
|
+
| Variable | Default | Description |
|
|
116
|
+
|---|---|---|
|
|
117
|
+
| `CLIENT_CODE_TYPE` | `redis` | Authorization code store backend: `redis` |
|
|
118
|
+
| `CLIENT_CODE_ENDPOINT_URI` | — | Redis connection URI for code storage |
|
|
119
|
+
| `CLIENT_CODE_PASSWORD` | — | Redis password for code storage |
|
|
120
|
+
| `CLIENT_CODE_DEFAULT_EXPIRES_IN` | `600` | Default authorization code lifetime in seconds |
|
|
121
|
+
|
|
122
|
+
### Endpoints
|
|
123
|
+
|
|
124
|
+
| Variable | Default | Description |
|
|
125
|
+
|---|---|---|
|
|
126
|
+
| `ENDPOINTS_LOGIN_URL` | — | URL of the login page (for redirects) |
|
|
127
|
+
| `ENDPOINTS_CLIENT_URL` | — | URL of the client application |
|
|
128
|
+
| `ENDPOINTS_AUTH_CALLBACK_URL` | — | URL of the authorization callback |
|
|
129
|
+
|
|
130
|
+
## Module Composition Order
|
|
131
|
+
|
|
132
|
+
Modules are registered in the following order. Each module registers its own routes and grant types.
|
|
133
|
+
|
|
134
|
+
1. **`oauthModule`** — Core OAuth 2.0 endpoints: `POST /oauth/token`, `POST /oauth/introspect`, `GET /oauth/authorize`
|
|
135
|
+
2. **`sessionModule`** — Session-based login/logout and federation: `POST /session/login`, `POST /session/logout`, `GET/POST /session/oauth/federation/*`
|
|
136
|
+
3. **`oauthSessionModule`** — Session grant type (exchanges a valid session for a token)
|
|
137
|
+
4. **`oauthAuthorizationModule`** — Authorization code and refresh token grant types
|
|
138
|
+
|
|
139
|
+
## Built-in Routes
|
|
140
|
+
|
|
141
|
+
The following routes are registered outside of the module system:
|
|
142
|
+
|
|
143
|
+
| Method | Path | Description |
|
|
144
|
+
|---|---|---|
|
|
145
|
+
| `GET` | `/health` | Health check endpoint |
|
|
146
|
+
| `GET` | `/.well-known/jwks.json` | Public key set (JWKS) for token verification |
|
|
147
|
+
|
|
148
|
+
## Docker
|
|
149
|
+
|
|
150
|
+
The Dockerfile uses multi-stage builds with the following targets:
|
|
151
|
+
|
|
152
|
+
| Target | Purpose |
|
|
153
|
+
|---|---|
|
|
154
|
+
| `runtime` | Production image (compiled JS only) |
|
|
155
|
+
| `develop` | Development image with hot reload (`pnpm run debug`) |
|
|
156
|
+
| `test` | Test runner image |
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
# Build the production image
|
|
160
|
+
make build IMAGE=my-auth-provider
|
|
161
|
+
|
|
162
|
+
# Start with docker compose (development)
|
|
163
|
+
make dev
|
|
164
|
+
|
|
165
|
+
# Run tests in Docker
|
|
166
|
+
make test
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
The `docker-compose.yml` starts the auth server together with a Redis container. Configure environment variables in `.env`.
|
|
170
|
+
|
|
171
|
+
## Adding Custom Modules
|
|
172
|
+
|
|
173
|
+
To add a custom module, import it in `src/app.mts` and include it in the `modules` array passed to `createApp`:
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
import { myCustomModule } from "./modules/my-custom-module.mjs";
|
|
177
|
+
|
|
178
|
+
const { init, router, grantRegistry } = createApp(express, {
|
|
179
|
+
// ...
|
|
180
|
+
modules: [
|
|
181
|
+
oauthModule({ clientRepository, codeRepository, express }),
|
|
182
|
+
sessionModule({ userRepository, express }),
|
|
183
|
+
oauthSessionModule({ clientRepository }),
|
|
184
|
+
oauthAuthorizationModule({ codeRepository }),
|
|
185
|
+
myCustomModule, // add here
|
|
186
|
+
],
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## npm Scripts
|
|
191
|
+
|
|
192
|
+
| Script | Description |
|
|
193
|
+
|---|---|
|
|
194
|
+
| `pnpm run build` | Compile TypeScript to `dist/` |
|
|
195
|
+
| `pnpm start` | Start the compiled server |
|
|
196
|
+
| `pnpm run debug` | Start with hot reload via `tsx watch` (development mode) |
|
|
197
|
+
| `pnpm test` | Run tests with Vitest |
|
|
198
|
+
|
|
199
|
+
## See Also
|
|
200
|
+
|
|
201
|
+
- [`@o3co/auth-provider-core`](../../packages/core) — Application factory and config schema
|
|
202
|
+
- [`@o3co/auth-provider-oauth`](../../packages/oauth) — OAuth module
|
|
203
|
+
- [`@o3co/auth-provider-session`](../../packages/session) — Session module
|
|
204
|
+
- [`@o3co/auth-provider-foundation`](../../packages/foundation) — Built-in adapter registration
|
|
205
|
+
- [`@o3co/create-auth-provider`](../../create-app) — CLI scaffolder that generates this template
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
http {
|
|
2
|
+
port = 3000
|
|
3
|
+
port = ${?HTTP_PORT}
|
|
4
|
+
trustProxy = false
|
|
5
|
+
trustProxy = ${?HTTP_TRUST_PROXY}
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
oauth {
|
|
9
|
+
jwt {
|
|
10
|
+
issuer = ${?OAUTH_JWT_ISSUER}
|
|
11
|
+
signingKey {
|
|
12
|
+
provider = "local"
|
|
13
|
+
provider = ${?OAUTH_JWT_SIGNING_KEY_PROVIDER}
|
|
14
|
+
local {
|
|
15
|
+
algorithm = "HS256"
|
|
16
|
+
algorithm = ${?OAUTH_JWT_ALGORITHM}
|
|
17
|
+
kid = "v0"
|
|
18
|
+
kid = ${?OAUTH_JWT_KID}
|
|
19
|
+
secret = ${?OAUTH_JWT_SECRET}
|
|
20
|
+
privateKey = ${?OAUTH_JWT_PRIVATE_KEY}
|
|
21
|
+
privateKeyPath = ${?OAUTH_JWT_PRIVATE_KEY_PATH}
|
|
22
|
+
publicKey = ${?OAUTH_JWT_PUBLIC_KEY}
|
|
23
|
+
publicKeyPath = ${?OAUTH_JWT_PUBLIC_KEY_PATH}
|
|
24
|
+
previousKeys = []
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
accessToken {
|
|
29
|
+
expiresIn = 3600
|
|
30
|
+
expiresIn = ${?OAUTH_ACCESS_TOKEN_EXPIRES_IN}
|
|
31
|
+
}
|
|
32
|
+
refreshToken {
|
|
33
|
+
expiresIn = 86400
|
|
34
|
+
expiresIn = ${?OAUTH_REFRESH_TOKEN_EXPIRES_IN}
|
|
35
|
+
}
|
|
36
|
+
grants {
|
|
37
|
+
session { enabled = true, enabled = ${?OAUTH_GRANTS_SESSION_ENABLED} }
|
|
38
|
+
authorization {
|
|
39
|
+
enabled = true
|
|
40
|
+
enabled = ${?OAUTH_GRANTS_AUTHORIZATION_ENABLED}
|
|
41
|
+
pkce {
|
|
42
|
+
requireS256 = false
|
|
43
|
+
requireS256 = ${?OAUTH_GRANTS_AUTHORIZATION_PKCE_REQUIRE_S256}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
refresh_token { enabled = true, enabled = ${?OAUTH_GRANTS_REFRESH_TOKEN_ENABLED} }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
session {
|
|
51
|
+
secret = ${?SESSION_SECRET}
|
|
52
|
+
maxAge = 3600000
|
|
53
|
+
maxAge = ${?SESSION_MAX_AGE}
|
|
54
|
+
secure = true
|
|
55
|
+
secure = ${?SESSION_SECURE}
|
|
56
|
+
sameSite = "lax"
|
|
57
|
+
sameSite = ${?SESSION_SAME_SITE}
|
|
58
|
+
domain = null
|
|
59
|
+
domain = ${?SESSION_DOMAIN}
|
|
60
|
+
storage {
|
|
61
|
+
type = "redis"
|
|
62
|
+
type = ${?SESSION_STORAGE_TYPE}
|
|
63
|
+
redis {
|
|
64
|
+
url = "redis://localhost:6379"
|
|
65
|
+
url = ${?SESSION_STORAGE_REDIS_URL}
|
|
66
|
+
password = ${?SESSION_STORAGE_REDIS_PASSWORD}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
rateLimit {
|
|
72
|
+
login { windowMs = 900000, limit = 20 }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
federations {
|
|
76
|
+
google {
|
|
77
|
+
enabled = false
|
|
78
|
+
enabled = ${?FEDERATIONS_GOOGLE_ENABLED}
|
|
79
|
+
clientId = ${?FEDERATIONS_GOOGLE_CLIENT_ID}
|
|
80
|
+
clientSecret = ${?FEDERATIONS_GOOGLE_CLIENT_SECRET}
|
|
81
|
+
callbackURL = "http://localhost:3000/session/oauth/federation/google/callback"
|
|
82
|
+
callbackURL = ${?FEDERATIONS_GOOGLE_CALLBACK_URL}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
repositories {
|
|
87
|
+
client {
|
|
88
|
+
type = "yaml"
|
|
89
|
+
type = ${?CLIENT_TYPE}
|
|
90
|
+
yaml {
|
|
91
|
+
path = "./config/clients.yaml"
|
|
92
|
+
path = ${?CLIENT_PATH}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
user {
|
|
96
|
+
type = "http"
|
|
97
|
+
type = ${?CLIENT_USER_TYPE}
|
|
98
|
+
yaml {
|
|
99
|
+
path = "./config/users.yaml"
|
|
100
|
+
}
|
|
101
|
+
http {
|
|
102
|
+
authenticateUrl = ${?CLIENT_USER_AUTHENTICATE_URL}
|
|
103
|
+
authenticateByTokenUrl = ${?CLIENT_USER_AUTHENTICATE_BY_TOKEN_URL}
|
|
104
|
+
timeout = 5000
|
|
105
|
+
timeout = ${?CLIENT_USER_TIMEOUT}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
code {
|
|
109
|
+
type = "redis"
|
|
110
|
+
type = ${?CLIENT_CODE_TYPE}
|
|
111
|
+
memory {
|
|
112
|
+
defaultExpiresIn = 600
|
|
113
|
+
defaultExpiresIn = ${?CLIENT_CODE_DEFAULT_EXPIRES_IN}
|
|
114
|
+
}
|
|
115
|
+
redis {
|
|
116
|
+
defaultExpiresIn = 600
|
|
117
|
+
defaultExpiresIn = ${?CLIENT_CODE_DEFAULT_EXPIRES_IN}
|
|
118
|
+
endpointUri = ${?CLIENT_CODE_ENDPOINT_URI}
|
|
119
|
+
password = ${?CLIENT_CODE_PASSWORD}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
endpoints {
|
|
125
|
+
login { url = ${?ENDPOINTS_LOGIN_URL} }
|
|
126
|
+
client { url = ${?ENDPOINTS_CLIENT_URL} }
|
|
127
|
+
authCallback { url = ${?ENDPOINTS_AUTH_CALLBACK_URL} }
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
cors {
|
|
131
|
+
allowedOrigins = []
|
|
132
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Example client definitions loaded via loadYamlMap into InMemoryClientRepository.
|
|
2
|
+
# Copy this file to clients.yaml and customize for your deployment.
|
|
3
|
+
#
|
|
4
|
+
# Secret format:
|
|
5
|
+
# - Plain text: "my-secret"
|
|
6
|
+
# - Bcrypt hash: "$2b$10$..." (generate with: node -e "require('bcrypt').hash('secret',10).then(console.log)")
|
|
7
|
+
|
|
8
|
+
my-app:
|
|
9
|
+
clientSecret: "$2b$10$REPLACE_WITH_BCRYPT_HASH"
|
|
10
|
+
allowedRedirectUris:
|
|
11
|
+
- "http://localhost:3000/callback"
|
|
12
|
+
allowedScopes:
|
|
13
|
+
- "read"
|
|
14
|
+
- "write"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Environment overlay for development.
|
|
2
|
+
# Loaded when CONFIG_ENV (or NODE_ENV) is "development" — the default.
|
|
3
|
+
# Values here override application.conf.
|
|
4
|
+
#
|
|
5
|
+
# Most values are controlled via environment variables (see ${?VAR}
|
|
6
|
+
# substitutions in application.conf). Override entries here only when
|
|
7
|
+
# you need a fixed development-specific value that cannot come from
|
|
8
|
+
# an environment variable.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Environment overlay for production.
|
|
2
|
+
# Loaded when CONFIG_ENV (or NODE_ENV) is "production".
|
|
3
|
+
# Values here override application.conf.
|
|
4
|
+
#
|
|
5
|
+
# Most production-sensitive values (OAUTH_JWT_SECRET, SESSION_SECRET, REDIS URLs,
|
|
6
|
+
# federation client secrets, etc.) should be supplied via environment variables —
|
|
7
|
+
# see the ${?VAR} substitutions in application.conf.
|
|
8
|
+
#
|
|
9
|
+
# Override entries here only when a value must be fixed per-environment rather
|
|
10
|
+
# than per-deployment (e.g. feature flags turned on only in production).
|