Application Development

A technical walkthrough of the Secure-2FA architecture — built on Ruby on Rails 7, PostgreSQL, Redis, and Sidekiq.

Application preview on the whole

The architecture is broken into modular components. The backend is a Ruby on Rails API that exposes two OTP endpoints and a management interface for developers.

Ruby on Rails 7
Backend framework & API
PostgreSQL
Primary database with encryption
Redis + Sidekiq
Async job processing
Devise + TOTP
Client authentication
OpenSSL AES-256
API data encryption
Docker
Containerized deployment
Backend Sections
Projects: Management of websites where you implement 2FA. Each project has unique Users and OTP Verifications, plus its own public/secret key pair.
Channels: Configuration for how OTP messages are delivered — by email or SMS — via your chosen third-party provider (Postmark, Twilio, Sendgrid, etc).
Templates: Define the content of OTP messages. Each template has multiple versions, one per language, containing the email subject and body with the {{OTP_CODE}} variable.
Logs: Full history of every OTP generation and verification API call — with timestamps, success/failure status, and level (info, warning, danger).
API Design: Two endpoints: POST /otp/generate receives the encrypted data and delivers the OTP code; POST /otp/verify receives and validates the user's submission. Both are detailed in the Using the Service section.

Generate the Rails application

Prerequisites: Ruby 3.1+, Rails 7, Node.js v18+, Yarn, PostgreSQL, and Foreman. Install Rails 7 if needed:

# Install Rails 7
$ gem install rails -v 7

# Create the application
$ rails new secure-2fa -j esbuild --css bootstrap -d postgresql

# Start the app
$ cd secure-2fa
$ bin/rails db:prepare
$ bin/dev
        

The Rails server starts at http://localhost:3000. The bin/dev command uses Foreman to start Rails, asset compilers, and Sidekiq together.

Application configuration

Add the required gems to Gemfile and run bundle install:

# Authentication
gem 'devise'
gem 'devise-two-factor'
gem 'rqrcode'

# Background jobs
gem 'sidekiq'
gem 'redis'

# OTP delivery providers
gem 'twilio-ruby'
gem 'mailjet'
gem 'plivo'
gem 'sendgrid-ruby'
        

Configure Sidekiq with Redis, set up database encryption keys, and initialize Devise:

# Configure Devise with async email via Sidekiq
$ bin/rails generate devise:install

# Generate encryption keys
$ bin/rails credentials:edit
  active_record_encryption:
    primary_key: ...
    deterministic_key: ...
    key_derivation_salt: ...
        

Build the authentication system

The Client model handles developer authentication. Registration is email-only; after confirming the email, the client sets a password and links an authenticator app (TOTP). Backup codes are provided if the authenticator is unavailable.

# Generate Devise for Client model
$ bin/rails generate devise Client

# Generate custom controllers
$ bin/rails generate devise:controllers clients
    -c=registrations confirmations sessions

# Custom OTP controllers
$ bin/rails generate controller clients/two_factor_auths
$ bin/rails generate controller clients/passwords
        

The session controller redirects clients to a TOTP verification page after successful password login. Backup codes allow access when the authenticator app is unavailable.

Scaffolding CRUD resources

Rails scaffold generates controllers, views, and routes for the three main backend resources:

$ bin/rails generate scaffold Project
    client:reference title:string
    public_key:string public_key_iv:string
    secret_key:string secret_key_iv:string
    channel:reference template:reference
    expiration_seconds:integer max_attempts:integer
    webhook_url:string webhook_url_iv:string

$ bin/rails generate scaffold Channel
    client:reference title:string
    method:string provider:string credentials:text

$ bin/rails generate scaffold Template
    client:reference title:string markup:string

# Model-only (no controller/views needed)
$ bin/rails generate model User
    project:reference uuid:string uuid_iv:string

$ bin/rails generate model Verification
    user:reference otp_code:string otp_code_iv:string
    otp_sent_at:datetime attempts:integer
    to:string to_iv:string lang:string
    template_request:boolean
    delivery_identifier:string delivery_status:string

$ bin/rails generate model TemplateVersion
    template:reference subject:string body:text lang:string

