Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ab3a680
Update README.md
datb-com Jan 8, 2020
fd06350
Added InputStream variant to setSPKeys().
datb-com Jan 8, 2020
3584438
pull from coveooss/saml-client (4.0.1SNAPSHOT)
datb-com May 19, 2020
ca423e5
Added support for Redirect binding for SSO.
datb-com May 20, 2020
3b9e773
Identifies as 4.0.1SNAPSHOTJ2
datb-com Jun 3, 2020
8a9f57c
SSO Redirect now uses SHA256 instead of SHA1
datb-com Jun 4, 2020
118c8c0
Merged
datb-com Dec 19, 2022
4dc80b8
Merge branch 'coveooss-master'
datb-com Dec 19, 2022
f615c62
A270669 Updated our changes to SamlClient to align with changes from …
datb-com Dec 21, 2022
44208a8
A275378 Support use of RetrievalMethod for the EncryptionKey for encr…
datb-com Sep 5, 2023
96cca77
C281958 setForceAuthn()
datb-com Oct 23, 2024
565ce26
4.1.3
datb-com Oct 24, 2024
beb5105
Update README.md
datb-com Jan 8, 2020
6bd8a1b
Added InputStream variant to setSPKeys().
datb-com Jan 8, 2020
741401b
pull from coveooss/saml-client (4.0.1SNAPSHOT)
datb-com May 19, 2020
0c5e5c5
Added support for Redirect binding for SSO.
datb-com May 20, 2020
b1437d1
Identifies as 4.0.1SNAPSHOTJ2
datb-com Jun 3, 2020
1b8b4bd
SSO Redirect now uses SHA256 instead of SHA1
datb-com Jun 4, 2020
27ed463
A270669 Updated our changes to SamlClient to align with changes from …
datb-com Dec 21, 2022
a616350
A275378 Support use of RetrievalMethod for the EncryptionKey for encr…
datb-com Sep 5, 2023
6776a4a
C281958 setForceAuthn()
datb-com Oct 23, 2024
f6891ec
samlclient5.0.1snapshot
datb-com Sep 15, 2025
d1cf136
merge with com.coveo 5.0.1 snapshot
datb-com Sep 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ target

.project
.classpath
.settings
.settings
.DS_Store/
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

# Dead Simple SAML 2.0 Client

This is coveooss/saml-client with some crude alterations to allow more flexibility for setting keys where they aren't held in files.

This library implements a very simple SAML 2.0 client that allows retrieving an authenticated identity from a compliant identity provider, using the HTTP POST binding.

It is based on the OpenSAML library, and only provides the necessary glue code to make it work in a basic scenario. This is by no means a complete implementation supporting all the nitty gritty SAML details, but it does perform the basic task of generating requests and validating responses. It's useful if you need to authenticate with SAML but don't want to bring in an uber large framework such as Spring Security.
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>com.coveo</groupId>
<artifactId>saml-client</artifactId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.1</version>
<packaging>jar</packaging>

<name>${project.groupId}:${project.artifactId}</name>
Expand Down
281 changes: 141 additions & 140 deletions src/main/java/com/coveo/saml/MetadataUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,146 +33,147 @@
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;


