Feature: Subject matching modes for federated client credentials
Feature description
Add a match mode to each federated client identity so the sub claim can be matched by prefix or suffix, not just exact equality. Default stays exact, so upgrades don't change behaviour.
Pitch
Federated client credentials compare sub with strict equality (jwt.WithSubject in federated_client_auth.go). That breaks for any issuer whose subject includes a per-instance identifier. Fly.io OIDC tokens use iss: https://oidc.fly.io/<org> and sub: <org>:<app>:<machine-name>, so an app running on three machines can produce three subjects.
It also looks like we can't configure multiple subjects to work around this as FederatedIdentityForIssuer matches on issuer alone and returns the first match, so a second identity with the same issuer would never be reached. Client lookup itself is fine since #1342, client_id comes from the form. This is only the sub comparison afterwards, not a rehash of #1333/#1122.
Proposal: add subjectMatch with the values equals (default), startsWith, endsWith to OidcClientFederatedIdentity alongside subject. Credentials are stored as a JSON document column, so no migration should be necessary as existing identities will unmarshal to the zero value and map to equals. I opted to skip contains as it can more easily overmatch and I don't have a use case, but easy to add if needed.
The API should also reject an empty subject unless the mode is equals as a blank prefix would match every subject the issuer signs. The existing "empty subject defaults to the client ID" fallback should stay equals only for the same reason.
#1712 mentions subject mapping for federated user credentials, so the naming should probably line up with whatever that lands on. I'd love to implement if the approach looks right.
Source: pocket-id/pocket-id