LATEST VERSION: 1.6 - CHANGELOG
Single Sign-On v1.6

Common App Architecture Patterns

This topic describes common app architecture patterns for enterprise developers.

External Apps Calling into PCF APIs

This section describes common architecture patterns for authenticating external apps calling into Pivotal Cloud Foundry (PCF) APIs. In these patterns, external apps secured either by Security Assertion Markup Language (SAML), or by OpenID Connect (OIDC) providers using JSON Web Tokens (JWTs), interact with Pivotal SSO to gain access to PCF services.

The following diagram is a conceptual view of an external app protected by a SAML or OIDC enterprise identity provider (IDP).

external-app

In this diagram:

  • You have an external app running outside PCF.
  • The external app is secured by an enterprise SAML or OIDC identity provider, that is, users authenticate via the SAML or OIDC provider into the external app.
  • The external app needs to invoke API-1 and API-2, which are Spring Boot microservices running on PCF.
  • API-1 and API-2 are protected by the SSO service using OAuth.

The following sections describe three authentication models that can be used for external apps using SAML or OIDC enterprise IDPs to call into PCF APIs:

UAA Authorization Code Grant — Browser

The following sequence diagram illustrates the UAA authorization code grant model. This diagram shows a SAML flow, but the interactions between the app, enterprise IDP, and UAA can also use an OIDC enterprise IDP. In that case, JWT ID tokens replace SAML and SAML assertions.

code-grant

In this model:

  • The authenticated user’s session against the enterprise IDP is leveraged, which makes direct user authentication against the UAA transparent to the user.

  • The user/browser calling a PCF microservice is redirected to UAA and then to the IDP. Because this user session with the IDP is already authenticated, the redirect back to UAA and then back to the PCF microservice is transparent to the user.

  • Your enterprise IDP applies any security policies defined by your corporate policy, for example, multi-factor authentication (MFA), Kerberos, or PKI certificates.

  • Behind the scenes the UAA authenticates the user via SAML, because the user has a logged in session with the IDP.

  • UAA then generates a JWT for the authenticated user.

SAML Bearer Token Exchange — Back End

The following sequence diagram illustrates the SAML bearer token exchange model.

token-exchange

In this model:

  • The user has already authenticated against the enterprise IDP via SAML through interactions with the existing integration with the IDP upstream.

  • The authenticated session against the IDP empowers the user to get a SAML assertion intended for the Pivotal SSO (also known as UAA) audience. In the SAML assertion, the recipient and audience must match UAA. See the Example SAML Assertion below.

  • The UAA allows for a token exchange permitting the upstream caller to get a UAA JSON web token (JWT), which authorizes access to PCF hosted microservices, leveraging the SAML assertion mentioned above.

Example SAML Assertion

In the SAML assertion used in the SAML bearer token exchange, the Recipient and Audience must match UAA. For example:

Recipient

The Recipient must match the UAA entity ID.

Recipient="https://demo.login.uaa-example.com/oauth/token/alias/demo.login.uaa-example.com"/>

Audience

The Audience must match the UAA assertion consumer service URL.

<saml2:AudienceRestriction><saml2:Audience>demo.login.uaa-example.com</saml2:Audience></saml2:AudienceRestriction>

Example SAML Assertion

The Recipient and Audience properties are located near the bottom of the example SAML assertion below.

