{"record":{"id":"48feff4588905b19","repo":"instructure/canvas-lms","slug":"cannot-generate-a-symmetric-non-encrypted-jwt","errorCode":null,"errorMessage":"Cannot generate a symmetric, non-encrypted JWT","messagePattern":"Cannot generate a symmetric, non-encrypted JWT","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"gems/canvas_security/lib/canvas_security/services_jwt.rb","lineNumber":78,"sourceCode":"  def id\n    original_token[:jti]\n  end\n\n  def user_global_id\n    original_token[:sub]\n  end\n\n  def masquerading_user_global_id\n    original_token[:masq_sub]\n  end\n\n  def expires_at\n    original_token[:exp]\n  end\n\n  # Symmetric services JWTs are now deprecated\n  def self.generate(payload_data, base64: true, symmetric: false, encrypt: true)\n    raise ArgumentError, \"Cannot generate a symmetric, non-encrypted JWT\" if symmetric && !encrypt\n\n    payload = create_payload(payload_data)\n    crypted_token = if !encrypt\n                      CanvasSecurity.create_jwt(\n                        payload,\n                        nil,\n                        CanvasSecurity::ServicesJwt::KeyStorage.present_key,\n                        :autodetect\n                      )\n                    elsif symmetric\n                      CanvasSecurity.create_encrypted_jwt(payload, signing_secret, encryption_secret)\n                    else\n                      CanvasSecurity.create_encrypted_jwt(\n                        payload,\n                        CanvasSecurity::ServicesJwt::KeyStorage.present_key,\n                        encryption_secret,\n                        :autodetect\n                      )","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/gems/canvas_security/lib/canvas_security/services_jwt.rb#L60-L96","documentation":"Symmetric (shared-secret) services JWTs are deprecated in Canvas, and a symmetric unencrypted JWT would be trivially forgeable since a symmetric signing key plus no encryption offers no security. The generate method therefore explicitly forbids the combination symmetric: true with encrypt: false by raising ArgumentError.","triggerScenarios":"Calling CanvasSecurity::ServicesJwt.generate(payload, symmetric: true, encrypt: false) — directly or via defaults set in a wrapper.","commonSituations":"Migrating old code that used symmetric tokens and flipping encrypt off for debugging; porting legacy Canvas code that predates the deprecation.","solutions":["Stop passing symmetric: true and use the default asymmetric/encrypted token path","If you must stay symmetric, keep encrypt: true (the default)","If you want unencrypted tokens, drop symmetric: true and use the asymmetric flow"],"exampleFix":"// before\nServicesJwt.generate(payload, symmetric: true, encrypt: false)\n// after\nServicesJwt.generate(payload)\n# or explicitly:\nServicesJwt.generate(payload, symmetric: true, encrypt: true)","handlingStrategy":"validation","validationCode":"raise ArgumentError if symmetric && !encrypt","typeGuard":"def valid_generate_opts?(symmetric:, encrypt:) = !symmetric || encrypt","tryCatchPattern":null,"preventionTips":["Use the default generate options unless migration requires otherwise","Treat symmetric tokens as deprecated; plan migration to asymmetric","Grep codebase for symmetric: true usages"],"tags":["ruby","jwt","security","deprecation"],"backgroundTag":"deprecated-api-usage","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"}