Grouper supports the pac4j plugin library (since Grouper v2.6.10+), which provides single sign-on (SSO) capabilities within Grouper. Since it runs in the same Java process, there is no need for an external Apache or Shibboleth SP service. Since Grouper v5, there is only a single Grouper process and Apache and the Shibboleth SP are not bundled in the image, so this is a useful option for providing SSO. Pac4j can be configured for OIDC and CAS authentication as well as SAML, so it may also be an attractive option even in v4.

Setup

For a fully integrated sample configuration, see the docker-compose setup in the src/test/docker folder of the Git repository. The environment includes sample Grouper configurations for SAML2, OIDC, or CAS, along with a Shibboleth IDP that can authenticate Grouper using any of these methods.

Adding the plugin jar to the image

The latest version of the pac4j plugin jar can be downloaded from Maven (if available), or https://github.internet2.edu/internet2/grouper-ext-auth/releases). The downloaded file can have the version number in the file name, but it is recommended to leave it out of the name when copied into the container. This way, the configuration does not need to be changed every time the version changes. For example:

COPY grouper-authentication-plugin-1.0.0.jar /opt/grouper/plugins/grouper-authentication-plugin.jar

The default location for plugin jars is /opt/grouper/plugins, but you can can use an alternate location and configure Grouper to use that directory.

Enabling the plugin in grouper.properties

In grouper.properties, add the following:

grouper.osgi.enable = true
grouper.osgi.jar.dir = /opt/grouper/plugins
grouper.osgi.framework.boot.delegation=org.osgi.*,javax.*,org.apache.commons.logging,edu.internet2.middleware.grouperClient.*,edu.internet2.middleware.grouper.*,org.w3c.*,org.xml.*,sun.*

grouperOsgiPlugin.0.jarName = grouper-authentication-plugin.jar


The grouper.osgi.jar.dir property should point to the directory you copied the file to in your image build file. Property grouperOsgiPlugin.0.jarName is the name of the file you copied in, inside of the OSGI plugin directory.

Configuring the UI or WS for authentication

In grouper-ui.properties (or grouper-ws.properties for WS), add properties appropriate for desired authentication. Note that only one authentication type can be used.

If you do not already have a grouper.properties and/or grouper-ui.properties because you are storing configuration in the database, it is recommended to create files specifically for the pac4 configuration, so that you can fix any login issues offline, in case you are locked out of the UI.

Most of the configuration for the underlying authentication library is exposed to the Grouper configuration. Any field in the Java classes can be directly set using the field name or a setter used by using a related property (setting attribute=value will call setAttribute(value) )

SAML2

For SAML2, for example:

grouper.is.extAuth.enabled = true
external.authentication.grouperContextUrl = https://grouper-ui.unicon.local/grouper

external.authentication.provider = saml
external.authentication.saml.identityProviderEntityId = https://idp-host-name/idp/shibboleth
external.authentication.saml.serviceProviderEntityId = http://localhost:8080/grouper
external.authentication.saml.serviceProviderMetadataPath = file:/opt/grouper/sp-metadata.xml
external.authentication.saml.identityProviderMetadataPath = file:/opt/grouper/idp-metadata.xml
external.authentication.saml.keystorePath = file:/opt/grouper/sp-keystore.p12
#external.authentication.saml.keyStoreType = PKCS12
external.authentication.saml.keystorePassword = testme
external.authentication.saml.privateKeyPassword = testme
external.authentication.saml.attributeAsId = urn:oid:0.9.2342.19200300.100.1.1

#external.authentication.exclusions = /status


The three Path properties above (serviceProviderMetadataPath, identityProviderMetadataPath, and keystorePath) can handle various urls:

  • the resource: or the classpath: prefixes refer to a classpath

  • the http: or the https: prefixes refer to a web url

  • the file: prefix or no prefix at all refer to a local filesystem file

The serviceProviderMetadataPath is optional, and pac4j will generate a new file at that location if it does not exist. If there is an existing SP metadata definition, it will use the HTTP-POST ACS url as the callback endpoint after login. However, the recommended callback URL to use is <grouperContextUrl>/callback for proper functionality. If pac4j generates the SP xml file, it will use <grouperContextUrl>/callback?client_name=client for the ACS url. The ACS url needs to be registered with your IDP for login to succeed.

Pac4j uses a keystore instead of separate PEM files for the SP key and certificate. Either a JKS or PKCS12 file type can be used. The keyStoreType configuration property is optional, as pac4j should be able to determine the file format without this. The keystorePassword and privateKeyPassword refer to the passphrase used when setting up the keystore.

The attributeAsId value is optional, and refers to the OID of a response attribute to use for the username, if it is not in the nameId field.

