認証仕様(共通)
認証仕様(共通)
Section titled “認証仕様(共通)”関連ドキュメント
Section titled “関連ドキュメント”本ドキュメントと合わせて以下を参照すること。
| ドキュメント | 内容 |
|---|---|
| ArchitecturePolicy_Common.md | 共通アーキテクチャ方針 |
| CodingConventions_Common.md | 共通コーディング規約 |
| 機能別認証仕様(例:AuthenticationSpecification_Coordination_Sprint1.md) | 機能・フェーズ固有の認証実装方針・コード例 |
本ドキュメントは、機能Aおよび関連APIにおける認証・認可仕様を定義する。
本システムでは、認証ロジックをアプリケーション本体から分離し、別プロセスである auth-service が以下を一括して担う。
- 複数OIDC IdPへの対応
- 外部JWT / ID Token の検証
- ユーザー解決
- 組織解決
- 権限解決
- 内部JWTの発行
- APIリクエストのプロキシ
- APIルート単位の認可チェック
アプリケーション本体、以降 バックエンドサービス(Java Spring Boot)と呼ぶ、は原則として外部IdPに直接依存しない。
フェーズ別認証実装について: 認証ロジックの実装スケジュールの都合により、機能Aの実装は段階的に行う。auth-service の実装が完了した後、バックエンドサービス側の認証連携を追加実装する(詳細は第 24 節参照)。
2. 全体アーキテクチャ
Section titled “2. 全体アーキテクチャ”認証・認可の全体構成は以下とする。
Browser ↓Caddy ↓auth-service ↓バックエンドサービス(Java Spring Boot) ↓PostgreSQL各コンポーネントの責務は以下の通りとする。
| コンポーネント | 責務 |
|---|---|
| Browser | 利用者操作、アクセストークンまたはID Tokenの送信 |
| Caddy | HTTPS終端、リバースプロキシ、auth-serviceへの転送 |
| auth-service | OIDC検証、ユーザー解決、RBAC解決、内部JWT発行、APIプロキシ |
| バックエンドサービス | 業務API処理、内部JWTの検証、業務ロジック、DBアクセス |
| PostgreSQL | ユーザー、組織、権限、業務データの保持、RLS適用 |
Note: 本アーキテクチャは ADR-002「提案(未決定・内部議論中)」段階であり、確定前に変更される可能性がある。
3. 認証方式の基本方針
Section titled “3. 認証方式の基本方針”本システムでは、外部認証方式としてOIDCを使用する。
現時点ではOIDCのみを対象とするが、将来的にSAML IdPへ対応する可能性を考慮し、IdPごとの設定はDBで管理する。
本システムでは、AWS ALB OIDC認証は使用しない。
理由は以下の通り。
- ALB OIDCはリスナー単位で単一IdPを前提とする
- 本システムでは組織ごとに異なるIdPを利用する
- 複数IdPを動的に切り替える必要がある
- 認証後にDB上のユーザー、組織、権限へマッピングする必要がある
そのため、認証・認可処理は auth-service で実装する。
4. 認証方式の対象範囲
Section titled “4. 認証方式の対象範囲”本仕様の対象範囲は以下とする。
- OIDC IdP設定管理
- OIDC Token検証
- issuerによるIdP動的選択
- subjectによる内部ユーザー解決
- 組織解決
- ロール解決
- Permission解決
- 内部JWT発行
- APIルート単位の認可チェック
- バックエンドサービスへのプロキシ
- PostgreSQL RLS連携
- JWT署名鍵管理
以下は現時点では対象外とする。
- SAML認証の実装
- ユーザー登録画面
- IdP管理画面
- パスワード認証
- MFAの独自実装
- OAuth2 Authorization Serverの独自実装
- ALB OIDCによる認証
- フロントエンド画面実装
ただし、将来的なSAML対応を妨げない設計とする。
5. 認証フロー概要
Section titled “5. 認証フロー概要”外部APIアクセス時(他社USS向け: <basePath>/uss/v1/feature-a/、UI向け: <basePath>/api/v1/feature-a/)の基本フローは以下とする。basePath = /utm(server.servlet.context-path で可変)。
1. Browser が Caddy に API リクエストを送信する2. Caddy が auth-service にリクエストを転送する3. auth-service が外部JWTまたはID Tokenを取得する4. auth-service が token の iss クレームを確認する5. auth-service が iss に対応する IdP 設定を idp_connections から取得する6. auth-service が IdP の JWKS を取得し、外部JWTを検証する7. auth-service が token の subject を取得する8. auth-service が subject をDBで検索し、内部 user_id / org_id を解決する9. auth-service が roles / permissions をDBから解決する10. auth-service が要求APIに必要なPermissionを確認する11. 認可OKの場合、auth-service が内部JWTを発行する12. auth-service が内部JWTを付与してバックエンドサービスにリバースプロキシする13. バックエンドサービスが内部JWTを検証する14. バックエンドサービスがDBトランザクション開始時に SET ROLE / SET app.current_user_id を実行する15. PostgreSQL RLS により行レベルのアクセス制御を行う内部API(/api/v1/internal/feature-a/)は認証不要であり、auth-service を経由しない(または auth-service でスルー扱いとする)。
6. auth-serviceの責務
Section titled “6. auth-serviceの責務”auth-service は認証・認可の中心コンポーネントとして、以下を担当する。
- 外部IdPの動的選択
- OIDC Token検証
- JWKS取得
- ユーザー解決
- 組織解決
- ロール解決
- Permission解決
- APIルート単位のPermissionチェック
- 内部JWT発行
- 内部JWT署名鍵管理
- バックエンドサービスへのリバースプロキシ
- 認証・認可エラー応答
auth-service は、業務ロジックを実装しない。
7. バックエンドサービスの責務
Section titled “7. バックエンドサービスの責務”バックエンドサービス(Java Spring Boot)は業務アプリケーション本体として、以下を担当する。
- 内部JWTの検証(Spring Security による JWT フィルター実装)
- 内部JWTから user_id / org_id / permissions を取得し SecurityContext に保持
- 業務API処理
- 業務ロジック
- DBトランザクション制御(
TransactionManager.executeを使用) - PostgreSQL RLS用のセッション変数設定(
TransactionSessionInitializer経由) - DBアクセス(MyBatis XML マッパー)
バックエンドサービス は、外部IdPとのOIDC検証を直接行わない。
バックエンドサービス は、外部JWTまたは外部ID Tokenを信頼しない。
バックエンドサービス が信頼するのは、auth-service が発行した内部JWTのみとする。
ただし、内部API(/api/v1/internal/feature-a/)は認証を必要としない(要確認事項 1.5 参照)。
フェーズ別認証実装: 認証連携実装前は、JWT検証フィルターおよびRLS設定は no-op とする(第 24 節参照)。
8. 複数IdP対応方針
Section titled “8. 複数IdP対応方針”本システムでは、組織ごとに異なるOIDC IdPを利用できるものとする。
IdP設定はDBの idp_connections テーブルで管理する。
auth-service は、受信した外部JWTの iss クレームを確認し、対応するIdP設定を動的に選択する。
この機能を MultiProvider Router と呼ぶ。
9. MultiProvider Router仕様
Section titled “9. MultiProvider Router仕様”9.1 目的
Section titled “9.1 目的”MultiProvider Router は、外部JWTの iss クレームをもとに、利用すべきOIDC Provider設定を動的に選択する。
9.2 入力
Section titled “9.2 入力”- 外部JWT
- 外部ID Token
- Authorizationヘッダ
- 必要に応じてCookie
9.3 処理
Section titled “9.3 処理”1. token をパースする2. token の iss クレームを取得する3. idp_connections から issuer に一致するIdP設定を取得する4. IdP設定が存在しない場合は 401 Unauthorized を返却する5. IdP設定が無効な場合は 401 Unauthorized を返却する6. 対応する OIDC Verifier に検証を委譲する9.4 issクレーム
Section titled “9.4 issクレーム”iss は外部IdPを識別するための主キー相当の値として扱う。
例:
{ "iss": "https://idp.example.com/oauth2/default", "sub": "00u123456789", "aud": "client-id", "exp": 1760000000, "iat": 1759999100}10. idp_connectionsテーブル方針
Section titled “10. idp_connectionsテーブル方針”IdP接続情報は idp_connections テーブルで管理する。
想定カラムは以下とする。
| カラム名 | 内容 |
|---|---|
| id | IdP接続ID |
| org_id | 組織ID |
| provider_type | IdP種別 |
| issuer | OIDC issuer |
| client_id | OIDC client_id |
| jwks_uri | JWKS URI |
| authorization_endpoint | 認可エンドポイント |
| token_endpoint | トークンエンドポイント |
| userinfo_endpoint | UserInfoエンドポイント |
| enabled | 有効フラグ |
| created_at | 作成日時 |
| updated_at | 更新日時 |
provider_type は以下を想定する。
| 値 | 内容 |
|---|---|
| oidc | OIDC IdP |
| saml | SAML IdP、将来対応用 |
現時点では provider_type = 'oidc' のみを実装対象とする。
11. OIDC Verifier仕様
Section titled “11. OIDC Verifier仕様”11.1 目的
Section titled “11.1 目的”OIDC Verifier は、外部IdPが発行したJWTまたはID Tokenを検証する。
本コンポーネントは auth-service が実装する。バックエンドサービスの実装対象外である。
11.2 使用ライブラリ(auth-service固有)
Section titled “11.2 使用ライブラリ(auth-service固有)”auth-service(Go)のOIDC検証には go-oidc v3 を使用する。
本項は auth-service(Go)固有の仕様であり、バックエンドサービス(Java Spring Boot)の実装対象外である。
11.3 検証項目
Section titled “11.3 検証項目”OIDC Verifierでは、少なくとも以下を検証する。
- 署名
- issuer
- audience
- expiration time
- issued at
- not before
- token形式
- JWKSとの整合性
- IdP設定が有効であること
11.4 JWKS取得
Section titled “11.4 JWKS取得”JWKSはIdP設定に基づいて取得する。
JWKSの取得元は以下のいずれかとする。
- OIDC discovery documentから取得
idp_connections.jwks_uriから取得
JWKSは毎リクエスト取得せず、キャッシュする。
キャッシュ期間および更新方針はIdPのレスポンスヘッダ、またはauth-serviceの設定に従う。
12. User Resolver仕様
Section titled “12. User Resolver仕様”12.1 目的
Section titled “12.1 目的”User Resolver は、外部IdPから返却された subject を内部ユーザーへマッピングする。
本コンポーネントは auth-service が実装する。バックエンドサービスの実装対象外である。
12.2 入力
Section titled “12.2 入力”- issuer
- subject
- organization hint
- external claims
12.3 処理概要
Section titled “12.3 処理概要”1. issuer に対応する idp_connection を取得する2. token の sub を取得する3. idp_connection_id と sub をキーにユーザーを検索する4. ユーザーが存在しない場合は 403 Forbidden を返却する5. ユーザーが無効な場合は 403 Forbidden を返却する6. ユーザーに紐づく org_id を取得する7. 組織が無効な場合は 403 Forbidden を返却する8. user_id / org_id を認証済みプリンシパルとして確定する13. ユーザー解決に使用するDB
Section titled “13. ユーザー解決に使用するDB”ユーザー解決では、以下のテーブルを使用する。
idp_connectionsusersorganizations
必要に応じて、外部IdPのsubjectを管理する専用テーブルを設けてもよい。
例:
user_identities想定カラム:
| カラム名 | 内容 |
|---|---|
| id | ID |
| user_id | 内部ユーザーID |
| idp_connection_id | IdP接続ID |
| external_subject | IdPのsubject |
| external_email | IdP上のemail |
| enabled | 有効フラグ |
| created_at | 作成日時 |
| updated_at | 更新日時 |
14. RBAC仕様
Section titled “14. RBAC仕様”本システムでは、認可方式としてRBACを採用する。
RBACは以下の概念で構成する。
| 概念 | 内容 |
|---|---|
| User | 利用者 |
| Organization | 組織 |
| Role | ロール |
| Permission | 権限 |
| RolePermission | ロールと権限の対応 |
| UserRole | ユーザーとロールの対応 |
15. RBACテーブル方針
Section titled “15. RBACテーブル方針”RBAC解決では、以下のテーブルを使用する。
usersorganizationsrolespermissionsuser_rolesrole_permissions
想定構造は以下とする。
users └─ user_roles └─ roles └─ role_permissions └─ permissionsauth-service は、認証済みユーザーの user_id および org_id をもとに、有効なPermission一覧を取得する。
16. Permission命名規約(判断不可。要確認。)
Section titled “16. Permission命名規約(判断不可。要確認。)”Permission名は、以下の形式を基本とする。
<resource>:<action>機能Aにおける具体的なリソース名(<resource>)および必要なアクション(<action>)は、OpenAPI yaml のエンドポイント設計確定後に決定する。(判断不可。要確認。)
参考:汎用的な命名パターン例
<resource>:read<resource>:create<resource>:update<resource>:delete<resource>:approveAPIルートごとに必要なPermissionを定義する。
17. APIルート別Permissionチェック
Section titled “17. APIルート別Permissionチェック”auth-service は /uss/v1/feature-a/* へのリクエストを バックエンドサービス にプロキシする前に、ルートごとのPermissionチェックを行う。
17.1 Permissionチェック方針
Section titled “17.1 Permissionチェック方針”1. リクエストメソッドとパスを取得する2. 認可設定から必要Permissionを取得する3. 認証済みユーザーが該当Permissionを持つか確認する4. Permissionが不足している場合は 403 Forbidden を返却する5. Permissionが十分な場合はバックエンドサービスへプロキシする内部API(/api/v1/internal/feature-a/)はPermissionチェックを行わない。
18. APIルート別Permission定義例(判断不可。要確認。)
Section titled “18. APIルート別Permission定義例(判断不可。要確認。)”以下はPermission定義の記述パターン例である。
実際の機能AのPermission定義は、OpenAPI yaml(他社USS向け外部API /uss/v1/feature-a/、UI向け外部API /api/v1/feature-a/、および内部API /api/v1/internal/feature-a/ のエンドポイント定義)の確定後に決定する。(判断不可。要確認。)
参考(汎用パターン例):
他社USS向け外部API(<basePath>/uss/v1/feature-a/)
| HTTP Method | Path パターン | 必要Permission パターン |
|---|---|---|
| GET | /uss/v1/feature-a/… | <resource>:read |
| POST | /uss/v1/feature-a/… | <resource>:create |
| PUT | /uss/v1/feature-a/…/{id} | <resource>:update |
| DELETE | /uss/v1/feature-a/…/{id} | <resource>:delete |
| POST | /uss/v1/feature-a/…/{id}/approve | <resource>:approve |
UI向け外部API(<basePath>/api/v1/feature-a/)
| HTTP Method | Path パターン | 必要Permission パターン |
|---|---|---|
| GET | /api/v1/feature-a/… | <resource>:read |
| POST | /api/v1/feature-a/… | <resource>:create |
| PUT | /api/v1/feature-a/…/{id} | <resource>:update |
| DELETE | /api/v1/feature-a/…/{id} | <resource>:delete |
内部API(<basePath>/api/v1/internal/feature-a/)は認証・認可不要とする(要確認事項 1.5 参照)。
19. Internal JWT仕様
Section titled “19. Internal JWT仕様”auth-service は、外部JWTの検証およびユーザー・権限解決に成功した後、内部JWTを発行する。
内部JWTは バックエンドサービス が信頼する唯一の認証情報とする。
19.1 署名方式
Section titled “19.1 署名方式”内部JWTの署名方式は以下とする。
RS256秘密鍵は auth-service のみが保持する。
公開鍵は、内部JWTを検証する必要があるコンポーネントに配布する。
20. Internal JWT TTL
Section titled “20. Internal JWT TTL”内部JWTのTTLは以下とする。
15分TTLを短くする理由は以下の通り。
- 権限変更の反映遅延を抑える
- 漏洩時の影響範囲を限定する
- 長期セッション管理を外部IdPまたはフロントエンド側に委ねる
21. Internal JWT Claims
Section titled “21. Internal JWT Claims”内部JWTには以下のClaimを含める。
| Claim | 内容 |
|---|---|
| iss | 内部JWT発行者 |
| sub | 内部ユーザーID |
| org_id | 組織ID |
| permissions | Permission一覧 |
| roles | ロール一覧 |
| idp_connection_id | IdP接続ID |
| external_sub | 外部IdPのsubject |
| iat | 発行日時 |
| exp | 有効期限 |
| jti | JWT ID |
例:
{ "iss": "auth-service", "sub": "user-123", "org_id": "org-001", "roles": [ "featureA-admin" ], "permissions": [ "featureA:read", "featureA:create", "featureA:update" ], "idp_connection_id": "idp-001", "external_sub": "00u123456789", "iat": 1760000000, "exp": 1760000900, "jti": "jwt-unique-id"}22. Internal JWT受け渡し方式
Section titled “22. Internal JWT受け渡し方式”auth-service は バックエンドサービス へリクエストをプロキシする際、内部JWTをHTTPヘッダに付与する。
使用するヘッダは以下とする。
Authorization: Bearer <internal-jwt>また、必要に応じて以下の内部ヘッダを付与してもよい。
X-User-Id: <user_id>X-Org-Id: <org_id>X-Permissions: <comma-separated-permissions>ただし、バックエンドサービス は内部ヘッダのみを信頼してはならない。
バックエンドサービス は必ず内部JWTを検証し、JWTのClaimを正とする。
23. バックエンドサービス側のInternal JWT検証
Section titled “23. バックエンドサービス側のInternal JWT検証”バックエンドサービス は、受信した内部JWTについて以下を検証する。
- 署名
- issuer
- expiration time
- issued at
- audience、必要な場合
- token形式
- 必須Claimの存在
- subの存在
- org_idの存在
- permissionsの存在
内部JWTが存在しない、または不正な場合は RFC 9457 Problem Details 形式で 401 Unauthorized を返却する。
Permissionが不足している場合は、原則として RFC 9457 Problem Details 形式で 403 Forbidden を返却する。
23.1 Java Spring Boot での実装方針(判断不可。要確認。)
Section titled “23.1 Java Spring Boot での実装方針(判断不可。要確認。)”バックエンドサービス(Java Spring Boot)における内部JWT検証の具体的な実装方式は以下のいずれかとする。(判断不可。要確認。)
- 方式A: Spring Security OAuth2 Resource Server + Nimbus JOSE + JWT(
spring-security-oauth2-resource-server依存) - 方式B:
OncePerRequestFilterを継承したカスタム JWT フィルター + Nimbus JOSE + JWT
いずれの方式でも、検証成功後は SecurityContextHolder にユーザー情報(user_id、org_id、permissions)を格納する。
23.2 公開鍵の取得・設定方法(判断不可。要確認。)
Section titled “23.2 公開鍵の取得・設定方法(判断不可。要確認。)”バックエンドサービスが内部JWT検証に使用する公開鍵の取得方式は以下のいずれかとする。(判断不可。要確認。)
- S3 から起動時にダウンロード
- AWS Systems Manager Parameter Store から取得
- auth-service の JWKSエンドポイントから動的取得
起動時設定例(ECS Task Definition での環境変数注入):
# application.yaml(将来実装時)app: auth: jwt: public-key: ${INTERNAL_JWT_PUBLIC_KEY} issuer: auth-service24. フェーズ別認証実装方針
Section titled “24. フェーズ別認証実装方針”機能Aは認証を段階的に実装する。
auth-service の実装スケジュールが確定した後、バックエンドサービス側の認証連携を追加実装する。
24.1 段階的実装の考え方
Section titled “24.1 段階的実装の考え方”認証連携が実装されるまでの間は以下の方針とする。
- JWT検証フィルターは実装しない(no-op または
permitAllで全リクエストを許可する) TransactionSessionInitializerは no-op 実装とし、認証仕様確定後に差し替える- PostgreSQL RLSは実装しない
SecurityContextHolderにユーザー情報を保持しない
24.2 認証追加時の実装手順
Section titled “24.2 認証追加時の実装手順”auth-service 実装後、以下を追加実装する。
- JWT検証フィルターを実装する(実装方式は第 23.1 節参照)
- SecurityContext に user_id / org_id / permissions を格納する
TransactionSessionInitializerを実装し、SET LOCAL ROLEとSET LOCAL app.current_user_idを実行する- 内部APIの permitAll 設定は維持する(認証不要)
- 外部APIには JWT 検証フィルターを適用する
各フェーズにおける具体的な実装方針・コード例は、機能別の認証仕様ドキュメント(例:AuthenticationSpecification_Coordination_Sprint1.md)を参照すること。
25. 認可責務の分担
Section titled “25. 認可責務の分担”認可は以下の2段階で行う。
| 段階 | 実施コンポーネント | 内容 |
|---|---|---|
| APIルート認可 | auth-service | HTTP method + path に対するPermissionチェック |
| 業務データ認可 | バックエンドサービス / PostgreSQL | org_id、user_id、RLS、業務状態に基づく制御 |
auth-service はAPIに入る前のルート認可を担当する。
バックエンドサービス は業務データ単位の認可を担当する。
PostgreSQL RLSは最終的な行レベル制御を担当する。
26. PostgreSQL RLS方針
Section titled “26. PostgreSQL RLS方針”本システムでは、PostgreSQLのRow Level Security、以降RLSを使用する。
RLSでは、DBセッションに設定された以下の情報をもとに、参照・更新可能な行を制御する。
- DB role
- app.current_user_id
- app.current_org_id
- 必要に応じた追加コンテキスト
Sprint1: RLS は実装しない。TransactionSessionInitializer は no-op とする。認証仕様確定後に実装する。
27. RLS設定方式
Section titled “27. RLS設定方式”バックエンドサービス は、DBトランザクション開始後、業務SQL実行前に以下を実行する。
SET LOCAL ROLE app_user;SELECT set_config('app.current_user_id', $1, true);SELECT set_config('app.current_org_id', $2, true);SET LOCAL を使用することで、トランザクションスコープにセッション変数を限定し、コネクションプールによるセッション汚染を防ぐ。
PostgreSQLのSET/SET LOCALは値部分にプリペアドステートメントのバインド変数を使えないため、user_id・org_idのような外部由来の値を設定する際は、バインドパラメータを受け付けるset_config(name, value, is_local)関数を使用する(is_local=trueがSET LOCAL相当のスコープ)。文字列連結でSQLを組み立てることは禁止する(ArchitecturePolicy_Common.md・CodingConventions_Common.mdのSQLインジェクション禁止方針を参照)。SET LOCAL ROLE app_userのようにアプリケーション側で完全に固定された値(外部入力を含まない)は、文字列連結ではなく固定文字列としてそのまま実行してよい。
RLS用の値は、内部JWTのClaimから取得する。
バックエンドサービスでは、TransactionSessionInitializer がトランザクション開始直後に上記を実行する設計とする。インターフェースは引数を取らない(initializeSession())。実装はJdbcTemplate(またはDataSource)をコンストラクタ注入し、現在のトランザクションに紐づくコネクションを介して実行する(SpringのJdbcTemplateはトランザクション同期済みのコネクションを自動的に使用するため、Connectionをインターフェースの引数として明示的に受け渡す必要はない)。
// TransactionSessionInitializer の設計(認証仕様確定後に実装)public class TransactionSessionInitializer {
private final JdbcTemplate jdbcTemplate; private final SecurityContext securityContext;
public void initializeSession() { var principal = securityContext.getAuthentication().getPrincipal(); jdbcTemplate.execute("SET LOCAL ROLE app_user"); jdbcTemplate.update("SELECT set_config('app.current_user_id', ?, true)", principal.getUserId()); jdbcTemplate.update("SELECT set_config('app.current_org_id', ?, true)", principal.getOrgId()); }}28. RLS実行タイミング
Section titled “28. RLS実行タイミング”RLS用の SET LOCAL ROLE および SET LOCAL app.current_user_id は、トランザクションごとに実行する。
実行タイミングは以下とする。
1. バックエンドサービスが内部JWTを検証する2. user_id / org_id をリクエストコンテキスト(SecurityContext)に保持する3. DBトランザクションを開始する(TransactionManager.execute の開始時)4. TransactionSessionInitializer.initializeSession() を呼び出す5. SET LOCAL ROLE を実行する6. set_config('app.current_user_id', ?, true) を実行する(バインドパラメータ使用)7. set_config('app.current_org_id', ?, true) を実行する(バインドパラメータ使用)8. 業務SQLを実行する9. commit または rollback するコネクションプールを使用する場合、セッション汚染を避けるため、原則として SET LOCAL を使用する。
29. RLSポリシー例
Section titled “29. RLSポリシー例”以下はRLSポリシーの例である。
ALTER TABLE feature_a ENABLE ROW LEVEL SECURITY;
CREATE POLICY feature_a_org_policyON feature_aUSING ( org_id = current_setting('app.current_org_id')::uuid);
CREATE POLICY feature_a_insert_policyON feature_aFOR INSERTWITH CHECK ( org_id = current_setting('app.current_org_id')::uuid);実際のRLSポリシーは、業務テーブルの設計および業務要件に従って定義する。
30. RLS利用時の注意事項
Section titled “30. RLS利用時の注意事項”RLS利用時は以下に注意する。
- アプリケーション用DBユーザーに過剰な権限を付与しない
- RLSをバイパス可能なDBロールをアプリで使用しない
SET LOCAL ROLE/SET LOCALの実行漏れを防ぐ- コネクションプール利用時のセッション変数残留を防ぐ
- 管理者用処理では、RLSバイパスの可否を明示的に設計する
- バッチ処理でRLSを適用するか、別ロールで実行するかを明確にする
31. JWT署名鍵管理方針
Section titled “31. JWT署名鍵管理方針”内部JWT署名鍵はAWS Secrets Managerで管理する。
31.1 秘密鍵
Section titled “31.1 秘密鍵”秘密鍵は以下で管理する。
AWS Secrets Manager秘密鍵はKMSで暗号化して保存する。
auth-service のECSタスク起動時に、ECS Task Definitionの secrets ブロックを利用してコンテナへ注入する。
秘密鍵はソースコード、Dockerイメージ、Gitリポジトリに含めない。
32. 秘密鍵注入方式
Section titled “32. 秘密鍵注入方式”ECS Task Definitionでは、Secrets Managerに格納された秘密鍵を環境変数またはファイルとしてコンテナに注入する。
例:
{ "secrets": [ { "name": "INTERNAL_JWT_PRIVATE_KEY", "valueFrom": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:internal-jwt-private-key" } ]}auth-service は起動時に INTERNAL_JWT_PRIVATE_KEY を読み込み、内部JWT署名に使用する。
33. 公開鍵配布方式
Section titled “33. 公開鍵配布方式”内部JWTの公開鍵は、検証が必要なコンポーネントへ配布する。
配布先の例:
- バックエンドサービス
- 管理API
- 非同期ワーカー
- API Gateway相当の内部コンポーネント
公開鍵の配布方式は以下のいずれかとする。
| 配布方式 | 用途 |
|---|---|
| S3 | ファイルとして配布する場合 |
| AWS Systems Manager Parameter Store | 設定値として配布する場合 |
| auth-service の JWKS エンドポイント | 動的取得する場合 |
現時点では、S3またはParameter Storeによる配布を想定する。
バックエンドサービスの具体的な公開鍵取得方式は第 23.2 節参照。(判断不可。要確認。)
将来的に複数鍵ローテーションを高度化する場合は、auth-serviceが内部JWKSエンドポイントを提供する方式も検討する。
34. 鍵ローテーション方針
Section titled “34. 鍵ローテーション方針”JWT署名鍵は、Secrets Managerのローテーション機能を利用して運用管理する。
ローテーション時は、既存JWTのTTLが15分であることを考慮し、旧鍵と新鍵の併用期間を設ける。
34.1 ローテーション時の注意事項
Section titled “34.1 ローテーション時の注意事項”- 新秘密鍵で署名を開始する
- バックエンドサービス側に新公開鍵を配布する
- 旧公開鍵も一定期間保持する
- 既存内部JWTのTTL満了後に旧鍵を無効化する
- JWTヘッダには
kidを付与することを推奨する
JWTヘッダ例:
{ "alg": "RS256", "typ": "JWT", "kid": "2026-06-key-001"}35. JWT kid方針
Section titled “35. JWT kid方針”内部JWTには、署名鍵識別子として kid を付与することを推奨する。
バックエンドサービス は kid を使用して、対応する公開鍵を選択する。
これにより、鍵ローテーション時に複数公開鍵を並行利用できる。
36. Caddyとの連携方針
Section titled “36. Caddyとの連携方針”Caddyは外部からのHTTPSリクエストを受け付け、auth-serviceへ転送する。
Caddyでは以下を行う。
- HTTPS終端
- リクエスト転送
- 必要に応じたヘッダ付与
- パスベースルーティング
Caddyは認証・認可の主処理を行わない。
認証・認可の主処理はauth-serviceで行う。
37. Proxy Handler仕様
Section titled “37. Proxy Handler仕様”auth-serviceの Proxy Handler は、認可済みの外部APIリクエスト(<basePath>/uss/v1/feature-a/* および <basePath>/api/v1/feature-a/*)をバックエンドサービスへリバースプロキシする。basePath = /utm。
37.1 処理概要
Section titled “37.1 処理概要”1. <basePath>/uss/v1/feature-a/* または <basePath>/api/v1/feature-a/* リクエストを受け取る2. 外部JWTを検証する3. ユーザー・組織・権限を解決する4. APIルートに必要なPermissionを確認する5. 内部JWTを発行する6. Authorizationヘッダに内部JWTを設定する7. バックエンドサービスへリクエストを転送する8. バックエンドサービスのレスポンスをBrowserへ返却する内部API(<basePath>/api/v1/internal/feature-a/*)はPermissionチェックを行わず、スルーする。
37.2 プロキシ時のヘッダ方針
Section titled “37.2 プロキシ時のヘッダ方針”auth-serviceは、外部から受け取った認証ヘッダをそのままバックエンドサービスへ転送しない。
バックエンドサービスへ転送する際は、auth-serviceが発行した内部JWTを設定する。
Authorization: Bearer <internal-jwt>外部JWTをバックエンドサービスへ渡さないことで、バックエンドサービスを外部IdP仕様から分離する。
38. 認証エラー応答
Section titled “38. 認証エラー応答”38.1 auth-service の認証エラー応答(判断不可。要確認。)
Section titled “38.1 auth-service の認証エラー応答(判断不可。要確認。)”auth-serviceが認証に失敗した場合、401 Unauthorized を返却する。この応答はauth-serviceが返すため、フォーマットはauth-service側の実装に依存する。(判断不可。要確認。)
認証エラーの例:
- Authorizationヘッダが存在しない
- Bearer tokenが存在しない
- token形式が不正
- issuerが未登録
- 署名検証に失敗
- token期限切れ
- audience不一致
- IdP設定が無効
38.2 バックエンドサービスの認証エラー応答
Section titled “38.2 バックエンドサービスの認証エラー応答”バックエンドサービスが内部JWTの検証に失敗した場合、RFC 9457 Problem Details 形式で 401 Unauthorized を返却する。
{ "type": "https://example.com/problems/unauthorized", "title": "認証に失敗しました", "status": 401, "detail": "内部JWTが無効または期限切れです。", "instance": "/utm/uss/v1/feature-a/xxx"}type URI のベースURLは未確定。(要確認事項 1.7 参照。)
39. 認可エラー応答
Section titled “39. 認可エラー応答”39.1 auth-service の認可エラー応答(判断不可。要確認。)
Section titled “39.1 auth-service の認可エラー応答(判断不可。要確認。)”auth-serviceが認可に失敗した場合、403 Forbidden を返却する。フォーマットはauth-service側の実装に依存する。(判断不可。要確認。)
認可エラーの例(auth-service側):
- ユーザーが無効
- 組織が無効
- ロールが存在しない
- 必要Permissionを保持していない
39.2 バックエンドサービスの認可エラー応答
Section titled “39.2 バックエンドサービスの認可エラー応答”バックエンドサービスが業務データ認可で権限不足と判断した場合、RFC 9457 Problem Details 形式で 403 Forbidden を返却する。
{ "type": "https://example.com/problems/forbidden", "title": "操作が許可されていません", "status": 403, "detail": "この操作を実行する権限がありません。", "instance": "/utm/uss/v1/feature-a/xxx"}RLSにより対象データへのアクセスが拒否された場合も、バックエンドサービスは 403 Forbidden を返却する。
40. エラーコード定義
Section titled “40. エラーコード定義”認証・認可関連のエラーコードは以下を基本とする。
| エラーコード | HTTPステータス | 内容 |
|---|---|---|
| AUTH_TOKEN_MISSING | 401 | トークンが存在しない |
| AUTH_TOKEN_INVALID | 401 | トークン形式または署名が不正 |
| AUTH_TOKEN_EXPIRED | 401 | トークン期限切れ |
| AUTH_ISSUER_UNKNOWN | 401 | issuerが未登録 |
| AUTH_IDP_DISABLED | 401 | IdP設定が無効 |
| AUTH_USER_NOT_FOUND | 403 | 内部ユーザーが存在しない |
| AUTH_USER_DISABLED | 403 | ユーザーが無効 |
| AUTH_ORG_DISABLED | 403 | 組織が無効 |
| AUTH_PERMISSION_DENIED | 403 | Permission不足 |
| AUTH_INTERNAL_ERROR | 500 | 認証基盤内部エラー |
41. ログ出力方針
Section titled “41. ログ出力方針”auth-serviceでは、認証・認可処理に関するログを出力する。
バックエンドサービスでは、業務処理に関するログとあわせて、JWT検証結果をINFOレベルで記録する。
41.1 出力する情報
Section titled “41.1 出力する情報”以下はログ出力してよい。
- request_id
- issuer
- idp_connection_id
- user_id
- org_id
- path
- method
- 認証結果
- 認可結果
- error_code
41.2 出力禁止情報
Section titled “41.2 出力禁止情報”以下はログ出力してはならない。
- 外部JWT全文
- 内部JWT全文
- Authorizationヘッダ全文
- refresh token
- access token
- ID Token
- 秘密鍵
- APIキー
- パスワード
- 個人情報
禁止例:
Authorization: Bearer eyJhbGciOi...推奨例:
auth failed. request_id=req-001 issuer=https://idp.example.com error_code=AUTH_TOKEN_EXPIRED42. 監査ログ方針
Section titled “42. 監査ログ方針”必要に応じて、以下の認証・認可イベントを監査ログとして保存する。
- ログイン成功
- ログイン失敗
- issuer未登録
- ユーザー未登録
- Permission不足
- 管理者権限操作
- 鍵ローテーション
- IdP設定変更
- Role変更
- Permission変更
監査ログには、トークンや秘密情報を保存しない。
43. セキュリティ方針
Section titled “43. セキュリティ方針”認証・認可実装では以下を遵守する。
- 外部JWTを検証せずに信頼しない
- バックエンドサービスは外部JWTを直接信頼しない
- 内部JWTはRS256で署名する
- 秘密鍵はSecrets Managerで管理する
- 秘密鍵をソースコードに含めない
- Permissionチェックを省略しない
- RLSの設定漏れを防ぐ
- Authorizationヘッダをログ出力しない
- CORS設定は必要最小限にする
- Cookieを使用する場合はSecure属性、HttpOnly属性、SameSite属性を適切に設定する
44. CORS方針
Section titled “44. CORS方針”フロントエンドからブラウザ経由でAPIを呼び出す場合、CORS設定はCaddyまたはauth-serviceで制御する。
CORSは必要最小限のOriginのみ許可する。
禁止:
Access-Control-Allow-Origin: *推奨:
Access-Control-Allow-Origin: https://app.example.com認証情報付きリクエストを許可する場合は、許可Originを明示する。
許可するOrigin(フロントエンドURL)は、フロントエンド設計確定後に決定する。(判断不可。要確認。)
45. Cookie利用方針
Section titled “45. Cookie利用方針”現時点では、AuthorizationヘッダによるBearer Token送信を基本とする。
Cookieを利用する場合は、以下を必須とする。
- Secure
- HttpOnly
- SameSite=Lax または Strict
- CSRF対策
- セッション固定攻撃対策
Cookie利用の有無は、フロントエンド設計およびIdP連携方式に応じて決定する。
46. 将来のSAML対応方針
Section titled “46. 将来のSAML対応方針”現時点ではOIDCのみを実装対象とする。
ただし、将来的にSAML IdPへ対応できるよう、IdP設定は provider_type により拡張可能とする。
将来のSAML対応時には、以下の追加実装を検討する。
- SAML metadata管理
- SAML Response検証
- NameIDによるユーザー解決
- SAML属性による組織・ユーザー補足情報取得
- OIDC subjectとの統一的な外部ID管理
外部認証方式がOIDCであってもSAMLであっても、内部的には以下へ正規化する。
external_provider_typeexternal_issuerexternal_subjectuser_idorg_idpermissionsバックエンドサービスは、OIDC/SAMLの違いを意識しない。
47. OpenAPIとの関係
Section titled “47. OpenAPIとの関係”OpenAPI yamlでは、各APIに必要なPermissionを拡張項目として記載することを推奨する。
例:
paths: /uss/v1/feature-a/{resource}: get: summary: 一覧取得 x-permission: <resource>:read responses: '200': description: OK post: summary: 作成 x-permission: <resource>:create responses: '201': description: Created実際のPermission定義はOpenAPI yaml確定後に決定する。(判断不可。要確認。)
auth-serviceは、OpenAPIまたは別途定義されたルート認可設定をもとにPermissionチェックを行う。
48. ルート認可設定ファイル方針(判断不可。要確認。)
Section titled “48. ルート認可設定ファイル方針(判断不可。要確認。)”APIルートとPermissionの対応は、設定ファイルまたはDBで管理する。
設定ファイル例(パターン):
routes: # 他社USS向け外部API - method: GET path: /uss/v1/feature-a/{resource} permission: <resource>:read
- method: POST path: /uss/v1/feature-a/{resource} permission: <resource>:create
- method: PUT path: /uss/v1/feature-a/{resource}/{id} permission: <resource>:update
- method: DELETE path: /uss/v1/feature-a/{resource}/{id} permission: <resource>:delete
# UI向け外部API - method: GET path: /api/v1/feature-a/{resource} permission: <resource>:read
- method: POST path: /api/v1/feature-a/{resource} permission: <resource>:create
- method: PUT path: /api/v1/feature-a/{resource}/{id} permission: <resource>:update
- method: DELETE path: /api/v1/feature-a/{resource}/{id} permission: <resource>:delete実際のルート定義は、OpenAPI yaml 確定後に決定する。(判断不可。要確認。)
OpenAPIの x-permission を正とするか、別設定を正とするかはプロジェクトで決定する。
推奨は、OpenAPIの x-permission を正とし、auth-service起動時に読み込む方式である。