LTI Launch Endpoints
These endpoints are called by the LMS during an LTI 1.3 launch. They are public endpoints and do not require an API key. The LMS redirects the student's browser to these URLs as part of the OIDC third-party login flow.
For a full walkthrough of the launch process, see the LTI Integration Guide.
GET /v1/platform/lti/login
OIDC login initiation endpoint. The LMS redirects the student's browser here to start
the LTI launch. TutorFlow validates the parameters, generates a state and nonce,
and redirects the browser back to the LMS authorization endpoint.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
iss | string | Yes | Issuer identifier of the LMS (must match a registered platformIssuer) |
login_hint | string | Yes | Opaque value from the LMS identifying the user |
target_link_uri | string | Yes | The URL the LMS wants to launch (TutorFlow's launch URL) |
lti_message_hint | string | No | Opaque value from the LMS passed through to the authorization request |
client_id | string | No | The client ID of the tool registration. Required when the same issuer has multiple registrations. |
Example
The LMS constructs a URL like this and redirects the student's browser to it:
https://api.tutorflow.io/v1/platform/lti/login
?iss=https://canvas.instructure.com
&login_hint=user-id-12345
&target_link_uri=https://api.tutorflow.io/v1/platform/lti/launch
<i_message_hint=assignment-context-hint
&client_id=10000000000001Response
This endpoint does not return a JSON body. It responds with an HTTP 302 redirect to the
LMS authorization endpoint. The redirect URL includes the following query parameters:
| Parameter | Description |
|---|---|
scope | Always openid |
response_type | Always id_token |
response_mode | Always form_post |
client_id | The registered client ID |
redirect_uri | TutorFlow's launch URL |
state | CSRF protection token (stored server-side) |
nonce | Replay protection token |
login_hint | Passed through from the original request |
lti_message_hint | Passed through from the original request (if provided) |
prompt | Always none (SSO, no re-authentication) |
Error Responses
| Status | Cause |
|---|---|
400 | Missing required parameters (iss, login_hint, or target_link_uri) |
400 | No LTI registration found for the given iss and client_id |
POST /v1/platform/lti/launch
Launch callback endpoint. After the LMS authenticates the user, it POSTs a form to this
URL containing the signed id_token (JWT) and the state value. TutorFlow validates
the JWT signature, checks the state and nonce, extracts LTI claims, and creates
a launch session.
Request Body (form-encoded)
| Field | Type | Required | Description |
|---|---|---|---|
id_token | string | Yes | The signed JWT from the LMS containing LTI launch claims |
state | string | Yes | The CSRF state token generated during the login step |
Content Type
Content-Type: application/x-www-form-urlencodedExample
The LMS auto-submits a form to this endpoint:
<form method="POST" action="https://api.tutorflow.io/v1/platform/lti/launch">
<input type="hidden" name="id_token" value="eyJhbGciOiJSUzI1NiIs..." />
<input type="hidden" name="state" value="csrf-state-token" />
</form>JWT Claims
The id_token JWT contains standard LTI 1.3 claims:
| Claim | Description |
|---|---|
iss | LMS issuer |
sub | User identifier in the LMS |
aud | Client ID (must match the registration) |
nonce | Must match the nonce from the login step |
https://purl.imsglobal.org/spec/lti/claim/message_type | LtiResourceLinkRequest or LtiDeepLinkingRequest |
https://purl.imsglobal.org/spec/lti/claim/version | Always 1.3.0 |
https://purl.imsglobal.org/spec/lti/claim/roles | Array of LTI role URIs |
https://purl.imsglobal.org/spec/lti/claim/resource_link | Resource link details (id, title) |
https://purl.imsglobal.org/spec/lti-ags/claim/endpoint | AGS endpoint for grade passback (if available) |
Response
On success, TutorFlow creates an LTI launch session and redirects the student's browser
to the tool UI. The response is an HTTP 302 redirect (or an HTML page rendered inside the
LMS iframe).
Validation Steps
TutorFlow performs the following validation on every launch:
- State check: The
stateparameter must match a valid, unexpired login session. - JWT signature: The
id_tokenis verified against the LMS JWKS (fetched from the registeredplatformJwksUrl). - Issuer check: The
issclaim must match theplatformIssuerin the registration. - Audience check: The
audclaim must include the registeredclientId. - Nonce check: The
noncemust match the value generated during the login step. - Expiry check: The token must not be expired (
expclaim). - Message type: Must be a supported LTI message type.
Error Responses
| Status | Cause |
|---|---|
400 | Missing id_token or state |
400 | Invalid or expired state token |
401 | JWT signature verification failed |
401 | Issuer, audience, or nonce mismatch |
401 | Token expired |
GET /v1/platform/lti/.well-known/jwks.json
Returns TutorFlow's public JSON Web Key Set (JWKS). The LMS uses this endpoint to retrieve the public keys needed to verify JWTs signed by TutorFlow (used during deep linking responses and other tool-initiated messages).
Authentication
None. This is a public endpoint.
Example Request
curl https://api.tutorflow.io/v1/platform/lti/.well-known/jwks.jsonResponse
{
"keys": [
{
"kty": "RSA",
"kid": "tutorflow-lti-key-1",
"use": "sig",
"alg": "RS256",
"n": "0vx7agoebGcQSuu...",
"e": "AQAB"
}
]
}Response Fields
| Field | Type | Description |
|---|---|---|
keys | array | Array of JWK objects |
keys[].kty | string | Key type (always RSA) |
keys[].kid | string | Key ID used to match the kid header in JWTs |
keys[].use | string | Key usage (always sig for signature verification) |
keys[].alg | string | Algorithm (always RS256) |
keys[].n | string | RSA modulus (Base64url-encoded) |
keys[].e | string | RSA exponent (Base64url-encoded) |
Caching
This endpoint supports HTTP caching. The response includes Cache-Control headers.
Keys may be rotated periodically. LMS platforms should re-fetch the JWKS if JWT
verification fails with a kid mismatch.