[RUBY] Implement LTI authentication in Rails

What is LTI certification?

There is a standard called LTI certification. LTI is an acronym for the word Learning Tools Interoperability, which means a standard for exchanging authentication information between learning support tools.

It's a fairly niche technology that has a specific field among authentication technologies, so many people may not know it. Before I specialized in educational technology, I didn't even know the word LTI.

Below is an explanatory diagram of the LTI certification officially issued by IMS GLOBAL.

Personally, I understand that it is a kind of OAuth after all, but the application we develop is Learning Tools, the main tool that is the basis of authentication is Learning Platform, and Learning Tools is an extension of Learning Platform. Instead of providing it, you will get your credentials from the Learning Platform. This has the advantage that users can use various apps properly without switching users. At this time, Learning Tools is also called LTI Tool Provider, and Learning Platform is also called LTI Consumer.

But the question is how to implement this. In fact, because of its niche technology, it lacks documentation, and even if you search on the net, there are not many implementation examples published. So, in this article, I would like to introduce the actual practice of LTI authentication using Ruby on Rails.

Implementation example

Library installation

First, install the required libraries.

$ bundle add devise
$ bundle add ims-lti
$ bundle add oauth

The advantage of using Rails is that there is a library for lti authentication. This time, we will implement it using ʻims-lti`.

Authentication key registration

Create a lti_settings.yml file under config. Please describe the following contents (use a safe character string for the consumer key and LTI secret as appropriate).

config/lti_settings.yml


production:
  __consumer_key__: '__lti_secret__'

development:
  __consumer_key__: '__lti_secret__'

Once created, add the settings to load it to config / application.rb.

config/application.rb


config.lti_settings = Rails.application.config_for(:lti_settings)

Creating a controller

Let's create ltis_controller and describe the following contents


require 'oauth/request_proxy/action_controller_request'

class LtisController < ApplicationController
  skip_before_action :verify_authenticity_token, only: :launch

  def lti_launch
    #The received consumer key is config/lti_settings.Check if it's in yml
    if not Rails.configuration.lti_settings[params[:oauth_consumer_key]]
      render :launch_error, status: 401
      return
    end 
    shared_secret = Rails.configuration.lti_settings[params[:oauth_consumer_key]]
    authenticator = IMS::LTI::Services::MessageAuthenticator.new(request.url, request.request_parameters, shared_secret)
    
    #Check if the signature is valid
    if not authenticator.valid_signature?
      render :launch_error, status: 401
      return
    end
    #check if the message is too old
    if DateTime.strptime(request.request_parameters['oauth_timestamp'],'%s') < 5.minutes.ago
      render :launch_error, status: 401
      return
    end
    
    #Save LTI information in session
    session_data = {
      "fullname" => authenticator&.message&.lis_person_name_full,
      "email" => authenticator&.message&.lis_person_contact_email_primary,
      "user_id" => authenticator&.message&.user_id,
      "context_id" => authenticator&.message&.context_id,
      "context_title" => authenticator&.message&.context_title,
      "tool_consumer_instance_name" => authenticator&.message&.tool_consumer_instance_name
    }
    print(session_data)
    session['lti-authenticator'] = session_data
    sign_in_and_redirect(User.first)
  end
end

You can easily authenticate by passing the request sent to MessageAuthenticator and the key information held here in advance. If the authentication is successful, the application side authentication is implemented by sign_in using the devise library.

Finally, let's define the endpoint to launch lti_launch.

config/routes.rb


  match 'lti/launch' => 'ltis#lti_launch', via: [:get, :post], as: :lti_launch

Settings on the Learning Platform side

Once this is done, the Learning Platform can write the authentication information described in lti_settings.yml in advance and set it to transition to the specified endpoint. This method differs depending on the individual Learning Platform, so I will omit it.

(Supplement) If a cross origin error occurs in Rails during LTI transition

Please add the following contents to config / application.rb

config/application.rb


    config.lti_settings = Rails.application.config_for(:lti_settings)
    config.action_dispatch.default_headers['Referrer-Policy'] = 'unsafe-url'
    config.action_controller.forgery_protection_origin_check = false
    config.action_controller.allow_forgery_protection = false

References

Rails LTI Tool Provider IMS LTI

Recommended Posts

Implement LTI authentication in Rails
Implement markdown in Rails
Implement application function in Rails
Implement follow function in Rails
Implement Basic authentication in Java
Implement import process in Rails
Implement simple login function in Rails
Implement a contact form in Rails
Implement CSV download function in Rails
Implement Rails pagination
Group_by in Rails
How to implement ranking functionality in Rails
Implement button transitions using link_to in Rails
Create authentication function in Rails application using devise
(Basic authentication) environment variables in rails and Docker
Implement star rating function using Raty in Rails6
How to implement a like feature in Rails
Implement the Like feature in Ajax with Rails.
Implement iteration in View by rendering collection [Rails]
Model association in Rails
Adding columns in Rails
[Rails] Implement search function
Disable turbolinks in Rails
CSRF measures in Rails
Implement Rails account BAN
^, $ in Rails regular expression
Use images in Rails
Implement CustomView in code
Understand migration in rails
Split routes.rb in Rails6
[Rails] Implement rake task
How to implement guest login in 5 minutes in rails portfolio
Implement post search function in Rails application (where method)
How to implement a like feature in Ajax in Rails
Introduce devise in Rails to implement user management functionality
Implement user follow function in Rails (I use Ajax) ①
Get UserAgent in [Rails] controller
[Rails] Implement User search function
Declarative transaction in Rails #ginzarb
[Beginner] Implement NavigationBar in code
SNS authentication using Rails google
Implement two-step verification in Java
Japaneseize using i18n with Rails
Implement login function in Rails simply by name and password (1)
Implement login function in Rails simply by name and password (2)
Error in rails db: migrate
Gem often used in Rails
Implement math combinations in Java
Display Flash messages in Rails
2 Implement simple parsing in Java
[Android] Implement Adblock in WebView
Implement Email Sending in Java
View monthly calendar in Rails
Implement Swift UITextField in code
Implement functional quicksort in Java
[Rails] How to implement scraping
Use multiple checkboxes in Rails6!
[Rails] Implement image posting function
Implement login function simply with name and password in Rails (3)
Rewrite Routes in Rails Engine
Implement user registration function and corporate registration function separately in Rails devise