<?xml version="1.0" encoding="UTF-8"?><saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="id41261893195735352003413868" IssueInstant="2018-01-24T19:23:15.522Z" Version="2.0" xmlns:xs="http://www.w3.org/2001/XMLSchema"><saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">http://www.okta.com/exk9sgb2ayyAikL150h7</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="#id41261893195735352003413868"><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/2001/04/xmlenc#sha256"/><ds:DigestValue>oTsVYrWJ4Yah1eM3p0e4DCLP3NlsgFoAZ6R/KIIonL8=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>BYg9pYj2MwO4OvQvtuH2WOWemcEew7R6dIyxEaUC9sAtTyMBB0dumMhDZMtOXu7G6+Uoba7B1XqAS8YM5/llQsW/oGZAH9NuhYhNW1eWw8eSrTQjpfKn61Vei2EmihWwTRptBZUucu4ZSblvqPnUYt0SF/hMfHqYRbILeicZTgT/TlllOIMoPcET7JHC1ZkMYGJfKjXue1t34FER55ce1CnQQIxBN435R0WWLhx0UNd9XGWPlB3ddtaMJlehO9EZDECe1ORGP1niVplLSsx0QE1inVTr7Qn7+x3tGlX/9MVgEDevZaGdZzwdbkwwfDssFWppjLpqpcBLZLK8USSN1Q==</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIDpDCCAoygAwIBAgIGAVmywjWWMA0GCSqGSIb3DQEBBQUAMIGSMQswCQYDVQQGEwJVUzETMBEG
A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU
MBIGA1UECwwLU1NPUHJvdmlkZXIxEzARBgNVBAMMCmRldi0yODEzNzYxHDAaBgkqhkiG9w0BCQEW
DWluZm9Ab2t0YS5jb20wHhcNMTcwMTE4MTgwNTI5WhcNMjcwMTE4MTgwNjI5WjCBkjELMAkGA1UE
BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNV
BAoMBE9rdGExFDASBgNVBAsMC1NTT1Byb3ZpZGVyMRMwEQYDVQQDDApkZXYtMjgxMzc2MRwwGgYJ
KoZIhvcNAQkBFg1pbmZvQG9rdGEuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
0okZSvNaxO/QzpT92pxALWevO1j3F0DyFRjWz1x4u8AbPjJDizbr42pnm/dOxw5bij2CecwIvI3b
G/LNMh0NMB1uuMwRppIpNkU0mu/8b3u1szmgMSULRiAtCQFIKAd8VXApbmnLlsfzN5CnJzeDEZ29
3E/RGVr0WvSUKWYZaij57BfH2r2A44TZfRNpfUgtsvsVVQvtwDgKBo+rNqZIkQoMi0hdpX2Z522Z
l6vpbDGu56kWR0fqyfoshKHPnHNvk/c0HkwkCIAm1l7DW95PnrTxjx7QQuLZibUFPD1sQE2S4Vxe
W6kxXhT8ttmML0OjirEqtD+98BcbqCc9SgYtzwIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQDKqs49
VBGpRTAWvGm+giBHT2uJd5JCefE6ap/OPp+ajfslXXH3yUOq6CiyK1iVgS9j15MOVBDTou8vTtsK
w0TmdG1NHKCJqpTe2h/+3uvCG2yv9D6rfDiQcO4KgeG+5hXnS2fGcFTuCmODX7ivEYB9eeAqXkJG
4LFwxVhse8j0rwkdPESkdL7KdTbzK5rsM3tWihSsuccm4a6Zp6faFZzWhvd6ujBGIlLtaVHP9jUG
eMHVqMYK6C91CalL4/kGUJYGsKhbuF4CdjwlK9PB4PvNLn+ijWk9dYkVlQYMH93Lg9T/2OYVBux7
MQsY0xtKYytwky+LiE1SjODZPQvYXaS3</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature><saml2:Subject xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">person1@company.com</saml2:NameID><saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml2:SubjectConfirmationData NotOnOrAfter="2018-01-24T19:28:15.522Z" 
Recipient="https://demo.login.uaa-example.com/oauth/token/alias/demo.login.uaa-example.com"/>
</saml2:SubjectConfirmation></saml2:Subject><saml2:Conditions NotBefore="2018-01-24T19:18:15.522Z" NotOnOrAfter="2018-01-24T19:28:15.522Z" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
<saml2:AudienceRestriction><saml2:Audience>demo.login.uaa-example.com</saml2:Audience></saml2:AudienceRestriction>
</saml2:Conditions><saml2:AuthnStatement AuthnInstant="2018-01-24T19:23:15.522Z" SessionIndex="id1516821795522.1919419636" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:AuthnContext><saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef></saml2:AuthnContext></saml2:AuthnStatement><saml2:AttributeStatement xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:Attribute Name="mail" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">person1@company.com</saml2:AttributeValue></saml2:Attribute><saml2:Attribute Name="fn" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"><saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Sree</saml2:AttributeValue></saml2:Attribute><saml2:Attribute Name="ln" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"><saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Tummidi</saml2:AttributeValue></saml2:Attribute><saml2:Attribute Name="roles" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"><saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Everyone</saml2:AttributeValue><saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Writer-Admin</saml2:AttributeValue><saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Reader</saml2:AttributeValue><saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Writer</saml2:AttributeValue><saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Writer-a-Admin</saml2:AttributeValue><saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Reader-Admin</saml2:AttributeValue></saml2:Attribute></saml2:AttributeStatement></saml2:Assertion>

