require_dependency "panda_pal/application_controller" module PandaPal class LtiV1P3Controller < ApplicationController skip_before_action :verify_authenticity_token before_action :validate_launch!, only: [:resource_link_request] around_action :switch_tenant, only: [:resource_link_request] def login current_session_data[:lti_oauth_nonce] = SecureRandom.uuid @form_action = current_lti_platform.authentication_redirect_url @method = :post @form_data = { scope: 'openid', response_type: 'id_token', response_mode: 'form_post', prompt: 'none', redirect_uri: params[:target_link_uri] || v1p3_resource_link_request_url, client_id: params.require(:client_id), login_hint: params.require(:login_hint), lti_message_hint: params.require(:lti_message_hint), state: current_session.session_key, nonce: current_session_data[:lti_oauth_nonce] } end def resource_link_request # Redirect to correct region/env? if params[:launch_type] current_session_data.merge!({ lti_version: 'v1p3', lti_launch_placement: params[:launch_type], launch_params: @decoded_lti_jwt, }) redirect_with_session_to(:"#{LaunchUrlHelpers.launch_route(params[:launch_type])}_url", route_context: main_app) end # render json: { # launch_type: params[:launch_type], # final_url: LaunchUrlHelpers.launch_url(params[:launch_type]), # final_route: LaunchUrlHelpers.launch_route(params[:launch_type]), # decoded_jwt: @decoded_lti_jwt, # } end def tool_config if PandaPal.lti_environments.empty? render plain: 'Domains must be set in lti_environments' return end platform = PandaPal.lti_options.delete(:platform) || 'canvas.instructure.com' request_url = "#{request.scheme}://#{request.host_with_port}" parsed_request_url = URI.parse(request_url) mapped_placements = PandaPal.lti_paths.map do |k, opts| opts = LaunchUrlHelpers.normalize_lti_launch_desc(opts) opts.merge!({ placement: k, target_link_uri: LaunchUrlHelpers.absolute_launch_url( k.to_sym, host: parsed_request_url, launch_handler: v1p3_resource_link_request_path, default_auto_launch: true ), }) opts end config_json = { title: PandaPal.lti_options[:title], scopes: [], public_jwk_url: v1p3_public_jwks_url, description: PandaPal.lti_options[:description] || 'PandaPal LTI', target_link_uri: v1p3_resource_link_request_url, #app_url(:resource_link_request, request), oidc_initiation_url: v1p3_oidc_login_url, extensions: [{ platform: platform, privacy_level: "public", settings: { placements: mapped_placements, environments: PandaPal.lti_environments, }, }], custom_fields: PandaPal.lti_custom_params, # PandaPal.lti_options[:custom_fields], } render json: config_json end def public_jwks render json: { keys: [JWT::JWK.new(PandaPal.lti_private_key).export] } end private def auth_redirect_query return unless params[:target_link_uri]&.include? 'platform_redirect_url=' platform_redirect_url = Rack::Utils.parse_query(URI(params[:target_link_uri]).query)&.dig('platform_redirect_url') "?platform_redirect_url=#{platform_redirect_url}" end end end