[{"data":1,"prerenderedAt":198},["ShallowReactive",2],{"i-lucide:layers":3,"i-lucide:git-commit-horizontal":8,"i-lucide:refresh-cw":10,"i-lucide:book-open":12,"i-lucide:image":14,"i-lucide:webhook":16,"i-lucide:home":18,"i-lucide:layout-grid":20,"i-lucide:tag":22,"i-lucide:newspaper":24,"i-lucide:map":26,"i-lucide:flask-conical":28,"i-lucide:arrow-right":30,"i-lucide:chevron-right":32,"i-lucide:search":34,"i-lucide:monitor":36,"content:\u002Finternal\u002Farchitecture\u002Fauthentication":38,"i-lucide:info":196,"i-lucide:layers-2":193,"i-lucide:key-round":193,"i-lucide:shield-check":193,"i-lucide:box":193,"i-lucide:share-2":193,"i-lucide:clock":193,"i-lucide:dices":193,"i-lucide:building-2":193,"i-lucide:bell":193,"i-lucide:puzzle":193,"i-lucide:shield-alert":193,"i-lucide:sprout":193,"i-lucide:activity":193,"i-lucide:blocks":193},{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":7},0,24,false,"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z\"\u002F>\u003Cpath d=\"M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12\"\u002F>\u003Cpath d=\"M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":9},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"3\"\u002F>\u003Cpath d=\"M3 12h6m6 0h6\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":11},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M3 12a9 9 0 0 1 9-9a9.75 9.75 0 0 1 6.74 2.74L21 8\"\u002F>\u003Cpath d=\"M21 3v5h-5m5 4a9 9 0 0 1-9 9a9.75 9.75 0 0 1-6.74-2.74L3 16\"\u002F>\u003Cpath d=\"M8 16H3v5\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":13},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 5v16m8.001-2A2 2 0 0 0 22 17V5a2 2 0 0 0-1.999-2L16 3.002A5 5 0 0 0 12 5a5 5 0 0 0-4-2H4a2 2 0 0 0-2 2v12a2 2 0 0 0 1.999 2H8a5 5 0 0 1 4 2a5 5 0 0 1 4-2z\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":15},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Crect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" ry=\"2\"\u002F>\u003Ccircle cx=\"9\" cy=\"9\" r=\"2\"\u002F>\u003Cpath d=\"m21 15l-3.086-3.086a2 2 0 0 0-2.828 0L6 21\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":17},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M18 16.98h-5.99c-1.1 0-1.95.94-2.48 1.9A4 4 0 0 1 2 17c.01-.7.2-1.4.57-2\"\u002F>\u003Cpath d=\"m6 17l3.13-5.78c.53-.97.1-2.18-.5-3.1a4 4 0 1 1 6.89-4.06\"\u002F>\u003Cpath d=\"m12 6l3.13 5.73C15.66 12.7 16.9 13 18 13a4 4 0 0 1 0 8\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":19},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8\"\u002F>\u003Cpath d=\"M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":21},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Crect width=\"7\" height=\"7\" x=\"3\" y=\"3\" rx=\"1\"\u002F>\u003Crect width=\"7\" height=\"7\" x=\"14\" y=\"3\" rx=\"1\"\u002F>\u003Crect width=\"7\" height=\"7\" x=\"14\" y=\"14\" rx=\"1\"\u002F>\u003Crect width=\"7\" height=\"7\" x=\"3\" y=\"14\" rx=\"1\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":23},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z\"\u002F>\u003Ccircle cx=\"7.5\" cy=\"7.5\" r=\".5\" fill=\"currentColor\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":25},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M15 18h-5m8-4h-8m-6 8h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v16a2 2 0 0 1-4 0v-9a2 2 0 0 1 2-2h2\"\u002F>\u003Crect width=\"8\" height=\"4\" x=\"10\" y=\"6\" rx=\"1\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":27},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M14.106 5.553a2 2 0 0 0 1.788 0l3.659-1.83A1 1 0 0 1 21 4.619v12.764a1 1 0 0 1-.553.894l-4.553 2.277a2 2 0 0 1-1.788 0l-4.212-2.106a2 2 0 0 0-1.788 0l-3.659 1.83A1 1 0 0 1 3 19.381V6.618a1 1 0 0 1 .553-.894l4.553-2.277a2 2 0 0 1 1.788 0zm.894.211v15M9 3.236v15\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":29},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2M6.453 15h11.094M8.5 2h7\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":31},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 12h14m-7-7l7 7l-7 7\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":33},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"m9 18l6-6l-6-6\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":35},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"m21 21l-4.34-4.34\"\u002F>\u003Ccircle cx=\"11\" cy=\"11\" r=\"8\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":37},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Crect width=\"20\" height=\"14\" x=\"2\" y=\"3\" rx=\"2\"\u002F>\u003Cpath d=\"M8 21h8m-4-4v4\"\u002F>\u003C\u002Fg>",{"id":39,"title":40,"body":41,"description":186,"extension":187,"eyebrow":188,"hostedOnly":6,"meta":189,"navigation":190,"path":191,"seo":192,"short":193,"stem":194,"__hash__":195},"docs\u002Finternal\u002Farchitecture\u002Fauthentication.md","Authentication & Identity",{"type":42,"value":43,"toc":175},"minimark",[44,53,58,61,64,67,71,78,81,85,100,104,107,111,118,124,128,135,142,149,153,162],[45,46,47,48,52],"p",{},"Before Chronicler can decide whether you may do something, it has to know who you are. That is authentication, and it runs before everything else. A request arrives with a bearer token, middleware verifies it, and the verified ",[49,50,51],"strong",{},"principal"," is put on the request context. From that point on, every layer reads the same principal. This page is about how a caller gets that token, and how an account carries the rest of its identity.",[54,55,57],"h2",{"id":56},"two-ways-in-one-account","Two ways in, one account",[45,59,60],{},"A user can sign in with a password or with an external provider such as Google. Provider login uses the standard OAuth Authorization Code flow with PKCE, which is the modern way to do it without a shared secret sitting in a browser.",[45,62,63],{},"There is one awkward moment in provider login worth explaining. The provider sends the user back to Chronicler as a browser navigation, but Chronicler hands out tokens in a response body, not in a redirect. Putting a token in the callback URL would leak it into browser history and server logs, so instead the callback carries a single-use handoff code, and the app trades that code for the real tokens. The token never rides in a URL.",[45,65,66],{},"A second question is what happens when the same person signs in two different ways. Chronicler links a provider account to a Chronicler account by a stable provider subject and a verified-email rule, so two logins that share a verified email land on one account rather than quietly creating two.",[54,68,70],{"id":69},"one-primitive-behind-every-email-link","One primitive behind every email link",[45,72,73,74,77],{},"Email verification, passwordless login, password reset, and the OAuth handoff all look different to a user, but underneath they are the same thing: a one-time token that proves the holder controls something. Chronicler builds them on a single ",[49,75,76],{},"one-time-token"," primitive, so the security-sensitive mechanics are written once and tested once, instead of being reinvented, slightly wrong, in each feature.",[45,79,80],{},"Sessions get the same care. A session can be revoked immediately through a per-family liveness check, a user can list their own sessions and end one, and a refresh token that is replayed is detected and rejected.",[54,82,84],{"id":83},"a-handle-a-name-and-a-login","A handle, a name, and a login",[45,86,87,88,91,92,95,96,99],{},"An account carries three separate things that are easy to confuse. The ",[49,89,90],{},"username"," is a global, unique, stable handle. It is what invites, links, and mentions point at, so it has to be stable. It is assigned automatically at registration, because forcing a new user to invent a unique handle at signup is friction for no reason, and it can be changed later, freeing the old one at once. The ",[49,93,94],{},"display name"," is freeform and only for presentation. The ",[49,97,98],{},"email"," is for login. Keeping them apart means a user can rename themselves without breaking every link that pointed at them.",[54,101,103],{"id":102},"a-face-worked-out-at-read-time","A face, worked out at read time",[45,105,106],{},"Rather than store one avatar field, Chronicler resolves an avatar through a fixed ladder, checked in order: an uploaded image, then a provider photo by rank, then Gravatar if the user opted in, then an initials monogram. The user's deliberate choices are real domain state in a preferences aggregate. The provider photo, on the other hand, is treated as cached reference data, refreshed on login and never event-sourced, because it belongs to the provider and can change without telling us.",[54,108,110],{"id":109},"fresh-proof-for-dangerous-actions","Fresh proof for dangerous actions",[45,112,113,114,117],{},"A logged-in session is proof that you signed in, once, maybe hours ago. It is not proof that you are still the person at the keyboard. For a dangerous action, that gap matters, so ",[49,115,116],{},"step-up authorisation",", a sudo mode, asks for a fresh re-proof of identity. The re-proof is recorded server-side as a short-lived grant tied to the session, not handed to the client as a token it has to carry and could lose. One guard reads that grant, and the same guard protects changing your MFA, changing billing, and impersonating a user.",[45,119,120,123],{},[49,121,122],{},"Multi-factor authentication"," is what makes the strong proof strong. A user can enrol an authenticator app, an email code, and single-use recovery codes, with one primary factor and the rest as fallbacks. The authenticator secret is stored as an encrypted, crypto-shreddable event field, so erasing an account destroys it along with everything else.",[54,125,127],{"id":126},"identities-that-are-not-people","Identities that are not people",[45,129,130,131,134],{},"Two kinds of principal are not a human at a keyboard. A ",[49,132,133],{},"service account"," is a machine user owned by a workspace. It cannot sign in, holds its own roles, and authenticates through a scoped API token whose real power is the overlap of its owner's live roles and its own narrower scope. It can never touch a denied capability and never acts as an administrator, so a leaked automation key is bounded by design.",[45,136,137,138,141],{},"The ",[49,139,140],{},"system account"," is the installation itself acting as a user, real but with no password, minted on first run. It is what shows up as the actor when the platform, rather than a person, did something.",[45,143,144,145,148],{},"And sometimes support staff need to see exactly what a user sees. ",[49,146,147],{},"Impersonation"," gives an authorised operator a read-only view as another user, with the real operator kept named in the audit trail, so the convenience never becomes an untraceable back door.",[54,150,152],{"id":151},"where-this-connects","Where this connects",[45,154,155,156,161],{},"Knowing who someone is only sets up the next question. ",[157,158,160],"a",{"href":159},"\u002Finternal\u002Farchitecture\u002Fauthorization","Authorization"," decides what they are allowed to do.",[163,164,167],"callout",{"title":165,"tone":166},"Read the record for the detail","note",[45,168,169,170,174],{},"This page is the guide. The full records under ",[171,172,173],"code",{},"docs\u002Fadrs"," carry the exact token lifetimes, the linking rules, and the alternatives that were weighed and rejected.",{"title":176,"searchDepth":177,"depth":177,"links":178},"",2,[179,180,181,182,183,184,185],{"id":56,"depth":177,"text":57},{"id":69,"depth":177,"text":70},{"id":83,"depth":177,"text":84},{"id":102,"depth":177,"text":103},{"id":109,"depth":177,"text":110},{"id":126,"depth":177,"text":127},{"id":151,"depth":177,"text":152},"How a caller proves who they are, and how an account carries a name, a face, and its login methods - and why each piece works the way it does.","md","Architecture",{},{"title":40},"\u002Finternal\u002Farchitecture\u002Fauthentication",{"title":40,"description":186},null,"internal\u002Farchitecture\u002Fauthentication","Zih0wIExC9PoonvQfr_1ip3x3bPIKE9JK0_yHUsU97M",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":197},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"M12 16v-4m0-4h.01\"\u002F>\u003C\u002Fg>",1788784065845]