Generates a random base32 TOTP shared secret for enrolling a user in two-factor authentication.
The secret is RFC 4648 base32, uppercase, with no padding, drawn from a cryptographic RNG. This is the first step of enrollment: store the secret on the user record (ideally wrapped via Inlay_TOTP_WRAP), then build a provisioning URI and QR code so the user can add it to an authenticator app.
Declaration
Inlay_TOTP_GENERATE_SECRET(20)
Parameters
ByteLength
Optional
raw entropy size in bytes, 10 to 64, default 20. 20 bytes is 160 bits, the RFC 4226 recommended size that authenticator apps expect.
Returns
JSON string
On success returns {"ok":true,"secret":"BASE32SECRET"}. Returns {"ok":false,"message":"invalid_args: byteLength must be 10..=64"} when ByteLength is out of range.
Use Cases
Mint a fresh secret when a user opts in to 2FA.
Re-key an account by generating a new secret and re-enrolling.
Notes
The returned base32 length is (ByteLength * 8 + 4) / 5 characters; 20 bytes yields 32 characters.
The plugin keeps no copy of the secret. Persist it yourself, encrypted where possible.
Builds an otpauth:// provisioning URI that authenticator apps can import.
The URI follows the Google Authenticator key URI format and is accepted by Google Authenticator, 1Password, Authy and similar apps. Pass it to Inlay_TOTP_QR to render a scannable code, or show the raw URI for manual entry. The algorithm is fixed to SHA1, the universally supported choice.
the base32 shared secret from Inlay_TOTP_GENERATE_SECRET.
AccountName
Required
the user identity shown in the app, typically an email or login.
Issuer
Optional
your app or company name, shown as the entry label. Omitted when empty.
Digits
Optional
code length, 6 to 8, default 6. Must match Inlay_TOTP_VERIFY.
Period
Optional
time step in seconds, 15 to 120, default 30. Must match Inlay_TOTP_VERIFY.
Returns
JSON string
On success returns {"ok":true,"uri":"otpauth://totp/..."}. Returns {"ok":false,"message":"..."} on an invalid base32 secret, empty account name, or out-of-range Digits/Period.
Use Cases
Generate the URI behind an enrollment QR code.
Offer a copyable setup link for users who cannot scan.
Notes
The label, account, and issuer are percent-encoded per RFC 3986.
Digits and Period are embedded in the URI, so the authenticator and your verify settings stay in sync.
Renders an otpauth:// URI as a scannable QR code, as a PNG container or an HTML img tag.
Feed this the URI from Inlay_TOTP_PROVISIONING_URI to produce the code a user scans during enrollment. The PNG form is suitable for a container or image field; the HTML form embeds a base64 data URL for a Web Viewer.
"png" (default) returns a PNG container, "html" returns an img tag in JSON.
PixelsPerModule
Optional
pixel size of each QR cell, 1 to 20, default 6.
Returns
JSON string
With Format "png" returns a PNG container (binary, not JSON) for an image field. With Format "html" returns {"ok":true,"html":"<img src=\"data:image/png;base64,...\" ...>"}. Returns {"ok":false,"message":"..."} on an empty URI, bad Format, out-of-range PixelsPerModule, or render failure.
Use Cases
Display an enrollment QR code in a container field.
Embed the code in a Web Viewer using the HTML data URL.
Notes
Typical otpauth URIs fit in a small QR version and render in a few milliseconds.
The URI is encoded verbatim; build it with Inlay_TOTP_PROVISIONING_URI so it is well formed.
Computes the current TOTP code for a base32 secret at the system clock.
This is the code an authenticator app would show right now for the same secret. It is meant for admin preview, testing, and confirming an enrollment, not for login checks. Production logins should use Inlay_TOTP_VERIFY, which tolerates clock drift and mints a session.
time step in seconds, 15 to 120, default 30. Match enrollment.
Returns
JSON string
On success returns {"ok":true,"code":"123456"}. Returns {"ok":false,"message":"..."} on an invalid base32 secret or out-of-range Digits/Period.
Use Cases
Show the expected code on an admin screen to confirm a user enrolled correctly.
Drive automated tests of a 2FA flow.
Notes
The algorithm is HMAC-SHA1, per RFC 6238.
The code changes every Period seconds. Do not use this for verification; use Inlay_TOTP_VERIFY.
Checks a user-entered TOTP code against a secret and, on success, mints a session token.
This is the function to call at login. It accepts codes within a small time window to tolerate clock drift, compares in constant time, and on a match returns a session_id you can pass to Inlay_TOTP_SESSION_VERIFY to gate later scripts or privilege-set calcs.
The session_id is an opaque token held in plugin memory with an 8-hour sliding idle timeout. The plugin does not track used codes, so store the last accepted time step yourself and reject replays.
the code the user entered, exactly Digits decimal digits.
Window
Optional
time steps accepted on each side of now, 0 to 10, default 1.
Digits
Optional
code length, 6 to 8, default 6. Must match enrollment.
Period
Optional
time step in seconds, 15 to 120, default 30. Must match enrollment.
Returns
JSON string
On a match returns {"ok":true,"valid":true,"skew":0,"session_id":"...","expires_at":1700000000} where skew is the matched offset within the window. On no match returns {"ok":true,"valid":false,"skew":null}. Returns {"ok":false,"message":"..."} on a malformed code, invalid base32 secret, or out-of-range argument.
Use Cases
Verify the 2FA code during login and start an authenticated session.
Step up an existing session by requiring a fresh code for a sensitive action.
Notes
skew is the matched offset in the range -Window..=Window, useful for detecting a drifting clock.
Comparison is constant-time across all candidates to avoid timing leaks.
Sessions live only in this plugin instance and are lost when FileMaker restarts.
Encrypts a TOTP secret so it can be stored at rest instead of the raw base32 value.
Uses ChaCha20-Poly1305 with a key derived as SHA-256 of the passphrase. Store the returned blob on the user record and recover the secret at login with Inlay_TOTP_UNWRAP. The passphrase should be a high-entropy server-side secret, for example one held in a protected FileMaker field, not a user password, because the key derivation is a single fast hash rather than Argon2 or PBKDF2.
the encryption passphrase. Keep it out of version control and the data file's open access.
Returns
JSON string
On success returns {"ok":true,"blob":"BASE64URLBLOB"} encoding a 12-byte random nonce, the ciphertext, and a 16-byte Poly1305 tag. Returns {"ok":false,"message":"..."} on an invalid base32 secret or empty passphrase.
Use Cases
Store the 2FA secret encrypted in the user record.
Rotate the server passphrase by unwrapping with the old one and wrapping with the new one.
Notes
The nonce is random, so wrapping the same secret twice yields different blobs.
The plugin never stores the passphrase or the secret; both are caller-managed.
Decrypts a blob produced by Inlay_TOTP_WRAP back into the original base32 secret.
Call this at login to recover the stored secret before verifying a code with Inlay_TOTP_VERIFY. The authenticated cipher detects a wrong passphrase or any tampering and fails closed rather than returning garbage.
JSON string
On success returns {"ok":true,"secret":"BASE32SECRET"}. Returns {"ok":false,"message":"decrypt_failed: ..."} on a wrong passphrase, tampered or truncated blob, or bad encoding.
Use Cases
Recover the secret at login so a code can be verified.
Confirm a passphrase rotation by unwrapping then re-wrapping.
Notes
A wrong passphrase and a tampered blob both surface as decrypt_failed; the two are not distinguished.
Keep the recovered secret in memory only as long as needed; do not persist it unencrypted.
Generates a batch of one-time recovery codes for users who lose access to their authenticator.
Show these once at enrollment and have the user save them somewhere safe. Store them however you prefer, plain or hashed, and clear each one as it is redeemed. The plugin does not track which codes have been used, so enforcing single use is the solution's responsibility.
Checks whether a session token from Inlay_TOTP_VERIFY is still valid and refreshes its idle timer.
Use this as a lightweight gate inside scripts and privilege-set calcs after the user has passed the 2FA check once. A valid call slides the session's idle timeout forward, so active users stay signed in while idle ones expire.
the session_id returned by a successful Inlay_TOTP_VERIFY.
Returns
JSON string
When valid returns {"ok":true,"valid":true,"expires_at":1700000000}. Otherwise returns {"ok":true,"valid":false,"reason":"unknown"} for a token the store has never seen, or "expired" for one past its idle window.
Use Cases
Gate a sensitive script step on a still-valid 2FA session.
Drive a per-record privilege-set calc that requires recent 2FA.
Notes
Sessions live only in this plugin instance; restarting FileMaker ends every session.
The idle timeout is 8 hours, refreshed on each successful verify. At most 5 sessions are kept, with the least recently used evicted on overflow.