Before you start
You need to have this information:
- Your Application's Id-Key pair (let's say 'appID' and 'appKey', but they won't look like that)
- The domain for your back-end service (which LMS are you calling)
- The callback point for your app/service where the LMS can send User Id-Key pairs when you request them
Creating the Service Account & Harvesting User Tokens
Your app will need to run with a Service Account, or other Account with sufficient privileges to perform the actions you need to take with the APIs. So, the first step is to create an account in the Learning Environment with sufficient privileges to make the API calls you need to make.
Once you create a Service Account:
- You need to manually harvest the user tokens using a utility such as the API Test Tool () to authenticate with your LMS.
- Store those keys securely, and configure your LMS to ensure the user tokens are long-lived. Many systems have a token timeout of 30 days, but when a headless integration is in place like the one you're proposing, it's often a good idea to make the timeout infinite. You can contact Desire2Learn Support to verify the timeout value for the user tokens.
Note: the "user tokens" above does not mean the timeout set via the d2l.SessionTimeout server setting. Rather, this is set via the d2l.Security.Api.TokenTimeout setting.
To clarify further:
- The d2l.SessionTimeout config variable refers to the timeout period of the web session (i.e. how long someone can be idle before the LMS web UI times out and prompts the user to log back in). This timeout is often set at an interval of several minutes to an hour.
- The d2l.Security.Api.TokenTimeout config variable refers to the timeout period of the user id\key pair associated with the Valence authentication process. Put another way, these tokens are the x_b value and the basis for the x_d value that are sent as query parameters on every API call to identify the user. This timeout is often set at an interval of several days, or could be set to never timeout. In the latter case, the user id\key pair can only be invalidated by changing the user's password or explicitly revoking app access. Once the user id\key pair becomes invalid, the app will have to re-initiate the authentication process to receive a new user id\key pair to sign API calls.
Build an app context
- Construct an App context with D2LAppContext( "appID", "appKey", ""); this fashions an app context that you can then use to make the request to fetch a User Id-Key pair from the LMS
- authURL = DAC.createWebUrlForAuthentication( new URI("")); this makes the "authURL", you need to direct a web-container (the user's browser) to this URL: that requests the LMS send back User Id-Key pair for the user session that will get established inside the requesting web container.
The LMS will send the User Id-Key pair by redirecting back to with the User Id-Key pair attached as query parameters on the redirect URL. Let's call this full redirect URL 'fullRedirectUrl'.
Build a user context
- You can either build a user context by using fullRedirectUrl: DAC.createUserContext( fullRedirectUrl )
- OR, you can extract the User Id-Key pair from fullRedirectUrl (let's say 'userID' and 'userKey', but they'll look different), and use: DAC.createUserContext( "userID", "userKey" )
Make calls
- You can now use your user context, DUC, to make calls: DUC.createAuthenticatedUri( "/d2l/api/lp/1.3/users/whoami", "GET")
Note: that here you provide the API route only, plus the HTTP method (uppercase); make sure you use a route in an API contract that your back-end LMS supports -- here we make the whoami call from the v1.3 API contract, but your LMS may not support that contract (or it may support a more recent one).
Troubleshooting
Question: To be clear, I'm taking the "...or" route mentioned above, by extracting the userID and userKey values from apitesttool. desire2learnvalence.com, and then doing createUserContext using those values (along with the D2L host name, port, etc --- the 6-parameter version of that function).
I get a context back from that method and when I use it to make an API call, the service returns a 403 "Invalid token."
Answer: If your App were not known to the LMS (i.e. not an enabled App ID) you would get back a slightly different error. This tells us that either you're not making a valid App Sig in your eventual API call (which likely means your App Key is not right for your App ID), or the User Id-Key you're using is somehow not useful.
We highly recommend that you step through the walk through on the Python client SDK docs page even if you don't intend to use Python in the long term. You can go through this process interactively and step by step, and thus have more direct control over what's going on at each step in the process to see what's going on. With an enterprise app built out of C# or Java, you're down to standard debugging techniques to feel your way through the workflow -- which also work fine: however an interactive shell lets you execute each step as you go along and inspect the results.
To get started with the python SDK as in that walkthrough, you need:
- To have a Python install, and it helps to have the 'pip' package installed to do python module install
- To have the 'requests' python module installed (which pip should find automagically)
- To have the 'd2lvalence' python module installed (which pip should find automagically)
Question: I went to look at the Python auth page you referenced, but I'm confused. That page advises me to go to the browser and catch the redirect URL after getting the result back from createUrlForAuthentication (using the php format for the function).
Answer:
The way the model works at a high level is as a three-legged auth model:
- You have a real user with a browser
- You have a third-party service or application that the user can visit (typically) that wants to make Valence API calls on behalf of that user
- You have the back-end LMS
When the real user visits the 3rd party app, before it can make API calls, it needs to ask the LMS for a User Id-Key pair to go with its App Id-Key pair so it can make API calls on behalf of the user. To do this, it redirects the user's browser to the LMS' "login path" to ensure that the LMS "knows" who the user/browser is:
- If the user's browser already has a web session withe LMS, then the LMS can say "I know who the user is" and send the right User Id-Key pair to the 3rd party app.
- If the user's browser doesn't yet have a session, then the LMS can seek to authenticate the user using the standard way for that LMS; THEN it can send the right User Id-Key pair back to the 3rd party app. (Note that, in both cases, the User Id-Key pair gets generated specifically for THAT user and THAT app: it's not transferable to any other App Id-Key pair.)
This three-legged auth system makes good sense in the case where you really do have three separate parties. In cases like yours, where you want to write a (headless) IT service app that has no real "currently operating user with their web browser", we typically recommend that you create a service account -- a special purpose, "user" and "role" that only gets used for your service application.
In this case, you need to have a way to go through the above auth process every single time you need a valid User Id-Key pair for this service "user". There are a bunch of ways to do that, but they all involve having (at some point) a real user with a real browser stand up and play the part of that third leg in the auth process.
Typically what you do is either use a locally hosted version of one of our simple samples, or our online "API Test Tool" (https://apitesttool. desire2learnvalence.com/), or an interactive Python session with our Python SDK (as in the auth walkthrough I pointed to). Typically, for best security, we'd recommend that you do this process inside a secure environment, so the API Test Tool is perhaps not the best solution, especially if you're not going to connect with HTTPS, because it's sitting out on the internet. However, if you use HTTPS to connect it to your LMS, it's probably sufficiently safe.
For the process of "gathering a User Id-Key pair" you need to use your OWN App Id-Key credentials (that is, the pair you have received for the app/service you are writing).
If you're doing it interactively, you create an app context with those credentials. If you're using a sample, or the online tool, you provide your own App Id-Key pair as the ones to use (not the default ones in the form: those are a single set of public keys for demo purposes). You have to provide the host name of your LMS, and the port to call on. Then you go through the "auth process":
- If you're using an interactive session with the Python SDK, you create an "authentication URL" that you'll cut and paste into your browser... in order to do this, you also need to provide a "callback URL" telling the LMS where to send the User Id-Key -- we suggest localhost because that will go back to the computer where your browser is, and likely your browser will complain it can't find the URL, but you can then copy the completed URL out of the address bar.
- If you're using the sample or online tool, the "callback URL" is hidden to you -- the form provides it automatically: the User Id-Key pair will get sent back to the web server running the sample/online tool.
When passing back the User Id-Key pair, the LMS will put them as query parameters onto that "callback URL" you provided in the request you just sent.
- If you're using the online tool or a sample, the tool/sample should automagically decompose this callback URL, peel out the credentials and show them to you.
- If you're using an interactive Python session, the LMS will redirect to "localhost" and tack on the User Id-Key pair in query parameters. Copy the entire URL from the browser's address bar, and feed that back into your Python session to create the "user context" -- this will peel the User Id-Keys out of that result URI, and create a user context for you that you can use to interactively make API calls... you can also inspect it to see what the User Id-Key pair are, save them to disk, and so forth.
In any case, you have not successfully "gathered a User Id-Key pair" for YOUR service account to go with YOUR App Id-Key. You can cache that in a safe place accessible to your admin script/app (like in a configuration file). We highly recommend that you treat your app's App Id-Key pair and all User Id-Keys that go with it as highly sensitive information -- with those tokens ANYONE can make a call to your LMS pretending to be your App as a valid user, so you wan to put them in a safe place visible only to a small number of people and to the script/app that needs to use them.
Whenever the password for this service account changes you will need to go through this process again to gather a new User Id-Key pair. If your LMS has API tokens set to expire in a fixed time, then you'll also need to re-do this process when that time elapses. LMSes by default have this expiry time set to "indefinite" and the User Id-Key tokens expire only when the user's password changes, or the site admin denies access to third party apps for that user.
For another point of explanation, you can see this topic on our developer blog: Getting Started with App Id-Key Authentication. It's several years old, but the material still holds true, and the diagrams are different to the ones that appear in our online docs, so that contrast can help get a rounder picture.https://d2l.someUniversity.eduhttps://yourServer.org/authCallback
https://apitesttool.desire2learnvalence.com/
https://d2l.someUniversity.eduhttps://yourServer.org/authCallbackhttps://yourServer.org/authCallback