{"record":{"id":"3160841602580d5e","repo":"instructure/canvas-lms","slug":"attachment-verifier-token-expired-verifier","errorCode":null,"errorMessage":"Attachment verifier token expired: #{verifier}","messagePattern":"Attachment verifier token expired: #(.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"app/models/attachments/verification.rb","lineNumber":87,"sourceCode":"  # probably want to use `valid_verifier_for_permission?`.\n  #\n  # @param verifier (String) - The verifier\n  #\n  # Returns nil if the verifier could not be decoded for whatever reason, and returns\n  # a Hash of the body contents if it can.\n  def decode_verifier(verifier)\n    begin\n      body = CanvasSecurity.decode_jwt(verifier)\n      if body[:id] != attachment.global_id\n        InstStatsd::Statsd.distributed_increment(\"attachments.token_verifier_id_mismatch\")\n        Rails.logger.warn(\"Attachment verifier token id mismatch. token id: #{body[:id]}, attachment id: #{attachment.global_id}, token: #{verifier}\")\n        return nil\n      end\n\n      InstStatsd::Statsd.distributed_increment(\"attachments.token_verifier_success\")\n    rescue CanvasSecurity::TokenExpired\n      InstStatsd::Statsd.distributed_increment(\"attachments.token_verifier_expired\")\n      Rails.logger.warn(\"Attachment verifier token expired: #{verifier}\")\n      return nil\n    rescue CanvasSecurity::InvalidToken\n      InstStatsd::Statsd.distributed_increment(\"attachments.token_verifier_invalid\")\n      Rails.logger.warn(\"Attachment verifier token invalid: #{verifier}\")\n      return nil\n    end\n\n    body\n  end\n\n  # Decodes a verifier and checks the user of the verifier has permission to access\n  # the attachment.\n  #\n  # @param verifier (String) - The verifier\n  # @param permission (Symbol) - Either :read or :download\n  #\n  # Returns a boolean\n  def valid_verifier_for_permission?(verifier, permission, root_account, session = {}, request: nil, files_domain: false)","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/attachments/verification.rb#L69-L105","documentation":"When CanvasSecurity.decode_jwt raises CanvasSecurity::TokenExpired, the attachment verifier JWT has passed its expiry. decode_verifier logs this warning with the raw token, increments the attachments.token_verifier_expired metric, and returns nil, denying access. Verifiers are short-lived by design.","triggerScenarios":"A user follows a download/inline link whose verifier JWT was generated earlier than the token TTL (e.g. a link emailed or bookmarked, opened after expiration).","commonSituations":"Emailed file links opened days later; cached HTML pages embedding expired verifiers; long-running browser sessions reusing old URLs; overly short verifier TTL settings.","solutions":["Regenerate the download URL so a fresh verifier is issued (re-request the file's download/inline URL)","Increase the verifier TTL if legitimate links expire too quickly (CanvasSecurity JWT TTL config)","Avoid persisting verifier URLs in emails/caches; link to a page that mints a fresh verifier","Tell users to re-open the file from Canvas UI rather than reusing old links"],"exampleFix":"// before\n<a href=\"<%= file.download_url %>\">  # embedded with expired verifier\n// after\n<a href=\"<%= course_file_path(context, file) %>\">  # fresh verifier minted per request","handlingStrategy":"validation","validationCode":"def verifier_expired?(verifier)\n  payload = CanvasSecurity.decode_jwt(verifier) rescue nil\n  payload.nil? # decode_verifier returns nil for expired tokens\nend","typeGuard":"def fresh_verifier?(verifier, ttl: 5.minutes)\n  payload = CanvasSecurity.decode_jwt(verifier) rescue nil\n  payload.is_a?(Hash) && payload[:exp].to_i > Time.now.to_i\nend","tryCatchPattern":"begin\n  body = CanvasSecurity.decode_jwt(verifier)\nrescue CanvasSecurity::TokenExpired\n  redirect_to fresh_download_url(attachment)\n  nil\nend","preventionTips":["Do not persist verifier URLs beyond the token TTL (emails, caches, bookmarks)","Mint verifiers at request time via download/inline URL helpers","Tune the verifier TTL to match realistic link-lifetime expectations","Monitor attachments.token_verifier_expired metrics to spot TTL misconfigurations"],"tags":["jwt","attachments","token-expired","authentication"],"backgroundTag":"jwt-token-expired","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}