Skip to content

mack-erel/KeyStone

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

446 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Keystone IDP

Cloudflare Workers ์œ„์—์„œ ๋™์ž‘ํ•˜๋Š” ์˜คํ”ˆ์†Œ์Šค Identity Provider์ž…๋‹ˆ๋‹ค. OIDC, SAML 2.0, WebAuthn/Passkey, TOTP 2FA, LDAP ์—ฐ๋™์„ ์ง€์›ํ•˜๋ฉฐ ๋ฉ€ํ‹ฐํ…Œ๋„ŒํŠธ ์กฐ์ง ๊ด€๋ฆฌ๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

๊ฐœ๋ฐœ ๋‹จ๊ณ„ ์•ˆ๋‚ด: ๋ณธ ํ”„๋กœ์ ํŠธ๋Š” ํ™œ๋ฐœํžˆ ๊ฐœ๋ฐœ ์ค‘์ธ ํ•™์Šต/์‹คํ—˜ ์„ฑ๊ฒฉ์˜ IdP์ž…๋‹ˆ๋‹ค. ํ”„๋กœ๋•์…˜ ๋„์ž… ์ „์—๋Š” ์œ„ํ˜‘ ๋ชจ๋ธ์— ๋งž์ถ˜ ์ž์ฒด ๋ณด์•ˆ ๊ฒ€ํ† ๋ฅผ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. ๋ณด์•ˆ ๊ด€๋ จ ์•Œ๋ ค์ง„ ํ•œ๊ณ„๋Š” ๋ณด์•ˆ ์ฐธ๊ณ ์‚ฌํ•ญ์„ ์ฐธ์กฐํ•˜์„ธ์š”.

๋ชฉ์ฐจ

์ฃผ์š” ๊ธฐ๋Šฅ

๊ธฐ๋Šฅ ์„ค๋ช…
OIDC Authorization Code + PKCE, Refresh Token, UserInfo, JWKS, End-Session
SAML 2.0 SP-Initiated SSO, HTTP-POST ๋ฐ”์ธ๋”ฉ, ForceAuthn, IsPassive, RequestedAuthnContext, SLO
ACR / AMR ์ธ์ฆ ๋ฐฉ์‹์— ๋”ฐ๋ฅธ ACR ์ž๋™ ๊ฒฐ์ • โ€” SAML Assertion ๋ฐ OIDC ID Token์— ํฌํ•จ
WebAuthn / Passkey ํŒจ์Šคํ‚ค ๋“ฑ๋ก ๋ฐ ์ธ์ฆ, challenge 1ํšŒ์šฉ DB ์ฒ˜๋ฆฌ, ํ…Œ๋„ŒํŠธ ๊ฒฉ๋ฆฌ
TOTP 2FA Google Authenticator ๋“ฑ ํ˜ธํ™˜, ๋ฐฑ์—… ์ฝ”๋“œ ์ง€์›
LDAP ์—ฐ๋™ LDAP ์ธ์ฆ ๋ฐ JIT ์‚ฌ์šฉ์ž ํ”„๋กœ๋น„์ €๋‹, ๊ด€๋ฆฌ์ž UI์—์„œ ํ”„๋กœ๋ฐ”์ด๋” ์„ค์ •
๊ณ„์ • ์ž๊ฐ€ ๊ด€๋ฆฌ ํ”„๋กœํ•„ ํŽธ์ง‘, ๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์„ค์ •/์ฐพ๊ธฐ, MFA ๋“ฑ๋ก, Passkey ๋“ฑ๋กยทํ•ด์ œ
์กฐ์ง ๊ด€๋ฆฌ ๋ถ€์„œ โ†’ ํŒ€ โ†’ ํŒŒํŠธ ๊ณ„์ธต, ์ง๊ธ‰/์ง์ฑ…, ๋ณต์ˆ˜ ์†Œ์†
๋ฉ€ํ‹ฐํ…Œ๋„ŒํŠธ ํ…Œ๋„ŒํŠธ๋ณ„ ๋…๋ฆฝ ์‚ฌ์šฉ์ž/ํด๋ผ์ด์–ธํŠธ/ํ‚ค ๊ด€๋ฆฌ
๊ด€๋ฆฌ์ž UI ์‚ฌ์šฉ์ž, ์กฐ์ง(๋ถ€์„œยทํŒ€ยทํŒŒํŠธยท์ง๊ธ‰), OIDC ํด๋ผ์ด์–ธํŠธ, SAML SP, LDAP ํ”„๋กœ๋ฐ”์ด๋”, ์„œ๋ช… ํ‚ค, ๋กœ๊ทธ์ธ ์Šคํ‚จ, ๊ฐ์‚ฌ ๋กœ๊ทธ CRUD
์ปค์Šคํ…€ ๋กœ๊ทธ์ธ ์Šคํ‚จ OIDC ํด๋ผ์ด์–ธํŠธ๋ณ„ ์ปค์Šคํ…€ CSS/์Šคํฌ๋ฆฝํŠธ, R2 ์บ์‹œ๋กœ ๋ฐฐํฌ
๊ฐ์‚ฌ ๋กœ๊ทธ ๋กœ๊ทธ์ธ, SSO, ํ† ํฐ ๋ฐœ๊ธ‰ ๋“ฑ ์ฃผ์š” ์ด๋ฒคํŠธ ์ž๋™ ๊ธฐ๋ก, ๊ด€๋ฆฌ์ž UI์—์„œ ์กฐํšŒ ๊ฐ€๋Šฅ
๊ตญ์ œํ™” ๋ฉ”์‹œ์ง€ ์นดํƒˆ๋กœ๊ทธ ๊ธฐ๋ฐ˜ i18n (ํ˜„์žฌ ํ•œ๊ตญ์–ด ์ œ๊ณต, ๋‹ค๊ตญ์–ด ํ™•์žฅ ๊ฐ€๋Šฅ)

