Single helper extension: MapSvrntyAltchaChallenge() exposes
GET /api/altcha/challenge (configurable prefix) that fetches a fresh
challenge from IAltchaChallengeProvider and projects it onto the
JSON shape the altcha widget v3 expects from its challengeurl —
{ algorithm, challenge, salt, signature, maxnumber } in lowercase.
AllowAnonymous on purpose: the whole point is gating mutations from
unauthenticated callers, so the challenge endpoint must be reachable
without credentials.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The default transport for IAltchaVerifier / IAltchaChallengeProvider —
calls a self-hosted altcha service over gRPC.
Wire contract
- Protos/altcha.proto defines svrnty.cqrs.altcha.v1.AltchaService with
CreateChallenge + VerifyChallenge RPCs. Shipped in this package as
source-of-truth; Go (and other) implementations vendor a copy.
- Challenge.challenge_hash is named (not "challenge") to avoid a C#
property/class name collision; the MinimalApi widget JSON remaps.
Runtime
- AltchaGrpcVerifier maps RpcException → AltchaVerifyResult.Fail with
a diagnostic reason ("verify-timeout", "service-unavailable", etc.)
so the auth check surfaces a clean Unauthorized without leaking
transport detail.
- AltchaGrpcChallengeProvider lets create-challenge failures bubble
(challenge endpoint should 5xx if altcha is down — clients retry).
- AltchaGrpcOptions.TokenProvider hook for consumer-supplied HMAC
service-token minting (plan-b will plug in ServiceTokenIssuer).
- AddGrpcClient<AltchaServiceClient> registered with HttpClientFactory.
AddSvrntyAltchaGrpcVerifier(Action<...>) and overload binding from
IConfiguration cover both wiring styles.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Altcha authorization check, plugged into the
ICommandAuthorizationCheck / IQueryAuthorizationCheck seam.
Behavior
- Self-applies: returns Allowed for any request whose type isn't
decorated with [Altcha]. No-op for the 99% of endpoints that don't
need PoW.
- Reads ctx.Items["mobile_attested"] for Phase 3 bypass when the
attribute's AllowMobileAttestationBypass is true.
- Pulls the solution off the request via IHasAltchaSolution and
delegates verification to IAltchaVerifier (resolved per-call from
the request scope, so any verifier lifetime works).
- Stashes a diagnostic reason in ctx.Items["altcha_reason"]
(missing / misconfigured / invalid / replayed / expired / etc.)
for downstream middleware to surface in error responses.
- Singleton itself — stateless; one instance shared via factory
registrations under both check interfaces.
AddSvrntyAltcha() registers the check. The verifier is provided by
a transport-specific module (e.g. Svrnty.CQRS.Altcha.Grpc, next).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Abstractions for the Altcha-based proof-of-work module:
- AltchaAttribute (AllowMobileAttestationBypass param)
- IHasAltchaSolution — marker interface for request POCOs carrying
the widget's solution payload over HTTP/gRPC transports
- IAltchaVerifier / IAltchaChallengeProvider — transport-agnostic
interfaces; default gRPC implementations ship in Svrnty.CQRS.Altcha.Grpc
- IMobileAttestationProvider — Phase 3 placeholder; concrete impls
stamp ctx.Items["mobile_attested"] for the Altcha check to read as
a bypass when AllowMobileAttestationBypass is true
- AltchaChallenge / AltchaVerifyResult DTOs
Lean dependencies — only references Svrnty.CQRS.Abstractions for the
auth-check pipeline types.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>