Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Test Identity Provider

Test Identity Provider is a small Go SAML 2.0 IdP for disposable test infrastructure. It supports SP-initiated login, file-backed users and service providers, a server-rendered admin portal, signing-certificate rotation, and live debug/authentication logs.

Test Identity Provider — Not for Production. The admin portal intentionally has no authentication, user passwords are stored in plaintext, HTTP is the only supported listener in this release, keys are local files, and logs grow until an operator manages them. It does not provide the access controls, secret management, audit guarantees, availability, hardening, or lifecycle controls required for production identity infrastructure.

Documentation

Start with the operator documentation index for installation, configuration, administration, SAML integration, operations, and troubleshooting.

Quick start: binary release

The prebuilt release runs on Linux AMD64 and does not require Go. Download testidp-1.0.0.tar.gz from GitHub Releases, or download it from a shell:

version=1.0.0
curl --fail --location --remote-name \
  "https://github.com/Gobo42/Test-IdP/releases/download/v${version}/testidp-${version}.tar.gz"

Extract and start it:

version=1.0.0
tar -xzf "testidp-${version}.tar.gz"
cd "testidp-${version}"
test -e config.toml || cp config.toml.example config.toml
./testidp -version
./testidp -config ./config.toml

The version command should print testidp 1.0.0. With the default configuration, open http://localhost:8080/ for login and http://localhost:8080/admin for administration.

Edit config.toml before exposing the listener to another machine. listen_address controls the network binding, while public_base_url must be the exact origin used by browsers and SPs. The /admin portal has no authentication; never expose it to an untrusted network.

When HTTPS is required, the recommended deployment is nginx TLS termination in front of a loopback-only TestIdP listener. The operations guide contains a complete HTTPS, redirect, forwarded-header, and WebSocket example.

For a private home-directory installation, per-user systemd service, upgrades, and uninstall, follow the installation guide.

Build from source

Go 1.26 or later is required only for source builds:

go build -o testidp ./cmd/testidp
./testidp -version
test -e config.toml || cp config.toml.example config.toml
./testidp -config ./config.toml

A minimal config.toml is:

port = 8080

The canonical commented template is config.toml.example. Complete copyable environments for loopback-only and isolated-network use are under examples/.

The default public base URL is http://localhost:<port>. Set it explicitly when SPs reach the IdP through another hostname:

port = 8080
listen_address = "0.0.0.0"
public_base_url = "http://idp.test.example:8080"
login_field = "username"

The listener defaults to 127.0.0.1 because the admin portal has no authentication. Set listen_address to an explicit IP such as 0.0.0.0 only on an isolated test network. The public base URL is HTTP-only in version one. TLS-aware configuration is reserved for the next release.

On first start, the program creates its data directories, an empty canonical users file, and a self-signed RSA IdP signing certificate/key. Open http://localhost:8080/ for direct login or http://localhost:8080/admin for the deliberately unauthenticated admin portal.

Storage layout

All paths are relative to the directory containing config.toml:

data/
  users.txt
  persistent-nameids.toml
  service-providers/
    <sp-slug>.toml
    <sp-slug>-metadata.xml
  certificates/
    idp.crt
    idp.key
    archive/
      <archive-id>-idp.crt
      <archive-id>-idp.key
    service-providers/
      <sp-slug>/
        signing-1.crt
  logs/
    debug.log
    auth.log

Portal writes use complete canonical files and atomic replacement. User edits coordinate users.txt with the persistent-NameID registry through a private owner-only recovery file; an interrupted update is rolled forward before user state is loaded or changed again. External file edits take effect only after a restart or the corresponding Reload action. Invalid user records are skipped; an unusable users reload keeps the previous live snapshot. SP files load independently, so one invalid SP is disabled without preventing other SPs from loading.

The <sp-slug> shown in the storage layout is an internal identifier derived from the SP Name. It remains in persisted TOML and internal admin routes for file compatibility, but the admin portal never asks an operator to enter or edit it.

Users file

The three sections are ordered. Custom claim order defines user columns and the default custom-mapping rows; each SP's mapping order controls assertion order. Each entry's last field is its preserved comment.

# Format: claim-name | entry-comment
# Claim order defines the custom-claim column order in [users].
[claims]
department | Department used in test assertions
manager | Manager username

