Class: BlogPreviewTokenService

Inherits:
Object
  • Object
show all
Defined in:
app/services/blog_preview_token_service.rb

Overview

Mints and decodes blog-preview tokens.

Tokens are signed with ActiveSupport::MessageVerifier (Rails primitive)
under the namespaced verifier :blog_preview and the explicit purpose tag
'blog_preview/v1' — defense in depth against a token signed for a
different feature being replayed against the blog preview path.

No legacy decoder: blog-preview tokens are minted on demand from the CRM
(an editor clicks "Preview" on a draft) and consumed immediately. Nothing
sits dormant in mailers, and the 1h TTL means any pre-migration JWT
expired before this code shipped — so there's no in-flight grace window
to keep around.

Constant Summary collapse

TOKEN_EXPIRY =
1.hour
PURPOSE =
'blog_preview/v1'

Class Method Summary collapse

Class Method Details

.decode(token) ⇒ Object

Returns a symbol-keyed payload hash on success, or nil on
tampered / expired / wrong-purpose tokens.



33
34
35
# File 'app/services/blog_preview_token_service.rb', line 33

def decode(token)
  verifier.verified(token, purpose: PURPOSE)&.symbolize_keys
end

.generate_token(post_id, user_id = nil, revision_id: nil) ⇒ Object



20
21
22
23
24
25
26
27
28
29
# File 'app/services/blog_preview_token_service.rb', line 20

def generate_token(post_id, user_id = nil, revision_id: nil)
  payload = {
    post_id: post_id,
    user_id: user_id,
    revision_id: revision_id,
    iat: Time.current.to_i
  }

  verifier.generate(payload, expires_in: TOKEN_EXPIRY, purpose: PURPOSE)
end

.valid_for_post?(token, post_id) ⇒ Boolean

Returns:

  • (Boolean)


37
38
39
40
41
42
# File 'app/services/blog_preview_token_service.rb', line 37

def valid_for_post?(token, post_id)
  payload = decode(token)
  return false unless payload

  payload[:post_id].to_i == post_id.to_i
end

.verifierObject



44
45
46
# File 'app/services/blog_preview_token_service.rb', line 44

def verifier
  Rails.application.message_verifier(:blog_preview)
end