[java-plugin-shibd-oidc] branch main updated: Support attribute resolution during the extract claims step

Codeberg noreply at shibboleth.net
Fri Nov 28 11:08:04 UTC 2025


This is an automated email from the git hooks/post-receive script.

codeberg pushed a commit to branch main
in repository java-plugin-shibd-oidc.

View the commit online:
https://codeberg.org/Shibboleth/java-plugin-shibd-oidc/commit/49cc7970ba28d579becab58c1002018ccd285647

The following commit(s) were added to refs/heads/main by this push:
     new 49cc797  Support attribute resolution during the extract claims step
49cc797 is described below

commit 49cc7970ba28d579becab58c1002018ccd285647
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Nov 28 11:07:57 2025 +0000

    Support attribute resolution during the extract claims step
    
     - Add a test resolver configuration
     - Need to think about UID lookup
---
 .../idp/module/conf/attribute-resolver.xml         | 57 +++++++++++++++++++
 .../sp/oidc/profile/impl/ExtractOIDCClaims.java    | 64 +++++++++++++++++++++-
 2 files changed, 118 insertions(+), 3 deletions(-)

diff --git a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/attribute-resolver.xml b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/attribute-resolver.xml
new file mode 100644
index 0000000..74f6bba
--- /dev/null
+++ b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/attribute-resolver.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+This file is a rudimentary example. While it is semi-functional, it isn't very
+interesting. It is here only as a starting point for your deployment process
+to avoid any dependency on components like an LDAP directory.
+
+Very few attribute definitions and data connectors are demonstrated, and the
+data is derived statically from the logged-in username and a static example
+connector.
+
+The file(s) in the examples directory contain more examples that involve more
+complex approaches. Deployers should refer to the documentation for a complete
+list of possible components and their options.
+-->
+<AttributeResolver
+        xmlns="urn:mace:shibboleth:2.0:resolver" 
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xmlns:oidc="urn:mace:shibboleth:2.0:resolver:oidc"
+        xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd
+            urn:mace:shibboleth:2.0:resolver:oidc http://shibboleth.net/schema/oidc/shibboleth-attribute-encoder-oidc.xsd">
+
+
+    
+<!--     <AttributeDefinition id="eduPersonPrincipalName" xsi:type="Scoped" scope="%{idp.scope}">
+        <InputAttributeDefinition ref="uid" />
+    </AttributeDefinition>
+
+    <AttributeDefinition id="uid" xsi:type="PrincipalName" /> -->
+
+    <AttributeDefinition id="mail" xsi:type="Template">
+        <InputDataConnector ref="staticAttributes" attributeNames="name" />
+        <Template>
+          <![CDATA[
+               ${name}@example.org
+          ]]>
+        </Template>
+    </AttributeDefinition>
+
+    <!--
+    This is an example of an attribute sourced from a data connector.
+    -->
+    <AttributeDefinition id="eduPersonScopedAffiliation" xsi:type="Scoped" scope="%{idp.scope}">
+        <InputDataConnector ref="staticAttributes" attributeNames="affiliation" />
+    </AttributeDefinition>
+
+
+    <DataConnector id="staticAttributes" xsi:type="Static" exportAttributes="schacHomeOrganization">
+        <Attribute id="affiliation">
+            <Value>member</Value>
+        </Attribute>
+        <Attribute id="name">
+            <Value>jdoe</Value>
+        </Attribute>
+    </DataConnector>
+    
+
+</AttributeResolver>
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ExtractOIDCClaims.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ExtractOIDCClaims.java
index c2d6ecf..2d69040 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ExtractOIDCClaims.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ExtractOIDCClaims.java
@@ -18,8 +18,10 @@ import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.function.Function;
+import java.util.stream.Collectors;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
 import org.opensaml.messaging.context.navigate.ChildContextLookup;
 import org.opensaml.messaging.context.navigate.RecursiveTypedParentContextLookup;
@@ -43,6 +45,8 @@ import net.shibboleth.idp.attribute.filter.AttributeFilter;
 import net.shibboleth.idp.attribute.filter.AttributeFilterException;
 import net.shibboleth.idp.attribute.filter.context.AttributeFilterContext;
 import net.shibboleth.idp.attribute.filter.context.AttributeFilterContext.Direction;
+import net.shibboleth.idp.attribute.resolver.AttributeResolver;
+import net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext;
 import net.shibboleth.idp.attribute.transcoding.AttributeTranscoder;
 import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
 import net.shibboleth.idp.attribute.transcoding.TranscoderSupport;
@@ -61,6 +65,7 @@ import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.primitive.LoggerFactory;
 import net.shibboleth.shared.service.ServiceException;
 import net.shibboleth.shared.service.ServiceableComponent;
