require 'securerandom'
require 'digest'
require 'openssl'
require 'rack/utils'
class RememberToken < Sequel::Model
SIGNING_KEY = ENV.fetch('REMEMBER_SIGNING_KEY')
def self.issue(user_id, ttl: 365 * 24 * 3600)
selector = SecureRandom.urlsafe_base64(12)
validator = SecureRandom.urlsafe_base64(24)
create(
user_id: user_id,
selector: selector,
validator_digest: Digest::SHA256.hexdigest(validator),
expires_at: Time.now + ttl
)
{ selector: selector, validator: validator }
end
def self.authenticate(selector, validator)
token = where(selector: selector).first
return nil unless token && token.expires_at > Time.now
expected = token.validator_digest
given = Digest::SHA256.hexdigest(validator)
return nil unless Rack::Utils.secure_compare(expected, given)
token.user_id
end
def self.cookie_value(selector, validator)
payload = "#{selector}:#{validator}"
sig = OpenSSL::HMAC.hexdigest('SHA256', SIGNING_KEY, payload)
"#{payload}:#{sig}"
end
def self.parse(raw)
return nil unless raw
selector, validator, sig = raw.split(':', 3)
return nil unless selector && validator && sig
payload = "#{selector}:#{validator}"
expected = OpenSSL::HMAC.hexdigest('SHA256', SIGNING_KEY, payload)
return nil unless Rack::Utils.secure_compare(expected, sig)
[selector, validator]
end
end
require 'sinatra/base'
require_relative 'remember_token'
class Web < Sinatra::Base
enable :sessions
before do
@current_user ||= session[:user_id] && User[session[:user_id]]
next if @current_user
parsed = RememberToken.parse(request.cookies['remember'])
if parsed
user_id = RememberToken.authenticate(*parsed)
if user_id
session[:user_id] = user_id
@current_user = User[user_id]
else
response.delete_cookie('remember')
end
end
end
post '/login' do
user = User.authenticate(params[:email], params[:password])
halt 401, 'Invalid credentials' unless user
session[:user_id] = user.id
if params[:remember] == '1'
pair = RememberToken.issue(user.id)
response.set_cookie('remember',
value: RememberToken.cookie_value(pair[:selector], pair[:validator]),
path: '/',
httponly: true,
secure: production?,
same_site: :lax,
max_age: 365 * 24 * 3600
)
end
redirect '/dashboard'
end
post '/logout' do
parsed = RememberToken.parse(request.cookies['remember'])
RememberToken.where(selector: parsed.first).delete if parsed
response.delete_cookie('remember')
session.clear
redirect '/'
end
helpers do
def production?
ENV['RACK_ENV'] == 'production'
end
end
end
Sequel.migration do
change do
create_table(:remember_tokens) do
primary_key :id
foreign_key :user_id, :users, null: false, on_delete: :cascade
String :selector, null: false, fixed: false
String :validator_digest, null: false
DateTime :expires_at, null: false
DateTime :created_at
index :selector, unique: true
index :user_id
end
end
end
This snippet builds a persistent 'remember me' login for a Sinatra app using signed, tamper-evident cookies. The core idea is that a login form can drop a long-lived cookie that survives browser restarts, but that cookie must not be forgeable: if a client could edit the user id inside it, they could impersonate anyone. The solution stores a random selector plus a hashed validator server-side, and signs the cookie payload with an HMAC so the server can detect tampering before it ever hits the database.
In RememberToken model, each remember token is split into a public selector and a secret validator. Only the SHA-256 digest of the validator is persisted in validator_digest, so a leaked database row cannot be replayed directly. RememberToken.issue mints a fresh pair with SecureRandom.urlsafe_base64 and returns the raw values to the caller exactly once. authenticate looks the row up by selector, checks expiry, and uses Rack::Utils.secure_compare for a constant-time comparison to avoid timing attacks. The cookie value is built by cookie_value and verified by parse using OpenSSL::HMAC, again with secure_compare, so a mismatched signature is rejected without a DB hit.
In app.rb, the /login route calls RememberToken.issue and writes the signed value through response.set_cookie with httponly and secure flags plus a one-year max_age; these flags keep the cookie out of JavaScript and off plain HTTP. The before filter reads request.cookies['remember'], runs it through RememberToken.parse and authenticate, and hydrates @current_user on success while silently clearing a bad cookie. /logout both deletes the DB row and calls response.delete_cookie so the token can never be reused.
The SIGNING_KEY is loaded from the environment because the whole scheme collapses if the key leaks or is hardcoded. A subtle trade-off worth noting: signed cookies prove integrity, not confidentiality, so the validator still lives only as a digest server-side. Rotating tokens on each use would harden this further against theft, at the cost of extra writes. This pattern is what a developer reaches for when sessions alone are too short-lived but storing a bare user id in a cookie is unacceptable.
Related snips
payload = {
sub: user.id,
iss: 'https://auth.example.com',
aud: 'codesnips-api',
exp: 15.minutes.from_now.to_i,
iat: Time.now.to_i,
JWT issuance and verification without common footguns
timestamp = request.headers.fetch('X-Signature-Timestamp')
signature = request.headers.fetch('X-Signature')
payload = request.raw_post
data = "#{timestamp}.#{payload}"
expected = OpenSSL::HMAC.hexdigest('SHA256', ENV.fetch('WEBHOOK_SECRET'), data)
HMAC signed API requests for webhook and partner integrity
require "csv"
class PeopleCsvStream
include Enumerable
HEADERS = %w[id full_name email signed_up_at plan].freeze
Resilient CSV Export as a Streamed Response
#!/usr/bin/env bash
set -euo pipefail
export VAULT_ADDR="https://vault.internal:8200"
export VAULT_TOKEN="${VAULT_TOKEN:?missing VAULT_TOKEN}"
Secrets management with environment isolation and Vault
import { randomBytes, createHash } from "crypto";
import jwt from "jsonwebtoken";
import { RefreshTokenStore } from "./store";
const ACCESS_SECRET = process.env.ACCESS_SECRET!;
const ACCESS_TTL = "15m";
JWT access + refresh token rotation (conceptual)
package files
import (
"context"
"time"
Presigned S3 upload URLs (AWS SDK v2)
Share this code
Here's the card — post it anywhere.