ACR / AMR ๋งคํ•‘

์„ธ์…˜์˜ ์ธ์ฆ ๋ฐฉ์‹(AMR)์— ๋”ฐ๋ผ ACR์ด ์ž๋™์œผ๋กœ ๊ฒฐ์ •๋˜์–ด SAML Assertion ๋ฐ OIDC ID Token์— ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

์ธ์ฆ ๋ฐฉ์‹ AMR ๊ฐ’ ACR ๊ฐ’
๋น„๋ฐ€๋ฒˆํ˜ธ๋งŒ pwd urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
๋น„๋ฐ€๋ฒˆํ˜ธ + TOTP pwd, totp https://refeds.org/profile/mfa
๋น„๋ฐ€๋ฒˆํ˜ธ + ๋ฐฑ์—… ์ฝ”๋“œ pwd, swk https://refeds.org/profile/mfa
WebAuthn / Passkey hwk https://refeds.org/profile/mfa

SAML SP๊ฐ€ RequestedAuthnContext๋กœ ํŠน์ • ACR์„ ์š”๊ตฌํ•˜๋Š” ๊ฒฝ์šฐ, ์„ธ์…˜ ACR์ด ํ•ด๋‹น ์ˆ˜์ค€์„ ๋งŒ์กฑํ•˜์ง€ ์•Š์œผ๋ฉด ์žฌ์ธ์ฆ์ด ๊ฐ•์ œ๋ฉ๋‹ˆ๋‹ค. MFA ๋ฏธ์„ค์ • ๋“ฑ์œผ๋กœ ๋งŒ์กฑ ๋ถˆ๊ฐ€ ์‹œ NoAuthnContext ์˜ค๋ฅ˜๊ฐ€ ACS URL๋กœ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.

SAML SP๋ณ„ ์†์„ฑ ํ•„ํ„ฐ๋ง

๊ฐ SP์— allowedAttributes ๋ชฉ๋ก์„ ์„ค์ •ํ•˜์—ฌ Assertion์— ํฌํ•จํ•  ์†์„ฑ์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฏธ์„ค์ • ์‹œ email, username, displayName๋งŒ ๊ธฐ๋ณธ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ์กฐ์ง ์ •๋ณด(department, team, jobTitle, position)๋Š” SP๊ฐ€ ๋ช…์‹œ์ ์œผ๋กœ ํ—ˆ์šฉํ•œ ๊ฒฝ์šฐ์—๋งŒ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