Property external.authentication.exclusions is optional, and represents the URI’s (comma-separated) that will be allowed without authentication. The default is /status, so that the health check endpoint can be accessed by external monitoring systems. To disable this exclusion, set the value to blank.

For more information and more options, see https://www.pac4j.org/5.7.x/docs/clients/saml.html and https://github.com/pac4j/pac4j/blob/5.7.x/pac4j-saml/src/main/java/org/pac4j/saml/config/SAML2Configuration.java

See the section below for specific instructions on migrating from a Shibboleth SP to pac4j.

OIDC

For OIDC, for example:

grouper.is.extAuth.enabled = true
external.authentication.grouperContextUrl = https://grouper-ui.unicon.local/grouper

external.authentication.provider = oidc
external.authentication.oidc.clientId = *****
external.authentication.oidc.discoveryURI = https://idp-host-name/.well-known/openid-configuration
external.authentication.oidc.secret = *****
external.authentication.oidc.claimAsUsername = preferred_username
 


For more information and more options, see https://www.pac4j.org/5.7.x/docs/clients/openid-connect.html and https://github.com/pac4j/pac4j/blob/5.7.x/pac4j-oidc/src/main/java/org/pac4j/oidc/config/OidcConfiguration.java

CAS

For CAS, for example:

grouper.is.extAuth.enabled = true
external.authentication.grouperContextUrl = https://grouper-ui.unicon.local/grouper

# Note for CAS: you'll need to make sure that the CAS server SSL certificate is available in the trust store
external.authentication.provider = cas
external.authentication.cas.prefixUrl = https://idp-host-name/idp/profile/cas
external.authentication.cas.protocol = CAS20


For more information and more options, see https://www.pac4j.org/5.7.x/docs/clients/cas.html and https://github.com/pac4j/pac4j/blob/5.7.x/pac4j-cas/src/main/java/org/pac4j/cas/config/CasConfiguration.java

Converting a Grouper image from Shibboleth SP to pac4j configuration

The following tips describe the basic steps needed to move from a Shibboleth SP running inside a Grouper container to a pac4j SAML configuration.

1) Include the pac4j jar file into your image (or mount it at runtime)

Download the jar, then copy into the image via the Dockerfile or mount into a running container, as described above.


2) Convert the SP cert and key PEM files to a keystore

Pac4j uses a keystore to read certificates instead of PEM files. The locations of the key and certificate files are defined in your /etc/shibboleth/shibboleth2.xml file, in the <CredentialResolver> section. use the following command to convert these into a PKCS12 keystore, renaming filenames as needed. The command will ask for a password, which will need to go into the configuration in the keystorePassword and privateKeyPassword properties.

openssl pkcs12 -export -out sp-keystore.p12 -inkey sp-key.pem -in sp-cert.pem

If there is also a CA certificate chain to include, the -certfile ca-cert.pem option can be added.


3) Extract other properties

Other files and properties needed for pac4j can be extracted from shibboleth2.xml, or from the currently running Shibboleth SP:

  • identityProviderEntityId: From shibboleth2.xml, <SSO entityID="YOUR_IDP_ENTITYID" ...>

  • serviceProviderEntityId: From shibboleth2.xml, <ApplicationDefaults entityID="YOUR_SP_ENTITYID" ...>

  • serviceProviderMetadataPath; The location of the SP metadata, which will be generated by pac4j if the file is missing. If pac4j generates the file, it will use <grouperContextUrl>/callback?client_name=client as the ACS callback endpoint. If you use your own existing SP metadata (from existing SP or IDP metadata files, or the deprecated /shibboleth.SSO/Metadata endpoint), you can set your own ACS url, but <grouperContextUrl>/callback (with or without extra query parameters) is the only one to reliably work.

  • identityProviderMetadataPath: From shibboleth2.xml, <MetadataProvider> node. This could be either a URL or a file.

  • attributeAsId (optional): If you are not using a nameId for the username and instead getting it from an attribute, this is the OID for it. The attribute you are currently using will be in shibboleth2.xml, likely the first item in the ApplicationDefaults REMOTE_USER="..." list. The OID for it is in its entry in attribute-map.xml.


4) Change the ACS endpoint

The callback endpoint after login will no longer be /Shibboleth.sso/SAML2/POST. The correct one for pac4j will be <grouperContextUrl>/callback?client_name=client (default), or a custom one if you have it defined in your SP metadata. This will need to be changed in the <AssertionConsumerService> SAML:2.0:bindings:HTTP-POST entry in the IDP metadata.


5) Add files to the Docker image, and update grouper.properties and grouper-ui.properties.

The keystore and metadata files need to be added to the Docker image, or mounted at runtime. Pac4j configuration is to be added to the appropriate Grouper configuration files residing in /opt/grouper/grouperWebapp/WEB-INF/classes.


  • No labels