Phone Number

전화번호 플러그인은 사용자가 전화번호를 사용하여 로그인하고 가입할 수 있도록 인증 시스템을 확장합니다. 전화번호를 검증하기 위한 OTP(일회용 비밀번호) 기능이 포함되어 있습니다.

설치

서버에 플러그인 추가

auth.ts
import { betterAuth } from "better-auth"
import { phoneNumber } from "better-auth/plugins"

const auth = betterAuth({
    plugins: [
        phoneNumber({  
            sendOTP: ({ phoneNumber, code }, request) => { 
                // SMS를 통해 OTP 코드 전송 구현
            } 
        }) 
    ]
})

데이터베이스 마이그레이션

마이그레이션을 실행하거나 스키마를 생성하여 필요한 필드와 테이블을 데이터베이스에 추가합니다.

npx @better-auth/cli migrate
npx @better-auth/cli generate

필드를 수동으로 추가하려면 Schema 섹션을 참조하세요.

클라이언트 플러그인 추가

auth-client.ts
import { createAuthClient } from "better-auth/client"
import { phoneNumberClient } from "better-auth/client/plugins"

const authClient =  createAuthClient({
    plugins: [ 
        phoneNumberClient() 
    ] 
})

사용법

검증을 위한 OTP 전송

사용자의 전화번호로 검증을 위한 OTP를 전송하려면 sendVerificationCode endpoint를 사용할 수 있습니다.

