[{"data":1,"prerenderedAt":1963},["ShallowReactive",2],{"navigation_docs":3,"-auth-auth-rules":172,"-auth-auth-rules-surround":1958},[4,26,67,98,124,140,161],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":25},"Getting Started","i-lucide-rocket","\u002Fgetting-started","1.getting-started",[10,15,20],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fgetting-started\u002Fintroduction","1.getting-started\u002F1.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"Installation","\u002Fgetting-started\u002Finstallation","1.getting-started\u002F2.installation","i-lucide-download",{"title":21,"path":22,"stem":23,"icon":24},"Quick Start","\u002Fgetting-started\u002Fquick-start","1.getting-started\u002F3.quick-start","i-lucide-play",false,{"title":27,"icon":28,"path":29,"stem":30,"children":31,"page":25},"Core","i-lucide-box","\u002Fcore","2.core",[32,37,42,47,52,57,62],{"title":33,"path":34,"stem":35,"icon":36},"Routing","\u002Fcore\u002Frouting","2.core\u002F1.routing","i-lucide-route",{"title":38,"path":39,"stem":40,"icon":41},"Handlers","\u002Fcore\u002Fhandlers","2.core\u002F2.handlers","i-lucide-braces",{"title":43,"path":44,"stem":45,"icon":46},"Request Binding","\u002Fcore\u002Frequest-binding","2.core\u002F3.request-binding","i-lucide-tags",{"title":48,"path":49,"stem":50,"icon":51},"Validation","\u002Fcore\u002Fvalidation","2.core\u002F4.validation","i-lucide-check-check",{"title":53,"path":54,"stem":55,"icon":56},"Context","\u002Fcore\u002Fcontext","2.core\u002F5.context","i-lucide-file-input",{"title":58,"path":59,"stem":60,"icon":61},"Errors","\u002Fcore\u002Ferrors","2.core\u002F6.errors","i-lucide-triangle-alert",{"title":63,"path":64,"stem":65,"icon":66},"Middleware","\u002Fcore\u002Fmiddleware","2.core\u002F7.middleware","i-lucide-layers",{"title":68,"icon":69,"path":70,"stem":71,"children":72,"page":25},"Auth","i-lucide-shield-check","\u002Fauth","3.auth",[73,78,83,88,93],{"title":74,"path":75,"stem":76,"icon":77},"Overview","\u002Fauth\u002Foverview","3.auth\u002F1.overview","i-lucide-shield",{"title":79,"path":80,"stem":81,"icon":82},"Auth Rules","\u002Fauth\u002Fauth-rules","3.auth\u002F2.auth-rules","i-lucide-lock",{"title":84,"path":85,"stem":86,"icon":87},"JWT Providers","\u002Fauth\u002Fjwt-providers","3.auth\u002F3.jwt-providers","i-lucide-key",{"title":89,"path":90,"stem":91,"icon":92},"Roles & Permissions","\u002Fauth\u002Froles-and-permissions","3.auth\u002F4.roles-and-permissions","i-lucide-users",{"title":94,"path":95,"stem":96,"icon":97},"Audit Logging","\u002Fauth\u002Faudit-logging","3.auth\u002F5.audit-logging","i-lucide-scroll-text",{"title":99,"icon":100,"path":101,"stem":102,"children":103,"page":25},"GoTrue","i-lucide-key-round","\u002Fgotrue","4.gotrue",[104,109,114,119],{"title":105,"path":106,"stem":107,"icon":108},"Proxy","\u002Fgotrue\u002Fproxy","4.gotrue\u002F1.proxy","i-lucide-shuffle",{"title":110,"path":111,"stem":112,"icon":113},"Endpoints","\u002Fgotrue\u002Fendpoints","4.gotrue\u002F2.endpoints","i-lucide-list",{"title":115,"path":116,"stem":117,"icon":118},"Client IP & Rate Limits","\u002Fgotrue\u002Fclient-ip-and-rate-limits","4.gotrue\u002F3.client-ip-and-rate-limits","i-lucide-network",{"title":120,"path":121,"stem":122,"icon":123},"Deployment","\u002Fgotrue\u002Fdeployment","4.gotrue\u002F4.deployment","i-lucide-server",{"title":125,"icon":126,"path":127,"stem":128,"children":129,"page":25},"API Docs","i-lucide-file-text","\u002Fapi-docs","5.api-docs",[130,135],{"title":131,"path":132,"stem":133,"icon":134},"OpenAPI","\u002Fapi-docs\u002Fopenapi","5.api-docs\u002F1.openapi","i-lucide-file-json",{"title":136,"path":137,"stem":138,"icon":139},"Scalar UI","\u002Fapi-docs\u002Fscalar","5.api-docs\u002F2.scalar","i-lucide-book-open-text",{"title":141,"icon":142,"path":143,"stem":144,"children":145,"page":25},"Guides","i-lucide-compass","\u002Fguides","6.guides",[146,151,156],{"title":147,"path":148,"stem":149,"icon":150},"Testing","\u002Fguides\u002Ftesting","6.guides\u002F1.testing","i-lucide-flask-conical",{"title":152,"path":153,"stem":154,"icon":155},"Performance","\u002Fguides\u002Fperformance","6.guides\u002F2.performance","i-lucide-gauge",{"title":157,"path":158,"stem":159,"icon":160},"Releasing","\u002Fguides\u002Freleasing","6.guides\u002F3.releasing","i-lucide-tag",{"title":162,"icon":163,"path":164,"stem":165,"children":166,"page":25},"Reference","i-lucide-code","\u002Freference","7.reference",[167],{"title":168,"path":169,"stem":170,"icon":171},"API Reference","\u002Freference\u002Fapi-reference","7.reference\u002F1.api-reference","i-lucide-list-tree",{"id":173,"title":79,"body":174,"description":1951,"extension":1952,"links":1953,"meta":1954,"navigation":1955,"path":80,"seo":1956,"stem":81,"__hash__":1957},"docs\u002F3.auth\u002F2.auth-rules.md",{"type":175,"value":176,"toc":1942},"minimark",[177,186,256,261,362,650,654,657,732,743,746,779,783,786,983,986,1077,1081,1085,1091,1270,1273,1591,1603,1607,1611,1853,1857,1920,1924,1938],[178,179,180,181,185],"p",{},"A rule is a small interface. The app calls ",[182,183,184],"code",{},"Check"," with the user your provider returned; nil means allow.",[187,188,193],"pre",{"className":189,"code":190,"language":191,"meta":192,"style":192},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","type AuthRule interface {\n    Check(user any) error\n    IsPublic() bool\n}\n","go","",[182,194,195,214,238,250],{"__ignoreMap":192},[196,197,200,204,208,211],"span",{"class":198,"line":199},"line",1,[196,201,203],{"class":202},"sMK4o","type",[196,205,207],{"class":206},"sBMFI"," AuthRule",[196,209,210],{"class":202}," interface",[196,212,213],{"class":202}," {\n",[196,215,217,221,224,228,231,234],{"class":198,"line":216},2,[196,218,220],{"class":219},"s2Zo4","    Check",[196,222,223],{"class":202},"(",[196,225,227],{"class":226},"sHdIc","user",[196,229,230],{"class":206}," any",[196,232,233],{"class":202},")",[196,235,237],{"class":236},"spNyl"," error\n",[196,239,241,244,247],{"class":198,"line":240},3,[196,242,243],{"class":219},"    IsPublic",[196,245,246],{"class":202},"()",[196,248,249],{"class":236}," bool\n",[196,251,253],{"class":198,"line":252},4,[196,254,255],{"class":202},"}\n",[257,258,260],"h2",{"id":259},"the-built-in-rules","The built-in rules",[262,263,264,277],"table",{},[265,266,267],"thead",{},[268,269,270,274],"tr",{},[271,272,273],"th",{},"Rule",[271,275,276],{},"Requires",[278,279,280,295,305,318,328,338,350],"tbody",{},[268,281,282,288],{},[283,284,285],"td",{},[182,286,287],{},"ezz.Public()",[283,289,290,291,294],{},"Nothing. ",[182,292,293],{},"Authenticate"," is not even called",[268,296,297,302],{},[283,298,299],{},[182,300,301],{},"ezz.Authenticated()",[283,303,304],{},"A non-nil user, so any valid token",[268,306,307,312],{},[283,308,309],{},[182,310,311],{},"ezz.Role(\"admin\")",[283,313,314,317],{},[182,315,316],{},"HasRole(\"admin\")"," returns true",[268,319,320,325],{},[283,321,322],{},[182,323,324],{},"ezz.AnyRole(\"admin\", \"mod\")",[283,326,327],{},"At least one of the roles",[268,329,330,335],{},[283,331,332],{},[182,333,334],{},"ezz.AllRoles(\"billing\", \"admin\")",[283,336,337],{},"Every listed role",[268,339,340,345],{},[283,341,342],{},[182,343,344],{},"ezz.Permission(\"posts:write\")",[283,346,347,317],{},[182,348,349],{},"HasPermission(\"posts:write\")",[268,351,352,357],{},[283,353,354],{},[182,355,356],{},"ezz.Custom(fn)",[283,358,359,317],{},[182,360,361],{},"fn(user)",[187,363,365],{"className":189,"code":364,"language":191,"meta":192,"style":192},"app.GET(\"\u002Fhealth\", health).Public()\napp.GET(\"\u002Fme\", profile).Auth(ezz.Authenticated())\napp.GET(\"\u002Fadmin\", panel).Auth(ezz.Role(\"admin\"))\napp.GET(\"\u002Freports\", reports).Auth(ezz.AnyRole(\"admin\", \"analyst\"))\napp.POST(\"\u002Finvoices\", create).Auth(ezz.AllRoles(\"billing\", \"admin\"))\napp.DELETE(\"\u002Fposts\u002F{id}\", remove).Auth(ezz.Permission(\"posts:delete\"))\n",[182,366,367,405,444,491,546,602],{"__ignoreMap":192},[196,368,369,373,376,379,381,384,388,390,393,396,399,402],{"class":198,"line":199},[196,370,372],{"class":371},"sTEyZ","app",[196,374,375],{"class":202},".",[196,377,378],{"class":219},"GET",[196,380,223],{"class":202},[196,382,383],{"class":202},"\"",[196,385,387],{"class":386},"sfazB","\u002Fhealth",[196,389,383],{"class":202},[196,391,392],{"class":202},",",[196,394,395],{"class":371}," health",[196,397,398],{"class":202},").",[196,400,401],{"class":219},"Public",[196,403,404],{"class":202},"()\n",[196,406,407,409,411,413,415,417,420,422,424,427,429,431,433,436,438,441],{"class":198,"line":216},[196,408,372],{"class":371},[196,410,375],{"class":202},[196,412,378],{"class":219},[196,414,223],{"class":202},[196,416,383],{"class":202},[196,418,419],{"class":386},"\u002Fme",[196,421,383],{"class":202},[196,423,392],{"class":202},[196,425,426],{"class":371}," profile",[196,428,398],{"class":202},[196,430,68],{"class":219},[196,432,223],{"class":202},[196,434,435],{"class":371},"ezz",[196,437,375],{"class":202},[196,439,440],{"class":219},"Authenticated",[196,442,443],{"class":202},"())\n",[196,445,446,448,450,452,454,456,459,461,463,466,468,470,472,474,476,479,481,483,486,488],{"class":198,"line":240},[196,447,372],{"class":371},[196,449,375],{"class":202},[196,451,378],{"class":219},[196,453,223],{"class":202},[196,455,383],{"class":202},[196,457,458],{"class":386},"\u002Fadmin",[196,460,383],{"class":202},[196,462,392],{"class":202},[196,464,465],{"class":371}," panel",[196,467,398],{"class":202},[196,469,68],{"class":219},[196,471,223],{"class":202},[196,473,435],{"class":371},[196,475,375],{"class":202},[196,477,478],{"class":219},"Role",[196,480,223],{"class":202},[196,482,383],{"class":202},[196,484,485],{"class":386},"admin",[196,487,383],{"class":202},[196,489,490],{"class":202},"))\n",[196,492,493,495,497,499,501,503,506,508,510,513,515,517,519,521,523,526,528,530,532,534,536,539,542,544],{"class":198,"line":252},[196,494,372],{"class":371},[196,496,375],{"class":202},[196,498,378],{"class":219},[196,500,223],{"class":202},[196,502,383],{"class":202},[196,504,505],{"class":386},"\u002Freports",[196,507,383],{"class":202},[196,509,392],{"class":202},[196,511,512],{"class":371}," reports",[196,514,398],{"class":202},[196,516,68],{"class":219},[196,518,223],{"class":202},[196,520,435],{"class":371},[196,522,375],{"class":202},[196,524,525],{"class":219},"AnyRole",[196,527,223],{"class":202},[196,529,383],{"class":202},[196,531,485],{"class":386},[196,533,383],{"class":202},[196,535,392],{"class":202},[196,537,538],{"class":202}," \"",[196,540,541],{"class":386},"analyst",[196,543,383],{"class":202},[196,545,490],{"class":202},[196,547,549,551,553,556,558,560,563,565,567,570,572,574,576,578,580,583,585,587,590,592,594,596,598,600],{"class":198,"line":548},5,[196,550,372],{"class":371},[196,552,375],{"class":202},[196,554,555],{"class":219},"POST",[196,557,223],{"class":202},[196,559,383],{"class":202},[196,561,562],{"class":386},"\u002Finvoices",[196,564,383],{"class":202},[196,566,392],{"class":202},[196,568,569],{"class":371}," create",[196,571,398],{"class":202},[196,573,68],{"class":219},[196,575,223],{"class":202},[196,577,435],{"class":371},[196,579,375],{"class":202},[196,581,582],{"class":219},"AllRoles",[196,584,223],{"class":202},[196,586,383],{"class":202},[196,588,589],{"class":386},"billing",[196,591,383],{"class":202},[196,593,392],{"class":202},[196,595,538],{"class":202},[196,597,485],{"class":386},[196,599,383],{"class":202},[196,601,490],{"class":202},[196,603,605,607,609,612,614,616,619,621,623,626,628,630,632,634,636,639,641,643,646,648],{"class":198,"line":604},6,[196,606,372],{"class":371},[196,608,375],{"class":202},[196,610,611],{"class":219},"DELETE",[196,613,223],{"class":202},[196,615,383],{"class":202},[196,617,618],{"class":386},"\u002Fposts\u002F{id}",[196,620,383],{"class":202},[196,622,392],{"class":202},[196,624,625],{"class":371}," remove",[196,627,398],{"class":202},[196,629,68],{"class":219},[196,631,223],{"class":202},[196,633,435],{"class":371},[196,635,375],{"class":202},[196,637,638],{"class":219},"Permission",[196,640,223],{"class":202},[196,642,383],{"class":202},[196,644,645],{"class":386},"posts:delete",[196,647,383],{"class":202},[196,649,490],{"class":202},[257,651,653],{"id":652},"know-what-the-rules-check-against","Know what the rules check against",[178,655,656],{},"Role and permission rules use interface assertions on the user value:",[187,658,660],{"className":189,"code":659,"language":191,"meta":192,"style":192},"type RoleChecker interface {\n    HasRole(role string) bool\n}\n\ntype PermissionChecker interface {\n    HasPermission(permission string) bool\n}\n",[182,661,662,673,690,694,700,711,727],{"__ignoreMap":192},[196,663,664,666,669,671],{"class":198,"line":199},[196,665,203],{"class":202},[196,667,668],{"class":206}," RoleChecker",[196,670,210],{"class":202},[196,672,213],{"class":202},[196,674,675,678,680,683,686,688],{"class":198,"line":216},[196,676,677],{"class":219},"    HasRole",[196,679,223],{"class":202},[196,681,682],{"class":226},"role",[196,684,685],{"class":236}," string",[196,687,233],{"class":202},[196,689,249],{"class":236},[196,691,692],{"class":198,"line":240},[196,693,255],{"class":202},[196,695,696],{"class":198,"line":252},[196,697,699],{"emptyLinePlaceholder":698},true,"\n",[196,701,702,704,707,709],{"class":198,"line":548},[196,703,203],{"class":202},[196,705,706],{"class":206}," PermissionChecker",[196,708,210],{"class":202},[196,710,213],{"class":202},[196,712,713,716,718,721,723,725],{"class":198,"line":604},[196,714,715],{"class":219},"    HasPermission",[196,717,223],{"class":202},[196,719,720],{"class":226},"permission",[196,722,685],{"class":236},[196,724,233],{"class":202},[196,726,249],{"class":236},[196,728,730],{"class":198,"line":729},7,[196,731,255],{"class":202},[178,733,734,735,738,739,742],{},"If the user does not implement the interface the rule needs, the request is denied with ",[182,736,737],{},"403",". The ",[182,740,741],{},"*auth.AuthorizedUser"," returned by the JWT provider implements both, so this only bites when you write a custom provider that returns your own type. Add the two methods and every rule works.",[178,744,745],{},"Where the roles come from depends on your setup:",[747,748,749,761],"ul",{},[750,751,752,753,757,758,760],"li",{},"With an ",[754,755,756],"a",{"href":90},"authorization resolver",", roles and permissions come from your store, and the token's ",[182,759,682],{}," claim is ignored.",[750,762,763,764,767,768,770,771,774,775,778],{},"Without one, ",[182,765,766],{},"HasRole"," reads the ",[182,769,682],{}," (string) and ",[182,772,773],{},"roles"," (array) claims from the token, and ",[182,776,777],{},"HasPermission"," always returns false, since a token carries no permission set.",[257,780,782],{"id":781},"set-rules-at-three-levels","Set rules at three levels",[178,784,785],{},"Precedence runs from broad to narrow.",[187,787,789],{"className":189,"code":788,"language":191,"meta":192,"style":192},"app := ezz.New().Auth(provider)              \u002F\u002F 1. app default: Authenticated\n\napi := app.Group(\"\u002Fapi\").Auth(ezz.Role(\"member\"))   \u002F\u002F 2. group rule\napi.GET(\"\u002Ffeed\", feed)                               \u002F\u002F member\napi.GET(\"\u002Fadmin\", adminPanel).Auth(ezz.Role(\"admin\")) \u002F\u002F 3. route rule wins\napi.GET(\"\u002Fstatus\", status).Public()                   \u002F\u002F route rule wins\n",[182,790,791,823,827,878,906,952],{"__ignoreMap":192},[196,792,793,796,799,802,804,807,810,812,814,817,819],{"class":198,"line":199},[196,794,795],{"class":371},"app ",[196,797,798],{"class":202},":=",[196,800,801],{"class":371}," ezz",[196,803,375],{"class":202},[196,805,806],{"class":219},"New",[196,808,809],{"class":202},"().",[196,811,68],{"class":219},[196,813,223],{"class":202},[196,815,816],{"class":371},"provider",[196,818,233],{"class":202},[196,820,822],{"class":821},"sHwdD","              \u002F\u002F 1. app default: Authenticated\n",[196,824,825],{"class":198,"line":216},[196,826,699],{"emptyLinePlaceholder":698},[196,828,829,832,834,837,839,842,844,846,849,851,853,855,857,859,861,863,865,867,870,872,875],{"class":198,"line":240},[196,830,831],{"class":371},"api ",[196,833,798],{"class":202},[196,835,836],{"class":371}," app",[196,838,375],{"class":202},[196,840,841],{"class":219},"Group",[196,843,223],{"class":202},[196,845,383],{"class":202},[196,847,848],{"class":386},"\u002Fapi",[196,850,383],{"class":202},[196,852,398],{"class":202},[196,854,68],{"class":219},[196,856,223],{"class":202},[196,858,435],{"class":371},[196,860,375],{"class":202},[196,862,478],{"class":219},[196,864,223],{"class":202},[196,866,383],{"class":202},[196,868,869],{"class":386},"member",[196,871,383],{"class":202},[196,873,874],{"class":202},"))",[196,876,877],{"class":821},"   \u002F\u002F 2. group rule\n",[196,879,880,883,885,887,889,891,894,896,898,901,903],{"class":198,"line":252},[196,881,882],{"class":371},"api",[196,884,375],{"class":202},[196,886,378],{"class":219},[196,888,223],{"class":202},[196,890,383],{"class":202},[196,892,893],{"class":386},"\u002Ffeed",[196,895,383],{"class":202},[196,897,392],{"class":202},[196,899,900],{"class":371}," feed",[196,902,233],{"class":202},[196,904,905],{"class":821},"                               \u002F\u002F member\n",[196,907,908,910,912,914,916,918,920,922,924,927,929,931,933,935,937,939,941,943,945,947,949],{"class":198,"line":548},[196,909,882],{"class":371},[196,911,375],{"class":202},[196,913,378],{"class":219},[196,915,223],{"class":202},[196,917,383],{"class":202},[196,919,458],{"class":386},[196,921,383],{"class":202},[196,923,392],{"class":202},[196,925,926],{"class":371}," adminPanel",[196,928,398],{"class":202},[196,930,68],{"class":219},[196,932,223],{"class":202},[196,934,435],{"class":371},[196,936,375],{"class":202},[196,938,478],{"class":219},[196,940,223],{"class":202},[196,942,383],{"class":202},[196,944,485],{"class":386},[196,946,383],{"class":202},[196,948,874],{"class":202},[196,950,951],{"class":821}," \u002F\u002F 3. route rule wins\n",[196,953,954,956,958,960,962,964,967,969,971,974,976,978,980],{"class":198,"line":604},[196,955,882],{"class":371},[196,957,375],{"class":202},[196,959,378],{"class":219},[196,961,223],{"class":202},[196,963,383],{"class":202},[196,965,966],{"class":386},"\u002Fstatus",[196,968,383],{"class":202},[196,970,392],{"class":202},[196,972,973],{"class":371}," status",[196,975,398],{"class":202},[196,977,401],{"class":219},[196,979,246],{"class":202},[196,981,982],{"class":821},"                   \u002F\u002F route rule wins\n",[178,984,985],{},"A nested group inherits the parent's rule at the moment it is created:",[187,987,989],{"className":189,"code":988,"language":191,"meta":192,"style":192},"api := app.Group(\"\u002Fapi\").Auth(ezz.Role(\"member\"))\nadmin := api.Group(\"\u002Fadmin\").Auth(ezz.Role(\"admin\"))\n",[182,990,991,1033],{"__ignoreMap":192},[196,992,993,995,997,999,1001,1003,1005,1007,1009,1011,1013,1015,1017,1019,1021,1023,1025,1027,1029,1031],{"class":198,"line":199},[196,994,831],{"class":371},[196,996,798],{"class":202},[196,998,836],{"class":371},[196,1000,375],{"class":202},[196,1002,841],{"class":219},[196,1004,223],{"class":202},[196,1006,383],{"class":202},[196,1008,848],{"class":386},[196,1010,383],{"class":202},[196,1012,398],{"class":202},[196,1014,68],{"class":219},[196,1016,223],{"class":202},[196,1018,435],{"class":371},[196,1020,375],{"class":202},[196,1022,478],{"class":219},[196,1024,223],{"class":202},[196,1026,383],{"class":202},[196,1028,869],{"class":386},[196,1030,383],{"class":202},[196,1032,490],{"class":202},[196,1034,1035,1038,1040,1043,1045,1047,1049,1051,1053,1055,1057,1059,1061,1063,1065,1067,1069,1071,1073,1075],{"class":198,"line":216},[196,1036,1037],{"class":371},"admin ",[196,1039,798],{"class":202},[196,1041,1042],{"class":371}," api",[196,1044,375],{"class":202},[196,1046,841],{"class":219},[196,1048,223],{"class":202},[196,1050,383],{"class":202},[196,1052,458],{"class":386},[196,1054,383],{"class":202},[196,1056,398],{"class":202},[196,1058,68],{"class":219},[196,1060,223],{"class":202},[196,1062,435],{"class":371},[196,1064,375],{"class":202},[196,1066,478],{"class":219},[196,1068,223],{"class":202},[196,1070,383],{"class":202},[196,1072,485],{"class":386},[196,1074,383],{"class":202},[196,1076,490],{"class":202},[1078,1079,1080],"warning",{},"Set a group's rule before registering routes on it or deriving children from it. Routes copy the rule when they are registered, and children copy it when they are created.",[257,1082,1084],{"id":1083},"write-a-custom-rule","Write a custom rule",[178,1086,1087,1090],{},[182,1088,1089],{},"ezz.Custom"," covers predicates over the user value:",[187,1092,1094],{"className":189,"code":1093,"language":191,"meta":192,"style":192},"app.GET(\"\u002Fbeta\", betaFeature).Auth(ezz.Custom(func(user any) bool {\n    u, ok := user.(auth.User)\n    if !ok {\n        return false\n    }\n    flag, _ := u.GetAttribute(\"beta_enabled\")\n    enabled, _ := flag.(bool)\n    return enabled\n}))\n",[182,1095,1096,1145,1174,1189,1198,1203,1234,1255,1264],{"__ignoreMap":192},[196,1097,1098,1100,1102,1104,1106,1108,1111,1113,1115,1118,1120,1122,1124,1126,1128,1131,1134,1136,1138,1140,1143],{"class":198,"line":199},[196,1099,372],{"class":371},[196,1101,375],{"class":202},[196,1103,378],{"class":219},[196,1105,223],{"class":202},[196,1107,383],{"class":202},[196,1109,1110],{"class":386},"\u002Fbeta",[196,1112,383],{"class":202},[196,1114,392],{"class":202},[196,1116,1117],{"class":371}," betaFeature",[196,1119,398],{"class":202},[196,1121,68],{"class":219},[196,1123,223],{"class":202},[196,1125,435],{"class":371},[196,1127,375],{"class":202},[196,1129,1130],{"class":219},"Custom",[196,1132,1133],{"class":202},"(func(",[196,1135,227],{"class":226},[196,1137,230],{"class":206},[196,1139,233],{"class":202},[196,1141,1142],{"class":236}," bool",[196,1144,213],{"class":202},[196,1146,1147,1150,1152,1155,1157,1160,1163,1166,1168,1171],{"class":198,"line":216},[196,1148,1149],{"class":371},"    u",[196,1151,392],{"class":202},[196,1153,1154],{"class":371}," ok ",[196,1156,798],{"class":202},[196,1158,1159],{"class":371}," user",[196,1161,1162],{"class":202},".(",[196,1164,1165],{"class":206},"auth",[196,1167,375],{"class":202},[196,1169,1170],{"class":206},"User",[196,1172,1173],{"class":202},")\n",[196,1175,1176,1180,1183,1186],{"class":198,"line":240},[196,1177,1179],{"class":1178},"s7zQu","    if",[196,1181,1182],{"class":202}," !",[196,1184,1185],{"class":371},"ok ",[196,1187,1188],{"class":202},"{\n",[196,1190,1191,1194],{"class":198,"line":252},[196,1192,1193],{"class":1178},"        return",[196,1195,1197],{"class":1196},"sfNiH"," false\n",[196,1199,1200],{"class":198,"line":548},[196,1201,1202],{"class":202},"    }\n",[196,1204,1205,1208,1210,1213,1215,1218,1220,1223,1225,1227,1230,1232],{"class":198,"line":604},[196,1206,1207],{"class":371},"    flag",[196,1209,392],{"class":202},[196,1211,1212],{"class":371}," _ ",[196,1214,798],{"class":202},[196,1216,1217],{"class":371}," u",[196,1219,375],{"class":202},[196,1221,1222],{"class":219},"GetAttribute",[196,1224,223],{"class":202},[196,1226,383],{"class":202},[196,1228,1229],{"class":386},"beta_enabled",[196,1231,383],{"class":202},[196,1233,1173],{"class":202},[196,1235,1236,1239,1241,1243,1245,1248,1250,1253],{"class":198,"line":729},[196,1237,1238],{"class":371},"    enabled",[196,1240,392],{"class":202},[196,1242,1212],{"class":371},[196,1244,798],{"class":202},[196,1246,1247],{"class":371}," flag",[196,1249,1162],{"class":202},[196,1251,1252],{"class":236},"bool",[196,1254,1173],{"class":202},[196,1256,1258,1261],{"class":198,"line":1257},8,[196,1259,1260],{"class":1178},"    return",[196,1262,1263],{"class":371}," enabled\n",[196,1265,1267],{"class":198,"line":1266},9,[196,1268,1269],{"class":202},"}))\n",[178,1271,1272],{},"For anything with its own status code or message, implement the interface directly:",[187,1274,1276],{"className":189,"code":1275,"language":191,"meta":192,"style":192},"type tenantRule struct{ tenant string }\n\nfunc (r tenantRule) Check(user any) error {\n    u, ok := user.(*Account)\n    if !ok {\n        return ezz.ErrForbidden\n    }\n    if u.Tenant != r.tenant {\n        return ezz.NewHTTPError(http.StatusNotFound, \"not found\")  \u002F\u002F hide existence\n    }\n    if u.Suspended {\n        return ezz.NewHTTPError(http.StatusPaymentRequired, \"account suspended\")\n    }\n    return nil\n}\n\nfunc (tenantRule) IsPublic() bool { return false }\n\nfunc Tenant(name string) ezz.AuthRule { return tenantRule{tenant: name} }\n",[182,1277,1278,1297,1301,1333,1353,1363,1374,1378,1402,1437,1442,1456,1487,1492,1500,1505,1510,1539,1544],{"__ignoreMap":192},[196,1279,1280,1282,1285,1288,1291,1294],{"class":198,"line":199},[196,1281,203],{"class":202},[196,1283,1284],{"class":206}," tenantRule",[196,1286,1287],{"class":202}," struct{",[196,1289,1290],{"class":371}," tenant ",[196,1292,1293],{"class":236},"string",[196,1295,1296],{"class":202}," }\n",[196,1298,1299],{"class":198,"line":216},[196,1300,699],{"emptyLinePlaceholder":698},[196,1302,1303,1306,1309,1312,1315,1317,1320,1322,1324,1326,1328,1331],{"class":198,"line":240},[196,1304,1305],{"class":202},"func",[196,1307,1308],{"class":202}," (",[196,1310,1311],{"class":226},"r ",[196,1313,1314],{"class":206},"tenantRule",[196,1316,233],{"class":202},[196,1318,1319],{"class":219}," Check",[196,1321,223],{"class":202},[196,1323,227],{"class":226},[196,1325,230],{"class":206},[196,1327,233],{"class":202},[196,1329,1330],{"class":236}," error",[196,1332,213],{"class":202},[196,1334,1335,1337,1339,1341,1343,1345,1348,1351],{"class":198,"line":252},[196,1336,1149],{"class":371},[196,1338,392],{"class":202},[196,1340,1154],{"class":371},[196,1342,798],{"class":202},[196,1344,1159],{"class":371},[196,1346,1347],{"class":202},".(*",[196,1349,1350],{"class":206},"Account",[196,1352,1173],{"class":202},[196,1354,1355,1357,1359,1361],{"class":198,"line":548},[196,1356,1179],{"class":1178},[196,1358,1182],{"class":202},[196,1360,1185],{"class":371},[196,1362,1188],{"class":202},[196,1364,1365,1367,1369,1371],{"class":198,"line":604},[196,1366,1193],{"class":1178},[196,1368,801],{"class":371},[196,1370,375],{"class":202},[196,1372,1373],{"class":371},"ErrForbidden\n",[196,1375,1376],{"class":198,"line":729},[196,1377,1202],{"class":202},[196,1379,1380,1382,1384,1386,1389,1392,1395,1397,1400],{"class":198,"line":1257},[196,1381,1179],{"class":1178},[196,1383,1217],{"class":371},[196,1385,375],{"class":202},[196,1387,1388],{"class":371},"Tenant ",[196,1390,1391],{"class":202},"!=",[196,1393,1394],{"class":371}," r",[196,1396,375],{"class":202},[196,1398,1399],{"class":371},"tenant ",[196,1401,1188],{"class":202},[196,1403,1404,1406,1408,1410,1413,1415,1418,1420,1423,1425,1427,1430,1432,1434],{"class":198,"line":1266},[196,1405,1193],{"class":1178},[196,1407,801],{"class":371},[196,1409,375],{"class":202},[196,1411,1412],{"class":219},"NewHTTPError",[196,1414,223],{"class":202},[196,1416,1417],{"class":371},"http",[196,1419,375],{"class":202},[196,1421,1422],{"class":371},"StatusNotFound",[196,1424,392],{"class":202},[196,1426,538],{"class":202},[196,1428,1429],{"class":386},"not found",[196,1431,383],{"class":202},[196,1433,233],{"class":202},[196,1435,1436],{"class":821},"  \u002F\u002F hide existence\n",[196,1438,1440],{"class":198,"line":1439},10,[196,1441,1202],{"class":202},[196,1443,1445,1447,1449,1451,1454],{"class":198,"line":1444},11,[196,1446,1179],{"class":1178},[196,1448,1217],{"class":371},[196,1450,375],{"class":202},[196,1452,1453],{"class":371},"Suspended ",[196,1455,1188],{"class":202},[196,1457,1459,1461,1463,1465,1467,1469,1471,1473,1476,1478,1480,1483,1485],{"class":198,"line":1458},12,[196,1460,1193],{"class":1178},[196,1462,801],{"class":371},[196,1464,375],{"class":202},[196,1466,1412],{"class":219},[196,1468,223],{"class":202},[196,1470,1417],{"class":371},[196,1472,375],{"class":202},[196,1474,1475],{"class":371},"StatusPaymentRequired",[196,1477,392],{"class":202},[196,1479,538],{"class":202},[196,1481,1482],{"class":386},"account suspended",[196,1484,383],{"class":202},[196,1486,1173],{"class":202},[196,1488,1490],{"class":198,"line":1489},13,[196,1491,1202],{"class":202},[196,1493,1495,1497],{"class":198,"line":1494},14,[196,1496,1260],{"class":1178},[196,1498,1499],{"class":202}," nil\n",[196,1501,1503],{"class":198,"line":1502},15,[196,1504,255],{"class":202},[196,1506,1508],{"class":198,"line":1507},16,[196,1509,699],{"emptyLinePlaceholder":698},[196,1511,1513,1515,1517,1519,1521,1524,1526,1528,1531,1534,1537],{"class":198,"line":1512},17,[196,1514,1305],{"class":202},[196,1516,1308],{"class":202},[196,1518,1314],{"class":206},[196,1520,233],{"class":202},[196,1522,1523],{"class":219}," IsPublic",[196,1525,246],{"class":202},[196,1527,1142],{"class":236},[196,1529,1530],{"class":202}," {",[196,1532,1533],{"class":1178}," return",[196,1535,1536],{"class":1196}," false",[196,1538,1296],{"class":202},[196,1540,1542],{"class":198,"line":1541},18,[196,1543,699],{"emptyLinePlaceholder":698},[196,1545,1547,1549,1552,1554,1557,1559,1561,1563,1565,1568,1570,1572,1574,1577,1580,1583,1586,1589],{"class":198,"line":1546},19,[196,1548,1305],{"class":202},[196,1550,1551],{"class":219}," Tenant",[196,1553,223],{"class":202},[196,1555,1556],{"class":226},"name",[196,1558,685],{"class":236},[196,1560,233],{"class":202},[196,1562,801],{"class":206},[196,1564,375],{"class":202},[196,1566,1567],{"class":206},"AuthRule",[196,1569,1530],{"class":202},[196,1571,1533],{"class":1178},[196,1573,1284],{"class":206},[196,1575,1576],{"class":202},"{",[196,1578,1579],{"class":371},"tenant",[196,1581,1582],{"class":202},":",[196,1584,1585],{"class":371}," name",[196,1587,1588],{"class":202},"}",[196,1590,1296],{"class":202},[178,1592,1593,1594,1597,1598,1600,1601,375],{},"An ",[182,1595,1596],{},"*ezz.HTTPError"," returned from ",[182,1599,184],{}," keeps its status and message. Any other error becomes a plain ",[182,1602,737],{},[1604,1605,1606],"note",{},"A rule only sees the user value. It cannot read the request, so anything path-dependent, such as \"the caller owns this document\", belongs in the handler where you have the request and your data layer.",[257,1608,1610],{"id":1609},"check-ownership-in-the-handler","Check ownership in the handler",[187,1612,1614],{"className":189,"code":1613,"language":191,"meta":192,"style":192},"app.GET(\"\u002Fdocuments\u002F{id}\", ezz.H(func(ctx *ezz.Context, req GetDocRequest) (*Document, error) {\n    user := ctx.User().(auth.User)\n\n    doc, err := store.Find(ctx, req.ID)\n    if err != nil {\n        return nil, ezz.NotFound(\"document not found\")\n    }\n    if doc.OwnerID != user.ID() {\n        return nil, ezz.NotFound(\"document not found\")   \u002F\u002F don't confirm it exists\n    }\n    return doc, nil\n}))\n",[182,1615,1616,1680,1705,1709,1744,1757,1782,1786,1810,1835,1839,1849],{"__ignoreMap":192},[196,1617,1618,1620,1622,1624,1626,1628,1631,1633,1635,1637,1639,1642,1644,1647,1650,1652,1654,1656,1658,1661,1664,1666,1669,1672,1674,1676,1678],{"class":198,"line":199},[196,1619,372],{"class":371},[196,1621,375],{"class":202},[196,1623,378],{"class":219},[196,1625,223],{"class":202},[196,1627,383],{"class":202},[196,1629,1630],{"class":386},"\u002Fdocuments\u002F{id}",[196,1632,383],{"class":202},[196,1634,392],{"class":202},[196,1636,801],{"class":371},[196,1638,375],{"class":202},[196,1640,1641],{"class":219},"H",[196,1643,1133],{"class":202},[196,1645,1646],{"class":226},"ctx",[196,1648,1649],{"class":202}," *",[196,1651,435],{"class":206},[196,1653,375],{"class":202},[196,1655,53],{"class":206},[196,1657,392],{"class":202},[196,1659,1660],{"class":226}," req",[196,1662,1663],{"class":206}," GetDocRequest",[196,1665,233],{"class":202},[196,1667,1668],{"class":202}," (*",[196,1670,1671],{"class":206},"Document",[196,1673,392],{"class":202},[196,1675,1330],{"class":236},[196,1677,233],{"class":202},[196,1679,213],{"class":202},[196,1681,1682,1685,1687,1690,1692,1694,1697,1699,1701,1703],{"class":198,"line":216},[196,1683,1684],{"class":371},"    user ",[196,1686,798],{"class":202},[196,1688,1689],{"class":371}," ctx",[196,1691,375],{"class":202},[196,1693,1170],{"class":219},[196,1695,1696],{"class":202},"().(",[196,1698,1165],{"class":206},[196,1700,375],{"class":202},[196,1702,1170],{"class":206},[196,1704,1173],{"class":202},[196,1706,1707],{"class":198,"line":240},[196,1708,699],{"emptyLinePlaceholder":698},[196,1710,1711,1714,1716,1719,1721,1724,1726,1729,1731,1733,1735,1737,1739,1742],{"class":198,"line":252},[196,1712,1713],{"class":371},"    doc",[196,1715,392],{"class":202},[196,1717,1718],{"class":371}," err ",[196,1720,798],{"class":202},[196,1722,1723],{"class":371}," store",[196,1725,375],{"class":202},[196,1727,1728],{"class":219},"Find",[196,1730,223],{"class":202},[196,1732,1646],{"class":371},[196,1734,392],{"class":202},[196,1736,1660],{"class":371},[196,1738,375],{"class":202},[196,1740,1741],{"class":371},"ID",[196,1743,1173],{"class":202},[196,1745,1746,1748,1750,1752,1755],{"class":198,"line":548},[196,1747,1179],{"class":1178},[196,1749,1718],{"class":371},[196,1751,1391],{"class":202},[196,1753,1754],{"class":202}," nil",[196,1756,213],{"class":202},[196,1758,1759,1761,1764,1766,1768,1771,1773,1775,1778,1780],{"class":198,"line":604},[196,1760,1193],{"class":1178},[196,1762,1763],{"class":202}," nil,",[196,1765,801],{"class":371},[196,1767,375],{"class":202},[196,1769,1770],{"class":219},"NotFound",[196,1772,223],{"class":202},[196,1774,383],{"class":202},[196,1776,1777],{"class":386},"document not found",[196,1779,383],{"class":202},[196,1781,1173],{"class":202},[196,1783,1784],{"class":198,"line":729},[196,1785,1202],{"class":202},[196,1787,1788,1790,1793,1795,1798,1800,1802,1804,1806,1808],{"class":198,"line":1257},[196,1789,1179],{"class":1178},[196,1791,1792],{"class":371}," doc",[196,1794,375],{"class":202},[196,1796,1797],{"class":371},"OwnerID ",[196,1799,1391],{"class":202},[196,1801,1159],{"class":371},[196,1803,375],{"class":202},[196,1805,1741],{"class":219},[196,1807,246],{"class":202},[196,1809,213],{"class":202},[196,1811,1812,1814,1816,1818,1820,1822,1824,1826,1828,1830,1832],{"class":198,"line":1266},[196,1813,1193],{"class":1178},[196,1815,1763],{"class":202},[196,1817,801],{"class":371},[196,1819,375],{"class":202},[196,1821,1770],{"class":219},[196,1823,223],{"class":202},[196,1825,383],{"class":202},[196,1827,1777],{"class":386},[196,1829,383],{"class":202},[196,1831,233],{"class":202},[196,1833,1834],{"class":821},"   \u002F\u002F don't confirm it exists\n",[196,1836,1837],{"class":198,"line":1439},[196,1838,1202],{"class":202},[196,1840,1841,1843,1845,1847],{"class":198,"line":1444},[196,1842,1260],{"class":1178},[196,1844,1792],{"class":371},[196,1846,392],{"class":202},[196,1848,1499],{"class":202},[196,1850,1851],{"class":198,"line":1458},[196,1852,1269],{"class":202},[257,1854,1856],{"id":1855},"what-the-caller-sees","What the caller sees",[262,1858,1859,1869],{},[265,1860,1861],{},[268,1862,1863,1866],{},[271,1864,1865],{},"Outcome",[271,1867,1868],{},"Response",[278,1870,1871,1885,1897,1907],{},[268,1872,1873,1876],{},[283,1874,1875],{},"No or invalid credentials",[283,1877,1878,1881,1882],{},[182,1879,1880],{},"401"," with ",[182,1883,1884],{},"{\"error\":true,\"message\":\"Unauthorized\",\"status\":401}",[268,1886,1887,1890],{},[283,1888,1889],{},"Rule denied the user",[283,1891,1892,1881,1894],{},[182,1893,737],{},[182,1895,1896],{},"{\"error\":true,\"message\":\"Forbidden\",\"status\":403}",[268,1898,1899,1904],{},[283,1900,1901,1902],{},"Rule returned an ",[182,1903,1596],{},[283,1905,1906],{},"That status and message",[268,1908,1909,1912],{},[283,1910,1911],{},"Authorization resolver failed",[283,1913,1914,1881,1917],{},[182,1915,1916],{},"500",[182,1918,1919],{},"{\"error\":true,\"message\":\"Authorization failed\",\"status\":500}",[257,1921,1923],{"id":1922},"next-steps","Next steps",[747,1925,1926,1932],{},[750,1927,1928,1931],{},[754,1929,1930],{"href":85},"JWT providers"," for the values these rules receive.",[750,1933,1934,1937],{},[754,1935,1936],{"href":90},"Roles and permissions"," for loading them from your database.",[1939,1940,1941],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}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 pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":192,"searchDepth":216,"depth":216,"links":1943},[1944,1945,1946,1947,1948,1949,1950],{"id":259,"depth":216,"text":260},{"id":652,"depth":216,"text":653},{"id":781,"depth":216,"text":782},{"id":1083,"depth":216,"text":1084},{"id":1609,"depth":216,"text":1610},{"id":1855,"depth":216,"text":1856},{"id":1922,"depth":216,"text":1923},"Require authentication, roles, permissions, or your own predicate, per route or per group.","md",null,{},{"icon":82},{"title":79,"description":1951},"5Sm2dZme1KxezKycpO8Kz6xR_cqbYMtJho7l8w7wFLs",[1959,1961],{"title":74,"path":75,"stem":76,"description":1960,"icon":77,"children":-1},"How authentication is wired into the app, and what happens on every request.",{"title":84,"path":85,"stem":86,"description":1962,"icon":87,"children":-1},"Issue and validate HS256 tokens, validate GoTrue tokens, or plug in your own credential scheme.",1784970047655]