Product

We run the protocol. You run your product.

LTI 1.3 is five network hops, three signature checks, and a set of replay defenses that only fail in production. LearnPort implements all of it and hands you a JSON object.

The handshake

What happens between the LMS and your callback

This is the flow we own. The only part you implement is step 5.

  1. LMS → LearnPort

    OIDC login

    The LMS sends issuer, client ID, and login hint. We verify the registration and resolve the destination from your allowlist, pinning it into the state.

  2. LearnPort → LMS

    Authorization redirect

    We redirect back to the platform authorization URL with state and nonce. State is stored once and consumed atomically, so it cannot be replayed.

  3. LMS → LearnPort

    id_token verification

    We fetch the platform JWKS, verify the signature, and check nonce, deployment, and required claims. The pinned destination is re-authorized against your current allowlist rather than trusted because it was valid earlier.

  4. LearnPort → your app

    One-time launch token

    Your callback receives a short-lived token. It is stored hashed and redeemable exactly once.

  5. Your app → LearnPort

    Redeem

    One authenticated POST exchanges the token for a normalized user, course context, and role set.

Your side

One endpoint is the whole integration

Redeem the token, get a normalized session, start yours. If you can write an OAuth callback, you can write this.

Your LTI callback
// app/lti/callback/route.ts
import { LearnPort } from "@learnport/sdk";

const learnport = new LearnPort({ apiKey: process.env.LEARNPORT_API_KEY! });

export async function GET(request: Request) {
  const token = new URL(request.url).searchParams.get("launch_token");
  if (!token) return new Response("Missing launch token", { status: 400 });

  const launch = await learnport.lti.launches.redeem(token);

  // launch.user, launch.context, launch.user.roles — your session starts here.
  return createSessionAndRedirect(launch);
}

In the dashboard

The surfaces around the launch

Most of the time an LTI integration costs is not the launch itself. It is registration, admin coordination, and debugging failures you cannot reproduce.

Connect wizards

Canvas-specific and generic LTI 1.3 flows that collect what the platform needs and hand back the login, launch, and JWKS URLs the admin has to paste in.

Install packs

A Markdown document generated from your real environment configuration, written for an LMS administrator rather than for you.

Launch simulator

A fake platform provisioned per environment. Launch as a student or an instructor and get the same handoff token a real LMS produces.

Launch debugger

Every attempt, successful or not, with a stable error code plus the expected and received values. Sensitive diagnostics never leave the dashboard.

Environments

Development and production environments per application, each with isolated API keys and platform registrations.

Typed SDK

@learnport/sdk covers the JSON API with types, and the sample customer app shows the whole integration end to end.

Try it against the simulator first.

You can run a complete LTI 1.3 launch without contacting a single university.