module Api class AuthController < BaseController rate_limit to: 20, within: 1.hour, only: :magic_link, with: -> { too_many } rate_limit to: 30, within: 15.minutes, only: :code, with: -> { too_many } # POST /api/auth/magic_link { email, return_to } # Always the same answer, so it can't be used to find out who has an account. def magic_link email = params.require(:email).to_s unless MagicLink.throttled?(email) link = MagicLink.issue!(email:, return_to: params[:return_to]) AuthMailer.magic_link(link, link.token, link.code).deliver_later end render json: { ok: true } rescue ActiveRecord::RecordInvalid render json: { error: "invalid_email" }, status: :unprocessable_entity end # POST /api/auth/code { email, code } -- the 6-digit code from the mail def code link = MagicLink.find_by_code(params.require(:email), params.require(:code)) return render(json: { error: "invalid_code" }, status: :unprocessable_entity) unless link start_session_for(link.redeem!) render json: { user: Current.user.as_json, redirect: link.destination } end # DELETE /api/auth/session def logout terminate_session render json: { ok: true } end private def too_many = render(json: { error: "rate_limited" }, status: :too_many_requests) end end