Core.Portal.SignIn 0.3.79

Core.Portal.SignIn

Core.Portal.SignIn contains product-agnostic helpers for broker-backed portal sign-in.

For the complete product, marketing, development, trust-boundary, and cross-surface context, read the authentication and authorization capability guide and cross-surface parity plan.

The package calls the Core auth API through Core.Api.Clients.AuthApiClient, validates the returned JWT, optionally enforces transitional configured roles, and returns a cookie-ready ClaimsPrincipal. Product portals still own login pages, branding, antiforgery, redirects, cookie scheme names, SmartScheme routing, named authorization policies, permission vocabulary, and tenant-specific configuration.

Email-first portal flows use ICorePortalAuthDiscoveryService.DiscoverAsync(...) before asking for a password. The service calls POST /api/core/auth/discover with email, app/surface/principal context, and optional tenant hints. Core resolves a tenant from explicit hints, API-key context, a known identity user, or configured SSO email domains, then returns one of:

  • redirect: start the returned NextProviderKey through /api/core/auth/start.
  • choice: show the returned interactive providers.
  • password: reveal the product-owned password form.
  • needs_tenant_hint: use a direct tenant link or another tenant-safe hint; do not show a tenant picker by default.

Password recovery uses ICorePortalPasswordResetService. The default broker-backed implementation calls the canonical Core JSON auth endpoints:

  • POST /api/core/auth/password/request-reset
  • POST /api/core/auth/password/complete-reset

It carries configured tenant context plus optional per-request product code, reset URL base, and return URL. Product portals still own the Razor pages, validation text, reset email copy, and local proof-mode behavior. A portal that is not yet backed by Core identity may register its own ICorePortalPasswordResetService implementation while keeping the same page flow and result contract.

This package must not reference persistence, API controllers, workers, or product repos.

Example

builder.Services.AddCorePortalSignIn(builder.Configuration, options =>
{
    options.CoreApiBaseUrl = "https://api.example.com/";
    options.TenantApiKey = builder.Configuration["Portal:TenantApiKey"];
    options.ProductCode = "product-code";
    options.AppId = "product-portal";
    options.IncludeAccessTokenClaim = true;
    options.AuthorizationContextRefreshInterval = TimeSpan.FromMinutes(5);
    options.CookieAuthenticationScheme = "ProductPortal.Cookies";
});

The product login page or controller calls ICorePortalSignInService.BeginPasswordSignInAsync(...). It either receives a cookie-ready principal or a second-factor challenge. The portal keeps the transaction token in encrypted server-side/HTTP-only state, posts the entered code through CompleteSecondFactorAsync(...), and issues its own cookie only after Core returns a principal. TryCreatePrincipalAsync(...) remains a compatibility wrapper and cannot complete a challenged sign-in.

Use ICorePortalSecondFactorHandoffStore for the password-to-code-page handoff. It places the broker transaction in a short-lived, data-protected, HTTP-only cookie, preserves it across invalid-code retries, and clears malformed or expired state. Configure a distinct SecondFactorHandoffCookieName if multiple portal applications share one host name.

Use ICorePortalSessionPolicy for every successful cookie sign-in instead of constructing AuthenticationProperties in the product. The default is a 30-day persistent, sliding ticket; products can explicitly request a browser-session-only ticket. AddCorePortalSignIn also post-configures the named cookie with the same lifetime and secure HTTP-only defaults.

var properties = sessionPolicy.CreateSignInProperties(
    isPersistent: null, // use the Core default
    redirectUri: returnUrl);
await HttpContext.SignInAsync(cookieScheme, principal, properties);

The broker issues an opaque rotating refresh token alongside its short-lived access token. The refresh token is stored only inside the encrypted portal authentication ticket, is hashed in the Core database, and is rotated when ICorePortalAuthorizationContextValidator receives an expired access-token response. This keeps a valid portal session alive across browser restarts without turning the access JWT into a long-lived bearer credential. A revoked, expired, replayed, or otherwise invalid refresh token fails closed.

Product logout handlers call ICorePortalSignInService.RevokeCurrentSessionAsync(User) before deleting their cookie. This revokes the broker refresh session as well as the browser ticket; deleting only the product cookie is not a complete broker logout.

ICorePortalSessionRefreshCoordinator coalesces simultaneous refresh attempts from the same browser ticket and keeps a 30-second in-process replay grace so parallel page requests do not race one another into a sign-out. Multi-replica portals should register a distributed implementation of that interface or use request affinity for the grace window.

Product authorization uses the broker-issued permissions claim together with product/application context. Role names are administered bundles and should not be checked by product routes or navigation. RequiredRoles remains only as a migration compatibility check.

AddCorePortalSignIn post-configures the named cookie scheme to call ICorePortalAuthorizationContextValidator.ValidateAsync(...) for principals carrying the Core broker-session marker. Product-owned local proof cookies are left alone. Invalid broker results are rejected; refreshed principals replace and renew the ticket. The validator calls GET /api/core/auth/me, recomputes the current normalized role/permission context, and fails closed when the broker session is revoked, the user is inactive, the access token is missing, or refresh fails. IncludeAccessTokenClaim must therefore be enabled for broker-backed cookies that use this validator.

When passkeys are enabled by the broker, provider discovery returns a provider whose Kind is passkey. Product login UI must use CorePortalAuthenticationPresentation.ShouldOfferPasskeySignIn(...); the shared rule returns true only while the current principal is signed out. Passkey enrollment is a separate authenticated account-security action and must never be offered as anonymous registration.

See Passkeys and durable portal sessions for broker configuration, deployment boundaries, and rollout requirements.

See Multi-factor authentication for the challenge handoff, assurance labels, recovery requirements, and deployment gates.

For shared signed-out UI, pair this package with Core.Portal.Chrome auth shell assets. The chrome package owns the Barlow typeface stack, html[data-theme] dark-mode behavior, and standard language selector/preference handoff; this sign-in package stays focused on broker-backed authentication.

No packages depend on Core.Portal.SignIn.

Version Downloads Last updated
0.3.79 14 08/11/2026
0.3.78 33 07/24/2026
0.3.77 112 07/20/2026
0.3.76 5 07/19/2026
0.3.75 10 07/19/2026
0.3.74 5 07/19/2026
0.3.73 7 07/18/2026
0.3.72 4 07/17/2026
0.3.71 4 07/17/2026
0.3.70 4 07/17/2026
0.3.69 4 07/17/2026
0.3.68 4 07/17/2026
0.3.67 4 07/17/2026
0.3.66 4 07/16/2026
0.3.65 4 07/16/2026
0.3.64 4 07/15/2026
0.3.63 4 07/15/2026
0.3.62 4 07/15/2026
0.3.61 4 07/15/2026
0.3.60 7 07/14/2026
0.3.59 6 07/14/2026
0.3.58 5 07/14/2026
0.3.57 4 07/14/2026
0.3.56 4 07/14/2026
0.3.55 4 07/14/2026
0.3.50 5 07/13/2026
0.3.49 3 07/13/2026
0.3.48 3 07/13/2026
0.3.27 5 07/05/2026
0.3.26 6 07/05/2026
0.3.24 4 07/03/2026
0.3.23 16 06/23/2026
0.3.22 5 06/22/2026
0.3.21 5 06/21/2026
0.3.20 5 06/19/2026
0.3.19 5 06/19/2026
0.3.18 5 06/19/2026
0.3.17 5 06/19/2026
0.3.16 4 06/19/2026
0.3.15 5 06/19/2026
0.3.14 5 06/19/2026
0.3.13 4 06/19/2026
0.3.12 4 06/19/2026
0.3.11 5 06/19/2026
0.3.10 6 06/19/2026
0.3.9 5 06/19/2026
0.3.8 5 06/19/2026
0.3.7 5 06/19/2026
0.3.6 5 06/18/2026
0.3.5 5 06/18/2026
0.3.4 7 06/10/2026
0.3.0 7 06/02/2026
0.2.0 5 06/02/2026
0.1.3-local.1 4 06/10/2026
0.1.2 6 05/31/2026
0.1.2-local.1 4 06/10/2026
0.1.1 19 05/28/2026