How To

How are SAML2 tokens stored in the CTS in AM (All versions)?

Last updated Jul 22, 2020

The purpose of this article is to provide information on how SAML2 tokens are stored in the CTS in AM. Importantly, SAML2 tokens are only stored during SAML failover. This article also includes example token formats and details on performing LDAP searches to retrieve token details from the CTS.


Overview

SAML2 tokens are only saved to the CTS when SAML2 failover is enabled. See SAML v2.0 Guide › Configuring SAML v2.0 Providers for Failover for further information. 

Depending on your setup, other SAML2 tokens may be saved:

  • Assertion - the assertion is saved to the CTS when the Assertion Cache is enabled for the IdP. See SAML v2.0 Guide › Assertion Cache for further information.
  • AuthnRequest - the AuthnRequest is saved as follows depending on whether AM is the IdP or SP:
    • IdP: the AuthnRequest is saved in the server’s cache (not the CTS), which means it cannot be recovered by another server. 
    • SP: the AuthnRequest is saved in the cache and the CTS.

The coreTokenObject can be either JSON or a base64 encoded string.

LDAP searches

You can query the CTS using ldapsearch. For example, if you want to list SAML2 tokens, you would filter on coreTokenType=SAML2:

$ ./ldapsearch --hostname ds1.example.com --port 1389 --bindDN "cn=Directory Manager" --bindPassword password --baseDN "ou=famrecords,ou=openam-session,ou=tokens,dc=openam,dc=forgerock,dc=org" coreTokenType=SAML2

Example SAML2 Token

When SAML2 failover is enabled, SAML2 tokens are saved to the CTS, which makes them accessible to other servers.

An example token looks like this:

dn: coreTokenId=733237633231656432303961383835626662623039343434653564666532323964366632376466343032,ou=famrecords,ou=openam-session,ou=tokens,dc=openam,dc=forgerock,dc=org
objectClass: top
objectClass: frCoreToken
coreTokenId: 733237633231656432303961383835626662623039343434653564666532323964366632376466343032
coreTokenType: SAML2
coreTokenExpirationDate: 20200617142726+0100
coreTokenObject:: eyJkb0xvZ291dEFsbCI6ZmFsc2UsIm1ldGFBbGlhcyI6Ii9pZHAiLCJuYW
1lSURhbmRTUHBhaXJzIjpbeyJuYW1lSUQiOnsiQGNsYXNzIjoiY29tLnN1bi5pZGVudGl0eS5zY
W1sMi5hc3NlcnRpb24uaW1wbC5OYW1lSURJbXBsIiwiZm9ybWF0IjoidXJuOm9hc2lzOm5hbWVz
OnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6cGVyc2lzdGVudCIsImlzTXV0YWJsZSI6dHJ1ZSw
ibmFtZVF1YWxpZmllciI6Imh0dHA6Ly9pZHAuZXhhbXBsZS5uZXQ6ODA4MC9vcGVuYW0iLCJzcE
5hbWVRdWFsaWZpZXIiOiJodHRwOi8vb3BlbmFtLmV4YW1wbGUuY29tOjE4MDgwL29wZW5hbSIsI
nNwUHJvdmlkZWRJRCI6bnVsbCwidmFsdWUiOiIreHh1dDFzcEJHSVZRYkswMkxscE1NSENLWWhX
In0sInNwRW50aXR5SUQiOiJodHRwOi8vb3BlbmFtLmV4YW1wbGUuY29tOjE4MDgwL29wZW5hbSJ
9XSwib3JpZ2luYXRpbmdMb2dvdXRSZXF1ZXN0QmluZGluZyI6bnVsbCwib3JpZ2luYXRpbmdMb2
dvdXRSZXF1ZXN0SUQiOm51bGwsIm9yaWdpbmF0aW5nTG9nb3V0U1BFbnRpdHlJRCI6bnVsbCwic
GVuZGluZ0xvZ291dFJlcXVlc3RJRCI6bnVsbCwic3NvVG9rZW5JRCI6IlVsTWNJblZVX0dVZ1hH
R203cE0wNEdodVh3by4qQUFKVFNRQUNNRE1BQWxOTEFCeHZXWE5TZG0xNFNXMVVLM1J6TlZCS1Y
xcHFORTgyWlRscWFqZzlBQVIwZVhCbEFBTkRWRk1BQWxNeEFBSXdNZy4uKiJ9
coreTokenString01: com.sun.identity.saml2.profile.IDPSessionCopy

If coreTokenObject is a string, you can decode it (for example, using base64decode.org) where this example decodes as follows:

{
   "doLogoutAll":false,
   "metaAlias":"/idp",
   "nameIDandSPpairs":[
      {
         "nameID":{
            "@class":"com.sun.identity.saml2.assertion.impl.NameIDImpl",
            "format":"urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
            "isMutable":true,
            "nameQualifier":"http://idp.example.net:8080/openam",
            "spNameQualifier":"http://openam.example.com:18080/openam",
            "spProvidedID":null,
            "value":"+xxut1spBGIVQbK02LlpMMHCKYhW"
         },
         "spEntityID":"http://openam.example.com:18080/openam"
      }
   ],
   "originatingLogoutRequestBinding":null,
   "originatingLogoutRequestID":null,
   "originatingLogoutSPEntityID":null,
   "pendingLogoutRequestID":null,
   "ssoTokenID":"UlMcInVU_GUgXGGm7pM04GhuXwo.*AAJTSQACMDMAAlNLABxvWXNSdm14SW1UK3RzNVBKV1pqNE82ZTlqajg9AAR0eXBlAANDVFMAAlMxAAIwMg..*"
}

