| name | saml-decoder |
|---|---|
| description | Decode and inspect Base64-encoded SAML responses, AuthnRequests, or metadata in the browser with the Secutils.dev SAML Decoder. Build a one-click prefilled URL the user can open by encoding the raw SAML payload string into the fragment of https://tools.secutils.dev/saml#{encoded}. Trigger when the user asks to "decode this SAML response", "inspect a SAML AuthnRequest", "view SAML metadata", or anything that names secutils.dev/saml. |
Hand the user a URL that opens the SAML Decoder pre-loaded with their payload. Decoding, attribute extraction, and pretty-printing all happen in the browser
- the fragment is never sent to the server, so identifiers and claims stay client-side. Both HTTP-POST (Base64) and HTTP-Redirect (Base64 + raw deflate) binding payloads are auto-detected.
| Field | Type | Default | Notes |
|---|---|---|---|
saml |
string | required | Base64-encoded SAML (HTTP-POST body, HTTP-Redirect query, or raw XML). URL-encoded values are accepted; deflate-compressed Redirect payloads are auto-inflated. |
State shape: a raw string - no JSON wrapper. The state value is exactly the SAML string the user supplied (after stripping any surrounding quotes / whitespace).
After URL-safe base64 decoding:
| 4 bytes uncompressed-length (LE u32) | N bytes raw DEFLATE of UTF-8 string |
Pipeline: take the SAML string verbatim → UTF-8 bytes → deflate-raw →
prepend the 4-byte LE u32 of the uncompressed length → base64url
(+→-, /→_, strip =).
Run this on any machine with Node ≥ 18 (no deps):
node -e '
const zlib = require("node:zlib");
const saml = process.argv[1];
const utf8 = Buffer.from(saml, "utf8");
const out = Buffer.concat([Buffer.alloc(4), zlib.deflateRawSync(utf8)]);
out.writeUInt32LE(utf8.length, 0);
const enc = out.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"");
console.log("https://tools.secutils.dev/saml#" + enc);
' 'PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiPi4uLjwvc2FtbHA6UmVzcG9uc2U+'Pass the SAML payload as the first argv (single-quoted so embedded + and
= survive untouched). The fragment is opaque - print the full URL.
The same tools.secutils.dev/saml URL with no fragment loads an empty
decoder if you'd rather just direct the user to paste themselves.
If the user only wants the parsed assertion in chat (no UI), you can decode
the SAML yourself: atob(saml) for POST-binding payloads,
pako.inflateRaw(atob(saml)) for Redirect-binding ones, then read the XML.
The wire format above is independent of that - it's only how state gets into
the configurator UI.
Hand the URL back in a fenced block or inline code. Keep summaries to one sentence; don't paraphrase the assertion contents.
- The SAML payload travels in the URL fragment. The fragment never reaches the Secutils server but is fully visible to anyone with the link. Real production SAML often carries email addresses, group memberships, and other PII - never share these URLs in public channels.
- Strip URL-encoding (
%2B,%3D, …) before encoding; the tool accepts pre-decoded payloads more reliably. - Signature verification requires the IdP's signing certificate and is out of scope for this tool. Use a server-side library for that.