instructure/canvas-lms · error · ArgumentError

Cannot generate a symmetric, non-encrypted JWT

Error message

Cannot generate a symmetric, non-encrypted JWT

What it means

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.

Solutions

  1. Stop passing symmetric: true and use the default asymmetric/encrypted token path
  2. If you must stay symmetric, keep encrypt: true (the default)
  3. If you want unencrypted tokens, drop symmetric: true and use the asymmetric flow

Example fix

// before
ServicesJwt.generate(payload, symmetric: true, encrypt: false)
// after
ServicesJwt.generate(payload)
# or explicitly:
ServicesJwt.generate(payload, symmetric: true, encrypt: true)
Defensive patterns

Strategy: validation

Validate before calling

raise ArgumentError if symmetric && !encrypt

Type guard

def valid_generate_opts?(symmetric:, encrypt:) = !symmetric || encrypt

Prevention

When it happens

Trigger: Calling CanvasSecurity::ServicesJwt.generate(payload, symmetric: true, encrypt: false) — directly or via defaults set in a wrapper.

Common situations: Migrating old code that used symmetric tokens and flipping encrypt off for debugging; porting legacy Canvas code that predates the deprecation.

Understand the failure class

Background: "is deprecated and will be removed" — deprecation warnings for old API names, keywords, and options, and how to migrate before the removal release — this error's family across 29 libraries.

Related errors


AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15). Data as JSON: /api/errors/48feff4588905b19. Report an issue: GitHub.

Appendix: source

Thrown at gems/canvas_security/lib/canvas_security/services_jwt.rb:78

  def id
    original_token[:jti]
  end

  def user_global_id
    original_token[:sub]
  end

  def masquerading_user_global_id
    original_token[:masq_sub]
  end

  def expires_at
    original_token[:exp]
  end

  # Symmetric services JWTs are now deprecated
  def self.generate(payload_data, base64: true, symmetric: false, encrypt: true)
    raise ArgumentError, "Cannot generate a symmetric, non-encrypted JWT" if symmetric && !encrypt

    payload = create_payload(payload_data)
    crypted_token = if !encrypt
                      CanvasSecurity.create_jwt(
                        payload,
                        nil,
                        CanvasSecurity::ServicesJwt::KeyStorage.present_key,
                        :autodetect
                      )
                    elsif symmetric
                      CanvasSecurity.create_encrypted_jwt(payload, signing_secret, encryption_secret)
                    else
                      CanvasSecurity.create_encrypted_jwt(
                        payload,
                        CanvasSecurity::ServicesJwt::KeyStorage.present_key,
                        encryption_secret,
                        :autodetect
                      )

View on GitHub (pinned to 1c9f0bb801)