A pure-Go (no cgo), MRI-faithful port of Ruby's ruby-saml gem —
the SAML 2.0 Service Provider toolkit exposed as OneLogin::RubySaml.
It mirrors the ruby-saml surface while standing on the pure-Go SAML ecosystem —
github.com/crewjam/saml for the SAML schema
types and github.com/russellhaering/goxmldsig
for XML digital-signature validation — rather than reimplementing XML signing or
the SAML schema from scratch. It is the SAML backend for
go-embedded-ruby but is a
standalone, reusable module with no dependency on the Ruby runtime.
ruby-saml (OneLogin::RubySaml) |
this module |
|---|---|
Settings |
Settings — idp_sso_target_url, idp_cert / idp_cert_fingerprint, sp_entity_id, assertion_consumer_service_url, name_identifier_format, private_key / certificate, want_assertions_signed, signature_method, digest_method |
Authrequest#create |
Authrequest.Create — SP-initiated SSO redirect URL + deflate+base64 SAMLRequest |
Response#is_valid?, #name_id, #attributes, #sessionindex, #status_code, #issuers |
Response |
Metadata#generate |
Metadata.Generate — SP metadata XML |
IdpMetadataParser#parse |
IdpMetadataParser.Parse |
Logoutrequest#create, SloLogoutresponse#create |
Logoutrequest, SloLogoutresponse — SP-initiated SLO |
ValidationError |
ValidationError |
Response.IsValid mirrors ruby-saml's is_valid?, collecting each failure into
Errors:
- Signature — verified against the configured
idp_certor itsidp_cert_fingerprint(SHA-1 / SHA-256), via goxmldsig. - Conditions —
NotBefore/NotOnOrAfterwithallowed_clock_drift. - Audience —
AudienceRestrictionmatchessp_entity_id. - Destination — matches
assertion_consumer_service_url. - InResponseTo — matches the originating
AuthnRequestID. - Issuer — matches
idp_entity_id. - Status / Assertion count —
Success, exactly one assertion.
All time-dependent checks read an injectable clock, so validation is
deterministic and timezone-independent (the suite runs under TZ=UTC).
settings := &saml.Settings{
IdPSSOTargetURL: "https://idp.example.com/sso",
IdPCert: idpCertPEM,
SPEntityID: "https://sp.example.com/metadata",
AssertionConsumerServiceURL: "https://sp.example.com/acs",
NameIdentifierFormat: saml.NameIDFormatEmail,
}
// SP-initiated SSO.
authn := &saml.Authrequest{}
redirect, _ := authn.Create(settings, "" /* relayState */)
// Consume the IdP response.
resp, _ := saml.NewResponse(base64SAMLResponse, settings)
resp.ExpectedInResponseTo = authn.UUID
if resp.IsValid() {
fmt.Println(resp.NameID(), resp.Attributes())
}Every fixture — RSA keys, X.509 certificates, and signed SAML documents (valid,
tampered, expired, wrong-audience, wrong-destination, wrong-issuer,
wrong-InResponseTo) — is embedded; no network is used. The suite runs with
-race and enforces 100% statement coverage across three OSes and the
six supported 64-bit architectures (amd64, arm64, riscv64, loong64, ppc64le,
and big-endian s390x).
GOWORK=off TZ=UTC go test -race -cover ./...BSD-3-Clause — see LICENSE. Copyright (c) 2026, the go-ruby-saml/saml authors.
Being pure Go (CGO=0), this library also compiles to WebAssembly — both
GOOS=js GOARCH=wasm (browser / Node.js) and GOOS=wasip1 GOARCH=wasm (WASI).
CI builds both targets on every push, alongside the six 64-bit native/qemu arches.
GOOS=js GOARCH=wasm go build ./... # browser / Node
GOOS=wasip1 GOARCH=wasm go build ./... # WASI (wasmtime, wasmer, wasmedge, …)