christian-riesen/base32
A strict RFC 4648 Base32 encoder and decoder for PHP, with support for the extended hex alphabet. Decoding rejects malformed input, and the package includes test vectors and unit tests.
otpauth:// provisioning standard and every mainstream authenticator app expects. This package removes a small but genuinely bug-prone piece of hand-rolled code (alphabet, padding, case handling, canonical trailing bits) from an authentication feature. Decision: buy — one Composer line versus a spike-plus-implementation-plus-test-vector workstream.Base32Hex class covers the extended-hex alphabet if a partner, DNSSEC-adjacent consumer, or legacy integration demands it.hash_equals verification. Making that boundary explicit early prevents the most common scoping error in this area: assuming "Base32 library" means "2FA library." It also makes the follow-on build/buy decision (OTP framework, TOTP verification, drift window) a separate, cleanly-argued call.Adopt when:
otpauth:// URI construction, or an external system that mandates Base32 (legacy APIs, hex-alphabet consumers).strtoupper, strip whitespace) becomes your code's responsibility.Look elsewhere, or re-scope, when:
spomky-labs/otphp or pragmarx/google2fa — they own the time step, HMAC, drift window, and constant-time comparison. This package is already a transitive dependency of that stack, so adding it directly is redundant.base64_encode or a ULID-style helper.isBase32()), no padding-preserving output mode, no timing-safe primitives. All are trivial to add locally, but must be scoped as work rather than assumed.Rule of thumb for the decision meeting: if Base32 appears in exactly one place and you're already adopting an OTP library, don't add this directly. If it appears in several places, add it and wrap it behind an interface.
For executives: A zero-cost, MIT-licensed, single-purpose library that lets us ship authenticator-app interoperability and two-factor enrolment on the existing roadmap instead of opening a new engineering workstream. It replaces hand-rolled encoding — a classic source of subtle, hard-to-test security bugs — with an implementation that passes the published RFC test vectors and whose strict decoder was hardened in response to a real audit finding. Integration cost is roughly one Composer line plus a thin wrapper class; ongoing cost is a version pin during routine dependency updates. The risks are narrow and known: it is encoding only (no cryptographic claims), and it is a small single-maintainer project — mitigated by pinning to a maintained release line and confining usage to one service class behind an interface.
For engineering: Drop-in via composer require christian-riesen/base32. Static API (Base32::encode() / Base32::decode()), PSR-4 autoloaded, no container registration, no config, no framework coupling. Three things to build around it: (1) wrap it in a Base32Codec interface so tests and any future swap don't hit static calls directly; (2) if you decode user-supplied input, normalize first — uppercase and strip whitespace — then catch \InvalidArgumentException, because 2.0 is strict and case-sensitive by design; (3) use hash_equals() on decoded bytes for any comparison, since the package exposes no timing-safe primitives. Never use Base32 and Base32Hex interchangeably. If we later need full OTP verification, we adopt an OTP library and this becomes its transitive dependency rather than a direct one — no wasted work either way.
How can I help you explore Laravel packages today?