# Research: Bringing Instructors into SolidProfessor from an LMS

**Status:** Research — not an implementation specification  
**Last reviewed:** August 5, 2026  
**Scope:** LTI 1.3 launches from Canvas and Schoology, current SolidProfessor backend behavior, and roster-based provisioning options.

## Executive Decision Guide

| Goal | Best-fit mechanism | What it does |
|---|---|---|
| Let one instructor enter when they click from the LMS | LTI 1.3 launch with just-in-time provisioning | Identifies the current person, course, school, and role |
| Let one instructor enter without sharing their email | Pseudonymous LTI launch | Uses the stable signed LTI subject instead of an email address |
| Read everyone enrolled in one LMS course | NRPS | Returns the membership and roles for that course or context |
| Provision an entire school or district | OneRoster, SIS sync, or an LMS administration API | Synchronizes organizations, schools, courses, users, and enrollments |
| Sign an existing user into an application | SSO | Authenticates the person but does not provide a course roster |

NRPS is part of the LTI Advantage family, not a replacement for LTI. It is course-level rostering. It should not be treated as the institution-wide provisioning solution for an entire school or district.

## Plain-English Answer

Yes. The same `user.id` variable can identify both students and instructors.

The LMS sends separate information for separate questions:

| Question | Information used |
|---|---|
| Who is this person? | The user identifier, normally the signed LTI `sub` claim |
| Which school should receive them? | SolidProfessor's custom `lms_school_id` parameter |
| Are they a student or instructor here? | The signed LTI `roles` claim |
| Which course launched the tool? | The standard LTI `context.id` claim |
| What is their name or email? | Optional, privacy-dependent identity claims |

An instructor does not need a special instructor ID. The user ID identifies the person. Their role identifies what they are allowed to do in the course.

This distinction matters because the same person can be an instructor in one course and a learner in another course.

## Direct Answers to the Original Questions

### Is there 1EdTech documentation for this?

Yes. IMS Global is now called **1EdTech**. The relevant standards are:

