Module: Solana::SessionAuth
- Extended by:
- ActiveSupport::Concern
- Included in:
- SolanaSessionsController
- Defined in:
- app/controllers/concerns/solana/session_auth.rb
Overview
Rails-session adapter around solana_studio’s pure Solana::AuthVerifier.verify!. Lives under app/controllers/concerns/ as ‘Solana::SessionAuth` (NOT `Solana::AuthVerifier`) — the gem owns that latter namespace, and Zeitwerk autoload skips colliding constants. By keeping this concern under a distinct module name, both pieces of code load reliably.
Controllers ‘include Solana::SessionAuth` and call `verify_solana_signature!(message:, signature_b58:, pubkey_b58:, session:)`. This shim pulls/deletes the nonce from session (delete-before-verify = replay protection) and delegates the cryptography to the gem.
Errors raised by the gem (‘Solana::AuthVerifier::VerificationError`) are passed through unwrapped — controllers rescue that gem-defined constant directly.
The ‘solana-studio` gem is provided by the consuming app (both McRitchie Studio + Turf Monster bundle it); this concern is only ever included when wallet sign-in is enabled, so a wallet-less app never loads ::Solana::AuthVerifier.
Lifted into studio-engine (was turf-monster app/controllers/concerns/solana/session_auth.rb).
Instance Method Summary collapse
Instance Method Details
#verify_solana_signature!(message:, signature_b58:, pubkey_b58:, session:, expected_user_id: nil) ⇒ Object
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'app/controllers/concerns/solana/session_auth.rb', line 26 def verify_solana_signature!(message:, signature_b58:, pubkey_b58:, session:, expected_user_id: nil) # OPSEC-005: when the caller is authenticated, require the signed # message to embed `User-ID: <current_user.id>` so a signature # captured against a *different* session's nonce can't be replayed # into this user's flow. Login (solana_sessions#verify) calls without # expected_user_id since there's no current_user yet — the nonce # delete-before-verify below remains replay protection for that path. if expected_user_id && !.to_s.include?("User-ID: #{expected_user_id}") raise ::Solana::AuthVerifier::VerificationError, "Signed message missing User-ID binding for current session" end # Delete nonce BEFORE verification to prevent replay stored_nonce = session.delete(:solana_nonce) nonce_at = session.delete(:solana_nonce_at) # OPSEC-018: bind the signature to this host. The client builds the # message with `window.location.host`; request.host_with_port is the # server-side equal (hostname, plus port only when non-default). ::Solana::AuthVerifier.verify!( message: , signature_b58: signature_b58, pubkey_b58: pubkey_b58, expected_host: request.host_with_port, stored_nonce: stored_nonce, nonce_at: nonce_at ) end |