gcp

Propagating SSO SAML Attributes to IAP Protected application

2022-11-30

A sample demo of creating a SAML assertion using your own SSO system which includes custom attributes that are propagated to an IAP protected application.

basically, a demo of: Configure SAML attribute propagation


To use this, you need a lot (like…you know, a Beyond Corp Enterprise license!):

  • a google workspace test domain where you can configure workspace SAML profiles for a given group
  • BeyondCorp Enterprise license, right
  • an IAP protected app which accepts the user presented by the SAML Assertion
  • a google workspace group called group1_3 and another called ssogroup (but really, you can pick anything but the examples below uses these)

So pick a user, in my case its user1@ is a member of group1_3@:

images/group_1_3.png

Then i configured a SAML Profile that uses the certificates from here (server.crt).

IMPORTANT i’m just using a cert from the internet here…do this ONLY for testing and remove the saml profile altogether once you’re done!!

images/saml_profile.png

Assign the saml profile to apply to group1_3@

images/saml_group_profile.png

Finally configure an IAP protected app that allows user1@. The specific app i’m using in this demo will simply echo all the headers images/iap_config.png


This whole thing basically allows your own SAML based SSO System to propagate attributes from the assertion into headers or IAP JWT claims back to your backend application.

So if your SAML Assertion includes attributes like this:

<saml2:AttributeStatement xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<saml2:Attribute Name="mygroups">
<saml2:AttributeValue xsi:type="xsd:string">ssoappgroup</saml2:AttributeValue>
<saml2:AttributeValue xsi:type="xsd:string">group1_3</saml2:AttributeValue>
</saml2:Attribute>
</saml2:AttributeStatement>

Then after SSO login, your IAP application could either see those claims translated to

  • HTTP headers
X-Goog-Iap-Attr-Mygroups: ssoappgroup,group1_3

or encoded in the JWT Assertion Header (X-Goog-Iap-Jwt-Assertion):

X-Goog-Iap-Jwt-Assertion: eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjBvZUxjUSJ9.eyJhZGRpdGlvbmFsX2NsYWltcyI6eyJteWdyb3VwcyI6WyJzc29hcHBncm91cCIsImdyb3VwMV8zIl19LCJhdWQiOiIvcHJvamVjdHMvMjQ4MDY2NzM5NTgyL2FwcHMvZmFibGVkLXJheS0xMDQxMTciLCJhenAiOiIvcHJvamVjdHMvMjQ4MDY2NzM5NTgyL2FwcHMvZmFibGVkLXJheS0xMDQxMTciLCJlbWFpbCI6InVzZXIxQGVzb2RlbW9hcHAyLmNvbSIsImV4cCI6MTY2OTY1MjI2NiwiaGQiOiJlc29kZW1vYXBwMi5jb20iLCJpYXQiOjE2Njk2NTE2NjYsImlzcyI6Imh0dHBzOi8vY2xvdWQuZ29vZ2xlLmNvbS9pYXAiLCJzdWIiOiJhY2NvdW50cy5nb29nbGUuY29tOjEwNDQ5NzAzMjI3MDIxOTc1ODIxMiJ9Cg.fXBGHAtmnuumhfHfUK3dpxlGyzLDpPg46UQk20Zt7kr-Hy1xv0SqkoHbYffo7KG9kkpXoz2RfrkXCJn_QsOjjw

which when decoded yields:

{
  "additional_claims": {
    "mygroups": [
      "ssoappgroup",
      "group1_3"
    ]
  },
  "aud": "/projects/248066739582/apps/fabled-ray-104117",
  "azp": "/projects/248066739582/apps/fabled-ray-104117",
  "email": "user1@esodemoapp2.com",
  "exp": 1669652266,
  "hd": "esodemoapp2.com",
  "iat": 1669651666,
  "iss": "https://cloud.google.com/iap",
  "sub": "accounts.google.com:104497032270219758212"
}

The configurration is pretty straightforward per docs

given iap.yaml which injects attributes found in SAML Claims called mygroups:

applicationSettings:
 attributePropagationSettings:
  expression: attributes.saml_attributes.filter(attribute, attribute.name in ["mygroups"])
  outputCredentials: ["HEADER", "JWT"]
  enable: true
 accessDeniedPageSettings:
  generateTroubleshootingUri: true

Apply that config to an IAP protected app (appengine in my case here)

export PROJECT_ID=`gcloud config get-value core/project`
gcloud iap settings set iap.yaml --project=$PROJECT_ID --resource-type=app-engine --service=default --version=1

Then to test this out, you need to follow along with a sample SAML app from googleapps-sso

Again, please note that the git repo there uses signing certs that are..well, on github. You are free to define your own keypair for SAML assertions. If you do, then remember to set the Workspace SAML Profile accordingly

sudo vi /etc/hosts
# add:
127.0.0.1	sso.idpdomain.com