If you use a non-default relay state parameter in the metadata, you will also get a token where the coreTokenObject equals the relay state parameter. 

Example Assertion Token

When SAML2 failover is enabled, the Assertion Cache is enabled and AM is acting as the IdP, assertions are saved to the CTS, which makes them accessible to other servers.

An example assertion looks like this:

dn: coreTokenId=4141514141465630674d52516d69643478435642777932316a714463507a5733566f62703738524a624b36523866755737303567545070624d44453d,ou=famrecords,ou=openam-session,ou=tokens,dc=openam,dc=forgerock,dc=org
control: 1.3.6.1.4.1.36733.2.1.5.1 false: bcb3efeb-14a9-47be-8716-9c18918322c8-19593/8
changetype: add
objectClass: frCoreToken
objectClass: top
coreTokenId: 4141514141465630674d52516d69643478435642777932316a714463507a5733566f62703738524a624b36523866755737303567545070624d44453d
coreTokenExpirationDate: 20200523081647Z
coreTokenType: SAML2
coreTokenObject: "<samlp:Response xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" ID=\"s2d254cb2c6567979aa293a25d1e0c2c185c976524\" Version=\"2.0\" IssueInstant=\"2020-06-22T11:17:47Z\" Destination=\"http://sp.example.com:8080/openam/Consumer/metaAlias/sp\"><saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">IdP</saml:Issuer><samlp:Status xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\">\n<samlp:StatusCode xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" Value=\"urn:oasis:names:tc:SAML:2.0:status:Success\">\n</samlp:StatusCode>\n</samlp:Status><saml:Assertion xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" Version=\"2.0\" ID=\"s2f4d9640d71d59c81f145d17cdb738c8ff4d9e5fc\" IssueInstant=\"2020-06-22T11:17:47Z\">\n<saml:Issuer>IdP</saml:Issuer><saml:Subject>\n<saml:NameID NameQualifier=\"IdP\" SPNameQualifier=\"SP\" Format=\"urn:oasis:names:tc:SAML:2.0:nameid-format:persistent\">L+OjhuzCtalCRDSox+F3eMcjxjt2</saml:NameID><saml:SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\">\n<saml:SubjectConfirmationData NotOnOrAfter=\"2020-06-22T11:27:47Z\" Recipient=\"http://sp.example.com:8080/openam/Consumer/metaAlias/sp\" ></saml:SubjectConfirmationData></saml:SubjectConfirmation>\n</saml:Subject><saml:Conditions NotBefore=\"2020-06-22T11:27:47Z\" NotOnOrAfter=\"2020-06-22T11:27:47Z\">\n<saml:AudienceRestriction>\n<saml:Audience>SP</saml:Audience>\n</saml:AudienceRestriction>\n</saml:Conditions>\n<saml:AuthnStatement AuthnInstant=\"2020-06-22T11:17:47Z\" SessionIndex=\"s251a8cdd305404bdf8a4d493860732c2f75842f01\"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement></saml:Assertion>\n</samlp:Response>"
coreTokenString01: java.lang.String

Example AuthnRequest Token

When SAML2 failover is enabled and AM is acting as the SP, AuthnRequests are saved to the CTS, which makes them accessible to other servers.

An example AuthnRequest looks like this:

dn: coreTokenId=733230323466363833626637636133316239333932316532616263653035616164656531323931613964,ou=famrecords,ou=openam-session,ou=tokens,dc=openam,dc=forgerock,dc=org
objectClass: frCoreToken
objectClass: top
coreTokenExpirationDate: 20200622180136+0100
coreTokenId: 733230323466363833626637636133316239333932316532616263653035616164656531323931613964
coreTokenObject: {"authnRequest":"<samlp:AuthnRequest  xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" ID=\"s2024f683bf7ca31b93921e2abce05aadee1291a9d\" Version=\"2.0\" IssueInstant=\"2020-06-22T16:51:36Z\" Destination=\"http://idp.example.net:8080/openam/SSORedirect/metaAlias/idp\" ForceAuthn=\"false\" IsPassive=\"false\" ProtocolBinding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" AssertionConsumerServiceURL=\"http://openam.example.com:18080/openam/Consumer/metaAlias/sp\">\n<saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">http://openam.example.com:18080/openam</saml:Issuer>\n<samlp:NameIDPolicy  xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" Format=\"urn:oasis:names:tc:SAML:2.0:nameid-format:persistent\" SPNameQualifier=\"http://openam.example.com:18080/openam\" AllowCreate=\"true\"></samlp:NameIDPolicy>\n<samlp:RequestedAuthnContext xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" Comparison=\"exact\"><saml:AuthnContextClassRef xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></samlp:RequestedAuthnContext>\n</samlp:AuthnRequest>","idpEntityID":"http://idp.example.net:8080/openam","paramsMap":{"binding":["HTTP-POST"]},"realm":"/","relayState":null,"spEntityID":"http://openam.example.com:18080/openam"}
coreTokenString01: com.sun.identity.saml2.profile.AuthnRequestInfoCopy
coreTokenType: SAML2

See Also

Understanding CTS token types in AM/OpenAM

SAML Federation in AM/OpenAM

SAML v2.0 Guide

Related Training

N/A

Related Issue Tracker IDs

OPENAM-14995 (IdP Initiated single logout only performs local logout if IdP session cannot be found in cache)

OPENAM-1194 (Unable to get AuthnRequest error in multiserver setup)



Copyright and TrademarksCopyright © 2020 ForgeRock, all rights reserved.
Loading...