๊ธฐ์ˆ  ์Šคํƒ

  • Runtime: Cloudflare Workers(๊ธฐ๋ณธ, nodejs_alsยทnodejs_compat) ๋˜๋Š” ์ˆœ์ˆ˜ Node ์„œ๋ฒ„(adapter-node, BUILD_TARGET=node) โ€” ๋ฐฐํฌ ํƒ€๊นƒ ์ฐธ๊ณ 
  • Framework: SvelteKit 2 + Svelte 5 (runes), @sveltejs/adapter-cloudflare
  • Database: Cloudflare D1(SQLite) / libSQL(SQLite) / PostgreSQL / MySQL ์ค‘ ํƒ1 (Drizzle ORM). PostgreSQLยทMySQL ์€ Hyperdrive ๋˜๋Š” DATABASE_URL ์ง๊ฒฐ (DB ๋ฐฉ์–ธ ์„ ํƒ ์ฐธ๊ณ )
  • Object Storage: Cloudflare R2 ๋˜๋Š” S3 ํ˜ธํ™˜(AWS S3ยทMinIO ๋“ฑ) โ€” ์ปค์Šคํ…€ ๋กœ๊ทธ์ธ ์Šคํ‚จ ์บ์‹œ
  • Styling: Tailwind CSS 4
  • Crypto: Web Crypto API (RSA/EC ์„œ๋ช…, argon2id ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•ด์‹œ), @simplewebauthn/*, xmldsigjs
  • Language / Tooling: TypeScript, Bun, ESLint, Prettier

๋””๋ ‰ํ„ฐ๋ฆฌ ๊ตฌ์กฐ

src/
โ”œโ”€โ”€ hooks.server.ts        # ์„ธ์…˜ ๋ณต์›, ๋ณด์•ˆ ํ—ค๋”, ํ…Œ๋„ŒํŠธ ์ปจํ…์ŠคํŠธ
โ”œโ”€โ”€ app.html
โ”œโ”€โ”€ routes/
โ”‚   โ”œโ”€โ”€ (auth)/            # login, signup, logout, mfa, find-id, find-password, reset-password
โ”‚   โ”œโ”€โ”€ account/           # profile, mfa, passkeys (๊ณ„์ • ์ž๊ฐ€ ๊ด€๋ฆฌ)
โ”‚   โ”œโ”€โ”€ admin/             # ๊ด€๋ฆฌ์ž UI (users, departments, teams, parts, positions,
โ”‚   โ”‚                      #            oidc-clients, saml-sps, ldap-providers,
โ”‚   โ”‚                      #            signing-keys, skins, audit, login)
โ”‚   โ”œโ”€โ”€ oidc/              # authorize, token, userinfo, jwks, end-session
โ”‚   โ”œโ”€โ”€ saml/              # sso, slo, metadata
โ”‚   โ””โ”€โ”€ api/               # webauthn/*, skin-scripts/*
โ””โ”€โ”€ lib/
    โ”œโ”€โ”€ i18n/              # ๋ฉ”์‹œ์ง€ ์นดํƒˆ๋กœ๊ทธ (ko.json)
    โ”œโ”€โ”€ assets/
    โ””โ”€โ”€ server/
        โ”œโ”€โ”€ auth/          # session, password, mfa, totp, webauthn, guards, bootstrap
        โ”œโ”€โ”€ oidc/          # client, grant, pkce, logout
        โ”œโ”€โ”€ saml/          # sp, metadata, parse-authn-request, response, slo
        โ”œโ”€โ”€ ldap/          # auth, client, provision
        โ”œโ”€โ”€ crypto/        # ์„œ๋ช… ํ‚ค ๊ด€๋ฆฌ, JWT ๋ฐœ๊ธ‰, ํ‚ค ํšŒ์ „
        โ”œโ”€โ”€ audit/         # ๊ฐ์‚ฌ ์ด๋ฒคํŠธ ๊ธฐ๋ก
        โ”œโ”€โ”€ org/           # ์กฐ์ง ๋ฉค๋ฒ„์‹ญ ์กฐํšŒ
        โ”œโ”€โ”€ ratelimit/     # ์ธ์ฆ ์—”๋“œํฌ์ธํŠธ ๋ ˆ์ดํŠธ ๋ฆฌ๋ฐ‹
        โ”œโ”€โ”€ skin/          # ์ปค์Šคํ…€ ๋กœ๊ทธ์ธ ์Šคํ‚จ
        โ””โ”€โ”€ db/            # Drizzle ์Šคํ‚ค๋งˆ ๋ฐ D1 ์ดˆ๊ธฐํ™”

drizzle/                   # ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ SQL (drizzle-kit generate ์‚ฐ์ถœ๋ฌผ)
docs/                      # ์„ค๊ณ„/๊ฐ์‚ฌ ๋ฌธ์„œ
scripts/setup.ts           # ๋Œ€ํ™”ํ˜• ์ดˆ๊ธฐ ์…‹์—… ์Šคํฌ๋ฆฝํŠธ

์—”๋“œํฌ์ธํŠธ ๊ฐœ์š”

OIDC (Discovery: /.well-known/openid-configuration)

๊ฒฝ๋กœ ์„ค๋ช…
/.well-known/openid-configuration OIDC Discovery ๋ฌธ์„œ
/oidc/authorize ์ธ์ฆ ์š”์ฒญ (Authorization Code + PKCE)
/oidc/token ํ† ํฐ ๊ตํ™˜ (Authorization Code, Refresh Token)
/oidc/userinfo UserInfo ์—”๋“œํฌ์ธํŠธ
/oidc/jwks JSON Web Key Set
/oidc/end-session RP-Initiated Logout

SAML 2.0

๊ฒฝ๋กœ ์„ค๋ช…
/saml/metadata IdP ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ XML
/saml/sso SP-Initiated SSO (AuthnRequest ์ฒ˜๋ฆฌ)
/saml/slo Single Logout (์ฒด์ธ SLO ์ง€์›)

WebAuthn / ๊ธฐํƒ€ API

๊ฒฝ๋กœ ์„ค๋ช…
/api/webauthn/register/options Passkey ๋“ฑ๋ก challenge ๋ฐœ๊ธ‰
/api/webauthn/register/verify Passkey ๋“ฑ๋ก attestation ๊ฒ€์ฆ
/api/webauthn/authenticate/options Passkey ์ธ์ฆ challenge ๋ฐœ๊ธ‰
/api/webauthn/authenticate/verify Passkey ์ธ์ฆ assertion ๊ฒ€์ฆ
/api/skin-scripts/* OIDC ํด๋ผ์ด์–ธํŠธ๋ณ„ ์ปค์Šคํ…€ ์Šคํ‚จ ์Šคํฌ๋ฆฝํŠธ

Service-to-Service TOTP API (DISPATCHER_SERVICE_TOKEN ํ•„์š”)

stardust dispatcher ๊ฐ™์€ ์‹ ๋ขฐ๋œ ์„œ๋น„์Šค๊ฐ€ ์‚ฌ์šฉ์ž ๋Œ€์‹  TOTP ๋“ฑ๋ก / ๊ฒ€์ฆ์„ ์œ„ํƒํ•˜๊ธฐ ์œ„ํ•œ API. ๋ชจ๋“  ์š”์ฒญ์€ Authorization: Bearer $DISPATCHER_SERVICE_TOKEN ํ—ค๋” ํ•„์š” (constant-time ๋น„๊ต).

๊ฒฝ๋กœ ๋ฉ”์„œ๋“œ ์„ค๋ช…
/api/totp/enroll/init POST {userId} โ†’ {secret, otpAuthUri, username} (stateless)
/api/totp/enroll/confirm POST {userId, secret, code} โ†’ ๊ฒ€์ฆ + ์˜๊ตฌ ์ €์žฅ + {backupCodes}
/api/totp/verify POST {userId, code} โ†’ step-up ๊ฒ€์ฆ + {ok, verifiedAt}
/api/totp/status GET ?userId=... โ†’ {enrolled, backupCodeCount, lastUsedAt}

์‹œ์ž‘ํ•˜๊ธฐ

์‚ฌ์ „ ์š”๊ตฌ์‚ฌํ•ญ

์„ค์น˜ ๋ฐ ์…‹์—…

git clone https://github.com/mack-erel/idp.git
cd idp
bun install
bun run setup

bun run setup์€ ์•„๋ž˜ ๊ณผ์ •์„ ๋Œ€ํ™”ํ˜•์œผ๋กœ ์•ˆ๋‚ดํ•ฉ๋‹ˆ๋‹ค. DB ๋ฐฉ์–ธ์€ --dialect(d1(๊ธฐ๋ณธ) | postgres | mysql | sqlite) ๋˜๋Š” DB_DIALECT ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ์„ ํƒํ•˜๋ฉฐ, ๋ฐฉ์–ธ์— ๋”ฐ๋ผ 3~5๋‹จ๊ณ„๊ฐ€ ๊ฐˆ๋ผ์ง‘๋‹ˆ๋‹ค:

  1. ์„ค์ • ํŒŒ์ผ ์ƒ์„ฑ โ€” wrangler.example.jsonc โ†’ wrangler.jsonc, .env.example โ†’ .env
  2. DB ๋ฐฉ์–ธ ๊ฒฐ์ • โ€” --dialect > DB_DIALECT env > .env > ํ”„๋กฌํ”„ํŠธ
  3. DB ์„ค์ •
    • d1: wrangler ๋กœ๊ทธ์ธ ํ™•์ธ ํ›„ D1 ์ƒˆ๋กœ ์ƒ์„ฑ ๋˜๋Š” ๊ธฐ์กด DB ์„ ํƒ (ํ”„๋ฆฌ๋ทฐ DB ์„ ํƒ์ ), DB IDยท๊ณ„์ • ID๋ฅผ wrangler.jsonc/.env์— ์ž๋™ ๊ธฐ์ž…
    • postgres/mysql/sqlite: DATABASE_URL ์ž…๋ ฅ(๋˜๋Š” ๊ธฐ์กด ๊ฐ’ ์žฌ์‚ฌ์šฉ), postgres/mysql ์€ Hyperdrive ๊ตฌ์„ฑ ID ์ž…๋ ฅ ์‹œ wrangler.jsonc ์˜ HYPERDRIVE ๋ฐ”์ธ๋”ฉ ์ž๋™ ์„ค์ •
  4. ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ โ€” ๋ฐฉ์–ธ๋ณ„ db:generate* ์‹คํ–‰ ํ›„ ์Šคํ‚ค๋งˆ ์ ์šฉ (D1 ์€ ์ถฉ๋Œ ํ…Œ์ด๋ธ” ๊ฐ์ง€ ๋ฐ ์ฒ˜๋ฆฌ ํฌํ•จ)
  5. ์ดˆ๊ธฐ ๊ด€๋ฆฌ์ž ๊ณ„์ • ์ƒ์„ฑ โ€” ์กฐ์ง๋ช…, ๊ด€๋ฆฌ์ž ๊ณ„์ • ์ •๋ณด, Issuer URL ์ž…๋ ฅ ํ›„ DB์— ์‹œ๋“œ (๋น„๋ฐ€๋ฒˆํ˜ธ ๋ฏธ์ž…๋ ฅ ์‹œ ์ž๋™ ์ƒ์„ฑ)
  6. ์„œ๋ช… ํ‚ค โ€” IDP_SIGNING_KEY_SECRET ์ž๋™ ์ƒ์„ฑ ๋˜๋Š” ์ง์ ‘ ์ž…๋ ฅ ํ›„ .env์— ์ €์žฅ
# ์˜ˆ: PostgreSQL ๋กœ ์…‹์—…
bun run setup -- --dialect postgres --database-url "postgres://user:pass@host:5432/db" --hyperdrive-id <id>

R2 ๋ฒ„ํ‚ท(keystone-skin-cache)์€ ์ปค์Šคํ…€ ๋กœ๊ทธ์ธ ์Šคํ‚จ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ๋•Œ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด wrangler.jsonc์˜ r2_buckets ํ•ญ๋ชฉ์„ ์ฃผ์„ ์ฒ˜๋ฆฌํ•ด๋„ ๋ฉ๋‹ˆ๋‹ค.

์…‹์—… ์™„๋ฃŒ ํ›„ ๋กœ์ปฌ ๊ฐœ๋ฐœ ์„œ๋ฒ„๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค:

bun run dev

ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ

bun run deploy

๋ฐฐํฌ ์ „ Wrangler Secret์œผ๋กœ ๋ฏผ๊ฐํ•œ ๊ฐ’์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค:

wrangler secret put IDP_SIGNING_KEY_SECRET

์ฐธ๊ณ : ๋กœ์ปฌ ๊ฐœ๋ฐœ์—์„œ๋Š” .env์— ํ‰๋ฌธ์œผ๋กœ ์ €์žฅํ•ด๋„ ๋ฌด๋ฐฉํ•˜์ง€๋งŒ, ํ”„๋กœ๋•์…˜์—์„œ๋Š” ๋ฐ˜๋“œ์‹œ Secret์œผ๋กœ ๊ด€๋ฆฌํ•˜์„ธ์š”.

ํ™˜๊ฒฝ๋ณ€์ˆ˜

๋ณ€์ˆ˜ ํ•„์ˆ˜ ์„ค๋ช…
IDP_ISSUER_URL โœ… OIDC/SAML ๋ฐœ๊ธ‰์ž URL (๋ฐฐํฌ ๋„๋ฉ”์ธ๊ณผ ์ผ์น˜). ํ”„๋กœ๋•์…˜ ํ•„์ˆ˜ โ€” ๋ฏธ์„ค์ • ์‹œ ์š”์ฒญ ์ดˆ๊ธฐ 503(fail-closed). dev ์—์„œ๋งŒ ์š”์ฒญ origin ์ž๋™ ๋Œ€์ฒด
IDP_SIGNING_KEY_SECRET โœ… ์„œ๋ช… ํ‚ค ์•”ํ˜ธํ™” KEK (ํ”„๋กœ๋•์…˜์€ ๋ฐ˜๋“œ์‹œ Secret). ํ”„๋กœ๋•์…˜ ํ•„์ˆ˜ โ€” ๋ฏธ์„ค์ • ์‹œ ์š”์ฒญ ์ดˆ๊ธฐ์— ์˜ค๋ฅ˜๋กœ ์ฐจ๋‹จ(fail-fast)
DISPATCHER_SERVICE_TOKEN ์„ ํƒ stardust dispatcher ๊ฐ€ /api/totp/* ํ˜ธ์ถœ ์‹œ ์‚ฌ์šฉํ•  Bearer ํ† ํฐ. ๋ฏธ์„ค์ •์ด๋ฉด ํ•ด๋‹น API 503
IDP_DEFAULT_TENANT_NAME ์„ ํƒ ๊ธฐ๋ณธ ํ…Œ๋„ŒํŠธ ์ด๋ฆ„ (๊ธฐ๋ณธ: My Organization)
CLOUDFLARE_ACCOUNT_ID ์„ ํƒ Cloudflare ๊ณ„์ • ID (๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์Šคํฌ๋ฆฝํŠธ์—์„œ ์‚ฌ์šฉ)
CLOUDFLARE_D1_DATABASE_ID ์„ ํƒ D1 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ID (๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์Šคํฌ๋ฆฝํŠธ์—์„œ ์‚ฌ์šฉ)
CLOUDFLARE_D1_PREVIEW_DATABASE_ID ์„ ํƒ ํ”„๋ฆฌ๋ทฐ์šฉ D1 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ID
CLOUDFLARE_D1_TOKEN ์„ ํƒ D1 API ํ† ํฐ (db:migrate ์Šคํฌ๋ฆฝํŠธ์—์„œ ์‚ฌ์šฉ)

์ฐธ๊ณ : ์ดˆ๊ธฐ ๊ด€๋ฆฌ์ž ๊ณ„์ •์€ bun run setup ์ด ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ˆ˜๋™/CI ์‹œ๋“œ๊ฐ€ ํ•„์š”ํ•˜๋ฉด IDP_BOOTSTRAP_ADMIN_USERNAME / IDP_BOOTSTRAP_ADMIN_EMAIL / IDP_BOOTSTRAP_ADMIN_PASSWORD (+์„ ํƒ IDP_BOOTSTRAP_ADMIN_NAME) ๋ฅผ ์„ค์ •ํ•˜๊ณ  bun run db:seed(๋ฐฉ์–ธ๋ณ„: db:seed:pg ๋“ฑ)๋ฅผ ์‹คํ–‰ํ•˜์„ธ์š”. ๋น„๋Œ€ํ™” ํ™˜๊ฒฝ์—์„œ๋Š” SEED_RESET=0|1 ๋กœ ์ดˆ๊ธฐํ™” ์—ฌ๋ถ€๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

Cloudflare ๋ฐ”์ธ๋”ฉ (wrangler.jsonc)

๋ฐ”์ธ๋”ฉ ์ข…๋ฅ˜ ์šฉ๋„
DB D1 ๋ฉ”์ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค (DB_DIALECT=d1 ์ผ ๋•Œ)
HYPERDRIVE Hyperdrive PostgreSQL/MySQL ์—ฐ๊ฒฐ (DB_DIALECT=postgres|mysql ์ผ ๋•Œ)
SKIN_CACHE R2 ์ปค์Šคํ…€ ๋กœ๊ทธ์ธ ์Šคํ‚จ ์บ์‹œ
ASSETS ์ •์  SvelteKit ๋นŒ๋“œ ์‚ฐ์ถœ๋ฌผ (.svelte-kit/cloudflare)

๊ฐœ๋ฐœ ์›Œํฌํ”Œ๋กœ

๋ช…๋ น ์„ค๋ช…
bun run dev Vite ๊ฐœ๋ฐœ ์„œ๋ฒ„ ์‹คํ–‰
bun run build ํ”„๋กœ๋•์…˜ ๋นŒ๋“œ
bun run preview Wrangler๋กœ ๋นŒ๋“œ ์‚ฐ์ถœ๋ฌผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ (localhost:4173)
bun run check wrangler types + svelte-check ํƒ€์ž… ๊ฒ€์‚ฌ
bun run lint Prettier + ESLint ๊ฒ€์‚ฌ
bun run format Prettier ์ž๋™ ํฌ๋งท
bun run gen Wrangler ํ™˜๊ฒฝ ํƒ€์ž… ์žฌ์ƒ์„ฑ
bun run db:generate Drizzle ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ SQL ์ƒ์„ฑ (D1)
bun run db:generate:sqlite Drizzle ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ SQL ์ƒ์„ฑ (libSQL/SQLite)
bun run db:generate:pg Drizzle ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ SQL ์ƒ์„ฑ (PostgreSQL)
bun run db:generate:mysql Drizzle ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ SQL ์ƒ์„ฑ (MySQL)
bun run db:migrate ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ ์šฉ + seed ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ (D1)
bun run db:migrate:pg ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ ์šฉ + seed ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ (PostgreSQL)
bun run db:seed ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ ์‹œ๋“œ โ€” ํ…Œ๋„ŒํŠธ/๊ด€๋ฆฌ์ž/์„œ๋น„์Šค role (D1)
bun run db:seed:pg ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ ์‹œ๋“œ (PostgreSQL)
bun run db:studio Drizzle Studio ์‹คํ–‰ (์Šคํ‚ค๋งˆ/๋ฐ์ดํ„ฐ GUI)
bun run deploy Cloudflare Workers ๋ฐฐํฌ

db:migrate / db:seed / db:push / db:studio ๋Š” :pg / :mysql / :sqlite ์ ‘๋ฏธ์‚ฌ๋กœ ๋ฐฉ์–ธ๋ณ„ ์‹คํ–‰์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. postgres/mysql/sqlite ๋Š” DATABASE_URL, D1 ์€ CLOUDFLARE_* ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. scripts/seed.ts ์™€ scripts/seed-migrate.ts ๋Š” ๋ฐฉ์–ธ ๋ฌด๊ด€(๊ณต์šฉ ํ—ฌํผ scripts/lib/db.ts)์ด๋ฉฐ, D1 ์€ REST API ๋กœ ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค.

DB ๋ฐฉ์–ธ ์„ ํƒ (d1 / sqlite / postgresql / mysql)

D1(SQLite)ยทlibSQL(SQLite)ยทPostgreSQLยทMySQL ์„ ์ง€์›ํ•˜๋ฉฐ, ๋ฐฐํฌ ๋‹จ์œ„๋กœ ํ•˜๋‚˜๋งŒ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. D1 ์€ ํŽธ์˜์„ฑ์ด ๋†’์ง€๋งŒ ์ง€์—ฐ์ด ํฐ ํŽธ์ด๋ผ, ๋‚ฎ์€ ์ง€์—ฐ์ด ํ•„์š”ํ•˜๋ฉด Cloudflare Hyperdrive ๋กœ PostgreSQL/MySQL ์— ์—ฐ๊ฒฐํ•˜๊ฑฐ๋‚˜ ์ž์ฒด DB ์— ์ง๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฐฉ์–ธ์€ DB_DIALECT ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค (d1(๊ธฐ๋ณธ) | sqlite | postgres | mysql). ์ด ๊ฐ’์€ ๋Ÿฐํƒ€์ž„๋ฟ ์•„๋‹ˆ๋ผ ๋นŒ๋“œยทํƒ€์ž…์ฒดํฌยท๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ƒ์„ฑ ์‹œ์ ์—๋„ ์ฐธ์กฐ๋˜์–ด ์Šคํ‚ค๋งˆยท๋“œ๋ผ์ด๋ฒ„๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.

๋ฐฉ์–ธ ๋“œ๋ผ์ด๋ฒ„ ์—ฐ๊ฒฐ ๋Œ€์ƒ
d1 drizzle-orm/d1 Cloudflare D1 (Workers ์ „์šฉ ๋ฐ”์ธ๋”ฉ)
sqlite libSQL ๋กœ์ปฌ ํŒŒ์ผ(file:) ๋˜๋Š” Turso (์ˆœ์ˆ˜ Node ๋“ฑ)
postgres postgres-js Hyperdrive ๋˜๋Š” DATABASE_URL ์ง๊ฒฐ
mysql mysql2 Hyperdrive ๋˜๋Š” DATABASE_URL ์ง๊ฒฐ
# PostgreSQL ๋กœ ๋นŒ๋“œยท๋ฐฐํฌ
DB_DIALECT=postgres bun run build
DB_DIALECT=postgres bun run deploy   # ๋˜๋Š” wrangler deploy

# MySQL ๋กœ ๋นŒ๋“œยท๋ฐฐํฌ
DB_DIALECT=mysql bun run build

# ๋กœ์ปฌ SQLite ํŒŒ์ผ(libSQL) ๋กœ Node ๊ตฌ๋™
BUILD_TARGET=node DB_DIALECT=sqlite DATABASE_URL="file:./keystone.db" bun run build && node build

๋™์ž‘ ๋ฐฉ์‹:

  • ์Šคํ‚ค๋งˆ๋Š” ๋ฐฉ์–ธ๋ณ„๋กœ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค: schema.sqlite.ts(d1ยทsqlite ๊ณต์šฉ) / schema.pg.ts / schema.mysql.ts. ์„ธ ์Šคํ‚ค๋งˆ๋Š” ํ…Œ์ด๋ธ”ยท์ปฌ๋Ÿผยท์ธ๋ฑ์Šค๋ช…๊ณผ JS ์ถ”๋ก  ํƒ€์ž…์ด ๋™์ผํ•˜๊ฒŒ ์œ ์ง€๋˜๋ฉฐ, ๋ฐฐ๋Ÿด schema.ts ๊ฐ€ DB_DIALECT ์— ๋งž๋Š” ํŒŒ์ผ๋กœ ํ•ด์„๋ฉ๋‹ˆ๋‹ค.
  • src/lib/server/db/index.ts ์˜ getDb() ๊ฐ€ ๋ฐฉ์–ธ์— ๋งž๋Š” ๋“œ๋ผ์ด๋ฒ„(d1 / libSQL / postgres-js / mysql2)๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. ๋นŒ๋“œ ์‹œ ํ™œ์„ฑ ๋ฐฉ์–ธ์˜ ๋“œ๋ผ์ด๋ฒ„๋งŒ ๋ฒˆ๋“ค์— ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.
  • ์—ฐ๊ฒฐ ๋ฌธ์ž์—ด ์šฐ์„ ์ˆœ์œ„: sqlite ๋Š” DATABASE_URL/SQLITE_URL(file: ์Šคํ‚ด ์—†์œผ๋ฉด ๋กœ์ปฌ ํŒŒ์ผ๋กœ ๊ฐ„์ฃผ). postgres/mysql ์€ Cloudflare ์—์„œ HYPERDRIVE ๋ฐ”์ธ๋”ฉ โ†’ DATABASE_URL(var/secret), ์ˆœ์ˆ˜ Node ์—์„œ DATABASE_URL.
# (Cloudflare) Hyperdrive ๊ตฌ์„ฑ ์ƒ์„ฑ (binding ์ด๋ฆ„์€ ๋ฐ˜๋“œ์‹œ HYPERDRIVE)
wrangler hyperdrive create keystone-pg    --connection-string="postgres://user:pass@host:5432/db"
wrangler hyperdrive create keystone-mysql --connection-string="mysql://user:pass@host:3306/db"
# โ†’ ์ถœ๋ ฅ๋œ id ๋ฅผ wrangler.jsonc ์˜ hyperdrive[].id ์— ์ฑ„์šฐ๊ณ  ์ฃผ์„ ํ•ด์ œ
#   (๋˜๋Š” bun run setup -- --dialect postgres --hyperdrive-id <id> ๋กœ ์ž๋™ ์„ค์ •)
# (Hyperdrive ์—†์ด ์ง๊ฒฐํ•˜๋ ค๋ฉด DATABASE_URL ์„ var/secret ์œผ๋กœ ์„ค์ •)

# ์Šคํ‚ค๋งˆ ์ ์šฉ + ์‹œ๋“œ (PostgreSQL ์˜ˆ์‹œ โ€” DATABASE_URL ์‚ฌ์šฉ)
bun run db:generate:pg && bun run db:migrate:pg
bun run db:seed:pg

๋ฐฐํฌ ํƒ€๊นƒ: Cloudflare Workers vs ์ˆœ์ˆ˜ Node

BUILD_TARGET(cloudflare(๊ธฐ๋ณธ) | node)์œผ๋กœ ์–ด๋Œ‘ํ„ฐ๋ฅผ ์ „ํ™˜ํ•ฉ๋‹ˆ๋‹ค. node ๋Š” @sveltejs/adapter-node ๋กœ ๋นŒ๋“œ๋˜์–ด Cloudflare ์—†์ด ์ˆœ์ˆ˜ Node ์„œ๋ฒ„๋กœ ๊ตฌ๋™๋ฉ๋‹ˆ๋‹ค. Node ์—๋Š” platform ๋ฐ”์ธ๋”ฉ์ด ์—†์œผ๋ฏ€๋กœ PostgreSQL/MySQL/sqlite(libSQL) ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ(D1 ์€ Workers ์ „์šฉ), ์—ฐ๊ฒฐ์€ DATABASE_URL, ์„ค์ •๊ฐ’์€ process.env ๋กœ ์ฝ์Šต๋‹ˆ๋‹ค.

# PostgreSQL + ์ˆœ์ˆ˜ Node ๋กœ ๋นŒ๋“œยท๊ตฌ๋™
export BUILD_TARGET=node DB_DIALECT=postgres
export DATABASE_URL="postgres://user:pass@host:5432/db"
# ํ•„์š”ํ•œ ์„ค์ •๋„ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ: IDP_SIGNING_KEY_SECRET, IDP_ISSUER_URL, IDP_DEFAULT_TENANT_NAME ...
bun run build
node build            # adapter-node ์‚ฐ์ถœ๋ฌผ (๊ธฐ๋ณธ PORT=3000)

์ฐธ๊ณ : MySQL ์€ UPDATE ... RETURNING ๊ณผ partial(๋ถ€๋ถ„) unique index ๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ๋กœ์ง(์ธ๊ฐ€ ์ฝ”๋“œ/WebAuthn challenge ์†Œ์ง„, signing key ํšŒ์ „, rate limit upsert)์€ ๋ฐฉ์–ธ๋ณ„๋กœ ๋ถ„๊ธฐ๋˜์–ด MySQL ์—์„œ๋Š” affectedRows ํŒ์ •ยท์žฌ์กฐํšŒ์™€ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋™๋“ฑํ•˜๊ฒŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

์Šคํ‚จ ์บ์‹œ ์Šคํ† ๋ฆฌ์ง€: R2 ๋˜๋Š” S3 ํ˜ธํ™˜

์ปค์Šคํ…€ ๋กœ๊ทธ์ธ ์Šคํ‚จ ์บ์‹œ๋Š” ๋‹ค์Œ ์šฐ์„ ์ˆœ์œ„๋กœ ์Šคํ† ๋ฆฌ์ง€๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค (๋ฏธ์„ค์ • ์‹œ ์บ์‹œ ์—†์ด ๋งค๋ฒˆ ์›๋ณธ fetch โ€” ์ •์ƒ ๋™์ž‘).

  1. Cloudflare R2 ๋ฐ”์ธ๋”ฉ(SKIN_CACHE) ์ด ์žˆ์œผ๋ฉด R2 ์‚ฌ์šฉ.
  2. ์—†๊ณ  S3 ํ˜ธํ™˜ ์„ค์ •(S3_ENDPOINT/S3_BUCKET/S3_ACCESS_KEY_ID/S3_SECRET_ACCESS_KEY)์ด ์žˆ์œผ๋ฉด S3 ํ˜ธํ™˜ ์Šคํ† ๋ฆฌ์ง€ ์‚ฌ์šฉ.

R2 ์ž์ฒด๊ฐ€ S3 ํ˜ธํ™˜์ด๋ฏ€๋กœ AWS S3ยทMinIOยทCeph ๋Š” ๋ฌผ๋ก  R2 ์˜ S3 endpoint ๋„ ๊ทธ๋Œ€๋กœ ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์„œ๋ช…์€ aws4fetch ๋กœ ํ•˜๋ฉฐ Workers/Node ์–‘์ชฝ์—์„œ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. path-style/virtual-host ๋Š” S3_FORCE_PATH_STYLE(๊ธฐ๋ณธ true, MinIO/R2 ๊ถŒ์žฅ)๋กœ ์ œ์–ดํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋ณ€์ˆ˜๋Š” .env.example ์ฐธ๊ณ .

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜

์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝ โ†’ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ƒ์„ฑ โ†’ ์ ์šฉ์˜ ํ๋ฆ„์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

# 1. ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์–ธ์˜ ์Šคํ‚ค๋งˆ ํŒŒ์ผ ์ˆ˜์ •
#    - D1 / sqlite โ†’ src/lib/server/db/schema.sqlite.ts (๊ณต์šฉ)
#    - PostgreSQL  โ†’ src/lib/server/db/schema.pg.ts
#    - MySQL       โ†’ src/lib/server/db/schema.mysql.ts
#    (์„ธ ์Šคํ‚ค๋งˆ์˜ ๊ตฌ์กฐ/ํƒ€์ž…์€ ๋™์ผํ•˜๊ฒŒ ์œ ์ง€ํ•  ๊ฒƒ)

# 2. SQL ์ƒ์„ฑ (์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์–ธ์— ๋งž๊ฒŒ)
bun run db:generate          # D1         โ†’ drizzle/*.sql
bun run db:generate:sqlite   # libSQL     โ†’ drizzle/sqlite/*.sql (DDL ์€ D1 ๊ณผ ๋™์ผ)
bun run db:generate:pg       # PostgreSQL โ†’ drizzle/pg/*.sql
bun run db:generate:mysql    # MySQL      โ†’ drizzle/mysql/*.sql

# 3. ์ƒ์„ฑ๋œ SQL ํŒŒ์ผ ๊ฒ€ํ† 

# 4. ์›๊ฒฉ DB์— ์ ์šฉ (์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ์‹คํ–‰)
bun run db:migrate          # D1 ํ”„๋กœ๋•์…˜
bun run db:migrate:preview  # D1 ํ”„๋ฆฌ๋ทฐ
# PostgreSQL/MySQL ์€ DATABASE_URL ์„ค์ • ํ›„ drizzle-kit migrate ๋กœ ์ ์šฉ

โš ๏ธ ์›๊ฒฉ D1์— ๋Œ€ํ•œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ ์šฉ์€ ๋˜๋Œ๋ฆฌ๊ธฐ ์–ด๋ ค์šฐ๋ฏ€๋กœ, ์ž๋™ํ™”๋œ ์Šคํฌ๋ฆฝํŠธ๋‚˜ ์—์ด์ „ํŠธ๊ฐ€ ์ž„์˜๋กœ ์‹คํ–‰ํ•˜์ง€ ์•Š๋„๋ก ์šด์˜ ์ •์ฑ…์— ํฌํ•จ์‹œํ‚ค๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

๋ณด์•ˆ ์ฐธ๊ณ ์‚ฌํ•ญ

๋น„๋ฐ€๋ฒˆํ˜ธ ํ•ด์‹ฑ

์‹ ๊ทœ ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” argon2id (@hicaru/argon2-pure.js, Workers ํ˜ธํ™˜ ์ˆœ์ˆ˜ JS ๊ตฌํ˜„)๋กœ ํ•ด์‹ฑ๋ฉ๋‹ˆ๋‹ค. ๊ณผ๊ฑฐ PBKDF2-SHA256 (100,000 iterations)์œผ๋กœ ์ €์žฅ๋œ ๋ ˆ๊ฑฐ์‹œ ํ•ด์‹œ๋Š” ๋กœ๊ทธ์ธ ๊ฒ€์ฆ ์‹œ ์ž๋™์œผ๋กœ argon2id๋กœ ์žฌํ•ด์‹ฑ๋ฉ๋‹ˆ๋‹ค.

์„œ๋ช… ํ‚ค

OIDC ID Token ๋ฐ SAML Response ์„œ๋ช…์— ์‚ฌ์šฉ๋˜๋Š” RSA ํ‚ค๋Š” IDP_SIGNING_KEY_SECRET์œผ๋กœ ์•”ํ˜ธํ™”๋˜์–ด D1์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ์ด ์‹œํฌ๋ฆฟ์ด ์œ ์ถœ๋˜๋ฉด ๋ชจ๋“  ์„œ๋ช… ํ‚ค๊ฐ€ ๋ณตํ˜ธํ™”๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋ฐ˜๋“œ์‹œ ๊ฐ•ํ•œ ๋žœ๋ค๊ฐ’(openssl rand -base64 32)์„ ์‚ฌ์šฉํ•˜๊ณ  ์ •๊ธฐ์ ์œผ๋กœ ๊ต์ฒดํ•˜์„ธ์š”. ํ‚ค ํšŒ์ „์€ ๊ด€๋ฆฌ์ž UI(/admin/signing-keys)์—์„œ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

WebAuthn challenge

WebAuthn ๋“ฑ๋กยท์ธ์ฆ challenge๋Š” D1์— 1ํšŒ์šฉ์œผ๋กœ ์ €์žฅ๋˜๋ฉฐ, ์†Œ์ง„ ์ฆ‰์‹œ ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค. challenge๋Š” ํ…Œ๋„ŒํŠธ ID๋กœ ๊ฒฉ๋ฆฌ๋˜์–ด ๋‹ค๋ฅธ ํ…Œ๋„ŒํŠธ์˜ challenge๋ฅผ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

LDAP ๊ณ„์ • ์—ฐ๊ฒฐ ์ •์ฑ…

LDAP ์ธ์ฆ ์„ฑ๊ณต ์‹œ, ๋™์ผ ์ด๋ฉ”์ผ์˜ ๊ธฐ์กด ๋กœ์ปฌ ๊ณ„์ •์ด ์žˆ๋Š” ๊ฒฝ์šฐ ์ž๋™ ์—ฐ๊ฒฐํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. LDAP ํ”„๋กœ๋ฐ”์ด๋”๊ฐ€ ์ด๋ฉ”์ผ์„ ์กฐ์ž‘ํ•ด ๊ธฐ์กด ๊ด€๋ฆฌ์ž ๊ณ„์ •์„ ํƒˆ์ทจํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค. ๊ธฐ์กด ๋กœ์ปฌ ๊ณ„์ •๊ณผ์˜ ์—ฐ๊ฒฐ์€ ๊ด€๋ฆฌ์ž๊ฐ€ ์ง์ ‘ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋ณด์•ˆ ํ—ค๋”

hooks.server.ts์—์„œ ๋ชจ๋“  ์‘๋‹ต์— ๋‹ค์Œ ํ—ค๋”๋ฅผ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • X-Frame-Options: DENY
  • X-Content-Type-Options: nosniff
  • Referrer-Policy: strict-origin-when-cross-origin
  • Strict-Transport-Security (HSTS)
  • Permissions-Policy (camera/microphone/geolocation/payment ๋น„ํ™œ์„ฑ)
  • ํ•ด์‹œ ๊ธฐ๋ฐ˜ Content-Security-Policy

๋ถ€ํŠธ์ŠคํŠธ๋žฉ ๊ด€๋ฆฌ์ž

์ดˆ๊ธฐ ๊ด€๋ฆฌ์ž ๊ณ„์ •์€ bun run setup ์‹คํ–‰ ์‹œ D1์— ์ง์ ‘ ์‚ฝ์ž…๋ฉ๋‹ˆ๋‹ค. ์…‹์—… ์™„๋ฃŒ ํ›„ ๊ฐ€๋Šฅํ•œ ๋นจ๋ฆฌ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ  MFA๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

๋ผ์ด์„ ์Šค

MIT

About

No description, website, or topics provided.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors