[{"data":1,"prerenderedAt":1742},["ShallowReactive",2],{"navigation_docs":3,"-auth-audit-logging":172,"-auth-audit-logging-surround":1737},[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":94,"body":174,"description":1730,"extension":1731,"links":1732,"meta":1733,"navigation":1734,"path":95,"seo":1735,"stem":96,"__hash__":1736},"docs\u002F3.auth\u002F5.audit-logging.md",{"type":175,"value":176,"toc":1723},"minimark",[177,186,443,448,587,601,607,611,685,692,695,961,965,968,1069,1509,1512,1522,1526,1532,1694,1702,1706,1719],[178,179,180,181,185],"p",{},"Auth events go to an ",[182,183,184],"code",{},"auth.AuditLogger",". Nothing is recorded until you configure one, and everything that emits events treats a nil logger as \"off\".",[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 AuditLogger interface {\n    LogAuthEvent(ctx context.Context, event AuditEvent)\n    LogLoginAttempt(ctx context.Context, userID, result string, details map[string]any)\n    LogAccessAttempt(ctx context.Context, userID, resource, action, result string, details map[string]any)\n    LogRoleCheck(ctx context.Context, userID, role, result string, details map[string]any)\n    LogPermissionCheck(ctx context.Context, userID, permission, result string, details map[string]any)\n}\n","go","",[182,194,195,214,248,297,347,392,437],{"__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"," AuditLogger",[196,209,210],{"class":202}," interface",[196,212,213],{"class":202}," {\n",[196,215,217,221,224,228,231,234,236,239,242,245],{"class":198,"line":216},2,[196,218,220],{"class":219},"s2Zo4","    LogAuthEvent",[196,222,223],{"class":202},"(",[196,225,227],{"class":226},"sHdIc","ctx",[196,229,230],{"class":206}," context",[196,232,233],{"class":202},".",[196,235,53],{"class":206},[196,237,238],{"class":202},",",[196,240,241],{"class":226}," event",[196,243,244],{"class":206}," AuditEvent",[196,246,247],{"class":202},")\n",[196,249,251,254,256,258,260,262,264,266,269,271,274,278,280,283,286,289,292,295],{"class":198,"line":250},3,[196,252,253],{"class":219},"    LogLoginAttempt",[196,255,223],{"class":202},[196,257,227],{"class":226},[196,259,230],{"class":206},[196,261,233],{"class":202},[196,263,53],{"class":206},[196,265,238],{"class":202},[196,267,268],{"class":226}," userID",[196,270,238],{"class":202},[196,272,273],{"class":226}," result",[196,275,277],{"class":276},"spNyl"," string",[196,279,238],{"class":202},[196,281,282],{"class":226}," details",[196,284,285],{"class":202}," map[",[196,287,288],{"class":276},"string",[196,290,291],{"class":202},"]",[196,293,294],{"class":206},"any",[196,296,247],{"class":202},[196,298,300,303,305,307,309,311,313,315,317,319,322,324,327,329,331,333,335,337,339,341,343,345],{"class":198,"line":299},4,[196,301,302],{"class":219},"    LogAccessAttempt",[196,304,223],{"class":202},[196,306,227],{"class":226},[196,308,230],{"class":206},[196,310,233],{"class":202},[196,312,53],{"class":206},[196,314,238],{"class":202},[196,316,268],{"class":226},[196,318,238],{"class":202},[196,320,321],{"class":226}," resource",[196,323,238],{"class":202},[196,325,326],{"class":226}," action",[196,328,238],{"class":202},[196,330,273],{"class":226},[196,332,277],{"class":276},[196,334,238],{"class":202},[196,336,282],{"class":226},[196,338,285],{"class":202},[196,340,288],{"class":276},[196,342,291],{"class":202},[196,344,294],{"class":206},[196,346,247],{"class":202},[196,348,350,353,355,357,359,361,363,365,367,369,372,374,376,378,380,382,384,386,388,390],{"class":198,"line":349},5,[196,351,352],{"class":219},"    LogRoleCheck",[196,354,223],{"class":202},[196,356,227],{"class":226},[196,358,230],{"class":206},[196,360,233],{"class":202},[196,362,53],{"class":206},[196,364,238],{"class":202},[196,366,268],{"class":226},[196,368,238],{"class":202},[196,370,371],{"class":226}," role",[196,373,238],{"class":202},[196,375,273],{"class":226},[196,377,277],{"class":276},[196,379,238],{"class":202},[196,381,282],{"class":226},[196,383,285],{"class":202},[196,385,288],{"class":276},[196,387,291],{"class":202},[196,389,294],{"class":206},[196,391,247],{"class":202},[196,393,395,398,400,402,404,406,408,410,412,414,417,419,421,423,425,427,429,431,433,435],{"class":198,"line":394},6,[196,396,397],{"class":219},"    LogPermissionCheck",[196,399,223],{"class":202},[196,401,227],{"class":226},[196,403,230],{"class":206},[196,405,233],{"class":202},[196,407,53],{"class":206},[196,409,238],{"class":202},[196,411,268],{"class":226},[196,413,238],{"class":202},[196,415,416],{"class":226}," permission",[196,418,238],{"class":202},[196,420,273],{"class":226},[196,422,277],{"class":276},[196,424,238],{"class":202},[196,426,282],{"class":226},[196,428,285],{"class":202},[196,430,288],{"class":276},[196,432,291],{"class":202},[196,434,294],{"class":206},[196,436,247],{"class":202},[196,438,440],{"class":198,"line":439},7,[196,441,442],{"class":202},"}\n",[444,445,447],"h2",{"id":446},"log-to-slog","Log to slog",[187,449,451],{"className":189,"code":450,"language":191,"meta":192,"style":192},"logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))\n\nprovider := auth.NewJWTAuthProvider(jwt).\n    WithAuditLogger(auth.NewSlogAuditLogger(logger))\n\napp := ezz.New().Logger(logger).Auth(provider)\n",[182,452,453,495,501,524,547,551],{"__ignoreMap":192},[196,454,455,459,462,465,467,470,472,475,477,480,482,485,487,490,492],{"class":198,"line":199},[196,456,458],{"class":457},"sTEyZ","logger ",[196,460,461],{"class":202},":=",[196,463,464],{"class":457}," slog",[196,466,233],{"class":202},[196,468,469],{"class":219},"New",[196,471,223],{"class":202},[196,473,474],{"class":457},"slog",[196,476,233],{"class":202},[196,478,479],{"class":219},"NewJSONHandler",[196,481,223],{"class":202},[196,483,484],{"class":457},"os",[196,486,233],{"class":202},[196,488,489],{"class":457},"Stdout",[196,491,238],{"class":202},[196,493,494],{"class":202}," nil))\n",[196,496,497],{"class":198,"line":216},[196,498,500],{"emptyLinePlaceholder":499},true,"\n",[196,502,503,506,508,511,513,516,518,521],{"class":198,"line":250},[196,504,505],{"class":457},"provider ",[196,507,461],{"class":202},[196,509,510],{"class":457}," auth",[196,512,233],{"class":202},[196,514,515],{"class":219},"NewJWTAuthProvider",[196,517,223],{"class":202},[196,519,520],{"class":457},"jwt",[196,522,523],{"class":202},").\n",[196,525,526,529,531,534,536,539,541,544],{"class":198,"line":299},[196,527,528],{"class":219},"    WithAuditLogger",[196,530,223],{"class":202},[196,532,533],{"class":457},"auth",[196,535,233],{"class":202},[196,537,538],{"class":219},"NewSlogAuditLogger",[196,540,223],{"class":202},[196,542,543],{"class":457},"logger",[196,545,546],{"class":202},"))\n",[196,548,549],{"class":198,"line":349},[196,550,500],{"emptyLinePlaceholder":499},[196,552,553,556,558,561,563,565,568,571,573,575,578,580,582,585],{"class":198,"line":394},[196,554,555],{"class":457},"app ",[196,557,461],{"class":202},[196,559,560],{"class":457}," ezz",[196,562,233],{"class":202},[196,564,469],{"class":219},[196,566,567],{"class":202},"().",[196,569,570],{"class":219},"Logger",[196,572,223],{"class":202},[196,574,543],{"class":457},[196,576,577],{"class":202},").",[196,579,68],{"class":219},[196,581,223],{"class":202},[196,583,584],{"class":457},"provider",[196,586,247],{"class":202},[178,588,589,590,592,593,596,597,600],{},"Passing nil to ",[182,591,538],{}," uses ",[182,594,595],{},"slog.Default()",". Events are written at info level under the message ",[182,598,599],{},"auth_event",", with the user ID, action, resource, result, request ID, IP address, user agent, and any extra details as attributes.",[178,602,603,606],{},[182,604,605],{},"auth.NewNoOpAuditLogger()"," discards everything, which is useful in tests or to disable auditing without changing the wiring.",[444,608,610],{"id":609},"what-gets-recorded-today","What gets recorded today",[612,613,614,630],"table",{},[615,616,617],"thead",{},[618,619,620,624,627],"tr",{},[621,622,623],"th",{},"Event",[621,625,626],{},"Emitted by",[621,628,629],{},"Method",[631,632,633,653,673],"tbody",{},[618,634,635,639,644],{},[636,637,638],"td",{},"Token validation failure",[636,640,641],{},[182,642,643],{},"JWTAuthProvider.Authenticate",[636,645,646,649,650],{},[182,647,648],{},"LogAccessAttempt"," with ",[182,651,652],{},"reason: invalid_token",[618,654,655,658,668],{},[636,656,657],{},"GoTrue sign-in and sign-up, success or failure",[636,659,660,661,664,665],{},"The GoTrue proxy on ",[182,662,663],{},"\u002Ftoken"," and ",[182,666,667],{},"\u002Fsignup",[636,669,670],{},[182,671,672],{},"LogLoginAttempt",[618,674,675,678,681],{},[636,676,677],{},"Every other proxied GoTrue call",[636,679,680],{},"The GoTrue proxy",[636,682,683],{},[182,684,648],{},[178,686,687,688,233],{},"The proxy details include the GoTrue path, the resolved client IP, the request ID, and the user agent. See ",[689,690,691],"a",{"href":106},"GoTrue proxy",[178,693,694],{},"Successful token validation is not logged, and role and permission decisions are not logged automatically. Call the logger yourself where you need those:",[187,696,698],{"className":189,"code":697,"language":191,"meta":192,"style":192},"app.DELETE(\"\u002Fusers\u002F{id}\", ezz.H(func(ctx *ezz.Context, req DeleteUserRequest) (any, error) {\n    user := ctx.User().(auth.User)\n\n    audit.LogAccessAttempt(ctx, user.ID(), \"\u002Fusers\u002F\"+req.ID, \"DELETE\", \"success\", map[string]any{\n        \"request_id\": ctx.GetRequestID(),\n    })\n    return nil, store.Delete(ctx, req.ID)\n})).Auth(ezz.Role(\"admin\"))\n",[182,699,700,770,796,800,874,897,902,933],{"__ignoreMap":192},[196,701,702,705,707,710,712,715,719,721,723,725,727,730,733,735,738,741,743,745,747,750,753,756,759,761,763,766,768],{"class":198,"line":199},[196,703,704],{"class":457},"app",[196,706,233],{"class":202},[196,708,709],{"class":219},"DELETE",[196,711,223],{"class":202},[196,713,714],{"class":202},"\"",[196,716,718],{"class":717},"sfazB","\u002Fusers\u002F{id}",[196,720,714],{"class":202},[196,722,238],{"class":202},[196,724,560],{"class":457},[196,726,233],{"class":202},[196,728,729],{"class":219},"H",[196,731,732],{"class":202},"(func(",[196,734,227],{"class":226},[196,736,737],{"class":202}," *",[196,739,740],{"class":206},"ezz",[196,742,233],{"class":202},[196,744,53],{"class":206},[196,746,238],{"class":202},[196,748,749],{"class":226}," req",[196,751,752],{"class":206}," DeleteUserRequest",[196,754,755],{"class":202},")",[196,757,758],{"class":202}," (",[196,760,294],{"class":206},[196,762,238],{"class":202},[196,764,765],{"class":276}," error",[196,767,755],{"class":202},[196,769,213],{"class":202},[196,771,772,775,777,780,782,785,788,790,792,794],{"class":198,"line":216},[196,773,774],{"class":457},"    user ",[196,776,461],{"class":202},[196,778,779],{"class":457}," ctx",[196,781,233],{"class":202},[196,783,784],{"class":219},"User",[196,786,787],{"class":202},"().(",[196,789,533],{"class":206},[196,791,233],{"class":202},[196,793,784],{"class":206},[196,795,247],{"class":202},[196,797,798],{"class":198,"line":250},[196,799,500],{"emptyLinePlaceholder":499},[196,801,802,805,807,809,811,813,815,818,820,823,826,829,832,834,837,840,842,844,846,848,850,852,854,856,859,861,863,865,867,869,871],{"class":198,"line":299},[196,803,804],{"class":457},"    audit",[196,806,233],{"class":202},[196,808,648],{"class":219},[196,810,223],{"class":202},[196,812,227],{"class":457},[196,814,238],{"class":202},[196,816,817],{"class":457}," user",[196,819,233],{"class":202},[196,821,822],{"class":219},"ID",[196,824,825],{"class":202},"(),",[196,827,828],{"class":202}," \"",[196,830,831],{"class":717},"\u002Fusers\u002F",[196,833,714],{"class":202},[196,835,836],{"class":202},"+",[196,838,839],{"class":457},"req",[196,841,233],{"class":202},[196,843,822],{"class":457},[196,845,238],{"class":202},[196,847,828],{"class":202},[196,849,709],{"class":717},[196,851,714],{"class":202},[196,853,238],{"class":202},[196,855,828],{"class":202},[196,857,858],{"class":717},"success",[196,860,714],{"class":202},[196,862,238],{"class":202},[196,864,285],{"class":202},[196,866,288],{"class":276},[196,868,291],{"class":202},[196,870,294],{"class":206},[196,872,873],{"class":202},"{\n",[196,875,876,879,882,884,887,889,891,894],{"class":198,"line":349},[196,877,878],{"class":202},"        \"",[196,880,881],{"class":717},"request_id",[196,883,714],{"class":202},[196,885,886],{"class":202},":",[196,888,779],{"class":457},[196,890,233],{"class":202},[196,892,893],{"class":219},"GetRequestID",[196,895,896],{"class":202},"(),\n",[196,898,899],{"class":198,"line":394},[196,900,901],{"class":202},"    })\n",[196,903,904,908,911,914,916,919,921,923,925,927,929,931],{"class":198,"line":439},[196,905,907],{"class":906},"s7zQu","    return",[196,909,910],{"class":202}," nil,",[196,912,913],{"class":457}," store",[196,915,233],{"class":202},[196,917,918],{"class":219},"Delete",[196,920,223],{"class":202},[196,922,227],{"class":457},[196,924,238],{"class":202},[196,926,749],{"class":457},[196,928,233],{"class":202},[196,930,822],{"class":457},[196,932,247],{"class":202},[196,934,936,939,941,943,945,947,950,952,954,957,959],{"class":198,"line":935},8,[196,937,938],{"class":202},"})).",[196,940,68],{"class":219},[196,942,223],{"class":202},[196,944,740],{"class":457},[196,946,233],{"class":202},[196,948,949],{"class":219},"Role",[196,951,223],{"class":202},[196,953,714],{"class":202},[196,955,956],{"class":717},"admin",[196,958,714],{"class":202},[196,960,546],{"class":202},[444,962,964],{"id":963},"write-your-own-logger","Write your own logger",[178,966,967],{},"Implement the interface to send events to a database, a queue, or a SIEM. The event struct is what a full record looks like:",[187,969,971],{"className":189,"code":970,"language":191,"meta":192,"style":192},"type AuditEvent struct {\n    Timestamp time.Time\n    UserID    string\n    Action    string\n    Resource  string\n    Result    string\n    Details   map[string]any\n    RequestID string\n    IPAddress string\n    UserAgent string\n}\n",[182,972,973,984,997,1005,1012,1019,1026,1041,1048,1056,1064],{"__ignoreMap":192},[196,974,975,977,979,982],{"class":198,"line":199},[196,976,203],{"class":202},[196,978,244],{"class":206},[196,980,981],{"class":202}," struct",[196,983,213],{"class":202},[196,985,986,989,992,994],{"class":198,"line":216},[196,987,988],{"class":457},"    Timestamp ",[196,990,991],{"class":206},"time",[196,993,233],{"class":202},[196,995,996],{"class":206},"Time\n",[196,998,999,1002],{"class":198,"line":250},[196,1000,1001],{"class":457},"    UserID    ",[196,1003,1004],{"class":276},"string\n",[196,1006,1007,1010],{"class":198,"line":299},[196,1008,1009],{"class":457},"    Action    ",[196,1011,1004],{"class":276},[196,1013,1014,1017],{"class":198,"line":349},[196,1015,1016],{"class":457},"    Resource  ",[196,1018,1004],{"class":276},[196,1020,1021,1024],{"class":198,"line":394},[196,1022,1023],{"class":457},"    Result    ",[196,1025,1004],{"class":276},[196,1027,1028,1031,1034,1036,1038],{"class":198,"line":439},[196,1029,1030],{"class":457},"    Details   ",[196,1032,1033],{"class":202},"map[",[196,1035,288],{"class":276},[196,1037,291],{"class":202},[196,1039,1040],{"class":206},"any\n",[196,1042,1043,1046],{"class":198,"line":935},[196,1044,1045],{"class":457},"    RequestID ",[196,1047,1004],{"class":276},[196,1049,1051,1054],{"class":198,"line":1050},9,[196,1052,1053],{"class":457},"    IPAddress ",[196,1055,1004],{"class":276},[196,1057,1059,1062],{"class":198,"line":1058},10,[196,1060,1061],{"class":457},"    UserAgent ",[196,1063,1004],{"class":276},[196,1065,1067],{"class":198,"line":1066},11,[196,1068,442],{"class":202},[187,1070,1072],{"className":189,"code":1071,"language":191,"meta":192,"style":192},"type DBAuditLogger struct {\n    events chan auth.AuditEvent\n}\n\nfunc NewDBAuditLogger(db *sql.DB) *DBAuditLogger {\n    l := &DBAuditLogger{events: make(chan auth.AuditEvent, 1024)}\n    go l.drain(db)\n    return l\n}\n\nfunc (l *DBAuditLogger) LogAuthEvent(ctx context.Context, event auth.AuditEvent) {\n    if event.Timestamp.IsZero() {\n        event.Timestamp = time.Now()\n    }\n    select {\n    case l.events \u003C- event:\n    default:\n        \u002F\u002F prefer dropping an event over blocking the request\n    }\n}\n\nfunc (l *DBAuditLogger) LogLoginAttempt(ctx context.Context, userID, result string, details map[string]any) {\n    l.LogAuthEvent(ctx, auth.AuditEvent{UserID: userID, Action: \"login\", Result: result, Details: details})\n}\n\n\u002F\u002F LogAccessAttempt, LogRoleCheck, and LogPermissionCheck follow the same pattern\n",[182,1073,1074,1085,1100,1104,1108,1140,1182,1201,1208,1212,1216,1259,1282,1307,1313,1321,1342,1350,1357,1362,1367,1372,1426,1493,1498,1503],{"__ignoreMap":192},[196,1075,1076,1078,1081,1083],{"class":198,"line":199},[196,1077,203],{"class":202},[196,1079,1080],{"class":206}," DBAuditLogger",[196,1082,981],{"class":202},[196,1084,213],{"class":202},[196,1086,1087,1090,1093,1095,1097],{"class":198,"line":216},[196,1088,1089],{"class":457},"    events ",[196,1091,1092],{"class":202},"chan",[196,1094,510],{"class":206},[196,1096,233],{"class":202},[196,1098,1099],{"class":206},"AuditEvent\n",[196,1101,1102],{"class":198,"line":250},[196,1103,442],{"class":202},[196,1105,1106],{"class":198,"line":299},[196,1107,500],{"emptyLinePlaceholder":499},[196,1109,1110,1113,1116,1118,1121,1123,1126,1128,1131,1133,1135,1138],{"class":198,"line":349},[196,1111,1112],{"class":202},"func",[196,1114,1115],{"class":219}," NewDBAuditLogger",[196,1117,223],{"class":202},[196,1119,1120],{"class":226},"db",[196,1122,737],{"class":202},[196,1124,1125],{"class":206},"sql",[196,1127,233],{"class":202},[196,1129,1130],{"class":206},"DB",[196,1132,755],{"class":202},[196,1134,737],{"class":202},[196,1136,1137],{"class":206},"DBAuditLogger",[196,1139,213],{"class":202},[196,1141,1142,1145,1147,1150,1152,1155,1158,1160,1163,1166,1168,1170,1173,1175,1179],{"class":198,"line":394},[196,1143,1144],{"class":457},"    l ",[196,1146,461],{"class":202},[196,1148,1149],{"class":202}," &",[196,1151,1137],{"class":206},[196,1153,1154],{"class":202},"{",[196,1156,1157],{"class":457},"events",[196,1159,886],{"class":202},[196,1161,1162],{"class":219}," make",[196,1164,1165],{"class":202},"(chan",[196,1167,510],{"class":206},[196,1169,233],{"class":202},[196,1171,1172],{"class":206},"AuditEvent",[196,1174,238],{"class":202},[196,1176,1178],{"class":1177},"sbssI"," 1024",[196,1180,1181],{"class":202},")}\n",[196,1183,1184,1187,1190,1192,1195,1197,1199],{"class":198,"line":439},[196,1185,1186],{"class":906},"    go",[196,1188,1189],{"class":457}," l",[196,1191,233],{"class":202},[196,1193,1194],{"class":219},"drain",[196,1196,223],{"class":202},[196,1198,1120],{"class":457},[196,1200,247],{"class":202},[196,1202,1203,1205],{"class":198,"line":935},[196,1204,907],{"class":906},[196,1206,1207],{"class":457}," l\n",[196,1209,1210],{"class":198,"line":1050},[196,1211,442],{"class":202},[196,1213,1214],{"class":198,"line":1058},[196,1215,500],{"emptyLinePlaceholder":499},[196,1217,1218,1220,1222,1225,1228,1230,1232,1235,1237,1239,1241,1243,1245,1247,1249,1251,1253,1255,1257],{"class":198,"line":1066},[196,1219,1112],{"class":202},[196,1221,758],{"class":202},[196,1223,1224],{"class":226},"l ",[196,1226,1227],{"class":202},"*",[196,1229,1137],{"class":206},[196,1231,755],{"class":202},[196,1233,1234],{"class":219}," LogAuthEvent",[196,1236,223],{"class":202},[196,1238,227],{"class":226},[196,1240,230],{"class":206},[196,1242,233],{"class":202},[196,1244,53],{"class":206},[196,1246,238],{"class":202},[196,1248,241],{"class":226},[196,1250,510],{"class":206},[196,1252,233],{"class":202},[196,1254,1172],{"class":206},[196,1256,755],{"class":202},[196,1258,213],{"class":202},[196,1260,1262,1265,1267,1269,1272,1274,1277,1280],{"class":198,"line":1261},12,[196,1263,1264],{"class":906},"    if",[196,1266,241],{"class":457},[196,1268,233],{"class":202},[196,1270,1271],{"class":457},"Timestamp",[196,1273,233],{"class":202},[196,1275,1276],{"class":219},"IsZero",[196,1278,1279],{"class":202},"()",[196,1281,213],{"class":202},[196,1283,1285,1288,1290,1293,1296,1299,1301,1304],{"class":198,"line":1284},13,[196,1286,1287],{"class":457},"        event",[196,1289,233],{"class":202},[196,1291,1292],{"class":457},"Timestamp ",[196,1294,1295],{"class":202},"=",[196,1297,1298],{"class":457}," time",[196,1300,233],{"class":202},[196,1302,1303],{"class":219},"Now",[196,1305,1306],{"class":202},"()\n",[196,1308,1310],{"class":198,"line":1309},14,[196,1311,1312],{"class":202},"    }\n",[196,1314,1316,1319],{"class":198,"line":1315},15,[196,1317,1318],{"class":906},"    select",[196,1320,213],{"class":202},[196,1322,1324,1327,1329,1331,1334,1337,1339],{"class":198,"line":1323},16,[196,1325,1326],{"class":906},"    case",[196,1328,1189],{"class":457},[196,1330,233],{"class":202},[196,1332,1333],{"class":457},"events ",[196,1335,1336],{"class":202},"\u003C-",[196,1338,241],{"class":457},[196,1340,1341],{"class":202},":\n",[196,1343,1345,1348],{"class":198,"line":1344},17,[196,1346,1347],{"class":906},"    default",[196,1349,1341],{"class":202},[196,1351,1353],{"class":198,"line":1352},18,[196,1354,1356],{"class":1355},"sHwdD","        \u002F\u002F prefer dropping an event over blocking the request\n",[196,1358,1360],{"class":198,"line":1359},19,[196,1361,1312],{"class":202},[196,1363,1365],{"class":198,"line":1364},20,[196,1366,442],{"class":202},[196,1368,1370],{"class":198,"line":1369},21,[196,1371,500],{"emptyLinePlaceholder":499},[196,1373,1375,1377,1379,1381,1383,1385,1387,1390,1392,1394,1396,1398,1400,1402,1404,1406,1408,1410,1412,1414,1416,1418,1420,1422,1424],{"class":198,"line":1374},22,[196,1376,1112],{"class":202},[196,1378,758],{"class":202},[196,1380,1224],{"class":226},[196,1382,1227],{"class":202},[196,1384,1137],{"class":206},[196,1386,755],{"class":202},[196,1388,1389],{"class":219}," LogLoginAttempt",[196,1391,223],{"class":202},[196,1393,227],{"class":226},[196,1395,230],{"class":206},[196,1397,233],{"class":202},[196,1399,53],{"class":206},[196,1401,238],{"class":202},[196,1403,268],{"class":226},[196,1405,238],{"class":202},[196,1407,273],{"class":226},[196,1409,277],{"class":276},[196,1411,238],{"class":202},[196,1413,282],{"class":226},[196,1415,285],{"class":202},[196,1417,288],{"class":276},[196,1419,291],{"class":202},[196,1421,294],{"class":206},[196,1423,755],{"class":202},[196,1425,213],{"class":202},[196,1427,1429,1432,1434,1437,1439,1441,1443,1445,1447,1449,1451,1454,1456,1458,1460,1463,1465,1467,1470,1472,1474,1477,1479,1481,1483,1486,1488,1490],{"class":198,"line":1428},23,[196,1430,1431],{"class":457},"    l",[196,1433,233],{"class":202},[196,1435,1436],{"class":219},"LogAuthEvent",[196,1438,223],{"class":202},[196,1440,227],{"class":457},[196,1442,238],{"class":202},[196,1444,510],{"class":206},[196,1446,233],{"class":202},[196,1448,1172],{"class":206},[196,1450,1154],{"class":202},[196,1452,1453],{"class":457},"UserID",[196,1455,886],{"class":202},[196,1457,268],{"class":457},[196,1459,238],{"class":202},[196,1461,1462],{"class":457}," Action",[196,1464,886],{"class":202},[196,1466,828],{"class":202},[196,1468,1469],{"class":717},"login",[196,1471,714],{"class":202},[196,1473,238],{"class":202},[196,1475,1476],{"class":457}," Result",[196,1478,886],{"class":202},[196,1480,273],{"class":457},[196,1482,238],{"class":202},[196,1484,1485],{"class":457}," Details",[196,1487,886],{"class":202},[196,1489,282],{"class":457},[196,1491,1492],{"class":202},"})\n",[196,1494,1496],{"class":198,"line":1495},24,[196,1497,442],{"class":202},[196,1499,1501],{"class":198,"line":1500},25,[196,1502,500],{"emptyLinePlaceholder":499},[196,1504,1506],{"class":198,"line":1505},26,[196,1507,1508],{"class":1355},"\u002F\u002F LogAccessAttempt, LogRoleCheck, and LogPermissionCheck follow the same pattern\n",[178,1510,1511],{},"Two things to keep in mind when writing one:",[1513,1514,1515,1519],"ul",{},[1516,1517,1518],"li",{},"Auditing happens on the request path. Buffer or batch anything that talks to the network, and never block the request on a slow sink.",[1516,1520,1521],{},"The context you receive is the request context, so it is cancelled when the response finishes. Copy what you need before handing work to a goroutine.",[444,1523,1525],{"id":1524},"use-the-same-logger-for-the-proxy","Use the same logger for the proxy",[178,1527,1528,1531],{},[182,1529,1530],{},"GoTrueConfig"," takes the same interface, so proxy traffic and your own routes land in one stream:",[187,1533,1535],{"className":189,"code":1534,"language":191,"meta":192,"style":192},"audit := auth.NewSlogAuditLogger(logger)\n\ngt := auth.NewGoTrue(auth.GoTrueConfig{\n    InternalURL: \"http:\u002F\u002Fgotrue:9999\",\n    JWTSecret:   secret,\n    AuditLogger: audit,\n})\n\napp := ezz.New().Auth(gt.AuthProvider().WithAuditLogger(audit))\ngt.Mount(app, \"\u002Fauth\")\n",[182,1536,1537,1556,1560,1584,1601,1613,1625,1629,1633,1671],{"__ignoreMap":192},[196,1538,1539,1542,1544,1546,1548,1550,1552,1554],{"class":198,"line":199},[196,1540,1541],{"class":457},"audit ",[196,1543,461],{"class":202},[196,1545,510],{"class":457},[196,1547,233],{"class":202},[196,1549,538],{"class":219},[196,1551,223],{"class":202},[196,1553,543],{"class":457},[196,1555,247],{"class":202},[196,1557,1558],{"class":198,"line":216},[196,1559,500],{"emptyLinePlaceholder":499},[196,1561,1562,1565,1567,1569,1571,1574,1576,1578,1580,1582],{"class":198,"line":250},[196,1563,1564],{"class":457},"gt ",[196,1566,461],{"class":202},[196,1568,510],{"class":457},[196,1570,233],{"class":202},[196,1572,1573],{"class":219},"NewGoTrue",[196,1575,223],{"class":202},[196,1577,533],{"class":206},[196,1579,233],{"class":202},[196,1581,1530],{"class":206},[196,1583,873],{"class":202},[196,1585,1586,1589,1591,1593,1596,1598],{"class":198,"line":299},[196,1587,1588],{"class":457},"    InternalURL",[196,1590,886],{"class":202},[196,1592,828],{"class":202},[196,1594,1595],{"class":717},"http:\u002F\u002Fgotrue:9999",[196,1597,714],{"class":202},[196,1599,1600],{"class":202},",\n",[196,1602,1603,1606,1608,1611],{"class":198,"line":349},[196,1604,1605],{"class":457},"    JWTSecret",[196,1607,886],{"class":202},[196,1609,1610],{"class":457},"   secret",[196,1612,1600],{"class":202},[196,1614,1615,1618,1620,1623],{"class":198,"line":394},[196,1616,1617],{"class":457},"    AuditLogger",[196,1619,886],{"class":202},[196,1621,1622],{"class":457}," audit",[196,1624,1600],{"class":202},[196,1626,1627],{"class":198,"line":439},[196,1628,1492],{"class":202},[196,1630,1631],{"class":198,"line":935},[196,1632,500],{"emptyLinePlaceholder":499},[196,1634,1635,1637,1639,1641,1643,1645,1647,1649,1651,1654,1656,1659,1661,1664,1666,1669],{"class":198,"line":1050},[196,1636,555],{"class":457},[196,1638,461],{"class":202},[196,1640,560],{"class":457},[196,1642,233],{"class":202},[196,1644,469],{"class":219},[196,1646,567],{"class":202},[196,1648,68],{"class":219},[196,1650,223],{"class":202},[196,1652,1653],{"class":457},"gt",[196,1655,233],{"class":202},[196,1657,1658],{"class":219},"AuthProvider",[196,1660,567],{"class":202},[196,1662,1663],{"class":219},"WithAuditLogger",[196,1665,223],{"class":202},[196,1667,1668],{"class":457},"audit",[196,1670,546],{"class":202},[196,1672,1673,1675,1677,1680,1682,1684,1686,1688,1690,1692],{"class":198,"line":1058},[196,1674,1653],{"class":457},[196,1676,233],{"class":202},[196,1678,1679],{"class":219},"Mount",[196,1681,223],{"class":202},[196,1683,704],{"class":457},[196,1685,238],{"class":202},[196,1687,828],{"class":202},[196,1689,70],{"class":717},[196,1691,714],{"class":202},[196,1693,247],{"class":202},[1695,1696,1697,1698,1701],"note",{},"Audit records from the proxy carry an empty user ID, because the proxy does not decode the token it forwards. Correlate with the request ID, which is present on both the audit record and the ",[182,1699,1700],{},"X-Request-ID"," response header.",[444,1703,1705],{"id":1704},"next-steps","Next steps",[1513,1707,1708,1713],{},[1516,1709,1710,1712],{},[689,1711,691],{"href":106}," for what the proxy records.",[1516,1714,1715,1718],{},[689,1716,1717],{"href":90},"Roles and permissions"," for the decisions worth auditing.",[1720,1721,1722],"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 .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 .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}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}",{"title":192,"searchDepth":216,"depth":216,"links":1724},[1725,1726,1727,1728,1729],{"id":446,"depth":216,"text":447},{"id":609,"depth":216,"text":610},{"id":963,"depth":216,"text":964},{"id":1524,"depth":216,"text":1525},{"id":1704,"depth":216,"text":1705},"Record authentication and authorization events through a pluggable audit logger.","md",null,{},{"icon":97},{"title":94,"description":1730},"gCsGHvC84mhkbV2e07YQ9VisWrCxQ7z3NkGlVECO2uk",[1738,1740],{"title":89,"path":90,"stem":91,"description":1739,"icon":92,"children":-1},"Load a caller's roles and permissions from your own data with an authorization resolver, and cache the result.",{"title":105,"path":106,"stem":107,"description":1741,"icon":108,"children":-1},"Keep GoTrue on a private network and expose typed, documented \u002Fauth\u002F* routes through your own service.",1784970048009]