Concierge.Auth.Client.Profiles
2.0.0
dotnet add package Concierge.Auth.Client.Profiles --version 2.0.0
NuGet\Install-Package Concierge.Auth.Client.Profiles -Version 2.0.0
<PackageReference Include="Concierge.Auth.Client.Profiles" Version="2.0.0" />
<PackageVersion Include="Concierge.Auth.Client.Profiles" Version="2.0.0" />
<PackageReference Include="Concierge.Auth.Client.Profiles" />
paket add Concierge.Auth.Client.Profiles --version 2.0.0
#r "nuget: Concierge.Auth.Client.Profiles, 2.0.0"
#:package Concierge.Auth.Client.Profiles@2.0.0
#addin nuget:?package=Concierge.Auth.Client.Profiles&version=2.0.0
#tool nuget:?package=Concierge.Auth.Client.Profiles&version=2.0.0
Concierge.Auth.Client.Profiles
Reads and writes user profiles against AuthService (contract v1.3 §10.7), and consumes
thiso.events (user.profile.updated, user.status.changed, user.identity.linked) into the
client service's own user entity via a host-supplied IUserProfileMapper<TClientUser>.
Not in scope
PATCH /api/v1/auth/me (contract §4.8) — that is the human self-service route, not a
client-credential concern, and is not implemented here.
Install and register
services.AddConciergeAuthClient(configuration, db => db.UseNpgsql(cs, npgsql =>
npgsql.MigrationsHistoryTable("__EFMigrationsHistory", "concierge"))); // base package — required first
services.AddConciergeSecretManagement(configuration); // Secrets package — required first
services.AddConciergeAuthClientTokens(configuration); // Tokens package — profile HTTP uses Bearer JWT
services.AddConciergeUserProfileSync<MyUser, MyProfileMapper, MyAccessHook>(configuration);
// binds "Concierge:AuthClient:ProfileSync"; TAccessHook is a required type parameter, not optional
HTTP surface
IUserProfileClient.GetProfileAsync(userId, ct)—GET .../users/{userId}/profile.IUserProfileClient.UpdateProfileAsync(userId, request, ct)—PATCH .../users/{userId}/profile. Writable fields aredisplayName,avatarUrl, andphone— there is noemailfield, by design (email is the magic-link authentication factor; a writable email would be an account-takeover primitive).avatarUrlandphoneuseOptional<string?>so "clear it" (null) is distinguishable from "leave it alone" (omitted from the request body); a plain nullable cannot express this and will silently wipe data — this bug class recurred three times in this project (avatarUrl in T-0083, phone in T-0091, anddisplayNameon the inbound event path in T-0092, where it was live and wiping display names on every phone-only update). A409 VERSION_CONFLICTis never auto-retried — it is surfaced viaProfileUpdateResultwith the server's current version so a human can decide; refetching and re-applying would silently clobber a concurrent human edit.- There is no
Idempotency-Keyon these routes (contract §10.7 defines none) — a replayed PATCH is not deduplicated by AuthService and is not a no-op at the protocol level; do not build retry logic that assumes otherwise. emailis read-only everywhere in this package: it appears on the inbound event (UserProfileChanged.Email) purely informationally and has no corresponding writable property onUpdateUserProfileRequest. Known limitation (T-0093, open): email itself is not presence-tracked the waydisplayName/avatarUrl/phoneare — there is noOptional<T>on it, so a mirror consuming this event cannot distinguish "email unchanged" from "email present but not worth carrying" the same principled way it can for the other fields.- Caveat (pending T-0097): a denial anywhere on the §10.7 routes is uninterpretable — bad
credential, missing
can_write_user_profilecapability, and unknown user all collapse to the same denial shape (contract §10.5). This package cannot tell you which one occurred. Awhoamiendpoint (contract v1.6 §10.8) will fix this; it has not shipped yet.
Event consumption
AddConciergeUserProfileSync<TClientUser, TMapper, TAccessHook>() registers a background
consumer that binds a queue to user.profile.updated, user.status.changed, and
user.identity.linked on the thiso.events topic exchange.
user.profile.updatedcarries the profile's ownversion. An incoming version that is lower than or equal to the version already known viaIUserProfileMapper<TClientUser>.GetVersionis dropped — RabbitMQ gives no cross-redelivery ordering guarantee.user.status.changedinvokesIAccessReevaluationHook.ReevaluateAsyncsynchronously, in-line with message handling — a suspension must not wait for token expiry (contract §6).- Events are not a durable channel.
IProfileReconciler<TClientUser>.ReconcileAsync(userId, ct)fetches viaGetProfileAsyncand re-applies through the same mapper, to repair a mirror that missed an event.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Concierge.Auth.Client (>= 1.1.1)
- Concierge.Auth.Client.Secrets (>= 1.0.2)
- Concierge.Auth.Client.Tokens (>= 1.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.11)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.11)
- Microsoft.Extensions.Http (>= 10.0.11)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.11)
- RabbitMQ.Client (>= 7.2.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.