[{"data":1,"prerenderedAt":3808},["ShallowReactive",2],{"search-de":3,"content-de-developer\u002Fauthentication":4,"surround-de-\u002Fdeveloper\u002Fauthentication":3799},[],{"id":5,"title":6,"body":7,"description":13,"extension":3793,"meta":3794,"navigation":229,"path":3795,"seo":3796,"stem":3797,"__hash__":3798},"content_de\u002F3.developer\u002F5.authentication.md","Authentifizierung",{"type":8,"value":9,"toc":3767},"minimark",[10,14,17,22,50,98,102,105,109,114,132,936,962,966,988,1467,1471,1475,1486,1668,1683,1687,1698,1912,1936,1940,1944,1961,2202,2206,2288,2292,2341,2345,2578,2582,2603,2633,2637,2746,2968,2972,2976,2979,2983,3013,3019,3144,3276,3279,3358,3362,3373,3400,3511,3526,3668,3677,3751,3763],[11,12,13],"p",{},"Owlat nutzt BetterAuth mit dem Convex-Adapter für Authentifizierung und die Verwaltung von Organisationen (Teams).",[15,16],"illustrations-auth-flow-illustration",{},[18,19,21],"h2",{"id":20},"überblick","Überblick",[23,24,25,33,39],"ul",{},[26,27,28,32],"li",{},[29,30,31],"strong",{},"BetterAuth"," übernimmt Registrierung, Login und Sessions der Nutzer",[26,34,35,38],{},[29,36,37],{},"Organization Plugin"," verwaltet die Team-Mitgliedschaft innerhalb der einen Organisation",[26,40,41,44,45,49],{},[29,42,43],{},"Session-basierter Kontext"," löst Nutzer, Rolle und aktive Org aus dem Request auf — es wird kein ",[46,47,48],"code",{},"teamId","-Parameter herumgereicht",[51,52,54],"callout",{"type":53},"warning",[11,55,56,59,60,63,64,69,70,73,74,77,78,81,82,85,86,89,90,93,94,97],{},[29,57,58],{},"Eine Organisation pro Instanz."," Jede Owlat-Installation beherbergt genau eine Organisation. Die Org wird zur Installationszeit von ",[46,61,62],{},"owlat quickstart"," gebootstrappt (siehe ",[65,66,68],"a",{"href":67},"\u002Fdeveloper\u002Fsetup-cli","Setup-CLI & Installer","), das den ersten Admin über die Backend-Action ",[46,71,72],{},"POST \u002Fseed\u002Fadmin"," (",[46,75,76],{},"apps\u002Fapi\u002Fconvex\u002FseedAdmin.ts",") erzeugt. Der BetterAuth-Endpoint ",[46,79,80],{},"auth\u002Forganization\u002Fcreate"," ist auf Plugin-Ebene deaktiviert (",[46,83,84],{},"allowUserToCreateOrganization: false","), der Auth-Client re-exportiert ",[46,87,88],{},"organization.create","\u002F",[46,91,92],{},"organization.delete"," nicht, und eine Laufzeit-Invariante in ",[46,95,96],{},"getBetterAuthSessionWithRole"," weist jeden Request zurück, dessen Session auf eine Nicht-Singleton-Org zeigt. Mandantenfähiges SaaS-Hosting übernimmt eine separate Control Plane (eine private Codebase), die pro Mandant eine dedizierte Owlat-Instanz bereitstellt.",[18,99,101],{"id":100},"architektur","Architektur",[103,104],"auth-architecture",{},[18,106,108],{"id":107},"konfiguration","Konfiguration",[110,111,113],"h3",{"id":112},"server-authts","Server (auth.ts)",[11,115,116,119,120,123,124,127,128,131],{},[46,117,118],{},"apps\u002Fapi\u002Fconvex\u002Fauth\u002Fauth.ts"," baut die BetterAuth-Instanz aus einem Convex-\n",[46,121,122],{},"ActionCtx"," auf. Die reale Konfiguration verdrahtet vier Plugins und eigene\nRollen und stellt sämtliche Auth-E-Mails über den konfigurierten\nSystem-E-Mail-Transport zu (",[46,125,126],{},"internal.systemMail.sendSystemEmail","), der zu dem\nZustellanbieter routet, der über ",[46,129,130],{},"EMAIL_PROVIDER"," (oder eine Provider-Route pro\nOrg) gesetzt ist — der eingebaute MTA, Amazon SES, Resend, ein generisches\nSMTP-Relay oder Mailchimp Transactional. Es gibt keinen impliziten Default: Ist\nkeiner davon konfiguriert, wird Auth-Mail abgelehnt statt über den MTA\nverschickt.",[133,134,139],"pre",{"className":135,"code":136,"language":137,"meta":138,"style":138},"language-typescript shiki shiki-themes github-light github-dark-dimmed","import { betterAuth } from 'better-auth';\nimport { organization, oneTimeToken } from 'better-auth\u002Fplugins';\nimport { createAccessControl } from 'better-auth\u002Fplugins\u002Faccess';\nimport { convex, crossDomain } from '@convex-dev\u002Fbetter-auth\u002Fplugins';\nimport { getOptional } from '..\u002Flib\u002Fenv';\n\n\u002F\u002F Custom access control: rename BetterAuth's default 'member' role to 'editor'.\nconst ac = createAccessControl({ ...defaultStatements });\nconst owner = ac.newRole({ ...ownerAc.statements });\nconst admin = ac.newRole({ ...adminAc.statements });\nconst editor = ac.newRole({ ...memberAc.statements });\n\nexport const createAuthOptions = (ctx: ActionCtx) => ({\n    database: authComponent.adapter(ctx),\n    secret: getOptional('BETTER_AUTH_SECRET'),\n    baseURL: getOptional('SITE_URL'),\n    emailAndPassword: {\n        enabled: true,\n        minPasswordLength: 10,\n        maxPasswordLength: 128,\n        sendResetPassword: async ({ user, token }) => {\n            await sendViaMta({ to: user.email, \u002F* …reset email… *\u002F });\n        },\n    },\n    session: {\n        expiresIn: 60 * 60 * 24 * 3, \u002F\u002F 3-day sessions\n        updateAge: 60 * 60 * 12,\n        cookieCache: { enabled: true, maxAge: 5 * 60 },\n    },\n    plugins: [\n        \u002F\u002F Cookieless cross-origin auth for the Tauri desktop app.\n        crossDomain({ siteUrl: getOptional('SITE_URL') }),\n        \u002F\u002F Short-lived token redeemed by the desktop deep-link handshake.\n        oneTimeToken(),\n        \u002F\u002F Provides \u002Fconvex\u002Ftoken + \u002Fconvex\u002Fjwks; mints the JWT Convex verifies.\n        \u002F\u002F The JWT payload carries `activeOrganizationId` so the backend can\n        \u002F\u002F resolve the active org from the claim without a session-table read.\n        convex({ authConfig, jwt: { definePayload: ({ user, session }) => ({\n            ...user,\n            activeOrganizationId: session.activeOrganizationId ?? null,\n        }) } }),\n        organization({\n            ac,\n            roles: { owner, admin, editor },\n            \u002F\u002F Single-org-per-instance: the one org is bootstrapped by \u002Fseed\u002Fadmin;\n            \u002F\u002F users cannot create more.\n            allowUserToCreateOrganization: false,\n            creatorRole: 'owner',\n            membershipLimit: 50,\n            invitationExpiresIn: 60 * 60 * 24 * 7, \u002F\u002F 7 days\n            \u002F\u002F Invitation emails are sent through the instance MTA.\n            sendInvitationEmail: async ({ email, organization: org, inviter, invitation }) => {\n                const acceptUrl = `${getOptional('SITE_URL')}\u002Finvite\u002Faccept?id=${invitation.id}`;\n                await sendViaMta({ to: email, \u002F* …invitation email… *\u002F });\n            },\n        }),\n    ],\n});\n","typescript","",[46,140,141,164,179,194,209,224,231,238,264,287,308,329,334,370,382,400,415,421,433,444,455,487,505,511,517,523,553,572,593,598,604,610,628,634,643,649,655,661,689,698,712,718,727,733,739,745,751,762,773,784,810,816,857,896,912,918,924,930],{"__ignoreMap":138},[142,143,146,150,154,157,161],"span",{"class":144,"line":145},"line",1,[142,147,149],{"class":148},"s7YZ4","import",[142,151,153],{"class":152},"sYgZi"," { betterAuth } ",[142,155,156],{"class":148},"from",[142,158,160],{"class":159},"s-HuK"," 'better-auth'",[142,162,163],{"class":152},";\n",[142,165,167,169,172,174,177],{"class":144,"line":166},2,[142,168,149],{"class":148},[142,170,171],{"class":152}," { organization, oneTimeToken } ",[142,173,156],{"class":148},[142,175,176],{"class":159}," 'better-auth\u002Fplugins'",[142,178,163],{"class":152},[142,180,182,184,187,189,192],{"class":144,"line":181},3,[142,183,149],{"class":148},[142,185,186],{"class":152}," { createAccessControl } ",[142,188,156],{"class":148},[142,190,191],{"class":159}," 'better-auth\u002Fplugins\u002Faccess'",[142,193,163],{"class":152},[142,195,197,199,202,204,207],{"class":144,"line":196},4,[142,198,149],{"class":148},[142,200,201],{"class":152}," { convex, crossDomain } ",[142,203,156],{"class":148},[142,205,206],{"class":159}," '@convex-dev\u002Fbetter-auth\u002Fplugins'",[142,208,163],{"class":152},[142,210,212,214,217,219,222],{"class":144,"line":211},5,[142,213,149],{"class":148},[142,215,216],{"class":152}," { getOptional } ",[142,218,156],{"class":148},[142,220,221],{"class":159}," '..\u002Flib\u002Fenv'",[142,223,163],{"class":152},[142,225,227],{"class":144,"line":226},6,[142,228,230],{"emptyLinePlaceholder":229},true,"\n",[142,232,234],{"class":144,"line":233},7,[142,235,237],{"class":236},"sDN9O","\u002F\u002F Custom access control: rename BetterAuth's default 'member' role to 'editor'.\n",[142,239,241,244,248,251,255,258,261],{"class":144,"line":240},8,[142,242,243],{"class":148},"const",[142,245,247],{"class":246},"sviXB"," ac",[142,249,250],{"class":148}," =",[142,252,254],{"class":253},"sPO5f"," createAccessControl",[142,256,257],{"class":152},"({ ",[142,259,260],{"class":148},"...",[142,262,263],{"class":152},"defaultStatements });\n",[142,265,267,269,272,274,277,280,282,284],{"class":144,"line":266},9,[142,268,243],{"class":148},[142,270,271],{"class":246}," owner",[142,273,250],{"class":148},[142,275,276],{"class":152}," ac.",[142,278,279],{"class":253},"newRole",[142,281,257],{"class":152},[142,283,260],{"class":148},[142,285,286],{"class":152},"ownerAc.statements });\n",[142,288,290,292,295,297,299,301,303,305],{"class":144,"line":289},10,[142,291,243],{"class":148},[142,293,294],{"class":246}," admin",[142,296,250],{"class":148},[142,298,276],{"class":152},[142,300,279],{"class":253},[142,302,257],{"class":152},[142,304,260],{"class":148},[142,306,307],{"class":152},"adminAc.statements });\n",[142,309,311,313,316,318,320,322,324,326],{"class":144,"line":310},11,[142,312,243],{"class":148},[142,314,315],{"class":246}," editor",[142,317,250],{"class":148},[142,319,276],{"class":152},[142,321,279],{"class":253},[142,323,257],{"class":152},[142,325,260],{"class":148},[142,327,328],{"class":152},"memberAc.statements });\n",[142,330,332],{"class":144,"line":331},12,[142,333,230],{"emptyLinePlaceholder":229},[142,335,337,340,343,346,348,350,354,357,361,364,367],{"class":144,"line":336},13,[142,338,339],{"class":148},"export",[142,341,342],{"class":148}," const",[142,344,345],{"class":253}," createAuthOptions",[142,347,250],{"class":148},[142,349,73],{"class":152},[142,351,353],{"class":352},"stnAF","ctx",[142,355,356],{"class":148},":",[142,358,360],{"class":359},"sOLd2"," ActionCtx",[142,362,363],{"class":152},") ",[142,365,366],{"class":148},"=>",[142,368,369],{"class":152}," ({\n",[142,371,373,376,379],{"class":144,"line":372},14,[142,374,375],{"class":152},"    database: authComponent.",[142,377,378],{"class":253},"adapter",[142,380,381],{"class":152},"(ctx),\n",[142,383,385,388,391,394,397],{"class":144,"line":384},15,[142,386,387],{"class":152},"    secret: ",[142,389,390],{"class":253},"getOptional",[142,392,393],{"class":152},"(",[142,395,396],{"class":159},"'BETTER_AUTH_SECRET'",[142,398,399],{"class":152},"),\n",[142,401,403,406,408,410,413],{"class":144,"line":402},16,[142,404,405],{"class":152},"    baseURL: ",[142,407,390],{"class":253},[142,409,393],{"class":152},[142,411,412],{"class":159},"'SITE_URL'",[142,414,399],{"class":152},[142,416,418],{"class":144,"line":417},17,[142,419,420],{"class":152},"    emailAndPassword: {\n",[142,422,424,427,430],{"class":144,"line":423},18,[142,425,426],{"class":152},"        enabled: ",[142,428,429],{"class":246},"true",[142,431,432],{"class":152},",\n",[142,434,436,439,442],{"class":144,"line":435},19,[142,437,438],{"class":152},"        minPasswordLength: ",[142,440,441],{"class":246},"10",[142,443,432],{"class":152},[142,445,447,450,453],{"class":144,"line":446},20,[142,448,449],{"class":152},"        maxPasswordLength: ",[142,451,452],{"class":246},"128",[142,454,432],{"class":152},[142,456,458,461,464,467,470,473,476,479,482,484],{"class":144,"line":457},21,[142,459,460],{"class":253},"        sendResetPassword",[142,462,463],{"class":152},": ",[142,465,466],{"class":148},"async",[142,468,469],{"class":152}," ({ ",[142,471,472],{"class":352},"user",[142,474,475],{"class":152},", ",[142,477,478],{"class":352},"token",[142,480,481],{"class":152}," }) ",[142,483,366],{"class":148},[142,485,486],{"class":152}," {\n",[142,488,490,493,496,499,502],{"class":144,"line":489},22,[142,491,492],{"class":148},"            await",[142,494,495],{"class":253}," sendViaMta",[142,497,498],{"class":152},"({ to: user.email, ",[142,500,501],{"class":236},"\u002F* …reset email… *\u002F",[142,503,504],{"class":152}," });\n",[142,506,508],{"class":144,"line":507},23,[142,509,510],{"class":152},"        },\n",[142,512,514],{"class":144,"line":513},24,[142,515,516],{"class":152},"    },\n",[142,518,520],{"class":144,"line":519},25,[142,521,522],{"class":152},"    session: {\n",[142,524,526,529,532,535,538,540,543,545,548,550],{"class":144,"line":525},26,[142,527,528],{"class":152},"        expiresIn: ",[142,530,531],{"class":246},"60",[142,533,534],{"class":148}," *",[142,536,537],{"class":246}," 60",[142,539,534],{"class":148},[142,541,542],{"class":246}," 24",[142,544,534],{"class":148},[142,546,547],{"class":246}," 3",[142,549,475],{"class":152},[142,551,552],{"class":236},"\u002F\u002F 3-day sessions\n",[142,554,556,559,561,563,565,567,570],{"class":144,"line":555},27,[142,557,558],{"class":152},"        updateAge: ",[142,560,531],{"class":246},[142,562,534],{"class":148},[142,564,537],{"class":246},[142,566,534],{"class":148},[142,568,569],{"class":246}," 12",[142,571,432],{"class":152},[142,573,575,578,580,583,586,588,590],{"class":144,"line":574},28,[142,576,577],{"class":152},"        cookieCache: { enabled: ",[142,579,429],{"class":246},[142,581,582],{"class":152},", maxAge: ",[142,584,585],{"class":246},"5",[142,587,534],{"class":148},[142,589,537],{"class":246},[142,591,592],{"class":152}," },\n",[142,594,596],{"class":144,"line":595},29,[142,597,516],{"class":152},[142,599,601],{"class":144,"line":600},30,[142,602,603],{"class":152},"    plugins: [\n",[142,605,607],{"class":144,"line":606},31,[142,608,609],{"class":236},"        \u002F\u002F Cookieless cross-origin auth for the Tauri desktop app.\n",[142,611,613,616,619,621,623,625],{"class":144,"line":612},32,[142,614,615],{"class":253},"        crossDomain",[142,617,618],{"class":152},"({ siteUrl: ",[142,620,390],{"class":253},[142,622,393],{"class":152},[142,624,412],{"class":159},[142,626,627],{"class":152},") }),\n",[142,629,631],{"class":144,"line":630},33,[142,632,633],{"class":236},"        \u002F\u002F Short-lived token redeemed by the desktop deep-link handshake.\n",[142,635,637,640],{"class":144,"line":636},34,[142,638,639],{"class":253},"        oneTimeToken",[142,641,642],{"class":152},"(),\n",[142,644,646],{"class":144,"line":645},35,[142,647,648],{"class":236},"        \u002F\u002F Provides \u002Fconvex\u002Ftoken + \u002Fconvex\u002Fjwks; mints the JWT Convex verifies.\n",[142,650,652],{"class":144,"line":651},36,[142,653,654],{"class":236},"        \u002F\u002F The JWT payload carries `activeOrganizationId` so the backend can\n",[142,656,658],{"class":144,"line":657},37,[142,659,660],{"class":236},"        \u002F\u002F resolve the active org from the claim without a session-table read.\n",[142,662,664,667,670,673,676,678,680,683,685,687],{"class":144,"line":663},38,[142,665,666],{"class":253},"        convex",[142,668,669],{"class":152},"({ authConfig, jwt: { ",[142,671,672],{"class":253},"definePayload",[142,674,675],{"class":152},": ({ ",[142,677,472],{"class":352},[142,679,475],{"class":152},[142,681,682],{"class":352},"session",[142,684,481],{"class":152},[142,686,366],{"class":148},[142,688,369],{"class":152},[142,690,692,695],{"class":144,"line":691},39,[142,693,694],{"class":148},"            ...",[142,696,697],{"class":152},"user,\n",[142,699,701,704,707,710],{"class":144,"line":700},40,[142,702,703],{"class":152},"            activeOrganizationId: session.activeOrganizationId ",[142,705,706],{"class":148},"??",[142,708,709],{"class":246}," null",[142,711,432],{"class":152},[142,713,715],{"class":144,"line":714},41,[142,716,717],{"class":152},"        }) } }),\n",[142,719,721,724],{"class":144,"line":720},42,[142,722,723],{"class":253},"        organization",[142,725,726],{"class":152},"({\n",[142,728,730],{"class":144,"line":729},43,[142,731,732],{"class":152},"            ac,\n",[142,734,736],{"class":144,"line":735},44,[142,737,738],{"class":152},"            roles: { owner, admin, editor },\n",[142,740,742],{"class":144,"line":741},45,[142,743,744],{"class":236},"            \u002F\u002F Single-org-per-instance: the one org is bootstrapped by \u002Fseed\u002Fadmin;\n",[142,746,748],{"class":144,"line":747},46,[142,749,750],{"class":236},"            \u002F\u002F users cannot create more.\n",[142,752,754,757,760],{"class":144,"line":753},47,[142,755,756],{"class":152},"            allowUserToCreateOrganization: ",[142,758,759],{"class":246},"false",[142,761,432],{"class":152},[142,763,765,768,771],{"class":144,"line":764},48,[142,766,767],{"class":152},"            creatorRole: ",[142,769,770],{"class":159},"'owner'",[142,772,432],{"class":152},[142,774,776,779,782],{"class":144,"line":775},49,[142,777,778],{"class":152},"            membershipLimit: ",[142,780,781],{"class":246},"50",[142,783,432],{"class":152},[142,785,787,790,792,794,796,798,800,802,805,807],{"class":144,"line":786},50,[142,788,789],{"class":152},"            invitationExpiresIn: ",[142,791,531],{"class":246},[142,793,534],{"class":148},[142,795,537],{"class":246},[142,797,534],{"class":148},[142,799,542],{"class":246},[142,801,534],{"class":148},[142,803,804],{"class":246}," 7",[142,806,475],{"class":152},[142,808,809],{"class":236},"\u002F\u002F 7 days\n",[142,811,813],{"class":144,"line":812},51,[142,814,815],{"class":236},"            \u002F\u002F Invitation emails are sent through the instance MTA.\n",[142,817,819,822,824,826,828,831,833,836,838,841,843,846,848,851,853,855],{"class":144,"line":818},52,[142,820,821],{"class":253},"            sendInvitationEmail",[142,823,463],{"class":152},[142,825,466],{"class":148},[142,827,469],{"class":152},[142,829,830],{"class":352},"email",[142,832,475],{"class":152},[142,834,835],{"class":352},"organization",[142,837,463],{"class":152},[142,839,840],{"class":352},"org",[142,842,475],{"class":152},[142,844,845],{"class":352},"inviter",[142,847,475],{"class":152},[142,849,850],{"class":352},"invitation",[142,852,481],{"class":152},[142,854,366],{"class":148},[142,856,486],{"class":152},[142,858,860,863,866,868,871,873,875,877,880,883,885,888,891,894],{"class":144,"line":859},53,[142,861,862],{"class":148},"                const",[142,864,865],{"class":246}," acceptUrl",[142,867,250],{"class":148},[142,869,870],{"class":159}," `${",[142,872,390],{"class":253},[142,874,393],{"class":159},[142,876,412],{"class":159},[142,878,879],{"class":159},")",[142,881,882],{"class":159},"}\u002Finvite\u002Faccept?id=${",[142,884,850],{"class":152},[142,886,887],{"class":159},".",[142,889,890],{"class":152},"id",[142,892,893],{"class":159},"}`",[142,895,163],{"class":152},[142,897,899,902,904,907,910],{"class":144,"line":898},54,[142,900,901],{"class":148},"                await",[142,903,495],{"class":253},[142,905,906],{"class":152},"({ to: email, ",[142,908,909],{"class":236},"\u002F* …invitation email… *\u002F",[142,911,504],{"class":152},[142,913,915],{"class":144,"line":914},55,[142,916,917],{"class":152},"            },\n",[142,919,921],{"class":144,"line":920},56,[142,922,923],{"class":152},"        }),\n",[142,925,927],{"class":144,"line":926},57,[142,928,929],{"class":152},"    ],\n",[142,931,933],{"class":144,"line":932},58,[142,934,935],{"class":152},"});\n",[51,937,940],{"type":938,"title":939},"info","E-Mail wird über den konfigurierten Transport versendet",[11,941,942,943,946,947,950,951,954,955,958,959,961],{},"Wenn der aufgelöste Provider der eingebaute MTA ist (",[46,944,945],{},"EMAIL_PROVIDER=mta","),\nsendet ",[46,948,949],{},"sendViaMta"," letztlich einen POST an ",[46,952,953],{},"${MTA_API_URL}\u002Fsend"," mit einem\n",[46,956,957],{},"Bearer ${MTA_API_KEY}","-Header — derselbe MTA, der Ihre Kampagnen zustellt.\nJede andere Art, die der Katalog deklariert, trägt Einladungs-, Passwort-Reset-\nund E-Mail-Änderungsnachrichten genauso gut, ausgewählt über ",[46,960,130],{},"\noder eine Provider-Route.",[110,963,965],{"id":964},"client-auth-clientts","Client (auth-client.ts)",[11,967,968,971,972,975,976,979,980,983,984,987],{},[46,969,970],{},"apps\u002Fweb\u002Fapp\u002Flib\u002Fauth-client.ts"," baut den BetterAuth-Vue-Client. Es gibt eine\n",[29,973,974],{},"Trennung zwischen Web und Desktop",": Der Web-Build proxyt Auth-Requests\nsame-origin (Nitro) über ",[46,977,978],{},"window.location.origin"," (mit Rückfall auf\n",[46,981,982],{},"NUXT_PUBLIC_SITE_URL"," während des SSR), während der Tauri-Desktop-Build direkt\nmit der Convex-Site-URL des aktiven Workspace spricht und die Session über den\n",[46,985,986],{},"crossDomainClient"," in einem Header mitführt (persistiert im Schlüsselbund des\nBetriebssystems).",[133,989,991],{"className":135,"code":990,"language":137,"meta":138,"style":138},"import { createAuthClient } from 'better-auth\u002Fvue';\nimport { convexClient, crossDomainClient } from '@convex-dev\u002Fbetter-auth\u002Fclient\u002Fplugins';\nimport { organizationClient } from 'better-auth\u002Fclient\u002Fplugins';\n\nfunction createWebAuthClient() {\n    const siteUrl =\n        typeof window !== 'undefined'\n            ? window.location.origin\n            : (globalThis.process?.env?.NUXT_PUBLIC_SITE_URL ?? 'http:\u002F\u002Flocalhost:3000');\n    return createAuthClient({\n        baseURL: siteUrl,\n        plugins: [convexClient(), organizationClient()],\n    });\n}\n\n\u002F\u002F Desktop (Tauri) adds the cross-domain plugin + keychain-backed storage.\nexport const authClient = isDesktopRuntime()\n    ? createAuthClient({\n            baseURL: getActiveWorkspace()?.convexSiteUrl ?? 'http:\u002F\u002Flocalhost:3211',\n            plugins: [convexClient(), organizationClient(), crossDomainClient({ storage: keychainStorage })],\n        })\n    : createWebAuthClient();\n\n\u002F\u002F Auth methods.\nexport const { signIn, signUp, signOut, useSession, getSession } = authClient;\n\n\u002F\u002F Organization methods. `organization.create`\u002F`organization.delete` are\n\u002F\u002F intentionally NOT re-exported — Owlat is single-org-per-instance.\nexport const {\n    organization: {\n        update: updateOrganization,\n        getFullOrganization,\n        list: listOrganizations,\n        setActive: setActiveOrganization,\n        checkSlug: checkOrgSlug,\n        inviteMember,\n        acceptInvitation,\n        rejectInvitation,\n        cancelInvitation,\n        removeMember,\n        updateMemberRole,\n        getActiveMember,\n        listMembers,\n        listInvitations,\n        leave: leaveOrganization,\n    },\n    useListOrganizations,\n    useActiveOrganization,\n} = authClient;\n",[46,992,993,1007,1021,1035,1039,1050,1061,1075,1083,1102,1112,1117,1134,1139,1144,1148,1153,1170,1179,1197,1215,1220,1230,1234,1239,1280,1284,1289,1294,1302,1310,1322,1329,1341,1353,1365,1372,1379,1386,1393,1400,1407,1414,1421,1428,1440,1444,1451,1458],{"__ignoreMap":138},[142,994,995,997,1000,1002,1005],{"class":144,"line":145},[142,996,149],{"class":148},[142,998,999],{"class":152}," { createAuthClient } ",[142,1001,156],{"class":148},[142,1003,1004],{"class":159}," 'better-auth\u002Fvue'",[142,1006,163],{"class":152},[142,1008,1009,1011,1014,1016,1019],{"class":144,"line":166},[142,1010,149],{"class":148},[142,1012,1013],{"class":152}," { convexClient, crossDomainClient } ",[142,1015,156],{"class":148},[142,1017,1018],{"class":159}," '@convex-dev\u002Fbetter-auth\u002Fclient\u002Fplugins'",[142,1020,163],{"class":152},[142,1022,1023,1025,1028,1030,1033],{"class":144,"line":181},[142,1024,149],{"class":148},[142,1026,1027],{"class":152}," { organizationClient } ",[142,1029,156],{"class":148},[142,1031,1032],{"class":159}," 'better-auth\u002Fclient\u002Fplugins'",[142,1034,163],{"class":152},[142,1036,1037],{"class":144,"line":196},[142,1038,230],{"emptyLinePlaceholder":229},[142,1040,1041,1044,1047],{"class":144,"line":211},[142,1042,1043],{"class":148},"function",[142,1045,1046],{"class":253}," createWebAuthClient",[142,1048,1049],{"class":152},"() {\n",[142,1051,1052,1055,1058],{"class":144,"line":226},[142,1053,1054],{"class":148},"    const",[142,1056,1057],{"class":246}," siteUrl",[142,1059,1060],{"class":148}," =\n",[142,1062,1063,1066,1069,1072],{"class":144,"line":233},[142,1064,1065],{"class":148},"        typeof",[142,1067,1068],{"class":152}," window ",[142,1070,1071],{"class":148},"!==",[142,1073,1074],{"class":159}," 'undefined'\n",[142,1076,1077,1080],{"class":144,"line":240},[142,1078,1079],{"class":148},"            ?",[142,1081,1082],{"class":152}," window.location.origin\n",[142,1084,1085,1088,1091,1093,1096,1099],{"class":144,"line":266},[142,1086,1087],{"class":148},"            :",[142,1089,1090],{"class":152}," (globalThis.process?.env?.",[142,1092,982],{"class":246},[142,1094,1095],{"class":148}," ??",[142,1097,1098],{"class":159}," 'http:\u002F\u002Flocalhost:3000'",[142,1100,1101],{"class":152},");\n",[142,1103,1104,1107,1110],{"class":144,"line":289},[142,1105,1106],{"class":148},"    return",[142,1108,1109],{"class":253}," createAuthClient",[142,1111,726],{"class":152},[142,1113,1114],{"class":144,"line":310},[142,1115,1116],{"class":152},"        baseURL: siteUrl,\n",[142,1118,1119,1122,1125,1128,1131],{"class":144,"line":331},[142,1120,1121],{"class":152},"        plugins: [",[142,1123,1124],{"class":253},"convexClient",[142,1126,1127],{"class":152},"(), ",[142,1129,1130],{"class":253},"organizationClient",[142,1132,1133],{"class":152},"()],\n",[142,1135,1136],{"class":144,"line":336},[142,1137,1138],{"class":152},"    });\n",[142,1140,1141],{"class":144,"line":372},[142,1142,1143],{"class":152},"}\n",[142,1145,1146],{"class":144,"line":384},[142,1147,230],{"emptyLinePlaceholder":229},[142,1149,1150],{"class":144,"line":402},[142,1151,1152],{"class":236},"\u002F\u002F Desktop (Tauri) adds the cross-domain plugin + keychain-backed storage.\n",[142,1154,1155,1157,1159,1162,1164,1167],{"class":144,"line":417},[142,1156,339],{"class":148},[142,1158,342],{"class":148},[142,1160,1161],{"class":246}," authClient",[142,1163,250],{"class":148},[142,1165,1166],{"class":253}," isDesktopRuntime",[142,1168,1169],{"class":152},"()\n",[142,1171,1172,1175,1177],{"class":144,"line":423},[142,1173,1174],{"class":148},"    ?",[142,1176,1109],{"class":253},[142,1178,726],{"class":152},[142,1180,1181,1184,1187,1190,1192,1195],{"class":144,"line":435},[142,1182,1183],{"class":152},"            baseURL: ",[142,1185,1186],{"class":253},"getActiveWorkspace",[142,1188,1189],{"class":152},"()?.convexSiteUrl ",[142,1191,706],{"class":148},[142,1193,1194],{"class":159}," 'http:\u002F\u002Flocalhost:3211'",[142,1196,432],{"class":152},[142,1198,1199,1202,1204,1206,1208,1210,1212],{"class":144,"line":446},[142,1200,1201],{"class":152},"            plugins: [",[142,1203,1124],{"class":253},[142,1205,1127],{"class":152},[142,1207,1130],{"class":253},[142,1209,1127],{"class":152},[142,1211,986],{"class":253},[142,1213,1214],{"class":152},"({ storage: keychainStorage })],\n",[142,1216,1217],{"class":144,"line":457},[142,1218,1219],{"class":152},"        })\n",[142,1221,1222,1225,1227],{"class":144,"line":489},[142,1223,1224],{"class":148},"    :",[142,1226,1046],{"class":253},[142,1228,1229],{"class":152},"();\n",[142,1231,1232],{"class":144,"line":507},[142,1233,230],{"emptyLinePlaceholder":229},[142,1235,1236],{"class":144,"line":513},[142,1237,1238],{"class":236},"\u002F\u002F Auth methods.\n",[142,1240,1241,1243,1245,1248,1251,1253,1256,1258,1261,1263,1266,1268,1271,1274,1277],{"class":144,"line":519},[142,1242,339],{"class":148},[142,1244,342],{"class":148},[142,1246,1247],{"class":152}," { ",[142,1249,1250],{"class":246},"signIn",[142,1252,475],{"class":152},[142,1254,1255],{"class":246},"signUp",[142,1257,475],{"class":152},[142,1259,1260],{"class":246},"signOut",[142,1262,475],{"class":152},[142,1264,1265],{"class":246},"useSession",[142,1267,475],{"class":152},[142,1269,1270],{"class":246},"getSession",[142,1272,1273],{"class":152}," } ",[142,1275,1276],{"class":148},"=",[142,1278,1279],{"class":152}," authClient;\n",[142,1281,1282],{"class":144,"line":525},[142,1283,230],{"emptyLinePlaceholder":229},[142,1285,1286],{"class":144,"line":555},[142,1287,1288],{"class":236},"\u002F\u002F Organization methods. `organization.create`\u002F`organization.delete` are\n",[142,1290,1291],{"class":144,"line":574},[142,1292,1293],{"class":236},"\u002F\u002F intentionally NOT re-exported — Owlat is single-org-per-instance.\n",[142,1295,1296,1298,1300],{"class":144,"line":595},[142,1297,339],{"class":148},[142,1299,342],{"class":148},[142,1301,486],{"class":152},[142,1303,1304,1307],{"class":144,"line":600},[142,1305,1306],{"class":352},"    organization",[142,1308,1309],{"class":152},": {\n",[142,1311,1312,1315,1317,1320],{"class":144,"line":606},[142,1313,1314],{"class":352},"        update",[142,1316,463],{"class":152},[142,1318,1319],{"class":246},"updateOrganization",[142,1321,432],{"class":152},[142,1323,1324,1327],{"class":144,"line":612},[142,1325,1326],{"class":246},"        getFullOrganization",[142,1328,432],{"class":152},[142,1330,1331,1334,1336,1339],{"class":144,"line":630},[142,1332,1333],{"class":352},"        list",[142,1335,463],{"class":152},[142,1337,1338],{"class":246},"listOrganizations",[142,1340,432],{"class":152},[142,1342,1343,1346,1348,1351],{"class":144,"line":636},[142,1344,1345],{"class":352},"        setActive",[142,1347,463],{"class":152},[142,1349,1350],{"class":246},"setActiveOrganization",[142,1352,432],{"class":152},[142,1354,1355,1358,1360,1363],{"class":144,"line":645},[142,1356,1357],{"class":352},"        checkSlug",[142,1359,463],{"class":152},[142,1361,1362],{"class":246},"checkOrgSlug",[142,1364,432],{"class":152},[142,1366,1367,1370],{"class":144,"line":651},[142,1368,1369],{"class":246},"        inviteMember",[142,1371,432],{"class":152},[142,1373,1374,1377],{"class":144,"line":657},[142,1375,1376],{"class":246},"        acceptInvitation",[142,1378,432],{"class":152},[142,1380,1381,1384],{"class":144,"line":663},[142,1382,1383],{"class":246},"        rejectInvitation",[142,1385,432],{"class":152},[142,1387,1388,1391],{"class":144,"line":691},[142,1389,1390],{"class":246},"        cancelInvitation",[142,1392,432],{"class":152},[142,1394,1395,1398],{"class":144,"line":700},[142,1396,1397],{"class":246},"        removeMember",[142,1399,432],{"class":152},[142,1401,1402,1405],{"class":144,"line":714},[142,1403,1404],{"class":246},"        updateMemberRole",[142,1406,432],{"class":152},[142,1408,1409,1412],{"class":144,"line":720},[142,1410,1411],{"class":246},"        getActiveMember",[142,1413,432],{"class":152},[142,1415,1416,1419],{"class":144,"line":729},[142,1417,1418],{"class":246},"        listMembers",[142,1420,432],{"class":152},[142,1422,1423,1426],{"class":144,"line":735},[142,1424,1425],{"class":246},"        listInvitations",[142,1427,432],{"class":152},[142,1429,1430,1433,1435,1438],{"class":144,"line":741},[142,1431,1432],{"class":352},"        leave",[142,1434,463],{"class":152},[142,1436,1437],{"class":246},"leaveOrganization",[142,1439,432],{"class":152},[142,1441,1442],{"class":144,"line":747},[142,1443,516],{"class":152},[142,1445,1446,1449],{"class":144,"line":753},[142,1447,1448],{"class":246},"    useListOrganizations",[142,1450,432],{"class":152},[142,1452,1453,1456],{"class":144,"line":764},[142,1454,1455],{"class":246},"    useActiveOrganization",[142,1457,432],{"class":152},[142,1459,1460,1463,1465],{"class":144,"line":775},[142,1461,1462],{"class":152},"} ",[142,1464,1276],{"class":148},[142,1466,1279],{"class":152},[18,1468,1470],{"id":1469},"frontend-composables","Frontend-Composables",[110,1472,1474],{"id":1473},"useauth","useAuth()",[11,1476,1477,1478,1481,1482,1485],{},"Das zentrale Authentifizierungs-Composable (",[46,1479,1480],{},"apps\u002Fweb\u002Fapp\u002Fcomposables\u002FuseAuth.ts",").\nEs kapselt ",[46,1483,1484],{},"authClient.useSession()"," und stellt eine reaktive Session sowie die\nAuth-Aktionen bereit.",[133,1487,1489],{"className":135,"code":1488,"language":137,"meta":138,"style":138},"const {\n    sessionData, \u002F\u002F Full BetterAuth session payload | null\n    user, \u002F\u002F Current user | null\n    currentSession, \u002F\u002F session row (carries activeOrganizationId)\n    status, \u002F\u002F 'pending' | 'authenticated' | 'unauthenticated' | 'error'\n    isAuthenticated, \u002F\u002F Boolean (status === 'authenticated')\n    isPending, \u002F\u002F Boolean (status === 'pending')\n    error, \u002F\u002F Error | null\n    activeOrganizationId, \u002F\u002F Active org ID from the session\n    hasActiveOrganization, \u002F\u002F Boolean\n    signInWithEmail, \u002F\u002F (email, password) => Promise\n    signUpWithEmail, \u002F\u002F (email, password, name) => Promise\n    signOut, \u002F\u002F () => Promise\n    forgotPassword, \u002F\u002F (email) => Promise\n    resetPassword, \u002F\u002F (newPassword, token) => Promise\n    refetch, \u002F\u002F force-refresh the session\n    waitUntilReady, \u002F\u002F await until status leaves 'pending'\n} = useAuth();\n",[46,1490,1491,1497,1507,1517,1527,1537,1547,1557,1567,1577,1587,1597,1607,1617,1627,1637,1647,1657],{"__ignoreMap":138},[142,1492,1493,1495],{"class":144,"line":145},[142,1494,243],{"class":148},[142,1496,486],{"class":152},[142,1498,1499,1502,1504],{"class":144,"line":166},[142,1500,1501],{"class":246},"    sessionData",[142,1503,475],{"class":152},[142,1505,1506],{"class":236},"\u002F\u002F Full BetterAuth session payload | null\n",[142,1508,1509,1512,1514],{"class":144,"line":181},[142,1510,1511],{"class":246},"    user",[142,1513,475],{"class":152},[142,1515,1516],{"class":236},"\u002F\u002F Current user | null\n",[142,1518,1519,1522,1524],{"class":144,"line":196},[142,1520,1521],{"class":246},"    currentSession",[142,1523,475],{"class":152},[142,1525,1526],{"class":236},"\u002F\u002F session row (carries activeOrganizationId)\n",[142,1528,1529,1532,1534],{"class":144,"line":211},[142,1530,1531],{"class":246},"    status",[142,1533,475],{"class":152},[142,1535,1536],{"class":236},"\u002F\u002F 'pending' | 'authenticated' | 'unauthenticated' | 'error'\n",[142,1538,1539,1542,1544],{"class":144,"line":226},[142,1540,1541],{"class":246},"    isAuthenticated",[142,1543,475],{"class":152},[142,1545,1546],{"class":236},"\u002F\u002F Boolean (status === 'authenticated')\n",[142,1548,1549,1552,1554],{"class":144,"line":233},[142,1550,1551],{"class":246},"    isPending",[142,1553,475],{"class":152},[142,1555,1556],{"class":236},"\u002F\u002F Boolean (status === 'pending')\n",[142,1558,1559,1562,1564],{"class":144,"line":240},[142,1560,1561],{"class":246},"    error",[142,1563,475],{"class":152},[142,1565,1566],{"class":236},"\u002F\u002F Error | null\n",[142,1568,1569,1572,1574],{"class":144,"line":266},[142,1570,1571],{"class":246},"    activeOrganizationId",[142,1573,475],{"class":152},[142,1575,1576],{"class":236},"\u002F\u002F Active org ID from the session\n",[142,1578,1579,1582,1584],{"class":144,"line":289},[142,1580,1581],{"class":246},"    hasActiveOrganization",[142,1583,475],{"class":152},[142,1585,1586],{"class":236},"\u002F\u002F Boolean\n",[142,1588,1589,1592,1594],{"class":144,"line":310},[142,1590,1591],{"class":246},"    signInWithEmail",[142,1593,475],{"class":152},[142,1595,1596],{"class":236},"\u002F\u002F (email, password) => Promise\n",[142,1598,1599,1602,1604],{"class":144,"line":331},[142,1600,1601],{"class":246},"    signUpWithEmail",[142,1603,475],{"class":152},[142,1605,1606],{"class":236},"\u002F\u002F (email, password, name) => Promise\n",[142,1608,1609,1612,1614],{"class":144,"line":336},[142,1610,1611],{"class":246},"    signOut",[142,1613,475],{"class":152},[142,1615,1616],{"class":236},"\u002F\u002F () => Promise\n",[142,1618,1619,1622,1624],{"class":144,"line":372},[142,1620,1621],{"class":246},"    forgotPassword",[142,1623,475],{"class":152},[142,1625,1626],{"class":236},"\u002F\u002F (email) => Promise\n",[142,1628,1629,1632,1634],{"class":144,"line":384},[142,1630,1631],{"class":246},"    resetPassword",[142,1633,475],{"class":152},[142,1635,1636],{"class":236},"\u002F\u002F (newPassword, token) => Promise\n",[142,1638,1639,1642,1644],{"class":144,"line":402},[142,1640,1641],{"class":246},"    refetch",[142,1643,475],{"class":152},[142,1645,1646],{"class":236},"\u002F\u002F force-refresh the session\n",[142,1648,1649,1652,1654],{"class":144,"line":417},[142,1650,1651],{"class":246},"    waitUntilReady",[142,1653,475],{"class":152},[142,1655,1656],{"class":236},"\u002F\u002F await until status leaves 'pending'\n",[142,1658,1659,1661,1663,1666],{"class":144,"line":423},[142,1660,1462],{"class":152},[142,1662,1276],{"class":148},[142,1664,1665],{"class":253}," useAuth",[142,1667,1229],{"class":152},[51,1669,1670],{"type":938},[11,1671,1672,1673,1676,1677,1679,1680,887],{},"Die ",[29,1674,1675],{},"Rolle"," des Aufrufers kommt nicht aus ",[46,1678,1474],{}," — sie liegt auf\n",[46,1681,1682],{},"useOrganization().currentMemberRole",[110,1684,1686],{"id":1685},"useorganization","useOrganization()",[11,1688,1689,1690,1693,1694,1697],{},"Verwaltung der Organisationsmitgliedschaft\n(",[46,1691,1692],{},"apps\u002Fweb\u002Fapp\u002Fcomposables\u002FuseOrganization.ts","). Unterlegt mit einem gemeinsamen\n",[46,1695,1696],{},"useState",", sodass sich alle Aufrufer pro Org-Wechsel einen Satz HTTP-Requests\nteilen.",[133,1699,1701],{"className":135,"code":1700,"language":137,"meta":138,"style":138},"const {\n    \u002F\u002F State\n    organization, \u002F\u002F active org | null\n    organizationId, \u002F\u002F active org ID | null\n    organizations, \u002F\u002F list of orgs the user belongs to\n    members, \u002F\u002F Ref\u003COrganizationMember[]>\n    invitations, \u002F\u002F Ref\u003COrganizationInvitation[]> (pending only)\n    currentMemberRole, \u002F\u002F 'owner' | 'admin' | 'editor' | null\n    isLoading,\n    isLoadingMembers,\n\n    \u002F\u002F Permission checks\n    canManageMembers, \u002F\u002F owner or admin\n    isOwner,\n\n    \u002F\u002F Actions\n    fetchMembers, \u002F\u002F ({ force }?) => Promise\n    invite, \u002F\u002F (email, role, mailbox?) => Promise\n    remove, \u002F\u002F (memberIdOrEmail) => Promise\n    updateRole, \u002F\u002F (memberId, role) => Promise\n    cancelInvite, \u002F\u002F (invitationId) => Promise\n    setActive, \u002F\u002F (orgId) => Promise\n    update, \u002F\u002F ({ name?, slug?, logo? }) => Promise\n    getFullOrganization, \u002F\u002F () => Promise\n} = useOrganization();\n",[46,1702,1703,1709,1714,1723,1733,1743,1753,1763,1773,1780,1787,1791,1796,1806,1813,1817,1822,1832,1842,1852,1862,1872,1882,1892,1901],{"__ignoreMap":138},[142,1704,1705,1707],{"class":144,"line":145},[142,1706,243],{"class":148},[142,1708,486],{"class":152},[142,1710,1711],{"class":144,"line":166},[142,1712,1713],{"class":236},"    \u002F\u002F State\n",[142,1715,1716,1718,1720],{"class":144,"line":181},[142,1717,1306],{"class":246},[142,1719,475],{"class":152},[142,1721,1722],{"class":236},"\u002F\u002F active org | null\n",[142,1724,1725,1728,1730],{"class":144,"line":196},[142,1726,1727],{"class":246},"    organizationId",[142,1729,475],{"class":152},[142,1731,1732],{"class":236},"\u002F\u002F active org ID | null\n",[142,1734,1735,1738,1740],{"class":144,"line":211},[142,1736,1737],{"class":246},"    organizations",[142,1739,475],{"class":152},[142,1741,1742],{"class":236},"\u002F\u002F list of orgs the user belongs to\n",[142,1744,1745,1748,1750],{"class":144,"line":226},[142,1746,1747],{"class":246},"    members",[142,1749,475],{"class":152},[142,1751,1752],{"class":236},"\u002F\u002F Ref\u003COrganizationMember[]>\n",[142,1754,1755,1758,1760],{"class":144,"line":233},[142,1756,1757],{"class":246},"    invitations",[142,1759,475],{"class":152},[142,1761,1762],{"class":236},"\u002F\u002F Ref\u003COrganizationInvitation[]> (pending only)\n",[142,1764,1765,1768,1770],{"class":144,"line":240},[142,1766,1767],{"class":246},"    currentMemberRole",[142,1769,475],{"class":152},[142,1771,1772],{"class":236},"\u002F\u002F 'owner' | 'admin' | 'editor' | null\n",[142,1774,1775,1778],{"class":144,"line":266},[142,1776,1777],{"class":246},"    isLoading",[142,1779,432],{"class":152},[142,1781,1782,1785],{"class":144,"line":289},[142,1783,1784],{"class":246},"    isLoadingMembers",[142,1786,432],{"class":152},[142,1788,1789],{"class":144,"line":310},[142,1790,230],{"emptyLinePlaceholder":229},[142,1792,1793],{"class":144,"line":331},[142,1794,1795],{"class":236},"    \u002F\u002F Permission checks\n",[142,1797,1798,1801,1803],{"class":144,"line":336},[142,1799,1800],{"class":246},"    canManageMembers",[142,1802,475],{"class":152},[142,1804,1805],{"class":236},"\u002F\u002F owner or admin\n",[142,1807,1808,1811],{"class":144,"line":372},[142,1809,1810],{"class":246},"    isOwner",[142,1812,432],{"class":152},[142,1814,1815],{"class":144,"line":384},[142,1816,230],{"emptyLinePlaceholder":229},[142,1818,1819],{"class":144,"line":402},[142,1820,1821],{"class":236},"    \u002F\u002F Actions\n",[142,1823,1824,1827,1829],{"class":144,"line":417},[142,1825,1826],{"class":246},"    fetchMembers",[142,1828,475],{"class":152},[142,1830,1831],{"class":236},"\u002F\u002F ({ force }?) => Promise\n",[142,1833,1834,1837,1839],{"class":144,"line":423},[142,1835,1836],{"class":246},"    invite",[142,1838,475],{"class":152},[142,1840,1841],{"class":236},"\u002F\u002F (email, role, mailbox?) => Promise\n",[142,1843,1844,1847,1849],{"class":144,"line":435},[142,1845,1846],{"class":246},"    remove",[142,1848,475],{"class":152},[142,1850,1851],{"class":236},"\u002F\u002F (memberIdOrEmail) => Promise\n",[142,1853,1854,1857,1859],{"class":144,"line":446},[142,1855,1856],{"class":246},"    updateRole",[142,1858,475],{"class":152},[142,1860,1861],{"class":236},"\u002F\u002F (memberId, role) => Promise\n",[142,1863,1864,1867,1869],{"class":144,"line":457},[142,1865,1866],{"class":246},"    cancelInvite",[142,1868,475],{"class":152},[142,1870,1871],{"class":236},"\u002F\u002F (invitationId) => Promise\n",[142,1873,1874,1877,1879],{"class":144,"line":489},[142,1875,1876],{"class":246},"    setActive",[142,1878,475],{"class":152},[142,1880,1881],{"class":236},"\u002F\u002F (orgId) => Promise\n",[142,1883,1884,1887,1889],{"class":144,"line":507},[142,1885,1886],{"class":246},"    update",[142,1888,475],{"class":152},[142,1890,1891],{"class":236},"\u002F\u002F ({ name?, slug?, logo? }) => Promise\n",[142,1893,1894,1897,1899],{"class":144,"line":513},[142,1895,1896],{"class":246},"    getFullOrganization",[142,1898,475],{"class":152},[142,1900,1616],{"class":236},[142,1902,1903,1905,1907,1910],{"class":144,"line":519},[142,1904,1462],{"class":152},[142,1906,1276],{"class":148},[142,1908,1909],{"class":253}," useOrganization",[142,1911,1229],{"class":152},[51,1913,1914],{"type":938},[11,1915,1916,1917,1920,1921,1924,1925,1928,1929,1932,1933,1935],{},"Das Rollenvokabular der App ist ",[46,1918,1919],{},"owner | admin | editor",". BetterAuth speichert\n",[46,1922,1923],{},"editor"," intern als seine eingebaute Rolle ",[46,1926,1927],{},"member"," — ",[46,1930,1931],{},"useOrganization"," bildet an\nder Grenze zwischen beiden ab, sodass Sie im App-Code immer ",[46,1934,1923],{}," sehen.",[18,1937,1939],{"id":1938},"authentifizierungsablauf","Authentifizierungsablauf",[110,1941,1943],{"id":1942},"registrierung","Registrierung",[11,1945,1946,1947,1950,1951,1954,1955,1957,1958,1960],{},"Die öffentliche Registrierung ist ausschließlich auf Einladung möglich. Die\nRegistrierungsseite (",[46,1948,1949],{},"apps\u002Fweb\u002Fapp\u002Fpages\u002Fauth\u002Fregister.vue",") zeigt den Hinweis\n\"Registration is disabled\", sofern sie nicht über einen Redirect von\n",[46,1952,1953],{},"\u002Finvite\u002Faccept?id=…"," erreicht wurde. Es gibt keinen Pfad, der bei der\nRegistrierung eine neue Organisation anlegt. Die erste Organisation wird einmalig\nwährend der Installation von ",[46,1956,72],{}," erzeugt (siehe\n",[46,1959,76],{},"); jeder weitere Nutzer tritt dieser Singleton-Org\nüber den Einladungsablauf als Editor\u002FAdmin\u002FOwner bei.",[133,1962,1966],{"className":1963,"code":1964,"language":1965,"meta":138,"style":138},"language-vue shiki shiki-themes github-light github-dark-dimmed","\u003Cscript setup>\nimport { api } from '@owlat\u002Fapi';\n\nconst { signUpWithEmail } = useAuth();\nconst { run: createUserProfile } = useBackendOperation(api.auth.userProfiles.create);\nconst route = useRoute();\n\nasync function register() {\n    \u002F\u002F 1. Create the BetterAuth user (also refreshes the session).\n    const result = await signUpWithEmail(email.value, password.value, name.value);\n\n    \u002F\u002F 2. Create the Convex-side user profile (non-blocking; failures are toasted).\n    if (result?.user?.id) {\n        await createUserProfile({\n            authUserId: result.user.id,\n            email: email.value,\n            name: name.value,\n        });\n    }\n\n    \u002F\u002F 3. Continue to the invite-accept page (or dashboard); BetterAuth attaches\n    \u002F\u002F    the new user to the singleton org with the invited role.\n    await navigateTo((route.query.redirect as string) || '\u002Fdashboard');\n}\n\u003C\u002Fscript>\n","vue",[46,1967,1968,1984,1998,2002,2019,2043,2057,2061,2073,2078,2096,2100,2105,2113,2123,2128,2133,2138,2143,2148,2152,2157,2162,2189,2193],{"__ignoreMap":138},[142,1969,1970,1973,1977,1981],{"class":144,"line":145},[142,1971,1972],{"class":152},"\u003C",[142,1974,1976],{"class":1975},"sIAta","script",[142,1978,1980],{"class":1979},"sMOND"," setup",[142,1982,1983],{"class":152},">\n",[142,1985,1986,1988,1991,1993,1996],{"class":144,"line":166},[142,1987,149],{"class":148},[142,1989,1990],{"class":152}," { api } ",[142,1992,156],{"class":148},[142,1994,1995],{"class":159}," '@owlat\u002Fapi'",[142,1997,163],{"class":152},[142,1999,2000],{"class":144,"line":181},[142,2001,230],{"emptyLinePlaceholder":229},[142,2003,2004,2006,2008,2011,2013,2015,2017],{"class":144,"line":196},[142,2005,243],{"class":148},[142,2007,1247],{"class":152},[142,2009,2010],{"class":246},"signUpWithEmail",[142,2012,1273],{"class":152},[142,2014,1276],{"class":148},[142,2016,1665],{"class":253},[142,2018,1229],{"class":152},[142,2020,2021,2023,2025,2028,2030,2033,2035,2037,2040],{"class":144,"line":211},[142,2022,243],{"class":148},[142,2024,1247],{"class":152},[142,2026,2027],{"class":352},"run",[142,2029,463],{"class":152},[142,2031,2032],{"class":246},"createUserProfile",[142,2034,1273],{"class":152},[142,2036,1276],{"class":148},[142,2038,2039],{"class":253}," useBackendOperation",[142,2041,2042],{"class":152},"(api.auth.userProfiles.create);\n",[142,2044,2045,2047,2050,2052,2055],{"class":144,"line":226},[142,2046,243],{"class":148},[142,2048,2049],{"class":246}," route",[142,2051,250],{"class":148},[142,2053,2054],{"class":253}," useRoute",[142,2056,1229],{"class":152},[142,2058,2059],{"class":144,"line":233},[142,2060,230],{"emptyLinePlaceholder":229},[142,2062,2063,2065,2068,2071],{"class":144,"line":240},[142,2064,466],{"class":148},[142,2066,2067],{"class":148}," function",[142,2069,2070],{"class":253}," register",[142,2072,1049],{"class":152},[142,2074,2075],{"class":144,"line":266},[142,2076,2077],{"class":236},"    \u002F\u002F 1. Create the BetterAuth user (also refreshes the session).\n",[142,2079,2080,2082,2085,2087,2090,2093],{"class":144,"line":289},[142,2081,1054],{"class":148},[142,2083,2084],{"class":246}," result",[142,2086,250],{"class":148},[142,2088,2089],{"class":148}," await",[142,2091,2092],{"class":253}," signUpWithEmail",[142,2094,2095],{"class":152},"(email.value, password.value, name.value);\n",[142,2097,2098],{"class":144,"line":310},[142,2099,230],{"emptyLinePlaceholder":229},[142,2101,2102],{"class":144,"line":331},[142,2103,2104],{"class":236},"    \u002F\u002F 2. Create the Convex-side user profile (non-blocking; failures are toasted).\n",[142,2106,2107,2110],{"class":144,"line":336},[142,2108,2109],{"class":148},"    if",[142,2111,2112],{"class":152}," (result?.user?.id) {\n",[142,2114,2115,2118,2121],{"class":144,"line":372},[142,2116,2117],{"class":148},"        await",[142,2119,2120],{"class":253}," createUserProfile",[142,2122,726],{"class":152},[142,2124,2125],{"class":144,"line":384},[142,2126,2127],{"class":152},"            authUserId: result.user.id,\n",[142,2129,2130],{"class":144,"line":402},[142,2131,2132],{"class":152},"            email: email.value,\n",[142,2134,2135],{"class":144,"line":417},[142,2136,2137],{"class":152},"            name: name.value,\n",[142,2139,2140],{"class":144,"line":423},[142,2141,2142],{"class":152},"        });\n",[142,2144,2145],{"class":144,"line":435},[142,2146,2147],{"class":152},"    }\n",[142,2149,2150],{"class":144,"line":446},[142,2151,230],{"emptyLinePlaceholder":229},[142,2153,2154],{"class":144,"line":457},[142,2155,2156],{"class":236},"    \u002F\u002F 3. Continue to the invite-accept page (or dashboard); BetterAuth attaches\n",[142,2158,2159],{"class":144,"line":489},[142,2160,2161],{"class":236},"    \u002F\u002F    the new user to the singleton org with the invited role.\n",[142,2163,2164,2167,2170,2173,2176,2179,2181,2184,2187],{"class":144,"line":507},[142,2165,2166],{"class":148},"    await",[142,2168,2169],{"class":253}," navigateTo",[142,2171,2172],{"class":152},"((route.query.redirect ",[142,2174,2175],{"class":148},"as",[142,2177,2178],{"class":246}," string",[142,2180,363],{"class":152},[142,2182,2183],{"class":148},"||",[142,2185,2186],{"class":159}," '\u002Fdashboard'",[142,2188,1101],{"class":152},[142,2190,2191],{"class":144,"line":513},[142,2192,1143],{"class":152},[142,2194,2195,2198,2200],{"class":144,"line":519},[142,2196,2197],{"class":152},"\u003C\u002F",[142,2199,1976],{"class":1975},[142,2201,1983],{"class":152},[110,2203,2205],{"id":2204},"login","Login",[133,2207,2209],{"className":1963,"code":2208,"language":1965,"meta":138,"style":138},"\u003Cscript setup>\nconst { signInWithEmail } = useAuth();\n\nasync function login() {\n    await signInWithEmail(email.value, password.value);\n    await navigateTo('\u002Fdashboard');\n}\n\u003C\u002Fscript>\n",[46,2210,2211,2221,2238,2242,2253,2263,2276,2280],{"__ignoreMap":138},[142,2212,2213,2215,2217,2219],{"class":144,"line":145},[142,2214,1972],{"class":152},[142,2216,1976],{"class":1975},[142,2218,1980],{"class":1979},[142,2220,1983],{"class":152},[142,2222,2223,2225,2227,2230,2232,2234,2236],{"class":144,"line":166},[142,2224,243],{"class":148},[142,2226,1247],{"class":152},[142,2228,2229],{"class":246},"signInWithEmail",[142,2231,1273],{"class":152},[142,2233,1276],{"class":148},[142,2235,1665],{"class":253},[142,2237,1229],{"class":152},[142,2239,2240],{"class":144,"line":181},[142,2241,230],{"emptyLinePlaceholder":229},[142,2243,2244,2246,2248,2251],{"class":144,"line":196},[142,2245,466],{"class":148},[142,2247,2067],{"class":148},[142,2249,2250],{"class":253}," login",[142,2252,1049],{"class":152},[142,2254,2255,2257,2260],{"class":144,"line":211},[142,2256,2166],{"class":148},[142,2258,2259],{"class":253}," signInWithEmail",[142,2261,2262],{"class":152},"(email.value, password.value);\n",[142,2264,2265,2267,2269,2271,2274],{"class":144,"line":226},[142,2266,2166],{"class":148},[142,2268,2169],{"class":253},[142,2270,393],{"class":152},[142,2272,2273],{"class":159},"'\u002Fdashboard'",[142,2275,1101],{"class":152},[142,2277,2278],{"class":144,"line":233},[142,2279,1143],{"class":152},[142,2281,2282,2284,2286],{"class":144,"line":240},[142,2283,2197],{"class":152},[142,2285,1976],{"class":1975},[142,2287,1983],{"class":152},[110,2289,2291],{"id":2290},"geschützte-routen","Geschützte Routen",[133,2293,2295],{"className":1963,"code":2294,"language":1965,"meta":138,"style":138},"\u003C!-- In page component -->\n\u003Cscript setup>\ndefinePageMeta({\n    middleware: 'auth',\n});\n\u003C\u002Fscript>\n",[46,2296,2297,2302,2312,2319,2329,2333],{"__ignoreMap":138},[142,2298,2299],{"class":144,"line":145},[142,2300,2301],{"class":236},"\u003C!-- In page component -->\n",[142,2303,2304,2306,2308,2310],{"class":144,"line":166},[142,2305,1972],{"class":152},[142,2307,1976],{"class":1975},[142,2309,1980],{"class":1979},[142,2311,1983],{"class":152},[142,2313,2314,2317],{"class":144,"line":181},[142,2315,2316],{"class":253},"definePageMeta",[142,2318,726],{"class":152},[142,2320,2321,2324,2327],{"class":144,"line":196},[142,2322,2323],{"class":152},"    middleware: ",[142,2325,2326],{"class":159},"'auth'",[142,2328,432],{"class":152},[142,2330,2331],{"class":144,"line":211},[142,2332,935],{"class":152},[142,2334,2335,2337,2339],{"class":144,"line":226},[142,2336,2197],{"class":152},[142,2338,1976],{"class":1975},[142,2340,1983],{"class":152},[110,2342,2344],{"id":2343},"einladung-annehmen","Einladung annehmen",[133,2346,2348],{"className":1963,"code":2347,"language":1965,"meta":138,"style":138},"\u003C!-- pages\u002Finvite\u002Faccept.vue -->\n\u003Cscript setup>\nconst { isAuthenticated } = useAuth();\nconst invitationId = useRoute().query.id;\n\nasync function acceptInvitation() {\n    await authClient.organization.acceptInvitation({\n        invitationId,\n    });\n    await navigateTo('\u002Fdashboard');\n}\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n    \u003Cdiv v-if=\"!isAuthenticated\">\n        Please \u003CNuxtLink to=\"\u002Fauth\u002Flogin\">log in\u003C\u002FNuxtLink> to accept this invitation.\n    \u003C\u002Fdiv>\n    \u003Cdiv v-else>\n        \u003CUiButton @click=\"acceptInvitation\"> Accept Invitation \u003C\u002FUiButton>\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[46,2349,2350,2355,2365,2382,2396,2400,2411,2423,2428,2432,2444,2448,2456,2460,2469,2487,2511,2520,2531,2562,2570],{"__ignoreMap":138},[142,2351,2352],{"class":144,"line":145},[142,2353,2354],{"class":236},"\u003C!-- pages\u002Finvite\u002Faccept.vue -->\n",[142,2356,2357,2359,2361,2363],{"class":144,"line":166},[142,2358,1972],{"class":152},[142,2360,1976],{"class":1975},[142,2362,1980],{"class":1979},[142,2364,1983],{"class":152},[142,2366,2367,2369,2371,2374,2376,2378,2380],{"class":144,"line":181},[142,2368,243],{"class":148},[142,2370,1247],{"class":152},[142,2372,2373],{"class":246},"isAuthenticated",[142,2375,1273],{"class":152},[142,2377,1276],{"class":148},[142,2379,1665],{"class":253},[142,2381,1229],{"class":152},[142,2383,2384,2386,2389,2391,2393],{"class":144,"line":196},[142,2385,243],{"class":148},[142,2387,2388],{"class":246}," invitationId",[142,2390,250],{"class":148},[142,2392,2054],{"class":253},[142,2394,2395],{"class":152},"().query.id;\n",[142,2397,2398],{"class":144,"line":211},[142,2399,230],{"emptyLinePlaceholder":229},[142,2401,2402,2404,2406,2409],{"class":144,"line":226},[142,2403,466],{"class":148},[142,2405,2067],{"class":148},[142,2407,2408],{"class":253}," acceptInvitation",[142,2410,1049],{"class":152},[142,2412,2413,2415,2418,2421],{"class":144,"line":233},[142,2414,2166],{"class":148},[142,2416,2417],{"class":152}," authClient.organization.",[142,2419,2420],{"class":253},"acceptInvitation",[142,2422,726],{"class":152},[142,2424,2425],{"class":144,"line":240},[142,2426,2427],{"class":152},"        invitationId,\n",[142,2429,2430],{"class":144,"line":266},[142,2431,1138],{"class":152},[142,2433,2434,2436,2438,2440,2442],{"class":144,"line":289},[142,2435,2166],{"class":148},[142,2437,2169],{"class":253},[142,2439,393],{"class":152},[142,2441,2273],{"class":159},[142,2443,1101],{"class":152},[142,2445,2446],{"class":144,"line":310},[142,2447,1143],{"class":152},[142,2449,2450,2452,2454],{"class":144,"line":331},[142,2451,2197],{"class":152},[142,2453,1976],{"class":1975},[142,2455,1983],{"class":152},[142,2457,2458],{"class":144,"line":336},[142,2459,230],{"emptyLinePlaceholder":229},[142,2461,2462,2464,2467],{"class":144,"line":372},[142,2463,1972],{"class":152},[142,2465,2466],{"class":1975},"template",[142,2468,1983],{"class":152},[142,2470,2471,2474,2477,2480,2482,2485],{"class":144,"line":384},[142,2472,2473],{"class":152},"    \u003C",[142,2475,2476],{"class":1975},"div",[142,2478,2479],{"class":1979}," v-if",[142,2481,1276],{"class":152},[142,2483,2484],{"class":159},"\"!isAuthenticated\"",[142,2486,1983],{"class":152},[142,2488,2489,2492,2495,2498,2500,2503,2506,2508],{"class":144,"line":402},[142,2490,2491],{"class":152},"        Please \u003C",[142,2493,2494],{"class":1975},"NuxtLink",[142,2496,2497],{"class":1979}," to",[142,2499,1276],{"class":152},[142,2501,2502],{"class":159},"\"\u002Fauth\u002Flogin\"",[142,2504,2505],{"class":152},">log in\u003C\u002F",[142,2507,2494],{"class":1975},[142,2509,2510],{"class":152},"> to accept this invitation.\n",[142,2512,2513,2516,2518],{"class":144,"line":417},[142,2514,2515],{"class":152},"    \u003C\u002F",[142,2517,2476],{"class":1975},[142,2519,1983],{"class":152},[142,2521,2522,2524,2526,2529],{"class":144,"line":423},[142,2523,2473],{"class":152},[142,2525,2476],{"class":1975},[142,2527,2528],{"class":1979}," v-else",[142,2530,1983],{"class":152},[142,2532,2533,2536,2539,2542,2545,2547,2551,2553,2555,2558,2560],{"class":144,"line":435},[142,2534,2535],{"class":152},"        \u003C",[142,2537,2538],{"class":1975},"UiButton",[142,2540,2541],{"class":152}," @",[142,2543,2544],{"class":1979},"click",[142,2546,1276],{"class":152},[142,2548,2550],{"class":2549},"s4G6O","\"",[142,2552,2420],{"class":152},[142,2554,2550],{"class":2549},[142,2556,2557],{"class":152},"> Accept Invitation \u003C\u002F",[142,2559,2538],{"class":1975},[142,2561,1983],{"class":152},[142,2563,2564,2566,2568],{"class":144,"line":446},[142,2565,2515],{"class":152},[142,2567,2476],{"class":1975},[142,2569,1983],{"class":152},[142,2571,2572,2574,2576],{"class":144,"line":457},[142,2573,2197],{"class":152},[142,2575,2466],{"class":1975},[142,2577,1983],{"class":152},[18,2579,2581],{"id":2580},"session-kontext-im-backend","Session-Kontext im Backend",[11,2583,2584,2585,2588,2589,2594,2595,2598,2599,2602],{},"Alle Session-Helfer des Backends liegen in\n",[46,2586,2587],{},"apps\u002Fapi\u002Fconvex\u002Flib\u002FsessionOrganization.ts",". Es gibt ",[29,2590,2591,2592],{},"kein ",[46,2593,48],{}," irgendwo\nim Session-Kontext des Backends — das Single-Org-Refactoring hat das Konzept der\nTeam-ID entfernt. Daten werden über die aktive Org gescopt, die die Helfer aus\ndem Claim ",[46,2596,2597],{},"activeOrganizationId"," des Convex-JWT lesen (mit Rückfall auf die\nBetterAuth-Session-Tabelle, solange ältere Tokens noch rotieren). Eine\nLaufzeit-Invariante (",[46,2600,2601],{},"assertSingletonOrgInvariant",") bestätigt, dass genau eine\nOrg existiert und dass die aktive Org der Session mit ihr übereinstimmt.",[51,2604,2606],{"type":2605},"tip",[11,2607,2608,2609,2612,2613,2612,2616,2619,2620,2623,2624,2627,2628,2632],{},"Die meisten öffentlichen Funktionen rufen diese Helfer nie von Hand auf. Sie\nnutzen die Wrapper ",[46,2610,2611],{},"authedQuery"," \u002F ",[46,2614,2615],{},"authedMutation",[46,2617,2618],{},"authedAction"," \u002F\n",[46,2621,2622],{},"adminMutation"," aus ",[46,2625,2626],{},"apps\u002Fapi\u002Fconvex\u002Flib\u002FauthedFunctions.ts"," (siehe\n",[65,2629,2631],{"href":2630},"\u002Fdeveloper\u002Fconvex","Convex-Backend","), die die Session-\u002FRollenauflösung und das\nBerechtigungs-Gate für Sie ausführen. Greifen Sie zu den Helfern unten, wenn Sie\ninnerhalb eines gewrappten Handlers feinere Kontrolle benötigen.",[110,2634,2636],{"id":2635},"session-helfer","Session-Helfer",[2638,2639,2640,2656],"table",{},[2641,2642,2643],"thead",{},[2644,2645,2646,2650,2653],"tr",{},[2647,2648,2649],"th",{},"Helfer",[2647,2651,2652],{},"Rückgabe",[2647,2654,2655],{},"Einsatz",[2657,2658,2659,2679,2696,2712,2728],"tbody",{},[2644,2660,2661,2667,2672],{},[2662,2663,2664],"td",{},[46,2665,2666],{},"getBetterAuthSession(ctx)",[2662,2668,2669],{},[46,2670,2671],{},"{ userId, activeOrganizationId } | null",[2662,2673,2674,2675,2678],{},"Weiches Lesen; ",[46,2676,2677],{},"null"," für anonyme Aufrufer",[2644,2680,2681,2686,2693],{},[2662,2682,2683],{},[46,2684,2685],{},"getBetterAuthSessionWithRole(ctx)",[2662,2687,2688,2689,2692],{},"ergänzt ",[46,2690,2691],{},"role: OrganizationRole | null","; erzwingt die Singleton-Org-Invariante",[2662,2694,2695],{},"Wenn Sie die Rolle des Aufrufers brauchen",[2644,2697,2698,2703,2709],{},[2662,2699,2700],{},[46,2701,2702],{},"getUserIdFromSession(ctx)",[2662,2704,2705,2708],{},[46,2706,2707],{},"string"," (userId)",[2662,2710,2711],{},"Nur lesende Queries, die ausschließlich die User-ID benötigen; wirft, wenn nicht authentifiziert",[2644,2713,2714,2719,2725],{},[2662,2715,2716],{},[46,2717,2718],{},"requireAuthenticatedIdentity(ctx)",[2662,2720,2721,2722],{},"Convex-",[46,2723,2724],{},"UserIdentity",[2662,2726,2727],{},"Anonyme Aufrufer in Mutations\u002FActions abweisen",[2644,2729,2730,2735,2743],{},[2662,2731,2732],{},[46,2733,2734],{},"getMutationContext(ctx)",[2662,2736,2737,73,2740,879],{},[46,2738,2739],{},"{ userId, role }",[46,2741,2742],{},"MutationSessionContext",[2662,2744,2745],{},"Primärer Helfer für session-basierte Mutations; wirft, wenn nicht authentifiziert, keine aktive Org vorhanden oder kein Mitglied",[133,2747,2749],{"className":135,"code":2748,"language":137,"meta":138,"style":138},"import {\n    getUserIdFromSession,\n    getMutationContext,\n} from '.\u002Flib\u002FsessionOrganization';\n\n\u002F\u002F In a query: just need the user.\nexport const list = authedQuery({\n    handler: async (ctx) => {\n        const userId = await getUserIdFromSession(ctx);\n        \u002F\u002F scope reads to the active org \u002F userId\n    },\n});\n\n\u002F\u002F In a mutation: need the role too. Note there is no teamId.\nexport const create = authedMutation({\n    args: { name: v.string() },\n    handler: async (ctx, args) => {\n        const { userId, role } = await getMutationContext(ctx);\n        requirePermission(hasPermission(role, 'contacts:manage'), 'Only owners\u002Fadmins can create contacts');\n    },\n});\n",[46,2750,2751,2757,2762,2767,2778,2782,2787,2803,2822,2840,2845,2849,2853,2857,2862,2878,2888,2911,2936,2960,2964],{"__ignoreMap":138},[142,2752,2753,2755],{"class":144,"line":145},[142,2754,149],{"class":148},[142,2756,486],{"class":152},[142,2758,2759],{"class":144,"line":166},[142,2760,2761],{"class":152},"    getUserIdFromSession,\n",[142,2763,2764],{"class":144,"line":181},[142,2765,2766],{"class":152},"    getMutationContext,\n",[142,2768,2769,2771,2773,2776],{"class":144,"line":196},[142,2770,1462],{"class":152},[142,2772,156],{"class":148},[142,2774,2775],{"class":159}," '.\u002Flib\u002FsessionOrganization'",[142,2777,163],{"class":152},[142,2779,2780],{"class":144,"line":211},[142,2781,230],{"emptyLinePlaceholder":229},[142,2783,2784],{"class":144,"line":226},[142,2785,2786],{"class":236},"\u002F\u002F In a query: just need the user.\n",[142,2788,2789,2791,2793,2796,2798,2801],{"class":144,"line":233},[142,2790,339],{"class":148},[142,2792,342],{"class":148},[142,2794,2795],{"class":246}," list",[142,2797,250],{"class":148},[142,2799,2800],{"class":253}," authedQuery",[142,2802,726],{"class":152},[142,2804,2805,2808,2810,2812,2814,2816,2818,2820],{"class":144,"line":240},[142,2806,2807],{"class":253},"    handler",[142,2809,463],{"class":152},[142,2811,466],{"class":148},[142,2813,73],{"class":152},[142,2815,353],{"class":352},[142,2817,363],{"class":152},[142,2819,366],{"class":148},[142,2821,486],{"class":152},[142,2823,2824,2827,2830,2832,2834,2837],{"class":144,"line":266},[142,2825,2826],{"class":148},"        const",[142,2828,2829],{"class":246}," userId",[142,2831,250],{"class":148},[142,2833,2089],{"class":148},[142,2835,2836],{"class":253}," getUserIdFromSession",[142,2838,2839],{"class":152},"(ctx);\n",[142,2841,2842],{"class":144,"line":289},[142,2843,2844],{"class":236},"        \u002F\u002F scope reads to the active org \u002F userId\n",[142,2846,2847],{"class":144,"line":310},[142,2848,516],{"class":152},[142,2850,2851],{"class":144,"line":331},[142,2852,935],{"class":152},[142,2854,2855],{"class":144,"line":336},[142,2856,230],{"emptyLinePlaceholder":229},[142,2858,2859],{"class":144,"line":372},[142,2860,2861],{"class":236},"\u002F\u002F In a mutation: need the role too. Note there is no teamId.\n",[142,2863,2864,2866,2868,2871,2873,2876],{"class":144,"line":384},[142,2865,339],{"class":148},[142,2867,342],{"class":148},[142,2869,2870],{"class":246}," create",[142,2872,250],{"class":148},[142,2874,2875],{"class":253}," authedMutation",[142,2877,726],{"class":152},[142,2879,2880,2883,2885],{"class":144,"line":402},[142,2881,2882],{"class":152},"    args: { name: v.",[142,2884,2707],{"class":253},[142,2886,2887],{"class":152},"() },\n",[142,2889,2890,2892,2894,2896,2898,2900,2902,2905,2907,2909],{"class":144,"line":417},[142,2891,2807],{"class":253},[142,2893,463],{"class":152},[142,2895,466],{"class":148},[142,2897,73],{"class":152},[142,2899,353],{"class":352},[142,2901,475],{"class":152},[142,2903,2904],{"class":352},"args",[142,2906,363],{"class":152},[142,2908,366],{"class":148},[142,2910,486],{"class":152},[142,2912,2913,2915,2917,2920,2922,2925,2927,2929,2931,2934],{"class":144,"line":423},[142,2914,2826],{"class":148},[142,2916,1247],{"class":152},[142,2918,2919],{"class":246},"userId",[142,2921,475],{"class":152},[142,2923,2924],{"class":246},"role",[142,2926,1273],{"class":152},[142,2928,1276],{"class":148},[142,2930,2089],{"class":148},[142,2932,2933],{"class":253}," getMutationContext",[142,2935,2839],{"class":152},[142,2937,2938,2941,2943,2946,2949,2952,2955,2958],{"class":144,"line":435},[142,2939,2940],{"class":253},"        requirePermission",[142,2942,393],{"class":152},[142,2944,2945],{"class":253},"hasPermission",[142,2947,2948],{"class":152},"(role, ",[142,2950,2951],{"class":159},"'contacts:manage'",[142,2953,2954],{"class":152},"), ",[142,2956,2957],{"class":159},"'Only owners\u002Fadmins can create contacts'",[142,2959,1101],{"class":152},[142,2961,2962],{"class":144,"line":446},[142,2963,516],{"class":152},[142,2965,2966],{"class":144,"line":457},[142,2967,935],{"class":152},[18,2969,2971],{"id":2970},"rollenbasierter-zugriff","Rollenbasierter Zugriff",[110,2973,2975],{"id":2974},"rollen","Rollen",[2977,2978],"roles-table",{},[110,2980,2982],{"id":2981},"berechtigungsprüfungen","Berechtigungsprüfungen",[11,2984,2985,2986,2989,2990,2993,2994,2997,2998,89,3001,3004,3005,3008,3009,3012],{},"Berechtigungen sind ein typisiertes ",[46,2987,2988],{},"scope:verb","-System, das ebenfalls in\n",[46,2991,2992],{},"lib\u002FsessionOrganization.ts"," definiert ist (es gibt kein Modul\n",[46,2995,2996],{},".\u002Flib\u002Fpermissions",", und die alten Helfer ",[46,2999,3000],{},"isAdminRole",[46,3002,3003],{},"isOwnerRole"," wurden\nentfernt). ",[46,3006,3007],{},"hasPermission(role, permission)"," bildet eine Rolle auf einen Boolean\nab; ",[46,3010,3011],{},"requirePermission(boolean, message?)"," ist das Assertion-Gate.",[11,3014,1672,3015,3018],{},[46,3016,3017],{},"Permission","-Union:",[2638,3020,3021,3031],{},[2641,3022,3023],{},[2644,3024,3025,3028],{},[2647,3026,3027],{},"Berechtigung",[2647,3029,3030],{},"Gewährt an",[2657,3032,3033,3049,3061,3073,3088,3097,3109,3118,3128],{},[2644,3034,3035,3046],{},[2662,3036,3037,475,3040,475,3043],{},[46,3038,3039],{},"campaigns:send",[46,3041,3042],{},"campaigns:manage",[46,3044,3045],{},"campaigns:schedule",[2662,3047,3048],{},"owner, admin",[2644,3050,3051,3059],{},[2662,3052,3053,475,3056],{},[46,3054,3055],{},"templates:manage",[46,3057,3058],{},"automations:manage",[2662,3060,3048],{},[2644,3062,3063,3071],{},[2662,3064,3065,475,3068],{},[46,3066,3067],{},"topics:manage",[46,3069,3070],{},"segments:manage",[2662,3072,3048],{},[2644,3074,3075,3086],{},[2662,3076,3077,475,3080,475,3083],{},[46,3078,3079],{},"media:manage",[46,3081,3082],{},"shareLinks:manage",[46,3084,3085],{},"imports:manage",[2662,3087,3048],{},[2644,3089,3090,3095],{},[2662,3091,3092],{},[46,3093,3094],{},"contacts:manage",[2662,3096,3048],{},[2644,3098,3099,3107],{},[2662,3100,3101,475,3104],{},[46,3102,3103],{},"organization:manage",[46,3105,3106],{},"settings:manage",[2662,3108,3048],{},[2644,3110,3111,3116],{},[2662,3112,3113],{},[46,3114,3115],{},"chat:manage",[2662,3117,3048],{},[2644,3119,3120,3125],{},[2662,3121,3122],{},[46,3123,3124],{},"organization:delete",[2662,3126,3127],{},"nur owner",[2644,3129,3130,3141],{},[2662,3131,3132,475,3135,475,3138],{},[46,3133,3134],{},"emails:test",[46,3136,3137],{},"knowledge:read",[46,3139,3140],{},"chat:participate",[2662,3142,3143],{},"jedes Mitglied",[133,3145,3147],{"className":135,"code":3146,"language":137,"meta":138,"style":138},"import {\n    hasPermission,\n    requirePermission,\n    requireAdminContext,\n    requireOwnerContext,\n    requireOrgPermission,\n    getMutationContext,\n} from '.\u002Flib\u002FsessionOrganization';\n\n\u002F\u002F Boolean check.\nif (hasPermission(role, 'settings:manage')) {\n    \u002F\u002F ...\n}\n\n\u002F\u002F Assertion: requirePermission takes a boolean + message (NOT a role).\nconst { role } = await getMutationContext(ctx);\nrequirePermission(hasPermission(role, 'settings:manage'), 'Only owners\u002Fadmins can change settings');\n",[46,3148,3149,3155,3160,3165,3170,3175,3180,3184,3194,3198,3203,3220,3225,3229,3233,3238,3256],{"__ignoreMap":138},[142,3150,3151,3153],{"class":144,"line":145},[142,3152,149],{"class":148},[142,3154,486],{"class":152},[142,3156,3157],{"class":144,"line":166},[142,3158,3159],{"class":152},"    hasPermission,\n",[142,3161,3162],{"class":144,"line":181},[142,3163,3164],{"class":152},"    requirePermission,\n",[142,3166,3167],{"class":144,"line":196},[142,3168,3169],{"class":152},"    requireAdminContext,\n",[142,3171,3172],{"class":144,"line":211},[142,3173,3174],{"class":152},"    requireOwnerContext,\n",[142,3176,3177],{"class":144,"line":226},[142,3178,3179],{"class":152},"    requireOrgPermission,\n",[142,3181,3182],{"class":144,"line":233},[142,3183,2766],{"class":152},[142,3185,3186,3188,3190,3192],{"class":144,"line":240},[142,3187,1462],{"class":152},[142,3189,156],{"class":148},[142,3191,2775],{"class":159},[142,3193,163],{"class":152},[142,3195,3196],{"class":144,"line":266},[142,3197,230],{"emptyLinePlaceholder":229},[142,3199,3200],{"class":144,"line":289},[142,3201,3202],{"class":236},"\u002F\u002F Boolean check.\n",[142,3204,3205,3208,3210,3212,3214,3217],{"class":144,"line":310},[142,3206,3207],{"class":148},"if",[142,3209,73],{"class":152},[142,3211,2945],{"class":253},[142,3213,2948],{"class":152},[142,3215,3216],{"class":159},"'settings:manage'",[142,3218,3219],{"class":152},")) {\n",[142,3221,3222],{"class":144,"line":331},[142,3223,3224],{"class":236},"    \u002F\u002F ...\n",[142,3226,3227],{"class":144,"line":336},[142,3228,1143],{"class":152},[142,3230,3231],{"class":144,"line":372},[142,3232,230],{"emptyLinePlaceholder":229},[142,3234,3235],{"class":144,"line":384},[142,3236,3237],{"class":236},"\u002F\u002F Assertion: requirePermission takes a boolean + message (NOT a role).\n",[142,3239,3240,3242,3244,3246,3248,3250,3252,3254],{"class":144,"line":402},[142,3241,243],{"class":148},[142,3243,1247],{"class":152},[142,3245,2924],{"class":246},[142,3247,1273],{"class":152},[142,3249,1276],{"class":148},[142,3251,2089],{"class":148},[142,3253,2933],{"class":253},[142,3255,2839],{"class":152},[142,3257,3258,3261,3263,3265,3267,3269,3271,3274],{"class":144,"line":417},[142,3259,3260],{"class":253},"requirePermission",[142,3262,393],{"class":152},[142,3264,2945],{"class":253},[142,3266,2948],{"class":152},[142,3268,3216],{"class":159},[142,3270,2954],{"class":152},[142,3272,3273],{"class":159},"'Only owners\u002Fadmins can change settings'",[142,3275,1101],{"class":152},[11,3277,3278],{},"Für häufige Fälle bevorzugen Sie die übergeordneten Gates, die den Kontext holen\nund in einem Aufruf prüfen:",[133,3280,3282],{"className":135,"code":3281,"language":137,"meta":138,"style":138},"\u002F\u002F Admin (owner\u002Fadmin) — wraps getMutationContext, mutation-only.\nconst session = await requireAdminContext(ctx);\n\n\u002F\u002F Owner only.\nconst session = await requireOwnerContext(ctx);\n\n\u002F\u002F Any typed permission, works in queries OR mutations (e.g. admin-gated reads).\nconst session = await requireOrgPermission(ctx, 'settings:manage');\n",[46,3283,3284,3289,3305,3309,3314,3329,3333,3338],{"__ignoreMap":138},[142,3285,3286],{"class":144,"line":145},[142,3287,3288],{"class":236},"\u002F\u002F Admin (owner\u002Fadmin) — wraps getMutationContext, mutation-only.\n",[142,3290,3291,3293,3296,3298,3300,3303],{"class":144,"line":166},[142,3292,243],{"class":148},[142,3294,3295],{"class":246}," session",[142,3297,250],{"class":148},[142,3299,2089],{"class":148},[142,3301,3302],{"class":253}," requireAdminContext",[142,3304,2839],{"class":152},[142,3306,3307],{"class":144,"line":181},[142,3308,230],{"emptyLinePlaceholder":229},[142,3310,3311],{"class":144,"line":196},[142,3312,3313],{"class":236},"\u002F\u002F Owner only.\n",[142,3315,3316,3318,3320,3322,3324,3327],{"class":144,"line":211},[142,3317,243],{"class":148},[142,3319,3295],{"class":246},[142,3321,250],{"class":148},[142,3323,2089],{"class":148},[142,3325,3326],{"class":253}," requireOwnerContext",[142,3328,2839],{"class":152},[142,3330,3331],{"class":144,"line":226},[142,3332,230],{"emptyLinePlaceholder":229},[142,3334,3335],{"class":144,"line":233},[142,3336,3337],{"class":236},"\u002F\u002F Any typed permission, works in queries OR mutations (e.g. admin-gated reads).\n",[142,3339,3340,3342,3344,3346,3348,3351,3354,3356],{"class":144,"line":240},[142,3341,243],{"class":148},[142,3343,3295],{"class":246},[142,3345,250],{"class":148},[142,3347,2089],{"class":148},[142,3349,3350],{"class":253}," requireOrgPermission",[142,3352,3353],{"class":152},"(ctx, ",[142,3355,3216],{"class":159},[142,3357,1101],{"class":152},[18,3359,3361],{"id":3360},"authentifizierung-per-api-key","Authentifizierung per API-Key",[11,3363,3364,3365,3368,3369,3372],{},"Öffentliche REST-Endpoints authentifizieren sich mit einem API-Key statt mit\neiner Session. Die Logik liegt in ",[46,3366,3367],{},"apps\u002Fapi\u002Fconvex\u002Fauth\u002FapiAuth.ts",". Keys sind\nmit ",[46,3370,3371],{},"lm_live_"," präfigiert, werden ausschließlich als SHA-256-Hash gespeichert und\nsind auf 10 Requests pro Sekunde und Key rate-limitiert.",[11,3374,3375,3378,3379,3382,3383,3386,3387,3390,3391,3394,3395,3399],{},[46,3376,3377],{},"authenticateApiRequest(ctx, request)"," liefert eine diskriminierte Union zurück —\nder Diskriminator ist ",[46,3380,3381],{},"success",", und im Erfolgsfall erhalten Sie eine ",[46,3384,3385],{},"keyId",", die\n",[46,3388,3389],{},"scopes"," des Keys und ",[46,3392,3393],{},"rateLimit"," (es gibt ",[29,3396,2591,3397],{},[46,3398,48],{},"; Keys sind\nkey-gescopt):",[133,3401,3403],{"className":135,"code":3402,"language":137,"meta":138,"style":138},"type ApiAuthResponse =\n    | { success: true; keyId: Id\u003C'apiKeys'>; scopes: string[]; rateLimit: RateLimitHeaders }\n    | { success: false; error: string; status: number; retryAfter?: number };\n",[46,3404,3405,3415,3466],{"__ignoreMap":138},[142,3406,3407,3410,3413],{"class":144,"line":145},[142,3408,3409],{"class":148},"type",[142,3411,3412],{"class":359}," ApiAuthResponse",[142,3414,1060],{"class":148},[142,3416,3417,3420,3422,3424,3426,3429,3432,3434,3436,3439,3441,3444,3447,3449,3451,3453,3456,3458,3460,3463],{"class":144,"line":166},[142,3418,3419],{"class":148},"    |",[142,3421,1247],{"class":152},[142,3423,3381],{"class":352},[142,3425,356],{"class":148},[142,3427,3428],{"class":246}," true",[142,3430,3431],{"class":152},"; ",[142,3433,3385],{"class":352},[142,3435,356],{"class":148},[142,3437,3438],{"class":359}," Id",[142,3440,1972],{"class":152},[142,3442,3443],{"class":159},"'apiKeys'",[142,3445,3446],{"class":152},">; ",[142,3448,3389],{"class":352},[142,3450,356],{"class":148},[142,3452,2178],{"class":246},[142,3454,3455],{"class":152},"[]; ",[142,3457,3393],{"class":352},[142,3459,356],{"class":148},[142,3461,3462],{"class":359}," RateLimitHeaders",[142,3464,3465],{"class":152}," }\n",[142,3467,3468,3470,3472,3474,3476,3479,3481,3484,3486,3488,3490,3493,3495,3498,3500,3503,3506,3508],{"class":144,"line":181},[142,3469,3419],{"class":148},[142,3471,1247],{"class":152},[142,3473,3381],{"class":352},[142,3475,356],{"class":148},[142,3477,3478],{"class":246}," false",[142,3480,3431],{"class":152},[142,3482,3483],{"class":352},"error",[142,3485,356],{"class":148},[142,3487,2178],{"class":246},[142,3489,3431],{"class":152},[142,3491,3492],{"class":352},"status",[142,3494,356],{"class":148},[142,3496,3497],{"class":246}," number",[142,3499,3431],{"class":152},[142,3501,3502],{"class":352},"retryAfter",[142,3504,3505],{"class":148},"?:",[142,3507,3497],{"class":246},[142,3509,3510],{"class":152}," };\n",[11,3512,3513,3514,3517,3518,3521,3522,3525],{},"Der idiomatische Weg, einen Endpoint zu bauen, ist ",[46,3515,3516],{},"createAuthenticatedHandler",":\nEr authentifiziert den Request, behandelt den CORS-Preflight, injiziert\n",[46,3519,3520],{},"{ keyId, scopes, rateLimit }"," und hängt Rate-Limit-Header an die Antwort. Keys\ntragen Scopes, die über ",[46,3523,3524],{},"requireScope(auth, scope)"," durchgesetzt werden:",[133,3527,3529],{"className":135,"code":3528,"language":137,"meta":138,"style":138},"import { createAuthenticatedHandler, requireScope } from '.\u002Fauth\u002FapiAuth';\n\nexport const handleRequest = createAuthenticatedHandler(async (ctx, request, auth) => {\n    \u002F\u002F auth.keyId — the validated API key ID\n    \u002F\u002F auth.scopes — the scopes granted to this key\n    \u002F\u002F auth.rateLimit — { limit, remaining, reset }\n    const denied = requireScope(auth, 'contacts:read', request.headers.get('Origin'));\n    if (denied) return denied; \u002F\u002F 403 when the key lacks the scope\n    return jsonResponse({ ok: true });\n});\n",[46,3530,3531,3545,3549,3587,3592,3597,3602,3634,3650,3664],{"__ignoreMap":138},[142,3532,3533,3535,3538,3540,3543],{"class":144,"line":145},[142,3534,149],{"class":148},[142,3536,3537],{"class":152}," { createAuthenticatedHandler, requireScope } ",[142,3539,156],{"class":148},[142,3541,3542],{"class":159}," '.\u002Fauth\u002FapiAuth'",[142,3544,163],{"class":152},[142,3546,3547],{"class":144,"line":166},[142,3548,230],{"emptyLinePlaceholder":229},[142,3550,3551,3553,3555,3558,3560,3563,3565,3567,3569,3571,3573,3576,3578,3581,3583,3585],{"class":144,"line":181},[142,3552,339],{"class":148},[142,3554,342],{"class":148},[142,3556,3557],{"class":246}," handleRequest",[142,3559,250],{"class":148},[142,3561,3562],{"class":253}," createAuthenticatedHandler",[142,3564,393],{"class":152},[142,3566,466],{"class":148},[142,3568,73],{"class":152},[142,3570,353],{"class":352},[142,3572,475],{"class":152},[142,3574,3575],{"class":352},"request",[142,3577,475],{"class":152},[142,3579,3580],{"class":352},"auth",[142,3582,363],{"class":152},[142,3584,366],{"class":148},[142,3586,486],{"class":152},[142,3588,3589],{"class":144,"line":196},[142,3590,3591],{"class":236},"    \u002F\u002F auth.keyId — the validated API key ID\n",[142,3593,3594],{"class":144,"line":211},[142,3595,3596],{"class":236},"    \u002F\u002F auth.scopes — the scopes granted to this key\n",[142,3598,3599],{"class":144,"line":226},[142,3600,3601],{"class":236},"    \u002F\u002F auth.rateLimit — { limit, remaining, reset }\n",[142,3603,3604,3606,3609,3611,3614,3617,3620,3623,3626,3628,3631],{"class":144,"line":233},[142,3605,1054],{"class":148},[142,3607,3608],{"class":246}," denied",[142,3610,250],{"class":148},[142,3612,3613],{"class":253}," requireScope",[142,3615,3616],{"class":152},"(auth, ",[142,3618,3619],{"class":159},"'contacts:read'",[142,3621,3622],{"class":152},", request.headers.",[142,3624,3625],{"class":253},"get",[142,3627,393],{"class":152},[142,3629,3630],{"class":159},"'Origin'",[142,3632,3633],{"class":152},"));\n",[142,3635,3636,3638,3641,3644,3647],{"class":144,"line":240},[142,3637,2109],{"class":148},[142,3639,3640],{"class":152}," (denied) ",[142,3642,3643],{"class":148},"return",[142,3645,3646],{"class":152}," denied; ",[142,3648,3649],{"class":236},"\u002F\u002F 403 when the key lacks the scope\n",[142,3651,3652,3654,3657,3660,3662],{"class":144,"line":266},[142,3653,1106],{"class":148},[142,3655,3656],{"class":253}," jsonResponse",[142,3658,3659],{"class":152},"({ ok: ",[142,3661,429],{"class":246},[142,3663,504],{"class":152},[142,3665,3666],{"class":144,"line":289},[142,3667,935],{"class":152},[11,3669,3670,3671,3674,3675,356],{},"Wenn Sie ",[46,3672,3673],{},"authenticateApiRequest"," direkt aufrufen, verzweigen Sie über ",[46,3676,3381],{},[133,3678,3680],{"className":135,"code":3679,"language":137,"meta":138,"style":138},"const auth = await authenticateApiRequest(ctx, request);\nif (!auth.success) {\n    return errorResponse(auth.status === 429 ? 'rate_limited' : 'unauthenticated', auth.error);\n}\n\u002F\u002F auth.keyId, auth.rateLimit are now available\n",[46,3681,3682,3699,3711,3742,3746],{"__ignoreMap":138},[142,3683,3684,3686,3689,3691,3693,3696],{"class":144,"line":145},[142,3685,243],{"class":148},[142,3687,3688],{"class":246}," auth",[142,3690,250],{"class":148},[142,3692,2089],{"class":148},[142,3694,3695],{"class":253}," authenticateApiRequest",[142,3697,3698],{"class":152},"(ctx, request);\n",[142,3700,3701,3703,3705,3708],{"class":144,"line":166},[142,3702,3207],{"class":148},[142,3704,73],{"class":152},[142,3706,3707],{"class":148},"!",[142,3709,3710],{"class":152},"auth.success) {\n",[142,3712,3713,3715,3718,3721,3724,3727,3730,3733,3736,3739],{"class":144,"line":181},[142,3714,1106],{"class":148},[142,3716,3717],{"class":253}," errorResponse",[142,3719,3720],{"class":152},"(auth.status ",[142,3722,3723],{"class":148},"===",[142,3725,3726],{"class":246}," 429",[142,3728,3729],{"class":148}," ?",[142,3731,3732],{"class":159}," 'rate_limited'",[142,3734,3735],{"class":148}," :",[142,3737,3738],{"class":159}," 'unauthenticated'",[142,3740,3741],{"class":152},", auth.error);\n",[142,3743,3744],{"class":144,"line":196},[142,3745,1143],{"class":152},[142,3747,3748],{"class":144,"line":211},[142,3749,3750],{"class":236},"\u002F\u002F auth.keyId, auth.rateLimit are now available\n",[11,3752,3753,3754,3758,3759,887],{},"API-Keys werden im Dashboard erstellt und verwaltet — siehe\n",[65,3755,3757],{"href":3756},"\u002Fguide\u002Fapi-keys-webhooks","API-Keys & Webhooks"," und die\n",[65,3760,3762],{"href":3761},"\u002Fapi\u002Fauthentication","Referenz der Authentifizierungs-API",[3764,3765,3766],"style",{},"html pre.shiki code .s7YZ4, html code.shiki .s7YZ4{--shiki-default:#D73A49;--shiki-dark:#F47067}html pre.shiki code .sYgZi, html code.shiki .sYgZi{--shiki-default:#24292E;--shiki-dark:#ADBAC7}html pre.shiki code .s-HuK, html code.shiki .s-HuK{--shiki-default:#032F62;--shiki-dark:#96D0FF}html pre.shiki code .sDN9O, html code.shiki .sDN9O{--shiki-default:#6A737D;--shiki-dark:#768390}html pre.shiki code .sviXB, html code.shiki .sviXB{--shiki-default:#005CC5;--shiki-dark:#6CB6FF}html pre.shiki code .sPO5f, html code.shiki .sPO5f{--shiki-default:#6F42C1;--shiki-dark:#DCBDFB}html pre.shiki code .stnAF, html code.shiki .stnAF{--shiki-default:#E36209;--shiki-dark:#F69D50}html pre.shiki code .sOLd2, html code.shiki .sOLd2{--shiki-default:#6F42C1;--shiki-dark:#F69D50}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sIAta, html code.shiki .sIAta{--shiki-default:#22863A;--shiki-dark:#8DDB8C}html pre.shiki code .sMOND, html code.shiki .sMOND{--shiki-default:#6F42C1;--shiki-dark:#6CB6FF}html pre.shiki code .s4G6O, html code.shiki .s4G6O{--shiki-default:#032F62;--shiki-dark:#ADBAC7}",{"title":138,"searchDepth":166,"depth":166,"links":3768},[3769,3770,3771,3775,3779,3785,3788,3792],{"id":20,"depth":166,"text":21},{"id":100,"depth":166,"text":101},{"id":107,"depth":166,"text":108,"children":3772},[3773,3774],{"id":112,"depth":181,"text":113},{"id":964,"depth":181,"text":965},{"id":1469,"depth":166,"text":1470,"children":3776},[3777,3778],{"id":1473,"depth":181,"text":1474},{"id":1685,"depth":181,"text":1686},{"id":1938,"depth":166,"text":1939,"children":3780},[3781,3782,3783,3784],{"id":1942,"depth":181,"text":1943},{"id":2204,"depth":181,"text":2205},{"id":2290,"depth":181,"text":2291},{"id":2343,"depth":181,"text":2344},{"id":2580,"depth":166,"text":2581,"children":3786},[3787],{"id":2635,"depth":181,"text":2636},{"id":2970,"depth":166,"text":2971,"children":3789},[3790,3791],{"id":2974,"depth":181,"text":2975},{"id":2981,"depth":181,"text":2982},{"id":3360,"depth":166,"text":3361},"md",{},"\u002Fdeveloper\u002Fauthentication",{"title":6,"description":13},"3.developer\u002F5.authentication","6s7Lhs20eNCMKjL0E5EfW84uiS1pMOestY8omdUGBfU",[3800,3804],{"title":3801,"path":3802,"stem":3803,"children":-1},"Einen Send-Provider schreiben","\u002Fdeveloper\u002Fplugin-send-providers","3.developer\u002F49.plugin-send-providers",{"title":3805,"path":3806,"stem":3807,"children":-1},"E-Mail-System","\u002Fdeveloper\u002Femail-system","3.developer\u002F6.email-system",1786915107521]