[{"data":1,"prerenderedAt":574},["ShallowReactive",2],{"\u002Farticles\u002Flaravel-sanctum-vue-spa-common-pitfalls":3},{"id":4,"title":5,"body":6,"createdAt":566,"description":567,"extension":568,"meta":569,"navigation":218,"path":570,"seo":571,"stem":572,"__hash__":573},"articles\u002Farticles\u002F2.laravel-sanctum-vue-spa-common-pitfalls.md","Common pitfalls to watch out for when integrating Laravel Sanctum with a Vue 3 Frontend",{"type":7,"value":8,"toc":564},"minimark",[9,13,22,25,37,40,43,51,54,101,546,550,553,560],[10,11,5],"h1",{"id":12},"common-pitfalls-to-watch-out-for-when-integrating-laravel-sanctum-with-a-vue-3-frontend",[14,15,16,17,21],"p",{},"When implementing a cookie based authentication in your Vue 3 Single Page Application with Laravel Sanctum, it can sometimes be frustrating when you keep geting ",[18,19,20],"strong",{},"401"," unauthorized in your browser console even when you are fully convinced that you have followed all the instructions from the documentation.",[14,23,24],{},"In this article, I will be sharing with you some common pitfalls I have identified based on my experience while working with Laravel Sanctum and a separate Vue SPA.",[14,26,27,28,36],{},"Before we proceed, I must say the easiest way to integrate Laravel Sanctum into your Laravel application is by using the ",[29,30,35],"a",{"href":31,"rel":32,"target":34},"https:\u002F\u002Flaravel.com\u002Fdocs\u002F11.x\u002Fstarter-kits#laravel-breeze",[33],"nofollow","\\_blank","Laravel Breeze"," package.",[14,38,39],{},"Laravel has excellent documentation covering how to set up Sanctum in your Laravel application. Please go through the documentation as we won't be covering the exact steps in this post.",[14,41,42],{},"This tutorial is focusing on the common pitfalls you would likely face even after going through the documentation.",[14,44,45,46,50],{},"Be sure to define the following environment variables in your Laravel ",[47,48,49],"code",{},".env"," file:",[14,52,53],{},"Make sure you add the port numbers.",[55,56,61],"pre",{"className":57,"code":58,"language":59,"meta":60,"style":60},"language-dotenv shiki shiki-themes dracula","APP_URL=http:\u002F\u002Flocalhost:80\nFRONTEND_URL=http:\u002F\u002Flocalhost:5173\nSESSION_DOMAIN=localhost\n","dotenv","",[47,62,63,79,90],{"__ignoreMap":60},[64,65,68,72,76],"span",{"class":66,"line":67},"line",1,[64,69,71],{"class":70},"sCdxs","APP_URL",[64,73,75],{"class":74},"s0Tla","=",[64,77,78],{"class":70},"http:\u002F\u002Flocalhost:80\n",[64,80,82,85,87],{"class":66,"line":81},2,[64,83,84],{"class":70},"FRONTEND_URL",[64,86,75],{"class":74},[64,88,89],{"class":70},"http:\u002F\u002Flocalhost:5173\n",[64,91,93,96,98],{"class":66,"line":92},3,[64,94,95],{"class":70},"SESSION_DOMAIN",[64,97,75],{"class":74},[64,99,100],{"class":70},"localhost\n",[102,103,104,162,518,529],"ul",{},[105,106,107,111,112,115,116,119,120,123,124,126,127,129,130],"li",{},[10,108,110],{"id":109},"_1-using-local-custom-domains","#1 Using local Custom domains","Let's assume you're using custom domains locally.\nSo your Frontend domain is ",[47,113,114],{},"demo.local"," and the Backend API is ",[47,117,118],{},"api.demo.local",".",[121,122],"br",{},"The updated .env variables will now look like below.\nNotice the leading ",[47,125,119],{}," in the ",[47,128,95],{}," variable",[55,131,133],{"className":57,"code":132,"language":59,"meta":60,"style":60},"APP_URL=http:\u002F\u002Fapi.demo.local:80\nFRONTEND_URL=http:\u002F\u002Fdemo.local:5173\nSESSION_DOMAIN=.demo.local\n\n",[47,134,135,144,153],{"__ignoreMap":60},[64,136,137,139,141],{"class":66,"line":67},[64,138,71],{"class":70},[64,140,75],{"class":74},[64,142,143],{"class":70},"http:\u002F\u002Fapi.demo.local:80\n",[64,145,146,148,150],{"class":66,"line":81},[64,147,84],{"class":70},[64,149,75],{"class":74},[64,151,152],{"class":70},"http:\u002F\u002Fdemo.local:5173\n",[64,154,155,157,159],{"class":66,"line":92},[64,156,95],{"class":70},[64,158,75],{"class":74},[64,160,161],{"class":70},".demo.local\n",[105,163,164,168,169,172,173,176,177,180,181,183,184,186,188,189,191,192,195,196,198,199],{},[10,165,167],{"id":166},"_2-misconfigured-cors-file","#2 Misconfigured CORS file","If you're integrating Sanctum into an existing laravel application that has a ",[47,170,171],{},"cors"," file already, check to make sure the ",[47,174,175],{},"paths"," and\n",[47,178,179],{},"allowed_origins"," values are set properly.\nMost importantly the ",[47,182,175],{}," value should allow requests from both the api and web routes.",[121,185],{},[47,187,179],{}," array should include your Frontend domain.",[121,190],{},"Also make sure ",[47,193,194],{},"support_credentials"," is set to true.",[121,197],{},"Below is a sample cors file",[55,200,204],{"className":201,"code":202,"language":203,"meta":60,"style":60},"language-php shiki shiki-themes dracula","  return [\n\n      \u002F*\n      |--------------------------------------------------------------------------\n      | Cross-Origin Resource Sharing (CORS) Configuration\n      |--------------------------------------------------------------------------\n      |\n      | Here you may configure your settings for cross-origin resource sharing\n      | or \"CORS\". This determines what cross-origin operations may execute\n      | in web browsers. You are free to adjust these settings as needed.\n      |\n      | To learn more: https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FHTTP\u002FCORS\n      |\n      *\u002F\n\n      'paths' => ['*'],\n\n      'allowed_methods' => ['*'],\n\n      'allowed_origins' => [env('FRONTEND_URL', 'http:\u002F\u002Flocalhost:5173')],\n\n      'allowed_origins_patterns' => [],\n\n      'allowed_headers' => ['*'],\n\n      'exposed_headers' => [],\n\n      'max_age' => 0,\n\n      'supports_credentials' => true,\n\n  ];\n","php",[47,205,206,214,220,226,232,238,243,249,255,261,267,272,278,283,289,294,323,328,350,355,395,400,415,420,442,447,461,466,485,490,507,512],{"__ignoreMap":60},[64,207,208,211],{"class":66,"line":67},[64,209,210],{"class":74},"  return",[64,212,213],{"class":70}," [\n",[64,215,216],{"class":66,"line":81},[64,217,219],{"emptyLinePlaceholder":218},true,"\n",[64,221,222],{"class":66,"line":92},[64,223,225],{"class":224},"shSDL","      \u002F*\n",[64,227,229],{"class":66,"line":228},4,[64,230,231],{"class":224},"      |--------------------------------------------------------------------------\n",[64,233,235],{"class":66,"line":234},5,[64,236,237],{"class":224},"      | Cross-Origin Resource Sharing (CORS) Configuration\n",[64,239,241],{"class":66,"line":240},6,[64,242,231],{"class":224},[64,244,246],{"class":66,"line":245},7,[64,247,248],{"class":224},"      |\n",[64,250,252],{"class":66,"line":251},8,[64,253,254],{"class":224},"      | Here you may configure your settings for cross-origin resource sharing\n",[64,256,258],{"class":66,"line":257},9,[64,259,260],{"class":224},"      | or \"CORS\". This determines what cross-origin operations may execute\n",[64,262,264],{"class":66,"line":263},10,[64,265,266],{"class":224},"      | in web browsers. You are free to adjust these settings as needed.\n",[64,268,270],{"class":66,"line":269},11,[64,271,248],{"class":224},[64,273,275],{"class":66,"line":274},12,[64,276,277],{"class":224},"      | To learn more: https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FHTTP\u002FCORS\n",[64,279,281],{"class":66,"line":280},13,[64,282,248],{"class":224},[64,284,286],{"class":66,"line":285},14,[64,287,288],{"class":224},"      *\u002F\n",[64,290,292],{"class":66,"line":291},15,[64,293,219],{"emptyLinePlaceholder":218},[64,295,297,301,304,307,310,313,315,318,320],{"class":66,"line":296},16,[64,298,300],{"class":299},"seVfx","      '",[64,302,175],{"class":303},"s-mGx",[64,305,306],{"class":299},"'",[64,308,309],{"class":74}," =>",[64,311,312],{"class":70}," [",[64,314,306],{"class":299},[64,316,317],{"class":303},"*",[64,319,306],{"class":299},[64,321,322],{"class":70},"],\n",[64,324,326],{"class":66,"line":325},17,[64,327,219],{"emptyLinePlaceholder":218},[64,329,331,333,336,338,340,342,344,346,348],{"class":66,"line":330},18,[64,332,300],{"class":299},[64,334,335],{"class":303},"allowed_methods",[64,337,306],{"class":299},[64,339,309],{"class":74},[64,341,312],{"class":70},[64,343,306],{"class":299},[64,345,317],{"class":303},[64,347,306],{"class":299},[64,349,322],{"class":70},[64,351,353],{"class":66,"line":352},19,[64,354,219],{"emptyLinePlaceholder":218},[64,356,358,360,362,364,366,368,372,375,377,379,381,384,387,390,392],{"class":66,"line":357},20,[64,359,300],{"class":299},[64,361,179],{"class":303},[64,363,306],{"class":299},[64,365,309],{"class":74},[64,367,312],{"class":70},[64,369,371],{"class":370},"sAOxA","env",[64,373,374],{"class":70},"(",[64,376,306],{"class":299},[64,378,84],{"class":303},[64,380,306],{"class":299},[64,382,383],{"class":70},",",[64,385,386],{"class":299}," '",[64,388,389],{"class":303},"http:\u002F\u002Flocalhost:5173",[64,391,306],{"class":299},[64,393,394],{"class":70},")],\n",[64,396,398],{"class":66,"line":397},21,[64,399,219],{"emptyLinePlaceholder":218},[64,401,403,405,408,410,412],{"class":66,"line":402},22,[64,404,300],{"class":299},[64,406,407],{"class":303},"allowed_origins_patterns",[64,409,306],{"class":299},[64,411,309],{"class":74},[64,413,414],{"class":70}," [],\n",[64,416,418],{"class":66,"line":417},23,[64,419,219],{"emptyLinePlaceholder":218},[64,421,423,425,428,430,432,434,436,438,440],{"class":66,"line":422},24,[64,424,300],{"class":299},[64,426,427],{"class":303},"allowed_headers",[64,429,306],{"class":299},[64,431,309],{"class":74},[64,433,312],{"class":70},[64,435,306],{"class":299},[64,437,317],{"class":303},[64,439,306],{"class":299},[64,441,322],{"class":70},[64,443,445],{"class":66,"line":444},25,[64,446,219],{"emptyLinePlaceholder":218},[64,448,450,452,455,457,459],{"class":66,"line":449},26,[64,451,300],{"class":299},[64,453,454],{"class":303},"exposed_headers",[64,456,306],{"class":299},[64,458,309],{"class":74},[64,460,414],{"class":70},[64,462,464],{"class":66,"line":463},27,[64,465,219],{"emptyLinePlaceholder":218},[64,467,469,471,474,476,478,482],{"class":66,"line":468},28,[64,470,300],{"class":299},[64,472,473],{"class":303},"max_age",[64,475,306],{"class":299},[64,477,309],{"class":74},[64,479,481],{"class":480},"sIQBb"," 0",[64,483,484],{"class":70},",\n",[64,486,488],{"class":66,"line":487},29,[64,489,219],{"emptyLinePlaceholder":218},[64,491,493,495,498,500,502,505],{"class":66,"line":492},30,[64,494,300],{"class":299},[64,496,497],{"class":303},"supports_credentials",[64,499,306],{"class":299},[64,501,309],{"class":74},[64,503,504],{"class":480}," true",[64,506,484],{"class":70},[64,508,510],{"class":66,"line":509},31,[64,511,219],{"emptyLinePlaceholder":218},[64,513,515],{"class":66,"line":514},32,[64,516,517],{"class":70},"  ];\n",[105,519,520,524,525,528],{},[10,521,523],{"id":522},"_3-sanctum-config-file","#3 Sanctum Config file","The generated sanctum file should be fine to work with, but you can review it to make sure your frontend application url is included or referenced in the ",[47,526,527],{},"stateful"," key.",[105,530,531,535,536,119,539,541,542,545],{},[10,532,534],{"id":533},"_4-use-web-route-for-authentication","#4 Use web route for authentication","Laravel will automatically place the authentication routes in the web.php file or include a reference of the auth.php if you use an authentication scafolding package like ",[29,537,35],{"href":31,"rel":538,"target":34},[33],[121,540],{},"In a case where you're defining the authentication routes and controllers manually, you should make sure the routes are placed in the ",[47,543,544],{},"web.php"," file.",[10,547,549],{"id":548},"conclusion","Conclusion",[14,551,552],{},"In conclusion Laravel Sanctum makes authenticating your Vue Single Page Application with a Laravel backend using cookies and sessions.",[14,554,555,556,559],{},"Laravel goes one step forward by providing ",[29,557,35],{"href":31,"rel":558},[33]," which makes it super easier to generate all your authentication boilerplate code.\nThere are however some common mistakes developers make when working with Laravel Sanctum and a Single Page Application and this post highlighted some of these mistakes and the suggested solutions.",[561,562,563],"style",{},"html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .s0Tla, html code.shiki .s0Tla{--shiki-default:#FF79C6}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 .shSDL, html code.shiki .shSDL{--shiki-default:#6272A4}html pre.shiki code .seVfx, html code.shiki .seVfx{--shiki-default:#E9F284}html pre.shiki code .s-mGx, html code.shiki .s-mGx{--shiki-default:#F1FA8C}html pre.shiki code .sAOxA, html code.shiki .sAOxA{--shiki-default:#50FA7B}html pre.shiki code .sIQBb, html code.shiki .sIQBb{--shiki-default:#BD93F9}",{"title":60,"searchDepth":81,"depth":81,"links":565},[],"Nov 22, 2024 10:26AM","When implementing a cookie based authentication in your Vue 3 Single Page Application with Laravel Sanctum, it can sometimes be frustrating when you keep geting **401** unauthorized in your browser console even when you are fully convinced that you have followed all the instructions from the documentation. In this article, I will be sharing with you some common pitfalls I have identified based on my experience while working with Laravel Sanctum and a separate Vue SPA...","md",{},"\u002Farticles\u002Flaravel-sanctum-vue-spa-common-pitfalls",{"title":5,"description":567},"articles\u002F2.laravel-sanctum-vue-spa-common-pitfalls","bOyn-NNQXTaIJHhwyiyS2tBbB-k7rc1MBhFTi7sgssQ",1785226970011]