string Version { get; }
Source
Gets the first 16 lowercase hexadecimal characters of the embedded asset SHA-256 hash.
Remarks
The version is suitable for cache busting; it is not a package or API version.
Builds privacy-safe PWA push readiness evidence from validated package configuration.
Registers the optional AppSurface Web Push safe rail.
IServiceCollection AddAppSurfaceWebPush(this IServiceCollection services, Action<AppSurfaceWebPushOptions> configure)
Registers Web Push with host-owned VAPID keys and exact push-service origins.
services
configure
The same service collection for chaining.
This method maps no route and does not enable the shared PWA worker. The host must separately enable WebOptions.Pwa.Push.Enabled
, register IAppSurfaceWebPushSubscriptionCustody
, and call one explicit protected mapping method. Calling this method also registers one singleton IPwaPushReadinessProvider
for Web's additive PWA diagnostics contract. The provider exposes only the active safe key identifier, a SHA-256 public-key fingerprint, and the synchronized route-mapped bit; it never exposes key material, route values, or subscription data. Options are validated during host startup; incomplete keys, non-canonical origins, and mismatched key pairs fail startup.
IServiceCollection AddAppSurfaceWebPushDevelopmentProofTransport(this IServiceCollection services, IHostEnvironment environment)
Replaces network transport with a deterministic HTTP 201 proof transport in Development only.
services
AddAppSurfaceWebPush(IServiceCollection, Action{AppSurfaceWebPushOptions})
.environment
The same service collection for chaining.
ArgumentNullException
InvalidOperationException
This seam exists only for canonical examples and local integration proofs. It still runs the package sender, encryption, validation, and response classifier, but it performs no network request and proves no delivery. Never register it in staging or production.
Serves the package-owned browser client from one fixed app-root-relative path.
Map the Web Push rail on the application-root endpoint builder so route-group prefixes cannot move this asset. The embedded resource is required package content; failure to locate it is a packaging error.
Task WriteAsync(HttpContext context)
Writes the JavaScript response, or headers only for a HEAD request.
context
A matching v
query value enables immutable caching. Missing or stale versions use no-cache
. The response always applies JavaScript content type and nosniff
headers.
string Version { get; }
Source
Gets the first 16 lowercase hexadecimal characters of the embedded asset SHA-256 hash.
The version is suitable for cache busting; it is not a package or API version.
Validates one bearer token for the antiforgery-free Web Push subscription rail.
The package extracts the token from a syntactically valid HTTP Authorization: Bearer
header and supplies only the token value. Implementations should validate issuer, audience, signature, lifetime, and any app-specific revocation requirements, then return an authenticated principal or null
. They must not fall back to cookies, ambient request identities, or other credentials. Bearer tokens are sensitive and must not be logged. Register exactly one implementation before mapping bearer subscription endpoints.
ValueTask<ClaimsPrincipal?> ValidateAsync(string bearerToken, CancellationToken cancellationToken = default)
Validates a bearer token and returns its authenticated principal when accepted.
bearerToken
cancellationToken
An authenticated principal when accepted; otherwise null
.
OperationCanceledException
Maps the explicit protected AppSurface Web Push subscription rail.
void MapAppSurfaceWebPushSubscriptions(this IEndpointRouteBuilder endpoints, string path, string authorizationPolicy, string? rateLimiterPolicy = null)
Maps cookie-authenticated subscription endpoints with package-owned antiforgery validation.
endpoints
path
authorizationPolicy
rateLimiterPolicy
ArgumentException
InvalidOperationException
Mapping is explicit and returns no convention builder, so callers cannot disable the package-owned security contract. Authorization is evaluated before VAPID configuration, antiforgery, parsing, or custody access.
void MapAppSurfaceWebPushBearerSubscriptions(this IEndpointRouteBuilder endpoints, string path, string authorizationPolicy, string? rateLimiterPolicy = null)
Maps token-only subscription endpoints using the app's registered bearer-token validator.
endpoints
path
authorizationPolicy
rateLimiterPolicy
ArgumentException
InvalidOperationException
Register exactly one IAppSurfaceWebPushBearerTokenValidator
. Requests must carry a nonblank HTTP Authorization: Bearer
credential. The package passes only that token to the validator and ignores ambient identities and authentication schemes. A missing, rejecting, or failing validator fails closed.
Coordinates route claims across repeated package mapping calls in one application.
Claims are synchronized and compared case-insensitively to match ASP.NET Core route behavior.
bool Claim(string path)
Claims one base path and reports whether this caller owns the one-time client asset mapping.
path
true
only for the first successful claim in the application.
InvalidOperationException
bool IsMapped { get; }
Source
Gets whether at least one package-owned Web Push route has been mapped.
Contains one sensitive browser push subscription snapshot.
string Endpoint { get; }
Source
Gets the sensitive push-service endpoint.
string P256Dh { get; }
Source
Gets the sensitive browser P-256 Diffie-Hellman public key.
string Auth { get; }
Source
Gets the sensitive browser authentication secret.
string VapidKeyId { get; }
Source
Gets the safe retained VAPID key identifier.
Identifies a sensitive browser subscription for an app-owned unregister operation.
string Endpoint { get; }
Source
Gets the sensitive push-service endpoint.
Provides the authenticated principal to app-owned subscription custody.
ClaimsPrincipal Principal { get; }
Source
Gets the authenticated principal. The host derives its own user and tenant keys.
Stores subscriptions and conditionally marks complete snapshots terminal.
ValueTask<AppSurfaceWebPushRegistrationDisposition> RegisterAsync(AppSurfaceWebPushSubscriptionWriteContext context, AppSurfaceWebPushSubscription subscription, CancellationToken cancellationToken)
Idempotently registers or refreshes a subscription for the authenticated principal.
context
subscription
cancellationToken
A disposition that distinguishes create, update, no-op, ownership conflict, and policy rejection.
ValueTask<AppSurfaceWebPushUnregistrationDisposition> UnregisterAsync(AppSurfaceWebPushSubscriptionWriteContext context, AppSurfaceWebPushSubscriptionReference subscription, CancellationToken cancellationToken)
Idempotently unregisters a subscription for the authenticated principal.
context
subscription
cancellationToken
A disposition that distinguishes removal, absence, ownership conflict, and policy rejection.
ValueTask<AppSurfaceWebPushTerminalDisposition> MarkTerminalAsync(AppSurfaceWebPushSubscription subscription, AppSurfaceWebPushTerminalReason reason, CancellationToken cancellationToken)
Compares and marks the complete subscription snapshot terminal. Implementations must not retire a replacement subscription that happens to reuse the same endpoint.
subscription
reason
cancellationToken
A compare-and-mark disposition; replacement records must produce AppSurfaceWebPushTerminalDisposition.AlreadyTerminal
.
Configures the active VAPID key ring and the exact push-service origins an AppSurface host permits.
string? ActiveVapidKeyId { get; set; }
Source
Gets or sets the safe identifier used for newly created browser subscriptions.
Defaults to null
. The value must match a retained VapidKeys
entry and use the documented safe-ID alphabet. Startup validation fails closed until an active key is configured.
IDictionary<string, AppSurfaceWebPushVapidKeyOptions> VapidKeys { get; }
Source
Gets the bounded VAPID key ring. Retain old keys while stored subscriptions still reference them.
The collection starts empty and must contain 1 through 8 valid entries. Add the new key before changing ActiveVapidKeyId
, then retain older entries until no stored subscription references them.
ISet<string> AllowedPushServiceOrigins { get; }
Source
Gets the exact normalized HTTPS origins allowed to receive push requests, for example https://updates.push.services.mozilla.com
. Wildcards and custom ports are rejected.
The collection starts empty and must contain 1 through 16 canonical default-port origins. Startup validation and delivery fail closed when the allowlist is empty or invalid.
Configures one retained VAPID signing key pair.
The private key is sensitive. Store it in user-secrets or a production secret provider.
string? Subject { get; set; }
Source
Gets or sets the RFC 8292 contact subject, as a mailto:
or HTTPS URI.
string? PublicKey { get; set; }
Source
Gets or sets the canonical unpadded base64url P-256 public key.
string? PrivateKey { get; set; }
Source
Gets or sets the canonical unpadded base64url P-256 private key.
Provides package-internal canonical validation for configuration, routes, and browser subscription material.
Validation methods return false
for malformed external input and never echo secret key material.
bool IsSafeKeyId(string? value)
Determines whether a key identifier uses 1 through 64 safe ASCII characters and begins alphanumerically.
value
true
when the identifier is safe for diagnostics and lookup.
bool TryDecodeCanonicalBase64Url(string? value, int expectedLength, out byte[] decoded)
Decodes a canonical unpadded base64url value with an exact decoded length.
value
expectedLength
decoded
true
only when decoding, length, alphabet, and canonical round-trip checks pass.
bool IsValidP256PublicKey(string? value)
Determines whether a value is a canonical uncompressed NIST P-256 public point.
value
true
when the runtime accepts the point on NIST P-256.
bool IsMatchingVapidPair(string? publicKey, string? privateKey)
Determines whether canonical P-256 public and private VAPID keys form one key pair.
publicKey
privateKey
true
when the derived public coordinates match in fixed time.
bool IsValidSubject(string? value)
Determines whether a VAPID subject is a canonical contact mailto:
or HTTPS URI.
value
true
when the value contains no whitespace, user-info, fragment, or unsupported scheme.
bool TryNormalizeAllowedOrigin(string? value, out string origin)
Normalizes and validates one exact HTTPS default-port push-service origin.
value
origin
true
only when the input already equals its canonical origin.
bool TryValidateEndpoint(string? value, ISet<string> allowedOrigins, out string origin)
Validates a push subscription endpoint against the exact allowed-origin set.
value
allowedOrigins
origin
true
when the endpoint shape is valid and its authority is allowlisted.
bool IsValidAssetPath(string? value)
Determines whether a local asset path is literal, app-root-relative, and free of query or traversal syntax.
value
true
when raw and repeatedly decoded forms remain safe.
bool IsValidDestinationPath(string? value)
Determines whether a local destination path is safe, allowing at most one query separator.
value
true
when raw and repeatedly decoded path forms remain app-root-relative and traversal-free.
bool IsValidTopic(string? value)
Determines whether an optional Web Push topic uses 1 through 32 URL-safe characters.
value
null
when no topic is requested.
true
for a missing or valid topic.
string Base64UrlEncode(ReadOnlySpan<byte> value)
Encodes bytes as canonical unpadded base64url text.
value
The canonical base64url representation without padding.
Validates the bounded VAPID key ring and exact push-service origin allowlist at startup.
Renders the CSP-compatible versioned AppSurface Web Push browser client script.
Microsoft.AspNetCore.Mvc.Rendering.ViewContext ViewContext { get; set; }
Source
Gets or sets the current Razor view context.
Contains a bounded payload matching AppSurface Web's PWA worker schema version 1.
string Title { get; }
Source
Gets the required 1-256 character title.
string? Body { get; }
Source
Gets the optional 1-2048 character body.
string? IconPath { get; }
Source
Gets the optional app-root-relative icon path.
string? BadgePath { get; }
Source
Gets the optional app-root-relative badge path.
string? Tag { get; }
Source
Gets the optional 1-128 character notification tag.
string? DestinationPath { get; }
Source
Gets the optional app-root-relative click destination, which may include one query string.
Configures one push-service attempt.
int TimeToLiveSeconds { get; }
Source
Gets the required retention time in seconds.
AppSurfaceWebPushUrgency Urgency { get; }
Source
Gets the push urgency.
string? Topic { get; }
Source
Gets the optional 1-32 character URL-safe collapse topic.
Contains one immutable, sensitive single-recipient send request.
AppSurfaceWebPushSubscription Subscription { get; }
Source
Gets the complete subscription snapshot.
AppSurfaceWebPushNotification Notification { get; }
Source
Gets the bounded worker payload.
AppSurfaceWebPushSendOptions Options { get; }
Source
Gets the one-attempt transport options.
Sends a single Web Push notification without retrying.
ValueTask<AppSurfaceWebPushSendResult> SendAsync(AppSurfaceWebPushSendRequest request, CancellationToken cancellationToken = default)
Performs at most one push-service request. AppSurfaceWebPushSendOutcome.Accepted
proves only an RFC 8030 201 Created
response; it does not prove browser delivery or display. Caller cancellation propagates. Terminal cleanup uses an independent hard five-second bound and is reported in the safe result rather than extending the caller's request indefinitely.
request
cancellationToken
A safe classification containing no subscription, payload, token, body, or exception data.
ArgumentException
OperationCanceledException
Contains safe send classification without endpoint, key, payload, token, body, or exception data.
AppSurfaceWebPushSendOutcome Outcome { get; }
Source
Gets the safe send outcome.
AppSurfaceWebPushCleanupState CleanupState { get; }
Source
Gets the terminal cleanup state.
int? StatusCode { get; }
Source
Gets the push-service HTTP status, when a response was observed.
TimeSpan? RetryAfter { get; }
Source
Gets a safe nonnegative Retry-After delay when supplied by the push service.
string ReasonCode { get; }
Source
Gets the stable safe reason code.
string VapidKeyId { get; }
Source
Gets the safe configured VAPID key identifier.
Describes an app-owned registration result.
Describes an app-owned unregister result.
Describes compare-and-mark terminal cleanup.
Identifies the terminal push-service response.
Defines Web Push urgency.
Classifies the safe result of one send attempt.
Classifies conditional terminal cleanup.