Translate all of docs/ from Spanish to English and rewrite the root README as a short project overview instead of a technical run guide. Remove web-player/design_handoff_musify_player, an old design-tool export that was no longer needed.
2.7 KiB
Authentication and identity
Provider: Zitadel (OIDC/OAuth2)
Login and token issuance are handled by Zitadel, not the API. The API is a resource server: it only validates the access token (JWT) it receives in Authorization: Bearer.
- Configured under the
Authenticationsection (AuthenticationConfiguration): metadata address, issuer, audience, client id, authorize/token endpoints, scopes. JwtBearerOptionsSetupsets up validation (issuer, audience, keys via OIDC metadata).- In dev,
RequireHttpsMetadata = false.
Key requirement in the Zitadel app
- Auth Token Type = JWT (otherwise Zitadel issues an opaque token with no dots, which
JwtBearercan't validate → 401 on everything protected). - User Info inside Token enabled if you want the access token to carry profile claims (
name,email,picture). By default the access token is minimal (sub,aud,iss,exp…), with no profile info.
sub is numeric → User.Id is long
Zitadel's sub is a 64-bit integer encoded as a string (e.g. 371953080444977155), not a GUID. Because of that:
User.Idand every user-related FK (PlayList.UserId,UserHasTrack.UserId,UploadIntent.UserId) arelong.User.Idhas[DatabaseGenerated(None)]so EF doesn't autogenerate it: it's assigned from thesub.CurrentUser.Idislong?(parsed withlong.TryParse);RequiredIdthrows if it's missing.
Why: the original code did Guid.Parse(sub), which threw a FormatException → 401 on every protected endpoint. The fix was to store the sub as-is, as a number.
User provisioning / sync
On every validated token, JwtBearerEventsHandler.TokenValidated:
- Reads
sub(id),name/preferred_username/email(with fallback), and the OIDCpictureclaim. - Sends a
SyncUserCommand.
SyncUserCommandHandler does an idempotent upsert:
- If the user doesn't exist yet → creates it.
- If it exists → updates the name and
ProfilePictureUrlonly if they changed (a DB write only happens when something is different).
Why "write only if changed": TokenValidated runs on every request; without that guard we'd be writing to the DB on every single call. It also needs to be resilient: if provisioning fails, it's logged but authentication doesn't fail because of it.
Note:
CreateUserCommand(the manualPOST /usersendpoint) is still create-only; login sync usesSyncUserCommand.
Profile picture
User.ProfilePictureUrl is filled from the token's picture claim. If Zitadel doesn't include profile info in the access token (see above), it arrives as null. The code is correct, there's just nothing to sync until that option is turned on.