6. API Key · CSR · 2FA · SSO · IP Allowlist
5 자격증명 평면의 영속화
API Key
- API user 당 1개 — "Each API user has an associated API key."
- Console API users list 에서 hover 로 복사 ("API User (ID)" 열)
- Rename 시 API key 불변
- Delete 시 즉시 invalid → 서드파티 통합 중단, in-flight tx 실패, 새 서명 거부
- IP allowlist 없으면 도난된 API key 가 인터넷 임의 머신에서 사용 가능
CREATE TABLE api_keys (
id BINARY(16) PRIMARY KEY,
api_user_id BINARY(16) NOT NULL UNIQUE,
csr_id BINARY(16) NOT NULL,
created_by_owner_user_id BINARY(16) NOT NULL,
created_at DATETIME(6) NOT NULL,
invalidated_at DATETIME(6), -- delete 시 set, immediate
KEY (invalidated_at)
);
CSR (Certificate Signing Request)
- RSA 4096 X.509
- Customer-side 에서 생성 —
fireblocks_secret.key 로컬 보관
- Sandbox: 브라우저에서 자동 생성 (private key 발급 단축)
- Co-signer end cert chain 의 leaf
CREATE TABLE csrs (
id BINARY(16) PRIMARY KEY,
subject_cn VARCHAR(255) NOT NULL,
public_key_pem TEXT NOT NULL,
fingerprint_sha256 BINARY(32) NOT NULL UNIQUE,
signed_by_intermediate_cert_id BINARY(16) NOT NULL,
end_cert_pem TEXT, -- Core Services 가 sign 후 set
signed_at DATETIME(6),
expires_at DATETIME(6) NOT NULL,
uploaded_at DATETIME(6) NOT NULL
);
2FA (TOTP)
- 모든 Console user 필수 — TOTP authenticator (Google / Microsoft / LastPass / Yubico)
- 6-digit code, time-sensitive (시간 동기화 필요)
- WebAuthn / FIDO2 / hardware key 지원 여부: 본 자료 미명시
- Reset 권한: 권한표 Owner + Security Admin. 본문은 Owner only (불일치 패턴)
- 본인 reset 불가 — Owner / Security Admin 에 요청 → Owner reset → 확인 이메일 → 재로그인하여 setup
CREATE TABLE user_2fa (
user_id BINARY(16) PRIMARY KEY,
-- ★ TOTP secret 은 plaintext 저장 금지. HSM-wrapped 만.
secret_hsm_keyref VARCHAR(128) NOT NULL,
enrolled_at DATETIME(6) NOT NULL,
last_used_at DATETIME(6),
KEY (last_used_at)
);
CREATE TABLE twofa_reset_events (
-- append-only
id BINARY(16) PRIMARY KEY,
target_user_id BINARY(16) NOT NULL,
actor_user_id BINARY(16) NOT NULL,
reset_at DATETIME(6) NOT NULL,
via_support BOOLEAN NOT NULL DEFAULT FALSE, -- Owner 본인의 reset 은 Support
KEY (target_user_id)
);
SSO (Auth0 → 외부 IdP)
- email domain 기반 authorization — workspace 가입자의 domain 이 list 에 있어야 로그인 가능
- SSO 는 login authorization 만 — workspace 내 user 추가/삭제는 별개 평면 (Owner/Admin Console)
- Service provider: Auth0 (
auth.fireblocks.io/login/callback)
- 변경: Fireblocks Support 경유, Workspace Owner 승인 필요
| IdP | Self-setup | 주요 입력 |
| Google Workspace | ✓ | Client ID, Secret, domain (OAuth 2.0) |
| Microsoft Entra ID | ✓ | Client ID, Secret value (★ ID 아님), domain |
| Okta | ✓ | customer-metadata.xml, X.509 (SAML, firstName/lastName/email 3 attr 매핑) |
| OpenID Connect | ✓ | Issuer URL, Client ID, domain |
| PingFederate | ✓ | Server URL, X.509 (Base64) |
| SAML 2.0 / 3.0 | ✓ | metadata.xml, X.509 |
| ADFS | — | Fireblocks Support 경유 |
| LDAP / AD | — | Fireblocks Support 경유 |
CREATE TABLE sso_configs (
id BINARY(16) PRIMARY KEY,
workspace_id BINARY(16) NOT NULL UNIQUE,
idp_type ENUM('google', 'entra', 'okta', 'oidc',
'pingfederate', 'saml2', 'saml3', 'adfs', 'ldap') NOT NULL,
callback_url VARCHAR(255) NOT NULL, -- auth.fireblocks.io/login/callback
client_id VARCHAR(255),
client_secret_hsm_keyref VARCHAR(128), -- HSM-wrapped, never plaintext
metadata_xml_blob BLOB, -- SAML 의 경우
configured_at DATETIME(6) NOT NULL,
changed_at DATETIME(6) -- Support 경유 변경
);
CREATE TABLE sso_authorized_domains (
sso_config_id BINARY(16) NOT NULL,
email_domain VARCHAR(255) NOT NULL,
PRIMARY KEY (sso_config_id, email_domain)
);
IP Allowlist
- API user/Key 단위: per-API-user,
/32 CIDR only
- 수정 권한: Owner / Security Admin (권한표) — 본문은 "Only workspace Owners" (불일치)
- 없으면 도난된 API key 의 인터넷 임의 사용 가능
- workspace-level IP allowlist (Console access 용) 도 별도 존재 — Owner 만
CREATE TABLE api_user_ip_allowlists (
id BINARY(16) PRIMARY KEY,
api_user_id BINARY(16) NOT NULL,
cidr VARCHAR(18) NOT NULL, -- /32 only
added_by_user_id BINARY(16) NOT NULL, -- Owner or Security Admin
added_at DATETIME(6) NOT NULL,
removed_at DATETIME(6),
KEY (api_user_id, removed_at)
);
CREATE TABLE workspace_ip_allowlists (
id BINARY(16) PRIMARY KEY,
workspace_id BINARY(16) NOT NULL,
cidr VARCHAR(18) NOT NULL, -- /32 only
added_by_owner_user_id BINARY(16) NOT NULL,
added_at DATETIME(6) NOT NULL,
removed_at DATETIME(6),
KEY (workspace_id)
);