@sidewinder1138/saml-idp
Version:
Test Identity Provider (IdP) for SAML 2.0 Web Browser SSO Profile
299 lines (211 loc) • 12.2 kB
Markdown
# NOTE: THIS IS A FORK
This version of saml-idp is a fork of the original work created by mcguinness (see here: https://github.com/mcguinness/saml-idp).
I took this approach for now just to get the ball rolling on some features I desperately needed. If possible I'd rather share my changes upstream and kill this fork eventually.
Changes so far:
v1.3.1 - No changes by me, I just released the latest version of what mcguinness had in github.
v1.4.0 - Improved the runServer() to work in an asynchronous way (via callback), added a "quiet" mode which silences the noisy console.log calls when running as a library.
# Introduction
This app provides a simple SAML Identity Provider (IdP) to test SAML 2.0 Service Providers (SPs) with the [SAML 2.0 Web Browser SSO Profile](http://en.wikipedia.org/wiki/SAML_2.0#Web_Browser_SSO_Profile) or the Single Logout Profile.
> **This sample is not intended for use with production systems!**
## Installation
### Global Command Line Tool
```shell
npm install --global saml-idp
```
### Manual
From inside a local copy of this repo
```shell
npm install
# or
npm link
```
### Library
```shell
npm install saml-idp
```
### Docker
1. docker-compose build
2. docker-compose up
Simply modify Dockerfile to specify your own parameters.
## Generating IdP Signing Certificate
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):
```shell
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
```
## Usage
### Library
An IdP server can be started using the exported `runServer` function. `runServer` accepts a config object which matches the interface of the `saml-idp` command.
```javascript
const { runServer } = require("saml-idp");
runServer({
acsUrl: `https://foo.okta.com/auth/saml20/assertion-consumer`,
audience: `https://foo.okta.com/auth/saml20/metadata`,
});
```
#### Custom user config (claims)
```javascript
const { runServer } = require("saml-idp");
runServer({
acsUrl: `https://foo.okta.com/auth/saml20/assertion-consumer`,
audience: `https://foo.okta.com/auth/saml20/metadata`,
config: {
user: userDefaults,
// The auth-service requires at least one AttributeStatement in the SAML assertion.
metadata: [
{
id: "email",
optional: false,
displayName: "E-Mail Address",
description: "The e-mail address of the user",
multiValue: false,
},
{
id: "userType",
optional: true,
displayName: "User Type",
description: "The type of user",
options: ["Admin", "Editor", "Commenter"],
},
],
user: {
email: "saml.jackson@example.com",
},
},
});
```
### Command Line
#### SSO Profile
```shell
saml-idp --acsUrl {POST URL} --audience {audience}
```
#### SSO & SLO Profile
```
saml-idp --acsUrl {POST URL} --sloUrl {POST URL} --audience {audience}
```
Open `http://localhost:7000` in your browser to start an IdP initiated flow to your SP
#### Example
```
saml-idp --acsUrl https://foo.okta.com/auth/saml20/example --audience https://www.okta.com/saml2/service-provider/spf5aFRRXFGIMAYXQPNV
```
#### Options
The following options can either be passed as `--<option>` or to `runServer` in an options object.
| Option (\* required) | Description | Default |
| -----------------------: | --------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
| **host** | IdP Web Server Listender Host | localhost |
| **port** | IdP Web Server Listener Port | 7000 |
| **cert** _\*_ | IdP Signature PublicKey Certificate | ./idp-public-cert.pem |
| **key** _\*_ | IdP Signature PrivateKey Certificate | ./idp-private-key.pem |
| **issuer** _\*_ | IdP Issuer URI | urn:example:idp |
| **acsUrl** _\*_ | SP Assertion Consumer URL |
| **sloUrl** | SP Single |
| **audience** _\*_ | SP Audience URI |
| **serviceProviderId** | SP Issuer/Entity URI |
| **relayState** | Default SAML RelayState |
| **disableRequestAcsUrl** | Disables ability for SP AuthnRequest to specify Assertion Consumer URL | false |
| **encryptAssertion** | Encrypts assertion with SP Public Key | false |
| **encryptionCert** | SP Certificate (pem) for Assertion Encryption |
| **encryptionPublicKey** | SP RSA Public Key (pem) for Assertion Encryption (e.g. openssl x509 -pubkey -noout -in sp-cert.pem) |
| **httpsPrivateKey** | Web Server TLS/SSL Private Key (pem) |
| **httpsCert** | Web Server TLS/SSL Certificate (pem) |
| **https** _\*_ | Enables HTTPS Listener (requires httpsPrivateKey and httpsCert) | false |
| **configFile** _\*_ | Path to a SAML attribute config file | saml-idp/config.js |
| **rollSession** | Create a new session for every authn request instead of reusing an existing session | false |
| **authnContextClassRef** | Authentication Context Class Reference | urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport |
| **authnContextDecl** | Authentication Context Declaration (XML FilePath) |
# IdP SAML Settings
## Issuer
The default IdP issuer is `urn:example:idp`. You can change this with the `--iss` argument.
## Signing Certificate
The signing certificate public key must be specified as a file path or PEM string using the `cert` argument.
To generate a self-signed certificate for the IdP run
```shell
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
```
The signing certificate private key must be specified as a file path or PEM string using the `key` argument
### Passing key/cert pairs from environment variables
Signing certificate key/cert pairs can also be passed from environment variables.
```
saml-idp --acsUrl {POST URL} --audience {audience} --cert="$SAML_CERT" --key="$SAML_KEY"
```
## Single Sign-On Service Binding
Both SSO POST and Redirect bindings are available on the same endpoint which by default is `http://localhost:7000/saml/sso`
| Binding | URL |
| ------------- | -------------------------------- |
| HTTP-Redirect | `http://localhost:port/saml/sso` |
| HTTP-POST | `http://localhost:port/saml/sso` |
## Single Logout Service Binding
Both SSO POST and Redirect bindings are available on the same endpoint which by default is `http://localhost:7000/saml/slo`
| Binding | URL |
| ------------- | -------------------------------- |
| HTTP-Redirect | `http://localhost:port/saml/slo` |
| HTTP-POST | `http://localhost:port/saml/slo` |
## SAML Metadata
IdP SAML metadata is available on `http://localhost:port/metadata`
## Assertion Attributes
The IdP mints the user's profile as a SAML Assertion Attribute Statement using the `metadata` property in `config.js`. Profile properties that match a metadata entry `id` property will be generated as a SAML Attribute with the same name. The IdP UI will automatically render an input for each entry defined via a `metadata` entry in `config.js` with a default value from the matching `profile` property.
#### Profile Property
```json
{
"email": "saml.jackson@example.com"
}
```
#### Metadata Entry
```json
{
"id": "email",
"optional": false,
"displayName": "E-Mail Address",
"description": "The e-mail address of the user",
"multiValue": false
}
```
#### SAML Assertion Attribute Statement
```xml
<saml:Attribute Name="email"><saml:AttributeValue xsi:type="xs:anyType">saml.jackson.com</saml:AttributeValue>
```
### Default Attributes
The default profile mappings are defined in `config.js` as:
| Profile Property | SAML Attribute Name |
| --------------------- | ------------------------------- |
| userName | Subject NameID |
| nameIdFormat | Subject NameID Format |
| nameIdNameQualifier | Subject NameID Name Qualifer |
| nameIdSPNameQualifier | Subject NameID SP Name Qualifer |
| nameIdSPProvidedID | Subject NameID SP ProvidedID |
| firstName | `firstName` |
| lastName | `lastName` |
| displayName | `displayName` |
| email | `email` |
| mobilePhone | `mobilePhone` |
| groups | `groups` |
> SAML attribute mappings currently default to [Okta (Inbound SAML)](developer.okta.com)
### Custom Attributes
New attributes can be defined at runtime in the IdP UI or statically by modifying the `profile` and `metadata` objects in `config.js`.
1. Add metadata entry for your new attributes. The `id` property must be the name of the SAML Attribute
```json
{
"id": "customAttribute",
"optional": false,
"displayName": "Custom Attribute",
"description": "My custom attribute",
"multiValue": false
}
```
2. Optionally add a default profile attribute value that will be used on startup
## Assertion Encryption
Encrypted assertions require both a certificate and public key from the target service provider in the PEM format (base64 encoding of `.der`, `.cer`, `.cert`, `.crt`). You can convert certificate formats with `openssl`
### DER to PEM
```shell
openssl x509 -inform der -in to-convert.der -out converted.pem
```
> The following formats or extensions should be convertible to the pem format: `.der`, `.cer`, `.cert`, `.crt
### PEM Certificate to Public Key
PEM files that contain the header `-----BEGIN CERTIFICATE-----` can also be converted to just the public key which is a file with just the `-----BEGIN PUBLIC KEY-----` header
```shell
openssl x509 -pubkey -noout -in cert.pem > pub.key
```