Skip to content

Allow only publishable key prefixes in browser clients #14

Closed

Description

@lui01212

Following your suggestion in gatepost-dev/js#9, I would like to agree the browser key rule in
client.md before changing the client or field code.

The guard now handles white space correctly, but it still refuses only the two known secret
prefixes. If a new secret prefix were introduced, that key could pass the guard. I favour
allowing only the documented publishable prefix, nipost_pk_, when a browser client has a key.
This is a check of the key's kind, not proof that the gateway will accept it.

Proposed behaviour

  • With no apiKey, keep the existing behaviour: the client sends no X-API-Key header. The field
    still makes no request without a key.
  • Classify a supplied browser key after trimming surrounding white space, as the current fix
    does. Allow nipost_pk_ keys, including the test and live forms used in the docs and fixtures.
  • Reject both known secret prefixes and any unrecognised prefix before sending a request.
    The client should raise a TypeError without including the key in the message.
  • Keep server clients unchanged. Secret keys remain valid there, including in the PHP client.

Compatibility and wording

This would reject browser keys with arbitrary prefixes that the current guard lets through.
Mock transports and applications using custom gateways may rely on that behaviour, so the
change needs a clear release note and an agreed release level.

For the field, should an unrecognised key use the existing secret_key state and message, or
should the spec distinguish an unsupported key from a known secret? Calling every unrecognised
key a secret would be misleading. I would like to settle that before extending the field rule.

If this direction is acceptable, I can prepare the wording in client.md and SEC-1, plus any
needed wording in field.md. The implementation can then cover publishable, absent, known secret,
unrecognised and whitespace-padded keys with synthetic fixtures only.

I used Codex to help draft this proposal and check the current docs.

Activity

  1. MobileMage commented on Oct 4, 2026

    @MobileMage
    Collaborator

    The direction is agreed: in a browser, a client accepts only a key with the publishable prefix nipost_pk_, and refuses everything else before it sends a request. An allow list fails safely if a new secret prefix appears, and the packages are still alphas, so this is the cheap time to change it.

    Answers to your points:

    Client

    • No key: unchanged. No X-API-Key header.
    • Trim, then classify. nipost_pk_ passes. Anything else throws a TypeError before any request, and the message never holds the key.
    • Keep two messages, so a developer knows which mistake they made: the existing one for the two known secret prefixes ("A web page must not hold a secret key..."), and a new one for a key that is not a publishable key.
    • Server clients: unchanged, in both languages. A secret key is valid where no document exists.

    Field

    • Keep secret_key for the two known secret prefixes. You are right that calling an unknown key a secret would mislead.
    • Add one error code, unsupported_key, for a key that is neither publishable nor a known secret. It behaves as secret_key does today: no request, no location button, the state error, one console error for the developer that never holds the key, and the event error in place of each lookup or reverse call that was due.
    • The person filling in the form does not need to know which kind of key the page holds. So the text on screen can be the same sentence as secret_key shows today. It still needs its own key in the catalogue (unsupported_key), because each state names its message.

    Where the wording goes

    • standards/CODING_STANDARDS.md, SEC-1: client-side code accepts only publishable keys.
    • client.md: the browser rule is not written there today. It lives only in SEC-1 and in the js client. Add it, with the two refusals and the trim.
    • field.md: the Interface row of the error event, the Lookups and Location rules, the Failures table (two new rows, for a lookup and for reverse), the States table and the Messages table.
    • messages/field_en.arb: the new key.
    • The doc tests in scripts/tests/ pin most of these lines, so they change in the same pull request.

    Release level

    • It adds a public error code, so the spec goes to 0.4.0, as a minor change. Say in the change notes that a browser key with any other prefix now stops working, with the mock and custom gateways named as the cases to check.
    • Order: the spec pull request first. Then js moves its spec pointer and changes the client, the field and the React wrapper's types, with a change file for each package. The PHP package only moves its pointer and version.

    Fixtures

    • fixtures/keys.json already has publishable and secret mock keys. An unrecognised key in a test can be any made-up string that no gateway issues, such as abc. No real key anywhere.

    If you take the spec wording, open the pull request against main and I will review it there. Thank you for settling the rule before the code.

  2. added a commit that references this issue on Oct 28, 2026
    b8a2119
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions