// take the raw json string and just append sharedsecret so it becomes "{json string}sharedSecret"
string itslAuthWithSharedSecret = string.Format(CultureInfo.InvariantCulture, "{0}{1}", itslAuthParamValuesJson, plugin.SharedSecret);
Best practices, tips and tricks
Best practices
Validate request parameters
itslearning will send a request to your application with parameters like ApiSessionId, UserId and Permissions. It is very important that you validate these parameters by comparing the Signature in the request with a signature you create from the given parameters and the SharedSecret. You should only accept the request if the signature in the request matches the signature you create. The signature is an MD5 hash of a string created from the concatenation of the application key, the shared secret, the session ID and the session timestamp. An example can be found in the SDK sample application (the SampleCommunicationHelper.ComputeHash function).
For for more information about verifying the signature, see the separate section further down on this page.
Filter input from user
Input from users (request parameters, form fields etc) must be filtered before you output it in a page or use it in code (look out for XSS and sql injections).
Use HTTPS
To keep data secure and to avoid Internet Explorer prompting the users about "viewing insecure content" you should serve your extension (including images) on a secure connection. If you don't, you risk IE users inadvertently choosing that they do not wish to load insecure content and thus won't be able to see parts of you extension.
Date and time
If you need to display date and time information, you should always display this in the user's time zone, use the parameter OlsonTimeZoneId or WindowsTimeZoneId. If you need to store date and time it is good practice to store this in UTC. E.g. deadlines are important to display correctly with the user's timezone taken into account.
Internationalization
Use the the Locale parameter sent from itslearning to set format for date time, numbers (decimal separator) etc, and use the Language parameter to display texts in user's preferred language (if you support it).
Session expiration
itslearning applications should handle session expiration. One option is to set session timeout to big value (for IIS 6.0 the maximum session timeout is 1440 minutes).
Another (better) solution is to keep session alive while user has an application page open. To do this you can make AJAX calls to a WebService every 10 minutes (if session timeout is set to 20 minutes). If an error occurs you can try to reduce the interval between the Web-service calls.
Example in javascript with jQuery:
$(document).ready(
function
() {setInterval(keepSessionAlive, 10*60);});
function
keepSessionAlive() {$.ajax({ type:
"GET"
,url:
"/Services/MyService.asmx/KeepAlive"
,error:
function
() { setTimeout(keepSessionAlive, 1*60); }});}
Problem with cookies in Internet Explorer (session expires)
Applications are executing in a FRAME which has some limitations. Internet Explorer blocks third party cookies by default so there might be a problem when using session state in the application.
A workaround might be to add a P3P header:
On each page, you could put:
<meta http-equiv=
"P3P"
content=
'CP="CAO PSA OUR"'
/>
or in Global.asax:
protected
void
Application_BeginRequest(object sender, EventArgs e){ HttpContext.Current.Response.AddHeader(
"p3p"
,
"CP="
CAO PSA OUR
""
);}
"P3P version 1.0 is a protocol designed to inform Web users about the data-collection practices of Web sites". Please take half a minute to read about the purpose of P3P and then what to include in your P3P header.
Safari also blocks third party cookies by default. Safari users must manually allow third party cookies in their browsers.
Use Session carefully
The same user may have several windows open in a browser window, to different instances. This means that you must not store parameters received in the URL (LearningObjectInstanceId, LearningObjectId) in Session.
Tips and howtos
How to access extended data
If you are developing extensions that should only be available for your own itslearning site, follow these steps:
-
In itslearning, go to Home > Administration > Manage apps > Extended data
-
Choose your Application, Plug-in or Module extension in the list and click Permit
If you are developing extensions that you want to make available for all itslearning customers or in selected countries, please send a mail to [email protected]
Verifying the signature parameter
The request from itslearning to the extension contains a signature that can (should) be used to verify the authenticity of the message.
Calculating the signature for modules or plugins
Here's a sample scenario, that describes the process of verifying the signature. First, itslearning calls the extension:
GET http://www.myitslextension.com:902/?itsl_auth=%7b%22TimeStamp%22%3a%222014-01-05T16%3a20%3a19%22%2c%22Country%22%3a%22NO%22%2c%22CustomerId%22%3a%221%22%2c%22EditReference%22%3a%22%22%2c%22EducationalLevel%22%3a%22Higher%22%2c%22FirstName%22%3a%22Admin%22%2c%22Language%22%3a%22en-US%22%2c%22LastName%22%3a%22Admin%22%2c%22OAuthToken%22%3a%2230093542-3827-4b61-b49a-f4914ffa8649%22%2c%22OAuthTokenSecret%22%3a%22822680ad-a438-4672-a009-e6e584a14f31%22%2c%22PersonId%22%3a%221%22%2c%22PostTo%22%3a%22https%3a%5c%2f%5c%2fwww.itslearning.com%5c%2feditor%5c%2fInsertPluginContentHtml.aspx%3fExtensionId%3d5006%26EditorClientInstanceId%3dctl00_ContentPlaceHolder_Description_DescriptionEditorCKEditor_ctl00%22%2c%22Role%22%3a%22Staff%22%7d&itsl_sign=7a52cb190b9d20db1f7f19d1946fc439 HTTP/1.1
Then, the contents of itsl_auth are URL-decoded:
{"TimeStamp":"2014-01-05T16:20:19","Country":"NO","CustomerId":"1","EditReference":"","EducationalLevel":"Higher","FirstName":"Admin","Language":"en-US","LastName":"Admin","OAuthToken":"30093542-3827-4b61-b49a-f4914ffa8649","OAuthTokenSecret":"822680ad-a438-4672-a009-e6e584a14f31","PersonId":"1","PostTo":"https:\/\/www.itslearning.com\/editor\/InsertPluginContentHtml.aspx?ExtensionId=5006&EditorClientInstanceId=ctl00_ContentPlaceHolder_Description_DescriptionEditorCKEditor_ctl00","Role":"Staff"}
We assume, that the extension's shared secret is set to
96002924-z56a-2369-z99z-5afdtt413ad9
Then, the base string for computing the signature is created, by concatenating contents of itsl_auth parameter and extension's shared secret:
{"TimeStamp":"2014-01-05T16:20:19","Country":"NO","CustomerId":"1","EditReference":"","EducationalLevel":"Higher","FirstName":"Admin","Language":"en-US","LastName":"Admin","OAuthToken":"30093542-3827-4b61-b49a-f4914ffa8649","OAuthTokenSecret":"822680ad-a438-4672-a009-e6e584a14f31","PersonId":"1","PostTo":"https:\/\/www.itslearning.com\/editor\/InsertPluginContentHtml.aspx?ExtensionId=5006&EditorClientInstanceId=ctl00_ContentPlaceHolder_Description_DescriptionEditorCKEditor_ctl00","Role":"Staff"}96002924-z56a-2369-z99z-5afdtt413ad9
Next, the MD5 hash is computed for the base string:
7a52cb190b9d20db1f7f19d1946fc439
Then, the MD5 hash is compared to the signatured, placed in itsl_sign parameter in the incoming request.
itsl_sign=7a52cb190b9d20db1f7f19d1946fc439
Since both values are equal, the signature is correct. It means that the request has not been tampered with and it comes from itslearning. That's because only itslearning and the extension itself know the value of the shared secret that is used to generate the incoming signature. The next step is to check if the request has not been repeated by a potential attacker. In order to do that, an arbitrary request expiration time should be introduced. After verifying the signature, the current date and time should be compared to the request's timestamp. If the difference does not exceed the expiration time, the request should be considered valid.
Calculating the signature for applications
The process of calculating the signature for applications is identical as for plugins or modules. The only difference is the base string. Instead of using contents of itsl_auth parameter, the base string is determined from the entire request URL:
https://www.myitslextension.com:3100/ViewRegistration.aspx?Accessibility=False&allowedhtmlcodelevel=LessRestricted&ApiSessionId=rundhq45ase1yne212uqpu55&ContextRole=Learner&CustomerId=1&email=johnsmith%40abc.com&Encoding=utf8&FirstName=Admin&Language=en-US&LastName=Admin&LearningObjectId=218&LearningObjectInstanceId=1216&Locale=nb-NO&OlsonTimeZoneId=Europe%2fOslo&Permissions=Read%2c+Participate%2c+Evaluate%2c+Modify&ReadOnly=False&Role=Staff&synckey=TheSyncKey&Use12HTimeFormat=False&UserId=2&Version=LatestOrDraft&WindowsTimeZoneId=Central+European+Standard+Time&Timestamp=2014-01-06T11:08:12&Signature=c2f593d119ade74d6077775a561eb7da
Then, the signature is removed:
https://www.myitslextension.com:3100/ViewRegistration.aspx?Accessibility=False&allowedhtmlcodelevel=LessRestricted&ApiSessionId=rundhq45ase1yne212uqpu55&ContextRole=Learner&CustomerId=1&email=johnsmith%40abc.com&Encoding=utf8&FirstName=Admin&Language=en-US&LastName=Admin&LearningObjectId=218&LearningObjectInstanceId=1216&Locale=nb-NO&OlsonTimeZoneId=Europe%2fOslo&Permissions=Read%2c+Participate%2c+Evaluate%2c+Modify&ReadOnly=False&Role=Staff&synckey=TheSyncKey&Use12HTimeFormat=False&UserId=2&Version=LatestOrDraft&WindowsTimeZoneId=Central+European+Standard+Time&Timestamp=2014-01-06T11:08:12
And the value is URL-decoded:
https://www.myitslextension.com:3100/ViewRegistration.aspx?Accessibility=False&allowedhtmlcodelevel=LessRestricted&ApiSessionId=rundhq45ase1yne212uqpu55&ContextRole=Learner&CustomerId=1&[email protected]&Encoding=utf8&FirstName=Admin&Language=en-US&LastName=Admin&LearningObjectId=218&LearningObjectInstanceId=1216&Locale=nb-NO&OlsonTimeZoneId=Europe/Oslo&Permissions=Read, Participate, Evaluate, Modify&ReadOnly=False&Role=Staff&synckey=TheSyncKey&Use12HTimeFormat=False&UserId=2&Version=LatestOrDraft&WindowsTimeZoneId=Central European Standard Time&Timestamp=2014-01-06T11:08:12
Finally, the application's shared secret is appended to the string:
https://www.myitslextension.com:3100/ViewRegistration.aspx?Accessibility=False&allowedhtmlcodelevel=LessRestricted&ApiSessionId=rundhq45ase1yne212uqpu55&ContextRole=Learner&CustomerId=1&[email protected]&Encoding=utf8&FirstName=Admin&Language=en-US&LastName=Admin&LearningObjectId=218&LearningObjectInstanceId=1216&Locale=nb-NO&OlsonTimeZoneId=Europe/Oslo&Permissions=Read, Participate, Evaluate, Modify&ReadOnly=False&Role=Staff&synckey=TheSyncKey&Use12HTimeFormat=False&UserId=2&Version=LatestOrDraft&WindowsTimeZoneId=Central European Standard Time&Timestamp=2014-01-06T11:08:1296002924-z56a-2369-z99z-5afdtt413ad9
The rest of the process is the same as for plugins and modules.
Sample code
To simplify the entire process for you, we have provided the code used to generate this signature below:
Below is example code for C#:
/* Below are the methods that compute the MD5 hash used in the signature (fromItslearning.Platform.RestApi.Sdk.Common.CryptographyHelper ).
Notice "encoding.GetBytes(input)". This interprets 'input' as the given encoding before getting the bytes representing the string. For ASCII characters the bytes are the same for UTF-8 and latin-1/Windows Codepage 1252 which are common encodings to use. When input contains non-ascii characters however different encodings will yield different bytes and thus different hashes which in turn will fail the signature validation. Again the encoding for validating the signature is UTF-8.
*/
public static string ComputeHash(string input)
{
return ComputeHash(input, Encoding.UTF8);
}
[Obsolete("Use ComputeHash(string) for UTF-8")]
public static string ComputeHash(string input, Encoding encoding)
{
var md5Hasher = new MD5CryptoServiceProvider();
byte[] data = md5Hasher.ComputeHash(encoding.GetBytes(input));
var sb = new StringBuilder();
// Loop through each byte of the hashed data and format each one as a two digit hexadecimal string.
for (int i = 0; i < data.Length; i++)
{
sb.Append(data[i].ToString("x2"));
}
return sb.ToString();
}
You need to interpret the characters in the URL as UTF-8 before verifying the signature (with SSOv2). This does not apply to legacy extensions using SSOv1
If you don't do this the signature will fail if non-ASCII characters are sent in the query string to your plugin.
E.g. for people with Norwegian special characters "æøå" in their names.
Troubleshooting
Manage menus
Page Administration > Manage apps > Manage menus > Add module
Problem |
Possible reason |
Administration > Manage apps > Manage menus tab is not visible |
|
I have added a Module to the Developer portal, but I cannot see the Module when I browse the app library. |
|
I have added a Module the the Manage apps > Add app page, but the Module does not appear in the location I have specified. |
|
Launching Modules
Problem |
Possible reason |
I get error message 'You are not authorized to view this page' when I try to launch a Module. |
|
Default availability for extensions
Page Administration > Manage apps > Default availability> Add application/Add plugin
Problem |
Possible reason |
Administration > Manage apps > Default availability tab is not visible |
|
I have added an application/plugin to the Developer portal, but I cannot see the application/plugin when I browse the app library. |
|
I have added an application to the Manage apps > Default availability >Add application, but the application does not appear in the location I have specified. |
|
I have added an plugin to the Manage apps > Default availability >Add plugin, but the plugin does not appear in the location I have specified. |
|
Launching Applications/Plugins
Problem |
Possible reason |
I get error message 'You are not authorized to view this page' when I try to launch a Module. |
|