Module: StandardId::Passwordless

Defined in:
lib/standard_id/passwordless.rb,
lib/standard_id/passwordless/sms_strategy.rb,
lib/standard_id/passwordless/base_strategy.rb,
lib/standard_id/passwordless/email_strategy.rb,
lib/standard_id/passwordless/verification_service.rb

Defined Under Namespace

Classes: BaseStrategy, EmailStrategy, SmsStrategy, VerificationService

Constant Summary collapse

DEFAULT_REALM =

Shared canonical realm used by the authentication flow. Kept in one place so BaseStrategy, VerificationService, and Otp can’t drift.

"authentication".freeze

Class Method Summary collapse

Class Method Details

.generate_otp_codeObject

Generate a zero-padded numeric OTP code at the configured length. Single source of truth — used by BaseStrategy and by the verify_email / verify_phone start controllers so a change to the generation formula only needs to happen here.

Codes may begin with leading zeros (e.g. “000123”). Host apps that display or round-trip codes should treat them as strings.



71
72
73
74
# File 'lib/standard_id/passwordless.rb', line 71

def generate_otp_code
  length = otp_code_length
  SecureRandom.random_number(10**length).to_s.rjust(length, "0")
end

.max_attempts_per_challengeObject

Resolve the per-challenge attempt ceiling, preferring the newer :max_attempts_per_challenge setting but falling back to :max_attempts for backwards compatibility with apps that configured the older name.



79
80
81
82
83
84
85
# File 'lib/standard_id/passwordless.rb', line 79

def max_attempts_per_challenge
  configured = StandardId.config.passwordless.max_attempts_per_challenge
  return configured.to_i if configured && configured.to_i.positive?

  legacy = StandardId.config.passwordless.max_attempts.to_i
  legacy.positive? ? legacy : 5
end

.otp_code_lengthObject

Resolve the configured OTP code length, clamped to a sane range. Shared by all OTP generators in the engine so one setting controls the code space end-to-end.



57
58
59
60
61
62
# File 'lib/standard_id/passwordless.rb', line 57

def otp_code_length
  configured = StandardId.config.passwordless.code_length
  length = configured.to_i
  length = 6 if length <= 0
  length.clamp(4, 10)
end

.verify(username:, code:, connection:, request:, allow_registration: true) ⇒ VerificationService::Result

Public API for verifying a passwordless OTP code.

This is the recommended entry point for host apps that need OTP verification without mounting WebEngine. It wraps VerificationService.verify with the same interface and result type.

Examples:

result = StandardId::Passwordless.verify(
  username: "user@example.com",
  code: "123456",
  connection: "email",
  request: request
)

if result.success?
  (result.)
else
  case result.error_code
  when :invalid_code then render_invalid_code
  when :max_attempts then render_locked_out
  when :not_found    then render_not_found
  end
end

Parameters:

  • username (String)

    The identifier value (email or phone number)

  • code (String)

    The OTP code to verify

  • connection (String)

    Channel type (“email” or “sms”)

  • request (ActionDispatch::Request)

    The current request

Returns:

  • (VerificationService::Result)

    A result with:

    • success? — true when verification succeeded

    • account — the authenticated/created account (nil on failure)

    • challenge — the consumed CodeChallenge (nil on failure)

    • error — human-readable message (nil on success)

    • error_code — machine-readable symbol (nil on success):

      :invalid_code, :max_attempts, :not_found, :blank_code,
      :account_not_found, :server_error
      
    • attempts — failed attempt count (nil on success)



44
45
46
47
48
49
50
51
52
# File 'lib/standard_id/passwordless.rb', line 44

def verify(username:, code:, connection:, request:, allow_registration: true)
  VerificationService.verify(
    connection: connection,
    username: username,
    code: code,
    request: request,
    allow_registration: allow_registration
  )
end