Search code examples
amazon-web-servicesspring-bootsingle-sign-onsamlspring-saml

Spring Boot SAML using AWS SSO as IdP errors with Bad Input


I want to build a site hosted with Spring Boot and I would like to use AWS SSO as the SAML identity provider for authentication. I have built a PoC application and tried to follow AWS configuration instructions and the Spring SAML examples I could find, but when I browse to my site (on localhost), AWS SSO successfully opens but then fails with "Bad Input".

In my PoC application (which only has code for the authentication and an index page) I have:

  • added the spring-security-saml2-service-provider dependency (Spring Boot v2.6.2)
  • set up a Custom Application in AWS SSO
  • generated a private key and a self-signed certificate
  • configured my application properties as follows:
spring:
  security:
    saml2:
      relyingparty:
        registration:
          metadata:
            signing.credentials:
                - private-key-location: classpath:key.pem
                  certificate-location: classpath:cert.pem
            identityprovider:
              metadata-uri: https://portal.sso.eu-west-1.amazonaws.com/saml/metadata/my-custom-applications-metadata-uri
  • and my configuration file has these beans:
@Bean
    RelyingPartyRegistrationResolver relyingPartyRegistrationResolver(
            RelyingPartyRegistrationRepository registrations) {
        return new DefaultRelyingPartyRegistrationResolver((id) -> registrations.findByRegistrationId("metadata"));
    }

    @Bean
    FilterRegistrationBean<Saml2MetadataFilter> metadata(RelyingPartyRegistrationResolver registrations) {
        Saml2MetadataFilter metadata = new Saml2MetadataFilter(registrations, new OpenSamlMetadataResolver());
        FilterRegistrationBean<Saml2MetadataFilter> filter = new FilterRegistrationBean<>(metadata);
        filter.setOrder(-101);
        return filter;
    }
  • then, I have downloaded the metadata from my spring boot application (via http://localhost:8080/saml2/service-provider-metadata/metadata) and entered it into the AWS custom application config. This set the ACS to http://localhost:8080/login/saml2/sso/metadata and audience to http://localhost:8080/saml2/service-provider-metadata/metadata which seems correct. My Application start URL and Relay States are blank. In the Attributes Mapping I have set Subject to ${user.email} with format emailAddress.

I have used the SAML-tracer plugin to try to figure out what happens when I make a request to localhost:8080/. We end up making a SAML POST (see below) to AWS, AWS reroutes to GET https://portal.sso.eu-west-1.amazonaws.com/token/whoAmI which succeeds, and then finally reroutes to GET https://portal.sso.eu-west-1.amazonaws.com/saml/v2/assertion/very-long-id which fails with 400 Bad Request which lands us on a page in AWS SSO where something went wrong with the message {message: "Bad input", __type: "com.amazonaws.switchboard.portal#InvalidRequestException"}. I tried checking CloudTrail to find more info but it just logs the events AssumeRoleWithSAML followed by ConsoleLogin which both succeed. This is the SAML data as displayed by SAML-tracer which my application POSTs to AWS (replaced info that may be sensitive):

<saml2p:AuthnRequest xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"
                     AssertionConsumerServiceURL="http://localhost:8080/login/saml2/sso/metadata"
                     Destination="https://portal.sso.eu-west-1.amazonaws.com/saml/assertion/insert-long-id"
                     ID="a-long-id"
                     IssueInstant="2022-01-10T13:59:52.963Z"
                     ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
                     Version="2.0"
                     >
    <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">http://localhost:8080/saml2/service-provider-metadata/metadata</saml2:Issuer>
    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <ds:SignedInfo>
            <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
            <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
            <ds:Reference URI="#Anot-sure-if-secret-random-characters">
                <ds:Transforms>
                    <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
                    <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                </ds:Transforms>
                <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
                <ds:DigestValue>some-more-characters</ds:DigestValue>
            </ds:Reference>
        </ds:SignedInfo>
        <ds:SignatureValue>a-long-base-64-encoded-block-of-characters</ds:SignatureValue>
        <ds:KeyInfo>
            <ds:X509Data>
                <ds:X509Certificate>a-long-encoded-certificate</ds:X509Certificate>
            </ds:X509Data>
        </ds:KeyInfo>
    </ds:Signature>
</saml2p:AuthnRequest>

I have tried to find more information on how AWS SSO expects the data to be sent or what "Bad Input" actually means, but it has been really difficult to find. I have also experimented a bit with trying to send the request without signing with my priv key/cert but that didn't seem to get me anywhere. I am suspecting that either the key/cert process is wrong or that I have not set up AWS correctly, but I am not sure how to proceed. Does anyone have any ideas?


Solution

  • The error was that the IdP certificate wasn't added to the spring application.

    To do this, download the AWS certificate in AWS SSO when configuring your custom application, then put that certificate at src/main/resources/credentials/idp-certificate.crt.

    Everything worked well after this step, including using localhost ACS URLs.

    I wrote a short tutorial on how to do it at https://maxk.se/posts/aws-sso-spring-boot/