git clone https://github.com/salrashid123/googlapps-sso.git
cd googlapps-sso/

## IMPORTANT, uncomment https://github.com/salrashid123/googlapps-sso/blob/master/saml_idp_gsuites.py#L111-L120

docker run -t -p 28080:28080   \
     -v `pwd`:/app/:ro  \
	 --entrypoint=/app/saml_idp_gsuites.py     salrashid123/appssso     --debug      --cert_file=/app/server.crt     --key_file=/app/server.key

Now open up a browser and go to the appengine app’s url: eg (https://$PROJECT_ID.appspot.com/)

Enter in the username set in group1_3 (i.,e a user that should go through the saml profile workspace login flows).

You should then get redirected to the SAML IdP your’e running locally. Once there, enter in the username and email again and enter in any password (since this is a demo, its fake)

You should get redirected to the appengine app….in my case the app simply echos back all the headers it got so i’d see this:

GET / HTTP/1.1
...
X-Goog-Authenticated-User-Email: accounts.google.com:user1@esodemoapp2.com
X-Goog-Authenticated-User-Id: accounts.google.com:104497032270219758212
X-Goog-Iap-Attr-Mygroups: ssoappgroup,group1_3
X-Goog-Iap-Jwt-Assertion: eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjBvZUxjUSJ9.eyJhZGRpdGlvbmFsX2NsYWltcyI6eyJteWdyb3VwcyI6WyJzc29hcHBncm91cCIsImdyb3VwMV8zIl19LCJhdWQiOiIvcHJvamVjdHMvMjQ4MDY2NzM5NTgyL2FwcHMvZmFibGVkLXJheS0xMDQxMTciLCJhenAiOiIvcHJvamVjdHMvMjQ4MDY2NzM5NTgyL2FwcHMvZmFibGVkLXJheS0xMDQxMTciLCJlbWFpbCI6InVzZXIxQGVzb2RlbW9hcHAyLmNvbSIsImV4cCI6MTY2OTY1MjI2NiwiaGQiOiJlc29kZW1vYXBwMi5jb20iLCJpYXQiOjE2Njk2NTE2NjYsImlzcyI6Imh0dHBzOi8vY2xvdWQuZ29vZ2xlLmNvbS9pYXAiLCJzdWIiOiJhY2NvdW50cy5nb29nbGUuY29tOjEwNDQ5NzAzMjI3MDIxOTc1ODIxMiJ9Cg.fXBGHAtmnuumhfHfUK3dpxlGyzLDpPg46UQk20Zt7kr-Hy1xv0SqkoHbYffo7KG9kkpXoz2RfrkXCJn_QsOjjw

Appendix

SAML Assertion

An example SAML Assertion in its full form that your on-prem system would send over

<?xml version="1.0" ?>
<saml2p:Response ID="_5be0334ae2da196e0acea5af924f068" InResponseTo="_26a2ce340105500792d05adf7b94d8f3" IssueInstant="2022-11-28T16:07:39Z" Version="2.0" xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol">
	<saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">authn.py</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="#_5be0334ae2da196e0acea5af924f068">
				<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/2000/09/xmldsig#sha1"/>
				<ds:DigestValue>s6DK1lFY2e5EYqAfF2ihD0dHLiE=</ds:DigestValue>
			</ds:Reference>
		</ds:SignedInfo>
		<ds:SignatureValue>GTYgXvNI1r9EF9wg3/2BjiYCGhtLCIXNkahjuDGX2+pPuxIdal+AYo/vYuaJjIXE
0YA8hZsWedSk/qgIPOhyVa7d3QjhbS6VdYpI34CMS5f0ydrMZH9SibFVFD/S3zSC
KI+gHddnf17/mNQFLKxMQjsIkYRkkVNoxwnacOOhKeWfxelXXtGtDvpvDapMdi79
HDv5S5dwmbQmt1ldX8OXVsPqlE6ArHN2qX//FkxhW3ZLkOJvnD1HE+t2JbT/DCe5
gfPv9qF5zT60YWzM23ThPLiSIfmWQUkwS9a25wvApJmGmuI/CWnMtiIJghia+mJT
TQgmTOGIjjfRdVVf0WVlEQ==</ds:SignatureValue>
		<ds:KeyInfo>
			<ds:X509Data>
				

				<ds:X509Certificate>MIIEPjCCAyagAwIBAgIBATANBgkqhkiG9w0BAQsFADBXMQswCQYDVQQGEwJVUzEP
MA0GA1UECgwGR29vZ2xlMRMwEQYDVQQLDApFbnRlcnByaXNlMSIwIAYDVQQDDBlF
bnRlcnByaXNlIFN1Ym9yZGluYXRlIENBMB4XDTIyMDEwOTIyMTAzNloXDTI3MDcw
MjIyMTAzNlowUzELMAkGA1UEBhMCVVMxDzANBgNVBAoMBkdvb2dsZTETMBEGA1UE
CwwKRW50ZXJwcmlzZTEeMBwGA1UEAwwVc2VydmVyLnlvdXJkb21haW4uY29tMIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw0PQuP452qvSZetyW/hqFIkg
CCSc+6Ryw+rRPUIcVZIuibonJeSJe+TXPvonUEzAgpt858ji+FUsFVDsLmcX2yLF
DO0AtV3JA04wbJb8bSujvVstzpVAvY/gIAADrs8gqybekgSnUxOtXlFeYmYjxdNF
sx1qFqaE0nrRJzZIlarhaym38+Tta5+rJPlrZKOVoCgOqAYyQ5LH0/epEJh5U2Du
g2aZRcdULdqORbftDqqliheG2atd/tCQxKxVHQrRnWmnyE0ZJEpnn9/faSX8vqtz
Zu/5Z4FUt5TcyB9dyKrvaKjGwM6ON+5QYsKI08FxlqQ/Uhp5PrRFni5TCmfJWQID
AQABo4IBFzCCARMwDgYDVR0PAQH/BAQDAgeAMAkGA1UdEwQCMAAwEwYDVR0lBAww
CgYIKwYBBQUHAwEwHQYDVR0OBBYEFEmBqhsOJAb2CGD7SiVS0tnJbQNOMB8GA1Ud
IwQYMBaAFLe6sAKh5740xsEFXGZ45btTXaFUMEQGCCsGAQUFBwEBBDgwNjA0Bggr
BgEFBQcwAoYoaHR0cDovL3BraS5lc29kZW1vYXBwMi5jb20vY2EvdGxzLWNhLmNl
cjA5BgNVHR8EMjAwMC6gLKAqhihodHRwOi8vcGtpLmVzb2RlbW9hcHAyLmNvbS9j
YS90bHMtY2EuY3JsMCAGA1UdEQQZMBeCFXNlcnZlci55b3VyZG9tYWluLmNvbTAN
BgkqhkiG9w0BAQsFAAOCAQEAghT4lTRNTN95pVLFqvopmuOhxt+MJVzGeeIwOZCH
IP2GOWzucuHqxyhlvDHtDmJSGB4tsC5EExZ+nThQdNiLB0QfMzj+OHUQHeA3RQp5
NgK3/Cvhzjd0zptC9X2pFC1vIRdvS+qGQMUbre0gZ01WCrd6p4WDRy8rblB9aW7J
OVgo6zV+4yAgson4RYa70N7HlcWgwfYmlS6YhRNJKMJbjDSFj1aA8HkCoUQZlkES
VT642mI/pwjL2U6eEyDVL0v+2yeDAsP4Jg+TdjNXumbLiWYIDUVa19Eqaz4LeXB+
T1Y96kb1pfIDuUf92nSnoUmTn4cijwdub8WqlJXX9Q6RNw==</ds:X509Certificate>
				

			</ds:X509Data>
		</ds:KeyInfo>
	</ds:Signature>
	<saml2p:Status xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol">
		<saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
	</saml2p:Status>
	<saml2:Assertion ID="_47adc5f29fa369412e9568b0f9992e7" IssueInstant="2022-11-28T16:07:39Z" Version="2.0" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
		<saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">authn.py</saml2:Issuer>
		<saml2:Subject>
			<saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">user1@esodemoapp2.com</saml2:NameID>
			<saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
				<saml2:SubjectConfirmationData InResponseTo="_26a2ce340105500792d05adf7b94d8f3" NotOnOrAfter="2022-11-28T16:57:39Z" Recipient="https://accounts.google.com/samlrp/acs?rpid=02hs5gsz0t2f56o"/>
			</saml2:SubjectConfirmation>
		</saml2:Subject>
		<saml2:Conditions NotBefore="2022-11-28T16:07:39Z" NotOnOrAfter="2022-11-28T16:57:39Z">
			<saml2:AudienceRestriction>
				<saml2:Audience>https://accounts.google.com/samlrp/metadata?rpid=02hs5gsz0t2f56o</saml2:Audience>
			</saml2:AudienceRestriction>
		</saml2:Conditions>
		<saml2:AuthnStatement AuthnInstant="2022-11-28T16:07:39Z" SessionIndex="_47adc5f29fa369412e9568b0f9992e7">
			<saml2:AuthnContext>
				<saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef>
			</saml2:AuthnContext>
		</saml2:AuthnStatement>
		<saml2:AttributeStatement xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
			<saml2:Attribute Name="mygroups">
				<saml2:AttributeValue xsi:type="xsd:string">ssoappgroup</saml2:AttributeValue>
				<saml2:AttributeValue xsi:type="xsd:string">group1_3</saml2:AttributeValue>
			</saml2:Attribute>
		</saml2:AttributeStatement>
	</saml2:Assertion>
</saml2p:Response>

This site supports webmentions. Send me a mention via this form.