Skip to content

Saving an unregistered Credential subclass through a platform store still throws a raw NotSupportedException, while InMemoryCredentialStore accepts it (#171 fixed only CredentialCache.AddOrReplace) #206

Closed

Description

@matt-edmondson

What's wrong

The #194 fix for #171 added CredentialSerialization.ThrowIfUnknownType (CredentialCache/Storage/CredentialSerialization.cs:146). It is called only from CredentialCache.AddOrReplace (CredentialCache/CredentialCache.cs:208).

A store can also be used directly through CredentialStoreFactory.CreateDefault(...) or CredentialCache.Instance.Store, and its Save skips that check. Each platform store calls CredentialSerialization.Serialize before any native call, so System.Text.Json's polymorphism error escapes:

  • Linux: NativeSecretBuffer.cs:152
  • Windows: WindowsCredentialStore.cs:79
  • macOS: MacOsCredentialStore.cs:77

InMemoryCredentialStore.Save doesn't serialize, so it accepts the same credential.

A probe test on Linux at b2c2772, with class ProbeA : Credential left unregistered:

linux store   Save(unregistered): System.NotSupportedException: Runtime type 'ProbeA' is not supported by polymorphic type 'Credential'
factory store Save(unregistered): System.NotSupportedException
factory store Save(built-in type, no secret service): CredentialStoreException   (control)
in-memory     Save(unregistered): ok

The public CredentialSerialization.Serialize, which the README tells custom-store authors to call, throws the same raw NotSupportedException.

Why it matters

This is the split #171 described, still present one layer down. A consumer whose tests run against InMemoryCredentialStore passes. In production the platform store throws an exception type that nothing around a store call expects, since store failures are otherwise CredentialStoreException. The behaviour is the same on all three platforms because the throw happens before any native code runs.

Suggested fix / acceptance criteria

  • Call ThrowIfUnknownType at the top of CredentialSerialization.Serialize/SerializeToString, so every platform store and every custom store built on them gets it. Also call it in InMemoryCredentialStore.Save.
  • Save of an unregistered subclass throws the same exception, with the same message, from the in-memory store, the platform stores and CredentialCache.AddOrReplace.
  • Add a test that calls Save directly on InMemoryCredentialStore and on the default store, with an unregistered subclass.

Activity

  1. matt-edmondson commented on Nov 8, 2026

    @matt-edmondson
    ContributorAuthor

    Triage


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions