메인 μ½˜ν…μΈ λ‘œ κ±΄λ„ˆλ›°κΈ°
SonamuλŠ” better-authλ₯Ό 기반으둜 ν•˜λŠ” 인증 μ‹œμŠ€ν…œμ„ μ œκ³΅ν•©λ‹ˆλ‹€. 이메일/λΉ„λ°€λ²ˆν˜Έ 인증, μ†Œμ…œ 둜그인 λ“± λ‹€μ–‘ν•œ 인증 방식을 μ§€μ›ν•˜λ©°, /api/auth/* 경둜둜 인증 APIκ°€ μžλ™ λ“±λ‘λ©λ‹ˆλ‹€.

기본 ꡬ쑰

import { defineConfig } from "sonamu";

export default defineConfig({
  server: {
    // κΈ°λ³Έ μ„€μ •μœΌλ‘œ 인증 ν™œμ„±ν™”
    auth: {
      emailAndPassword: {
        enabled: true,
      },
    },

    // λ˜λŠ” 상세 μ„€μ •
    auth: {
      basePath: "/api/auth",
      emailAndPassword: {
        enabled: true,
      },
      socialProviders: {
        google: {
          clientId: process.env.GOOGLE_CLIENT_ID!,
          clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
        },
      },
    },
  },
  // ...
});

auth μ„€μ •

νƒ€μž…

νƒ€μž…: BetterAuthOptions (better-auth 라이브러리의 μ„€μ • νƒ€μž…)
export default defineConfig({
  server: {
    auth: {
      // better-auth μ„€μ • μ˜΅μ…˜
    },
  },
});

이메일/λΉ„λ°€λ²ˆν˜Έ 인증

export default defineConfig({
  server: {
    auth: {
      emailAndPassword: {
        enabled: true,
        // 선택: λΉ„λ°€λ²ˆν˜Έ μ΅œμ†Œ 길이
        minPasswordLength: 8,
      },
    },
  },
});

μ†Œμ…œ 둜그인 (Google)

export default defineConfig({
  server: {
    auth: {
      emailAndPassword: {
        enabled: true,
      },
      socialProviders: {
        google: {
          clientId: process.env.GOOGLE_CLIENT_ID!,
          clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
        },
      },
    },
  },
});

μ†Œμ…œ 둜그인 (GitHub)

export default defineConfig({
  server: {
    auth: {
      socialProviders: {
        github: {
          clientId: process.env.GITHUB_CLIENT_ID!,
          clientSecret: process.env.GITHUB_CLIENT_SECRET!,
        },
      },
    },
  },
});

basePath μ»€μŠ€ν„°λ§ˆμ΄μ§•

κΈ°λ³Έ κ²½λ‘œλŠ” /api/authμž…λ‹ˆλ‹€. 변경이 ν•„μš”ν•œ 경우:
export default defineConfig({
  server: {
    auth: {
      basePath: "/auth", // /auth/* 둜 λ³€κ²½
      emailAndPassword: {
        enabled: true,
      },
    },
  },
});

μ—”ν‹°ν‹° 생성

better-authλ₯Ό μ‚¬μš©ν•˜λ €λ©΄ λ¨Όμ € ν•„μš”ν•œ 엔티티듀을 생성해야 ν•©λ‹ˆλ‹€.

CLI둜 μ—”ν‹°ν‹° 생성

pnpm sonamu better-auth
이 λͺ…령은 λ‹€μŒ 엔티티듀을 μƒμ„±ν•©λ‹ˆλ‹€:
μ—”ν‹°ν‹°ν…Œμ΄λΈ”μ„€λͺ…
Userusersμ‚¬μš©μž 정보
Sessionsessionsμ„Έμ…˜ 정보
AccountaccountsOAuth 계정 연동 정보
Verificationverifications이메일 인증 λ“±
이미 User μ—”ν‹°ν‹°κ°€ μžˆλŠ” 경우, λͺ…λ Ή μ‹€ν–‰ μ‹œ λˆ„λ½λœ ν•„λ“œλ§Œ μΆ”κ°€λ©λ‹ˆλ‹€.

ν”ŒλŸ¬κ·ΈμΈκ³Ό ν•¨κ»˜ μ—”ν‹°ν‹° 생성

better-auth ν”ŒλŸ¬κ·ΈμΈμ„ μ‚¬μš©ν•˜λ €λ©΄ --plugins μ˜΅μ…˜μœΌλ‘œ ν•„μš”ν•œ ν”ŒλŸ¬κ·ΈμΈλ“€μ„ μ§€μ •ν•©λ‹ˆλ‹€:
# 단일 ν”ŒλŸ¬κ·ΈμΈ
pnpm sonamu better-auth --plugins=2fa

# μ—¬λŸ¬ ν”ŒλŸ¬κ·ΈμΈ (μ‰Όν‘œλ‘œ ꡬ뢄)
pnpm sonamu better-auth --plugins=2fa,admin,username
μ§€μ›ν•˜λŠ” ν”ŒλŸ¬κ·ΈμΈ:
ν”ŒλŸ¬κ·ΈμΈ IDμ„€λͺ…μΆ”κ°€λ˜λŠ” ν•„λ“œ/ν…Œμ΄λΈ”
2fa2단계 인증 (TOTP)TwoFactor ν…Œμ΄λΈ”, User.two_factor_enabled
adminκ΄€λ¦¬μž κΈ°λŠ₯User.role, User.banned, User.ban_reason, User.ban_expires, Session.impersonated_by
usernameμ‚¬μš©μžλͺ… 둜그인User.username (unique), User.display_username
phone-numberμ „ν™”λ²ˆν˜Έ 인증User.phone_number (unique), User.phone_number_verified
passkeyWebAuthn νŒ¨μŠ€ν‚€ 인증Passkey ν…Œμ΄λΈ”
ssoSSO 둜그인 (OIDC/SAML)SsoProvider ν…Œμ΄λΈ”
api-keyAPI ν‚€ 인증ApiKey ν…Œμ΄λΈ”
jwtJWT 토큰 λ°œκΈ‰Jwks ν…Œμ΄λΈ”
organization쑰직/νŒ€ 관리Organization, Member, Invitation, Team, TeamMember ν…Œμ΄λΈ”, Session.active_organization_id, Session.active_team_id
anonymous읡λͺ… μ‚¬μš©μžUser.is_anonymous
ν”ŒλŸ¬κ·ΈμΈ μ‚¬μš© μ‹œ sonamu.config.tsμ—μ„œλ„ ν•΄λ‹Ή ν”ŒλŸ¬κ·ΈμΈμ„ ν™œμ„±ν™”ν•΄μ•Ό ν•©λ‹ˆλ‹€. μžμ„Έν•œ λ‚΄μš©μ€ μ•„λž˜ ν”ŒλŸ¬κ·ΈμΈ μ„€μ • μ„Ήμ…˜μ„ μ°Έμ‘°ν•˜μ„Έμš”.

ν•„λ“œ λ§€ν•‘

SonamuλŠ” snake_case 컬럼λͺ…을 μ‚¬μš©ν•˜λ―€λ‘œ, better-auth의 camelCase ν•„λ“œλͺ…이 μžλ™μœΌλ‘œ λ§€ν•‘λ©λ‹ˆλ‹€:
// better-auth β†’ Sonamu
emailVerified β†’ email_verified
createdAt β†’ created_at
updatedAt β†’ updated_at
ipAddress β†’ ip_address
userAgent β†’ user_agent
userId β†’ user_id
// ...

인증 API

better-authκ°€ λ“±λ‘λ˜λ©΄ λ‹€μŒ API듀이 μžλ™μœΌλ‘œ μ‚¬μš© κ°€λŠ₯ν•©λ‹ˆλ‹€:

νšŒμ›κ°€μž…

POST /api/auth/sign-up/email
Content-Type: application/json

{
  "name": "홍길동",
  "email": "user@example.com",
  "password": "password123"
}

둜그인

POST /api/auth/sign-in/email
Content-Type: application/json

{
  "email": "user@example.com",
  "password": "password123"
}

λ‘œκ·Έμ•„μ›ƒ

POST /api/auth/sign-out

ν˜„μž¬ μ„Έμ…˜

GET /api/auth/get-session

μ†Œμ…œ 둜그인 (Google)

GET /api/auth/sign-in/social?provider=google
전체 API λͺ©λ‘μ€ better-auth λ¬Έμ„œλ₯Ό μ°Έμ‘°ν•˜μ„Έμš”.

Contextμ—μ„œ μ‚¬μš©μž 정보 μ ‘κ·Ό

인증된 μš”μ²­μ—μ„œλŠ” Contextλ₯Ό 톡해 μ‚¬μš©μž 정보에 μ ‘κ·Όν•  수 μžˆμŠ΅λ‹ˆλ‹€.
import { api, Sonamu } from "sonamu";

export class MyModel {
  @api()
  static async myApi() {
    const ctx = Sonamu.getContext();

    // ν˜„μž¬ λ‘œκ·ΈμΈν•œ μ‚¬μš©μž
    const user = ctx.user; // User | null

    // ν˜„μž¬ μ„Έμ…˜ 정보
    const session = ctx.session; // Session | null

    if (!user) {
      throw new UnauthorizedError("둜그인이 ν•„μš”ν•©λ‹ˆλ‹€");
    }

    return { userId: user.id, userName: user.name };
  }
}

User νƒ€μž…

type User = {
  id: string;
  name: string;
  email: string;
  emailVerified: boolean;
  image: string | null;
  createdAt: Date;
  updatedAt: Date;
};

Session νƒ€μž…

type Session = {
  id: string;
  expiresAt: Date;
  token: string;
  createdAt: Date;
  updatedAt: Date;
  ipAddress: string | null;
  userAgent: string | null;
  userId: string;
};

Guardλ₯Ό μ΄μš©ν•œ μ ‘κ·Ό μ œμ–΄

import { api } from "sonamu";

export class AdminModel {
  @api({ guards: ["admin"] })
  static async adminOnly() {
    // admin guardκ°€ ν†΅κ³Όν•œ κ²½μš°μ—λ§Œ μ‹€ν–‰
    return { message: "κ΄€λ¦¬μž μ „μš© API" };
  }
}
guardHandler μ„€μ •:
export default defineConfig({
  server: {
    auth: {
      emailAndPassword: { enabled: true },
    },

    apiConfig: {
      guardHandler: async (guard, request) => {
        // Contextμ—μ„œ user κ°€μ Έμ˜€κΈ°
        const { user } = Sonamu.getContext();

        if (guard === "auth" && !user) {
          throw new UnauthorizedError("둜그인이 ν•„μš”ν•©λ‹ˆλ‹€");
        }

        if (guard === "admin") {
          if (!user) {
            throw new UnauthorizedError("둜그인이 ν•„μš”ν•©λ‹ˆλ‹€");
          }
          // User 엔티티에 role ν•„λ“œκ°€ μžˆλ‹€κ³  κ°€μ •
          const fullUser = await UserModel.findById("A", user.id);
          if (fullUser.role !== "admin") {
            throw new UnauthorizedError("κ΄€λ¦¬μž κΆŒν•œμ΄ ν•„μš”ν•©λ‹ˆλ‹€");
          }
        }
      },
    },
  },
});

ν΄λΌμ΄μ–ΈνŠΈ μΈ‘ 연동

Reactμ—μ„œ μ‚¬μš©

// lib/auth-client.ts
import { createAuthClient } from "better-auth/react";

export const authClient = createAuthClient({
  baseURL: "http://localhost:4000",
});
// components/LoginForm.tsx
import { authClient } from "../lib/auth-client";

export function LoginForm() {
  const handleLogin = async (email: string, password: string) => {
    const result = await authClient.signIn.email({
      email,
      password,
    });

    if (result.error) {
      alert(result.error.message);
      return;
    }

    // 둜그인 성곡
    window.location.href = "/dashboard";
  };

  // ...
}
// components/GoogleLoginButton.tsx
import { authClient } from "../lib/auth-client";

export function GoogleLoginButton() {
  const handleGoogleLogin = () => {
    authClient.signIn.social({
      provider: "google",
      callbackURL: "/dashboard",
    });
  };

  return <button onClick={handleGoogleLogin}>Google둜 둜그인</button>;
}

ν”ŒλŸ¬κ·ΈμΈ μ„€μ •

better-auth ν”ŒλŸ¬κ·ΈμΈμ„ μ‚¬μš©ν•˜λ €λ©΄ sonamu.config.ts의 auth.plugins 배열에 ν”ŒλŸ¬κ·ΈμΈμ„ μΆ”κ°€ν•©λ‹ˆλ‹€.
ν”ŒλŸ¬κ·ΈμΈμ„ μ„€μ •ν•˜κΈ° 전에 λ¨Όμ € pnpm sonamu better-auth --plugins=... λͺ…λ ΉμœΌλ‘œ ν•„μš”ν•œ 엔티티와 ν•„λ“œλ₯Ό 생성해야 ν•©λ‹ˆλ‹€.

2단계 인증 (2FA)

TOTP 기반 2단계 인증을 ν™œμ„±ν™”ν•©λ‹ˆλ‹€:
import { defineConfig } from "sonamu";
import { twoFactor, TWO_FACTOR_SCHEMA } from "sonamu/auth";

export default defineConfig({
  server: {
    auth: {
      emailAndPassword: { enabled: true },
      plugins: [
        twoFactor({
          issuer: "My App", // OTP 앱에 ν‘œμ‹œλ  이름
          schema: TWO_FACTOR_SCHEMA,
        }),
      ],
    },
  },
});
2FA κ΄€λ ¨ API:
  • POST /api/auth/two-factor/enable - 2FA ν™œμ„±ν™” μ‹œμž‘
  • POST /api/auth/two-factor/verify - 2FA μ½”λ“œ 검증
  • POST /api/auth/two-factor/disable - 2FA λΉ„ν™œμ„±ν™”

κ΄€λ¦¬μž ν”ŒλŸ¬κ·ΈμΈ (Admin)

μ‚¬μš©μž μ—­ν• , 차단 κΈ°λŠ₯, λŒ€λ¦¬ 둜그인(impersonation)을 μ§€μ›ν•©λ‹ˆλ‹€:
import { defineConfig } from "sonamu";
import { admin, ADMIN_SCHEMA } from "sonamu/auth";

export default defineConfig({
  server: {
    auth: {
      emailAndPassword: { enabled: true },
      plugins: [
        admin({
          schema: ADMIN_SCHEMA,
        }),
      ],
    },
  },
});
Admin ν”ŒλŸ¬κ·ΈμΈμ΄ User ν…Œμ΄λΈ”μ— μΆ”κ°€ν•˜λŠ” ν•„λ“œ:
  • role - μ‚¬μš©μž μ—­ν•  (κΈ°λ³Έκ°’: β€œuser”)
  • banned - 차단 μ—¬λΆ€
  • ban_reason - 차단 μ‚¬μœ 
  • ban_expires - 차단 만료 μ‹œκ°„ (Unix timestamp)

μ‚¬μš©μžλͺ… ν”ŒλŸ¬κ·ΈμΈ (Username)

이메일 λŒ€μ‹  μ‚¬μš©μžλͺ…μœΌλ‘œ λ‘œκ·ΈμΈν•  수 μžˆμŠ΅λ‹ˆλ‹€:
import { defineConfig } from "sonamu";
import { username, USERNAME_SCHEMA } from "sonamu/auth";

export default defineConfig({
  server: {
    auth: {
      plugins: [
        username({
          schema: USERNAME_SCHEMA,
        }),
      ],
    },
  },
});
Username ν”ŒλŸ¬κ·ΈμΈμ΄ User ν…Œμ΄λΈ”μ— μΆ”κ°€ν•˜λŠ” ν•„λ“œ:
  • username - μ •κ·œν™”λœ μ‚¬μš©μžλͺ… (μ†Œλ¬Έμž, unique 인덱슀)
  • display_username - ν‘œμ‹œμš© μ‚¬μš©μžλͺ… (원본 μΌ€μ΄μŠ€ μœ μ§€)

μ „ν™”λ²ˆν˜Έ ν”ŒλŸ¬κ·ΈμΈ (Phone Number)

μ „ν™”λ²ˆν˜Έ 인증을 μ§€μ›ν•©λ‹ˆλ‹€:
import { defineConfig } from "sonamu";
import { phoneNumber, PHONE_NUMBER_SCHEMA } from "sonamu/auth";

export default defineConfig({
  server: {
    auth: {
      plugins: [
        phoneNumber({
          schema: PHONE_NUMBER_SCHEMA,
        }),
      ],
    },
  },
});
Phone Number ν”ŒλŸ¬κ·ΈμΈμ΄ User ν…Œμ΄λΈ”μ— μΆ”κ°€ν•˜λŠ” ν•„λ“œ:
  • phone_number - μ „ν™”λ²ˆν˜Έ (unique 인덱슀)
  • phone_number_verified - μ „ν™”λ²ˆν˜Έ 인증 μ—¬λΆ€

νŒ¨μŠ€ν‚€ ν”ŒλŸ¬κ·ΈμΈ (Passkey)

WebAuthn/FIDO2 기반 νŒ¨μŠ€ν‚€ 인증을 μ§€μ›ν•©λ‹ˆλ‹€:
import { defineConfig } from "sonamu";
import { passkey, PASSKEY_SCHEMA } from "sonamu/auth";

export default defineConfig({
  server: {
    auth: {
      emailAndPassword: { enabled: true },
      plugins: [
        passkey({
          schema: PASSKEY_SCHEMA,
        }),
      ],
    },
  },
});
Passkey ν”ŒλŸ¬κ·ΈμΈμ΄ μƒμ„±ν•˜λŠ” ν…Œμ΄λΈ”:
  • passkeys - μ‚¬μš©μžμ˜ νŒ¨μŠ€ν‚€ 정보 (κ³΅κ°œν‚€, 자격 증λͺ… ID λ“±)
Passkey κ΄€λ ¨ API:
  • POST /api/auth/passkey/generate-register-options - νŒ¨μŠ€ν‚€ 등둝 μ˜΅μ…˜ 생성
  • POST /api/auth/passkey/verify-registration - νŒ¨μŠ€ν‚€ 등둝 검증
  • POST /api/auth/passkey/generate-authentication-options - νŒ¨μŠ€ν‚€ 인증 μ˜΅μ…˜ 생성
  • POST /api/auth/passkey/verify-authentication - νŒ¨μŠ€ν‚€ 인증 검증
Passkey ν”ŒλŸ¬κ·ΈμΈ μ‚¬μš© μ‹œ @better-auth/passkey νŒ¨ν‚€μ§€κ°€ ν•„μš”ν•©λ‹ˆλ‹€.

SSO ν”ŒλŸ¬κ·ΈμΈ

μ™ΈλΆ€ IdP(OIDC, SAML)λ₯Ό ν†΅ν•œ SSO λ‘œκ·ΈμΈμ„ μ§€μ›ν•©λ‹ˆλ‹€:
import { defineConfig } from "sonamu";
import { sso, SSO_SCHEMA } from "sonamu/auth";

export default defineConfig({
  server: {
    auth: {
      plugins: [
        sso({
          ...SSO_SCHEMA,
        }),
      ],
    },
  },
});
SSO ν”ŒλŸ¬κ·ΈμΈμ΄ μƒμ„±ν•˜λŠ” ν…Œμ΄λΈ”:
  • sso_providers - SSO 제곡자 μ„€μ • (OIDC/SAML μ„€μ • 포함)
SSO ν”ŒλŸ¬κ·ΈμΈ μ‚¬μš© μ‹œ @better-auth/sso νŒ¨ν‚€μ§€κ°€ ν•„μš”ν•©λ‹ˆλ‹€.

API ν‚€ ν”ŒλŸ¬κ·ΈμΈ (API Key)

API ν‚€ 기반 인증을 μ§€μ›ν•©λ‹ˆλ‹€:
import { defineConfig } from "sonamu";
import { apiKey, API_KEY_SCHEMA } from "sonamu/auth";

export default defineConfig({
  server: {
    auth: {
      plugins: [
        apiKey({
          schema: API_KEY_SCHEMA,
        }),
      ],
    },
  },
});
API Key ν”ŒλŸ¬κ·ΈμΈμ΄ μƒμ„±ν•˜λŠ” ν…Œμ΄λΈ”:
  • api_keys - API ν‚€ 정보 (ν•΄μ‹œλœ ν‚€, Rate Limit μ„€μ • λ“±)
API Key κ΄€λ ¨ API:
  • POST /api/auth/api-key/create - API ν‚€ 생성
  • POST /api/auth/api-key/revoke - API ν‚€ 폐기
  • GET /api/auth/api-key/list - API ν‚€ λͺ©λ‘ 쑰회

JWT ν”ŒλŸ¬κ·ΈμΈ

JWT 토큰 λ°œκΈ‰ 및 JWKS ν‚€ 관리λ₯Ό μ§€μ›ν•©λ‹ˆλ‹€:
import { defineConfig } from "sonamu";
import { jwt, JWT_SCHEMA } from "sonamu/auth";

export default defineConfig({
  server: {
    auth: {
      plugins: [
        jwt({
          schema: JWT_SCHEMA,
        }),
      ],
    },
  },
});
JWT ν”ŒλŸ¬κ·ΈμΈμ΄ μƒμ„±ν•˜λŠ” ν…Œμ΄λΈ”:
  • jwks - JSON Web Key Set 정보 (κ³΅κ°œν‚€, λΉ„λ°€ν‚€)
JWT κ΄€λ ¨ API:
  • GET /api/auth/.well-known/jwks.json - JWKS μ—”λ“œν¬μΈνŠΈ
  • POST /api/auth/jwt/generate - JWT 토큰 생성

쑰직 ν”ŒλŸ¬κ·ΈμΈ (Organization)

쑰직, 멀버, μ΄ˆλŒ€, νŒ€ 관리λ₯Ό μ§€μ›ν•©λ‹ˆλ‹€:
import { defineConfig } from "sonamu";
import { organization, ORGANIZATION_SCHEMA } from "sonamu/auth";

export default defineConfig({
  server: {
    auth: {
      plugins: [
        organization({
          schema: ORGANIZATION_SCHEMA,
        }),
      ],
    },
  },
});
Organization ν”ŒλŸ¬κ·ΈμΈμ΄ μƒμ„±ν•˜λŠ” ν…Œμ΄λΈ”:
  • organizations - 쑰직 정보
  • members - 쑰직 멀버
  • invitations - 쑰직 μ΄ˆλŒ€
  • teams - νŒ€
  • team_members - νŒ€ 멀버
Organization ν”ŒλŸ¬κ·ΈμΈμ΄ Session ν…Œμ΄λΈ”μ— μΆ”κ°€ν•˜λŠ” ν•„λ“œ:
  • active_organization_id - ν˜„μž¬ ν™œμ„± 쑰직 ID
  • active_team_id - ν˜„μž¬ ν™œμ„± νŒ€ ID
Organization κ΄€λ ¨ API:
  • POST /api/auth/organization/create - 쑰직 생성
  • POST /api/auth/organization/invite - 멀버 μ΄ˆλŒ€
  • POST /api/auth/organization/accept-invitation - μ΄ˆλŒ€ 수락
  • POST /api/auth/organization/set-active - ν™œμ„± 쑰직 μ„€μ •

읡λͺ… μ‚¬μš©μž ν”ŒλŸ¬κ·ΈμΈ (Anonymous)

읡λͺ… μ‚¬μš©μž 인증을 μ§€μ›ν•©λ‹ˆλ‹€. νšŒμ›κ°€μž… 없이 μž„μ‹œ μ‚¬μš©μžλ₯Ό 생성할 수 μžˆμŠ΅λ‹ˆλ‹€:
import { defineConfig } from "sonamu";
import { anonymous, ANONYMOUS_SCHEMA } from "sonamu/auth";

export default defineConfig({
  server: {
    auth: {
      plugins: [
        anonymous({
          schema: ANONYMOUS_SCHEMA,
        }),
      ],
    },
  },
});
Anonymous ν”ŒλŸ¬κ·ΈμΈμ΄ User ν…Œμ΄λΈ”μ— μΆ”κ°€ν•˜λŠ” ν•„λ“œ:
  • is_anonymous - 읡λͺ… μ‚¬μš©μž μ—¬λΆ€
Anonymous κ΄€λ ¨ API:
  • POST /api/auth/sign-in/anonymous - 읡λͺ… 둜그인
  • POST /api/auth/anonymous/link - 읡λͺ… 계정을 정식 κ³„μ •μœΌλ‘œ μ—°κ²°

μ—¬λŸ¬ ν”ŒλŸ¬κ·ΈμΈ ν•¨κ»˜ μ‚¬μš©

import { defineConfig } from "sonamu";
import {
  admin,
  ADMIN_SCHEMA,
  twoFactor,
  TWO_FACTOR_SCHEMA,
  username,
  USERNAME_SCHEMA,
  passkey,
  PASSKEY_SCHEMA,
  organization,
  ORGANIZATION_SCHEMA,
} from "sonamu/auth";

export default defineConfig({
  server: {
    auth: {
      emailAndPassword: { enabled: true },
      plugins: [
        admin({ schema: ADMIN_SCHEMA }),
        twoFactor({
          issuer: "My App",
          schema: TWO_FACTOR_SCHEMA,
        }),
        username({ schema: USERNAME_SCHEMA }),
        passkey({ schema: PASSKEY_SCHEMA }),
        organization({ schema: ORGANIZATION_SCHEMA }),
      ],
    },
  },
});
각 ν”ŒλŸ¬κ·ΈμΈμ˜ μŠ€ν‚€λ§ˆ(*_SCHEMA)λŠ” Sonamu의 snake_case 컬럼λͺ…κ³Ό better-auth의 camelCase ν•„λ“œλͺ…을 λ§€ν•‘ν•˜λŠ” 역할을 ν•©λ‹ˆλ‹€. λ°˜λ“œμ‹œ ν•΄λ‹Ή ν”ŒλŸ¬κ·ΈμΈκ³Ό ν•¨κ»˜ 전달해야 ν•©λ‹ˆλ‹€.

μ‹€μ „ μ˜ˆμ‹œ

κΈ°λ³Έ μ„€μ •

import { defineConfig } from "sonamu";

export default defineConfig({
  server: {
    auth: {
      emailAndPassword: {
        enabled: true,
      },
    },

    apiConfig: {
      guardHandler: async (guard) => {
        const { user } = Sonamu.getContext();

        if (guard === "auth" && !user) {
          throw new UnauthorizedError("둜그인이 ν•„μš”ν•©λ‹ˆλ‹€");
        }
      },
    },
  },
});

μ†Œμ…œ 둜그인 + 이메일 인증

import { defineConfig } from "sonamu";

export default defineConfig({
  server: {
    auth: {
      emailAndPassword: {
        enabled: true,
        requireEmailVerification: true,
      },
      socialProviders: {
        google: {
          clientId: process.env.GOOGLE_CLIENT_ID!,
          clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
        },
        github: {
          clientId: process.env.GITHUB_CLIENT_ID!,
          clientSecret: process.env.GITHUB_CLIENT_SECRET!,
        },
      },
    },
  },
});

μ£Όμ˜μ‚¬ν•­

1. μ—”ν‹°ν‹° 생성 ν•„μˆ˜

# auth μ„€μ • 전에 λ¨Όμ € μ—”ν‹°ν‹° 생성
pnpm sonamu better-auth

# λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ μ‹€ν–‰
pnpm sonamu migrate run

2. ν™˜κ²½ λ³€μˆ˜ μ„€μ •

# .env
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret

3. CORS μ„€μ •

ν΄λΌμ΄μ–ΈνŠΈκ°€ λ‹€λ₯Έ λ„λ©”μΈμ—μ„œ μ‹€ν–‰λ˜λŠ” 경우:
export default defineConfig({
  server: {
    plugins: {
      cors: {
        origin: ["http://localhost:3000"],
        credentials: true,
      },
    },

    auth: {
      emailAndPassword: { enabled: true },
    },
  },
});

4. κΈ°μ‘΄ User μ—”ν‹°ν‹°μ™€μ˜ ν˜Έν™˜μ„±

이미 User μ—”ν‹°ν‹°κ°€ μžˆλŠ” 경우, pnpm sonamu better-auth μ‹€ν–‰ μ‹œ λˆ„λ½λœ ν•„λ“œλ§Œ μΆ”κ°€λ©λ‹ˆλ‹€. κΈ°μ‘΄ λ°μ΄ν„°λŠ” μœ μ§€λ©λ‹ˆλ‹€.

λ‹€μŒ 단계

인증 섀정을 μ™„λ£Œν–ˆλ‹€λ©΄:
  • Context - Contextμ—μ„œ μ‚¬μš©μž 정보 μ ‘κ·Ό
  • Guards - API μ ‘κ·Ό μ œμ–΄