title: LMS & SSO description: Sign in with Google Workspace or Microsoft 365, restrict a school’s join codes to its own email domains, import a class roster from CSV, and connect an LMS over LTI 1.3.
LMS & SSO
Four ways to get a class into de:volt without typing thirty passwords. The first three are on every education plan; LTI 1.3 is Education Plus and site licences only.
Signing in with a school account
de:volt has no passwords at all. A student or teacher signs in one of three ways, and all three land on the same account if they share an email address:
| Method | Best for |
|---|---|
| Continue with Google | Google Workspace for Education schools |
| Continue with Microsoft | Microsoft 365 / Entra ID schools |
| Email sign-in link | Everyone else, and as a fallback |
Nothing needs configuring on your side for either provider: de:volt is
registered as a multi-tenant app. Some tenants do require an administrator to
approve a new app once before students may use it, which is the usual cause of
a Need admin approval or AADSTS65004 message on the Microsoft button.
The exact steps for an IT administrator — including the OAuth redirect URI to
register if your tenant prefers its own app registration, and the scopes
de:volt asks for (openid, email, profile, and nothing else) — are on the
school access page.
The email sign-in link always works, with no administrator involvement. If a provider button is blocked on the morning of a lesson, use the link and sort the approval out afterwards.
Restricting sign-in to your school’s domains
A class code is six characters and usually ends up on a whiteboard, so treat it as public. A school owner can require that anyone joining a class signs in with an address on one of the school’s own domains.
On any class page, under Allowed sign-in domains, enter the domains one per line:
mahanamacollege.lk
students.mahanamacollege.lkThen:
- The rule applies to every class in the school, not just the one you set it from.
- Only a school owner can change it. Teachers see the current value.
- Students who have already joined keep their place. The rule is checked when someone joins, not continuously.
- A student on the wrong address is told “Sign in with your school email address to join this class”, which is usually a student who used a personal Google account by habit.
- Leaving the field empty allows any address, which is the default.
Enter only the part after the @. A leading @ or an https:// prefix is
stripped for you; anything that is not a domain name is refused rather than
saved, because a half-typed rule would lock the school out of its own classes.
Importing a roster from CSV
On a class page, Import a roster takes a CSV export from your existing system and enrols those students the first time each one signs in. No code, no link to share.
File format
One student per line: email address and name. A header row is optional, and the column order is worked out for you.
email,name
ada@school.lk,Ada Lovelace
lin@school.lk,"Chen, Lin"What the importer handles, because real exports do all of it: a UTF-8 BOM,
Windows line endings, comma / semicolon / tab delimiters, quoted names
containing commas, blank lines, and header wording such as Email Address or
Display Name. Up to 500 students per import — split a larger file.
Lines that cannot be read are counted and left out; the rest still import, and the result says how many of each. A student already on the roster, or already invited to this class, is skipped, so re-uploading a corrected file is safe.
The name from the CSV is used only to fill in a student’s display name when they do not have one, so the roster and gradebook read as names from day one. A student who later sets their own name keeps it.
Seats
An invitation does not use a seat. A seat is used when the student signs in and joins.
That means you can import next term’s roster now without paying for students who have not arrived. It also means an import can silently run out of room: if the school’s seats are full when a student signs in, they are not enrolled and no error reaches them. The class page shows seats used against seats licensed — check it after a large import.
Two further points worth knowing:
- A student in several classes in one school uses one seat, not one per class.
- A class must have consent attested before anyone can be enrolled into it, by roster import or by code. See Teaching.
Pending invitations are listed under the roster as Invited, not yet signed in, with a Remove control, and expire 90 days after the import.
The school’s allowed sign-in domains are deliberately not applied to a roster import: an address the teacher typed out is a stronger statement of intent than the blanket rule, which exists because join codes leak.
LTI 1.3 Advantage (Education Plus)
LTI 1.3 Advantage lets a school launch a de:volt lesson from inside Moodle, Canvas or Blackboard, sync the class roster from the LMS, and send assignment scores back to the LMS gradebook. It is an Education Plus and site licence feature: a launch into a school on Standard is refused with a message naming Education Plus, and so is a launch into a school whose licence has lapsed. Standard schools use the roster import above.
de:volt is the tool; your LMS is the platform. Registering means each side holds the other’s details, and there is no shared secret in either direction — de:volt signs with a private key whose public half your LMS fetches from the keyset URL below.
Registration
Give your LMS:
| Field | Value |
|---|---|
| OIDC login initiation URL | https://learn.devoltapp.com/api/lti/login |
| Redirect / target link URI | https://learn.devoltapp.com/api/lti/launch |
| Public keyset (JWKS) URL | https://learn.devoltapp.com/api/lti/jwks |
Request the NRPS (names and role provisioning) and AGS (assignment and
grade services) scopes when you add the tool, and attach a line item to the
LMS activity you want scores to land in.
Give de:volt:
| Field | Where it comes from |
|---|---|
Issuer (iss) | Your LMS’s platform ID, e.g. https://moodle.school.lk |
| Client ID | Issued by the LMS when you add the tool |
| Deployment ID | Issued by the LMS per deployment |
| OIDC authentication endpoint | LMS login/auth URL |
| Access token endpoint | LMS OAuth2 token URL |
| Public keyset (JWKS) URL | LMS JWKS URL |
A de:volt operator records those as one row per deployment, under the service role (the table is not writable from the app):
insert into public.learn_lti_platform
(org_id, issuer, client_id, deployment_id,
auth_login_url, auth_token_url, jwks_url)
values (
'<learn_org id>',
'https://moodle.school.lk',
'<client id from the LMS>',
'<deployment id from the LMS>',
'https://moodle.school.lk/mod/lti/auth.php',
'https://moodle.school.lk/mod/lti/token.php',
'https://moodle.school.lk/mod/lti/certs.php'
);The issuer is matched verbatim, so a trailing slash is a different platform.
One school can hold several rows — the same client deployed twice, or two LMS
instances — and the (issuer, client_id, deployment_id) triple is what a
launch is matched on. An org owner can read their own school’s rows to check
what was recorded.
Binding an activity to a class
The launch token says which LMS activity was clicked. It cannot say which de:volt class that activity means, so the mapping is recorded once, per activity:
insert into public.learn_lti_resource_link
(platform_id, resource_link_id, class_id, module_id)
values (
'<learn_lti_platform id>',
'<resource_link_id from the LMS>',
'<learn_class id>',
null -- or a learn_module id to open one lesson
);class_id is where launched users are enrolled. module_id is optional: set
it and the launch opens that lesson, leave it null and the launch opens the
class page. To find the resource_link_id, launch the activity once — the
refusal names it.
The AGS line item and the NRPS endpoint are filled in by de:volt on the first launch: both appear only inside a launch token, and both are needed later, out of band, when a teacher marks work or runs a roster sync.
What a launch does
- Verifies the LMS’s signature, issuer, audience, deployment and nonce. A replayed launch is refused.
- Checks the school’s licence is Education Plus.
- Finds or creates the de:volt account for the LMS user. The same LMS user always reaches the same de:volt account, even if the school later changes the mailbox; an address that already has a de:volt account is reused rather than duplicated. A platform that releases no email still works — the account is created against an internal address that receives no mail.
- Enrols the user in the bound class as a teacher (LMS
Instructor,TeachingAssistant,ContentDeveloper,MentororAdministrator) or a student (Learner). Teachers consume no seat; a student does, and a launch into a school with no free seats is refused with a message saying so. A student who already holds a seat at the school does not consume a second one. - Signs the user in and opens the class or the bound lesson. There is no de:volt login step.
Roster sync (NRPS)
A teacher launch also pulls the LMS course membership and turns the students in it into the same pending invitations the CSV import creates, so the seat rules above apply unchanged: an invitation costs nothing until the student signs in. Students appear on the class page under Invited, not yet signed in until then, and the class’s consent attestation still gates the enrolment.
Two deliberate limits:
- Instructors are not imported. Adding a co-teacher grants marking rights and stays a decision someone at the school makes.
- Removals are not mirrored. A student the LMS drops keeps their de:volt work and their membership until a teacher removes them, because an LMS enrolment edit mid-term is as often a mistake as an intention.
A member the LMS reports without an email address cannot be matched to a sign-in, and is skipped; those students join with the class code.
Grade pass-back (AGS)
When a teacher saves a mark in de:volt, the total (points awarded against the rubric’s total available) is posted to the LMS line item for that class.
Pass-back is best-effort and silent: a student who joined by code rather than by launch, an activity with no line item, a rubric worth zero points, or an LMS that is down all mean no score is sent. The mark itself is already saved in de:volt and stands regardless, and nothing a teacher does waits more than five seconds on the LMS.
Testing a registration
There is no Moodle instance in de:volt’s CI — an LTI platform is too large a dependency to stand up per commit, so the protocol logic is unit tested against a locally generated key pair and the end-to-end path is verified by hand against a real LMS. Run this checklist once per registration:
- Fetch
https://learn.devoltapp.com/api/lti/jwksand confirm it returns a key. (A 503 means the deployment has no tool key configured yet — stop here and tell de:volt.) - Register the tool in Moodle (Site administration → Plugins → External tool → Manage tools) using the three de:volt URLs above, with the NRPS and AGS scopes.
- Send de:volt the issuer, client ID, deployment ID and the three LMS URLs, and confirm the registration is recorded against your school.
- Add an External tool activity to a course, point it at de:volt, and launch it once. Expect Activity not linked naming a resource link ID; send that ID and the class it should open to de:volt.
- Launch as a teacher. Expect to land on the mapped de:volt class or lesson, already signed in, with no de:volt login step.
- Launch as a student in the same course. Expect the same destination and a de:volt account created from the LMS email.
- Launch again as the same student. Expect the same de:volt account, not a second one.
- Check the class page after the teacher launch in step 5: every LMS student appears under Invited, not yet signed in or as a member.
- Add a line item to the activity, mark that student’s submission in de:volt, and check the score in the Moodle gradebook.
- Launch from a course whose school is on Standard. Expect a refusal naming Education Plus.
- Remove a student in the LMS, re-launch as the teacher, and confirm de:volt does not enrol them again.
A launch that stops working at the start of a term is almost always an LMS-side key rotation — de:volt reads your keyset live, so nothing needs re-registering, but the message says “could not verify the launch” and step 3 is where to re-check. A launch that fails with Launch expired on every attempt is usually a browser blocking cookies for embedded content: open the activity in a new tab instead of the LMS’s iframe.
Keep the results of steps 5-10 with the registration.