Uploaded image for project: 'OpenAM'
  1. OpenAM
  2. OPENAM-14500

SAML SP-initiated SSO without existing SSO Session - value of 'goto' parameter not URLencoded

    XMLWordPrintable

    Details

    • Bug
    • Status: Open
    • Major
    • Resolution: Unresolved
    • 13.0.0, 13.5.0, 13.5.1, 13.5.2, 14.0.0, 14.1.0, 14.1.1, 14.5.0, 14.5.1, 5.5.1, 6.0.0, 6.0.0.1, 6.0.0.2, 6.0.0.3, 6.0.0.4, 6.0.0.5, 6.5.0, 6.0.0.6, 6.5.0.1, 7.0.0
    • None
    • SAML
    • Oracle JDK jdk1.8.0_201
      Apache Tomcat/9.0.8
      AM 7.0.0 (c36edcc20aab37e8bc86e092e0552951ba0cc6a5)
    • Rank:
      1|hzxp0n:

      Description

      Bug description

      When performing SP-initiated SSO without exising SSOSession, the value of the 'goto' parameter as part of the redirect at the IdP to the authentication URL is not URL encoded. This causes issues with single page applications

      How to reproduce the issue

      1. Configure AM using amster (e.g. install-openam --adminPwd SOME_PASSWORD --acceptLicense --cfgDir /var/AM-Deployments/amMaster --serverUrl http://amMaster.test.xyz:8080/am
      2. create sub-realm
      3. register hosted IdP in sub-realm and CoT
      4. configure external Auth URL for IdP (IdP -> Assertion Processing -> Local Configuration -> Auth URL)
      5. register remote SP in that realm / CoT
      6. Perform SP-iniated SSO
      Expected behaviour
      value of 'goto' parameter in the value of the Location response header value should be URL encoded when the IdP redirects for authentication.
      
      Current behaviour

      value of 'goto' parameter is not URL encoded

      excerpt from network trace - request
      Frame 37: 1234 bytes on wire (9872 bits), 1234 bytes captured (9872 bits)
      Null/Loopback
      Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
      Transmission Control Protocol, Src Port: 51929, Dst Port: 8080, Seq: 1, Ack: 1, Len: 1178
      Hypertext Transfer Protocol
           [truncated]GET /am/SSORedirect/metaAlias/sub1/idp?SAMLRequest=....
          Host: ammaster.test.xyz:8080\r\n
      ....
      
      excerpt from network trace - response
      Frame 7: 649 bytes on wire (5192 bits), 649 bytes captured (5192 bits)
      Null/Loopback
      Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
      Transmission Control Protocol, Src Port: 8080, Dst Port: 52389, Seq: 1, Ack: 1179, Len: 593
      Hypertext Transfer Protocol
          HTTP/1.1 302 \r\n
          X-Frame-Options: SAMEORIGIN\r\n
          Set-Cookie: JSESSIONID=9B8F36E483B104F3F5529CA56CD33233; Path=/am; HttpOnly\r\n
           [truncated]Location: http://ammaster.test.xyz:8080/login?spEntityID=test-sp&goto=http://ammaster.test.xyz:8080/am/SSORedirect/metaAlias/sub1/idp?ReqID%3Ds22ce6d229760383f3eab010cbadabf2762a9b44a2%26index%3Dnull%26acsURL%3Dhttp://pr
      ....
      

      Code analysis

      org.forgerock.openam.saml2.UtilProxySAMLAuthenticator.java
          private static void redirectAuthentication(HttpServletRequest request, HttpServletResponse response,
                                                     IDPAuthnContextInfo info, IDPSSOFederateRequest data,
                                                     boolean isSessionUpgrade)
                  throws SAML2Exception, IOException {
      
              String classMethod = "UtilProxySAMLAuthenticator.redirectAuthentication: ";
              // get the authentication service url
              String authService = IDPSSOUtil.getAuthenticationServiceURL(data.getRealm(), data.getIdpEntityID(), request);
              StringBuilder appliRootUrl = getAppliRootUrl(request);
              boolean forward;
              StringBuffer newURL;
      
              // build newUrl to auth service and test if redirect or forward
              if (FSUtils.isSameContainer(request, authService)) {
                  forward = true;
                  String relativePath = getRelativePath(authService, appliRootUrl.toString());
                  // in this case continue to forward to SSORedirect after login
                  newURL = new StringBuffer(relativePath).append("&forward=true");
              } else {
                  // cannot forward so redirect
                  forward = false;
                  newURL = new StringBuffer(authService);
              }
      
              // Pass spEntityID to IdP Auth Module
              if (data.getSpEntityID() != null) {
                  if (newURL.indexOf("?") == -1) {
                      newURL.append("?");
                  } else {
                      newURL.append("&");
                  }
      
                  newURL.append(SAML2Constants.SPENTITYID)
                        .append("=")
                        .append(urlEncodeQueryParameterNameOrValue(data.getSpEntityID()));
              }
      
              Set<String> authnTypeAndValues = info.getAuthnTypeAndValues();
              if (CollectionUtils.isNotEmpty(authnTypeAndValues)) {
                  boolean isFirst = true;
                  StringBuilder authSB = new StringBuilder();
      
                  for (String authnTypeAndValue : authnTypeAndValues) {
                      int index = authnTypeAndValue.indexOf("=");
                      if (index != -1) {
                          if (isFirst) {
                              isFirst = false;
                          } else {
                              authSB.append("&");
                          }
                          authSB.append(authnTypeAndValue.substring(0, index + 1))
                                .append(urlEncodeQueryParameterNameOrValue(authnTypeAndValue.substring(index + 1)));
                      }
                  }
      
                  if (newURL.indexOf("?") == -1) {
                      newURL.append("?");
                  } else {
                      newURL.append("&");
                  }
      
                  newURL.append(authSB.toString());
      
                  logger.debug("{} authString= {}", classMethod, authSB.toString());
              }
      
              if (newURL.indexOf("?") == -1) {
                  if (isSessionUpgrade) {
                      newURL.append("?ForceAuth=true&goto=");
                  } else {
                      newURL.append("?goto=");
                  }
      
              } else {
                  if (isSessionUpgrade) {
                      newURL.append("&ForceAuth=true");
                  }
                  newURL.append("&goto=");
              }
      
              // compute gotoURL differently in case of forward or in case
              // of redirection, forward needs a relative URI.
              StringBuffer gotoURL;
              if (forward) {
                  gotoURL = new StringBuffer(getRelativePath(request.getRequestURI(), request.getContextPath()));
              } else {
                  String rpUrl = IDPSSOUtil.getAttributeValueFromIDPSSOConfig(data.getRealm(),
                          data.getIdpEntityID(), SAML2Constants.RP_URL);
                  if (StringUtils.isNotEmpty(rpUrl)) {
                      gotoURL = new StringBuffer(rpUrl);
                      gotoURL.append(getRelativePath(request.getRequestURI(), request.getContextPath()));
                  } else {
                      gotoURL = request.getRequestURL();
                  }
              }
      
              //adding these extra parameters will ensure that we can send back SAML error response to the SP even when the
              //originally received AuthnRequest gets lost.
              gotoURL.append("?ReqID=").append(data.getAuthnRequest().getID()).append('&')
                      .append(INDEX).append('=').append(data.getAuthnRequest().getAssertionConsumerServiceIndex()).append('&')
                      .append(ACS_URL).append('=')
                      .append(urlEncodeQueryParameterNameOrValue(data.getAuthnRequest().getAssertionConsumerServiceURL()))
                      .append('&')
                      .append(SP_ENTITY_ID).append('=')
                      .append(urlEncodeQueryParameterNameOrValue(data.getAuthnRequest().getIssuer().getValue())).append('&')
                      .append(BINDING).append('=')
                      .append(urlEncodeQueryParameterNameOrValue(data.getAuthnRequest().getProtocolBinding()));
      
              newURL.append(urlEncodeQueryParameterNameOrValue(gotoURL.toString()));
      
      ...
      

        Attachments

          Activity

            People

            Unassigned Unassigned
            bthalmayr Bernhard Thalmayr
            Votes:
            0 Vote for this issue
            Watchers:
            6 Start watching this issue

              Dates

              Created:
              Updated: