Skip to content

DBSC derived cookie schemes should inherit the source scheme's DataProtectionProvider #69619

Open

Description

@rokonec

Summary

The experimental DBSC implementation generates refresh and session cookie schemes from a source cookie scheme, but the generated schemes do not inherit an explicitly configured DataProtectionProvider from that source. Their tickets are protected with the application's default data protection provider instead of the provider configured for the source scheme.

Related to #66478 (DBSC tracking), #68117 (API proposal), and #69618 (derived session cookie path inheritance). The implementation was removed from .NET 11 by #69462 and backported by #69479, so this issue tracks a requirement for any future DBSC reintroduction rather than a .NET 11 servicing change.

What is wrong

  • Generated refresh and session schemes should use the same data protection configuration as their source scheme unless the application explicitly configures them differently.
  • PostConfigureDeviceBoundSessionDerivedCookieOptions.CopyFromSource copies cookie attributes and lifetime settings but not DataProtectionProvider.
  • Copying the provider only in the existing post-configure step would still be too late. AddCookie registers PostConfigureCookieAuthenticationOptions first; that component assigns the default provider and builds the fallback TicketDataFormat before DBSC's post-configure runs.
  • The AddDeviceBoundSession surface does not document that generated schemes otherwise fall back to the default provider.

Why it matters

  • Applications that configure a dedicated provider on the source scheme, such as a dedicated key ring, application name, or key protection, expect credentials derived from that scheme to use the same protection configuration.
  • In deployments where only the source scheme's provider is persisted or shared across instances, derived DBSC cookies can fail to unprotect after restart or on another instance even though source cookies work.

Affected code

Permalinks point to b3d5795c6f, the last main commit before the DBSC removal.

Recommended fix

Assign the source provider during named configuration, before PostConfigureCookieAuthenticationOptions builds the fallback ticket format. Preserve an explicit derived-scheme provider or ticket format, and leave ticket-format construction to the core cookie post-configure so each generated scheme keeps its scheme-specific protector purpose.

private void InheritDataProtection(string sourceScheme, CookieAuthenticationOptions target)
{
    if (target.DataProtectionProvider is not null ||
        target.TicketDataFormat is not null)
    {
        return;
    }

    var source = _services.GetRequiredService<IOptionsMonitor<CookieAuthenticationOptions>>().Get(sourceScheme);
    target.DataProtectionProvider = source.DataProtectionProvider;
}
  • Have the derived-options component also implement IConfigureNamedOptions<CookieAuthenticationOptions>, apply the helper only to generated refresh and session scheme names, and register it for both named configuration and post-configuration in AddDeviceBoundSessionCore.
  • Keep application configuration authoritative: an explicit provider or ticket format on a generated scheme is left unchanged.
  • Document inheritance and override semantics on AddDeviceBoundSession.

Alternatives considered:

  • Copy the provider in the existing post-configure step only. Rejected because the fallback TicketDataFormat already exists by then.
  • Reuse the source scheme's TicketDataFormat instance. Rejected because it removes the per-scheme purpose separation between source, refresh, and session tickets.
  • Require applications to configure each generated scheme. Rejected because generated scheme names are an implementation detail and the current surface does not disclose that requirement.
  • Open question: define the expected derived-scheme behavior when the source uses a custom TicketDataFormat without a provider, or a SessionStore (see DBSC: share source scheme ITicketStore (server-side revocation) with derived cookies #67794).

Compatibility: no public API change is expected. After the change, derived cookies previously protected with a different provider will no longer unprotect, and affected browsers must re-register or re-authenticate. Because the package is experimental and removed from .NET 11, apply this before any future DBSC package is published.

Acceptance criteria

  • When the source scheme has an explicit DataProtectionProvider, tickets issued by the generated refresh and session schemes are protected with that provider rather than the application-default provider.
  • An explicitly configured DataProtectionProvider or TicketDataFormat on a generated scheme is preserved.
  • Source, refresh, and session tickets remain non-interchangeable; DerivedCookieSchemes_RemainDataProtected_AndSchemeKeyed continues to pass.
  • Behavior without an explicit source provider is unchanged.
  • Tests exercise the real AddCookie and AddDeviceBoundSession registration path, not only direct PostConfigure calls, so options-pipeline ordering is covered.
  • AddDeviceBoundSession documentation describes provider inheritance and override semantics.
  • Support Device Bound Session Credentials (DBSC) #66478 records this requirement before DBSC is reintroduced.

Activity

  1. added
    area-authIncludes: authentication, authorization, OAuth, OIDC, and access token validation
    on Sep 30, 2026
  2. rokonec commented on Oct 6, 2026

    @rokonec
    MemberAuthor

    Note for the fix: this issue also affects the challenge protector, not only the derived cookies.

    DeviceBoundSessionChallengeProtector builds its registration and refresh protectors from the application-default IDataProtectionProvider, and it is registered as a single singleton. After this issue is fixed, derived cookies would use the source scheme's provider while challenges would still use the default one. With a dedicated or persisted source provider, cookies would still unprotect after a restart or on another instance while challenges would not.

    The challenge purposes (…Challenge.Registration.v1 and …Challenge.Refresh.v1) also do not include the DBSC scheme name, unlike the derived cookie tickets, whose purpose chain includes the scheme name. With several DBSC schemes, a challenge from one scheme therefore unprotects on another when the principal (and session id) match.

    Inheriting the provider is orthogonal to purpose isolation, so this change does not weaken per-scheme separation, as long as the derived tickets keep their scheme-specific purpose as proposed here. The challenge protector should follow the same model: root it in the source scheme's effective provider and add the DBSC scheme name to its purpose chain.

    I am tracking the multi-scheme work, including this Data Protection isolation, in a new issue linked below.

    Tracking issue: #69682

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-authIncludes: authentication, authorization, OAuth, OIDC, and access token validation

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions