14 Appendix: Security
bcnav handles sensitive data—
14.1 Token storage
When you use bc-authenticate/cached!, bcnav stores your OAuth tokens using your operating system’s native credential storage:
On Windows, tokens are encrypted using the Data Protection API (DPAPI). This ties the
encryption to your Windows user account—
On macOS, tokens are stored in the login Keychain using the security command-line tool. The Keychain is unlocked when you log into your Mac and provides hardware-backed encryption on machines with a Secure Enclave. You can inspect or delete stored tokens using Keychain Access.app or by running security find-generic-password -s bcnav -a bcnav-token.
Both approaches mean you don’t need to enter credentials on every session—
14.2 Input handling
OAuth tokens and other data that bcnav processes may come from external sources (Microsoft’s authentication servers, Business Central’s API responses). The library treats this data as potentially untrusted and handles it defensively.
When interacting with system commands (such as PowerShell on Windows or the security CLI on macOS), bcnav never embeds token data directly into command strings. Instead, data is passed through stdin or other mechanisms that prevent shell injection attacks. This matters because a malicious OAuth server could theoretically return a crafted token designed to exploit naive string concatenation.
The interactive authentication flow displays a success or error page in your browser after the OAuth redirect. Error messages from the OAuth provider are HTML-escaped before being inserted into this page, preventing cross-site scripting even if an attacker crafts a malicious error response.
When following pagination links in API responses, bcnav validates that the @odata.nextLink URL points to the expected Business Central API domain. This prevents a compromised or malicious server from redirecting the client to arbitrary URLs (a class of vulnerability called server-side request forgery).
14.3 Concurrency and ETags
Business Central’s API uses ETags for optimistic concurrency control. When you update or delete a record, you can provide an ETag value that represents the version of the record you’re modifying. If someone else changed the record since you retrieved it, the API will reject your modification rather than silently overwriting their changes.
By default, bcnav sends If-Match: * (a wildcard) for update and delete operations when
you don’t provide an explicit ETag. This tells Business Central "apply this change regardless
of the current version." For interactive REPL sessions and simple scripts, this is usually
what you want—
For production code where concurrent access is possible, you should retrieve the ETag from the response when you fetch a record and pass it to the update function:
(define cust (customer "some-id")) (define etag (hash-ref cust 'odata.etag)) (customer-update "some-id" (hash 'phoneNumber "555-1234") #:etag etag)
If the record was modified between your fetch and update, you’ll get a 412 Precondition Failed error rather than silently overwriting changes.
14.4 The authorization flow
Interactive authentication opens a browser and starts a local web server to receive the OAuth callback. This is a standard pattern for desktop applications (used by CLIs like gcloud and az), but it’s worth understanding what happens:
The local server binds only to 127.0.0.1 (localhost), not to all network interfaces. This means other machines on your network cannot connect to the callback server. The server only accepts requests to the specific callback path (/oauth/authorization), returning 404 for anything else.
To prevent cross-site request forgery, bcnav generates a cryptographically random state parameter
for each authentication attempt. The callback handler verifies that the state in the response
matches the state that was sent—
The authorization flow has a default timeout of 5 minutes. If you don’t complete the browser login within that window, the local server shuts down and authentication fails. This prevents the server from running indefinitely if you abandon the flow.
14.5 Sensitive data in memory
Access tokens are held in memory while your program runs. This is unavoidable—
The practical risk is low: an attacker who can read your process memory typically already has enough access to do whatever they want on your machine. Still, be mindful of this in security-sensitive environments. Tokens are short-lived (typically one hour), and if you’re using interactive authentication with token caching, the refresh token allows bcnav to get new access tokens without storing long-term credentials in memory.
14.6 What bcnav doesn’t do
A few things are explicitly outside bcnav’s scope:
bcnav doesn’t implement certificate pinning. It relies on your operating system’s certificate
store and TLS implementation to validate that it’s actually talking to Microsoft’s servers.
For most users, this is the right tradeoff—
bcnav doesn’t provide field-level encryption or data masking. The data you retrieve from Business Central is decrypted and available in your program. If you’re handling particularly sensitive data, you’re responsible for protecting it appropriately.
bcnav doesn’t audit or log API operations by default. You can enable request logging with (current-bc-log-requests #t), but this is for debugging, not security auditing. Business Central maintains its own audit logs for compliance purposes.