Skip to content

Device Bound Session Credentials (DBSC) for cookie authentication (prototype)#67388

Open
rokonec wants to merge 30 commits into
mainfrom
roman/dbsc-prototype
Open

Device Bound Session Credentials (DBSC) for cookie authentication (prototype)#67388
rokonec wants to merge 30 commits into
mainfrom
roman/dbsc-prototype

Conversation

@rokonec

@rokonec rokonec commented Jun 23, 2026

Copy link
Copy Markdown
Member

Device Bound Session Credentials (DBSC) for cookie authentication (prototype)

Draft / prototype. The public API is gated behind the experimental analyzer diagnostic ASP0030. The wire protocol is a W3C Editor's Draft ("may change at any moment") and is shipping single-vendor (Chromium) behind a flag, so both the protocol and this API are expected to change. This PR is for design review, not merge.

What this adds

A new experimental authentication component, Microsoft.AspNetCore.Authentication.DeviceBoundSessions, that implements the server side of Device Bound Session Credentials. DBSC binds a browser session to a device-held private key: the short-lived session cookie is periodically refreshed only when the browser can produce a fresh signed proof-of-possession, so a stolen cookie alone is useless.

It is designed as a drop-in hardening layer over an existing cookie auth scheme: you point it at a "source" sign-in scheme and it manages the registration handshake, a path-scoped refresh cookie, and a short-lived session cookie.

Shape of the API

builder.Services.AddAuthentication()
    .AddCookie("Application", o => { /* normal sign-in cookie */ })
    .AddDeviceBoundSession("Application", o =>
    {
        o.ShortLivedCookieExpiration = TimeSpan.FromMinutes(10);
        o.ScopeSpecifications.Add(new DeviceBoundSessionScopeRule { /* ... */ });
    });

All public types carry [Experimental("ASP0030")]; consumers must explicitly opt in.

How it works

  • Registration (/.well-known/dbsc/registration): validates the browser's signed proof JWT, binds it to the authenticated principal via a data-protected, time-limited challenge, then stamps a path-scoped refresh cookie (carrying the device public key + session id) and a short-lived session cookie.
  • Refresh (/.well-known/dbsc/refresh): authenticates the refresh cookie, issues a Secure-Session-Challenge (403), validates the returned proof JWT signature against the bound key and the challenge binding, then re-mints the short-lived session cookie.
  • Domain separation: registration and refresh challenges use distinct data-protection purposes, so a challenge from one flow can never be decrypted or confused as the other.
  • Lifetime: the refresh cookie inherits the source scheme's ExpireTimeSpan/SlidingExpiration and slides exactly like the auth cookie it replaces (no session-lifetime regression when DBSC is enabled). The session cookie stays deliberately short-lived and non-sliding.
  • Logging: per-reason LoggerMessage events for every validation failure (challenge undecryptable/malformed/mismatch, proof malformed/wrong-typ/unsupported-alg/bad-signature), at Debug, never logging secrets.

Components

  • Handler, options, extensions, and DTOs under src/Security/Authentication/DeviceBoundSessions/src/
  • Challenge protector (CBOR payloads, time-limited data protection) and JWT proof validator (ES256/RS256)
  • DbscDebugServer sample with a live dashboard that decodes cookies, proofs, and challenges for inspection
  • Tests for the challenge protector, JWT validator, and derived cookie protection/sliding

Testing

  • src/Security/Authentication/test/DeviceBoundSessions/ — 38 tests covering challenge round-trips and domain separation, JWT proof validation, data-protection of derived cookies, and refresh-cookie sliding inheritance.
  • Manually exercised end-to-end against Chromium (registration → refresh → slide) via the DbscDebugServer sample.

Open questions for review

  • Protocol is an unstable Editor's Draft; we have already absorbed breaking changes (header renames, 401→403 re-challenge). Is experimental gating the right call, or should this live out-of-band until the spec stabilizes?
  • Diagnostic id ASP0030 is reserved in docs/list-of-diagnostics.md; confirm allocation.
  • Cookie attributes advertised in the session instruction are currently hard-coded rather than derived from the session scheme's cookie builder.
  • DbscDebugServer is not sample but tool, we shall consider if we keep it, and if we add or replace it with some simple sample customers can follow
  • /plans/dbsc-design.md is residual planning document which shall be removed before merge, we keeping it for now for reviewers

References

Specification

Browser implementation & documentation

Chromium source code

TODO

  • Decide future and clean DbscDebugServer
  • Consider to create simple sample
  • Delete /plans/dbsc-design.md

javiercn and others added 20 commits June 3, 2026 17:48
Implements the DBSC protocol (W3C spec) in the cookie authentication handler.
Uses a stateless two-cookie pattern:
- Long-lived cookie: auth ticket + embedded public key (refresh token equivalent)
- Short-lived cookie (__dbsc suffix): device-bound credential (access token equivalent)

Includes:
- Registration/refresh middleware
- ES256/RS256 JWT validation
- Stateless challenge generation via DataProtection
- Sample app with HTTP logging for testing
- 16 unit/integration tests

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New separate authentication handler project following the OAuth/OIDC delegation
pattern. The DBSC handler manages the registration/refresh protocol and delegates
cookie stamping to separate cookie authentication schemes.

Architecture:
- Policy scheme routes auth: tries Session cookie first, falls back to source
- Registration handler reads source scheme, stamps Refresh + Session cookies
- Refresh handler reads Refresh cookie (path-scoped), stamps fresh Session cookie
- Source (long-lived) cookie deleted after registration

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sample app demonstrates the new DBSC architecture:
- Source cookie scheme with DeviceBoundSessionCookieEvents
- AddDeviceBoundSession() wires up refresh/session/policy schemes
- Policy scheme fallback verified working

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The v1 approach embedded DBSC directly in the cookie authentication handler.
This has been replaced by the v2 architecture in the separate
DeviceBoundSessions project which uses the OAuth/OIDC delegation pattern.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tyModel JWT

- Challenges now use ITimeLimitedDataProtector (5min expiry) and include
  the user's NameIdentifier claim for binding validation
- Registration validates NameIdentifier from challenge matches the
  authenticated principal
- Cookie sign-in header emission is now automatic via IPostConfigureOptions
  (no manual DeviceBoundSessionCookieEvents wiring in sample)
- JWT validation uses Microsoft.IdentityModel.JsonWebTokens
- Base64Url encoding uses WebEncoders from framework
- Added DataProtection.Extensions and WebUtilities references

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove IsAspNetCoreApp and IsPackable=false (matches JwtBearer pattern)
- Revert SharedFramework.External.props changes
- Add Microsoft.IdentityModel.JsonWebTokens to Dependencies.props/Versions.props
  as a LatestPackageReference (same as OpenIdConnect, WsFederation)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace simple NameIdentifier with a stable user identifier that follows
the same priority as antiforgery: sub > NameIdentifier > UPN > SHA256
hash of all claims. This ensures the challenge is bound to the user
regardless of which identity claims are available.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Registration challenge: claimUid|nonce (no session ID, not known yet)
- Refresh challenge: claimUid|nonce|sessionId (binds to specific session)
- Use RandomNumberGenerator.Fill(Span<byte>) instead of GetBytes()
- Use stackalloc for nonce/hash buffers
- Remove the RegistrationSessionId constant (was a design smell)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace pipe-separated string with CBOR map encoding
  Registration: { uid: claimUid }
  Refresh: { uid: claimUid, sid: sessionId }
- Remove nonce (ITimeLimitedDataProtector already provides uniqueness
  and replay protection via timestamp + MAC)
- Use byte[] overload of ITimeLimitedDataProtector.Protect/Unprotect
- Add System.Formats.Cbor reference

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Use CBOR definite-length array instead of map (simpler, smaller):
  Registration: [ claimUid ]
  Refresh: [ claimUid, sessionId ]
