Cvmcosta / ltijs

Turn your application into a fully integratable LTI 1.3 tool provider.
https://cvmcosta.github.io/ltijs/
Apache License 2.0
300 stars 67 forks source link

TypeError: platform.platformActive is not a function #92

Closed rtgc-w closed 3 years ago

rtgc-w commented 3 years ago

Hi There,

I am encountering a TypeError: platform.platformActive is not a function error when launching most requests from an IMS Reference Implementation Platform. Line 112 of Auth.js tries to call platform.platformActive() but breakpoints show that platform is an array of a single element at this point; so it seems like it ought to be platform[0].platformActive()?

However, I think this error might be a side effect of my not having set some config property correctly (or aligned it correctly with the reference implementation fields, which don't have a lot of documentation that i can find) but have not been able to overcome the issue. I'm using the ltijs-sequelize plugin with mysql as well, though I'm not sure it makes a difference here.

Here are as many details as i can think of that would help recreate the issue:

Line in question:

https://github.com/Cvmcosta/ltijs/blob/3ab92b8a33802613b5d2b8c6e3db0174c588d76a/dist/Utils/Auth.js#L112

IMS Platform Link:

https://lti-ri.imsglobal.org/platforms/1879

Debug log:

Debugger attached.
2021-03-29T15:52:39.041Z provider:main Attempting to connect to database
2021-03-29T15:52:39.042Z provider:database Using Sequelize Database Plugin - Cvmcosta
2021-03-29T15:52:39.043Z provider:database Dialect: mysql
2021-03-29T15:52:39.578Z provider:database Cleaning up expired records ...
2021-03-29T15:52:39.595Z provider:database Expired idtoken: 0
2021-03-29T15:52:39.609Z provider:database Expired contexttoken: 0
2021-03-29T15:52:39.624Z provider:database Expired accesstoken: 0
2021-03-29T15:52:39.637Z provider:database Expired nonce: 0
2021-03-29T15:52:39.652Z provider:database Expired state: 0
2021-03-29T15:52:39.662Z provider:main Ltijs started listening on port:  171
2021-03-29T15:52:39.681Z provider:main Platform already registered
2021-03-29T15:52:46.572Z provider:main Receiving request at path: /grade
2021-03-29T15:52:46.572Z provider:main Path does not match reserved endpoints
2021-03-29T15:52:46.572Z provider:main Cookies received: 
2021-03-29T15:52:46.573Z provider:main [Object: null prototype] {}
2021-03-29T15:52:46.573Z provider:main Received idtoken for validation
2021-03-29T15:52:46.573Z provider:auth Response state: undefined
2021-03-29T15:53:31.645Z provider:auth Attempting to validate iss claim
2021-03-29T15:53:34.802Z provider:auth Request Iss claim: undefined
2021-03-29T15:53:57.994Z provider:auth Response Iss claim: https://lti-ri.imsglobal.org/platforms/1879/
2021-03-29T15:54:12.530Z provider:auth Dev Mode enabled: Missing state validation cookies will be ignored
2021-03-29T15:54:52.494Z provider:auth Attempting to retrieve registered platform
2021-03-29T16:07:13.195Z provider:auth TypeError: platform.platformActive is not a function
    at Function.validateToken (...\node_modules\ltijs\dist\Utils\Auth.js:112:43)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at async sessionValidator (...\node_modules\ltijs\dist\Provider\Provider.js:400:27)
2021-03-29T16:07:13.195Z provider:main Passing request to invalid token handler

Server.js (secrets redacted):

// modeled after https://github.com/Cvmcosta/ltijs#quick-start & https://github.com/Cvmcosta/ltijs-demo-server/blob/master/index.js
const path = require("path");
const lti = require("ltijs").Provider;
const Database = require("ltijs-sequelize");
const routes = require("./src/routes");

const dbConfig = {
  database: "REDACTED",
  user: "REDACTED",
  pass: "REDACTED",
  options:{
    host: "REDACTED",
    port: "REDACTED",
    dialect: "mysql",
    appName: "REDACTED"
  },
};
const db = new Database(dbConfig.database, dbConfig.user, dbConfig.pass, dbConfig.options);
const setupObject = {
  signingKey: "LTIKEY",
  ltiAddons:{
    plugin: db
  },
  ltiOptions: {
    cookies: { secure: false, samesite: "None" },
    devMode: true
  }
};
const platformConfig = {
  imsReference: {
    url:"https://lti-ri.imsglobal.org/platforms/1879/",
    name:"NGTEST",
    clientId:"TAP-TEST",
    authenticationEndpoint:"https://lti-ri.imsglobal.org/platforms/1879/authorizations/new",
    accesstokenEndpoint:"https://lti-ri.imsglobal.org/platforms/1879/access_tokens",
    authConfig: {
      method: "JWK_SET", key:"https://lti-ri.imsglobal.org/platforms/1879/platform_keys/1783.json"
    }
  }
};

lti.setup(setupObject.signingKey, setupObject.ltiAddons, setupObject.ltiOptions);
// lti.onConnect((token, req, res)=>{
//   console.dir(token);
//   return res.send("token made");
// });

lti.app.use(routes);

const serve = async () => {
  await lti.deploy({ port: 171 });
  await lti.registerPlatform(platformConfig.imsReference);

};

serve();

Steps:

  1. Start the LTI server locally (listens to localhost:171)
  2. Open IMS Platform Link
  3. Navigate to Course->"View Line Items"->"View Resource Link"
  4. Click "Perform Launch"

Expected:

Launch to complete or validate

Observed:

401 response of


{
    "details": {
        "description": "Error validating ltik or IdToken",
        "message": "platform.platformActive is not a function"
    }
}
Cvmcosta commented 3 years ago

Hello @rtgc-w! I think i know what is going on, if i am right this is an issue with the Reference implementation. Basically, to maintain compatibility with older versions, the more recent versions of Ltijs return an array of platforms if no clientId is passed to getPlatform(), so my guess is that the reference implementation is failing to pass an clientId.

Can you monitor the network tab during a launch and send me the id_token JWT sent to Ltijs at the end of a launch?

rtgc-w commented 3 years ago

Hi @Cvmcosta, thanks for the reply:

here is the id_token sent on the POST request that fails:

id_token: {
    "utf8": "✓",
    "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IlBZSnk1YlplaTh3NzJkTXJMeUFFLWt2cF9hMllWOFRPaXRzUEsyeHpZSDgifQ.eyJodHRwczovL3B1cmwuaW1zZ2xvYmFsLm9yZy9zcGVjL2x0aS9jbGFpbS9tZXNzYWdlX3R5cGUiOiJMdGlSZXNvdXJjZUxpbmtSZXF1ZXN0IiwiZ2l2ZW5fbmFtZSI6Ik1vc2hlIiwiZmFtaWx5X25hbWUiOiJTci4iLCJtaWRkbGVfbmFtZSI6IlBhdWNlayIsInBpY3R1cmUiOiJodHRwOi8vZXhhbXBsZS5vcmcvTW9zaGUuanBnIiwiZW1haWwiOiJNb3NoZS5Tci5AZXhhbXBsZS5vcmciLCJuYW1lIjoiTW9zaGUgUGF1Y2VrIFZvblJ1ZWRlbiBTci4iLCJodHRwczovL3B1cmwuaW1zZ2xvYmFsLm9yZy9zcGVjL2x0aS9jbGFpbS9yb2xlcyI6WyJodHRwOi8vcHVybC5pbXNnbG9iYWwub3JnL3ZvY2FiL2xpcy92Mi9tZW1iZXJzaGlwI0xlYXJuZXIiLCJodHRwOi8vcHVybC5pbXNnbG9iYWwub3JnL3ZvY2FiL2xpcy92Mi9pbnN0aXR1dGlvbi9wZXJzb24jU3R1ZGVudCIsImh0dHA6Ly9wdXJsLmltc2dsb2JhbC5vcmcvdm9jYWIvbGlzL3YyL21lbWJlcnNoaXAjTWVudG9yIl0sImh0dHBzOi8vcHVybC5pbXNnbG9iYWwub3JnL3NwZWMvbHRpL2NsYWltL3JvbGVfc2NvcGVfbWVudG9yIjpbImE2MmM1MmMwMmJhMjYyMDAzZjVlIl0sImh0dHBzOi8vcHVybC5pbXNnbG9iYWwub3JnL3NwZWMvbHRpL2NsYWltL3Jlc291cmNlX2xpbmsiOnsiaWQiOiIzMzAzMCIsInRpdGxlIjoiVGVzdCBUaXRsZSIsImRlc2NyaXB0aW9uIjoiVGVzdCBEZXNjcmlwdGlvbiJ9LCJodHRwczovL3B1cmwuaW1zZ2xvYmFsLm9yZy9zcGVjL2x0aS9jbGFpbS9jb250ZXh0Ijp7ImlkIjoiMjA2NDMiLCJsYWJlbCI6IkFCQy0xMjMiLCJ0aXRsZSI6IlRlc3QgQ291cnNlIiwidHlwZSI6WyJzdHVkZW50Il19LCJodHRwczovL3B1cmwuaW1zZ2xvYmFsLm9yZy9zcGVjL2x0aS9jbGFpbS90b29sX3BsYXRmb3JtIjp7Im5hbWUiOiJOR1RFU1QiLCJjb250YWN0X2VtYWlsIjoiIiwiZGVzY3JpcHRpb24iOiIiLCJ1cmwiOiIiLCJwcm9kdWN0X2ZhbWlseV9jb2RlIjoiIiwidmVyc2lvbiI6IjEuMCIsImd1aWQiOjE4Nzl9LCJodHRwczovL3B1cmwuaW1zZ2xvYmFsLm9yZy9zcGVjL2x0aS1hZ3MvY2xhaW0vZW5kcG9pbnQiOnsic2NvcGUiOlsiaHR0cHM6Ly9wdXJsLmltc2dsb2JhbC5vcmcvc3BlYy9sdGktYWdzL3Njb3BlL2xpbmVpdGVtIiwiaHR0cHM6Ly9wdXJsLmltc2dsb2JhbC5vcmcvc3BlYy9sdGktYWdzL3Njb3BlL3Jlc3VsdC5yZWFkb25seSIsImh0dHBzOi8vcHVybC5pbXNnbG9iYWwub3JnL3NwZWMvbHRpLWFncy9zY29wZS9zY29yZSJdLCJsaW5laXRlbXMiOiJodHRwczovL2x0aS1yaS5pbXNnbG9iYWwub3JnL3BsYXRmb3Jtcy8xODc5L2NvbnRleHRzLzIwNjQzL2xpbmVfaXRlbXMiLCJsaW5laXRlbSI6Imh0dHBzOi8vbHRpLXJpLmltc2dsb2JhbC5vcmcvcGxhdGZvcm1zLzE4NzkvY29udGV4dHMvMjA2NDMvbGluZV9pdGVtcy8xNTYwOSJ9LCJodHRwczovL3B1cmwuaW1zZ2xvYmFsLm9yZy9zcGVjL2x0aS1ucnBzL2NsYWltL25hbWVzcm9sZXNlcnZpY2UiOnsiY29udGV4dF9tZW1iZXJzaGlwc191cmwiOiJodHRwczovL2x0aS1yaS5pbXNnbG9iYWwub3JnL3BsYXRmb3Jtcy8xODc5L2NvbnRleHRzLzIwNjQzL21lbWJlcnNoaXBzIiwic2VydmljZV92ZXJzaW9ucyI6WyIyLjAiXX0sImh0dHBzOi8vcHVybC5pbXNnbG9iYWwub3JnL3NwZWMvbHRpLWNlcy9jbGFpbS9jYWxpcGVyLWVuZHBvaW50LXNlcnZpY2UiOnsic2NvcGVzIjpbImh0dHBzOi8vcHVybC5pbXNnbG9iYWwub3JnL3NwZWMvbHRpLWNlcy92MXAwL3Njb3BlL3NlbmQiXSwiY2FsaXBlcl9lbmRwb2ludF91cmwiOiJodHRwczovL2x0aS1yaS5pbXNnbG9iYWwub3JnL3BsYXRmb3Jtcy8xODc5L3NlbnNvcnMiLCJjYWxpcGVyX2ZlZGVyYXRlZF9zZXNzaW9uX2lkIjoidXJuOnV1aWQ6MDNhYzAxM2RjMTZlNjQ4ZTM3MDMifSwiaXNzIjoiaHR0cHM6Ly9sdGktcmkuaW1zZ2xvYmFsLm9yZy9wbGF0Zm9ybXMvMTg3OS8iLCJhdWQiOiIiLCJpYXQiOjE2MTcwNDAxMTIsImV4cCI6MTYxNzA0MDQxMiwic3ViIjoiOWU0YjY1ZWEyN2Y4ODZlZTk0NTUiLCJub25jZSI6IjViMTk5ZWE3ZTQyZjgwYWJmY2ZhIiwiaHR0cHM6Ly9wdXJsLmltc2dsb2JhbC5vcmcvc3BlYy9sdGkvY2xhaW0vdmVyc2lvbiI6IjEuMy4wIiwibG9jYWxlIjoiZW4tVVMiLCJodHRwczovL3B1cmwuaW1zZ2xvYmFsLm9yZy9zcGVjL2x0aS9jbGFpbS9sYXVuY2hfcHJlc2VudGF0aW9uIjp7ImRvY3VtZW50X3RhcmdldCI6ImlmcmFtZSIsImhlaWdodCI6MzIwLCJ3aWR0aCI6MjQwLCJyZXR1cm5fdXJsIjoiaHR0cHM6Ly9sdGktcmkuaW1zZ2xvYmFsLm9yZy9wbGF0Zm9ybXMvMTg3OS9yZXR1cm5zIn0sImh0dHBzOi8vd3d3LmV4YW1wbGUuY29tL2V4dGVuc2lvbiI6eyJjb2xvciI6InZpb2xldCJ9LCJodHRwczovL3B1cmwuaW1zZ2xvYmFsLm9yZy9zcGVjL2x0aS9jbGFpbS9jdXN0b20iOnsibXlDdXN0b21WYWx1ZSI6IjEyMyJ9LCJodHRwczovL3B1cmwuaW1zZ2xvYmFsLm9yZy9zcGVjL2x0aS9jbGFpbS9kZXBsb3ltZW50X2lkIjoiMSIsImh0dHBzOi8vcHVybC5pbXNnbG9iYWwub3JnL3NwZWMvbHRpL2NsYWltL3RhcmdldF9saW5rX3VyaSI6Imh0dHA6Ly9sb2NhbGhvc3Q6MTcxL2dyYWRlIn0.pTFZoIV3fX_9trSkBA5l2x8v7pC_DE-7Y7TXCLedS2-yt2sgeGGLjcx0_ufxcaW3J74NUE9Ba1vZ624rh_q5kHRUS3MnAqszAdaOkj_eoF9K5WmSTzaGKBOW4bCm425psMJSn5ym8uxmOm4Rw8WIxzbUbLn3GhHBXCPnDcGg9Dl70Ng1eOzTebw3-6yf05jd5cc0Foz2RASUhmex0UFvb-8w_5gazst9LoGUbFszBBUZ6dZCgn7UA7-8OqJlnbfvlK3nRp2OHCcIEXUktLPL0LhfHIZEbQjJtdDobVaq7I-gxcW1q3OGYxOUzEauiyQDXPfXSHIZPLV6XrPSXxVIPw",
    "commit": "Perform+Launch"
}

let me know if that was the wrong and we need something else.

Thanks again!

Cvmcosta commented 3 years ago

Yes, as i suspected the ID Token sent by this platform has an empty aud claim, that is causing the error. This goes against the LTI protocol specification:

aud REQUIRED. Audience(s) for whom this ID Token is intended i.e. the Tool. It MUST contain the OAuth 2.0 client_id of the Tool as an audience value. It MAY also contain identifiers for other audiences. In the general case, the aud value is an array of case-sensitive strings. In the common special case when there is one audience, the aud value MAY be a single case-sensitive string.

In the ID Token the aud claim is empty, it should contain the clientID:

...
   "iss":"https://lti-ri.imsglobal.org/platforms/1879/",
   "aud":"",
   "iat":1617040112,
...
rtgc-w commented 3 years ago

That looks like it did it! I think earlier I had removed a client id from the platform details for an unrelated reason and forgot to return it, Though it would have saved me some time if the error was more closely related to what was missing, rather than a type error.

In any case, I greatly appreciate the assistance! And thanks so much for this library, it's been a great help in trying to better learn LTI!

Cvmcosta commented 3 years ago

I completely understand, it was just an unexpected error, i try to assume the LTI spec is being obeyed in some cases, maybe i should try to validate the idtoken more thoroughly.

Thanks!