[{"data":1,"prerenderedAt":1783},["ShallowReactive",2],{"navigation":3,"/essentials/identity":259,"/essentials/identity-surround":1778},[4,16,56,104,140,151,235,239,243,247,251,255],{"title":5,"path":6,"stem":7,"children":8},"Getting Started","/getting-started","1.getting-started",[9,12],{"title":10,"path":6,"stem":11},"Introduction","1.getting-started/index",{"title":13,"path":14,"stem":15},"Installation","/getting-started/installation","1.getting-started/1.installation",{"title":17,"path":18,"stem":19,"children":20},"Essentials","/essentials","2.essentials",[21,24,28,32,36,40,44,48,52],{"title":22,"path":18,"stem":23},"Core concepts","2.essentials/index",{"title":25,"path":26,"stem":27},"Routing","/essentials/routing","2.essentials/1.routing",{"title":29,"path":30,"stem":31},"Authentication","/essentials/authentication","2.essentials/2.authentication",{"title":33,"path":34,"stem":35},"Identity & User Store","/essentials/identity","2.essentials/3.identity",{"title":37,"path":38,"stem":39},"Requests & Responses","/essentials/requests-responses","2.essentials/4.requests-responses",{"title":41,"path":42,"stem":43},"Controllers","/essentials/controllers","2.essentials/5.controllers",{"title":45,"path":46,"stem":47},"Database","/essentials/database","2.essentials/6.database",{"title":49,"path":50,"stem":51},"Validation","/essentials/validation","2.essentials/7.validation",{"title":53,"path":54,"stem":55},"Migrations","/essentials/migrations","2.essentials/8.migrations",{"title":57,"path":58,"stem":59,"children":60},"Features","/features","3.features",[61,64,68,72,76,80,84,88,92,96,100],{"title":62,"path":58,"stem":63},"Built‑in Capabilities","3.features/index",{"title":65,"path":66,"stem":67},"Caching","/features/caching","3.features/caching",{"title":69,"path":70,"stem":71},"CORS & CSRF","/features/cors-csrf","3.features/cors-csrf",{"title":73,"path":74,"stem":75},"Distributed Locks","/features/distributed-locks","3.features/distributed-locks",{"title":77,"path":78,"stem":79},"Events & Listeners","/features/events","3.features/events",{"title":81,"path":82,"stem":83},"Field Selection","/features/field-selection","3.features/field-selection",{"title":85,"path":86,"stem":87},"File Uploads","/features/file-uploads","3.features/file-uploads",{"title":89,"path":90,"stem":91},"Notifications","/features/notifications","3.features/notifications",{"title":93,"path":94,"stem":95},"Queues & Jobs","/features/queues-jobs","3.features/queues-jobs",{"title":97,"path":98,"stem":99},"Rate Limiting","/features/rate-limiting","3.features/rate-limiting",{"title":101,"path":102,"stem":103},"Task Scheduling","/features/scheduling","3.features/scheduling",{"title":105,"path":106,"stem":107,"children":108},"Advanced","/advanced","4.advanced",[109,112,116,120,124,128,132,136],{"title":110,"path":106,"stem":111},"Techniques and Patterns","4.advanced/index",{"title":113,"path":114,"stem":115},"Configuration","/advanced/configuration","4.advanced/configuration",{"title":117,"path":118,"stem":119},"Dependency Injection","/advanced/dependency-injection","4.advanced/dependency-injection",{"title":121,"path":122,"stem":123},"Middleware","/advanced/middleware","4.advanced/middleware",{"title":125,"path":126,"stem":127},"Performance","/advanced/performance","4.advanced/performance",{"title":129,"path":130,"stem":131},"Repositories","/advanced/repositories","4.advanced/repositories",{"title":133,"path":134,"stem":135},"Service Providers","/advanced/service-providers","4.advanced/service-providers",{"title":137,"path":138,"stem":139},"Testing","/advanced/testing","4.advanced/testing",{"title":141,"path":142,"stem":143,"children":144},"Overview","/tooling","5.tooling",[145,147],{"title":141,"path":142,"stem":146},"5.tooling/index",{"title":148,"path":149,"stem":150},"AI Skills","/tooling/ai-skills","5.tooling/1.ai-skills",{"title":152,"path":153,"stem":154,"children":155},"Cookbook","/cookbook","6.cookbook",[156,159,163,167,171,175,179,183,187,191,195,199,203,207,211,215,219,223,227,231],{"title":157,"path":153,"stem":158},"Recipes & How‑Tos","6.cookbook/index",{"title":160,"path":161,"stem":162},"Routing Recipes","/cookbook/routing","6.cookbook/1.routing",{"title":164,"path":165,"stem":166},"Caching Recipes","/cookbook/caching","6.cookbook/10.caching",{"title":168,"path":169,"stem":170},"Queue Infrastructure","/cookbook/queues-and-jobs","6.cookbook/11.queues-and-jobs",{"title":172,"path":173,"stem":174},"Notification Channels & Templates","/cookbook/notifications","6.cookbook/13.notifications",{"title":176,"path":177,"stem":178},"Storage","/cookbook/storage","6.cookbook/15.storage",{"title":180,"path":181,"stem":182},"Image Processing Examples","/cookbook/image-processing","6.cookbook/16.image-processing",{"title":184,"path":185,"stem":186},"Permissions and Authorization","/cookbook/permissions-and-authorization","6.cookbook/18.permissions-and-authorization",{"title":188,"path":189,"stem":190},"Session Analytics","/cookbook/sessions-analytics","6.cookbook/19.sessions-analytics",{"title":192,"path":193,"stem":194},"Writing Middleware","/cookbook/middleware","6.cookbook/2.middleware",{"title":196,"path":197,"stem":198},"API Metrics","/cookbook/api-metrics","6.cookbook/20.api-metrics",{"title":200,"path":201,"stem":202},"Performance Deep Dive","/cookbook/performance","6.cookbook/21.performance",{"title":204,"path":205,"stem":206},"Memory Management","/cookbook/memory-management","6.cookbook/22.memory-management",{"title":208,"path":209,"stem":210},"Writing Console Commands","/cookbook/console-commands","6.cookbook/23.console-commands",{"title":212,"path":213,"stem":214},"Glueful Extensions","/cookbook/extensions","6.cookbook/25.extensions",{"title":216,"path":217,"stem":218},"Error Handling Guide","/cookbook/error-handling","6.cookbook/4.error-handling",{"title":220,"path":221,"stem":222},"Security Guide","/cookbook/security","6.cookbook/5.security",{"title":224,"path":225,"stem":226},"Service Options Resolver","/cookbook/configuration","6.cookbook/6.configuration",{"title":228,"path":229,"stem":230},"Logging","/cookbook/logging","6.cookbook/7.logging",{"title":232,"path":233,"stem":234},"Database Advanced Features","/cookbook/database","6.cookbook/8.database",{"title":236,"path":237,"stem":238},"API Reference","/api-reference","7.api-reference",{"title":240,"path":241,"stem":242},"CLI Reference","/cli-reference","8.cli-reference",{"title":244,"path":245,"stem":246},"Extensions","/extensions","9.extensions",{"title":248,"path":249,"stem":250},"Contributing","/contributing","contributing",{"title":252,"path":253,"stem":254},"Release Notes","/releases","releases",{"title":256,"path":257,"stem":258},"Release Archive","/releases-archive","releases-archive",{"id":260,"title":33,"body":261,"description":1772,"extension":1773,"links":1774,"meta":1775,"navigation":528,"path":34,"seo":1776,"stem":35,"__hash__":1777},"docs/2.essentials/3.identity.md",{"type":262,"value":263,"toc":1759},"minimark",[264,297,340,345,434,438,448,460,463,482,696,710,714,723,742,801,816,821,827,934,951,955,963,1093,1250,1262,1266,1281,1526,1636,1646,1650,1659,1711,1715,1733,1737,1755],[265,266,267,268,272,273,277,278,282,283,286,287,296],"p",{},"Glueful's core is ",[269,270,271],"strong",{},"provider-agnostic about who your users are."," The framework owns the security spine — sessions, tokens, API keys, auth middleware, authorization — but the ",[274,275,276],"em",{},"concrete user store"," (the ",[279,280,281],"code",{},"users"," table, password hashing, account lifecycle) lives in an ",[269,284,285],{},"extension"," behind a small contract. The first-party store is ",[288,289,293],"a",{"href":290,"rel":291},"https://github.com/glueful/users",[292],"nofollow",[279,294,295],{},"glueful/users","; you can swap your own (LDAP, an existing database, a SaaS directory) by implementing one interface.",[298,299,300],"blockquote",{},[265,301,302,305,306,309,310,313,314,317,318,320,321,324,325,309,328,331,332,335,336,339],{},[269,303,304],{},"Core ships no user store."," Without one enabled, core binds a fail-closed ",[279,307,308],{},"NullUserProvider"," and ",[269,311,312],{},"authentication is disabled by design"," — every credential check returns ",[279,315,316],{},"null",". A fresh app must enable a store; the api-skeleton enables ",[279,319,295],{}," by default. (The old in-core ",[279,322,323],{},"User","/",[279,326,327],{},"UserRepository",[279,329,330],{},"AuthenticatedUser"," were removed in 1.50 — everything now flows through ",[279,333,334],{},"UserIdentity"," + ",[279,337,338],{},"UserProviderInterface",".)",[341,342,344],"h2",{"id":343},"the-pieces","The pieces",[346,347,348,361],"table",{},[349,350,351],"thead",{},[352,353,354,358],"tr",{},[355,356,357],"th",{},"Type",[355,359,360],{},"Role",[362,363,364,379,389,401,411,424],"tbody",{},[352,365,366,372],{},[367,368,369],"td",{},[279,370,371],{},"Glueful\\Auth\\UserIdentity",[367,373,374,375,378],{},"The one canonical runtime identity — identity facts + claims. Immutable, ",[279,376,377],{},"final",".",[352,380,381,386],{},[367,382,383],{},[279,384,385],{},"Glueful\\Auth\\Contracts\\UserProviderInterface",[367,387,388],{},"Looks users up and verifies credentials. Implemented by a user store.",[352,390,391,396],{},[367,392,393],{},[279,394,395],{},"Glueful\\Auth\\NullUserProvider",[367,397,398,399,378],{},"Fail-closed default binding — every lookup returns ",[279,400,316],{},[352,402,403,408],{},[367,404,405],{},[279,406,407],{},"Glueful\\Auth\\IdentityResolver",[367,409,410],{},"Post-auth: applies the account-status gate and folds in claims providers.",[352,412,413,418],{},[367,414,415],{},[279,416,417],{},"Glueful\\Auth\\Contracts\\IdentityClaimsProviderInterface",[367,419,420,421,378],{},"Decorates an identity with claims (e.g. roles). Implemented by RBAC such as ",[279,422,423],{},"glueful/aegis",[352,425,426,431],{},[367,427,428],{},[279,429,430],{},"Glueful\\Auth\\Contracts\\TwoFactorServiceInterface",[367,432,433],{},"Optional 2FA, provided by an extension.",[341,435,437],{"id":436},"the-login-flow","The login flow",[439,440,445],"pre",{"className":441,"code":443,"language":444},[442],"language-text","POST /auth/login\n  → AuthenticationService::verifyCredentials()\n      → UserProviderInterface::verifyCredentials($identifier, $password)   // the store checks the hash\n      → IdentityResolver::resolve($identity)\n            → status gate (allowed_login_statuses)\n            → fold each IdentityClaimsProvider::enrich()  (roles / permissions / …)\n  → session/token layer attaches sessionUuid + provider, persists identity + claims\n","text",[279,446,443],{"__ignoreMap":447},"",[265,449,450,451,454,455,277,457,459],{},"If no user store is installed, ",[279,452,453],{},"verifyCredentials()"," returns ",[279,456,316],{},[279,458,308],{},") and login fails closed.",[341,461,334],{"id":462},"useridentity",[265,464,465,466,469,470,473,474,477,478,481],{},"The authenticated identity ",[269,467,468],{},"plus its runtime claims"," — not a database row. It carries identity facts (uuid, email, username, status), runtime context (session uuid, provider), and an open ",[269,471,472],{},"claims bag"," (roles, scopes, permissions, …). It's immutable; ",[279,475,476],{},"with*()"," methods return copies. Accessors are ",[269,479,480],{},"methods",":",[439,483,487],{"className":484,"code":485,"language":486,"meta":447,"style":447},"language-php shiki shiki-themes material-theme-lighter github-light github-dark monokai","$user = $this->currentUser;       // ?Glueful\\Auth\\UserIdentity  (or $requestUserContext->getUser())\n\n$user->uuid();                    // 'u-abc123'\n$user->email();                   // ?string\n$user->status();                  // ?string  ('active', …)\n$user->roles();                   // list\u003Cstring> — typed claim accessor\n$user->scopes();                  // list\u003Cstring>\n$user->claim('permissions', []);  // arbitrary claim with a default\n$user->attr('tenant_id');         // non-claim attribute\n$user->toArray();                 // array shape used for session/user_data\n","php",[279,488,489,523,530,550,567,584,601,618,652,679],{"__ignoreMap":447},[490,491,494,498,502,506,510,513,516,519],"span",{"class":492,"line":493},"line",1,[490,495,497],{"class":496},"swvn1","$",[490,499,501],{"class":500},"ss--_","user ",[490,503,505],{"class":504},"sGXK2","=",[490,507,509],{"class":508},"sSBr1"," $this",[490,511,512],{"class":504},"->",[490,514,515],{"class":500},"currentUser",[490,517,518],{"class":496},";",[490,520,522],{"class":521},"ss7Ak","       // ?Glueful\\Auth\\UserIdentity  (or $requestUserContext->getUser())\n",[490,524,526],{"class":492,"line":525},2,[490,527,529],{"emptyLinePlaceholder":528},true,"\n",[490,531,533,535,538,540,544,547],{"class":492,"line":532},3,[490,534,497],{"class":496},[490,536,537],{"class":500},"user",[490,539,512],{"class":504},[490,541,543],{"class":542},"sD0ED","uuid",[490,545,546],{"class":496},"();",[490,548,549],{"class":521},"                    // 'u-abc123'\n",[490,551,553,555,557,559,562,564],{"class":492,"line":552},4,[490,554,497],{"class":496},[490,556,537],{"class":500},[490,558,512],{"class":504},[490,560,561],{"class":542},"email",[490,563,546],{"class":496},[490,565,566],{"class":521},"                   // ?string\n",[490,568,570,572,574,576,579,581],{"class":492,"line":569},5,[490,571,497],{"class":496},[490,573,537],{"class":500},[490,575,512],{"class":504},[490,577,578],{"class":542},"status",[490,580,546],{"class":496},[490,582,583],{"class":521},"                  // ?string  ('active', …)\n",[490,585,587,589,591,593,596,598],{"class":492,"line":586},6,[490,588,497],{"class":496},[490,590,537],{"class":500},[490,592,512],{"class":504},[490,594,595],{"class":542},"roles",[490,597,546],{"class":496},[490,599,600],{"class":521},"                   // list\u003Cstring> — typed claim accessor\n",[490,602,604,606,608,610,613,615],{"class":492,"line":603},7,[490,605,497],{"class":496},[490,607,537],{"class":500},[490,609,512],{"class":504},[490,611,612],{"class":542},"scopes",[490,614,546],{"class":496},[490,616,617],{"class":521},"                  // list\u003Cstring>\n",[490,619,621,623,625,627,630,633,637,641,643,646,649],{"class":492,"line":620},8,[490,622,497],{"class":496},[490,624,537],{"class":500},[490,626,512],{"class":504},[490,628,629],{"class":542},"claim",[490,631,632],{"class":496},"(",[490,634,636],{"class":635},"siCPE","'",[490,638,640],{"class":639},"sLACW","permissions",[490,642,636],{"class":635},[490,644,645],{"class":496},",",[490,647,648],{"class":496}," []);",[490,650,651],{"class":521},"  // arbitrary claim with a default\n",[490,653,655,657,659,661,664,666,668,671,673,676],{"class":492,"line":654},9,[490,656,497],{"class":496},[490,658,537],{"class":500},[490,660,512],{"class":504},[490,662,663],{"class":542},"attr",[490,665,632],{"class":496},[490,667,636],{"class":635},[490,669,670],{"class":639},"tenant_id",[490,672,636],{"class":635},[490,674,675],{"class":496},");",[490,677,678],{"class":521},"         // non-claim attribute\n",[490,680,682,684,686,688,691,693],{"class":492,"line":681},10,[490,683,497],{"class":496},[490,685,537],{"class":500},[490,687,512],{"class":504},[490,689,690],{"class":542},"toArray",[490,692,546],{"class":496},[490,694,695],{"class":521},"                 // array shape used for session/user_data\n",[265,697,698,701,702,705,706,709],{},[269,699,700],{},"Identity facts vs. claims:"," a claims provider can change ",[274,703,704],{},"what a user can do"," (claims), never ",[274,707,708],{},"who they are"," (identity facts) — the resolver re-pins the facts after enrichment.",[341,711,713],{"id":712},"enabling-a-user-store","Enabling a user store",[265,715,716,717,324,719,722],{},"Install and enable the first-party store, then migrate (",[279,718,281],{},[279,720,721],{},"profiles"," ship with the extension; the auth spine ships with core):",[439,724,728],{"className":725,"code":726,"language":727,"meta":447,"style":447},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark monokai","composer require glueful/users\n","bash",[279,729,730],{"__ignoreMap":447},[490,731,732,736,739],{"class":492,"line":493},[490,733,735],{"class":734},"sR7ES","composer",[490,737,738],{"class":639}," require",[490,740,741],{"class":639}," glueful/users\n",[439,743,745],{"className":484,"code":744,"language":486,"meta":447,"style":447},"// config/extensions.php\n'enabled' => [\n    'Glueful\\\\Extensions\\\\Users\\\\UsersServiceProvider',\n],\n",[279,746,747,752,767,796],{"__ignoreMap":447},[490,748,749],{"class":492,"line":493},[490,750,751],{"class":521},"// config/extensions.php\n",[490,753,754,756,759,761,764],{"class":492,"line":525},[490,755,636],{"class":635},[490,757,758],{"class":639},"enabled",[490,760,636],{"class":635},[490,762,763],{"class":504}," =>",[490,765,766],{"class":496}," [\n",[490,768,769,772,775,779,781,783,786,788,791,793],{"class":492,"line":532},[490,770,771],{"class":635},"    '",[490,773,774],{"class":639},"Glueful",[490,776,778],{"class":777},"sQeA1","\\\\",[490,780,244],{"class":639},[490,782,778],{"class":777},[490,784,785],{"class":639},"Users",[490,787,778],{"class":777},[490,789,790],{"class":639},"UsersServiceProvider",[490,792,636],{"class":635},[490,794,795],{"class":496},",\n",[490,797,798],{"class":492,"line":552},[490,799,800],{"class":496},"],\n",[439,802,804],{"className":725,"code":803,"language":727,"meta":447,"style":447},"php glueful migrate:run\n",[279,805,806],{"__ignoreMap":447},[490,807,808,810,813],{"class":492,"line":493},[490,809,486],{"class":734},[490,811,812],{"class":639}," glueful",[490,814,815],{"class":639}," migrate:run\n",[817,818,820],"h3",{"id":819},"account-endpoints","Account endpoints",[265,822,823,824,826],{},"With ",[279,825,295],{}," enabled you also get the account lifecycle and read endpoints it ships:",[346,828,829,839],{},[349,830,831],{},[352,832,833,836],{},[355,834,835],{},"Endpoint",[355,837,838],{},"Purpose",[362,840,841,851,868,888,905,918],{},[352,842,843,848],{},[367,844,845],{},[279,846,847],{},"GET /me",[367,849,850],{},"The current authenticated user.",[352,852,853,858],{},[367,854,855],{},[279,856,857],{},"GET /users/{uuid}",[367,859,860,861,864,865,378],{},"Look up a user by uuid. ",[269,862,863],{},"Opt-in"," — ",[279,866,867],{},"USERS_USER_LOOKUP_ENABLED",[352,869,870,875],{},[367,871,872],{},[279,873,874],{},"GET /users",[367,876,877,878,864,880,883,884,887],{},"Paginated user list. ",[269,879,863],{},[279,881,882],{},"USERS_USER_LIST_ENABLED"," (email filtering via ",[279,885,886],{},"USERS_USER_LIST_ALLOW_EMAIL_FILTER",").",[352,889,890,902],{},[367,891,892,895,896,895,899],{},[279,893,894],{},"POST /auth/verify-email",", ",[279,897,898],{},"/auth/verify-otp",[279,900,901],{},"/auth/resend-otp",[367,903,904],{},"Email / OTP verification.",[352,906,907,915],{},[367,908,909,895,912],{},[279,910,911],{},"POST /auth/forgot-password",[279,913,914],{},"/auth/reset-password",[367,916,917],{},"Password recovery.",[352,919,920,931],{},[367,921,922,895,925,895,928],{},[279,923,924],{},"POST /2fa/enable",[279,926,927],{},"/2fa/verify",[279,929,930],{},"/2fa/disable",[367,932,933],{},"Email-PIN two-factor.",[265,935,936,937,940,941,944,945,948,949,887],{},"The lookup and list endpoints are ",[269,938,939],{},"off by default"," and require the ",[279,942,943],{},"users.read"," permission — enable them with the flags above and grant the permission (e.g. ",[279,946,947],{},"php glueful aegis:bootstrap-admin --user=\u003Cuuid>"," if you use ",[279,950,423],{},[341,952,954],{"id":953},"writing-your-own-user-store","Writing your own user store",[265,956,957,958,960,961,481],{},"Implement ",[279,959,338],{}," — three methods, authentication only (registration/profile writes belong to the store) — and alias it to the contract so it replaces ",[279,962,308],{},[439,964,966],{"className":484,"code":965,"language":486,"meta":447,"style":447},"interface UserProviderInterface\n{\n    public function findByUuid(string $uuid): ?UserIdentity;\n    public function findByLogin(string $identifier): ?UserIdentity;            // email/username/etc.\n    public function verifyCredentials(string $identifier, string $password): ?UserIdentity;\n}\n",[279,967,968,978,983,1020,1051,1088],{"__ignoreMap":447},[490,969,970,974],{"class":492,"line":493},[490,971,973],{"class":972},"srJo8","interface",[490,975,977],{"class":976},"sKvfc"," UserProviderInterface\n",[490,979,980],{"class":492,"line":525},[490,981,982],{"class":496},"{\n",[490,984,985,989,992,995,997,1001,1004,1006,1009,1011,1014,1017],{"class":492,"line":532},[490,986,988],{"class":987},"sTNss","    public",[490,990,991],{"class":972}," function",[490,993,994],{"class":542}," findByUuid",[490,996,632],{"class":496},[490,998,1000],{"class":999},"shWJe","string",[490,1002,1003],{"class":496}," $",[490,1005,543],{"class":500},[490,1007,1008],{"class":496},")",[490,1010,481],{"class":504},[490,1012,1013],{"class":504}," ?",[490,1015,334],{"class":1016},"s_MOj",[490,1018,1019],{"class":496},";\n",[490,1021,1022,1024,1026,1029,1031,1033,1035,1038,1040,1042,1044,1046,1048],{"class":492,"line":552},[490,1023,988],{"class":987},[490,1025,991],{"class":972},[490,1027,1028],{"class":542}," findByLogin",[490,1030,632],{"class":496},[490,1032,1000],{"class":999},[490,1034,1003],{"class":496},[490,1036,1037],{"class":500},"identifier",[490,1039,1008],{"class":496},[490,1041,481],{"class":504},[490,1043,1013],{"class":504},[490,1045,334],{"class":1016},[490,1047,518],{"class":496},[490,1049,1050],{"class":521},"            // email/username/etc.\n",[490,1052,1053,1055,1057,1060,1062,1064,1066,1068,1070,1073,1075,1078,1080,1082,1084,1086],{"class":492,"line":569},[490,1054,988],{"class":987},[490,1056,991],{"class":972},[490,1058,1059],{"class":542}," verifyCredentials",[490,1061,632],{"class":496},[490,1063,1000],{"class":999},[490,1065,1003],{"class":496},[490,1067,1037],{"class":500},[490,1069,645],{"class":496},[490,1071,1072],{"class":999}," string",[490,1074,1003],{"class":496},[490,1076,1077],{"class":500},"password",[490,1079,1008],{"class":496},[490,1081,481],{"class":504},[490,1083,1013],{"class":504},[490,1085,334],{"class":1016},[490,1087,1019],{"class":496},[490,1089,1090],{"class":492,"line":586},[490,1091,1092],{"class":496},"}\n",[439,1094,1096],{"className":484,"code":1095,"language":486,"meta":447,"style":447},"// in your extension's services()\nuse Glueful\\Auth\\Contracts\\UserProviderInterface;\n\nMyUserProvider::class => [\n    'class'     => MyUserProvider::class,\n    'shared'    => true,\n    'arguments' => ['@' . MyDirectoryClient::class],\n    'alias'     => [UserProviderInterface::class], // rebinds the contract away from NullUserProvider\n],\n",[279,1097,1098,1103,1131,1135,1150,1170,1188,1221,1246],{"__ignoreMap":447},[490,1099,1100],{"class":492,"line":493},[490,1101,1102],{"class":521},"// in your extension's services()\n",[490,1104,1105,1108,1112,1116,1119,1121,1124,1126,1129],{"class":492,"line":525},[490,1106,1107],{"class":999},"use",[490,1109,1111],{"class":1110},"s91G_"," Glueful",[490,1113,1115],{"class":1114},"sv8o3","\\",[490,1117,1118],{"class":1110},"Auth",[490,1120,1115],{"class":1114},[490,1122,1123],{"class":1110},"Contracts",[490,1125,1115],{"class":1114},[490,1127,338],{"class":1128},"seZir",[490,1130,1019],{"class":496},[490,1132,1133],{"class":492,"line":532},[490,1134,529],{"emptyLinePlaceholder":528},[490,1136,1137,1140,1143,1146,1148],{"class":492,"line":552},[490,1138,1139],{"class":1016},"MyUserProvider",[490,1141,1142],{"class":504},"::",[490,1144,1145],{"class":999},"class",[490,1147,763],{"class":504},[490,1149,766],{"class":496},[490,1151,1152,1154,1156,1158,1161,1164,1166,1168],{"class":492,"line":569},[490,1153,771],{"class":635},[490,1155,1145],{"class":639},[490,1157,636],{"class":635},[490,1159,1160],{"class":504},"     =>",[490,1162,1163],{"class":1016}," MyUserProvider",[490,1165,1142],{"class":504},[490,1167,1145],{"class":999},[490,1169,795],{"class":496},[490,1171,1172,1174,1177,1179,1182,1186],{"class":492,"line":586},[490,1173,771],{"class":635},[490,1175,1176],{"class":639},"shared",[490,1178,636],{"class":635},[490,1180,1181],{"class":504},"    =>",[490,1183,1185],{"class":1184},"sMTiH"," true",[490,1187,795],{"class":496},[490,1189,1190,1192,1195,1197,1199,1202,1204,1207,1209,1212,1215,1217,1219],{"class":492,"line":603},[490,1191,771],{"class":635},[490,1193,1194],{"class":639},"arguments",[490,1196,636],{"class":635},[490,1198,763],{"class":504},[490,1200,1201],{"class":496}," [",[490,1203,636],{"class":635},[490,1205,1206],{"class":639},"@",[490,1208,636],{"class":635},[490,1210,1211],{"class":504}," .",[490,1213,1214],{"class":1016}," MyDirectoryClient",[490,1216,1142],{"class":504},[490,1218,1145],{"class":999},[490,1220,800],{"class":496},[490,1222,1223,1225,1228,1230,1232,1234,1236,1238,1240,1243],{"class":492,"line":620},[490,1224,771],{"class":635},[490,1226,1227],{"class":639},"alias",[490,1229,636],{"class":635},[490,1231,1160],{"class":504},[490,1233,1201],{"class":496},[490,1235,338],{"class":1016},[490,1237,1142],{"class":504},[490,1239,1145],{"class":999},[490,1241,1242],{"class":496},"],",[490,1244,1245],{"class":521}," // rebinds the contract away from NullUserProvider\n",[490,1247,1248],{"class":492,"line":654},[490,1249,800],{"class":496},[265,1251,1252,1253,1255,1256,1258,1259,1261],{},"Return ",[279,1254,334],{}," instances from lookups; for ",[279,1257,453],{}," return the identity on a correct password and ",[279,1260,316],{}," otherwise. Treat the uuid as an opaque principal id.",[341,1263,1265],{"id":1264},"adding-claims-roles-permissions","Adding claims (roles, permissions, …)",[265,1267,1268,1269,1272,1273,1276,1277,1280],{},"A claims provider enriches every authenticated identity after login. Implement ",[279,1270,1271],{},"IdentityClaimsProviderInterface"," and tag it ",[279,1274,1275],{},"identity.claims_provider"," — the ",[279,1278,1279],{},"IdentityResolver"," collects and invokes all of them, additively:",[439,1282,1284],{"className":484,"code":1283,"language":486,"meta":447,"style":447},"use Glueful\\Auth\\Contracts\\IdentityClaimsProviderInterface;\nuse Glueful\\Auth\\UserIdentity;\n\nfinal class MyRoleClaims implements IdentityClaimsProviderInterface\n{\n    public function enrich(UserIdentity $identity): UserIdentity\n    {\n        $roles = $this->roleStore->rolesFor($identity->uuid());   // list\u003Cstring>\n        return $roles === []\n            ? $identity                                           // never fabricate membership\n            : $identity->withClaims([\n                'roles' => array_values(array_unique([...$identity->roles(), ...$roles])),\n            ]);\n    }\n}\n",[279,1285,1286,1306,1322,1326,1343,1347,1372,1377,1414,1430,1443,1461,1509,1515,1521],{"__ignoreMap":447},[490,1287,1288,1290,1292,1294,1296,1298,1300,1302,1304],{"class":492,"line":493},[490,1289,1107],{"class":999},[490,1291,1111],{"class":1110},[490,1293,1115],{"class":1114},[490,1295,1118],{"class":1110},[490,1297,1115],{"class":1114},[490,1299,1123],{"class":1110},[490,1301,1115],{"class":1114},[490,1303,1271],{"class":1128},[490,1305,1019],{"class":496},[490,1307,1308,1310,1312,1314,1316,1318,1320],{"class":492,"line":525},[490,1309,1107],{"class":999},[490,1311,1111],{"class":1110},[490,1313,1115],{"class":1114},[490,1315,1118],{"class":1110},[490,1317,1115],{"class":1114},[490,1319,334],{"class":1128},[490,1321,1019],{"class":496},[490,1323,1324],{"class":492,"line":532},[490,1325,529],{"emptyLinePlaceholder":528},[490,1327,1328,1330,1333,1336,1339],{"class":492,"line":552},[490,1329,377],{"class":987},[490,1331,1332],{"class":972}," class",[490,1334,1335],{"class":976}," MyRoleClaims",[490,1337,1338],{"class":987}," implements",[490,1340,1342],{"class":1341},"sW3Pz"," IdentityClaimsProviderInterface\n",[490,1344,1345],{"class":492,"line":569},[490,1346,982],{"class":496},[490,1348,1349,1351,1353,1356,1358,1360,1362,1365,1367,1369],{"class":492,"line":586},[490,1350,988],{"class":987},[490,1352,991],{"class":972},[490,1354,1355],{"class":542}," enrich",[490,1357,632],{"class":496},[490,1359,334],{"class":1016},[490,1361,1003],{"class":496},[490,1363,1364],{"class":500},"identity",[490,1366,1008],{"class":496},[490,1368,481],{"class":504},[490,1370,1371],{"class":1016}," UserIdentity\n",[490,1373,1374],{"class":492,"line":603},[490,1375,1376],{"class":496},"    {\n",[490,1378,1379,1382,1385,1387,1389,1391,1394,1396,1399,1402,1404,1406,1408,1411],{"class":492,"line":620},[490,1380,1381],{"class":496},"        $",[490,1383,1384],{"class":500},"roles ",[490,1386,505],{"class":504},[490,1388,509],{"class":508},[490,1390,512],{"class":504},[490,1392,1393],{"class":500},"roleStore",[490,1395,512],{"class":504},[490,1397,1398],{"class":542},"rolesFor",[490,1400,1401],{"class":496},"($",[490,1403,1364],{"class":500},[490,1405,512],{"class":504},[490,1407,543],{"class":542},[490,1409,1410],{"class":496},"());",[490,1412,1413],{"class":521},"   // list\u003Cstring>\n",[490,1415,1416,1420,1422,1424,1427],{"class":492,"line":654},[490,1417,1419],{"class":1418},"sRxSC","        return",[490,1421,1003],{"class":496},[490,1423,1384],{"class":500},[490,1425,1426],{"class":504},"===",[490,1428,1429],{"class":496}," []\n",[490,1431,1432,1435,1437,1440],{"class":492,"line":681},[490,1433,1434],{"class":504},"            ?",[490,1436,1003],{"class":496},[490,1438,1439],{"class":500},"identity                                           ",[490,1441,1442],{"class":521},"// never fabricate membership\n",[490,1444,1446,1449,1451,1453,1455,1458],{"class":492,"line":1445},11,[490,1447,1448],{"class":504},"            :",[490,1450,1003],{"class":496},[490,1452,1364],{"class":500},[490,1454,512],{"class":504},[490,1456,1457],{"class":542},"withClaims",[490,1459,1460],{"class":496},"([\n",[490,1462,1464,1467,1469,1471,1473,1477,1479,1482,1485,1488,1490,1492,1494,1496,1499,1502,1504,1506],{"class":492,"line":1463},12,[490,1465,1466],{"class":635},"                '",[490,1468,595],{"class":639},[490,1470,636],{"class":635},[490,1472,763],{"class":504},[490,1474,1476],{"class":1475},"sMLJd"," array_values",[490,1478,632],{"class":496},[490,1480,1481],{"class":1475},"array_unique",[490,1483,1484],{"class":496},"([",[490,1486,1487],{"class":504},"...",[490,1489,497],{"class":496},[490,1491,1364],{"class":500},[490,1493,512],{"class":504},[490,1495,595],{"class":542},[490,1497,1498],{"class":496},"(),",[490,1500,1501],{"class":504}," ...",[490,1503,497],{"class":496},[490,1505,595],{"class":500},[490,1507,1508],{"class":496},"])),\n",[490,1510,1512],{"class":492,"line":1511},13,[490,1513,1514],{"class":496},"            ]);\n",[490,1516,1518],{"class":492,"line":1517},14,[490,1519,1520],{"class":496},"    }\n",[490,1522,1524],{"class":492,"line":1523},15,[490,1525,1092],{"class":496},[439,1527,1529],{"className":484,"code":1528,"language":486,"meta":447,"style":447},"// services()\nMyRoleClaims::class => [\n    'class'     => MyRoleClaims::class,\n    'arguments' => ['@' . MyRoleStore::class],\n    'shared'    => true,\n    'tags'      => ['identity.claims_provider'],\n],\n",[279,1530,1531,1536,1549,1567,1596,1610,1632],{"__ignoreMap":447},[490,1532,1533],{"class":492,"line":493},[490,1534,1535],{"class":521},"// services()\n",[490,1537,1538,1541,1543,1545,1547],{"class":492,"line":525},[490,1539,1540],{"class":1016},"MyRoleClaims",[490,1542,1142],{"class":504},[490,1544,1145],{"class":999},[490,1546,763],{"class":504},[490,1548,766],{"class":496},[490,1550,1551,1553,1555,1557,1559,1561,1563,1565],{"class":492,"line":532},[490,1552,771],{"class":635},[490,1554,1145],{"class":639},[490,1556,636],{"class":635},[490,1558,1160],{"class":504},[490,1560,1335],{"class":1016},[490,1562,1142],{"class":504},[490,1564,1145],{"class":999},[490,1566,795],{"class":496},[490,1568,1569,1571,1573,1575,1577,1579,1581,1583,1585,1587,1590,1592,1594],{"class":492,"line":552},[490,1570,771],{"class":635},[490,1572,1194],{"class":639},[490,1574,636],{"class":635},[490,1576,763],{"class":504},[490,1578,1201],{"class":496},[490,1580,636],{"class":635},[490,1582,1206],{"class":639},[490,1584,636],{"class":635},[490,1586,1211],{"class":504},[490,1588,1589],{"class":1016}," MyRoleStore",[490,1591,1142],{"class":504},[490,1593,1145],{"class":999},[490,1595,800],{"class":496},[490,1597,1598,1600,1602,1604,1606,1608],{"class":492,"line":569},[490,1599,771],{"class":635},[490,1601,1176],{"class":639},[490,1603,636],{"class":635},[490,1605,1181],{"class":504},[490,1607,1185],{"class":1184},[490,1609,795],{"class":496},[490,1611,1612,1614,1617,1619,1622,1624,1626,1628,1630],{"class":492,"line":586},[490,1613,771],{"class":635},[490,1615,1616],{"class":639},"tags",[490,1618,636],{"class":635},[490,1620,1621],{"class":504},"      =>",[490,1623,1201],{"class":496},[490,1625,636],{"class":635},[490,1627,1275],{"class":639},[490,1629,636],{"class":635},[490,1631,800],{"class":496},[490,1633,1634],{"class":492,"line":603},[490,1635,800],{"class":496},[265,1637,1638,1639,1641,1642,1645],{},"This is exactly how ",[279,1640,423],{}," adds RBAC role claims. Enrichment is ",[269,1643,1644],{},"additive only"," — a claims provider can grant capabilities, never change who the user is.",[341,1647,1649],{"id":1648},"account-status-gate","Account-status gate",[265,1651,1652,1653,1655,1656,1658],{},"After credentials verify, ",[279,1654,1279],{}," rejects any user whose ",[279,1657,578],{}," isn't permitted to log in:",[439,1660,1662],{"className":484,"code":1661,"language":486,"meta":447,"style":447},"// config/security.php\n'auth' => [\n    'allowed_login_statuses' => ['active'],   // others are rejected at login\n],\n",[279,1663,1664,1669,1682,1707],{"__ignoreMap":447},[490,1665,1666],{"class":492,"line":493},[490,1667,1668],{"class":521},"// config/security.php\n",[490,1670,1671,1673,1676,1678,1680],{"class":492,"line":525},[490,1672,636],{"class":635},[490,1674,1675],{"class":639},"auth",[490,1677,636],{"class":635},[490,1679,763],{"class":504},[490,1681,766],{"class":496},[490,1683,1684,1686,1689,1691,1693,1695,1697,1700,1702,1704],{"class":492,"line":532},[490,1685,771],{"class":635},[490,1687,1688],{"class":639},"allowed_login_statuses",[490,1690,636],{"class":635},[490,1692,763],{"class":504},[490,1694,1201],{"class":496},[490,1696,636],{"class":635},[490,1698,1699],{"class":639},"active",[490,1701,636],{"class":635},[490,1703,1242],{"class":496},[490,1705,1706],{"class":521},"   // others are rejected at login\n",[490,1708,1709],{"class":492,"line":552},[490,1710,800],{"class":496},[341,1712,1714],{"id":1713},"optional-two-factor","Optional: two-factor",[265,1716,1717,1718,1721,1722,1725,1726,1729,1730,1732],{},"If an extension registers a ",[279,1719,1720],{},"TwoFactorServiceInterface"," implementation, the login flow routes through it (",[279,1723,1724],{},"isEnabled()"," / ",[279,1727,1728],{},"beginLogin()","); with none registered, 2FA is skipped entirely. ",[279,1731,295],{}," provides one.",[341,1734,1736],{"id":1735},"next-steps","Next steps",[1738,1739,1740,1746],"ul",{},[1741,1742,1743,1745],"li",{},[288,1744,29],{"href":30}," — the login, token, and refresh flows that sit on top of identity.",[1741,1747,1748,1276,1750,1752,1753,378],{},[288,1749,244],{"href":245},[279,1751,295],{}," store and RBAC via ",[279,1754,423],{},[1756,1757,1758],"style",{},"html pre.shiki code .swvn1, html code.shiki .swvn1{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2}html pre.shiki code .ss--_, html code.shiki .ss--_{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2}html pre.shiki code .sGXK2, html code.shiki .sGXK2{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#F92672}html pre.shiki code .sSBr1, html code.shiki .sSBr1{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#FD971F}html pre.shiki code .ss7Ak, html code.shiki .ss7Ak{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit;--shiki-sepia:#88846F;--shiki-sepia-font-style:inherit}html pre.shiki code .sD0ED, html code.shiki .sD0ED{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#A6E22E}html pre.shiki code .siCPE, html code.shiki .siCPE{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#E6DB74}html pre.shiki code .sLACW, html code.shiki .sLACW{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#E6DB74}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sR7ES, html code.shiki .sR7ES{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#A6E22E}html pre.shiki code .sQeA1, html code.shiki .sQeA1{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#AE81FF}html pre.shiki code .srJo8, html code.shiki .srJo8{--shiki-light:#9C3EDA;--shiki-light-font-style:inherit;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit;--shiki-sepia:#66D9EF;--shiki-sepia-font-style:italic}html pre.shiki code .sKvfc, html code.shiki .sKvfc{--shiki-light:#E2931D;--shiki-light-text-decoration:inherit;--shiki-default:#6F42C1;--shiki-default-text-decoration:inherit;--shiki-dark:#B392F0;--shiki-dark-text-decoration:inherit;--shiki-sepia:#A6E22E;--shiki-sepia-text-decoration:underline}html pre.shiki code .sTNss, html code.shiki .sTNss{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#F92672}html pre.shiki code .shWJe, html code.shiki .shWJe{--shiki-light:#F76D47;--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#F92672}html pre.shiki code .s_MOj, html code.shiki .s_MOj{--shiki-light:#E2931D;--shiki-light-font-style:inherit;--shiki-default:#005CC5;--shiki-default-font-style:inherit;--shiki-dark:#79B8FF;--shiki-dark-font-style:inherit;--shiki-sepia:#66D9EF;--shiki-sepia-font-style:italic}html pre.shiki code .s91G_, html code.shiki .s91G_{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#F8F8F2}html pre.shiki code .sv8o3, html code.shiki .sv8o3{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#F8F8F2}html pre.shiki code .seZir, html code.shiki .seZir{--shiki-light:#90A4AE;--shiki-light-font-style:inherit;--shiki-default:#005CC5;--shiki-default-font-style:inherit;--shiki-dark:#79B8FF;--shiki-dark-font-style:inherit;--shiki-sepia:#66D9EF;--shiki-sepia-font-style:italic}html pre.shiki code .sMTiH, html code.shiki .sMTiH{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#AE81FF}html pre.shiki code .sW3Pz, html code.shiki .sW3Pz{--shiki-light:#E2931D;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;--shiki-default:#6F42C1;--shiki-default-font-style:inherit;--shiki-default-text-decoration:inherit;--shiki-dark:#B392F0;--shiki-dark-font-style:inherit;--shiki-dark-text-decoration:inherit;--shiki-sepia:#A6E22E;--shiki-sepia-font-style:italic;--shiki-sepia-text-decoration:underline}html pre.shiki code .sRxSC, html code.shiki .sRxSC{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit;--shiki-sepia:#F92672;--shiki-sepia-font-style:inherit}html pre.shiki code .sMLJd, html code.shiki .sMLJd{--shiki-light:#6182B8;--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#66D9EF}",{"title":447,"searchDepth":493,"depth":525,"links":1760},[1761,1762,1763,1764,1767,1768,1769,1770,1771],{"id":343,"depth":525,"text":344},{"id":436,"depth":525,"text":437},{"id":462,"depth":525,"text":334},{"id":712,"depth":525,"text":713,"children":1765},[1766],{"id":819,"depth":532,"text":820},{"id":953,"depth":525,"text":954},{"id":1264,"depth":525,"text":1265},{"id":1648,"depth":525,"text":1649},{"id":1713,"depth":525,"text":1714},{"id":1735,"depth":525,"text":1736},"How Glueful models who your users are — the provider-agnostic identity contract, the pluggable user store, and claims.","md",null,{},{"title":33,"description":1772},"WbSlv_0LRzEwfvzeFVUVORMdnxKSnI2vl1W-UtMf8Mo",[1779,1781],{"title":29,"path":30,"stem":31,"description":1780,"children":-1},"Built-in authentication endpoints, token flows, and how to extend them",{"title":37,"path":38,"stem":39,"description":1782,"children":-1},"Read request data and return consistent JSON responses",1780958514114]