- Use WebEncoders.Base64UrlEncode/Decode consistently (not manual replace)
- ITimeLimitedDataProtector byte[] API is appropriate here (not hot path,
  ISpanDataProtector doesn't support time-limited)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Challenge payload is just consecutive CBOR text strings, no array framing:
  Registration: textstring(claimUid)
  Refresh: textstring(claimUid) textstring(sessionId)
- Default ShortLivedCookieExpiration changed from 30s to 10 minutes

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Switch from Microsoft.IdentityModel.JsonWebTokens (new entry) to
  Microsoft.IdentityModel.Protocols.OpenIdConnect (already in repo,
  transitively includes JsonWebTokens)
- Remove added Dependencies.props and Versions.props entries
- ComputeClaimUid now uses CBOR encoding for claim triples:
  - Known claims (sub/nid/upn): CBOR sequence of 3 text strings
  - Fallback: SHA256 of CBOR-encoded sorted claims (no intermediate strings)
- No more string interpolation for claim UIDs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Split DeviceBoundSessionConfiguration.cs into separate files per class
- Split DeviceBoundSessionCookieEvents.cs into PostConfigure, EventsWrapper, SourceSchemes
- Move DeviceBoundSessionJwtResult to own file
- Remove unused SessionCookieSuffix and RefreshCookieSuffix from Defaults

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rotector

- PostConfigureDeviceBoundSessionDerivedCookieOptions copies HttpOnly,
  Secure, SameSite, Domain, ExpireTimeSpan from source cookie scheme
  to refresh and session cookie schemes
- Refresh cookie overrides path to /.well-known/dbsc
- DeviceBoundSessionChallengeProtector is now a non-static singleton
  that caches the ITimeLimitedDataProtector instance
- Handler creates protector from injected IDataProtectionProvider
- Removed TryAddSingleton for protector (handler creates its own)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reflects: NuGet package model, CBOR challenges without nonce,
ITimeLimitedDataProtector, antiforgery-style claim UID, cookie settings
inheritance via IPostConfigureOptions, 10min default expiry, file structure,
JWT validation via Microsoft.IdentityModel, singleton challenge protector.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…pand debug sample

- emit Secure-Session-Challenge only on 403, not on success
- rename JSON DTOs to SessionInstruction/Scope/ScopeRule/Credential
- add DbscSampleV2 debug dashboard and HAR capture
- add challenge protector and JWT validator tests

🔒 - Generated by Copilot
…ntal

- add per-reason debug logging in challenge protector and JWT validator
- separate DP purposes for registration/refresh; fix CBOR validation
- gate public API as experimental (ASP0030); anchor refresh cookie lifetime
- rename DbscSampleV2 sample to DbscDebugServer; add gap tests

🔒 - Generated by Copilot
- inherit SlidingExpiration from the source scheme; drop refresh-scheme override
- stop anchoring refresh cookie lifetime at registration so it slides like the auth cookie
- add tests for refresh sliding inheritance and short-lived cookie staying fixed
- sample: surface the refresh cookie on the dashboard and decode challenges with split purposes

🔒 - Generated by Copilot
@rokonec

rokonec commented Jun 23, 2026

Copy link
Copy Markdown
Member Author

@copilot resolve the merge conflicts in this pull request

Copilot AI and others added 2 commits June 23, 2026 16:08
Co-authored-by: rokonec <25249058+rokonec@users.noreply.github.com>
Co-authored-by: rokonec <25249058+rokonec@users.noreply.github.com>

Copilot AI commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Merge conflicts resolved. The only conflict was in AspNetCore.slnx — main had added aspnetcoretools.csproj to the /src/Tools/NativeAot/ folder, which I included alongside the existing test project. Merged in aad0d70.

@rokonec rokonec marked this pull request as ready for review June 23, 2026 16:13
@rokonec rokonec requested a review from tdykstra as a code owner June 23, 2026 16:13
Copilot AI review requested due to automatic review settings June 23, 2026 16:13
@rokonec rokonec requested a review from a team as a code owner June 23, 2026 16:13
- document experimental ASP0030 gating and per-reason logging
- describe domain-separated challenge protectors and refresh-cookie sliding
- refresh file structure and add the lifetime-anchoring alternative

🔒 - Generated by Copilot

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces a new experimental authentication component (Microsoft.AspNetCore.Authentication.DeviceBoundSessions) that implements the server side of the W3C Device Bound Session Credentials (DBSC) draft for hardening cookie authentication via device-bound proof-of-possession refresh.

Changes:

  • Adds a new Microsoft.AspNetCore.Authentication.DeviceBoundSessions package with handler/options/extensions, JWT proof validation, and challenge protection.
  • Adds unit tests for challenge protection, JWT validation, and derived-cookie protection/sliding behavior.
  • Adds a DbscDebugServer sample plus repo wiring (project references, solution entries, diagnostic list update).

Reviewed changes

Copilot reviewed 42 out of 42 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/Security/Authentication/test/Microsoft.AspNetCore.Authentication.Test.csproj References the new DeviceBoundSessions project from the shared authentication test project.
src/Security/Authentication/test/DeviceBoundSessions/DeviceBoundSessionJwtValidatorTests.cs Adds tests covering JWT proof validation behaviors and failure modes.
src/Security/Authentication/test/DeviceBoundSessions/DeviceBoundSessionCookieProtectionTests.cs Adds tests verifying derived cookie schemes remain data-protected and inherit sliding/lifetime behavior.
src/Security/Authentication/test/DeviceBoundSessions/DeviceBoundSessionChallengeProtectorTests.cs Adds tests for registration/refresh challenge round-trips and domain separation.
src/Security/Authentication/test/DeviceBoundSessions/DbscProofKey.cs Test helper for minting ES256/RS256 DBSC proof JWTs.
src/Security/Authentication/DeviceBoundSessions/src/SessionScopeRule.cs JSON DTO for session scope rules (wire format).
src/Security/Authentication/DeviceBoundSessions/src/SessionScope.cs JSON DTO for session scope (wire format).
src/Security/Authentication/DeviceBoundSessions/src/SessionInstruction.cs JSON DTO for the session instruction response.
src/Security/Authentication/DeviceBoundSessions/src/SessionCredential.cs JSON DTO for credentials list entries (cookie credential).
src/Security/Authentication/DeviceBoundSessions/src/PublicAPI.Unshipped.txt Declares new public API surface (experimental-gated).
src/Security/Authentication/DeviceBoundSessions/src/PublicAPI.Shipped.txt Initializes shipped API file for the new project.
src/Security/Authentication/DeviceBoundSessions/src/PostConfigureDeviceBoundSessionDerivedCookieOptions.cs Copies cookie settings from source scheme to refresh/session derived schemes.
src/Security/Authentication/DeviceBoundSessions/src/PostConfigureDeviceBoundSessionCookieOptions.cs Hooks Secure-Session-Registration header emission into cookie sign-in.
src/Security/Authentication/DeviceBoundSessions/src/Microsoft.AspNetCore.Authentication.DeviceBoundSessions.csproj Defines the new package project and its references/InternalsVisibleTo.
src/Security/Authentication/DeviceBoundSessions/src/LoggingExtensions.cs Adds source-generated logging events for DBSC validation failures.
src/Security/Authentication/DeviceBoundSessions/src/DeviceBoundSessionSourceSchemes.cs Tracks source/derived scheme mappings for post-configure logic.
src/Security/Authentication/DeviceBoundSessions/src/DeviceBoundSessionScopeRule.cs Options model for configuring scope rules.
src/Security/Authentication/DeviceBoundSessions/src/DeviceBoundSessionRegistrationHeader.cs Emits the Secure-Session-Registration response header.
src/Security/Authentication/DeviceBoundSessions/src/DeviceBoundSessionOptions.cs Options for DBSC handler (paths, schemes, lifetime, scope, allowed initiators).
src/Security/Authentication/DeviceBoundSessions/src/DeviceBoundSessionJwtValidator.cs Validates DBSC proof JWTs (ES256/RS256) and extracts claims.
src/Security/Authentication/DeviceBoundSessions/src/DeviceBoundSessionJwtResult.cs Result model for validated proof JWTs.
src/Security/Authentication/DeviceBoundSessions/src/DeviceBoundSessionJsonContext.cs Source-generated JSON serialization context for DBSC DTOs.
src/Security/Authentication/DeviceBoundSessions/src/DeviceBoundSessionHttpContextExtensions.cs Public HttpContext extension to emit registration header for already-signed-in users.
src/Security/Authentication/DeviceBoundSessions/src/DeviceBoundSessionHandler.cs Main protocol handler for registration/refresh endpoints and cookie minting.
src/Security/Authentication/DeviceBoundSessions/src/DeviceBoundSessionExtensions.cs AddDeviceBoundSession(...) registration wiring (policy + derived cookie schemes + handler).
src/Security/Authentication/DeviceBoundSessions/src/DeviceBoundSessionDefaults.cs Default scheme and endpoint paths.
src/Security/Authentication/DeviceBoundSessions/src/DeviceBoundSessionCookieEvents.cs Events decorator for EventsType cookie scenarios to emit registration header.
src/Security/Authentication/DeviceBoundSessions/src/DeviceBoundSessionConstants.cs Protocol constants (headers, algorithms, advertised list).
src/Security/Authentication/DeviceBoundSessions/src/DeviceBoundSessionChallengeProtector.cs Time-limited DP + CBOR challenge minting/validation with domain separation.
src/Security/Authentication/DeviceBoundSessions/samples/DbscDebugServer/Properties/launchSettings.json Launch profile for the debug server sample.
src/Security/Authentication/DeviceBoundSessions/samples/DbscDebugServer/Program.cs Debug server sample host and endpoints for manual protocol exercise.
src/Security/Authentication/DeviceBoundSessions/samples/DbscDebugServer/HarLoggingMiddleware.cs Sample middleware to log traffic into HAR with DBSC annotations.
src/Security/Authentication/DeviceBoundSessions/samples/DbscDebugServer/DbscDebugServer.csproj Sample project file referencing the new DBSC package and dependencies.
src/Security/Authentication/DeviceBoundSessions/samples/DbscDebugServer/DbscDebug.cs Sample debug state, decoders, and capture middleware for the dashboard.
src/Security/Authentication/DeviceBoundSessions/samples/DbscDebugServer/Dashboard.cs Embedded HTML/JS dashboard UI for live protocol inspection.
src/Security/Authentication/DeviceBoundSessions/plans/dbsc-design.md Design document describing goals, scheme topology, protocol flow, and risks.
src/Security/Authentication/Cookies/src/Microsoft.AspNetCore.Authentication.Cookies.csproj Adds InternalsVisibleTo for authentication tests.
eng/ProjectReferences.props Registers the new project for reference resolution.
docs/list-of-diagnostics.md Documents ASP0030 experimental diagnostic for DBSC APIs.
AspNetCore.slnx Adds DeviceBoundSessions projects to the solution structure.

Comment thread src/Security/Authentication/DeviceBoundSessions/src/DeviceBoundSessionHandler.cs Outdated
Comment thread src/Security/Authentication/DeviceBoundSessions/src/DeviceBoundSessionHandler.cs Outdated
rokonec added 2 commits June 23, 2026 19:41
- remove the temporary 20-minute ExpireTimeSpan used for manual sliding tests

🔒 - Generated by Copilot
- point AspNetCore.slnx at the renamed DbscDebugServer sample
- remove the non-existent DeviceBoundSessions test project (tests live in the shared Authentication.Test project)

🔒 - Generated by Copilot
rokonec added 2 commits June 23, 2026 23:30
- run GenerateProjectList to fix ordering/formatting of the DBSC entry
- updates ProjectReferences, ShippingAssemblies, and TrimmableProjects

🔒 - Generated by Copilot
…wiring

Resolve correctness issues raised in PR review and tighten the handler's
dependency wiring:

- Mirror the source sign-in's IsPersistent on the refresh cookie instead of
  forcing it persistent, so DBSC no longer changes session/persistence
  semantics for session-only sign-ins.
- Derive the session instruction's credential name and attributes from the
  session scheme's CookieAuthenticationOptions so they stay in lock-step with
  the cookie actually emitted; throw on a missing scheme/name rather than
  emitting a guessed default that can never match.
- Derive the refresh cookie path from the configured RefreshPath so a
  customized RefreshPath still receives the cookie.
- Resolve the session scheme's cookie name at request time in the policy
  scheme selector so a custom Cookie.Name is still matched.
- Constructor-inject IOptionsMonitor<CookieAuthenticationOptions> instead of
  service-locating it per request; update PublicAPI.Unshipped accordingly.
- Use the idiomatic JsonSerializer.Deserialize<JsonElement> in the proof-key
  test helper instead of an undisposed JsonDocument.

🤖 Generated by Copilot
DBSC ships as a standalone NuGet package (it is not part of the
Microsoft.AspNetCore.App shared framework) and its public surface is
entirely experimental (ASP0030). Set VersionPrefix to the repo's
ExperimentalVersionPrefix so it publishes as a 0.x preview (0.11.0)
rather than the stable framework version (11.0.0), matching the
convention used by the experimental Microsoft.AspNetCore.Grpc.Swagger
package and signaling that the API is not yet stable.

🤖 Generated by Copilot
@rokonec

rokonec commented Jun 30, 2026

Copy link
Copy Markdown
Member Author

@copilot resolve the merge conflicts in this pull request

Copilot AI commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

The merge conflicts have been resolved locally — the only conflict was the src/submodules/googletest submodule, resolved by accepting main's version. However, pushing the resulting merge commit is being blocked by repository branch protection rules (GH013: "Cannot update this protected ref"). The previous merge was pushed successfully, so this may be a recent rule change. Could you check the repository rules for roman/dbsc-prototype at https://github.com/dotnet/aspnetcore/rules?ref=refs%2Fheads%2Froman%2Fdbsc-prototype or push the merge manually? The local commit is a88bbe9954.

rokonec added a commit that referenced this pull request Jun 30, 2026
- reset MessagePack-CSharp and googletest gitlinks to match main

- resolves merge conflict in PR #67388
- restore MessagePack-CSharp and googletest gitlinks to branch point so PR touches neither
@rokonec rokonec force-pushed the roman/dbsc-prototype branch from 353a815 to d98dcb1 Compare June 30, 2026 22:34
await Context.SignInAsync(Options.SessionScheme, principal, sessionProperties);

// 3. Delete the long-lived source cookie (exchange complete)
await Context.SignOutAsync(Options.RegistrationSourceScheme);

@halter73 halter73 Jul 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After this succeeds, the source cookie is gone and the live auth state is in the DBSC refresh/session cookies. Does a later SignOutAsync(Options.RegistrationSourceScheme) clear those derived cookies too? It looks like logout would only delete the already-removed source cookie.

Since app code does not need to know the derived scheme names to create these cookies, it should not need to know them to clear them.

}
else if (schemes.SessionSchemes.TryGetValue(name, out var sessionSourceScheme))
{
CopyFromSource(sessionSourceScheme, options);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We copy cookie attributes from the source scheme, but not its validation/revocation behavior. After DBSC registration, Identity's OnValidatePrincipal security-stamp check and any source SessionStore revocation are bypassed by the session/refresh cookies. Can we preserve those semantics? I think we might just have to copy over the event handlers from the source cookie options.

builder.Services.Configure<AuthenticationOptions>(o =>
{
o.DefaultAuthenticateScheme = policyScheme;
o.DefaultSignInScheme = sourceScheme;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think AddDeviceBoundSession should automatically set the specified sourceScheme as the DefaultSignInScheme. Nor should it set DefaultAuthenticationScheme . These are things that should be configured explicitly.

I know we have logic where if you have a single scheme, we treat that as the default, and this never will be a single scheme, but I don't think this is the solution.

We could have had our OIDC handler automatically configure itself as the default sign in scheme, but we don't, because we prefer to be explicit.

Identity does set default schemes in its extension methods, but it's the only thing to do so, and it's special since it takes more complete ownership of the user's Identity. None of our authentication handlers should change default schemes on their own.

/// <summary>
/// Gets the list of allowed refresh initiator host patterns.
/// </summary>
public IList<string> AllowedRefreshInitiators { get; } = new List<string>();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AlowedRefreshInitiators never appears in the JSON as allowed_refresh_initiators, so setting the option has no effect. I assume we want to keep this setting, but we just weren't flowing it? Can we add a test for this?

return new SessionInstruction
{
SessionIdentifier = sessionId,
RefreshUrl = Options.RefreshPath.Value,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this include OriginalPathBase, like AuthenticationHandler<T>.BuildRedirectUri(...) does for remote auth callback URLs? Maybe we should just use it?

I think the Secure-Session-Registration path has the same issue, otherwise an app mounted under /foo tells the browser to POST to /.well-known/dbsc/... instead of /foo/.well-known/dbsc/...

We should add some tests with a non-default path base too.

CopyFromSource saved and restored target.Cookie.Name/Path around the
attribute copy, but the copy never touches Name or Path, so the
save-restore was a no-op. Remove it; callers set the refresh cookie path
explicitly and the session scheme keeps its configured name.

Addresses PR review feedback.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants