Community Member

Starting with LTI 1.3

Hello, I am new to LTI development and I am currently trying to get my first "Hello World" on LTI 1.3, but I have encountered some difficulties.

I have integrated a test application that I found on github to make sure I understand the configuration parameters, after solving a couple of problems I have succeeded.

The problem with this application is that it does not have very clean code to be able to carry this to a framework such as laravel or similar, for which I find myself in the need to understand the integration flow in detail in order to create my own work environment.

Sample application 

The problem comes because I am not receiving the id_token after executing my query, failing that I get an "authenticity_token" next to "login_required", which I don't understand exactly what it means.

My connection code looks like this:

    use GuzzleHttp\Client;

    // This constant are obtained from LMS platform.
    define('PLATFORM_ID', 'https://canvas.beta.instructure.com');
    define('CLIENT_ID', 'MY_CLIENT_ID');
    define('AUTHENTICATION_URL', 'https://canvas.beta.instructure.com/api/lti/authorize_redirect');
    define('PLATFORM_JSON_WEBKEY_URL', 'https://canvas.beta.instructure.com/api/lti/security/jwks');
    // STEP 1
    // Receiving authentication parameters
    // die;

    // STEP 2 
    // Creating authentication request
    if(isset($_REQUEST['iss']) && isset($_REQUEST['login_hint']) && isset($_REQUEST['lti_message_hint'])){
        $nonce = getRandomString();
        $params = array(
            'client_id' => CLIENT_ID,
            'login_hint' => $_REQUEST['login_hint'],
            'lti_message_hint' => $_REQUEST['lti_message_hint'],
            'nonce' => $nonce,
            'prompt' => 'none',
            'redirect_uri' => "http://localhost/clean_integration/connect.php",
            'response_mode' => 'form_post',
            'response_type' => 'id_token',
            'scope' => 'openid',
            'state' => $nonce
        $options = [
            'form_params' => $params
            $client = new Client();
            $res = $client->request('POST', AUTHENTICATION_URL, $options);
            echo $res->getBody();
        }catch(Exception $e){
            die("Exception: {$e}");

    function getRandomString($length = 8){
        $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
        $value = '';
        $charsLength = strlen($chars) - 1;
        for ($i = 1; $i <= $length; $i++) {
            $value .= $chars[rand(0, $charsLength)];
        return $value;


When my app start, 

    [iss] => https://canvas.beta.instructure.com
    [login_hint] => HINT
    [client_id] => CLIENT_ID
    [target_link_uri] => http://localhost/clean_integration/connect.php
    [lti_message_hint] => HASH1.HASH2.HASH3
    [canvas_region] => us-east-1

After running my request I get the following, when actually I should get the token id (From what I understand I could be wrong at this point)

    [utf8] => ✓
    [authenticity_token] => SOME TOKEN THAT IS NOT JWT
    [error] => login_required
    [error_description] => Must have an active user session
    [state] => 9kofXdDs


If anyone can help me with the following questions I am very grateful.

1 - What am I doing wrong so that the token id with the user and resource data does not return? Am I missing a step?
2.- How can I use the member service (LTI Adventage)

Labels (1)
0 Kudos
3 Replies
Community Champion

I don't have much experience doing auth with PHP, so I can't give insight on using frameworks to clean this up. Canvas expects an OAuth2 request, which requires your client to request a code which is then used to request an authorization token with the scopes you define in the Developer Keys. If you can find a Laravel example of that flow, you could pretty easily work the LTI config in.

I would also suggest you post this in the Developers Group on the Community. There are all kinds of people with varying experience who can help you troubleshoot.

0 Kudos
Community Member

You cannot use a client like Guzzle to perform that part of the authentication, looking at some examples from the IMS and ceLTIc project I have realized that both work by making the request with an auto-submit form with javascript.
This may be due to some LMS cookie that is only available in the loading context in the browser and therefore is not accessible when consuming the endpoint with Guzzle.

By submitting the same data in a self-submitting form everything has worked wonderfully.

0 Kudos
Community Participant

I am experiencing the same issue using PHP code.  However, I am not using the Guzzle client, I am issuing a straight cUrl call.  This issue lies in the curl set opts:

curl_setopt($curl, CURLOPT_RETURNTRANSFER, false);

curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);

The Authorization Response from Canvas should (according to IMS Global standards) be sent to the tool URI.  As it says in Canvas documentation:

Canvas then sends its auth response to the redirect_ur ithat the tool provided in Step 2.  Where the tool uri is supposed to be the uri listed in the redirect box of the dev key form.

However, this results in the error you are getting.  Guzzle must be configured to have these same curl opts.

If I do not have the RETURNTRANSFER set to false it returns the id_token to the login_initiation php code.  This is very frustrating and I haven't found the magic combination of configuration settings yet.


Good Luck


0 Kudos