public class MetadataUtils {

private static final Logger logger = LoggerFactory.getLogger(SamlClient.class);

public static String generateSpMetadata(String entityId, String assertionConsumerServiceURL, String logoutServiceURL) {
return generateSpMetadata(entityId, assertionConsumerServiceURL, logoutServiceURL, null);
}

public static String generateSpMetadata(String entityId, String assertionConsumerServiceURL, String singleLogoutServiceURL, X509Certificate certificate) {
try {
InitializationService.initialize();

EntityDescriptor spEntityDescriptor = createSAMLObject(EntityDescriptor.class);
if (spEntityDescriptor == null) {
return null;
}
spEntityDescriptor.setEntityID(entityId);
SPSSODescriptor spSSODescriptor = createSAMLObject(SPSSODescriptor.class);
if (spSSODescriptor == null) {
return null;
}

spSSODescriptor.setWantAssertionsSigned(false);
spSSODescriptor.setAuthnRequestsSigned(false);

if (certificate != null) {

spSSODescriptor.setWantAssertionsSigned(true);
spSSODescriptor.setAuthnRequestsSigned(true);

X509KeyInfoGeneratorFactory keyInfoGeneratorFactory = new X509KeyInfoGeneratorFactory();
keyInfoGeneratorFactory.setEmitEntityCertificate(true);
KeyInfoGenerator keyInfoGenerator = keyInfoGeneratorFactory.newInstance();

KeyDescriptor encKeyDescriptor = createSAMLObject(KeyDescriptor.class);
if (encKeyDescriptor == null) {
return null;
}

encKeyDescriptor.setUse(UsageType.ENCRYPTION);

Credential credential = new BasicX509Credential(certificate);

try {
encKeyDescriptor.setKeyInfo(keyInfoGenerator.generate(credential));
}
catch (Exception e) {
logger.error("Error while creating credentials", e);
}
spSSODescriptor.getKeyDescriptors().add(encKeyDescriptor);

KeyDescriptor signKeyDescriptor = createSAMLObject(KeyDescriptor.class);
if (signKeyDescriptor == null) {
return null;
}

signKeyDescriptor.setUse(UsageType.SIGNING); // Set usage

try {
signKeyDescriptor.setKeyInfo(keyInfoGenerator.generate(credential));
}
catch (SecurityException e) {
logger.error("Error while creating credentials", e);
}
spSSODescriptor.getKeyDescriptors().add(signKeyDescriptor);
}

SingleLogoutService singleLogoutService = createSAMLObject(SingleLogoutService.class);
if (singleLogoutService == null) {
return null;
}
singleLogoutService.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI);
singleLogoutService.setLocation(singleLogoutServiceURL);
spSSODescriptor.getSingleLogoutServices().add(singleLogoutService);

NameIDFormat nameIDFormat = createSAMLObject(NameIDFormat.class);
if (nameIDFormat == null) {
return null;
}

nameIDFormat.setFormat("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified");
spSSODescriptor.getNameIDFormats().add(nameIDFormat);

AssertionConsumerService assertionConsumerService = createSAMLObject(AssertionConsumerService.class);
if (assertionConsumerService == null) {
return null;
}
assertionConsumerService.setIndex(1);
assertionConsumerService.setBinding(SAMLConstants.SAML2_POST_BINDING_URI);

assertionConsumerService.setLocation(assertionConsumerServiceURL);
spSSODescriptor.getAssertionConsumerServices().add(assertionConsumerService);

spSSODescriptor.addSupportedProtocol(SAMLConstants.SAML20P_NS);

spEntityDescriptor.getRoleDescriptors().add(spSSODescriptor);

DocumentBuilder builder;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);

builder = factory.newDocumentBuilder();
Document document = builder.newDocument();
Marshaller out = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(spEntityDescriptor);
out.marshall(spEntityDescriptor, document);

TransformerFactory transformerfactory = TransformerFactory.newInstance();
transformerfactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
Transformer transformer = transformerfactory.newTransformer();
StringWriter stringWriter = new StringWriter();
StreamResult streamResult = new StreamResult(stringWriter);
DOMSource source = new DOMSource(document);
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
transformer.transform(source, streamResult);
stringWriter.close();

return stringWriter.toString();
}
catch (Exception e) {
logger.error("Error while generation SP metadata", e);
return null;
}

}

public static <T> T createSAMLObject(final Class<T> clazz) {
XMLObjectBuilderFactory builderFactory = XMLObjectProviderRegistrySupport.getBuilderFactory();

QName defaultElementName = null;
try {
defaultElementName = (QName) clazz.getDeclaredField("DEFAULT_ELEMENT_NAME").get(null);
}
catch (Exception e) {
logger.error("Error while creating SAML object", e);
return null;
}
T object = (T) builderFactory.getBuilder(defaultElementName).buildObject(defaultElementName);

return object;
}
private static final Logger logger = LoggerFactory.getLogger(SamlClient.class);

public static String generateSpMetadata(
String entityId, String assertionConsumerServiceURL, String logoutServiceURL) {
return generateSpMetadata(entityId, assertionConsumerServiceURL, logoutServiceURL, null);
}

public static String generateSpMetadata(
String entityId,
String assertionConsumerServiceURL,
String singleLogoutServiceURL,
X509Certificate certificate) {
try {
InitializationService.initialize();

EntityDescriptor spEntityDescriptor = createSAMLObject(EntityDescriptor.class);
if (spEntityDescriptor == null) {
return null;
}
spEntityDescriptor.setEntityID(entityId);
SPSSODescriptor spSSODescriptor = createSAMLObject(SPSSODescriptor.class);
if (spSSODescriptor == null) {
return null;
}

spSSODescriptor.setWantAssertionsSigned(false);
spSSODescriptor.setAuthnRequestsSigned(false);

if (certificate != null) {

spSSODescriptor.setWantAssertionsSigned(true);
spSSODescriptor.setAuthnRequestsSigned(true);

X509KeyInfoGeneratorFactory keyInfoGeneratorFactory = new X509KeyInfoGeneratorFactory();
keyInfoGeneratorFactory.setEmitEntityCertificate(true);
KeyInfoGenerator keyInfoGenerator = keyInfoGeneratorFactory.newInstance();

KeyDescriptor encKeyDescriptor = createSAMLObject(KeyDescriptor.class);
if (encKeyDescriptor == null) {
return null;
}

encKeyDescriptor.setUse(UsageType.ENCRYPTION);

Credential credential = new BasicX509Credential(certificate);

try {
encKeyDescriptor.setKeyInfo(keyInfoGenerator.generate(credential));
} catch (Exception e) {
logger.error("Error while creating credentials", e);
}
spSSODescriptor.getKeyDescriptors().add(encKeyDescriptor);

KeyDescriptor signKeyDescriptor = createSAMLObject(KeyDescriptor.class);
if (signKeyDescriptor == null) {
return null;
}

signKeyDescriptor.setUse(UsageType.SIGNING); // Set usage

try {
signKeyDescriptor.setKeyInfo(keyInfoGenerator.generate(credential));
} catch (SecurityException e) {
logger.error("Error while creating credentials", e);
}
spSSODescriptor.getKeyDescriptors().add(signKeyDescriptor);
}

SingleLogoutService singleLogoutService = createSAMLObject(SingleLogoutService.class);
if (singleLogoutService == null) {
return null;
}
singleLogoutService.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI);
singleLogoutService.setLocation(singleLogoutServiceURL);
spSSODescriptor.getSingleLogoutServices().add(singleLogoutService);

NameIDFormat nameIDFormat = createSAMLObject(NameIDFormat.class);
if (nameIDFormat == null) {
return null;
}

nameIDFormat.setFormat("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified");
spSSODescriptor.getNameIDFormats().add(nameIDFormat);

AssertionConsumerService assertionConsumerService =
createSAMLObject(AssertionConsumerService.class);
if (assertionConsumerService == null) {
return null;
}
assertionConsumerService.setIndex(1);
assertionConsumerService.setBinding(SAMLConstants.SAML2_POST_BINDING_URI);

assertionConsumerService.setLocation(assertionConsumerServiceURL);
spSSODescriptor.getAssertionConsumerServices().add(assertionConsumerService);

spSSODescriptor.addSupportedProtocol(SAMLConstants.SAML20P_NS);

spEntityDescriptor.getRoleDescriptors().add(spSSODescriptor);

DocumentBuilder builder;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);

builder = factory.newDocumentBuilder();
Document document = builder.newDocument();
Marshaller out =
XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(spEntityDescriptor);
out.marshall(spEntityDescriptor, document);

TransformerFactory transformerfactory = TransformerFactory.newInstance();
transformerfactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
Transformer transformer = transformerfactory.newTransformer();
StringWriter stringWriter = new StringWriter();
StreamResult streamResult = new StreamResult(stringWriter);
DOMSource source = new DOMSource(document);
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
transformer.transform(source, streamResult);
stringWriter.close();

return stringWriter.toString();
} catch (Exception e) {
logger.error("Error while generation SP metadata", e);
return null;
}
}

public static <T> T createSAMLObject(final Class<T> clazz) {
XMLObjectBuilderFactory builderFactory = XMLObjectProviderRegistrySupport.getBuilderFactory();

QName defaultElementName = null;
try {
defaultElementName = (QName) clazz.getDeclaredField("DEFAULT_ELEMENT_NAME").get(null);
} catch (Exception e) {
logger.error("Error while creating SAML object", e);
return null;
}
T object = (T) builderFactory.getBuilder(defaultElementName).buildObject(defaultElementName);

return object;
}
}
Loading