diff --git a/.gitignore b/.gitignore
index 57e4ed34104d..069d8ebe3635 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,7 @@ lms/envs/private.py
cms/envs/private.py
.venv/
CLAUDE.md
+.claude/
AGENTS.md
# end-noclean
diff --git a/common/djangoapps/third_party_auth/docs/how_tos/testing_saml_locally.rst b/common/djangoapps/third_party_auth/docs/how_tos/testing_saml_locally.rst
new file mode 100644
index 000000000000..2be1cc1dacf6
--- /dev/null
+++ b/common/djangoapps/third_party_auth/docs/how_tos/testing_saml_locally.rst
@@ -0,0 +1,156 @@
+Testing SAML Authentication Locally with MockSAML
+==================================================
+
+This guide walks through setting up and testing SAML authentication in a local Open edX devstack environment using MockSAML.com as a test Identity Provider (IdP).
+
+Overview
+--------
+
+SAML (Security Assertion Markup Language) authentication in Open edX requires three configuration objects to work together:
+
+1. **SAMLConfiguration**: Configures the Service Provider (SP) metadata - entity ID, keys, and organization info
+2. **SAMLProviderConfig**: Configures a specific Identity Provider (IdP) connection with metadata URL and attribute mappings
+3. **SAMLProviderData**: Stores the IdP's metadata (SSO URL, public key) fetched from the IdP's metadata endpoint
+
+**Critical Requirement**: The SAMLConfiguration object MUST have the slug "default" because this value is hardcoded in the authentication execution path at ``common/djangoapps/third_party_auth/models.py:906``.
+
+Prerequisites
+-------------
+
+* Local Open edX devstack running
+* Access to Django admin at http://localhost:18000/admin/
+* MockSAML.com account (free service for SAML testing)
+
+Step 1: Configure SAMLConfiguration
+------------------------------------
+
+The SAMLConfiguration defines your Open edX instance as a SAML Service Provider (SP).
+
+1. Navigate to Django Admin → Third Party Auth → SAML Configurations
+2. Click "Add SAML Configuration"
+3. Configure with these **required** values:
+
+ ============ ===================================================
+ Field Value
+ ============ ===================================================
+ Site localhost:18000
+ **Slug** **default** (MUST be "default" - hardcoded in code)
+ Entity ID https://saml.example.com/entityid
+ Enabled ✓ (checked)
+ ============ ===================================================
+
+4. For local testing with MockSAML, you can leave the keys blank.
+
+5. Optionally configure Organization Info (use default or customize):
+
+ .. code-block:: json
+
+ {
+ "en-US": {
+ "url": "http://localhost:18000",
+ "displayname": "Local Open edX",
+ "name": "localhost"
+ }
+ }
+
+6. Click "Save"
+
+Step 2: Configure SAMLProviderConfig
+-------------------------------------
+
+The SAMLProviderConfig connects to a specific SAML Identity Provider (MockSAML in this case).
+
+1. Navigate to Django Admin → Third Party Auth → Provider Configuration (SAML IdPs)
+2. Click "Add Provider Configuration (SAML IdP)"
+3. Configure with these values:
+
+ ========================= ===================================================
+ Field Value
+ ========================= ===================================================
+ Name Test Localhost (or any descriptive name)
+ Slug default (to match test URLs)
+ Backend Name tpa-saml
+ Entity ID https://saml.example.com/entityid
+ Metadata Source https://mocksaml.com/api/saml/metadata
+ Site localhost:18000
+ SAML Configuration Select the SAMLConfiguration created in Step 1
+ Enabled ✓ (checked)
+ Visible ☐ (unchecked for testing)
+ Skip hinted login dialog ✓ (checked - recommended)
+ Skip registration form ✓ (checked - recommended)
+ Skip email verification ✓ (checked - recommended)
+ Send to registration first ✓ (checked - recommended)
+ ========================= ===================================================
+
+4. Leave all attribute mappings (User ID, Email, Full Name, etc.) blank to use defaults
+5. Click "Save"
+
+**Important**: The Entity ID in SAMLProviderConfig MUST match the Entity ID in SAMLConfiguration.
+
+Step 3: Set IdP Data
+--------------------
+
+The SAMLProviderData stores metadata from the Identity Provider (MockSAML), create a record with
+
+* **Entity ID**: https://saml.example.com/entityid
+* **SSO URL**: https://mocksaml.com/api/saml/sso
+* **Public Key**: The IdP's signing certificate
+* **Expires At**: Set to 1 year from fetch time
+
+
+Step 4: Test SAML Authentication
+---------------------------------
+
+1. Navigate to: http://localhost:18000/auth/idp_redirect/saml-default
+2. You should be redirected to MockSAML.com
+3. Complete the authentication on MockSAML - just click "Sign In" with whatever is in the form.
+4. You should be redirected back to Open edX
+5. If this is a new user, you'll see the registration form
+6. After registration, you should be logged in
+
+Expected Behavior
+^^^^^^^^^^^^^^^^^
+
+1. Initial redirect to MockSAML (https://mocksaml.com/api/saml/sso)
+2. MockSAML displays the login page
+3. After authentication, MockSAML POSTs the SAML assertion back to Open edX
+4. Open edX validates the assertion and creates/logs in the user
+5. User is redirected to the dashboard or registration form (if new user)
+
+Reference Configuration
+-----------------------
+
+Here's a summary of a working test configuration:
+
+**SAMLConfiguration** (id=6):
+
+* Site: localhost:18000
+* Slug: **default**
+* Entity ID: https://saml.example.com/entityid
+* Enabled: True
+
+**SAMLProviderConfig** (id=11):
+
+* Name: Test Localhost
+* Slug: default
+* Entity ID: https://saml.example.com/entityid
+* Metadata Source: https://mocksaml.com/api/saml/metadata
+* Backend Name: tpa-saml
+* Site: localhost:18000
+* SAML Configuration: → SAMLConfiguration (id=6)
+* Enabled: True
+
+**SAMLProviderData** (id=3):
+
+* Entity ID: https://saml.example.com/entityid
+* SSO URL: https://mocksaml.com/api/saml/sso
+* Public Key: (certificate from MockSAML metadata)
+* Fetched At: 2026-02-27 18:05:40+00:00
+* Expires At: 2027-02-27 18:05:41+00:00
+* Valid: True
+
+**MockSAML Configuration**:
+
+* SP Entity ID: https://saml.example.com/entityid
+* ACS URL: http://localhost:18000/auth/complete/tpa-saml/
+* Test User Attributes: email, firstName, lastName, uid
diff --git a/common/djangoapps/third_party_auth/migrations/0014_samlproviderconfig_optional_email_checkboxes.py b/common/djangoapps/third_party_auth/migrations/0014_samlproviderconfig_optional_email_checkboxes.py
new file mode 100644
index 000000000000..34fcf3c97b58
--- /dev/null
+++ b/common/djangoapps/third_party_auth/migrations/0014_samlproviderconfig_optional_email_checkboxes.py
@@ -0,0 +1,26 @@
+# Generated migration for adding optional checkbox skip configuration field
+
+from django.db import migrations, models
+import django.utils.translation
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('third_party_auth', '0013_default_site_id_wrapper_function'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='samlproviderconfig',
+ name='skip_registration_optional_checkboxes',
+ field=models.BooleanField(
+ default=False,
+ help_text=django.utils.translation.gettext_lazy(
+ "If enabled, optional checkboxes (marketing emails opt-in, etc.) will not be rendered "
+ "on the registration form for users registering via this provider. When these checkboxes "
+ "are skipped, their values are inferred as False (opted out)."
+ ),
+ ),
+ ),
+ ]
diff --git a/common/djangoapps/third_party_auth/models.py b/common/djangoapps/third_party_auth/models.py
index ceab0fa8c711..d3beafb4db88 100644
--- a/common/djangoapps/third_party_auth/models.py
+++ b/common/djangoapps/third_party_auth/models.py
@@ -745,6 +745,14 @@ class SAMLProviderConfig(ProviderConfig):
"immediately after authenticating with the third party instead of the login page."
),
)
+ skip_registration_optional_checkboxes = models.BooleanField(
+ default=False,
+ help_text=_(
+ "If enabled, optional checkboxes (marketing emails opt-in, etc.) will not be rendered "
+ "on the registration form for users registering via this provider. When these checkboxes "
+ "are skipped, their values are inferred as False (opted out)."
+ ),
+ )
other_settings = models.TextField(
verbose_name="Advanced settings", blank=True,
help_text=(
diff --git a/lms/static/js/student_account/views/RegisterView.js b/lms/static/js/student_account/views/RegisterView.js
index 42ab7c8857a8..bd958c2d8bd4 100644
--- a/lms/static/js/student_account/views/RegisterView.js
+++ b/lms/static/js/student_account/views/RegisterView.js
@@ -58,6 +58,7 @@
);
this.currentProvider = data.thirdPartyAuth.currentProvider || '';
this.syncLearnerProfileData = data.thirdPartyAuth.syncLearnerProfileData || false;
+ this.skipRegistrationOptionalCheckboxes = data.thirdPartyAuth.skipRegistrationOptionalCheckboxes || false;
this.errorMessage = data.thirdPartyAuth.errorMessage || '';
this.platformName = data.platformName;
this.autoSubmit = data.thirdPartyAuth.autoSubmitRegForm;
@@ -156,6 +157,7 @@
fields: fields,
currentProvider: this.currentProvider,
syncLearnerProfileData: this.syncLearnerProfileData,
+ skipRegistrationOptionalCheckboxes: this.skipRegistrationOptionalCheckboxes,
providers: this.providers,
hasSecondaryProviders: this.hasSecondaryProviders,
platformName: this.platformName,
diff --git a/lms/templates/student_account/register.underscore b/lms/templates/student_account/register.underscore
index 84cdb09d5ce1..1822bafc7b64 100644
--- a/lms/templates/student_account/register.underscore
+++ b/lms/templates/student_account/register.underscore
@@ -56,14 +56,16 @@