Module: Studio::LinkToken

Defined in:
lib/studio/link_token.rb

Overview

Pure-Ruby helpers behind the Studio::Link model — token minting, the kind rules, and the input sanitizers. Kept free of ActiveRecord so it loads (and unit-tests) without a database, mirroring the other lib/studio/*.rb pure classes. The AR model (app/models/studio/link.rb) delegates to this.

Constant Summary collapse

KINDS =

The kinds of link that share the studio_links table + the /l/<token> entry point.

magic_link — single-use, short-lived passwordless sign-in / sign-up
referral   — reusable, non-expiring share link owned by a User
%w[magic_link referral].freeze
SINGLE_USE_KINDS =

Kinds burned on first successful consume. Referral links are reusable, so they are deliberately NOT single-use.

%w[magic_link].freeze
TOKEN_BYTES =

96 bits of entropy → ~16 URL-safe chars (e.g. “PP-PDbEj5V3-aNh4”). Short enough to keep the URL clean, far too large to brute-force — especially for single-use, expiring magic links. Matches turf-monster’s proven format.

12

Class Method Summary collapse

Class Method Details

.generateObject

A fresh URL-safe token. urlsafe_base64 emits only [A-Za-z0-9_-], so the token satisfies the %r[^/]+ route constraint and survives URL generation without extra encoding.



31
32
33
# File 'lib/studio/link_token.rb', line 31

def generate
  SecureRandom.urlsafe_base64(TOKEN_BYTES)
end

.kind?(kind) ⇒ Boolean

Returns:

  • (Boolean)


35
36
37
# File 'lib/studio/link_token.rb', line 35

def kind?(kind)
  KINDS.include?(kind.to_s)
end

.normalize_email(email) ⇒ Object



43
44
45
# File 'lib/studio/link_token.rb', line 43

def normalize_email(email)
  email.to_s.strip.downcase
end

.sanitize_path(path) ⇒ Object

Only same-origin absolute paths survive; protocol-relative (“//evil”), absolute URLs, and blanks collapse to nil so callers fall back to a safe default redirect. Mirrors the MagicLink service’s sanitizer.



50
51
52
53
# File 'lib/studio/link_token.rb', line 50

def sanitize_path(path)
  p = path.to_s
  p.start_with?("/") && !p.start_with?("//") ? p : nil
end

.single_use?(kind) ⇒ Boolean

Returns:

  • (Boolean)


39
40
41
# File 'lib/studio/link_token.rb', line 39

def single_use?(kind)
  SINGLE_USE_KINDS.include?(kind.to_s)
end