instructure/canvas-lms · error · InvalidAuthJwt
the Tool Proxy must be using a split secret
Error message
the Tool Proxy must be using a split secret
What it means
After validating the tool proxy's developer key, the LTI OAuth2 authorization validator parses the stored tool proxy JSON and requires that its enabled_capabilities include 'Security.splitSecret' or 'OAuth.splitSecret'. JWT-based token exchange for tool proxies is only supported when the tool stores its secret in split (half/half) form; proxies registered with a plain shared secret are rejected with InvalidAuthJwt.
Solutions
- Re-register the tool proxy including 'Security.splitSecret' (or 'OAuth.splitSecret') in enabled_capabilities and store the secret split in half.
- Update the tool proxy's raw_data to add the splitSecret capability, then re-split the shared secret (each half stored separately) via console.
- If the tool cannot use split secrets, fall back to the legacy OAuth 1.0 signature flow instead of the JWT OAuth2 endpoint.
Example fix
// before proxy['enabled_capability'] # => ['Security.halfSharedSecret'] // after (re-register / patch raw_data) proxy['enabled_capability'] = ['Security.halfSharedSecret', 'Security.splitSecret'] tp.update!(raw_data: proxy.to_json)
Defensive patterns
Strategy: validation
Validate before calling
caps = JSON.parse(tp.raw_data)['enabled_capability'] || [] unless caps.intersect?(['Security.splitSecret', 'OAuth.splitSecret']) raise 'tool proxy must declare splitSecret capability' end
Type guard
def split_secret?(tool_proxy)
caps = JSON.parse(tool_proxy.raw_data)['enabled_capability'] || []
caps.any? { |c| %w[Security.splitSecret OAuth.splitSecret].include?(c) }
end Try / catch
begin
token = oauth2.request_access_token
rescue Lti::Oauth2::AuthorizationValidator::InvalidAuthJwt => e
Rails.logger.warn("split secret required: #{e.message}")
end Prevention
- Always include Security.splitSecret in enabled_capabilities when registering tool proxies.
- Never hand-edit raw_data without re-verifying capabilities.
- Use current Canvas tool registration flows that default to split secrets.
When it happens
Trigger: Requesting an OAuth2 access token via LTI JWT authorization using a ToolProxy whose raw_data 'enabled_capability' list lacks both 'Security.splitSecret' and 'OAuth.splitSecret'.
Common situations: Legacy tool proxies registered before split-secret became standard; tools manually registered with a full shared secret; tool proxy JSON regenerated/edited losing the capability entry; migrating an old TC-registered proxy to JWT auth.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- Access token expired
- Access token invalid - signature likely incorrect
- Developer key mismatch
- either the tool proxy or developer key were not found
- iat must be in the past
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/e5291fb78c11834f.
Report an issue: GitHub.
Appendix: source
Thrown at lib/lti/oauth2/authorization_validator.rb:64
unless validator.valid?
raise InvalidAuthJwt, validator.error_message
end
validated_jwt
end
end
alias_method :validate!, :jwt
def tool_proxy
@tool_proxy ||=
if (tp = ToolProxy.where(guid: unverified_jwt[:sub], workflow_state: "active").first)
developer_key = tp.product_family.developer_key
raise InvalidAuthJwt, "the Developer Key is not active or available in this environment" if developer_key.present? && !developer_key.usable?
ims_tool_proxy = ::IMS::LTI::Models::ToolProxy.from_json(tp.raw_data)
unless ims_tool_proxy.enabled_capabilities.intersect?(["Security.splitSecret", "OAuth.splitSecret"])
raise InvalidAuthJwt, "the Tool Proxy must be using a split secret"
end
tp
end
end
def developer_key
@_developer_key ||= begin
dev_key = DeveloperKey.find_cached(unverified_jwt[:sub])
raise MissingAuthorizationCode if dev_key && @code.blank?
dev_key
rescue ActiveRecord::RecordNotFound
nil
end
end
def subView on GitHub (pinned to 1c9f0bb801)