diff --git a/authentik/providers/saml/api/providers.py b/authentik/providers/saml/api/providers.py
index c5c837ec426c..9577ba8a8fdf 100644
--- a/authentik/providers/saml/api/providers.py
+++ b/authentik/providers/saml/api/providers.py
@@ -180,6 +180,7 @@ class Meta:
             "session_valid_not_on_or_after",
             "property_mappings",
             "name_id_mapping",
+            "authn_context_class_ref_mapping",
             "digest_algorithm",
             "signature_algorithm",
             "signing_kp",
diff --git a/authentik/providers/saml/migrations/0017_samlprovider_authn_context_class_ref_mapping.py b/authentik/providers/saml/migrations/0017_samlprovider_authn_context_class_ref_mapping.py
new file mode 100644
index 000000000000..c86dcccfa7e9
--- /dev/null
+++ b/authentik/providers/saml/migrations/0017_samlprovider_authn_context_class_ref_mapping.py
@@ -0,0 +1,28 @@
+# Generated by Django 5.0.13 on 2025-03-18 17:41
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("authentik_providers_saml", "0016_samlprovider_encryption_kp_and_more"),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name="samlprovider",
+            name="authn_context_class_ref_mapping",
+            field=models.ForeignKey(
+                blank=True,
+                default=None,
+                help_text="Configure how the AuthnContextClassRef value will be created. When left empty, the AuthnContextClassRef will be set based on which authentication methods the user used to authenticate.",
+                null=True,
+                on_delete=django.db.models.deletion.SET_DEFAULT,
+                related_name="+",
+                to="authentik_providers_saml.samlpropertymapping",
+                verbose_name="AuthnContextClassRef Property Mapping",
+            ),
+        ),
+    ]
diff --git a/authentik/providers/saml/models.py b/authentik/providers/saml/models.py
index 928493b6d52d..0325d40cd75f 100644
--- a/authentik/providers/saml/models.py
+++ b/authentik/providers/saml/models.py
@@ -71,6 +71,20 @@ class SAMLProvider(Provider):
             "the NameIDPolicy of the incoming request will be considered"
         ),
     )