POST
/phone-number/send-otp
const { data, error } = await authClient.phoneNumber.sendOtp({    phoneNumber: "+1234567890", // required});
PropDescriptionType
phoneNumber
OTP를 전송할 전화번호입니다.
string

전화번호 검증

OTP가 전송된 후 사용자는 코드를 제공하여 전화번호를 검증할 수 있습니다.

POST
/phone-number/verify
const { data, error } = await authClient.phoneNumber.verify({    phoneNumber: "+1234567890", // required    code: "123456", // required    disableSession: false,    updatePhoneNumber: true,});
PropDescriptionType
phoneNumber
검증할 전화번호입니다.
string
code
OTP 코드입니다.
string
disableSession?
검증 후 세션 생성을 비활성화합니다.
boolean
updatePhoneNumber?
세션이 있는지 확인하고 전화번호를 업데이트합니다.
boolean

전화번호가 검증되면 사용자 테이블의 phoneNumberVerified 필드가 true로 설정됩니다. disableSessiontrue로 설정되지 않으면 사용자를 위한 세션이 생성됩니다. 또한 callbackOnVerification이 제공되면 호출됩니다.

전화번호로 가입 허용

사용자가 전화번호를 사용하여 가입할 수 있도록 하려면 플러그인 구성에 signUpOnVerification 옵션을 전달할 수 있습니다. 사용자를 위한 임시 이메일을 생성하는 getTempEmail 함수를 전달해야 합니다.

auth.ts
export const auth = betterAuth({
    plugins: [
        phoneNumber({
            sendOTP: ({ phoneNumber, code }, request) => {
                // SMS를 통해 OTP 코드 전송 구현
            },
            signUpOnVerification: {
                getTempEmail: (phoneNumber) => {
                    return `${phoneNumber}@my-site.com`
                },
                //선택적으로, 사용자를 위한 임시 이름을 생성하는 `getTempName` 함수도 전달할 수 있습니다
                getTempName: (phoneNumber) => {
                    return phoneNumber //기본적으로 전화번호를 이름으로 사용합니다
                }
            }
        })
    ]
})

전화번호로 로그인

전송-검증 플로우를 사용하여 사용자를 로그인하는 것 외에도 전화번호를 식별자로 사용하고 전화번호와 비밀번호를 사용하여 사용자를 로그인할 수도 있습니다.

POST
/sign-in/phone-number
const { data, error } = await authClient.signIn.phoneNumber({    phoneNumber: "+1234567890", // required    password, // required    rememberMe: true,});
PropDescriptionType
phoneNumber
로그인할 전화번호입니다.
string
password
로그인에 사용할 비밀번호입니다.
string
rememberMe?
세션을 기억합니다.
boolean

전화번호 업데이트

전화번호 업데이트는 전화번호를 검증하는 것과 동일한 프로세스를 사용합니다. 사용자는 새 전화번호를 검증하기 위한 OTP 코드를 받습니다.

auth-client.ts
await authClient.phoneNumber.sendOtp({
    phoneNumber: "+1234567890" // 새 전화번호
})

그런 다음 OTP 코드로 새 전화번호를 검증합니다.

auth-client.ts
const isVerified = await authClient.phoneNumber.verify({
    phoneNumber: "+1234567890",
    code: "123456",
    updatePhoneNumber: true // 전화번호를 업데이트하려면 true로 설정
})

사용자 세션이 존재하면 전화번호가 자동으로 업데이트됩니다.

세션 생성 비활성화

기본적으로 플러그인은 전화번호를 검증한 후 사용자를 위한 세션을 생성합니다. verify 메서드에 disableSession: true를 전달하여 이 동작을 비활성화할 수 있습니다.

auth-client.ts
const isVerified = await authClient.phoneNumber.verify({
    phoneNumber: "+1234567890",
    code: "123456",
    disableSession: true
})

비밀번호 재설정 요청

phoneNumber를 사용하여 비밀번호 재설정 플로우를 시작하려면 클라이언트에서 requestPasswordReset을 호출하여 사용자의 전화번호로 OTP 코드를 전송할 수 있습니다.

POST
/phone-number/request-password-reset
const { data, error } = await authClient.phoneNumber.requestPasswordReset({    phoneNumber: "+1234567890", // required});
PropDescriptionType
phoneNumber
사용자와 연결된 전화번호입니다.
string

그런 다음 클라이언트에서 OTP 코드와 새 비밀번호로 resetPassword를 호출하여 비밀번호를 재설정할 수 있습니다.

POST
/phone-number/reset-password
const { data, error } = await authClient.phoneNumber.resetPassword({    otp: "123456", // required    phoneNumber: "+1234567890", // required    newPassword: "new-and-secure-password", // required});
PropDescriptionType
otp
비밀번호를 재설정할 일회용 비밀번호입니다.
string
phoneNumber
비밀번호를 재설정하려는 계정의 전화번호입니다.
string
newPassword
새 비밀번호입니다.
string

옵션

  • otpLength: 생성할 OTP 코드의 길이입니다. 기본값은 6입니다.
  • sendOTP: 사용자의 전화번호로 OTP 코드를 전송하는 함수입니다. 전화번호와 OTP 코드를 인수로 받습니다.
  • expiresIn: OTP 코드가 만료되는 시간(초)입니다. 기본값은 300초입니다.
  • callbackOnVerification: 전화번호가 검증된 후 호출되는 함수입니다. 전화번호와 사용자 객체를 첫 번째 인수로, 요청 객체를 두 번째 인수로 받습니다.
export const auth = betterAuth({
    plugins: [
        phoneNumber({
            sendOTP: ({ phoneNumber, code }, request) => {
                // SMS를 통해 OTP 코드 전송 구현
            },
            callbackOnVerification: async ({ phoneNumber, user }, request) => {
                // 전화번호 검증 후 콜백 구현
            }
        })
    ]
})
  • sendPasswordResetOTP: 비밀번호 재설정을 위해 사용자의 전화번호로 OTP 코드를 전송하는 함수입니다. 전화번호와 OTP 코드를 인수로 받습니다.

  • phoneNumberValidator: 전화번호를 검증하는 사용자 정의 함수입니다. 전화번호를 인수로 받고 전화번호가 유효한지 여부를 나타내는 부울을 반환합니다.

  • signUpOnVerification: 다음 속성을 가진 객체입니다:

    • getTempEmail: 사용자를 위한 임시 이메일을 생성하는 함수입니다. 전화번호를 인수로 받고 임시 이메일을 반환합니다.
    • getTempName: 사용자를 위한 임시 이름을 생성하는 함수입니다. 전화번호를 인수로 받고 임시 이름을 반환합니다.
  • requireVerification: 활성화하면 사용자가 전화번호를 검증할 때까지 전화번호로 로그인할 수 없습니다. 검증되지 않은 사용자가 로그인을 시도하면 서버는 401 오류(PHONE_NUMBER_NOT_VERIFIED)로 응답하고 검증 프로세스를 시작하기 위해 OTP 전송을 자동으로 트리거합니다.

Schema

플러그인은 사용자 테이블에 2개의 필드를 추가해야 합니다

User 테이블

Field NameTypeKeyDescription
phoneNumberstring사용자의 전화번호
phoneNumberVerifiedboolean전화번호가 검증되었는지 여부

OTP 검증 시도

전화번호 플러그인에는 각 OTP 코드에 대한 검증 시도 횟수를 제한하여 무차별 대입 공격에 대한 내장 보호 기능이 포함되어 있습니다.

phoneNumber({
  allowedAttempts: 3, // 기본값은 3입니다
  // ... 기타 옵션
})

사용자가 허용된 검증 시도 횟수를 초과하면:

  • OTP 코드가 자동으로 삭제됩니다
  • 추가 검증 시도는 "너무 많은 시도" 메시지와 함께 403 (Forbidden) 상태를 반환합니다
  • 사용자는 계속하려면 새 OTP 코드를 요청해야 합니다

시도 횟수를 초과한 후 오류 응답 예제:

{
  "error": {
    "status": 403,
    "message": "너무 많은 시도"
  }
}

403 상태를 받으면 사용자에게 새 OTP 코드를 요청하도록 안내하세요