$ bin/rails generate model Log
    client:reference project:reference
    user:reference message:text level:string

$ bin/rails db:migrate
        

OTP Controller & Background Worker

The OTP controller handles the two API endpoints. It decrypts the incoming data using AES-256, manages OTP generation, delivery, verification, and logging:

$ bin/rails generate controller otp generate verify

# Key controller logic:
#   generate — decrypts data, finds/creates user,
#              generates OTP, triggers delivery via Sidekiq
#   verify   — decrypts data, validates OTP code,
#              checks expiry, max attempts, device match
#              fires webhook if configured
        

The OTP delivery is handled asynchronously via a Sidekiq background worker, so the API response returns immediately while the email or SMS is sent in the background:

# app/jobs/otp_delivery_job.rb

class OtpDeliveryJob < ApplicationJob

  queue_as :default

  def perform(verification_id)

    verification = Verification.find(verification_id)

    service = verification.channel.provider

    # Routes to the correct service class
    # e.g. PostmarkService, TwilioService, SmtpService

    "#{service.camelize}Service"
      .constantize
      .new(verification)
      .send_otp

  end

end
        

Message Delivery Services

The application supports multiple email and SMS delivery providers. Each is a service class in app/services/ that follows the same interface:

Postmark
Email
Sendgrid
Email
Mailjet
Email
Sparkpost
Email
Elasticemail
Email
SMTP
Email
Twilio
SMS
Plivo
SMS
Clicksend
Email + SMS

Administration Area

The first registered Client is automatically set as admin. Admin accounts have access to the full Clients list and the Sidekiq dashboard for monitoring background jobs.

Application Menu
Dashboard Projects Channels Templates Profile Clients (admin) Sidekiq (admin)

The Dashboard (root path) displays application logs. Logs are also shown per-project and per-user page. All log levels: info, warning, danger, success.

Project files & folder structure

secure-2fa/
├── app/
│   ├── controllers/
│   │   ├── application_controller.rb
│   │   ├── otp_controller.rb
│   │   ├── projects_controller.rb
│   │   ├── channels_controller.rb
│   │   ├── templates_controller.rb
│   │   └── clients/
│   │       ├── registrations_controller.rb
│   │       ├── confirmations_controller.rb
│   │       ├── sessions_controller.rb
│   │       ├── passwords_controller.rb
│   │       └── two_factor_auths_controller.rb
│   ├── jobs/
│   │   ├── otp_delivery_job.rb
│   │   └── webhook_call_job.rb
│   ├── models/
│   │   ├── client.rb
│   │   ├── project.rb
│   │   ├── channel.rb
│   │   ├── template.rb
│   │   ├── template_version.rb
│   │   ├── user.rb
│   │   ├── verification.rb
│   │   ├── log.rb
│   │   └── concerns/
│   │       └── check_linked_projects.rb
│   ├── services/
│   │   ├── postmark_service.rb
│   │   ├── sendgrid_service.rb
│   │   ├── mailjet_service.rb
│   │   ├── twilio_service.rb
│   │   ├── plivo_service.rb
│   │   ├── clicksend_service.rb
│   │   └── smtp_service.rb
│   └── views/
│       ├── projects/
│       ├── channels/
│       ├── templates/
│       └── clients/
├── config/
│   ├── routes.rb
│   ├── initializers/
│   │   ├── devise.rb
│   │   └── sidekiq.rb
│   └── sidekiq.yml
├── db/
│   ├── migrate/
│   └── schema.rb
├── Gemfile
├── Procfile
└── Dockerfile
        
Sections
  • 1 — Application overview
  • 2 — Generate Rails app
  • 3 — Configuration
  • 4 — Authentication system
  • 5 — Scaffolding CRUD
  • 6 — Models logic
  • 7 — OTP Controller
  • 8 — Delivery Services
  • 9 — Admin area
  • 10 — File structure
Full source on GitHub

Views, CSS, JavaScript and Docker configuration are in the open-source repository.

View Repository

Want to use the service?

See the API documentation and integration examples in all major programming languages.

Using the Service →