When running a multilingual Moodle site with SAML2 single sign-on via the auth_saml2 plugin, you may need the login button label to appear in different languages based on the user’s interface language. Two approaches that look like they should work, do not:

  • Putting <span class="multilang" lang="en">...</span> tags into the “IdP label override” setting has no effect. The multilang filter only runs on content rendered through Moodle’s format_text() or format_string(). The auth_saml2 plugin passes the label directly from the database to the template without a filter pass.
  • Using the Language customization tool (Site administration > Language > Language customization) to override auth_saml2 language strings will not help for a custom label either. That tool only modifies strings defined in language files, not values stored as admin settings in the database.

The root cause: auth_saml2 stores the IdP label as a plain string in mdl_config_plugins and renders it via {{name}} in the login form template with no language awareness.

The correct approach: theme template override

The reliable solution is to override core/loginform.mustache in your Moodle theme and replace the dynamic {{name}} rendering with Moodle language strings defined in the theme.

Moodle’s {{#str}} Mustache helper always resolves against the current user’s interface language. By moving the label from a database-stored admin setting into theme language files, you get full multilang support through the standard Moodle mechanism.

Prerequisites

Template overrides must live inside a theme — there is no other mechanism in Moodle for overriding Mustache templates. If you are not already on a custom theme, create a minimal child theme before proceeding. Editing a third-party theme directly will get overwritten on the next theme update.

A minimal child theme only needs three files.

theme/yourtheme/config.php:

<?php
defined('MOODLE_INTERNAL') || die();

$THEME->name    = 'yourtheme';
$THEME->parents = ['parenttheme'];
$THEME->sheets  = [];

theme/yourtheme/version.php:

<?php
defined('MOODLE_INTERNAL') || die();

$plugin->component = 'theme_yourtheme';
$plugin->version   = 2024010100;
$plugin->requires  = 2022041900;
$plugin->maturity  = MATURITY_STABLE;

theme/yourtheme/lang/en/theme_yourtheme.php:

<?php
defined('MOODLE_INTERNAL') || die();

$string['pluginname'] = 'Your Theme';

Drop the theme directory into theme/ and visit the Moodle notifications page to register it. You do not need to activate it as the default theme yet — do that once the template override is in place.

Step 1: Override the login form template

Copy lib/templates/core/loginform.mustache from Moodle core into your theme at:

theme/yourtheme/templates/core/loginform.mustache

If your parent theme already overrides this template, copy from the parent theme instead of from core, so you preserve its customizations.

In the template, find the block that renders identity providers. The default Moodle core template renders the button label as:

{{#identityproviders}}
    <a href="{{{url}}}" class="btn btn-secondary">
        {{#iconurl}}
            <img src="{{iconurl}}" alt="" width="24" height="24"/>
        {{/iconurl}}
        {{name}}
    </a>
{{/identityproviders}}

Replace {{name}} with a theme language string reference:

{{#identityproviders}}
    <a href="{{{url}}}" class="btn btn-secondary">
        {{#iconurl}}
            <img src="{{iconurl}}" alt="" width="24" height="24"/>
        {{/iconurl}}
        {{#str}}saml_login_label, theme_yourtheme{{/str}}
    </a>
{{/identityproviders}}

For multi-IdP setups where each provider needs a distinct translated label, the loop approach breaks down because there is no per-provider identifier in the template context. In that case, bypass the generic loop and hard-code one button per provider, each pointing to a known URL and using a dedicated string key. This makes the template more tightly coupled to a specific IdP configuration, but gives you full label control per provider.

Step 2: Add language files

Create a language file for each language your site supports:

theme/yourtheme/lang/en/theme_yourtheme.php
theme/yourtheme/lang/nl/theme_yourtheme.php

Each file defines the same string keys with translated values.

lang/en/theme_yourtheme.php:

<?php
defined('MOODLE_INTERNAL') || die();
$string['saml_login_label'] = 'Log in with your organization account';

lang/nl/theme_yourtheme.php:

<?php
defined('MOODLE_INTERNAL') || die();
$string['saml_login_label'] = 'Inloggen met uw organisatieaccount';

Moodle will automatically use the file matching the user’s current interface language. If no file exists for the user’s language, Moodle falls back to English.

Step 3: Purge caches

After deploying your changes, purge Moodle’s theme and template caches: Site administration > Development > Purge all caches.

Caveats

This approach fully decouples the login button label from the auth_saml2 admin setting. Once you override the template, the “IdP label override” field in the plugin settings has no effect on sites using your theme. You own the label entirely from the theme side.

If you later reconfigure auth_saml2 (add a new IdP, change metadata), you will need to update the template and lang files manually to match. This is a minor maintenance trade-off for a clean, built-in multilang solution.

Active Directory SSO with SAML2 in Moodle requires installing an authentication plugin, exchanging metadata with your AD administrator, and careful mapping of claim types to user fields. This guide covers setup, testing against SAMLtest.id, and troubleshooting common claim mismatches.

Assumptions

This SOP makes the following assumptions:

  • The customer has an AD (Active Directory) based system.
  • They want their users to be able to access Moodle or Totara without logging in (or at the very least they should be able to use their ‘current’ username and password).
  • Their AD system supports SAML2

Make sure to check these assumptions with your users!

Terminology

  • Service Provider (SP): In our scenario, Moodle (or Totara) is the service provider – the application that provides the service the user wants to get access to.
  • Identity Provider (IdP): The customer’s system where the user is authenticated
  • Claims (or ClaimTypes): user attributes (properties of the user, i.e. information about the user)

Install SAML2 Plugin

Moodle does not support SAML2 out of the box (and neither does Totara). You have to install an authentication plugin: SAML2 Single sign on. If you don’t have access to the web server, try to install the plugin through the upload form for plugins: Site administration > Plugins > Install plugins. That should land you on this url: /admin/tool/installaddon/index.php.

Activate the SAML2 Authentication Plugin

Go to Site Administration > Plugins > Manage Authentication. You should end up on this page: /admin/settings.php?section=manageauths.

‘Enable’ the plugin by clicking on the eye icon (or something similar).

From this screen, you can also directly access the SAML2 plugin’s configuration settings by clicking on the Settings link.

Exchange Metadata with the Customer

Get IdP metadata xml or a public xml URL from the Customer

In order to configure the plugin, you need to exchange metadata with your users. Ask them for the IdP metadata xml or a public xml URL. This should be filled out here:

Sometimes your users will tell you what claims they’re providing (user attributes). In my experience, this information may or may not be accurate. Keep in mind that your users’s IdP metadata is authoritative. If a ClaimType isn’t mentioned there, it means it won’t be made available in Moodle either, through SAML2.

Please note that the reverse is sometimes also true: not all claim types that are mentioned in the IdP metadata are always automatically available. Apparently, the administrator of the IdP system has to ‘turn on’ the claim types or something like that.

Provide the Customer with the SP metadata

After you have done that, you should provide them with the SP (Service Provider) metadata, which can be obtained here:

Copy the url from the ‘View Service Provider Metadata’ link and give it to your users. They should know what to do with it, but just in case they ask you for it:

  • The Identifier (Entity ID) can be found in the attribute entityID of the SP Metadata.

For instance, in the following snippet, I have highlighted the entityID attribute:

<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" entityID="https://staging.contoso-learning.example/auth/saml2/sp/metadata.php">
  • The Reply URL (Assertion Consumer Service URL) can be found in the Location attribute of an AssertionConsumerService node.

Here’s another example where I have highlighted the Location attribute:

<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://staging.contoso-learning.example/auth/saml2/sp/saml2-acs.php/staging.contoso-learning.example" index="0"/>

N.B.: if you have trouble generating (accessing) the SP Metadata, wait till you have completed the remaining configuration (see the next section), then try again.

Configure the SAML2 Plugin

To configure the SAML2 plugin, take a close look at your users’s IdP metadata xml. What you need to extract from it, are the fields you need to map in Moodle.

These are the fields that you need at minimum: uid, email address, first name, and last name.

In my experience, these are typically called:

  • uid: uid, upn or objectidentifier (see subsection below)
  • email address: emailaddress
  • first name: givenname
  • last name: surname

Officially, the names are much longer, e.g.:

http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname

But if you set ‘Simplify attributes’ to ‘Yes’ (the default), then you can use the much shorter names:

The defaults for the remaining configuration options are pretty sensible, although you might want to set ‘Auto create users’ to ‘Yes’, depending on whether the users already exist in Moodle or Totara. For testing purposes, set it to ‘Yes’.

Mapping IdP: uid, upn or objectidentifier

If you link accounts in two different systems (which is basically what SSO comes down to), then you need a way to uniquely identify a user in both systems. In SAML2, this typically done with one of the following attributes:

  • uid
  • upn (which stands for User Principal Name)
  • objectidentifier

The default in Moodle (and Totara) is to use uid, but this won’t work if the attribute (claimtype) is not actually present in your users’s IdP metadata xml.

So, find the appropriate attribute, and fill it in here, in Mapping IdP:

Typically, the claimtype is a complete url, but usually it is sufficient to only fill in the last part. So, instead of http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress, it is sufficient to put in emailaddress.

Attention: the claim types seem to be case sensitive. For instance, the DisplayName may contain capital letters which are missing in the actual ClaimType value. You should always use the actual ClaimType value.

Mapping to Custom Profile Fields

SAML2 fully supports mapping claim types to custom profile fields. In fact, if you have custom profile fields, they will automatically show up in the configuration screen of the plugin: /admin/settings.php?section=authsettingsaml2 (Site Administration > Plugins > Authentication > SAML2). The resulting field name will be something like: auth_saml2 | field_map_profile_field_isDocentTrainer.

Test the SAML2 Based SSO

  • All the SAML2 configuration is done and tested on staging. Ask your users for a test AD account, and try to login through SAML2.
  • If necessary, visit the /auth/saml2/test.php once you’ve got a saml2 based session going. You should see the details of the info the IdP is sending over.For instance, visiting https://www.contoso-learning.example/auth/saml2/test.php with my Microsoft test account (provided to me by the client’s IT staff) yields:
  • This is useful information, especially if the Claim Types the client is giving you are not actually working. The screen above shows the right names to use for mapping the IdP fields to Moodle fields.

From Staging to Production

Once the SAML2 configuration has been successfully tested, we have to move it to production. There are two procedures here: one for a new customer and one for an existing customer.

New Customer

  • In case of a new customer, after testing we then copy the staging environment to production. Because the domain for will be different, we have to provide your users with the new SP metadata url.

Existing Customer

For an existing customer, the procedure is:

  • Test the SAML2 configuration on staging.
  • Copy the settings from staging to production.
  • Provide your users with the SP metadata url for production.

Troubleshooting

Turn on the development and debugging mode. If you’ve reached a point where you can’t realistically test with your users’s AD system (e.g. because you’re not allowed to go in there), use this testing system:

SAMLtest.id

Please keep in mind that your test site needs to be https (it must have an ssl certificate) .

Connecting to Test IDP SAMLtest.id

To test your Moodle or Totara system against SAMLtest.id, use the following settings in the auth Saml2 plugin:

  • IdP metadata xml OR public xml URL: https://samltest.id/saml/idp
  • Dual login: Yes
  • Mapping IdP: uid – Which IdP attribute should be matched against a Moodle user field
  • Mapping Moodle: Username
  • Data mapping (First name): givenName
  • Data mapping (Surname): sn
  • Data mapping (Email address): mail

(See also https://samltest.id/download/#Attributes_Sent)

Please note: the selected IdP attribute will always be used to create a Moodle username, no matter what you choose for the Mapping Moodle field.

Special Case: Mapping the IdP attribute to Something Other than Username

In the auth Saml2 plugin settings screen:

  • IdP metadata xml OR public xml URL: https://samltest.id/saml/idp
  • Dual login: Yes
  • Mapping IdP: uid – Which IdP attribute should be matched against a Moodle user field
  • Mapping Moodle: ID number (this is the mdl_user.idnumber attribute)
  • Data mapping (First name): givenName
  • Data mapping (Surname): sn
  • Data mapping (Email address): mail
  • Data mapping (ID number): uid

Please keep in mind that the IdP attribute will also be used to create the username. In the example above, the uid will be used as the username, and it will be used as the ID number.

You can test this by having a SAMLtest.id test user login, and then change their Moodle username afterwards – they will still be able to login through SAMLtest.id (because the SAMLtest.id login matches with the ID number, given the settings above).

In most scenarios, it’s superfluous to set the ‘Mapping Moodle’ to anything other than username, since the ‘Mapping IdP’ will be stored there anyway.

However, if you want to link existing Moodle accounts to external accounts, it may be easier to set ‘Mapping Moodle’ to idnumber. That way you only need to supply the external value of Mapping IdP to idnumber. You don’t need to change the existing Moodle username – which can be confusing to users.

‘Common’ Errors

You may have encountered this message on the login screen (/login/index.php):

Error: “Exception – Call to a member function export_for_template() on string”

We do not recommend this since it would be a core hack, but you could ask a developer to go to line 731 of /lib/authlib.php and comment out this part:

$idp['icon'] = [
       'context' => $icon->export_for_template($output),
       'template' => $icon->get_template($output)
];

And change:

$idp['icon'] = array_merge($idp['icon'], $icon->export_for_pix($output;

To:

$idp['icon'] = '';

The reason behind this bug: when the IdP provides an icon, the variable $idp['icon'] is set to a string. But Moodle (or Totara) expects an instance of pix_icon there.

Troubleshooting

You can use an IdP service like https://idp.ssocircle.com/ to test saml2. But please keep in mind that you should only do that if you think the saml2 plugin is somehow not working properly (e.g. you just installed a new version of the plugin).

OAuth2 in Moodle lets you delegate authentication to an external identity provider. This guide covers finding the correct endpoints, configuring the issuer and user field mappings, troubleshooting callback failures and missing user data, and working around Totara’s email verification requirement for custom services.

Finding the correct OAuth2 endpoints

For Moodle’s OAuth2 login to work, you need three endpoints from your identity provider: authorization_endpoint, token_endpoint, and userinfo_endpoint. IdP admins sometimes hand over a metadata URL rather than the individual endpoints. If the URLs you’ve been given don’t work, check whether they point to an OpenID Connect discovery document (a JSON file at a .well-known/openid-configuration path). That document contains all the endpoints you need.

For example, a Keycloak-based IdP might give you a discovery URL like this:

https://account.example.com/auth/realms/business/.well-known/openid-configuration

Fetching that URL returns a JSON document with all the endpoint URLs already filled in:

{
  "issuer": "https://account.example.com/auth/realms/business",
  "authorization_endpoint": "https://account.example.com/auth/realms/business/protocol/openid-connect/auth",
  "token_endpoint": "https://account.example.com/auth/realms/business/protocol/openid-connect/token",
  "userinfo_endpoint": "https://account.example.com/auth/realms/business/protocol/openid-connect/userinfo",
  "end_session_endpoint": "https://account.example.com/auth/realms/business/protocol/openid-connect/logout",
  "jwks_uri": "https://account.example.com/auth/realms/business/protocol/openid-connect/certs"
}

Copy the individual endpoint values into Moodle’s OAuth 2 service configuration. Note that even a correct endpoint set can still fail if the configured scopes don’t cause the IdP to include username and email in the userinfo response — that’s covered in the field mapping section below.

Why /admin/oauth2callback.php throws invalidsesskey

When Moodle redirects back from the identity provider and the user sees an invalidsesskey error, the problem is not with the authorization code itself. In Moodle’s callback flow, the state parameter carries a local return URL that contains a sesskey. Moodle extracts that sesskey and validates it against the current session. If the check fails, Moodle stores invalidsesskey as the login error and sends the user back to the login page.

This means the error is a session-continuity failure at callback time, not a generic OAuth2 problem. Several things can cause it.

Session cookie not sent on the callback. The most common cause is the browser not sending the Moodle session cookie on the cross-site return from the IdP. Check the SameSite attribute on the PHP session cookie. SameSite=Strict is a common culprit: browsers do not send Strict cookies on cross-site top-level navigations, which is exactly what an OAuth2 callback is. SameSite=Lax allows cookies on top-level navigations and is usually the right setting here.

wwwroot mismatch. If $CFG->wwwroot in config.php does not exactly match the URL in the browser address bar, including protocol and any path prefix, session validation can fail. A common variant is wwwroot set to http while the site is served over https.

Reverse proxy configuration. When Moodle sits behind a load balancer or reverse proxy that terminates SSL, the session may be keyed to a different server address than the one the browser sees. Make sure $CFG->reverseproxy and $CFG->sslproxy are set correctly in config.php to reflect the proxy setup.

Multi-node session storage. If the Moodle application runs on more than one node without shared session storage, the node that handles the callback may not have the session that was created when the login flow started. Shared session storage (database or cache) is required for OAuth2 to work reliably across nodes.

Why Moodle reports that the returned user information does not contain a username and email address

After the token exchange, Moodle calls the userinfo endpoint and tries to map the response fields to its internal user attributes. If it cannot find values for username and email, it shows this error. There are two distinct causes.

The IdP is not returning the expected claims. Some providers return email as “mail” or “email_address”. Some return the username equivalent as “preferred_username”, “upn”, “samaccountname”, or a custom attribute. Decode a sample token or inspect the raw userinfo response to see exactly what field names and values the provider is returning.

The Moodle field mappings do not match. Moodle needs to know which field in the userinfo response corresponds to its internal “username” and “email” fields. For OpenID Connect services, these mappings are often created automatically. For custom OAuth2 services they usually need to be added manually. Go to Site administration > Server > OAuth 2 services, open the service, and click “Configure user field mappings”.

A typical mapping table for a Microsoft Entra ID or ADFS provider might look like this:

Internal field name   | External field name (from userinfo response)
----------------------|---------------------------------------------
username              | preferred_username   (or: upn, samaccountname)
email                 | mail                 (or: email)
firstname             | given_name
lastname              | family_name

Mapping firstname and lastname is not required for login, but without them Moodle may force a profile-completion step after the first login.

How to verify the setup before testing login

Moodle includes a built-in test action that is worth using before attempting a full login flow. Go to Site administration > Plugins > Authentication > Manage authentication, find the OAuth 2 row, and click “Test settings”. This checks whether Moodle can reach the configured endpoints and returns the raw userinfo response from the provider.

Use the test output to confirm two things independently: first, that the provider is actually returning username and email claims in its userinfo response; and second, that the field names in those claims match the mappings configured in Moodle. Separating those two checks makes it much easier to diagnose whether a login failure is an endpoint problem, a mapping problem, or a callback session problem.

Issue with Email Verification in OAuth2 for Custom Services in Totara

Description of the Issue

When configuring OAuth2-based SSO in Totara, administrators may encounter an issue where the system enforces email verification for custom OAuth2 providers. Unlike predefined providers such as Google, Microsoft, and Facebook, custom services do not offer the option to disable the "Require email verification" setting in the user interface. This behavior results in user accounts being marked as "pending email confirmation," preventing successful logins.

Observations
  • This restriction does not apply to predefined OAuth2 services, where the "Require email verification" setting can be toggled.
  • In Moodle 4.5, this limitation has been addressed, allowing custom OAuth2 providers to disable email verification.
  • The issue stems from a default database configuration that requires email verification for custom services.
Example Scenario

Upon authentication via a custom OAuth2 provider:

  • A record is created in the oauth2_linked_login table with confirmed set to 0.
  • Users remain unable to log in until email verification is completed.
Suggested Remedy by Totara

Totara HQ has provided an unsupported workaround involving a direct database query. The query modifies the oauth2_issuer table to disable the email verification requirement for a specific OAuth2 service:

UPDATE [prefix]_oauth2_issuer
SET requireconfirmation = 0
WHERE name = 'name_of_issuer_here';

Important Notes:

  • Replace [prefix] with the database prefix used in the Totara installation (e.g., ttr or mdl).
  • Ensure that the name_of_issuer_here matches the exact name of the custom OAuth2 service.
Risks and Limitations
  • Totara does not support this approach as it bypasses a core security measure.
  • Directly modifying the database introduces a risk of unintended consequences and may compromise system security.
  • Any issues arising from this change will not be supported by Totara HQ.
Recommendations
  • Evaluate whether disabling email verification is essential for the specific use case.
  • If email verification must be disabled, proceed with the query cautiously, ensuring a backup of the database before execution.
  • Report the requirement to Totara HQ to encourage future support for this feature in the user interface.

LDAP and SSO are fundamentally different approaches to user authentication in Moodle. LDAP performs credential lookups directly against a directory service; SSO delegates authentication to a centralized identity provider. Understanding the distinction helps you choose the right approach for your setup.

LDAP Authentication

With Moodle’s LDAP plugin, each Moodle instance authenticates users itself by querying an LDAP directory — typically an on-premises Active Directory server accessible over the local network. When a user logs in:

  1. Moodle connects to the LDAP server and queries the credentials.
  2. If the credentials match, Moodle checks whether the user account exists locally; if not, it creates one.
  3. Moodle creates a session.

This works well when your LDAP server is on the same network as Moodle. It is not SSO — each Moodle instance authenticates independently, so a user with access to three instances must log in to each one separately.

Single Sign-On (SSO)

An SSO solution centralizes authentication in a dedicated identity provider (IdP) — such as Azure AD, Okta, or any SAML2-compatible service. When a user logs in to a connected application:

  1. The application redirects the user to the IdP.
  2. The user authenticates once at the IdP — or is recognized as already authenticated.
  3. The IdP redirects back with the outcome and the user’s profile data.
  4. Moodle checks whether the user account exists locally; if not, it creates one.
  5. Moodle creates a session.

Moodle never sees the user’s credentials — it only receives the result from the IdP. If the same user then accesses a second Moodle instance, they are already authenticated at the IdP and pass through without logging in again.

Azure AD: LDAP Is Not Available by Default

Azure AD (Entra ID) is a cloud service and does not expose a traditional LDAP endpoint. To use LDAP with Azure AD, you would need to set up Azure AD Domain Services (AD DS) — a managed domain add-on that does expose LDAP. This is a complex configuration and carries meaningful security risk: exposing LDAP over the internet is roughly equivalent to opening a database port publicly.

For Azure AD environments, SSO via SAML2 or OIDC is the straightforward and recommended path. If you do need to evaluate AD DS, these references cover the setup:

Which to Use

Use LDAP if:

  • You have an on-premises Active Directory server on the same network as Moodle.
  • You have a single Moodle instance, or separate logins per instance are acceptable.

Use SSO if:

  • Your identity provider is cloud-based (Azure AD, Okta, Google Workspace, etc.).
  • You have multiple Moodle instances or other applications that should share a single login.
  • You want to decouple Moodle from the specifics of where credentials are stored.

LTI 1.3 uses OAuth 2.0-based authentication to securely share courses between Moodle instances or with external platforms. This guide covers tool registration, course publishing, and the complete launch flow — including troubleshooting JWT and cookie issues.

LTI at a Glance

LTI 1.3 (Learning Tools Interoperability) makes it possible to share learning content between different learning environments. A course can be hosted in one system (the Tool) and made accessible in another system (the Platform). This means you can either offer content to other platforms (for example, as an e-learning provider) or consume content that another system has made available.

The key concepts in LTI 1.3 are:

  • Platform: your users’s system that wants to use the content. Technically, this is the system that initiates the LTI connection. Examples include Moodle, Blackboard, or Canvas on the consumer side.
  • Tool: the system that hosts and delivers the content or course via LTI. This is where the learning activity actually runs.
  • Registration: the mutual trust relationship between the Tool and Platform, established either dynamically or manually.
  • Deployment: a specific activation of the connection, in which the Platform is granted access to certain courses or content.
  • Launch: the moment when a user clicks an LTI link in the Platform and is redirected to the Tool.
  • Line item and scores: parts of the LTI standard used—for example—to send grades from the Tool back to the Platform.

In this guide, we explain how to register a Platform (client) in the ‘server’, how to publish content as an LTI 1.3 Tool, and how a customer can consume that content in their own Moodle.

1. Enable LTI in Moodle

  • Log in as an administrator.
  • Go to Site administration > Plugins > Manage enrol plugins (/admin/settings.php?section=manageenrols).
  • Locate Publish as LTI tool and click the toggle to Activate.
  • (Optional) Configure defaults: Site administration > Plugins > Enrolments > Publish as LTI tool > Settings.

2. Perform Automated (Dynamic) Registration

Use when the Tool Consumer (Moodle B) supports dynamic registration.

2.1 On the Tool Provider (Moodle A)

  • Go to Site administration > Plugins > Enrolments > Publish as LTI tool > Tool registration.
  • Click Register a platform.
  • Enter a Platform name (e.g., "Moodle B") and click 'Continue'.
  • Copy the Registration URL displayed under Tool details and share it with your consumer.

2.2 On the Platform (Moodle B, the consumer)

  • Go to Site administration > Plugins > Activity modules > External tool > Manage tools.
  • In the Add tool field at the top, paste the Registration URL.
  • Click Add LTI Advantage. Moodle will import the tool and list it under Tools.

2.3 Configure Custom parameters on the Registered Tool

  • Still under Manage tools (Moodle B), locate your newly registered LTI tool.
  • Click the Edit (pencil or gear) icon to open the tool settings.
  • Scroll down (or expand Show more… if you’re doing this in the activity settings) until you see the Custom parameters field.
  • Enter the resource identifier from Moodle A’s Published content > Custom properties, for example:id=fe3dfbec-bd5b-4532-8bd5-0804a7102631
  • Click Save changes.

3. Publish Course as LTI Content (Tool Provider)

  • Navigate to the course you want to share (e.g., /course/view.php?id=<courseid>).
  • From the More menu, select Publish as LTI tool.
  • On the Published content page under the LTI Advantage tab, Moodle will automatically list the Launch URL and Custom properties (including your previously configured deployment_id).
  • No additional input is required here—Moodle handles the URL and ID generation.
  • Copy the Launch URL and Custom properties values for use when registering the tool on the consumer side.

Note: you also have the option to set the ‘Maximum enrolled users’ for the LTI course.

4. Add External Tool to a Course

Before adding the activity, ensure the LTI tool is visible in the course activity chooser:

  • In your course, click the More menu and select LTI External tools.
  • On the LTI External tools page, locate your tool in the list and toggle Show in activity chooser to On.
  • Close the LTI External tools page by clicking on the Course tab.

Now add the External tool activity:

  • In your course, click Add an activity or resource and click on the name of the External tool.
  • Under New External tool, enter an Activity name and adjust settings (appearance, grades, privacy) as needed.
  • Click Save and return to course.

5. Test the LTI Launch. Test the LTI Launch

  • As a test user, click the External tool link in Moodle B.
  • Confirm you are redirected into Moodle A content.
  • Verify navigation, content access, and grade passback if enabled.

6. Troubleshooting

  • Invalid launch data: custom claim 'id' missing: Ensure the id=<deployment_id> in the External tool’s Custom parameters matches the Deployment ID published in the Tool Provider’s course settings. See section 2.5: Configure Custom parameters on the Registered Tool.
  • Unable to find deployment: Verify the deployment_id claim in the JWT matches the Deployment ID on the Tool Provider under Publish as LTI tool Configure Deployments.
  • JWT signature or key errors: Confirm both sites’ JWKS URLs are correct and reachable.
  • No grades returned: Make sure Line item services are enabled in both Moodle A’s Platform details and the Platform’s External tool settings.
  • Browser blocking LTI cookies in IFRAME: If the tool is embedded in an iframe and cookies are rejected despite allowing third‑party cookies:
  • In Moodle A’s web root, add or update your .htaccess (Apache) or server config (NGINX) to mark cookies with SameSite=None; Secure. For Apache:Header always edit Set-Cookie ^(.*)$ "$1; SameSite=None; Secure"
  • For NGINX, add in yyour server block:proxy_cookie_path / "/; SameSite=None; Secure";
  • Reload/restart your web server.
  • Clear your browser cookies for the Moodle A domain and try the launch again.