Summary
On any host whose WebCrypto lacks Ed25519, every authenticated request 500s as soon as the OIDC provider is enabled — which is the default whenever the MCP server is on. Reproduced on StackBlitz/WebContainer (Node 22.22.3) with @objectstack/* 16.1.0.
Symptom
Sign-in succeeds, then the first /api/v1/auth/get-session returns 500:
[Better Auth]: OperationError DOMException [OperationError]: The operation failed for an operation-specific reason
at Object.cfrgGenerateKey (builtins.js:74:3727)
at async SubtleCrypto.generateKey (builtins.js:90:10600) {
[cause]: TypeError: o.run is not a function
at Module.generateKeyPair (jose/dist/webapi/key/generate_key_pair.js:111:26)
at generateExportedKeyPair (better-auth/dist/plugins/jwt/utils.mjs:42:48)
at Module.createJwk (better-auth/dist/plugins/jwt/utils.mjs:61:58)
at resolveSigningKey (better-auth/dist/plugins/jwt/sign.mjs:76:26)
}
[AuthManager] better-auth returned error: 500
GET /api/v1/auth/jwks reproduces it with no credentials at all — that route generates the key pair on first use.
Root cause
plugin-auth registers better-auth's JWT plugin as jwt({ schema: buildJwtPluginSchema() }) — no jwks.keyPairConfig, so better-auth's default EdDSA (Ed25519) applies.
- WebContainer's WebCrypto has no Ed25519, so
cfrgGenerateKey throws.
- The plugin's
after hook on /get-session signs a set-auth-jwt header for every session, so the failure hits normal cookie login — not just OAuth clients.
- It is on by default in a plain dev server:
resolveOidcProviderEnabled(pluginConfig) falls back to isMcpServerEnabled(), which defaults to true.
So an app that never asked for OIDC still gets an unusable login on such a host, and the error message points at better-auth rather than at the algorithm choice.
Workaround
OS_OIDC_PROVIDER_ENABLED=false drops the plugin and login works (verified end to end: sign-in/email 200, get-session 200, jwks 404). Apps can't do better than this today because the algorithm isn't configurable through AuthPluginConfig.
Suggested fix
Any of, roughly in order of preference:
- Pass an algorithm the host supports — e.g. probe
crypto.subtle.generateKey({name:'Ed25519'}, …) once at startup and fall back to ES256 when it throws.
- Expose
jwks.keyPairConfig through AuthPluginConfig so an app can pin ES256/RS256.
- At minimum, catch the keygen failure and degrade the OIDC provider (there is already
getDegradedAuthFeatures) instead of 500ing the whole session path — plus an error that names Ed25519.
Environment
Summary
On any host whose WebCrypto lacks Ed25519, every authenticated request 500s as soon as the OIDC provider is enabled — which is the default whenever the MCP server is on. Reproduced on StackBlitz/WebContainer (Node 22.22.3) with
@objectstack/*16.1.0.Symptom
Sign-in succeeds, then the first
/api/v1/auth/get-sessionreturns 500:GET /api/v1/auth/jwksreproduces it with no credentials at all — that route generates the key pair on first use.Root cause
plugin-authregisters better-auth's JWT plugin asjwt({ schema: buildJwtPluginSchema() })— nojwks.keyPairConfig, so better-auth's default EdDSA (Ed25519) applies.cfrgGenerateKeythrows.afterhook on/get-sessionsigns aset-auth-jwtheader for every session, so the failure hits normal cookie login — not just OAuth clients.resolveOidcProviderEnabled(pluginConfig)falls back toisMcpServerEnabled(), which defaults totrue.So an app that never asked for OIDC still gets an unusable login on such a host, and the error message points at better-auth rather than at the algorithm choice.
Workaround
OS_OIDC_PROVIDER_ENABLED=falsedrops the plugin and login works (verified end to end:sign-in/email200,get-session200,jwks404). Apps can't do better than this today because the algorithm isn't configurable throughAuthPluginConfig.Suggested fix
Any of, roughly in order of preference:
crypto.subtle.generateKey({name:'Ed25519'}, …)once at startup and fall back toES256when it throws.jwks.keyPairConfigthroughAuthPluginConfigso an app can pinES256/RS256.getDegradedAuthFeatures) instead of 500ing the whole session path — plus an error that names Ed25519.Environment
@objectstack/*16.1.0, Node 22.22.3, StackBlitz WebContainer