Class: AssemblyaiCallbackTokenService
- Inherits:
-
Object
- Object
- AssemblyaiCallbackTokenService
- Defined in:
- app/services/assemblyai_callback_token_service.rb
Overview
Generates and validates time-limited tokens for AssemblyAI webhook authentication.
Tokens are embedded in the callback URL to prevent unauthorized submissions.
Supports both call records and video transcriptions.
Tokens are signed with ActiveSupport::MessageVerifier (Rails primitive)
under the namespaced verifier :assemblyai_callback and the explicit
purpose tag 'assemblyai_callback/v1' — defense in depth against a token
signed for a different feature being replayed against this webhook.
No legacy decoder: this service previously used JWT.encode / JWT.decode,
but those tokens cross neither verifier (different signature format). At
deploy time, any AssemblyAI job already in flight will lose its callback
(the transcription itself completes on AssemblyAI's side; we just never
ingest the WebhookLog). Recovery is to re-trigger the transcription from
the affected CallRecord / Video.
Uses API_HOSTNAME_WITHOUT_PORT constant for environment-aware URLs:
- Production: api.warmlyyours.com
- Staging: api.warmlyyours.ws
- Development: Uses dev tunnel (api-hostname.warmlyyours.dev)
Constant Summary collapse
- TOKEN_EXPIRY =
Token expiry.
24.hours
- PURPOSE =
Verifier purpose tag.
'assemblyai_callback/v1'- SUPPORTED_RESOURCE_TYPES =
Recognised supported resource types.
%w[CallRecord Video].freeze
Class Method Summary collapse
-
.call_record_webhook_url(call_record_id:) ⇒ String
Backwards-compatible webhook URL builder for call records.
-
.callback_url(resource_type:, resource_id:) ⇒ String
Generate a callback URL for production/staging.
-
.dev_callback_url(resource_type:, resource_id:) ⇒ String
Generate a dev callback URL for testing via Cloudflare tunnel.
-
.generate_token(resource_type:, resource_id:) ⇒ String
Generate a verifier-signed token for callback authentication.
-
.valid_token?(token) ⇒ Boolean
Check if a token is valid.
-
.validate_token(token) ⇒ Hash?
Validate and decode a token.
-
.video_webhook_url(video_id:) ⇒ String
Webhook URL builder for video transcriptions.
-
.webhook_url(resource_type:, resource_id:) ⇒ String
Get the appropriate callback URL based on environment.
Class Method Details
.call_record_webhook_url(call_record_id:) ⇒ String
Backwards-compatible webhook URL builder for call records.
Delegates to webhook_url with resource_type: 'CallRecord'.
110 111 112 |
# File 'app/services/assemblyai_callback_token_service.rb', line 110 def call_record_webhook_url(call_record_id:) webhook_url(resource_type: 'CallRecord', resource_id: call_record_id) end |
.callback_url(resource_type:, resource_id:) ⇒ String
Generate a callback URL for production/staging.
79 80 81 82 |
# File 'app/services/assemblyai_callback_token_service.rb', line 79 def callback_url(resource_type:, resource_id:) token = generate_token(resource_type: resource_type, resource_id: resource_id) "https://#{API_HOSTNAME_WITHOUT_PORT}/webhooks/v1/assemblyai?token=#{CGI.escape(token)}" end |
.dev_callback_url(resource_type:, resource_id:) ⇒ String
Generate a dev callback URL for testing via Cloudflare tunnel.
88 89 90 91 92 |
# File 'app/services/assemblyai_callback_token_service.rb', line 88 def dev_callback_url(resource_type:, resource_id:) hostname = `hostname -s`.strip.downcase token = generate_token(resource_type: resource_type, resource_id: resource_id) "https://api-#{hostname}.warmlyyours.dev/webhooks/v1/assemblyai?token=#{CGI.escape(token)}" end |
.generate_token(resource_type:, resource_id:) ⇒ String
Generate a verifier-signed token for callback authentication.
48 49 50 51 52 53 54 55 56 57 |
# File 'app/services/assemblyai_callback_token_service.rb', line 48 def generate_token(resource_type:, resource_id:) raise ArgumentError, "Unsupported resource type: #{resource_type}" unless SUPPORTED_RESOURCE_TYPES.include?(resource_type) payload = { 'resource_type' => resource_type, 'resource_id' => resource_id } verifier.generate(payload, expires_in: TOKEN_EXPIRY, purpose: PURPOSE) end |
.valid_token?(token) ⇒ Boolean
Check if a token is valid.
71 72 73 |
# File 'app/services/assemblyai_callback_token_service.rb', line 71 def valid_token?(token) validate_token(token).present? end |
.validate_token(token) ⇒ Hash?
Validate and decode a token.
62 63 64 65 66 |
# File 'app/services/assemblyai_callback_token_service.rb', line 62 def validate_token(token) return nil if token.blank? verifier.verified(token, purpose: PURPOSE) end |
.video_webhook_url(video_id:) ⇒ String
Webhook URL builder for video transcriptions.
Delegates to webhook_url with resource_type: 'Video'.
118 119 120 |
# File 'app/services/assemblyai_callback_token_service.rb', line 118 def video_webhook_url(video_id:) webhook_url(resource_type: 'Video', resource_id: video_id) end |
.webhook_url(resource_type:, resource_id:) ⇒ String
Get the appropriate callback URL based on environment.
98 99 100 101 102 103 104 |
# File 'app/services/assemblyai_callback_token_service.rb', line 98 def webhook_url(resource_type:, resource_id:) if Rails.env.development? dev_callback_url(resource_type: resource_type, resource_id: resource_id) else callback_url(resource_type: resource_type, resource_id: resource_id) end end |