How do I use the SAML policy to generate a SAML assertion with a custom template?

I have a SAML Assertion template that I want to use to generate a SAML assertion after I have validated the username and password. I don’t want to use the default assertion generated by Apigee. How do I use my template to dynamically generate the SAML assertion after the username and password are validated (assuming I use Apigee BaaS)?

My SAML Template is shown below. I'll post the answer shortly.

<?xml version="1.0" encoding="UTF-8"?>
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:enc="http://www.w3.org/2001/04/xmlenc#" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:x500="urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Destination="https://liveperson.net" ID="ppVee6mrzFP" IssueInstant="2017-06-06T19:50:58Z" Version="2.0">
   <saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://example.com</saml:Issuer>
   <samlp:Status>
      <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" />
   </samlp:Status>
   <saml:Assertion ID="id-gLNuoAc3" IssueInstant="2017-06-06T19:50:58Z" Version="2.0">
      <saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://example.com</saml:Issuer>
      <dsig:Signature>
         <dsig:SignedInfo>
            <dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
            <dsig:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
            <dsig:Reference URI="#id-gLNuFhD9M3">
               <dsig:Transforms>
                  <dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
                  <dsig:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
               </dsig:Transforms>
               <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
               <dsig:DigestValue>NEQc=</dsig:DigestValue>
            </dsig:Reference>
         </dsig:SignedInfo>
         <dsig:SignatureValue>XhjPzGkx</dsig:SignatureValue>
      </dsig:Signature>
      <saml:Subject>
         <saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">username</saml:NameID>
         <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
            <saml:SubjectConfirmationData NotOnOrAfter="2017-06-06T19:55:58Z" Recipient="https://liveperson.net/" />
         </saml:SubjectConfirmation>
      </saml:Subject>
      <saml:Conditions NotBefore="2017-06-06T19:50:58Z" NotOnOrAfter="2017-06-06T19:55:58Z">
         <saml:AudienceRestriction>
            <saml:Audience>LivePerson</saml:Audience>
         </saml:AudienceRestriction>
      </saml:Conditions>
      <saml:AuthnStatement AuthnInstant="2017-06-06T19:08:11Z" SessionIndex="id-sKouq-ydUXs" SessionNotOnOrAfter="2017-06-06T20:50:58Z">
         <saml:AuthnContext>
            <saml:AuthnContextClassRef>Scheme</saml:AuthnContextClassRef>
         </saml:AuthnContext>
      </saml:AuthnStatement>
      <saml:AttributeStatement>
         <saml:Attribute Name="siteId" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
            <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">22</saml:AttributeValue>
         </saml:Attribute>
         <saml:Attribute Name="loginName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
            <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">user@email.com</saml:AttributeValue>
         </saml:Attribute>
      </saml:AttributeStatement>
   </saml:Assertion>
</samlp:Response>
0 9 2,139
9 REPLIES 9

SAML uses a public/private key, so you should follow the steps here, which describe how to:

  • Create a public/private key
  • Create a keystore
  • Upload the keystore to Edge

Apigee BaaS

The next step is to create a user within Apigee BaaS in your Sandbox application. Use the Apigee BaaS sandbox application as a POC only, as it does not have the appropriate security for production applications. Use the UI to create the user.

In the top left corner of Apigee BaaS make sure that the “Sandbox” application is selected.

5136-screen-shot-2017-06-19-at-124957-pm.png

Click Users on the right side of the screen.

5137-screen-shot-2017-06-19-at-125111-pm.png

Click the “add new user” button.

5138-screen-shot-2017-06-19-at-125220-pm.png

The “Create New User” form will open. Enter the new user’s info here.

5139-screen-shot-2017-06-19-at-125309-pm.png

Generate SAML Policy Template

GenerateSAMLAssertion policy is shown below.

Notice the following:

  • The KeyStore element is included in the proxy and it is used to sign the assertion.
    • This is based on the Key store that you created and uploaded to Edge.
  • Subject element is used to store the username pulled from the request
  • Template element stores the Assertion format.
    • Any items that are surrounded by curly brackets {} are variable names and Edge will replace those with the flow variable values.
    • Anything that is prefixed with saml, such as {saml.issueInstant}, was a variable created by the GenerateSAMLAssertion policy.
<?xml version="1.0" encoding="UTF-8"?>
<GenerateSAMLAssertion ignoreContentType="false" name="GenerateSAML-Assertion2">
   <DisplayName>GenerateSAML-Assertion2</DisplayName>
   <KeyStore>
      <Name ref="reference">SAML</Name>
      <Alias ref="reference">demo.com</Alias>
   </KeyStore>
   <Subject ref="username" />
   <Issuer ref="issuer" />
   <Template ignoreUnresolvedVariables="false"><![CDATA[
<saml:Assertion xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"  xmlns:enc="http://www.w3.org/2001/04/xmlenc#" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"  xmlns:x500="urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  Destination="https://liveperson.net/"  ID="55555" IssueInstant="2017-06-06T19:50:58Z" Version="2.0">  
<saml:Subject>  
  <saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">{apigee.username}</saml:NameID>  
  <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">  <saml:SubjectConfirmationData NotOnOrAfter="{saml.issueInstant}"  Destination="https://liveperson.net"/>  
 </saml:SubjectConfirmation>  
</saml:Subject>  
<saml:Conditions NotBefore="{mysaml.now}" NotOnOrAfter="{mysaml.expiry}">  
  <saml:AudienceRestriction>  
    <saml:Audience>LivePerson</saml:Audience>  </saml:AudienceRestriction>  </saml:Conditions>  
<saml:AuthnStatement AuthnInstant="{mysaml.now}">  
  <saml:AuthnContext>  
  <saml:AuthnContextClassRef>{saml.authnContextClassRef}</saml:AuthnContextClassRef>  
  </saml:AuthnContext>  
</saml:AuthnStatement>  
<saml:AttributeStatement>  
  <saml:Attribute Name="siteId" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">  
  <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">7  
  </saml:AttributeValue>  
  </saml:Attribute>  
  <saml:Attribute Name="loginName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">  
  <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">  {apigee.mail}  </saml:AttributeValue>  
  </saml:Attribute>  </saml:AttributeStatement>  
</saml:Assertion>]]></Template>
   <OutputVariable>
      <FlowVariable name="assertion.content" />
   </OutputVariable>
</GenerateSAMLAssertion>

Generate SAML Policy Result

The result of the policy is shown below.

A couple of items to point out.

  • The <saml2:Issuer …> element was not included in the template, but it was added automatically to the assertion after the policy executed.
  • The <ds:Signature > element was added automatically to the assertion and it includes the 509 certificate that should be used to validate the assertion.
  • Notice the Conditions element was populated with the {mysaml.now} and {mysaml.expiry} time, which is implemented in a JavaScript policy to create the current timestamp and increment it by 1 hour.
<?xml version="1.0" encoding="UTF-8"?>
<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:enc="http://www.w3.org/2001/04/xmlenc#" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:x500="urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="_810fec639be5361041181654789e7bc7" IssueInstant="2017-06-19T16:21:07.310Z" Version="2.0">
   <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://yourdomain.com/oam/fed</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/2000/09/xmldsig#rsa-sha1" />
         <ds:Reference URI="#_89e7bc7">
            <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#">
                  <ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xs" />
               </ds:Transform>
            </ds:Transforms>
            <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
            <ds:DigestValue>9iBSrY=</ds:DigestValue>
         </ds:Reference>
      </ds:SignedInfo>
      <ds:SignatureValue>cbFUpfcSZg=</ds:SignatureValue>
      <ds:KeyInfo>
         <ds:X509Data>
            <ds:X509Certificate>MIICRgHBEZX9+k</ds:X509Certificate>
         </ds:X509Data>
      </ds:KeyInfo>
   </ds:Signature>
   <saml:Subject>
      <saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">username</saml:NameID>
      <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
         <saml:SubjectConfirmationData Destination="https://liveperson.net/" NotOnOrAfter="2017-06-19T16:21:07.310Z" />
      </saml:SubjectConfirmation>
   </saml:Subject>
   <saml:Conditions NotBefore="2017-06-19T16:21:07Z" NotOnOrAfter="2017-06-19T17:21:07Z">
      <saml:AudienceRestriction>
         <saml:Audience>LivePerson</saml:Audience>
      </saml:AudienceRestriction>
   </saml:Conditions>
   <saml:AuthnStatement AuthnInstant="2017-06-19T16:21:07Z">
      <saml:AuthnContext>
         <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
      </saml:AuthnContext>
   </saml:AuthnStatement>
   <saml:AttributeStatement>
      <saml:Attribute Name="siteId" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
         <saml:AttributeValue xsi:type="xs:string">97</saml:AttributeValue>
      </saml:Attribute>
      <saml:Attribute Name="loginName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
         <saml:AttributeValue xsi:type="xs:string">email@yourdomain.com</saml:AttributeValue>
      </saml:Attribute>
   </saml:AttributeStatement>
</saml:Assertion>

This code is implemented in the following repository.

The POST request to test is shown below.

curl -X POST \
  https://org-env.apigee.net/saml/generate-saml -H 'content-type: application/json' \
  -d '{
"username":"youruser",
"password":"password"
}'

saml-error.png

Hi @swilliams,

I installed your repo, (made a small pull request as well ;-)), but I am unable to solve an error.

First I changed the CURL command to include the content-type XML, because it complains about the media type. (This could also be resolved by setting: ignoreContentType="true" for that matter.)

But I seem to be running into the same error over and over again. (On the easiest request).

curl -X GET \
  https://ORG-ENV.apigee.net/saml/generate-saml \
  -H 'cache-control: no-cache' -H "Content-Type:application/xml"

I generated my own certificates and keystores, tried your sample key files (which are expired for about 2 months by the way).

(I also have the same issue on my own created GenerateSAMLAssertion).

The error is: "Error transforming assertion into message.".

Errorcode: "steps.saml.generate.ErrorUpdatingPayload".

Attached the Trace output, which also indicates: "premature end of file" and an "UncheckedException".

Does anybody got a clue what I might be doing wrong?

Hello @Gijs Zonneveld

Can you send me your GenerateSAMLAssertion policy? It sounds like there is an issue within the Template element.

@swilliams,

Thanks for your response.

I am using the one as unmodified as possible from your sample.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<GenerateSAMLAssertion ignoreContentType="false" name="GenerateSAML-Assertion">
    <DisplayName>GenerateSAML-Assertion</DisplayName>
    <KeyStore>
        <Name>keystorename</Name>
        <Alias>keystorealias</Alias>
        <!--use reference to refer to a flow variable instead of hardcoding.-->
        <!--Name ref="flow.variable"></Name-->
        <!--Alias ref="flow.variable"></Alias-->
    </KeyStore>
    <Subject ref="reference">1234</Subject>
    <Issuer ref="reference">samltest</Issuer>
    <!--use reference to refer to a flow variable instead of hardcoding.-->
    <!--Subject ref="flow.variable"></Subject-->
    <!--Issuer ref="flow.variable"></Issuer-->
    <Template ignoreUnresolvedVariables="false"/>
    <OutputVariable>
        <Message name="message">
            <Namespaces>
                <Namespace prefix="soapenv">http://schemas.xmlsoap.org/soap/envelope/</Namespace>
            </Namespaces>
            <XPath>/soapenv:Envelope/soapenv:Header</XPath>
        </Message>
    </OutputVariable>
</GenerateSAMLAssertion>

Am I overlooking something?

Thanks!


@Gijs Zonneveld

I updated my repo. I had a couple of copy and paste errors.

1) The GenerateSAMLAssertion output variable should be

<OutputVariable>
    <FlowVariable name="assertion.content"/>
</OutputVariable>

2) The proxies default.xml file should not include a route (see below).

  <RouteRule name="route">
    </RouteRule>

It should work after you make these changes as long as the keystore and alias that you created matches what is defined in the GenerateSAMLAssertion policy. Also, it should be deployed to the same environment where you created the keystore/alias.

<KeyStore>
        <Name>keystorename</Name>
        <Alias>keystorealias</Alias>
       
    </KeyStore>

@swilliam

Thanks for your great support! I now made my first SAML request, thanks for your help, really appreciated!

apigee-4
Participant II

Hi @swilliams,

Sorry to bother again and maybe a little off-topic.

I am now trying to define a template te create a signed Meta-Data Consumer request, containing some md:SPSSODescriptor elements.

I don't seem to get it working, by Apigee edge. Would you be able to help me out with a start?

Thanks in advance.


Hello @Gijs Zonneveld

Please create a new question in the community and include as much information as possible.

Is Apigee supposed to generate the SAML or validate it?

Provide a sample of the SAML request.

Provide info about the consumer of the SAML token.

What attributes need to be variables in the template?

The more information you provide about the context of your question will make it easier for someone to answer quickly.

apigee-4
Participant II

Thanks! @swilliams I created a new ticket, altered the question a little and added a sample request, defining more or less what I want to achive.

Related topic