Two new configuration options for the Saml broker:
* wantAssertionsSigned: This will toggle the flag in the SP Metadata Descriptor, and validate the signature if and only if "Validate signature" is selected. * wantAssertionsEncrypted: This will simply require that the assertion is encrypted. Default behavior is unchanged. The signature validation uses the original XML, and supports therefore an IdP that adds whitespace and line breaks between tags (for example OpenAM).
This commit is contained in:
parent
75909a0add
commit
89c6cda2ac
9 changed files with 116 additions and 11 deletions
|
@ -23,10 +23,10 @@ package org.keycloak.saml;
|
||||||
*/
|
*/
|
||||||
public class SPMetadataDescriptor {
|
public class SPMetadataDescriptor {
|
||||||
|
|
||||||
public static String getSPDescriptor(String binding, String assertionEndpoint, String logoutEndpoint, boolean wantAuthnRequestsSigned, String entityId, String nameIDPolicyFormat, String signingCerts) {
|
public static String getSPDescriptor(String binding, String assertionEndpoint, String logoutEndpoint, boolean wantAuthnRequestsSigned, boolean wantAssertionsSigned, String entityId, String nameIDPolicyFormat, String signingCerts) {
|
||||||
String descriptor =
|
String descriptor =
|
||||||
"<EntityDescriptor xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\" entityID=\"" + entityId + "\">\n" +
|
"<EntityDescriptor xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\" entityID=\"" + entityId + "\">\n" +
|
||||||
" <SPSSODescriptor AuthnRequestsSigned=\"" + wantAuthnRequestsSigned + "\"\n" +
|
" <SPSSODescriptor AuthnRequestsSigned=\"" + wantAuthnRequestsSigned + "\" WantAssertionsSigned=\"" + wantAssertionsSigned + "\"\n" +
|
||||||
" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.1:protocol http://schemas.xmlsoap.org/ws/2003/07/secext\">\n";
|
" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.1:protocol http://schemas.xmlsoap.org/ws/2003/07/secext\">\n";
|
||||||
if (wantAuthnRequestsSigned && signingCerts != null) {
|
if (wantAuthnRequestsSigned && signingCerts != null) {
|
||||||
descriptor += signingCerts;
|
descriptor += signingCerts;
|
||||||
|
|
|
@ -32,6 +32,7 @@ import org.keycloak.dom.saml.v2.assertion.StatementAbstractType;
|
||||||
import org.keycloak.dom.saml.v2.assertion.SubjectType;
|
import org.keycloak.dom.saml.v2.assertion.SubjectType;
|
||||||
import org.keycloak.dom.saml.v2.assertion.SubjectType.STSubType;
|
import org.keycloak.dom.saml.v2.assertion.SubjectType.STSubType;
|
||||||
import org.keycloak.dom.saml.v2.protocol.ResponseType;
|
import org.keycloak.dom.saml.v2.protocol.ResponseType;
|
||||||
|
import org.keycloak.rotation.KeyLocator;
|
||||||
import org.keycloak.saml.common.ErrorCodes;
|
import org.keycloak.saml.common.ErrorCodes;
|
||||||
import org.keycloak.saml.common.PicketLinkLogger;
|
import org.keycloak.saml.common.PicketLinkLogger;
|
||||||
import org.keycloak.saml.common.PicketLinkLoggerFactory;
|
import org.keycloak.saml.common.PicketLinkLoggerFactory;
|
||||||
|
@ -41,7 +42,6 @@ import org.keycloak.saml.common.exceptions.ParsingException;
|
||||||
import org.keycloak.saml.common.exceptions.ProcessingException;
|
import org.keycloak.saml.common.exceptions.ProcessingException;
|
||||||
import org.keycloak.saml.common.exceptions.fed.IssueInstantMissingException;
|
import org.keycloak.saml.common.exceptions.fed.IssueInstantMissingException;
|
||||||
import org.keycloak.saml.common.util.DocumentUtil;
|
import org.keycloak.saml.common.util.DocumentUtil;
|
||||||
import org.keycloak.saml.common.util.StaxParserUtil;
|
|
||||||
import org.keycloak.saml.common.util.StaxUtil;
|
import org.keycloak.saml.common.util.StaxUtil;
|
||||||
import org.keycloak.saml.processing.api.saml.v2.response.SAML2Response;
|
import org.keycloak.saml.processing.api.saml.v2.response.SAML2Response;
|
||||||
import org.keycloak.saml.processing.api.saml.v2.sig.SAML2Signature;
|
import org.keycloak.saml.processing.api.saml.v2.sig.SAML2Signature;
|
||||||
|
@ -287,6 +287,22 @@ public class AssertionUtil {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an assertion element, validate the signature.
|
||||||
|
*/
|
||||||
|
public static boolean isSignatureValid(Element assertionElement, KeyLocator keyLocator) {
|
||||||
|
try {
|
||||||
|
Document doc = DocumentUtil.createDocument();
|
||||||
|
Node n = doc.importNode(assertionElement, true);
|
||||||
|
doc.appendChild(n);
|
||||||
|
|
||||||
|
return new SAML2Signature().validate(doc, keyLocator);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.signatureAssertionValidationError(e);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether the assertion has expired
|
* Check whether the assertion has expired
|
||||||
*
|
*
|
||||||
|
@ -541,7 +557,23 @@ public class AssertionUtil {
|
||||||
return responseType.getAssertions().get(0).getAssertion();
|
return responseType.getAssertions().get(0).getAssertion();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ResponseType decryptAssertion(ResponseType responseType, PrivateKey privateKey) throws ParsingException, ProcessingException, ConfigurationException {
|
public static boolean isAssertionEncrypted(ResponseType responseType) throws ProcessingException {
|
||||||
|
List<ResponseType.RTChoiceType> assertions = responseType.getAssertions();
|
||||||
|
|
||||||
|
if (assertions.isEmpty()) {
|
||||||
|
throw new ProcessingException("No assertion from response.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ResponseType.RTChoiceType rtChoiceType = assertions.get(0);
|
||||||
|
return rtChoiceType.getEncryptedAssertion() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method modifies the given responseType, and replaces the encrypted assertion with a decrypted version.
|
||||||
|
*
|
||||||
|
* It returns the assertion element as it was decrypted. This can be used in sginature verification.
|
||||||
|
*/
|
||||||
|
public static Element decryptAssertion(ResponseType responseType, PrivateKey privateKey) throws ParsingException, ProcessingException, ConfigurationException {
|
||||||
SAML2Response saml2Response = new SAML2Response();
|
SAML2Response saml2Response = new SAML2Response();
|
||||||
|
|
||||||
Document doc = saml2Response.convert(responseType);
|
Document doc = saml2Response.convert(responseType);
|
||||||
|
@ -560,11 +592,11 @@ public class AssertionUtil {
|
||||||
SAMLParser parser = new SAMLParser();
|
SAMLParser parser = new SAMLParser();
|
||||||
|
|
||||||
JAXPValidationUtil.checkSchemaValidation(decryptedDocumentElement);
|
JAXPValidationUtil.checkSchemaValidation(decryptedDocumentElement);
|
||||||
AssertionType assertion = (AssertionType) parser.parse(StaxParserUtil.getXMLEventReader(DocumentUtil
|
AssertionType assertion = (AssertionType) parser.parse(parser.createEventReader(DocumentUtil
|
||||||
.getNodeAsStream(decryptedDocumentElement)));
|
.getNodeAsStream(decryptedDocumentElement)));
|
||||||
|
|
||||||
responseType.replaceAssertion(oldID, new ResponseType.RTChoiceType(assertion));
|
responseType.replaceAssertion(oldID, new ResponseType.RTChoiceType(assertion));
|
||||||
|
|
||||||
return responseType;
|
return decryptedDocumentElement;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -49,9 +49,12 @@ import org.keycloak.protocol.saml.SamlProtocolUtils;
|
||||||
import org.keycloak.saml.SAML2LogoutResponseBuilder;
|
import org.keycloak.saml.SAML2LogoutResponseBuilder;
|
||||||
import org.keycloak.saml.SAMLRequestParser;
|
import org.keycloak.saml.SAMLRequestParser;
|
||||||
import org.keycloak.saml.common.constants.GeneralConstants;
|
import org.keycloak.saml.common.constants.GeneralConstants;
|
||||||
|
import org.keycloak.saml.common.constants.JBossSAMLConstants;
|
||||||
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||||
import org.keycloak.saml.common.exceptions.ConfigurationException;
|
import org.keycloak.saml.common.exceptions.ConfigurationException;
|
||||||
import org.keycloak.saml.common.exceptions.ProcessingException;
|
import org.keycloak.saml.common.exceptions.ProcessingException;
|
||||||
|
import org.keycloak.saml.common.util.DocumentUtil;
|
||||||
|
import org.keycloak.saml.processing.api.saml.v2.sig.SAML2Signature;
|
||||||
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
|
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
|
||||||
import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants;
|
import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants;
|
||||||
import org.keycloak.saml.processing.core.saml.v2.util.AssertionUtil;
|
import org.keycloak.saml.processing.core.saml.v2.util.AssertionUtil;
|
||||||
|
@ -74,6 +77,7 @@ import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.UriBuilder;
|
import javax.ws.rs.core.UriBuilder;
|
||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
import javax.xml.namespace.QName;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
|
@ -83,6 +87,8 @@ import java.util.List;
|
||||||
import org.keycloak.rotation.HardcodedKeyLocator;
|
import org.keycloak.rotation.HardcodedKeyLocator;
|
||||||
import org.keycloak.rotation.KeyLocator;
|
import org.keycloak.rotation.KeyLocator;
|
||||||
import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator;
|
import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
@ -344,7 +350,38 @@ public class SAMLEndpoint {
|
||||||
if (responseType.getAssertions() == null || responseType.getAssertions().isEmpty()) {
|
if (responseType.getAssertions() == null || responseType.getAssertions().isEmpty()) {
|
||||||
return callback.error(relayState, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
return callback.error(relayState, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||||
}
|
}
|
||||||
AssertionType assertion = AssertionUtil.getAssertion(responseType, keys.getPrivateKey());
|
|
||||||
|
boolean assertionIsEncrypted = AssertionUtil.isAssertionEncrypted(responseType);
|
||||||
|
|
||||||
|
if (config.isWantAssertionsEncrypted() && !assertionIsEncrypted) {
|
||||||
|
logger.error("The assertion is not encrypted, which is required.");
|
||||||
|
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
|
||||||
|
event.error(Errors.INVALID_SAML_RESPONSE);
|
||||||
|
return ErrorPage.error(session, Messages.INVALID_REQUESTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
Element assertionElement;
|
||||||
|
|
||||||
|
if (assertionIsEncrypted) {
|
||||||
|
// This methods writes the parsed and decrypted assertion back on the responseType parameter:
|
||||||
|
assertionElement = AssertionUtil.decryptAssertion(responseType, keys.getPrivateKey());
|
||||||
|
} else {
|
||||||
|
/* We verify the assertion using original document to handle cases where the IdP
|
||||||
|
includes whitespace and/or newlines inside tags. */
|
||||||
|
assertionElement = DocumentUtil.getElement(holder.getSamlDocument(), new QName(JBossSAMLConstants.ASSERTION.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.isWantAssertionsSigned() && config.isValidateSignature()) {
|
||||||
|
if (!AssertionUtil.isSignatureValid(assertionElement, getIDPKeyLocator())) {
|
||||||
|
logger.error("validation failed");
|
||||||
|
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
|
||||||
|
event.error(Errors.INVALID_SIGNATURE);
|
||||||
|
return ErrorPage.error(session, Messages.INVALID_REQUESTER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AssertionType assertion = responseType.getAssertions().get(0).getAssertion();
|
||||||
|
|
||||||
SubjectType subject = assertion.getSubject();
|
SubjectType subject = assertion.getSubject();
|
||||||
SubjectType.STSubType subType = subject.getSubType();
|
SubjectType.STSubType subType = subject.getSubType();
|
||||||
NameIDType subjectNameID = (NameIDType) subType.getBaseID();
|
NameIDType subjectNameID = (NameIDType) subType.getBaseID();
|
||||||
|
|
|
@ -233,6 +233,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
||||||
|
|
||||||
|
|
||||||
boolean wantAuthnRequestsSigned = getConfig().isWantAuthnRequestsSigned();
|
boolean wantAuthnRequestsSigned = getConfig().isWantAuthnRequestsSigned();
|
||||||
|
boolean wantAssertionsSigned = getConfig().isWantAssertionsSigned();
|
||||||
String entityId = getEntityId(uriInfo, realm);
|
String entityId = getEntityId(uriInfo, realm);
|
||||||
String nameIDPolicyFormat = getConfig().getNameIDPolicyFormat();
|
String nameIDPolicyFormat = getConfig().getNameIDPolicyFormat();
|
||||||
|
|
||||||
|
@ -244,7 +245,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
||||||
for (RsaKeyMetadata key : keys) {
|
for (RsaKeyMetadata key : keys) {
|
||||||
addKeyInfo(keysString, key, KeyTypes.SIGNING.value());
|
addKeyInfo(keysString, key, KeyTypes.SIGNING.value());
|
||||||
}
|
}
|
||||||
String descriptor = SPMetadataDescriptor.getSPDescriptor(authnBinding, endpoint, endpoint, wantAuthnRequestsSigned, entityId, nameIDPolicyFormat, keysString.toString());
|
String descriptor = SPMetadataDescriptor.getSPDescriptor(authnBinding, endpoint, endpoint, wantAuthnRequestsSigned, wantAssertionsSigned, entityId, nameIDPolicyFormat, keysString.toString());
|
||||||
return Response.ok(descriptor, MediaType.APPLICATION_XML_TYPE).build();
|
return Response.ok(descriptor, MediaType.APPLICATION_XML_TYPE).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -121,6 +121,22 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
|
||||||
getConfig().put("wantAuthnRequestsSigned", String.valueOf(wantAuthnRequestsSigned));
|
getConfig().put("wantAuthnRequestsSigned", String.valueOf(wantAuthnRequestsSigned));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isWantAssertionsSigned() {
|
||||||
|
return Boolean.valueOf(getConfig().get("wantAssertionsSigned"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWantAssertionsSigned(boolean wantAssertionsSigned) {
|
||||||
|
getConfig().put("wantAssertionsSigned", String.valueOf(wantAssertionsSigned));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWantAssertionsEncrypted() {
|
||||||
|
return Boolean.valueOf(getConfig().get("wantAssertionsEncrypted"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWantAssertionsEncrypted(boolean wantAssertionsEncrypted) {
|
||||||
|
getConfig().put("wantAssertionsEncrypted", String.valueOf(wantAssertionsEncrypted));
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isAddExtensionsElementWithKeyInfo() {
|
public boolean isAddExtensionsElementWithKeyInfo() {
|
||||||
return Boolean.valueOf(getConfig().get("addExtensionsElementWithKeyInfo"));
|
return Boolean.valueOf(getConfig().get("addExtensionsElementWithKeyInfo"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,8 @@ public class SamlSPDescriptorClientInstallation implements ClientInstallationPro
|
||||||
String nameIdFormat = samlClient.getNameIDFormat();
|
String nameIdFormat = samlClient.getNameIDFormat();
|
||||||
if (nameIdFormat == null) nameIdFormat = SamlProtocol.SAML_DEFAULT_NAMEID_FORMAT;
|
if (nameIdFormat == null) nameIdFormat = SamlProtocol.SAML_DEFAULT_NAMEID_FORMAT;
|
||||||
String spCertificate = SPMetadataDescriptor.xmlKeyInfo(" ", null, samlClient.getClientSigningCertificate(), KeyTypes.SIGNING.value(), true);
|
String spCertificate = SPMetadataDescriptor.xmlKeyInfo(" ", null, samlClient.getClientSigningCertificate(), KeyTypes.SIGNING.value(), true);
|
||||||
return SPMetadataDescriptor.getSPDescriptor(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get(), assertionUrl, logoutUrl, samlClient.requiresClientSignature(), client.getClientId(), nameIdFormat, spCertificate);
|
return SPMetadataDescriptor.getSPDescriptor(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get(), assertionUrl, logoutUrl,
|
||||||
|
samlClient.requiresClientSignature(), samlClient.requiresAssertionSignature(), client.getClientId(), nameIdFormat, spCertificate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -78,7 +78,7 @@ public class ValidationTest {
|
||||||
public void testBrokerExportDescriptor() throws Exception {
|
public void testBrokerExportDescriptor() throws Exception {
|
||||||
URL schemaFile = getClass().getResource("/schema/saml/v2/saml-schema-metadata-2.0.xsd");
|
URL schemaFile = getClass().getResource("/schema/saml/v2/saml-schema-metadata-2.0.xsd");
|
||||||
Source xmlFile = new StreamSource(new ByteArrayInputStream(SPMetadataDescriptor.getSPDescriptor(
|
Source xmlFile = new StreamSource(new ByteArrayInputStream(SPMetadataDescriptor.getSPDescriptor(
|
||||||
"POST", "http://realm/assertion", "http://realm/logout", true, "test", SamlProtocol.SAML_DEFAULT_NAMEID_FORMAT, KeycloakModelUtils.generateKeyPairCertificate("test").getCertificate()
|
"POST", "http://realm/assertion", "http://realm/logout", true, false, "test", SamlProtocol.SAML_DEFAULT_NAMEID_FORMAT, KeycloakModelUtils.generateKeyPairCertificate("test").getCertificate()
|
||||||
).getBytes()), "SP Descriptor");
|
).getBytes()), "SP Descriptor");
|
||||||
SchemaFactory schemaFactory = SchemaFactory
|
SchemaFactory schemaFactory = SchemaFactory
|
||||||
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
||||||
|
|
|
@ -524,7 +524,11 @@ http-post-binding-response.tooltip=Indicates whether to respond to requests usin
|
||||||
http-post-binding-for-authn-request=HTTP-POST Binding for AuthnRequest
|
http-post-binding-for-authn-request=HTTP-POST Binding for AuthnRequest
|
||||||
http-post-binding-for-authn-request.tooltip=Indicates whether the AuthnRequest must be sent using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used.
|
http-post-binding-for-authn-request.tooltip=Indicates whether the AuthnRequest must be sent using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used.
|
||||||
want-authn-requests-signed=Want AuthnRequests Signed
|
want-authn-requests-signed=Want AuthnRequests Signed
|
||||||
want-authn-requests-signed.tooltip=Indicates whether the identity provider expects signed a AuthnRequest.
|
want-authn-requests-signed.tooltip=Indicates whether the identity provider expects a signed AuthnRequest.
|
||||||
|
want-assertions-signed=Want Assertions Signed
|
||||||
|
want-assertions-signed.tooltip=Indicates whether this service provider expects a signed Assertion.
|
||||||
|
want-assertions-encrypted=Want Assertions Encrypted
|
||||||
|
want-assertions-encrypted.tooltip=Indicates whether this service provider expects an encrypted Assertion.
|
||||||
force-authentication=Force Authentication
|
force-authentication=Force Authentication
|
||||||
identity-provider.force-authentication.tooltip=Indicates whether the identity provider must authenticate the presenter directly rather than rely on a previous security context.
|
identity-provider.force-authentication.tooltip=Indicates whether the identity provider must authenticate the presenter directly rather than rely on a previous security context.
|
||||||
validate-signature=Validate Signature
|
validate-signature=Validate Signature
|
||||||
|
|
|
@ -149,6 +149,20 @@
|
||||||
</div>
|
</div>
|
||||||
<kc-tooltip>{{:: 'want-authn-requests-signed.tooltip' | translate}}</kc-tooltip>
|
<kc-tooltip>{{:: 'want-authn-requests-signed.tooltip' | translate}}</kc-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-2 control-label" for="wantAssertionsSigned">{{:: 'want-assertions-signed' | translate}}</label>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input ng-model="identityProvider.config.wantAssertionsSigned" id="wantAssertionsSigned" name="wantAssertionsSigned" onoffswitchvalue on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
|
||||||
|
</div>
|
||||||
|
<kc-tooltip>{{:: 'want-assertions-signed.tooltip' | translate}}</kc-tooltip>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-2 control-label" for="wantAssertionsEncrypted">{{:: 'want-assertions-encrypted' | translate}}</label>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input ng-model="identityProvider.config.wantAssertionsEncrypted" id="wantAssertionsEncrypted" name="wantAssertionsEncrypted" onoffswitchvalue on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
|
||||||
|
</div>
|
||||||
|
<kc-tooltip>{{:: 'want-assertions-encrypted.tooltip' | translate}}</kc-tooltip>
|
||||||
|
</div>
|
||||||
<div class="form-group" data-ng-show="identityProvider.config.wantAuthnRequestsSigned == 'true'">
|
<div class="form-group" data-ng-show="identityProvider.config.wantAuthnRequestsSigned == 'true'">
|
||||||
<label class="col-md-2 control-label" for="signatureAlgorithm">{{:: 'signature-algorithm' | translate}}</label>
|
<label class="col-md-2 control-label" for="signatureAlgorithm">{{:: 'signature-algorithm' | translate}}</label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
|
|
Loading…
Reference in a new issue