Skip to content

smrt-profiles OidcIdentityCollection rejects issuer/subject at runtime #1289

@willgriffin

Description

@willgriffin

Summary

UserCollection.getOrCreateFromOidc() can fail during OIDC login because smrt-profiles calls OidcIdentity.findBySubject(issuer, subject), which creates an OidcIdentityCollection and then runs findOne({ where: { issuer, subject } }).

At runtime, the collection field validator rejects issuer before SQL executes, even though the generated manifest/schema and database table include issuer and subject.

Observed error

Invalid WHERE clause field: 'issuer'. Field does not exist on OidcIdentity. Valid fields: context, created_at, id, profile_id, slug, updated_at\n```\n\nStack shape observed from a SvelteKit OIDC callback:\n\n```text\nOidcIdentityCollection.convertWhereKeys\nOidcIdentityCollection.findOne\nOidcIdentity.findBySubject\ncreateProfileFromOidc\nUserCollection.getOrCreateFromOidc\ncompleteHappyVerticalLogin\n```\n\n## Expected behavior\n\n`OidcIdentityCollection.findBySubject(issuer, subject)` should accept `issuer` and `subject` as valid fields and query `oidc_identities.issuer` / `oidc_identities.subject`.\n\n## Evidence\n\nThe installed `@happyvertical/smrt-profiles` manifest contains the fields on `OidcIdentity`:\n\n- `profileId`\n- `provider`\n- `issuer`\n- `subject`\n- `email`\n- `lastUsedAt`\n\nThe generated schema for `oidc_identities` includes columns:\n\n- `profile_id`\n- `provider`\n- `issuer`\n- `subject`\n- `email`\n- `last_used_at`\n\nThe production database table also had those columns. The failure happened before SQL execution in runtime field validation.\n\n## Likely cause\n\n`SmrtCollection.create()` caches fields using runtime field metadata from `fieldsFromClass(this._itemClass)`. For `OidcIdentity`, that runtime field cache appears to include only base fields plus `profileId`, not the manifest/schema fields (`issuer`, `subject`, etc.).\n\nSo `convertWhereKeys()` sees the valid fields as only:\n\n```text\ncontext, created_at, id, profile_id, slug, updated_at\n```\n\nand rejects `issuer`.\n\n## Possible fixes\n\nOne of these likely needs to happen upstream:\n\n1. Decorate all runtime queryable `OidcIdentity` fields (`provider`, `issuer`, `subject`, `email`, `lastUsedAt`) so `fieldsFromClass()` sees them.\n2. Make collection field validation use the registered ObjectRegistry/manifest schema fields when available, so runtime validation is consistent with generated schema.\n3. Add a regression test around `UserCollection.getOrCreateFromOidc()` / `OidcIdentityCollection.findBySubject()` ensuring `issuer` and `subject` are accepted.\n\n## Impact\n\nThis breaks OIDC login flows that rely on `UserCollection.getOrCreateFromOidc()`.\n\nIn `willgriffin.dev`, I added a temporary app-level workaround that creates/updates the `User` directly from validated OIDC claims and keeps tenant/session setup unchanged, but that bypasses profile/OIDC identity linking and should be removed once this is fixed upstream.\n

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions