On this page:
14.1 Token storage
14.2 Input handling
14.3 Concurrency and ETags
14.4 The authorization flow
14.5 Sensitive data in memory
14.6 What bcnav doesn’t do
9.3

14 Appendix: Security🔗

bcnav handles sensitive data—OAuth tokens, API credentials, and access to your business systems. This appendix describes the security measures built into the library and offers guidance for using it safely.

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—the encrypted data can only be decrypted by your user profile on that specific machine. The encrypted blob is stored in your user preferences directory with restrictive file permissions (only your account can read or write the file). DPAPI is the same mechanism that browsers like Chrome use for storing saved passwords on Windows.

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—the token persists securely and bcnav can refresh it automatically when it expires. If you prefer to handle token storage yourself, use the lower-level bc-authenticate/interactive! and manage the returned token struct directly.

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—you just fetched the data, you’re the only one working with it, and you don’t want to manually track ETags.

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—if it doesn’t, authentication fails. This prevents attackers from tricking you into authenticating with a token they control.

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—bcnav needs the token to make API calls. If an attacker can read your process memory, they could extract the token. This is true of any program that handles credentials.

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—certificate pinning would break corporate proxies and network inspection tools that many organizations use.

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.