+    authn_context_class_ref_mapping = models.ForeignKey(
+        "SAMLPropertyMapping",
+        default=None,
+        blank=True,
+        null=True,
+        on_delete=models.SET_DEFAULT,
+        verbose_name=_("AuthnContextClassRef Property Mapping"),
+        related_name="+",
+        help_text=_(
+            "Configure how the AuthnContextClassRef value will be created. When left empty, "
+            "the AuthnContextClassRef will be set based on which authentication methods the user "
+            "used to authenticate."
+        ),
+    )
 
     assertion_valid_not_before = models.TextField(
         default="minutes=-5",
@@ -170,7 +184,6 @@ class SAMLProvider(Provider):
     def launch_url(self) -> str | None:
         """Use IDP-Initiated SAML flow as launch URL"""
         try:
-
             return reverse(
                 "authentik_providers_saml:sso-init",
                 kwargs={"application_slug": self.application.slug},
diff --git a/authentik/providers/saml/processors/assertion.py b/authentik/providers/saml/processors/assertion.py
index dd618f2800ce..c32b5f0c1c9d 100644
--- a/authentik/providers/saml/processors/assertion.py
+++ b/authentik/providers/saml/processors/assertion.py
@@ -1,5 +1,6 @@
 """SAML Assertion generator"""
 
+from datetime import datetime
 from hashlib import sha256
 from types import GeneratorType
 
@@ -52,6 +53,7 @@ class AssertionProcessor:
     _assertion_id: str
     _response_id: str
 
+    _auth_instant: str
     _valid_not_before: str
     _session_not_on_or_after: str
     _valid_not_on_or_after: str
@@ -65,6 +67,11 @@ def __init__(self, provider: SAMLProvider, request: HttpRequest, auth_n_request:
         self._assertion_id = get_random_id()
         self._response_id = get_random_id()
 
+        _login_event = get_login_event(self.http_request)
+        _login_time = datetime.now()
+        if _login_event:
+            _login_time = _login_event.created
+        self._auth_instant = get_time_string(_login_time)
         self._valid_not_before = get_time_string(
             timedelta_from_string(self.provider.assertion_valid_not_before)
         )
@@ -131,7 +138,7 @@ def get_issuer(self) -> Element:
     def get_assertion_auth_n_statement(self) -> Element:
         """Generate AuthnStatement with AuthnContext and ContextClassRef Elements."""
         auth_n_statement = Element(f"{{{NS_SAML_ASSERTION}}}AuthnStatement")
-        auth_n_statement.attrib["AuthnInstant"] = self._valid_not_before
+        auth_n_statement.attrib["AuthnInstant"] = self._auth_instant
         auth_n_statement.attrib["SessionIndex"] = sha256(
             self.http_request.session.session_key.encode("ascii")
         ).hexdigest()
@@ -158,6 +165,28 @@ def get_assertion_auth_n_statement(self) -> Element:
                 auth_n_context_class_ref.text = (
                     "urn:oasis:names:tc:SAML:2.0:ac:classes:MobileOneFactorContract"
                 )
+        if self.provider.authn_context_class_ref_mapping:
+            try:
+                value = self.provider.authn_context_class_ref_mapping.evaluate(
+                    user=self.http_request.user,
+                    request=self.http_request,
+                    provider=self.provider,
+                )
+                if value is not None:
+                    auth_n_context_class_ref.text = str(value)
+                return auth_n_statement
+            except PropertyMappingExpressionException as exc:
+                Event.new(
+                    EventAction.CONFIGURATION_ERROR,
+                    message=(
+                        "Failed to evaluate property-mapping: "
+                        f"'{self.provider.authn_context_class_ref_mapping.name}'"
+                    ),
+                    provider=self.provider,
+                    mapping=self.provider.authn_context_class_ref_mapping,
+                ).from_http(self.http_request)
+                LOGGER.warning("Failed to evaluate property mapping", exc=exc)
+                return auth_n_statement
         return auth_n_statement
 
     def get_assertion_conditions(self) -> Element:
diff --git a/authentik/providers/saml/tests/test_auth_n_request.py b/authentik/providers/saml/tests/test_auth_n_request.py
index 48d6d713b686..499221c76147 100644
--- a/authentik/providers/saml/tests/test_auth_n_request.py
+++ b/authentik/providers/saml/tests/test_auth_n_request.py
@@ -294,6 +294,61 @@ def test_signed_static(self):
         self.assertEqual(parsed_request.id, "aws_LDxLGeubpc5lx12gxCgS6uPbix1yd5re")
         self.assertEqual(parsed_request.name_id_policy, SAML_NAME_ID_FORMAT_EMAIL)
 
+    def test_authn_context_class_ref_mapping(self):
+        """Test custom authn_context_class_ref"""
+        authn_context_class_ref = generate_id()
+        mapping = SAMLPropertyMapping.objects.create(
+            name=generate_id(), expression=f"""return '{authn_context_class_ref}'"""
+        )
+        self.provider.authn_context_class_ref_mapping = mapping
+        self.provider.save()
+        user = create_test_admin_user()
+        http_request = get_request("/", user=user)
+
+        # First create an AuthNRequest
+        request_proc = RequestProcessor(self.source, http_request, "test_state")
+        request = request_proc.build_auth_n()
+
+        # To get an assertion we need a parsed request (parsed by provider)
+        parsed_request = AuthNRequestParser(self.provider).parse(
+            b64encode(request.encode()).decode(), "test_state"
+        )
+        # Now create a response and convert it to string (provider)
+        response_proc = AssertionProcessor(self.provider, http_request, parsed_request)
+        response = response_proc.build_response()
+        self.assertIn(user.username, response)
+        self.assertIn(authn_context_class_ref, response)
+
+    def test_authn_context_class_ref_mapping_invalid(self):
+        """Test custom authn_context_class_ref (invalid)"""
+        mapping = SAMLPropertyMapping.objects.create(name=generate_id(), expression="q")
+        self.provider.authn_context_class_ref_mapping = mapping
+        self.provider.save()
+        user = create_test_admin_user()
+        http_request = get_request("/", user=user)
+
+        # First create an AuthNRequest
+        request_proc = RequestProcessor(self.source, http_request, "test_state")
+        request = request_proc.build_auth_n()
+
+        # To get an assertion we need a parsed request (parsed by provider)
+        parsed_request = AuthNRequestParser(self.provider).parse(
+            b64encode(request.encode()).decode(), "test_state"
+        )
+        # Now create a response and convert it to string (provider)
+        response_proc = AssertionProcessor(self.provider, http_request, parsed_request)
+        response = response_proc.build_response()
+        self.assertIn(user.username, response)
+
+        events = Event.objects.filter(
+            action=EventAction.CONFIGURATION_ERROR,
+        )
+        self.assertTrue(events.exists())
+        self.assertEqual(
+            events.first().context["message"],
+            f"Failed to evaluate property-mapping: '{mapping.name}'",
+        )
+
     def test_request_attributes(self):
         """Test full SAML Request/Response flow, fully signed"""
         user = create_test_admin_user()
@@ -321,8 +376,10 @@ def test_request_attributes_invalid(self):
         request = request_proc.build_auth_n()
 
         # Create invalid PropertyMapping
-        scope = SAMLPropertyMapping.objects.create(name="test", saml_name="test", expression="q")
-        self.provider.property_mappings.add(scope)
+        mapping = SAMLPropertyMapping.objects.create(
+            name=generate_id(), saml_name="test", expression="q"
+        )
+        self.provider.property_mappings.add(mapping)
 
         # To get an assertion we need a parsed request (parsed by provider)
         parsed_request = AuthNRequestParser(self.provider).parse(
@@ -338,7 +395,7 @@ def test_request_attributes_invalid(self):
         self.assertTrue(events.exists())
         self.assertEqual(
             events.first().context["message"],
-            "Failed to evaluate property-mapping: 'test'",
+            f"Failed to evaluate property-mapping: '{mapping.name}'",
         )
 
     def test_idp_initiated(self):
diff --git a/authentik/providers/saml/utils/time.py b/authentik/providers/saml/utils/time.py
index dda87d5ca87d..8d5af313a914 100644
--- a/authentik/providers/saml/utils/time.py
+++ b/authentik/providers/saml/utils/time.py
@@ -1,12 +1,16 @@
 """Time utilities"""
 
-import datetime
+from datetime import datetime, timedelta
 
+from django.utils.timezone import now
 
-def get_time_string(delta: datetime.timedelta | None = None) -> str:
+
+def get_time_string(delta: timedelta | datetime | None = None) -> str:
     """Get Data formatted in SAML format"""
     if delta is None:
-        delta = datetime.timedelta()
-    now = datetime.datetime.now()
-    final = now + delta
+        delta = timedelta()
+    if isinstance(delta, timedelta):
+        final = now() + delta
+    else:
+        final = delta
     return final.strftime("%Y-%m-%dT%H:%M:%SZ")
diff --git a/blueprints/schema.json b/blueprints/schema.json
index c181b308df42..8fcf08074d29 100644
--- a/blueprints/schema.json
+++ b/blueprints/schema.json
@@ -6462,6 +6462,11 @@
                     "title": "NameID Property Mapping",
                     "description": "Configure how the NameID value will be created. When left empty, the NameIDPolicy of the incoming request will be considered"
                 },
+                "authn_context_class_ref_mapping": {
+                    "type": "integer",
+                    "title": "AuthnContextClassRef Property Mapping",
+                    "description": "Configure how the AuthnContextClassRef value will be created. When left empty, the AuthnContextClassRef will be set based on which authentication methods the user used to authenticate."
+                },
                 "digest_algorithm": {
                     "type": "string",
                     "enum": [
diff --git a/schema.yml b/schema.yml
index 1283656f055b..26a2b6aabcdc 100644
--- a/schema.yml
+++ b/schema.yml
@@ -22191,6 +22191,11 @@ paths:
         schema:
           type: string
           format: uuid
+      - in: query
+        name: authn_context_class_ref_mapping
+        schema:
+          type: string
+          format: uuid
       - in: query
         name: authorization_flow
         schema:
@@ -25745,7 +25750,7 @@ paths:
           description: ''
     delete:
       operationId: sources_all_destroy
-      description: Source Viewset
+      description: Prevent deletion of built-in sources
       parameters:
       - in: path
         name: slug
@@ -52228,6 +52233,14 @@ components:
           title: NameID Property Mapping
           description: Configure how the NameID value will be created. When left empty,
             the NameIDPolicy of the incoming request will be considered
+        authn_context_class_ref_mapping:
+          type: string
+          format: uuid
+          nullable: true
+          title: AuthnContextClassRef Property Mapping
+          description: Configure how the AuthnContextClassRef value will be created.
+            When left empty, the AuthnContextClassRef will be set based on which authentication
+            methods the user used to authenticate.
         digest_algorithm:
           $ref: '#/components/schemas/DigestAlgorithmEnum'
         signature_algorithm:
@@ -55183,6 +55196,14 @@ components:
           title: NameID Property Mapping
           description: Configure how the NameID value will be created. When left empty,
             the NameIDPolicy of the incoming request will be considered
+        authn_context_class_ref_mapping:
+          type: string
+          format: uuid
+          nullable: true
+          title: AuthnContextClassRef Property Mapping
+          description: Configure how the AuthnContextClassRef value will be created.
+            When left empty, the AuthnContextClassRef will be set based on which authentication
+            methods the user used to authenticate.
         digest_algorithm:
           $ref: '#/components/schemas/DigestAlgorithmEnum'
         signature_algorithm:
@@ -55348,6 +55369,14 @@ components:
           title: NameID Property Mapping
           description: Configure how the NameID value will be created. When left empty,
             the NameIDPolicy of the incoming request will be considered
+        authn_context_class_ref_mapping:
+          type: string
+          format: uuid
+          nullable: true
+          title: AuthnContextClassRef Property Mapping
+          description: Configure how the AuthnContextClassRef value will be created.
+            When left empty, the AuthnContextClassRef will be set based on which authentication
+            methods the user used to authenticate.
         digest_algorithm:
           $ref: '#/components/schemas/DigestAlgorithmEnum'
         signature_algorithm:
diff --git a/web/src/admin/providers/saml/SAMLProviderFormForm.ts b/web/src/admin/providers/saml/SAMLProviderFormForm.ts
index 1652b10d900f..b798cb40af0d 100644
--- a/web/src/admin/providers/saml/SAMLProviderFormForm.ts
+++ b/web/src/admin/providers/saml/SAMLProviderFormForm.ts
@@ -245,6 +245,41 @@ export function renderForm(
                         )}
                     </p>
                 </ak-form-element-horizontal>
+                <ak-form-element-horizontal
+                    label=${msg("AuthnContextClassRef Property Mapping")}
+                    name="authnContextClassRefMapping"
+                >
+                    <ak-search-select
+                        .fetchObjects=${async (query?: string): Promise<SAMLPropertyMapping[]> => {
+                            const args: PropertymappingsProviderSamlListRequest = {
+                                ordering: "saml_name",
+                            };
+                            if (query !== undefined) {
+                                args.search = query;
+                            }
+                            const items = await new PropertymappingsApi(
+                                DEFAULT_CONFIG,
+                            ).propertymappingsProviderSamlList(args);
+                            return items.results;
+                        }}
+                        .renderElement=${(item: SAMLPropertyMapping): string => {
+                            return item.name;
+                        }}
+                        .value=${(item: SAMLPropertyMapping | undefined): string | undefined => {
+                            return item?.pk;
+                        }}
+                        .selected=${(item: SAMLPropertyMapping): boolean => {
+                            return provider?.authnContextClassRefMapping === item.pk;
+                        }}
+                        ?blankable=${true}
+                    >
+                    </ak-search-select>
+                    <p class="pf-c-form__helper-text">
+                        ${msg(
+                            "Configure how the AuthnContextClassRef value will be created. When left empty, the AuthnContextClassRef will be set based on which authentication methods the user used to authenticate.",
+                        )}
+                    </p>
+                </ak-form-element-horizontal>
 
                 <ak-text-input
                     name="assertionValidNotBefore"