JWT Bearer Token Exchange

The following sequence diagram illustrates the JWT bearer token exchange model. This flow is for external apps using OIDC.

jwt-exchange

This model is similar to the SAML bearer token exchange flow:

  • The upstream app contacts UAA and requests a PCF-native JWT. The app provides a JWT generated by the enterprise IDP as evidence that the user has been authenticated.

  • The returned PCF-native JWT can then be used to invoke protected microservices hosted within PCF.

The difference between this flow and the SAML exchange one is that there is no need to get a specific SAML assertion for the UAA audience.

Example JWT Token Content

{
  "sub": "mysub",
  "iss": "https://my.idp.com",
  "aud": "http://appliesto/myidpjwt",
  "iat": 1517486551,
  "exp": 1517490151,
  "sess": "bf8d6812-0747-11e8-a94b-005056be1e86",
  "groups": [
    "my-admins"
  ],
  "emailAddress": [
    "person1@company.com"
  ]
}

Handling the JWT Token Exchange Using a Gateway

In the sequence illustrated above, the token exchange is handled by the app. However, you can abstract away the token exchange from the app. Instead, a gateway such as Apigee or Mulesoft intercepts the call and handles the token exchange transparently, as shown in the diagram below.

jwt-exchange-gateway

Set Up for SAML or JWT Bearer Token Exchange

The following setup procedure uses the example shown in the conceptual diagram above.

  1. Create API-1 and API-2 as resources in the SSO service, for example, api1.read, api1.write and api2.read, api2.write.

    For instructions, see Create or Edit Resources.

  2. Create an Admin Client with the ability to create more OAuth Clients under the SSO Service plan. Therefore, use the scope clients.admin.

    For instructions, see Create Admin Client.

  3. Use UAAC to target the SSO Service Plan and log in using the Admin Client created in the previous step.

    For instructions, see Step 4 in Manage Users with the UAA CLI (UAAC).

  4. For each client of the external app, run the following command to register the client:

    uaac client add -i

    In this example, there are two clients: API-1 and API-2.

  5. When prompted:

    1. Specify a client ID and secret, and record them for future use in the API call.

    2. Specify the Grant Type as either of the following:
      • For SAML: urn:ietf:params:oauth:grant-type:saml2-bearer
      • For JWT: urn:ietf:params:oauth:grant-type:jwt-bearer
    3. Specify the scopes. In this example, they are either: api1.read and api1.write, or api2.read and api2.write.

    4. You can leave the redirect URI and other options empty.
  6. A plan admin must do the following in the SSO Plan Administrator Dashboard:

    1. Add the enterprise SAML or OIDC (for JWT) provider.

      For instructions, see Add an External Identity Provider.

    2. Do one of the following:

      • Set up external group mappings for api1.read and other scopes.

        For instructions, see Create or Edit Resource Permissions.

      • Add the corresponding scopes to the users that require this access.

        For instructions, see Managing Users.

      • Make the scopes as default authorities so that users do not need to be assigned the groups individually.

        For instructions, see Add Default Groups for Users.

  7. The external app, or gateway, must make the following API call:

    1. Invoke the /oauth/token endpoint of the SSO plan with the parameters laid out in this documentation:
    2. Pass in the following:
      • Client ID and client secret registered for the external app
      • The SAML assertion or JWT token from the external app

    The response is a token that you must add in the authorization header when making a call to API-1 or API-2.

Create a pull request or raise an issue on the source for this page in GitHub