# Format: group-name | entry-comment
# Only groups listed here are eligible for release in SAML assertions.
[groups]
admins | Administrative test group
developers | Development test group

# Format: name-identifier | username | plaintext-password | display-name | email | upn | comma-separated-groups | department | manager | entry-comment
# Passwords are stored in plaintext. This file is for test use only.
[users]
alice-id | alice | password123 | Alice Example | alice@example.test | alice@example.test | admins,developers | Engineering | bob | Primary demo user

Fields use pipe-delimited CSV quoting. A user's group list is comma-delimited. Unknown group memberships remain in the record but are ignored in assertions until that group is defined. Admin validation prevents invalid portal submissions from changing disk. Arbitrary standalone comments from external edits are discarded on the next portal rewrite; entry comments are preserved.

Admin → Users displays passwords in unobscured plaintext on Add/Edit forms. Edit prepopulates the stored value for inspection and copy/paste, and password is required on every save. Anyone who can reach the unauthenticated admin portal can view those passwords.

Name Identifier and UPN are mandatory for every canonical user.

Name Identifier is globally unique. data/persistent-nameids.toml reserves each active value and retains replaced or deleted values as retired so a persistent identifier is never silently reassigned. A username rename transfers ownership when Name Identifier is unchanged. Manual file conflicts are skipped and diagnosed on startup or Reload. The Users Actions section can deliberately clear retired history for disposable tests; it preserves active reservations and requires confirmation. Back up the registry together with users.txt.

Login and SAML flow

Visiting / shows the test-only warning and login form. A successful direct login creates an opaque, process-local session and shows Signed in as <display name or username>. Later SP-initiated requests reuse that session unless the AuthnRequest sets ForceAuthn.

login_field globally selects nameIdentifier, username, email, or upn; omission defaults to username. It can also be changed immediately from Admin → Users. Matching is exact and case-sensitive. Blank identifiers never match. Records are checked in file order, so only the first duplicate identifier can authenticate; a blank or duplicate later record is never used as a fallback.

For a valid SP request, the IdP builds and signs the response, then shows a ten-second interstitial. The page lists the exact NameID plus each Friendly Name, outgoing Claim, Format, and value being sent, offers a Continue now button, and automatically POSTs SAMLResponse and the opaque RelayState to the registered ACS after the countdown.

The canonical IdP Entity ID is also a well-known metadata endpoint:

http://localhost:8080/entity

The compatibility metadata-download alias is:

http://localhost:8080/saml/metadata

The SSO endpoint accepts SAML HTTP-Redirect and HTTP-POST AuthnRequests:

http://localhost:8080/saml/sso

Responses always use HTTP-POST. Each SP has independent Sign Assertion and Sign Response controls. Assertion-only, Response-only, and Both are valid; at least one must be selected, and new/imported SPs default to Both. Generated signatures use RSA-SHA256 with SHA-256 digests.

One shared per-SP Canonicalization dropdown applies to both Assertion and Response signatures. The choices are Exclusive C14N 1.0 (recommended and the default), Inclusive C14N 1.1, and legacy Inclusive C14N 1.0. TestIdP builds the complete Response tree, signs the Assertion while attached, signs the Response last when selected, and serializes the XML once. Never pretty-print or otherwise modify signed XML before validation.

NameID value source, NameID format, signed-request enforcement, ACS endpoints, and attribute mappings are also configured per SP.

The form labels these controls NameID value source and NameID format. The raw format remains editable. Compact Unspecified and Persistent presets, plus More formats…, replace only that raw format string. Persistent format requires the Name Identifier source; the common-format dialog links to the official OASIS values.

Changing only the signing selections or Canonicalization does not change the signing certificate, so it does not require metadata re-import. Start a fresh SP-initiated login after a change rather than refreshing or resubmitting an old ACS POST.

Service providers

Use Admin → Service providers to:

  • Import metadata by upload or pasted XML.
  • Add a normalized SP manually.
  • Map and select standard attributes and current custom claims.
  • Configure NameID and signing behavior.
  • Edit or explicitly confirm removal of an SP.

