This app provides a SAML SP and a SAML IDP that allows it to proxy SAML requests.
Requires Docker and/or Node.js > 16.
You'll need to create a configuration JSON file with at the least the following minimum fields.
dev-config.json
{
"idpAcsUrl": "https://idp.example.com/acs/url",
"idpIssuer": "idp-issuer.example.com",
"idpAudience": "https://idp.example.com/audience",
"idpBaseUrl": "https://idp.example.com/base/url",
"spIdpMetaUrl": "https://sp.example.com/idp/meta/url",
"spNameIDFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
"spAudience": "sp-audience.example.com",
"spIdpIssuer": "sp-idp-issuer.example.com",
"spAuthnContextClassRef": "http://sp.example.com/authn/context/class/ref",
"spAcsUrl": "/samlproxy/sp/saml/sso",
"idpCert": "./idp-public-cert.pem",
"idpKey": "./idp-private-key.pem",
"spCert": "./sp-cert.pem",
"spKey": "./sp-key.pem",
"idpEncryptAssertion": true,
"idpEncryptionCert": "./idp-encryption-cert.pem",
"idpEncryptionPublicKey": "./idp-encryption-pub.key",
"mpiUserEndpont": "https://dev.example.com/mpiuser",
"vsoUserEndpont": "https://dev.example.com/vsouser",
"accessKey": "auth-acceess-key",
"vetsAPIToken": "vets-api-token",
"sessionSecret": "fake-session-secret",
"cacheEnabled": true,
"redisPort": "6379",
"redisHost": "127.0.0.1",
"spIdpSsoBinding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
"idpSamlLoginsEnabled": true,
"idpSamlLogins":
[
{
"category": "example2SamlIdp",
"spIdpMetaUrl": "https://saml-idp-2.example.com/metadata",
"spNameIDFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
"spAudience": "https://saml-idp-2.example.com",
"spAuthnContextClassRef": "http://sp-2.example.com/authn/context/class/ref",
"spRequestAuthnContext": true,
"spRequestNameIDFormat": true,
"spIdpSignupLinkEnabled": true
}
]
}
idpAcsUrl
, idpIssuer
, idpAudience
, and idBaseUrl
are all configuration provided by the IDP.
logStyleElementsEnabled
is a optional config that can be added to disable all logs related to styling elements like (png, css, woff etc) for local development (to disable logs set to false)
A functional dev-config file can be found in the saml-proxy-configs repository. Fields with the FIX_ME
value must be replaced with real values.
Docker:
Stop redis, if it is running locally. Make sure the following config value is set: redisHost: redis
.
docker build -t saml-idp .
docker run -p 7000:7000 saml-idp --config dev-config.json
Node:
Redis must be running locally. If docker is avaiable it can be run locally with
docker-compose up -d redis
Make sure the following config value is set properly: redisHost: 127.0.0.1
.
npm install
npm run-script start-dev
Login.Gov only supports https
ACS endpoints. Run the following command to set up an https proxy for the saml-proxy.
npm install -g local-ssl-proxy
local-ssl-proxy --source 9001 --target 7000
This key will not work with ID.me without further configuration.
You must generate a self-signed certificate for the IdP.
The private key should be unique to your test IdP and not shared!
You can generate a keypair using the following command (requires openssl in your path):
openssl req -x509 -new -newkey rsa:2048 -nodes -subj '/C=US/ST=California/L=San Francisco/O=JankyCo/CN=Test Identity Provider' -keyout idp-private-key.pem -out idp-public-cert.pem -days 7300
You will also need to generated a self-signed certificate of the SP functions
openssl req -x509 -new -newkey rsa:2048 -nodes -subj '/C=US/ST=California/L=San Francisco/O=JankyCo/CN=Test Identity Provider' -keyout sp-key.pem -out sp-cert.pem -days 7300
You can also grab the development certificates from here.
There are cases where a developer may want to confirm that sentry error reporting is working.
Clone sentry/onpremise from github
git clone https://github.com/getsentry/onpremise.git
In the onpremise repository run the following command and follow its prompts:
./initialize.sh
After initialization build project
docker-compose up -d
you can log into your local sentry instance at http://127.0.0.1:9000/
.
Set the following variables in your local config:
"sentryDSN": "http://[email protected]:9000/1",
"sentryEnvironment": "TEST"
To create an error run one of the following cases:
- Intercept a SAMLRequest and malform the xml.
- Intercept a SAMLResponse and malform the xml.
The proxy fills both roles typically seen in a SAML interaction:
- It acts as an Identity Provider (IDP) relative to Okta. It receives a SAML request from Okta, and returns a SAML response.
- It acts as a Service Provider (SP) relative to selected IDP eg. Login.gov and ID.me. It sends a SAML request to the user-selected IDP, and receives a SAML response.
These two interactions are interleaved,
- the request received from Okta is re-signed and passed along to the coresponding IDP service. Then the respone from the IDP is validated, transformed, re-signed, and passed along to Okta.
Flow of the SAML login process:
+-----------------------+
| |
| User clicks |
| "Login with Va.gov" |
| |
+---|-------------------+
|
|
|
+---v---------------+ +-------------------------------+ +-------------------------+
| | | | | |
| GET /authorize | | POST /samlproxy/idp/saml/sso | | User is presented |
| Okta starts SAML +-----> Proxy receives AuthNRequest +-----+ with a list of IDP |
| | | | | login options |
+-------------------+ +-------------------------------+ | |
+----|--------------------+
|
+-----------------------------------------------------------+
|
|
|
+-----v------------------+
| |
| User selects |
| one of the IDP options |
| |
| Eg: Login.gov, ID.me, |
| DS Logon, or |
| My HealtheVet |
+-----|------------------+
|
|
+--------v-------------------+
| |
| The IDP service gets the |
| AuthNRequest with the |
| corresponding AuthNContext |
| |
+--|-------------------------+
|
|
|
|
|
| +-------------------------------+ +---------------------------+
+-v------------+ | | | |
| | | GET /samlproxy/sp/saml/sso | | Proxy POSTS SAMLResponse |
| User logs in +----> Proxy receives redirect from +----> To Okta |
| | | the IDP service with the | | |
+--------------+ | SAMLResponse | +---------------------------+
| |
+-------------------------------+
This is a hybrid JavaScript/Typescript application. Our goal is eventually have it be completely written in Typescript, therefore all new features should be written in TypeScript and have accompanying test written using Jest.