- [LTI 1.3 Core — Users and Roles](https://www.imsglobal.org/spec/lti/v1p3#users-and-roles)
- [LTI 1.3 Core — User Identity Claims](https://www.imsglobal.org/spec/lti/v1p3#user-identity-claims)
- [LTI 1.3 Core — Roles Claim](https://www.imsglobal.org/spec/lti/v1p3#roles-claim)
- [LTI Deep Linking 2.0](https://www.imsglobal.org/spec/lti-dl/v2p0/)
- [Names and Role Provisioning Services 2.0](https://www.imsglobal.org/spec/lti-nrps/v2p0/)

### Would an instructor launch contain the same information as a student launch?

Mostly yes.

Both launches can contain:

- A stable user identifier
- The LMS issuer and deployment information
- The course or context
- The school mapping custom parameter
- Optional name and email, depending on privacy settings

The important difference is the `roles` claim. A student normally has a `Learner` role. An instructor normally has an `Instructor` role. Teaching assistants and administrators can have their own role values.

### Do Canvas or Schoology need another custom parameter for instructors?

No additional custom user identifier should be required. The existing `user_id` substitution identifies whichever logged-in person launches the tool.

Canvas:

```text
user_id=$Canvas.user.id
lms_school_id=$Canvas.account.id
```

Schoology:

```text
user_id=$User.id
lms_school_id=$com.schoology.Context.Building.id
```

The application should read the signed LTI `roles` claim to determine whether the identified person is an instructor. A custom `is_instructor` parameter should not be necessary.

Common context-role values include:

```text
http://purl.imsglobal.org/vocab/lis/v2/membership#Learner
http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor
http://purl.imsglobal.org/vocab/lis/v2/membership#TeachingAssistant
```

## Can an Instructor Enter Without an Email or Pre-Provisioned Account?

Yes, in principle. LTI can support just-in-time, pseudonymous instructor onboarding without first provisioning an entire institution and without receiving the instructor's email address.

The LMS would still need to send:

- A stable, opaque signed `sub` value so SolidProfessor can recognize the same person later
- A signed `Instructor` role for the current context
- The school and course context needed to scope access

SolidProfessor could create an LMS-only account tied to that stable identifier. The instructor would not need a SolidProfessor password or an email-based login. This is pseudonymous rather than completely anonymous: without any stable identifier, the platform could not recognize the instructor on their next launch or safely retain their work.

This flow does not require NRPS. NRPS becomes useful when SolidProfessor needs the rest of the course roster before those people individually launch.

The current SolidProfessor backend does not yet provide this instructor flow. It assigns newly provisioned LTI users the Student role, and the existing instructor Deep Linking path expects an existing SolidProfessor account to be linked.

## LTI, SSO, NRPS, and Institution Provisioning

- **SSO answers:** Who is signing in?
- **Standard LTI launch answers:** Who is launching, from which course or school context, and with which role?
- **NRPS answers:** Who else belongs to this particular course or context, and what are their roles?
- **OneRoster, SIS sync, or an LMS administration API answers:** Which schools, courses, users, and enrollments belong to the larger institution?

For one-at-a-time instructor onboarding, standard LTI is enough if SolidProfessor implements trusted role mapping and just-in-time instructor creation. For a complete school or district rollout, institution-level provisioning is the appropriate solution; LTI can remain the login and course-launch experience.

## Simple Mental Model

Think of an LMS launch as showing three cards:

1. **Identity card:** “This is user 12345.”
2. **School card:** “This launch belongs to School 678.”
3. **Role card:** “In this course, user 12345 is an instructor.”

The signed LTI `sub` claim supplies the identity card, the custom `lms_school_id` supplies the school card, `context.id` supplies the course card, and LTI's standard `roles` claim supplies the role card. The vendor-specific `user_id` value duplicates part of the identity information for compatibility and troubleshooting.

The `sub` identifier should be scoped to its LTI issuer (`iss`) when it is stored or matched. A subject value is stable within its issuer; it should not be assumed to be globally unique across unrelated LMS platforms.

## Canvas Findings

Canvas documents `$Canvas.user.id` as returning the Canvas ID of the **launching user**. It is available whenever a logged-in user launches the tool, so it covers both learners and instructors.

Canvas also documents:

- `$Canvas.account.id` as the account ID for the current context.
- `$Canvas.membership.roles` as the current Canvas course membership roles.
- The standard LTI launch as containing user identifiers, contextual data, and custom data in a signed JWT.

The standard LTI `roles` claim should be preferred over adding a new custom role flag.

Sources:

- [Canvas LTI variable substitutions](https://developerdocs.instructure.com/services/canvas/external-tools/lti/file.tools_variable_substitutions)
- [Canvas LTI launch overview](https://developerdocs.instructure.com/services/canvas/external-tools/lti/file.lti_launch_overview)
- [Canvas user provisioning guidance](https://developerdocs.instructure.com/services/canvas/external-tools/lti/file.provisioning)
- [Current SolidProfessor Canvas setup guide](../knowledge-base/articles/lms-integration-reviewed/setting-up-canvas-integration.md)

## Schoology Findings

Schoology documents `$User.id` as the user's unique Schoology ID. It is not limited to students.

Schoology's `$com.schoology.Context.Building.id` is contextual:

- Course launch: course building ID
- Group launch: group building ID
- User launch: the user's primary building ID

That contextual behavior should be considered when mapping a Schoology building to a SolidProfessor school, but it does not require an instructor-specific user parameter.

Sources:

- [Schoology LTI apps and custom parameter variables](https://developers.schoology.com/app-platform/lti-apps/#custom-parameter-variables)
- [Current SolidProfessor Schoology setup guide](../knowledge-base/articles/lms-integration-reviewed/setting-up-schoology-integration.md)

## What the SolidProfessor Backend Does Today

The standard supports distinguishing learners from instructors, but the current backend does not use that distinction when provisioning a new user.

### School matching

The backend requires `custom.lms_school_id` and uses it to find the mapped SolidProfessor school.

- [Backend school lookup](https://github.com/solidprofessorhub/platform-backend/blob/c8799473d5a5826f619e2613eaecbac84b26fb5d/src/app/Academic/Lti/Launch/Actions/FindSchoolByLmsId.php#L21-L34)

```php
if (! $id = Arr::get($parameters, 'lms_school_id')) {
    throw new MissingCustomParameter('lms_school_id');
}
```

### User matching

The backend does not use the custom `user_id` parameter for matching. It uses the signed LTI `sub` claim.

- [Backend user identifier](https://github.com/solidprofessorhub/platform-backend/blob/c8799473d5a5826f619e2613eaecbac84b26fb5d/src/app/Academic/Lti/Launch/Actions/FindOrCreateUserForLtiContext.php#L61-L88)

```php
$userIdentifier = Str::of($launchData['sub'])->before('::');
```

That makes the custom `user_id` useful for troubleshooting or compatibility, but it is not currently the identifier used by the provisioning action.

There is also a consistency risk in the current identifier handling. Resource launches truncate Schoology-style `sub` values at `::`, while the instructor Deep Linking connection path stores and matches the complete `sub`. The same Schoology user could therefore appear to have different identifiers between the two launch paths.

### New resource-launch users

A newly provisioned user is currently assigned `Student` regardless of the LMS `roles` claim.

- [New-user role assignment](https://github.com/solidprofessorhub/platform-backend/blob/c8799473d5a5826f619e2613eaecbac84b26fb5d/src/app/Academic/Lti/Launch/Actions/FindOrCreateUserForLtiContext.php#L142-L157)

```php
return tap(ProvisionUserToSchool::fromLti()->run(
    $school->account,
    $school,
    $user,
    'Student',
    // ...
));
```

If the person already has a SolidProfessor instructor, district administrator, or super administrator role, that privileged role is preserved.

- [Existing privileged-role handling](https://github.com/solidprofessorhub/platform-backend/blob/c8799473d5a5826f619e2613eaecbac84b26fb5d/src/app/Academic/Lti/Launch/Actions/FindOrCreateUserForLtiContext.php#L88-L107)

### Instructor Deep Linking

The instructor Deep Linking path currently expects to find an existing SolidProfessor school user by LTI ID. If it cannot find one, the instructor is sent through the SolidProfessor login and account-linking flow.

- [Instructor Deep Linking lookup and login fallback](https://github.com/solidprofessorhub/platform-backend/blob/c8799473d5a5826f619e2613eaecbac84b26fb5d/src/app/Academic/Lti/Launch/Actions/DoLtiDeepLinkLaunch.php#L63-L108)
- [Connect the signed-in school user to the LTI subject](https://github.com/solidprofessorhub/platform-backend/blob/c8799473d5a5826f619e2613eaecbac84b26fb5d/src/app/Interfaces/Api/v1/Academic/Lti/Controllers/LtiConnectController.php#L13-L27)

This means the current supported instructor experience is best described as **link an existing SolidProfessor instructor to the LMS**, not **automatically create an instructor from the LMS role**.

### Role claim

The current LTI backend path does not read the incoming LMS `roles` claim. Therefore, recognizing the LMS user ID is not enough to automatically grant SolidProfessor instructor access.

This also means the statement in the existing [LMS Integration FAQ](../knowledge-base/articles/lms-integration-reviewed/lms-integration-faq.md#does-lti-create-user-records-automatically) that the platform assigns “student or instructor based on the LMS role” does not match the current backend implementation. The FAQ should be corrected after the intended product behavior is confirmed.

## If “Bring Instructors In” Means Pre-Provisioning

Launch-time recognition only creates or connects someone after they click the LTI tool.

If the goal is to import instructors before they launch, the relevant 1EdTech standard is **Names and Role Provisioning Services (NRPS)**. NRPS returns course members with, at minimum:

```json
{
  "user_id": "stable-lti-user-id",
  "roles": [
    "http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor"
  ]
}
```

Names, email addresses, and other personal information are optional and depend on the privacy agreement between the LMS and the tool. NRPS can filter a course roster by role, including `Instructor`.

Canvas and Schoology both document NRPS support. However, SolidProfessor currently has only an `accepts_names_roles_services` deployment setting; no backend code was found that calls or processes the NRPS API.

- [Backend deployment setting](https://github.com/solidprofessorhub/platform-backend/blob/c8799473d5a5826f619e2613eaecbac84b26fb5d/src/app/Academic/Lti/Setup/Models/LtiRegistrationDeployment.php#L24-L39)
- [1EdTech NRPS minimum member data](https://www.imsglobal.org/spec/lti-nrps/v2p0/#sharing-of-personal-data)
- [1EdTech NRPS role filter](https://www.imsglobal.org/spec/lti-nrps/v2p0/#role-query-parameter)
- [Canvas NRPS provisioning](https://developerdocs.instructure.com/services/canvas/external-tools/lti/file.provisioning#lti-advantage)
- [Schoology LTI Advantage services](https://developers.schoology.com/app-platform/lti-apps/#lti-advantage-extensions)

## Additional Schoology Deep-Linking Risk to Validate

Schoology states that custom parameters configured at the app or district level are not automatically included in LTI Resource Links created through Deep Linking. The tool needs to include the needed custom values in the returned content item.

The current SolidProfessor Deep Linking response creates each resource with a title, URL, and line item, but does not explicitly include `lms_school_id` or other custom values.

- [Schoology custom parameter behavior](https://developers.schoology.com/app-platform/lti-apps/#lti-advantage-extensions)
- [SolidProfessor Deep Linking resource creation](https://github.com/solidprofessorhub/platform-backend/blob/c8799473d5a5826f619e2613eaecbac84b26fb5d/src/app/Academic/Lti/DeepLinking/Actions/CreateDeepLinks.php#L34-L60)

This is separate from identifying instructors, but it should be tested because `lms_school_id` is required by the backend on later resource launches.

## Practical Product Choices

There are three different possible meanings of “bring instructors into SolidProfessor”:

1. **Recognize an existing instructor when they launch.** This is close to the current flow: use the LTI subject to find them, or ask them to sign in and link their account.
2. **Automatically create an instructor on first launch.** The backend would need to consume and map the signed LTI `roles` claim instead of assigning every new user the Student role.
3. **Import course instructors before they launch.** Implement NRPS roster synchronization and filter members by the Instructor role.

The existing custom user and school parameters do not need to change for option 1. Options 2 and 3 require backend product and security decisions.

## Suggested Response

The same `user.id` variable can identify both students and instructors. It identifies the person who launched the tool; it does not identify their role. LTI sends the role separately in the signed `roles` claim, which tells us whether that person is a learner, instructor, teaching assistant, or administrator in the current course.

That means the existing Canvas and Schoology user and school parameters should be sufficient for identifying an instructor. We should not need an instructor-specific custom parameter. However, the current SolidProfessor backend does not automatically create an instructor from the LMS role. It creates new resource-launch users as students, while the instructor Deep Linking flow expects an existing SolidProfessor instructor to sign in and connect their LMS identity.

If the goal is to import instructors before they launch SolidProfessor, 1EdTech's Names and Role Provisioning Service is the relevant standard, but SolidProfessor does not currently implement that roster-sync service.

## Concise Teams Message

Short answer: the same `user.id` works for both students and instructors. It identifies **who the person is**; the standard LTI `roles` claim tells us **whether they are a student or instructor** in that course. We should not need another custom parameter for instructors.

The current SolidProfessor backend does not yet use the LMS role to auto-create instructors. New LTI resource-launch users are created as students, while instructors currently sign in and link an existing SolidProfessor account. If we want automatic instructor creation or roster import, that would require additional backend work.