+import net.shibboleth.sp.Application;
 import net.shibboleth.sp.profile.AbstractApplicationAction;
 
 /**
@@ -248,10 +253,64 @@ public class ExtractOIDCClaims extends AbstractApplicationAction {
         
         //TODO custom extraction, attribute resolution
         
+        // TODO Extract standard attributes. We might want to extract additional authentication information, not sure yet
+        
+        
+        final Function<ProfileRequestContext,Collection<IdPAttribute>> aes =
+                profileConfiguration.getAttributeExtractionStrategy(profileRequestContext);
+        if (aes != null) {
+            log.debug("{} Applying custom attribute extraction strategy", getLogPrefix());
+            final Collection<IdPAttribute> newAttributes = aes.apply(profileRequestContext);
+            if (newAttributes != null && !newAttributes.isEmpty()) {
+                if (log.isDebugEnabled()) {
+                    log.debug("{} Extracted attributes with custom strategy: {}", getLogPrefix(),
+                            newAttributes.stream().map(IdPAttribute::getId).collect(Collectors.toUnmodifiableList()));
+                }
+                IdPAttributeSupport.withMapMergeDuplicates(accumulator, newAttributes);
+            }
+        }
+        
+        if (profileConfiguration.isResolveAttributes(profileRequestContext)) {
+            log.debug("{} Performing internal attribute resolution", getLogPrefix());
+            final Collection<IdPAttribute> newAttributes = resolveAttributes(profileRequestContext);
+            if (newAttributes != null && !newAttributes.isEmpty()) {
+                if (log.isDebugEnabled()) {
+                    log.debug("{} Resolved attributes internally: {}", getLogPrefix(),
+                            newAttributes.stream().map(IdPAttribute::getId).collect(Collectors.toUnmodifiableList()));
+                }
+                IdPAttributeSupport.withMapMergeDuplicates(accumulator, newAttributes);
+            }
+        }
+        
         // Install the final result back.
         attributeContext.setIdPAttributes(accumulator);
     }
     
+    /**
+     * Perform internal attribute resolution using the {@link Application}-associated
+     * {@link AttributeResolver} service.
+     * 
+     * @param profileRequestContext profile request context
+     * 
+     * @return resolved attributes
+     */
+    @Nullable private Collection<IdPAttribute> resolveAttributes(
+            @Nonnull final ProfileRequestContext profileRequestContext) {
+        
+        final AttributeResolutionContext resolutionContext = new AttributeResolutionContext();
+
+        resolutionContext.setResolutionLabel("sp/consumer/oidc")
+            .setPrincipal(profileConfiguration.getAttributeResolutionPrincipal(profileRequestContext))
+            .setAttributeIssuerID(issuerLookupStrategy.apply(profileRequestContext))
+            .setAttributeRecipientID(requesterLookupStrategy.apply(profileRequestContext))
+            .setAttributeRecipientGroupID(profileConfiguration.getAttributeRecipientGroupID(profileRequestContext))
+            .setRequestedIdPAttributeNames(profileConfiguration.getRequestedIdPAttributeNames(profileRequestContext));
+        
+        profileRequestContext.addSubcontext(resolutionContext, true);
+        resolutionContext.resolveAttributes(ensureApplication().getAttributeResolver());
+        return resolutionContext.getResolvedIdPAttributes().values();
+    }
+    
     /**
      * Process the inbound OIDC Claims.
      * 
@@ -313,7 +372,7 @@ public class ExtractOIDCClaims extends AbstractApplicationAction {
                 final ServiceableComponent<MetadataResolver> metadataResolverComponent =
                         ensureApplication().getMetadataResolver().getServiceableComponent()) {
             
-            // Populate here for locking scope.
+            // TODO, can only use SAML metadata here. Populate here for locking scope.
             //filterContext.setMetadataResolver(metadataResolverComponent.getComponent());
             
             final AttributeFilter filter = filterComponent.getComponent();
@@ -348,8 +407,7 @@ public class ExtractOIDCClaims extends AbstractApplicationAction {
             .setProxiedRequesterContextLookupStrategy(null)
             .setAttributeIssuerID(issuerLookupStrategy.apply(profileRequestContext))
             .setAttributeRecipientID(requesterLookupStrategy.apply(profileRequestContext))
-            //TODO add this back
-            //.setAttributeRecipientGroupID(profileConfiguration.getAttributeRecipientGroupID(profileRequestContext))
+            .setAttributeRecipientGroupID(profileConfiguration.getAttributeRecipientGroupID(profileRequestContext))
             .setIncludeUnfilteredAttributes(acceptUnfilteredAttributes);
     }
     

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list