What Is LTI® 1.3?
LTI® 1.3 is the current version of Learning Tools Interoperability, the standard that lets a learning management system hand a user over to an external learning tool and lets that tool read and write data back. It is published by 1EdTech Consortium, Inc., and the normative text is the LTI® 1.3 core specification — dense, authoritative, and the thing to check when a vendor's documentation and reality disagree.
The launch is built on two existing standards rather than inventing its own cryptography: OpenID Connect for the handshake, and JSON Web Tokens for the signed payload.
The problem it solves is combinatorial. A tool used by a hundred institutions running six different platforms would otherwise need bespoke integration work for each one. With LTI®, the tool implements the standard once and any conformant platform can launch it.
This page covers what the standard actually does during a launch. If you want the vocabulary first, the terminology map is a better starting point, because 1.3 renamed several things that older documentation still uses the old names for.
What a launch has to establish
When someone clicks a link to a tool inside their LMS, the tool receives a request from a browser it has never seen before. Before showing anything, it needs to establish four things, and none of them can be taken on trust from the browser:
- Which platform this is. Anyone can send an HTTP request claiming to be a university's LMS.
- Who the user is, and what role they hold in this course.
- What they are opening — which course, and which placement of the tool within it.
- What it is allowed to do afterwards: read the roster, write grades, neither.
LTI® 1.3 answers all four with a signed token. The signature is what makes the claims trustworthy, and obtaining it is what the handshake is for.
The launch sequence
A launch is five steps. The user sees one page change.
1. The platform starts a login
The platform does not send the payload first. It sends a small request to the tool's login initiation URL saying only that a launch is starting:
POST https://tool.example.com/lti/login
Content-Type: application/x-www-form-urlencoded
iss=https://lms.example.edu
&login_hint=1a2b3c
&target_link_uri=https://tool.example.com/launch
<i_message_hint=9f8e7d
&client_id=1000000000042
<i_deployment_id=42:abcdef
iss identifies the platform and client_id identifies this tool's
registration with it. Together they are how the tool works out which of its
registrations is being used. login_hint and lti_message_hint are opaque
values the platform will want back.
This step comes from OpenID Connect, where it is called third-party initiated login. It exists so the tool can create state before any data arrives.
2. The tool asks for the token
The tool replies by redirecting the browser to the platform's authentication endpoint, echoing the hints back and adding two values of its own:
GET https://lms.example.edu/api/lti/authorize_redirect
?scope=openid
&response_type=id_token
&response_mode=form_post
&prompt=none
&client_id=1000000000042
&redirect_uri=https://tool.example.com/launch
&login_hint=1a2b3c
<i_message_hint=9f8e7d
&state=<tool generated>
&nonce=<tool generated>
state is how the tool recognises the response as belonging to the request it
made. nonce is single-use, and rejecting a repeat is what stops a captured
launch being replayed later.
redirect_uri must exactly match one the platform has registered. A trailing
slash present in one place and absent in the other is enough to fail here, and
it is one of the most common setup problems.
3. The platform posts the ID Token
The platform posts back to the tool's redirect URI:
POST https://tool.example.com/launch
Content-Type: application/x-www-form-urlencoded
id_token=eyJhbGciOiJSUzI1NiIsImtpZCI6IjIwMjYtMDgifQ...
&state=<the value from step 2>
The id_token is a JWT. Decoded, its payload carries the claims the tool needs:
{
"iss": "https://lms.example.edu",
"aud": "1000000000042",
"sub": "9a8b7c6d-user-identifier",
"nonce": "<the value from step 2>",
"exp": 1786531200,
"https://purl.imsglobal.org/spec/lti/claim/message_type": "LtiResourceLinkRequest",
"https://purl.imsglobal.org/spec/lti/claim/version": "1.3.0",
"https://purl.imsglobal.org/spec/lti/claim/deployment_id": "42:abcdef",
"https://purl.imsglobal.org/spec/lti/claim/target_link_uri": "https://tool.example.com/launch",
"https://purl.imsglobal.org/spec/lti/claim/roles": [
"http://purl.imsglobal.org/vocab/lis/v2/membership#Learner"
],
"https://purl.imsglobal.org/spec/lti/claim/context": {
"id": "course-2026-cs101",
"label": "CS101",
"title": "Computer Science 101"
},
"https://purl.imsglobal.org/spec/lti/claim/resource_link": {
"id": "placement-88",
"title": "Week 3 exercise"
}
}
The long claim names are not decoration. They are namespaced URIs so that LTI® claims cannot collide with anything else in a JWT.
4. The tool verifies it
This is the step that makes the rest trustworthy, and it is where an implementation is most likely to be quietly wrong. The tool must check:
- The signature, against the platform's key set. The token's
header names a
kid; the key with that identifier is fetched from the platform's published JWKS URL. issandaudmatch the registration it thinks this is.exphas not passed.noncehas not been seen before.statematches the value it sent in step 2.- The message type and version claims are the ones it supports.
Skipping the nonce check leaves the launch replayable. Skipping the aud check
means a token minted for a different tool will be accepted.
5. The tool renders
Only now does the tool know who it is dealing with. It maps the platform
identifier plus sub onto a user in its own database, creates a session, and
renders the right view for the role and the resource link.
A tool should key its users on the platform and the subject together. sub is
unique within a platform, not globally, so two platforms can legitimately issue
the same one.
What happens after the launch
The launch establishes identity. Three optional services do the rest, and together with the core they make up LTI® Advantage:
| Service | What it does | Direction |
|---|---|---|
| Names and Role Provisioning | Read the roster of the course and each member's role | Tool reads from platform |
| Assignment and Grade Services | Create gradebook columns and post scores | Tool writes to platform |
| Deep Linking | Let a teacher browse the tool and choose content to embed | Tool returns a selection |
These calls are server to server and authenticated with OAuth 2.0 client credentials rather than the launch token: the tool signs a JWT assertion with its own key, exchanges it for an access token at the platform's token endpoint, and calls the service with that.
Availability is granted per course. A tool can have grade access in one course and not another, so it should check what the ID Token says is available for this launch rather than assuming.
What this costs to implement
Every step above is work: an OIDC login endpoint, JWKS fetching with caching and key rotation, JWT verification, nonce storage, state handling, an OAuth 2.0 client credentials flow, and a token cache. Then the same again per service, and then the differences between how each platform interprets the specification.
That is the case for not implementing it yourself. LTIAAS performs the handshake and hands your application the verified result:
// After LTIAAS has completed the launch it redirects to your URL with an ltik.
const headers = { Authorization: `LTIK-AUTH-V2 ${API_KEY}:${ltik}` }
const idtoken = await requests.get('https://your.ltiaas.com/api/idtoken', { headers })
const userId = idtoken.user.id
const courseId = idtoken.launch.context.id
const roles = idtoken.user.roles
There is no JWT in that code, and no key handling, because the verification has already happened.
Common questions
Is LTI® 1.3 the same as LTI® Advantage?
Not quite. LTI® 1.3 is the core specification that defines the launch. LTI® Advantage is that core plus three services — Names and Role Provisioning, Assignment and Grade Services, and Deep Linking. A platform can support the core without offering all three.
Does LTI® 1.3 use OAuth?
It uses OAuth 2.0 client credentials for service calls, and OpenID Connect for the launch itself. It does not use the OAuth 1.0a request signing that LTI® 1.1 relied on, which is the part most older documentation describes.
Do I need a certificate to use LTI® 1.3?
No. Each side publishes a JSON Web Key Set at a URL and signs with the corresponding private key. There is no certificate authority involved and no certificate to buy.
Can one integration work with every LMS?
The protocol is the same everywhere, so the code you write is the same. What differs is registration — where the values are entered in each LMS admin console, and which services that platform grants.
Next
- Tool, platform, provider, consumer — the vocabulary, and what 1.3 renamed
- LTI® 1.3 vs LTI® 1.1 — what changed and why
- How to add LTI® 1.3 to your application
