Birdigo.ShareLinks 2.0.0
Birdigo.ShareLinks
Stateless, self-contained shareable links. A URL is serialized, optionally DEFLATE-compressed, Base64Url-encoded and version-prefixed, then carried inside the link itself. The receiving app decodes it locally and navigates — no shortener service, no database, no resolve API, no network round-trip. The application is its own resolver.
Usage
using Birdigo;
string shareUrl = ShareableLink.Encode("https://app.birdigo.com/organizations/628/leads/300?section=Notes¬eId=1");
// https://app.birdigo.com/s/v1.AG9yZ2FuaXphdGlvbnMvNjI4L2xlYWRzLzMwMD9zZWN0aW9uPU5vdGVzJm5vdGVJZD0x
string url = ShareableLink.Decode(shareUrl);
// https://app.birdigo.com/organizations/628/leads/300?section=Notes¬eId=1
On navigation hot paths, where the input is attacker-supplied, prefer the non-throwing variant:
string target = ShareableLink.TryDecode(shareUrl, out string? url)
? url!
: "https://app.birdigo.com/dashboard";
That is the entire API: Encode, Decode, TryDecode, plus ShareLinkException to catch.
Any origin works
The origin is read from the URL you pass in and re-attached on the way out. Only the path, query and fragment travel inside the token.
ShareableLink.Encode("https://app.deltax.com/tenant/628/sales/300?company=Notes");
// https://app.deltax.com/s/v1.AHRlbmFudC82Mjgvc2FsZXMvMzAwP2NvbXBhbnk9Tm90ZXM
A staging link therefore decodes back to staging, and the same path on two hosts produces the same token. It also means the host in a share URL is attacker-controlled — never use it to select a tenant.
Behaviour worth knowing
Encode requires an absolute URL. A relative string like leads/300 throws ArgumentException
rather than silently assuming an origin.
Round trips are equivalent, not byte-identical. Encode parses its input as a Uri, which
normalises: a literal space becomes %20, https://app.birdigo.com gains a trailing slash, an
uppercase host is lowercased, and an explicit :443 is dropped.
Compression is automatic. Long, repetitive URLs are DEFLATE-compressed; short ones are stored raw, because the block overhead would make them longer. A token is never larger than its uncompressed form. Nothing to configure.
ShareableLink is static, stateless and thread-safe. No DI registration needed.
Token format
{origin}/s/v1.<Base64Url(frame)>
The frame is a flags byte followed by the payload — raw UTF-8, or raw DEFLATE (RFC 1951) of it.
Only bit 0x01 of the flags byte is defined; it marks a compressed payload. Reserved bits must be
zero, and a token that sets one is rejected. Because the token records its own compression state, the
decoder never has to guess.
The v1. prefix is what keeps old links alive. A future codec version ships behind a v2. prefix
while v1 tokens keep resolving; the URL shape never changes.
Security
This is an encoding, not encryption. Anyone can read, edit or forge a token — leadId=300 becomes
leadId=999 in one line. That is acceptable only because a token is a routing hint.
Never place access tokens, session ids, secrets, permission flags, or any user or tenant id you intend to trust inside a share link. No authorization decision may depend on token contents. Re-authorize the decoded destination server-side, exactly as if the user had typed that URL by hand — because they can.
Requirements
.NET 10. No dependencies beyond the base class library.
No packages depend on Birdigo.ShareLinks.
.NET 10.0
- No dependencies.