[{"data":1,"prerenderedAt":2214},["ShallowReactive",2],{"/projects/laye":3,"/projects/laye--related":978},{"id":4,"title":5,"body":6,"date":961,"description":962,"extension":963,"image_url":964,"link":59,"meta":965,"navigation":158,"path":967,"seo":968,"stem":969,"tags":970,"__hash__":977},"projects/projects/laye.md","Laye",{"type":7,"value":8,"toc":951},"minimark",[9,15,19,32,37,41,83,87,92,110,113,117,122,378,382,386,540,543,547,581,697,700,704,730,810,814,818,928,947],[10,11],"content-time-detail",{"className":12,"date":14},[13],"mb-2","2026-05-13",[16,17,5],"h1",{"id":18},"laye",[20,21,22,23,27,28,31],"p",{},"A framework-agnostic role and permission based access control library for Rust. Implement the ",[24,25,26],"code",{},"Principal"," trait on your auth type, build composable ",[24,29,30],{},"AccessPolicy"," rules, and wire the provided middleware into actix-web or tower/axum.",[33,34],"div",{"className":35},[36],"mb-3",[38,39],"tag-list",{":tags":40},"[\"Rust\", \"RBAC\", \"Access Control\", \"Library\", \"Actix Web\", \"Axum\"]",[33,42,50,70],{"className":43},[44,45,46,47,48,49],"flex","flex-wrap","items-center","gap-3","mt-4","mb-6",[51,52,63,66],"a",{"className":53,"href":59,"rel":60,"target":62},[54,55,56,57,58],"button","button-xs","xs:button-sm","sm:button-base","button-amberwood","https://github.com/dev-davexoyinbo/laye",[61],"nofollow","\\_blank",[20,64,65],{},"View on GitHub",[67,68],"content-icon",{"icon":69},"mdi:external-link",[51,71,78,81],{"className":72,"href":76,"rel":77,"target":62},[54,55,56,57,73,74,75],"button-plain","border","border-charcoal-800","https://crates.io/crates/laye",[61],[20,79,80],{},"crates.io",[67,82],{"icon":69},[33,84],{"className":85},[86],"mb-10",[88,89,91],"h2",{"id":90},"overview","Overview",[33,93,96],{"className":94},[95],"mb-14",[20,97,98,99,101,102,105,106,109],{},"Laye handles the authorization layer without dictating how you authenticate. You implement the ",[24,100,26],{}," trait on whatever auth struct your application already uses, giving laye access to roles, permissions, and authentication state. From there you compose policies using ",[24,103,104],{},"require_all"," (AND) and ",[24,107,108],{},"require_any"," (OR), and attach the middleware to any route. Unauthenticated requests get a 401; authenticated but unauthorized requests get a 403. The library ships with optional feature flags for actix-web and tower so you only pull in what you need.",[33,111],{"className":112},[86],[88,114,116],{"id":115},"usage","Usage",[118,119,121],"h3",{"id":120},"implement-principal","Implement Principal",[123,124,129],"pre",{"className":125,"code":126,"language":127,"meta":128,"style":128},"language-rust shiki shiki-themes github-dark","use laye::Principal;\n\n#[derive(Clone)]\nstruct MyUser {\n    roles: Vec\u003CString>,\n    permissions: Vec\u003CString>,\n    authenticated: bool,\n}\n\nimpl Principal for MyUser {\n    fn roles(&self) -> &[String] { &self.roles }\n    fn permissions(&self) -> &[String] { &self.permissions }\n    fn is_authenticated(&self) -> bool { self.authenticated }\n}\n","rust","",[24,130,131,153,160,172,184,205,221,235,241,246,262,308,343,373],{"__ignoreMap":128},[132,133,136,140,144,147,149],"span",{"class":134,"line":135},"line",1,[132,137,139],{"class":138},"snl16","use",[132,141,143],{"class":142},"svObZ"," laye",[132,145,146],{"class":138},"::",[132,148,26],{"class":142},[132,150,152],{"class":151},"s95oV",";\n",[132,154,156],{"class":134,"line":155},2,[132,157,159],{"emptyLinePlaceholder":158},true,"\n",[132,161,163,166,169],{"class":134,"line":162},3,[132,164,165],{"class":151},"#[derive(",[132,167,168],{"class":142},"Clone",[132,170,171],{"class":151},")]\n",[132,173,175,178,181],{"class":134,"line":174},4,[132,176,177],{"class":138},"struct",[132,179,180],{"class":142}," MyUser",[132,182,183],{"class":151}," {\n",[132,185,187,190,193,196,199,202],{"class":134,"line":186},5,[132,188,189],{"class":151},"    roles",[132,191,192],{"class":138},":",[132,194,195],{"class":142}," Vec",[132,197,198],{"class":151},"\u003C",[132,200,201],{"class":142},"String",[132,203,204],{"class":151},">,\n",[132,206,208,211,213,215,217,219],{"class":134,"line":207},6,[132,209,210],{"class":151},"    permissions",[132,212,192],{"class":138},[132,214,195],{"class":142},[132,216,198],{"class":151},[132,218,201],{"class":142},[132,220,204],{"class":151},[132,222,224,227,229,232],{"class":134,"line":223},7,[132,225,226],{"class":151},"    authenticated",[132,228,192],{"class":138},[132,230,231],{"class":142}," bool",[132,233,234],{"class":151},",\n",[132,236,238],{"class":134,"line":237},8,[132,239,240],{"class":151},"}\n",[132,242,244],{"class":134,"line":243},9,[132,245,159],{"emptyLinePlaceholder":158},[132,247,249,252,255,258,260],{"class":134,"line":248},10,[132,250,251],{"class":138},"impl",[132,253,254],{"class":142}," Principal",[132,256,257],{"class":138}," for",[132,259,180],{"class":142},[132,261,183],{"class":151},[132,263,265,268,271,274,277,281,284,287,290,293,295,298,300,302,305],{"class":134,"line":264},11,[132,266,267],{"class":138},"    fn",[132,269,270],{"class":142}," roles",[132,272,273],{"class":151},"(",[132,275,276],{"class":138},"&",[132,278,280],{"class":279},"sDLfK","self",[132,282,283],{"class":151},") ",[132,285,286],{"class":138},"->",[132,288,289],{"class":138}," &",[132,291,292],{"class":151},"[",[132,294,201],{"class":142},[132,296,297],{"class":151},"] { ",[132,299,276],{"class":138},[132,301,280],{"class":279},[132,303,304],{"class":138},".",[132,306,307],{"class":151},"roles }\n",[132,309,311,313,316,318,320,322,324,326,328,330,332,334,336,338,340],{"class":134,"line":310},12,[132,312,267],{"class":138},[132,314,315],{"class":142}," permissions",[132,317,273],{"class":151},[132,319,276],{"class":138},[132,321,280],{"class":279},[132,323,283],{"class":151},[132,325,286],{"class":138},[132,327,289],{"class":138},[132,329,292],{"class":151},[132,331,201],{"class":142},[132,333,297],{"class":151},[132,335,276],{"class":138},[132,337,280],{"class":279},[132,339,304],{"class":138},[132,341,342],{"class":151},"permissions }\n",[132,344,346,348,351,353,355,357,359,361,363,366,368,370],{"class":134,"line":345},13,[132,347,267],{"class":138},[132,349,350],{"class":142}," is_authenticated",[132,352,273],{"class":151},[132,354,276],{"class":138},[132,356,280],{"class":279},[132,358,283],{"class":151},[132,360,286],{"class":138},[132,362,231],{"class":142},[132,364,365],{"class":151}," { ",[132,367,280],{"class":279},[132,369,304],{"class":138},[132,371,372],{"class":151},"authenticated }\n",[132,374,376],{"class":134,"line":375},14,[132,377,240],{"class":151},[33,379],{"className":380},[381],"mb-8",[118,383,385],{"id":384},"define-a-policy","Define a Policy",[123,387,389],{"className":125,"code":388,"language":127,"meta":128,"style":128},"use laye::{AccessPolicy, AccessRule};\n\nlet policy = AccessPolicy::require_all()\n    .add_rule(AccessRule::Authenticated)\n    .add_policy(\n        AccessPolicy::require_any()\n            .add_rule(AccessRule::Role(\"admin\".into()))\n            .add_rule(AccessRule::Role(\"editor\".into())),\n    );\n",[24,390,391,413,417,438,458,468,479,509,535],{"__ignoreMap":128},[132,392,393,395,397,399,402,404,407,410],{"class":134,"line":135},[132,394,139],{"class":138},[132,396,143],{"class":142},[132,398,146],{"class":138},[132,400,401],{"class":151},"{",[132,403,30],{"class":142},[132,405,406],{"class":151},", ",[132,408,409],{"class":142},"AccessRule",[132,411,412],{"class":151},"};\n",[132,414,415],{"class":134,"line":155},[132,416,159],{"emptyLinePlaceholder":158},[132,418,419,422,425,428,431,433,435],{"class":134,"line":162},[132,420,421],{"class":138},"let",[132,423,424],{"class":151}," policy ",[132,426,427],{"class":138},"=",[132,429,430],{"class":142}," AccessPolicy",[132,432,146],{"class":138},[132,434,104],{"class":142},[132,436,437],{"class":151},"()\n",[132,439,440,443,446,448,450,452,455],{"class":134,"line":174},[132,441,442],{"class":138},"    .",[132,444,445],{"class":142},"add_rule",[132,447,273],{"class":151},[132,449,409],{"class":142},[132,451,146],{"class":138},[132,453,454],{"class":142},"Authenticated",[132,456,457],{"class":151},")\n",[132,459,460,462,465],{"class":134,"line":186},[132,461,442],{"class":138},[132,463,464],{"class":142},"add_policy",[132,466,467],{"class":151},"(\n",[132,469,470,473,475,477],{"class":134,"line":207},[132,471,472],{"class":142},"        AccessPolicy",[132,474,146],{"class":138},[132,476,108],{"class":142},[132,478,437],{"class":151},[132,480,481,484,486,488,490,492,495,497,501,503,506],{"class":134,"line":223},[132,482,483],{"class":138},"            .",[132,485,445],{"class":142},[132,487,273],{"class":151},[132,489,409],{"class":142},[132,491,146],{"class":138},[132,493,494],{"class":142},"Role",[132,496,273],{"class":151},[132,498,500],{"class":499},"sU2Wk","\"admin\"",[132,502,304],{"class":138},[132,504,505],{"class":142},"into",[132,507,508],{"class":151},"()))\n",[132,510,511,513,515,517,519,521,523,525,528,530,532],{"class":134,"line":237},[132,512,483],{"class":138},[132,514,445],{"class":142},[132,516,273],{"class":151},[132,518,409],{"class":142},[132,520,146],{"class":138},[132,522,494],{"class":142},[132,524,273],{"class":151},[132,526,527],{"class":499},"\"editor\"",[132,529,304],{"class":138},[132,531,505],{"class":142},[132,533,534],{"class":151},"())),\n",[132,536,537],{"class":134,"line":243},[132,538,539],{"class":151},"    );\n",[33,541],{"className":542},[381],[118,544,546],{"id":545},"wire-into-actix-web","Wire into actix-web",[123,548,552],{"className":549,"code":550,"language":551,"meta":128,"style":128},"language-toml shiki shiki-themes github-dark","[dependencies]\nlaye = { version = \"0.1\", features = [\"actix-web\"] }\n","toml",[24,553,554,564],{"__ignoreMap":128},[132,555,556,558,561],{"class":134,"line":135},[132,557,292],{"class":151},[132,559,560],{"class":142},"dependencies",[132,562,563],{"class":151},"]\n",[132,565,566,569,572,575,578],{"class":134,"line":155},[132,567,568],{"class":151},"laye = { version = ",[132,570,571],{"class":499},"\"0.1\"",[132,573,574],{"class":151},", features = [",[132,576,577],{"class":499},"\"actix-web\"",[132,579,580],{"class":151},"] }\n",[123,582,584],{"className":125,"code":583,"language":127,"meta":128,"style":128},"use laye::actix::PolicyMiddlewareFactory;\n\nApp::new()\n    .service(\n        web::resource(\"/admin\")\n            .wrap(PolicyMiddlewareFactory::new(policy))\n            .route(web::get().to(handler)),\n    )\n",[24,585,586,604,608,620,629,646,664,692],{"__ignoreMap":128},[132,587,588,590,592,594,597,599,602],{"class":134,"line":135},[132,589,139],{"class":138},[132,591,143],{"class":142},[132,593,146],{"class":138},[132,595,596],{"class":142},"actix",[132,598,146],{"class":138},[132,600,601],{"class":142},"PolicyMiddlewareFactory",[132,603,152],{"class":151},[132,605,606],{"class":134,"line":155},[132,607,159],{"emptyLinePlaceholder":158},[132,609,610,613,615,618],{"class":134,"line":162},[132,611,612],{"class":142},"App",[132,614,146],{"class":138},[132,616,617],{"class":142},"new",[132,619,437],{"class":151},[132,621,622,624,627],{"class":134,"line":174},[132,623,442],{"class":138},[132,625,626],{"class":142},"service",[132,628,467],{"class":151},[132,630,631,634,636,639,641,644],{"class":134,"line":186},[132,632,633],{"class":142},"        web",[132,635,146],{"class":138},[132,637,638],{"class":142},"resource",[132,640,273],{"class":151},[132,642,643],{"class":499},"\"/admin\"",[132,645,457],{"class":151},[132,647,648,650,653,655,657,659,661],{"class":134,"line":207},[132,649,483],{"class":138},[132,651,652],{"class":142},"wrap",[132,654,273],{"class":151},[132,656,601],{"class":142},[132,658,146],{"class":138},[132,660,617],{"class":142},[132,662,663],{"class":151},"(policy))\n",[132,665,666,668,671,673,676,678,681,684,686,689],{"class":134,"line":223},[132,667,483],{"class":138},[132,669,670],{"class":142},"route",[132,672,273],{"class":151},[132,674,675],{"class":142},"web",[132,677,146],{"class":138},[132,679,680],{"class":142},"get",[132,682,683],{"class":151},"()",[132,685,304],{"class":138},[132,687,688],{"class":142},"to",[132,690,691],{"class":151},"(handler)),\n",[132,693,694],{"class":134,"line":237},[132,695,696],{"class":151},"    )\n",[33,698],{"className":699},[381],[118,701,703],{"id":702},"wire-into-axumtower","Wire into axum/tower",[123,705,707],{"className":549,"code":706,"language":551,"meta":128,"style":128},"[dependencies]\nlaye = { version = \"0.1\", features = [\"tower\"] }\n",[24,708,709,717],{"__ignoreMap":128},[132,710,711,713,715],{"class":134,"line":135},[132,712,292],{"class":151},[132,714,560],{"class":142},[132,716,563],{"class":151},[132,718,719,721,723,725,728],{"class":134,"line":155},[132,720,568],{"class":151},[132,722,571],{"class":499},[132,724,574],{"class":151},[132,726,727],{"class":499},"\"tower\"",[132,729,580],{"class":151},[123,731,733],{"className":125,"code":732,"language":127,"meta":128,"style":128},"use laye::tower::AccessControlLayer;\n\nlet app = Router::new()\n    .route(\"/admin\", get(handler))\n    .layer(AccessControlLayer::new(policy));\n",[24,734,735,753,757,775,792],{"__ignoreMap":128},[132,736,737,739,741,743,746,748,751],{"class":134,"line":135},[132,738,139],{"class":138},[132,740,143],{"class":142},[132,742,146],{"class":138},[132,744,745],{"class":142},"tower",[132,747,146],{"class":138},[132,749,750],{"class":142},"AccessControlLayer",[132,752,152],{"class":151},[132,754,755],{"class":134,"line":155},[132,756,159],{"emptyLinePlaceholder":158},[132,758,759,761,764,766,769,771,773],{"class":134,"line":162},[132,760,421],{"class":138},[132,762,763],{"class":151}," app ",[132,765,427],{"class":138},[132,767,768],{"class":142}," Router",[132,770,146],{"class":138},[132,772,617],{"class":142},[132,774,437],{"class":151},[132,776,777,779,781,783,785,787,789],{"class":134,"line":174},[132,778,442],{"class":138},[132,780,670],{"class":142},[132,782,273],{"class":151},[132,784,643],{"class":499},[132,786,406],{"class":151},[132,788,680],{"class":142},[132,790,791],{"class":151},"(handler))\n",[132,793,794,796,799,801,803,805,807],{"class":134,"line":186},[132,795,442],{"class":138},[132,797,798],{"class":142},"layer",[132,800,273],{"class":151},[132,802,750],{"class":142},[132,804,146],{"class":138},[132,806,617],{"class":142},[132,808,809],{"class":151},"(policy));\n",[33,811],{"className":812},[813],"mb-12",[88,815,817],{"id":816},"highlights","Highlights",[33,819,826,857,882,903],{"className":820},[821,822,823,824,825],"grid","grid-cols-2","sm:grid-cols-4","gap-4","md:gap-6",[827,828,831,835],"content-card",{"className":829},[830],"col-span-2",[118,832,834],{"id":833},"policy-dsl","Policy DSL",[33,836,840],{"className":837},[838,839],"text-xs","text-dune-600",[841,842,843,849,854],"ul",{},[844,845,846,848],"li",{},[24,847,104],{}," for AND logic across rules and nested policies",[844,850,851,853],{},[24,852,108],{}," for OR logic",[844,855,856],{},"Unlimited nesting depth for complex access scenarios",[827,858,860,864],{"className":859},[830],[118,861,863],{"id":862},"framework-support","Framework Support",[33,865,867],{"className":866},[838,839],[841,868,869,874,879],{},[844,870,871,872],{},"actix-web middleware via ",[24,873,601],{},[844,875,876,877],{},"tower/axum middleware via ",[24,878,750],{},[844,880,881],{},"Feature flags keep unused framework code out of your binary",[827,883,885,889],{"className":884},[830],[118,886,888],{"id":887},"http-responses","HTTP Responses",[33,890,892],{"className":891},[838,839],[841,893,894,897,900],{},[844,895,896],{},"401 Unauthorized for requests with no authenticated principal",[844,898,899],{},"403 Forbidden for authenticated but policy-failing requests",[844,901,902],{},"No manual status code handling needed in your handlers",[827,904,906,910],{"className":905},[830],[118,907,909],{"id":908},"ergonomics","Ergonomics",[33,911,913],{"className":912},[838,839],[841,914,915,918,921],{},[844,916,917],{},"Zero coupling to your auth implementation",[844,919,920],{},"Bring your own user struct, roles, and permissions",[844,922,923,924,927],{},"Minimal install: ",[24,925,926],{},"laye = \"0.1\""," for the core with no framework overhead",[33,929,932,940],{"className":930},[44,45,46,47,931],"mt-8",[51,933,936,938],{"className":934,"href":59,"rel":935,"target":62},[54,55,56,57,58],[61],[20,937,65],{},[67,939],{"icon":69},[51,941,944],{"className":942,"href":943},[54,55,56,57,73,74,75],"/projects",[20,945,946],{},"Back to Projects",[948,949,950],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}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 pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}",{"title":128,"searchDepth":155,"depth":155,"links":952},[953,954,960],{"id":90,"depth":155,"text":91},{"id":115,"depth":155,"text":116,"children":955},[956,957,958,959],{"id":120,"depth":162,"text":121},{"id":384,"depth":162,"text":385},{"id":545,"depth":162,"text":546},{"id":702,"depth":162,"text":703},{"id":816,"depth":155,"text":817},"2026-05-13T00:00:00.000Z","Framework-agnostic RBAC library for Rust with composable AccessPolicy rules and plug-and-play middleware for actix-web and tower/axum.","md","/projects/laye/main.png",{"license":966},"MIT","/projects/laye",{"title":5,"description":962},"projects/laye",[971,972,973,974,975,976],"Rust","RBAC","Access Control","Library","Actix Web","Axum","bIyqL-anx4_BYI50IpLC52ELdMnJH2SWaxgB_mKTehQ",[979,1524,1745],{"id":980,"title":981,"body":982,"date":1510,"description":1511,"extension":963,"image_url":1041,"link":1006,"meta":1512,"navigation":158,"path":1514,"seo":1515,"stem":1516,"tags":1517,"__hash__":1523},"projects/projects/payaza-web-sdk.md","Payaza Web SDK",{"type":7,"value":983,"toc":1502},[984,988,991,994,997,1000,1034,1042,1044,1051,1053,1146,1149,1153,1157,1457,1460,1464,1483,1499],[10,985],{"className":986,"date":987},[13],"2021-10-01",[16,989,981],{"id":990},"payaza-web-sdk",[20,992,993],{},"Note: This was built as part of my work at Payaza Africa. I focused on the SDK design, integration ergonomics, and developer documentation—not the entire Payaza product.",[33,995],{"className":996},[36],[38,998],{":tags":999},"[\"SDK\", \"JavaScript\", \"Payments\", \"Nuxt.js\", \"Web\"]",[33,1001,1003,1014,1024],{"className":1002},[44,45,46,47,48,49],[51,1004,1009,1012],{"className":1005,"href":1006,"rel":1007,"target":1008},[54,55,56,57,58],"https://www.npmjs.com/package/@payaza/web-sdk",[61],"_blank",[20,1010,1011],{},"NPM Package",[67,1013],{"icon":69},[51,1015,1019,1022],{"className":1016,"href":1017,"rel":1018,"target":1008},[54,55,56,57,73,74,75],"https://payaza.africa/",[61],[20,1020,1021],{},"Visit Payaza",[67,1023],{"icon":69},[51,1025,1029,1032],{"className":1026,"href":1027,"rel":1028,"target":1008},[54,55,56,57,73,74,75],"https://payaza.africa/checkout/",[61],[20,1030,1031],{},"Try Checkout",[67,1033],{"icon":69},[1035,1036],"content-main-image",{"alt":1037,"className":1038,"image":1041},"payaza web sdk",[1039,1040],"my-16","h-[30vh]","/projects/payaza-web-sdk/main.png",[88,1043,91],{"id":90},[1045,1046,1048],"content-overview",{"className":1047},[95],[20,1049,1050],{},"The Payaza Web SDK provides a straightforward way to embed Payaza checkout into web apps, handling configuration, initialization, and event hooks with a clean API. Built during my tenure at Payaza to improve developer onboarding and accelerate integrations.\nMy contributions included building the embeddable Vue checkout application loaded by the SDK, implementing a Node.js reverse proxy that adds real‑time communication via Socket.IO to the backend, and designing/publishing the @payaza/web-sdk NPM package.",[88,1052,817],{"id":816},[33,1054,1056,1080,1104,1125],{"className":1055},[821,822,823,824,825],[827,1057,1059,1063],{"className":1058},[830],[118,1060,1062],{"id":1061},"sdk-features","SDK Features",[33,1064,1066],{"className":1065},[838,839],[841,1067,1068,1071,1074,1077],{},[844,1069,1070],{},"Lightweight, simple initialization",[844,1072,1073],{},"Configurable callbacks and events",[844,1075,1076],{},"Seamless checkout invocation",[844,1078,1079],{},"Clear error states and typed payloads",[827,1081,1083,1087],{"className":1082},[830],[118,1084,1086],{"id":1085},"developer-experience","Developer Experience",[33,1088,1090],{"className":1089},[838,839],[841,1091,1092,1095,1098,1101],{},[844,1093,1094],{},"Usage guides and examples",[844,1096,1097],{},"Minimal setup steps",[844,1099,1100],{},"Works with modern frameworks (Nuxt/Vue, React)",[844,1102,1103],{},"Focus on integration ergonomics",[827,1105,1107,1111],{"className":1106},[830],[118,1108,1110],{"id":1109},"my-contribution","My Contribution",[33,1112,1114],{"className":1113},[838,839],[841,1115,1116,1119,1122],{},[844,1117,1118],{},"Built the embeddable checkout application (Vue) loaded by the SDK package",[844,1120,1121],{},"Implemented a Node.js reverse proxy to add real‑time communication via Socket.IO",[844,1123,1124],{},"Designed and published the @payaza/web-sdk NPM package",[827,1126,1128,1132],{"className":1127},[830],[118,1129,1131],{"id":1130},"outcome","Outcome",[33,1133,1135],{"className":1134},[838,839],[841,1136,1137,1140,1143],{},[844,1138,1139],{},"Faster onboarding for partners",[844,1141,1142],{},"Reduced integration friction",[844,1144,1145],{},"Consistent checkout experiences across apps",[33,1147],{"className":1148},[95],[88,1150,1152],{"id":1151},"code-snippets","Code Snippets",[118,1154,1156],{"id":1155},"initialize-and-invoke-checkout","Initialize and Invoke Checkout",[123,1158,1163],{"className":1159,"code":1160,"filename":1161,"language":1162,"meta":128,"style":128},"language-html shiki shiki-themes github-dark","\u003Cscript src=\"https://cdn.payaza.africa/sdk/web.min.js\">\u003C/script>\n\u003Cscript>\n  document.addEventListener('DOMContentLoaded', function () {\n    const payaza = PayazaWebSDK.init({\n      publicKey: 'pk_test_XXXXXXXXXXXXXXXX',\n      customer: { email: 'user@example.com' },\n      amount: 5000, // in NGN kobo\n      currency: 'NGN',\n      onSuccess: (ref) => console.log('Success:', ref),\n      onClose: () => console.log('Closed'),\n      onError: (err) => console.error('Error:', err),\n    });\n\n    document.getElementById('pay-button').addEventListener('click', () => {\n      payaza.checkout();\n    });\n  });\n\u003C/script>\n\u003Cbutton id=\"pay-button\">Pay with Payaza\u003C/button>\n","index.html","html",[24,1164,1165,1189,1197,1218,1238,1248,1259,1273,1283,1314,1336,1363,1368,1372,1402,1414,1419,1425,1435],{"__ignoreMap":128},[132,1166,1167,1169,1173,1176,1178,1181,1184,1186],{"class":134,"line":135},[132,1168,198],{"class":151},[132,1170,1172],{"class":1171},"s4JwU","script",[132,1174,1175],{"class":142}," src",[132,1177,427],{"class":151},[132,1179,1180],{"class":499},"\"https://cdn.payaza.africa/sdk/web.min.js\"",[132,1182,1183],{"class":151},">\u003C/",[132,1185,1172],{"class":1171},[132,1187,1188],{"class":151},">\n",[132,1190,1191,1193,1195],{"class":134,"line":155},[132,1192,198],{"class":151},[132,1194,1172],{"class":1171},[132,1196,1188],{"class":151},[132,1198,1199,1202,1205,1207,1210,1212,1215],{"class":134,"line":162},[132,1200,1201],{"class":151},"  document.",[132,1203,1204],{"class":142},"addEventListener",[132,1206,273],{"class":151},[132,1208,1209],{"class":499},"'DOMContentLoaded'",[132,1211,406],{"class":151},[132,1213,1214],{"class":138},"function",[132,1216,1217],{"class":151}," () {\n",[132,1219,1220,1223,1226,1229,1232,1235],{"class":134,"line":174},[132,1221,1222],{"class":138},"    const",[132,1224,1225],{"class":279}," payaza",[132,1227,1228],{"class":138}," =",[132,1230,1231],{"class":151}," PayazaWebSDK.",[132,1233,1234],{"class":142},"init",[132,1236,1237],{"class":151},"({\n",[132,1239,1240,1243,1246],{"class":134,"line":186},[132,1241,1242],{"class":151},"      publicKey: ",[132,1244,1245],{"class":499},"'pk_test_XXXXXXXXXXXXXXXX'",[132,1247,234],{"class":151},[132,1249,1250,1253,1256],{"class":134,"line":207},[132,1251,1252],{"class":151},"      customer: { email: ",[132,1254,1255],{"class":499},"'user@example.com'",[132,1257,1258],{"class":151}," },\n",[132,1260,1261,1264,1267,1269],{"class":134,"line":223},[132,1262,1263],{"class":151},"      amount: ",[132,1265,1266],{"class":279},"5000",[132,1268,406],{"class":151},[132,1270,1272],{"class":1271},"sAwPA","// in NGN kobo\n",[132,1274,1275,1278,1281],{"class":134,"line":237},[132,1276,1277],{"class":151},"      currency: ",[132,1279,1280],{"class":499},"'NGN'",[132,1282,234],{"class":151},[132,1284,1285,1288,1291,1295,1297,1300,1303,1306,1308,1311],{"class":134,"line":243},[132,1286,1287],{"class":142},"      onSuccess",[132,1289,1290],{"class":151},": (",[132,1292,1294],{"class":1293},"s9osk","ref",[132,1296,283],{"class":151},[132,1298,1299],{"class":138},"=>",[132,1301,1302],{"class":151}," console.",[132,1304,1305],{"class":142},"log",[132,1307,273],{"class":151},[132,1309,1310],{"class":499},"'Success:'",[132,1312,1313],{"class":151},", ref),\n",[132,1315,1316,1319,1322,1324,1326,1328,1330,1333],{"class":134,"line":248},[132,1317,1318],{"class":142},"      onClose",[132,1320,1321],{"class":151},": () ",[132,1323,1299],{"class":138},[132,1325,1302],{"class":151},[132,1327,1305],{"class":142},[132,1329,273],{"class":151},[132,1331,1332],{"class":499},"'Closed'",[132,1334,1335],{"class":151},"),\n",[132,1337,1338,1341,1343,1346,1348,1350,1352,1355,1357,1360],{"class":134,"line":264},[132,1339,1340],{"class":142},"      onError",[132,1342,1290],{"class":151},[132,1344,1345],{"class":1293},"err",[132,1347,283],{"class":151},[132,1349,1299],{"class":138},[132,1351,1302],{"class":151},[132,1353,1354],{"class":142},"error",[132,1356,273],{"class":151},[132,1358,1359],{"class":499},"'Error:'",[132,1361,1362],{"class":151},", err),\n",[132,1364,1365],{"class":134,"line":310},[132,1366,1367],{"class":151},"    });\n",[132,1369,1370],{"class":134,"line":345},[132,1371,159],{"emptyLinePlaceholder":158},[132,1373,1374,1377,1380,1382,1385,1388,1390,1392,1395,1398,1400],{"class":134,"line":375},[132,1375,1376],{"class":151},"    document.",[132,1378,1379],{"class":142},"getElementById",[132,1381,273],{"class":151},[132,1383,1384],{"class":499},"'pay-button'",[132,1386,1387],{"class":151},").",[132,1389,1204],{"class":142},[132,1391,273],{"class":151},[132,1393,1394],{"class":499},"'click'",[132,1396,1397],{"class":151},", () ",[132,1399,1299],{"class":138},[132,1401,183],{"class":151},[132,1403,1405,1408,1411],{"class":134,"line":1404},15,[132,1406,1407],{"class":151},"      payaza.",[132,1409,1410],{"class":142},"checkout",[132,1412,1413],{"class":151},"();\n",[132,1415,1417],{"class":134,"line":1416},16,[132,1418,1367],{"class":151},[132,1420,1422],{"class":134,"line":1421},17,[132,1423,1424],{"class":151},"  });\n",[132,1426,1428,1431,1433],{"class":134,"line":1427},18,[132,1429,1430],{"class":151},"\u003C/",[132,1432,1172],{"class":1171},[132,1434,1188],{"class":151},[132,1436,1438,1440,1442,1445,1447,1450,1453,1455],{"class":134,"line":1437},19,[132,1439,198],{"class":151},[132,1441,54],{"class":1171},[132,1443,1444],{"class":142}," id",[132,1446,427],{"class":151},[132,1448,1449],{"class":499},"\"pay-button\"",[132,1451,1452],{"class":151},">Pay with Payaza\u003C/",[132,1454,54],{"class":1171},[132,1456,1188],{"class":151},[33,1458],{"className":1459},[95],[88,1461,1463],{"id":1462},"gallery","Gallery",[33,1465,1469,1475,1479],{"className":1466},[821,1467,1468,824,825,381],"grid-cols-1","xs:grid-cols-[repeat(auto-fill,minmax(280px,1fr))]",[1470,1471],"content-image",{"className":1472,"image":1474},[1473],"h-[220px]","/projects/payaza-web-sdk/1.png",[1470,1476],{"className":1477,"image":1478},[1473],"/projects/payaza-web-sdk/2.png",[1470,1480],{"className":1481,"image":1482},[1473],"/projects/payaza-web-sdk/3.png",[33,1484,1486,1494],{"className":1485},[44,45,46,47],[51,1487,1490,1492],{"className":1488,"href":1006,"rel":1489,"target":1008},[54,55,56,57,58],[61],[20,1491,1011],{},[67,1493],{"icon":69},[51,1495,1497],{"className":1496,"href":943},[54,55,56,57,73,74,75],[20,1498,946],{},[948,1500,1501],{},"html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .s4JwU, html code.shiki .s4JwU{--shiki-default:#85E89D}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}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);}",{"title":128,"searchDepth":155,"depth":155,"links":1503},[1504,1505,1506,1509],{"id":90,"depth":155,"text":91},{"id":816,"depth":155,"text":817},{"id":1151,"depth":155,"text":1152,"children":1507},[1508],{"id":1155,"depth":162,"text":1156},{"id":1462,"depth":155,"text":1463},"2021-10-01T00:00:00.000Z","A JavaScript Web SDK that simplifies integrating Payaza checkout on web applications. Built as part of my role at Payaza Africa.",{"license":1513},null,"/projects/payaza-web-sdk",{"title":981,"description":1511},"projects/payaza-web-sdk",[1518,1519,1520,1521,1522],"SDK","JavaScript","Payments","Nuxt.js","Web","ZkzkMQ4iP0O_2gg9fqF5BP-yVh2gOR6pcvUnqk2Gces",{"id":1525,"title":1526,"body":1527,"date":1732,"description":1733,"extension":963,"image_url":1734,"link":1551,"meta":1735,"navigation":158,"path":1736,"seo":1737,"stem":1738,"tags":1739,"__hash__":1744},"projects/projects/country-and-currency-package.md","Country flags and currency package",{"type":7,"value":1528,"toc":1727},[1529,1533,1536,1539,1542,1545,1567,1569,1575,1577,1622,1626,1700,1724],[10,1530],{"className":1531,"date":1532},[13],"2024-12-04",[16,1534,1526],{"id":1535},"country-flags-and-currency-package",[20,1537,1538],{},"Type-safe country and currency data with handy helpers for distances and lookups.",[33,1540],{"className":1541},[36],[38,1543],{":tags":1544},"[\"TypeScript\", \"Library\", \"NPM\", \"Geography\", \"Currency\"]",[33,1546,1548,1557],{"className":1547},[44,45,46,47,48,49],[51,1549,1553,1555],{"className":1550,"href":1551,"rel":1552,"target":1008},[54,55,56,57,58],"https://www.npmjs.com/package/@workmate/country-and-currency",[61],[20,1554,1011],{},[67,1556],{"icon":69},[51,1558,1562,1565],{"className":1559,"href":1560,"rel":1561,"target":1008},[54,55,56,57,73,74,75],"https://github.com/work-mate/country-and-currency-ts",[61],[20,1563,1564],{},"GitHub Repo",[67,1566],{"icon":69},[88,1568,91],{"id":90},[1045,1570,1572],{"className":1571},[95],[20,1573,1574],{},"Provides countries, currencies, flags (unicode + SVG), capitals, dial codes, and simple geospatial utilities like distances and closest/farthest country queries. Zero dependencies and ships with TypeScript types.",[88,1576,817],{"id":816},[33,1578,1580,1601],{"className":1579},[821,822,823,824,825],[827,1581,1583,1587],{"className":1582},[830],[118,1584,1586],{"id":1585},"data-types","Data + Types",[33,1588,1590],{"className":1589},[838,839],[841,1591,1592,1595,1598],{},[844,1593,1594],{},"Strong TS types for Country, Currency, and Location",[844,1596,1597],{},"Country flags (unicode + SVG), capitals, dial codes",[844,1599,1600],{},"Handy methods: getCurrencies, getCountries, getCountriesBy, distanceBetweenLocations",[827,1602,1604,1608],{"className":1603},[830],[118,1605,1607],{"id":1606},"zero-dependencies","Zero dependencies",[33,1609,1611],{"className":1610},[838,839],[841,1612,1613,1616,1619],{},[844,1614,1615],{},"Lightweight and tree-shakeable",[844,1617,1618],{},"Works via ESM import or CDN bundle",[844,1620,1621],{},"MIT-licensed",[88,1623,1625],{"id":1624},"minimal-usage","Minimal usage",[123,1627,1631],{"className":1628,"code":1629,"language":1630,"meta":128,"style":128},"language-ts shiki shiki-themes github-dark","import CountryAndCurrency from \"@workmate/country-and-currency\";\n\n// Look up a currency by code\nconst mxn = CountryAndCurrency.getCurrencyBy(\"code\", \"MXN\");\nconsole.log(mxn?.symbol); // $\n","ts",[24,1632,1633,1649,1653,1658,1687],{"__ignoreMap":128},[132,1634,1635,1638,1641,1644,1647],{"class":134,"line":135},[132,1636,1637],{"class":138},"import",[132,1639,1640],{"class":151}," CountryAndCurrency ",[132,1642,1643],{"class":138},"from",[132,1645,1646],{"class":499}," \"@workmate/country-and-currency\"",[132,1648,152],{"class":151},[132,1650,1651],{"class":134,"line":155},[132,1652,159],{"emptyLinePlaceholder":158},[132,1654,1655],{"class":134,"line":162},[132,1656,1657],{"class":1271},"// Look up a currency by code\n",[132,1659,1660,1663,1666,1668,1671,1674,1676,1679,1681,1684],{"class":134,"line":174},[132,1661,1662],{"class":138},"const",[132,1664,1665],{"class":279}," mxn",[132,1667,1228],{"class":138},[132,1669,1670],{"class":151}," CountryAndCurrency.",[132,1672,1673],{"class":142},"getCurrencyBy",[132,1675,273],{"class":151},[132,1677,1678],{"class":499},"\"code\"",[132,1680,406],{"class":151},[132,1682,1683],{"class":499},"\"MXN\"",[132,1685,1686],{"class":151},");\n",[132,1688,1689,1692,1694,1697],{"class":134,"line":186},[132,1690,1691],{"class":151},"console.",[132,1693,1305],{"class":142},[132,1695,1696],{"class":151},"(mxn?.symbol); ",[132,1698,1699],{"class":1271},"// $\n",[33,1701,1703,1711,1719],{"className":1702},[44,45,46,47,931],[51,1704,1707,1709],{"className":1705,"href":1551,"rel":1706,"target":1008},[54,55,56,57,58],[61],[20,1708,1011],{},[67,1710],{"icon":69},[51,1712,1715,1717],{"className":1713,"href":1560,"rel":1714,"target":1008},[54,55,56,57,73,74,75],[61],[20,1716,1564],{},[67,1718],{"icon":69},[51,1720,1722],{"className":1721,"href":943},[54,55,56,57,73,74,75],[20,1723,946],{},[948,1725,1726],{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}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);}",{"title":128,"searchDepth":155,"depth":155,"links":1728},[1729,1730,1731],{"id":90,"depth":155,"text":91},{"id":816,"depth":155,"text":817},{"id":1624,"depth":155,"text":1625},"2024-12-04T00:00:00.000Z","Lightweight TypeScript library for country flags, capitals, dial codes, currencies, and simple location distance utilities.","/projects/country-and-currency-package/main.png",{"license":966},"/projects/country-and-currency-package",{"title":1526,"description":1733},"projects/country-and-currency-package",[1740,974,1741,1742,1743],"TypeScript","NPM","Geography","Currency","ji9kg5Pgmy_OdniFb8dy_Mz1q9PHwp1xb-y1o_gFII4",{"id":1746,"title":1747,"body":1748,"date":2203,"description":2204,"extension":963,"image_url":2205,"link":1772,"meta":2206,"navigation":158,"path":2207,"seo":2208,"stem":2209,"tags":2210,"__hash__":2213},"projects/projects/nuxt-auth-module.md","Auth module for Nuxt server apps",{"type":7,"value":1749,"toc":2198},[1750,1754,1757,1760,1763,1766,1787,1789,1799,1801,1846,1849,1851,2033,2036,2171,2195],[10,1751],{"className":1752,"date":1753},[13],"2025-01-22",[16,1755,1747],{"id":1756},"auth-module-for-nuxt-server-apps",[20,1758,1759],{},"Authentication module for Nuxt 3 apps: Local login, GitHub/Google providers, token handling, route/global middlewares, and a small set of composables.",[33,1761],{"className":1762},[36],[38,1764],{":tags":1765},"[\"TypeScript\", \"Nuxt 3\", \"Auth\", \"Library\", \"NPM\"]",[33,1767,1769,1778],{"className":1768},[44,45,46,47,48,49],[51,1770,1774,1776],{"className":1771,"href":1772,"rel":1773,"target":1008},[54,55,56,57,58],"https://www.npmjs.com/package/@workmate/nuxt-auth",[61],[20,1775,1011],{},[67,1777],{"icon":69},[51,1779,1783,1785],{"className":1780,"href":1781,"rel":1782,"target":1008},[54,55,56,57,73,74,75],"https://github.com/work-mate/nuxt-auth-module",[61],[20,1784,1564],{},[67,1786],{"icon":69},[88,1788,91],{"id":90},[1045,1790,1792],{"className":1791},[95],[20,1793,1794,1795,1798],{},"Supports Local auth (login/logout/user/refresh), GitHub and Google providers, token persistence (cookies), redirects, ",[24,1796,1797],{},"$authFetch"," client, and route/global middlewares. Ships with TypeScript types and works in server apps.",[88,1800,817],{"id":816},[33,1802,1804,1825],{"className":1803},[821,822,823,824,825],[827,1805,1807,1811],{"className":1806},[830],[118,1808,1810],{"id":1809},"providers","Providers",[33,1812,1814],{"className":1813},[838,839],[841,1815,1816,1819,1822],{},[844,1817,1818],{},"Local auth (login, logout, user, refresh)",[844,1820,1821],{},"GitHub & Google OAuth",[844,1823,1824],{},"Configurable endpoints and scopes",[827,1826,1828,1832],{"className":1827},[830],[118,1829,1831],{"id":1830},"middlewares-tokens","Middlewares + Tokens",[33,1833,1835],{"className":1834},[838,839],[841,1836,1837,1840,1843],{},[844,1838,1839],{},"Route and global middlewares (auth, auth-guest)",[844,1841,1842],{},"Cookie-based tokens with max age and token type",[844,1844,1845],{},"Redirects for logged-in/out states",[33,1847],{"className":1848},[381],[88,1850,1625],{"id":1624},[123,1852,1855],{"className":1628,"code":1853,"filename":1854,"language":1630,"meta":128,"style":128},"export default defineNuxtConfig({\n  modules: [\"@workmate/nuxt-auth\"],\n  auth: {\n    apiClient: {\n      baseURL: \"http://localhost:8080/api/v1\", // set to your backend base URL\n    },\n    providers: {\n      local: {\n        endpoints: {\n          signIn: {\n            path: \"/api/auth/login/password\",\n            method: \"POST\",\n            tokenKey: \"token\",\n            refreshTokenKey: \"refresh_token\",\n            body: { principal: \"email_address\", password: \"password\" },\n          },\n          user: { path: \"/api/auth/user\", userKey: \"user\" },\n        },\n      },\n    },\n  },\n});\n","nuxt.config.ts",[24,1856,1857,1870,1881,1886,1891,1904,1909,1914,1919,1924,1929,1939,1949,1959,1969,1985,1990,2006,2011,2016,2021,2027],{"__ignoreMap":128},[132,1858,1859,1862,1865,1868],{"class":134,"line":135},[132,1860,1861],{"class":138},"export",[132,1863,1864],{"class":138}," default",[132,1866,1867],{"class":142}," defineNuxtConfig",[132,1869,1237],{"class":151},[132,1871,1872,1875,1878],{"class":134,"line":155},[132,1873,1874],{"class":151},"  modules: [",[132,1876,1877],{"class":499},"\"@workmate/nuxt-auth\"",[132,1879,1880],{"class":151},"],\n",[132,1882,1883],{"class":134,"line":162},[132,1884,1885],{"class":151},"  auth: {\n",[132,1887,1888],{"class":134,"line":174},[132,1889,1890],{"class":151},"    apiClient: {\n",[132,1892,1893,1896,1899,1901],{"class":134,"line":186},[132,1894,1895],{"class":151},"      baseURL: ",[132,1897,1898],{"class":499},"\"http://localhost:8080/api/v1\"",[132,1900,406],{"class":151},[132,1902,1903],{"class":1271},"// set to your backend base URL\n",[132,1905,1906],{"class":134,"line":207},[132,1907,1908],{"class":151},"    },\n",[132,1910,1911],{"class":134,"line":223},[132,1912,1913],{"class":151},"    providers: {\n",[132,1915,1916],{"class":134,"line":237},[132,1917,1918],{"class":151},"      local: {\n",[132,1920,1921],{"class":134,"line":243},[132,1922,1923],{"class":151},"        endpoints: {\n",[132,1925,1926],{"class":134,"line":248},[132,1927,1928],{"class":151},"          signIn: {\n",[132,1930,1931,1934,1937],{"class":134,"line":264},[132,1932,1933],{"class":151},"            path: ",[132,1935,1936],{"class":499},"\"/api/auth/login/password\"",[132,1938,234],{"class":151},[132,1940,1941,1944,1947],{"class":134,"line":310},[132,1942,1943],{"class":151},"            method: ",[132,1945,1946],{"class":499},"\"POST\"",[132,1948,234],{"class":151},[132,1950,1951,1954,1957],{"class":134,"line":345},[132,1952,1953],{"class":151},"            tokenKey: ",[132,1955,1956],{"class":499},"\"token\"",[132,1958,234],{"class":151},[132,1960,1961,1964,1967],{"class":134,"line":375},[132,1962,1963],{"class":151},"            refreshTokenKey: ",[132,1965,1966],{"class":499},"\"refresh_token\"",[132,1968,234],{"class":151},[132,1970,1971,1974,1977,1980,1983],{"class":134,"line":1404},[132,1972,1973],{"class":151},"            body: { principal: ",[132,1975,1976],{"class":499},"\"email_address\"",[132,1978,1979],{"class":151},", password: ",[132,1981,1982],{"class":499},"\"password\"",[132,1984,1258],{"class":151},[132,1986,1987],{"class":134,"line":1416},[132,1988,1989],{"class":151},"          },\n",[132,1991,1992,1995,1998,2001,2004],{"class":134,"line":1421},[132,1993,1994],{"class":151},"          user: { path: ",[132,1996,1997],{"class":499},"\"/api/auth/user\"",[132,1999,2000],{"class":151},", userKey: ",[132,2002,2003],{"class":499},"\"user\"",[132,2005,1258],{"class":151},[132,2007,2008],{"class":134,"line":1427},[132,2009,2010],{"class":151},"        },\n",[132,2012,2013],{"class":134,"line":1437},[132,2014,2015],{"class":151},"      },\n",[132,2017,2019],{"class":134,"line":2018},20,[132,2020,1908],{"class":151},[132,2022,2024],{"class":134,"line":2023},21,[132,2025,2026],{"class":151},"  },\n",[132,2028,2030],{"class":134,"line":2029},22,[132,2031,2032],{"class":151},"});\n",[33,2034],{"className":2035},[381],[123,2037,2039],{"className":1628,"code":2038,"language":1630,"meta":128,"style":128},"// In a component/composable\nconst { login, loggedIn } = useAuth();\nawait login(\"local\", { principal, password });\n\n// Authenticated fetch helpers\n// 1) useAuthFetch: composable that behaves like useFetch but sends the auth header\nconst { data: me } = await useAuthFetch(\"/api/auth/user\");\n\n// 2) $authFetch: $fetch-style helper that sends the auth header\nconst { $authFetch } = useNuxtApp();\nawait $authFetch(\"/api/protected\");\n",[24,2040,2041,2046,2070,2086,2090,2095,2100,2131,2135,2140,2157],{"__ignoreMap":128},[132,2042,2043],{"class":134,"line":135},[132,2044,2045],{"class":1271},"// In a component/composable\n",[132,2047,2048,2050,2052,2055,2057,2060,2063,2065,2068],{"class":134,"line":155},[132,2049,1662],{"class":138},[132,2051,365],{"class":151},[132,2053,2054],{"class":279},"login",[132,2056,406],{"class":151},[132,2058,2059],{"class":279},"loggedIn",[132,2061,2062],{"class":151}," } ",[132,2064,427],{"class":138},[132,2066,2067],{"class":142}," useAuth",[132,2069,1413],{"class":151},[132,2071,2072,2075,2078,2080,2083],{"class":134,"line":162},[132,2073,2074],{"class":138},"await",[132,2076,2077],{"class":142}," login",[132,2079,273],{"class":151},[132,2081,2082],{"class":499},"\"local\"",[132,2084,2085],{"class":151},", { principal, password });\n",[132,2087,2088],{"class":134,"line":174},[132,2089,159],{"emptyLinePlaceholder":158},[132,2091,2092],{"class":134,"line":186},[132,2093,2094],{"class":1271},"// Authenticated fetch helpers\n",[132,2096,2097],{"class":134,"line":207},[132,2098,2099],{"class":1271},"// 1) useAuthFetch: composable that behaves like useFetch but sends the auth header\n",[132,2101,2102,2104,2106,2109,2112,2115,2117,2119,2122,2125,2127,2129],{"class":134,"line":223},[132,2103,1662],{"class":138},[132,2105,365],{"class":151},[132,2107,2108],{"class":1293},"data",[132,2110,2111],{"class":151},": ",[132,2113,2114],{"class":279},"me",[132,2116,2062],{"class":151},[132,2118,427],{"class":138},[132,2120,2121],{"class":138}," await",[132,2123,2124],{"class":142}," useAuthFetch",[132,2126,273],{"class":151},[132,2128,1997],{"class":499},[132,2130,1686],{"class":151},[132,2132,2133],{"class":134,"line":237},[132,2134,159],{"emptyLinePlaceholder":158},[132,2136,2137],{"class":134,"line":243},[132,2138,2139],{"class":1271},"// 2) $authFetch: $fetch-style helper that sends the auth header\n",[132,2141,2142,2144,2146,2148,2150,2152,2155],{"class":134,"line":248},[132,2143,1662],{"class":138},[132,2145,365],{"class":151},[132,2147,1797],{"class":279},[132,2149,2062],{"class":151},[132,2151,427],{"class":138},[132,2153,2154],{"class":142}," useNuxtApp",[132,2156,1413],{"class":151},[132,2158,2159,2161,2164,2166,2169],{"class":134,"line":264},[132,2160,2074],{"class":138},[132,2162,2163],{"class":142}," $authFetch",[132,2165,273],{"class":151},[132,2167,2168],{"class":499},"\"/api/protected\"",[132,2170,1686],{"class":151},[33,2172,2174,2182,2190],{"className":2173},[44,45,46,47,931],[51,2175,2178,2180],{"className":2176,"href":1772,"rel":2177,"target":1008},[54,55,56,57,58],[61],[20,2179,1011],{},[67,2181],{"icon":69},[51,2183,2186,2188],{"className":2184,"href":1781,"rel":2185,"target":1008},[54,55,56,57,73,74,75],[61],[20,2187,1564],{},[67,2189],{"icon":69},[51,2191,2193],{"className":2192,"href":943},[54,55,56,57,73,74,75],[20,2194,946],{},[948,2196,2197],{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}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 pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":128,"searchDepth":155,"depth":155,"links":2199},[2200,2201,2202],{"id":90,"depth":155,"text":91},{"id":816,"depth":155,"text":817},{"id":1624,"depth":155,"text":1625},"2025-01-22T00:00:00.000Z","Auth module for Nuxt 3 server apps with local and social providers, tokens, middlewares, and typed composables.","/projects/nuxt-auth-module/main.png",{"license":966},"/projects/nuxt-auth-module",{"title":1747,"description":2204},"projects/nuxt-auth-module",[1740,2211,2212,974,1741],"Nuxt 3","Auth","IfohDOmTUGAqqlvqIKYGsRAS0nL5mgDnaPuXVyClNog",1778666975542]