Imported metadata and extracted SP signing certificates are retained beside the normalized SP definition. Invalid metadata writes nothing. A deleted custom claim referenced by an externally edited SP is ignored at runtime and displayed as a warning.

The portal derives internal SP storage names from Name. It trims surrounding space, lowercases ASCII letters, replaces each run outside [a-z0-9] with one hyphen, and removes outer hyphens. For example, Microsoft Entra Test becomes microsoft-entra-test, and Auth0 / QA becomes auth0-qa. A Name containing no ASCII letter or number is rejected.

Changing an existing SP Name also renames its definition, retained metadata, certificate directory, certificate paths, and stored internal identifier as one recoverable operation. If two Names derive to the same identifier, the portal rejects the save and requires changing Name. TestIdP never adds a numeric or hashed suffix automatically.

Each SP stores ordered mapping tables:

[[attribute_mappings]]
name = "displayName"
claim = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"
format = "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
enabled = true

[[attribute_mappings]]
name = "claim:department"
claim = "department"
format = "urn:oasis:names:tc:SAML:2.0:attrname-format:basic"
enabled = true

name is the stable internal user field/FriendlyName; claim becomes the SAML Attribute Name; format becomes NameFormat; Description is admin-only explanatory text. The six standard internal names are ordered nameIdentifier, username, displayName, email, upn, and singular capitalized Group. New and imported SPs enable all six defaults. A claim added later appears blank and unchecked on existing SPs.

At least one mapping must be enabled. Enabled rows require a nonblank outgoing Claim, an absolute Format URI, and unique outgoing Claims after trimming. Unchecked rows may retain arbitrary Claim/Format text without affecting the assertion. A later custom-claim deletion leaves a stale mapping inert at runtime; the next admin save omits it.

See Compatibility and migration only when loading older persisted users or SP fields.

Quick SP-initiated test

  1. Start Test IdP and open /admin/service-providers.
  2. Import your test SP metadata, or manually enter its entity ID and HTTP-POST ACS URL.
  3. Review the attribute mappings, select at least one to send, and save.
  4. Give the SP the IdP metadata from /entity or /saml/metadata, and ensure its trusted IdP Entity ID is the advertised /entity URL.
  5. Open / and sign in with a user from data/users.txt.
  6. Start SAML login at the SP.
  7. Confirm the IdP interstitial shows the intended NameID, groups, and claims.
  8. Select Continue now or wait ten seconds, then inspect the SP result and the authentication log.

Only registered HTTP-POST ACS endpoints are accepted. Unknown issuers, mismatched destinations, unregistered ACS URLs, stale/replayed requests, and required signature failures are rejected and correlated in auth.log.

Signing certificates

The IdP uses one global signing key pair:

data/certificates/idp.crt
data/certificates/idp.key

The certificate page displays its subject, serial, UTC validity, and SHA-256 fingerprint. Archive and regenerate moves the current pair into certificates/archive/ and activates a new pair. Restoring an archive first archives the current pair, so the operation is reversible.

Rotation immediately changes generated IdP metadata and SAML signatures. Service providers may cache old metadata or trust the old certificate; refresh their metadata/trust before expecting new responses to validate.

Logs and disk usage

data/logs/debug.log contains startup, storage, validation, and operational diagnostics. data/logs/auth.log traces login and SAML decisions with correlation IDs. Admin → Logs tails either file and appends new lines over a same-origin WebSocket without requiring a page reload.

The admin dashboard shows live configuration gauges, since-restart login/SAML counters, separate valid, invalid/unverifiable, and unsigned AuthnRequest signature counters, and the ten newest high-level activity events. Signature assessment is written to auth.log even when signed requests are optional. Dashboard metrics are held only in process memory and reset whenever TestIdP restarts; auth.log remains the durable source for detailed test-flow diagnosis.

Both files are append-only. This test tool does not rotate, truncate, or cap them: the operator is responsible for monitoring disk usage and removing or archiving old logs while the process is stopped. Passwords, private keys, cookies, raw SAML requests, and encoded SAML responses are not intentionally logged.

License

Test Identity Provider is available under the MIT License.

About

A self-contained, file-based SAML 2.0 test Identity Provider written in Go, with configurable service providers, claims, signing, and an admin portal. Not for production use.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages