{"id":1,"date":"2025-08-23T19:25:58","date_gmt":"2025-08-23T19:25:58","guid":{"rendered":"https:\/\/1v0.net\/blog\/?p=1"},"modified":"2025-08-26T08:51:05","modified_gmt":"2025-08-26T08:51:05","slug":"how-to-add-google-login-laravel-12","status":"publish","type":"post","link":"https:\/\/1v0.net\/blog\/how-to-add-google-login-laravel-12\/","title":{"rendered":"How to Add Google Login to a Laravel 12 App with Socialite"},"content":{"rendered":"\n<p>Every time I built a new Laravel project, the request came up: <\/p>\n\n\n\n<p><em>\u201cCan we let users sign in with Google?\u201d<\/em> <\/p>\n\n\n\n<p>At first, I\u2019d copy snippets from old projects, tweak migrations, and hope nothing broke. Eventually, I streamlined the process \u2014 and in this post I\u2019ll walk you through the clean, Laravel 12-friendly way to add Google login with Socialite.<\/p>\n\n\n\n<p>Google login removes password friction and boosts conversions. In Laravel, the easiest, most secure way to add it is with <strong>Socialite<\/strong>.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>What you\u2019ll build:<\/strong> a \u201cContinue with Google\u201d button that redirects users to Google, returns with their profile, and logs them in (or signs them up) safely.<\/p>\n<\/blockquote>\n\n\n\n<p><\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>1 &#8211; Install Socialite<\/strong><\/h2>\n\n\n\n<p><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">composer <span class=\"hljs-built_in\">require<\/span> laravel\/socialite<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Register the facade if you don\u2019t use package auto-discovery (most apps don\u2019t need this):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-code-table shcb-line-numbers shcb-wrap-lines\"><span class='shcb-loc'><span><span class=\"hljs-comment\">\/\/ config\/app.php (only if NOT auto-discovered)<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">'aliases'<\/span> =&gt; &#91;\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-comment\">\/\/ ...<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">'Socialite'<\/span> =&gt; Laravel\\Socialite\\Facades\\Socialite::class,\n<\/span><\/span><span class='shcb-loc'><span>];\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>2 &#8211; Create Google OAuth credentials<\/strong><\/h2>\n\n\n\n<p><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Go to <strong>Google Cloud Console \u2192 APIs &amp; Services \u2192 <\/strong><\/li>\n\n\n\n<li><strong>Credentials<\/strong>. Create <strong>OAuth 2.0 Client ID<\/strong> (type: <em>Web application<\/em>).<\/li>\n\n\n\n<li>Add an <strong>Authorized redirect URI<\/strong> (must match exactly):<br><\/li>\n<\/ol>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">https:<span class=\"hljs-comment\">\/\/your-domain.com\/auth\/google\/callback<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>for local dev<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">http:<span class=\"hljs-comment\">\/\/localhost:8000\/auth\/google\/callback<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Copy your <strong>Client ID<\/strong> and <strong>Client Secret<\/strong>.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>3 &#8211; Configure Laravel<\/strong><\/h2>\n\n\n\n<p><\/p>\n\n\n\n<p>.env<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-code-table shcb-line-numbers shcb-wrap-lines\"><span class='shcb-loc'><span>GOOGLE_CLIENT_ID=your_id_here\n<\/span><\/span><span class='shcb-loc'><span>GOOGLE_CLIENT_SECRET=your_secret_here\n<\/span><\/span><span class='shcb-loc'><span>GOOGLE_REDIRECT_URI=${APP_URL}\/auth\/google\/callback\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"># Make sure APP_URL and session domain are correct for your env:<\/span>\n<\/span><\/span><span class='shcb-loc'><span>APP_URL=http:<span class=\"hljs-comment\">\/\/localhost:8000<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"># SESSION_DOMAIN=.your-domain.com     # if using subdomains like demo.your-domain.com<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<p>config\/services.php<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-code-table shcb-line-numbers shcb-wrap-lines\"><span class='shcb-loc'><span><span class=\"hljs-string\">'google'<\/span> =&gt; &#91;\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">'client_id'<\/span>     =&gt; env(<span class=\"hljs-string\">'GOOGLE_CLIENT_ID'<\/span>),\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">'client_secret'<\/span> =&gt; env(<span class=\"hljs-string\">'GOOGLE_CLIENT_SECRET'<\/span>),\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">'redirect'<\/span>      =&gt; env(<span class=\"hljs-string\">'GOOGLE_REDIRECT_URI'<\/span>),\n<\/span><\/span><span class='shcb-loc'><span>],\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>4 &#8211; Add routes<\/strong><\/h2>\n\n\n\n<p><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-code-table shcb-line-numbers shcb-wrap-lines\"><span class='shcb-loc'><span><span class=\"hljs-comment\">\/\/ routes\/web.php<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Controllers<\/span>\\<span class=\"hljs-title\">Auth<\/span>\\<span class=\"hljs-title\">GoogleController<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>Route::middleware(<span class=\"hljs-string\">'guest'<\/span>)-&gt;group(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">()<\/span> <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>    Route::get(<span class=\"hljs-string\">'\/auth\/google\/redirect'<\/span>, &#91;GoogleController::class, <span class=\"hljs-string\">'redirect'<\/span>])\n<\/span><\/span><span class='shcb-loc'><span>        -&gt;name(<span class=\"hljs-string\">'google.redirect'<\/span>);\n<\/span><\/span><span class='shcb-loc'><span>    Route::get(<span class=\"hljs-string\">'\/auth\/google\/callback'<\/span>, &#91;GoogleController::class, <span class=\"hljs-string\">'callback'<\/span>])\n<\/span><\/span><span class='shcb-loc'><span>        -&gt;name(<span class=\"hljs-string\">'google.callback'<\/span>);\n<\/span><\/span><span class='shcb-loc'><span>});\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\/\/ (Optional) If you\u2019ll allow linking a Google account for logged-in users<\/span>\n<\/span><\/span><span class='shcb-loc'><span>Route::middleware(<span class=\"hljs-string\">'auth'<\/span>)-&gt;group(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">()<\/span> <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>    Route::post(<span class=\"hljs-string\">'\/account\/google\/disconnect'<\/span>, &#91;GoogleController::class, <span class=\"hljs-string\">'disconnect'<\/span>])\n<\/span><\/span><span class='shcb-loc'><span>        -&gt;name(<span class=\"hljs-string\">'google.disconnect'<\/span>);\n<\/span><\/span><span class='shcb-loc'><span>});\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>5 &#8211; Prepare your users table (store google_id, avatar, etc.)<\/strong><\/h2>\n\n\n\n<p><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">php<\/span> <span class=\"hljs-selector-tag\">artisan<\/span> <span class=\"hljs-selector-tag\">make<\/span><span class=\"hljs-selector-pseudo\">:migration<\/span> <span class=\"hljs-selector-tag\">add_google_columns_to_users_table<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-code-table shcb-line-numbers shcb-wrap-lines\"><span class='shcb-loc'><span><span class=\"hljs-comment\">\/\/ database\/migrations\/xxxx_xx_xx_xxxxxx_add_google_columns_to_users_table.php<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Migrations<\/span>\\<span class=\"hljs-title\">Migration<\/span>;\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Schema<\/span>\\<span class=\"hljs-title\">Blueprint<\/span>;\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">Schema<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Migration<\/span> <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">up<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">void<\/span> <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>        Schema::table(<span class=\"hljs-string\">'users'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">(Blueprint $table)<\/span> <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>            $table-&gt;string(<span class=\"hljs-string\">'google_id'<\/span>)-&gt;nullable()-&gt;index();\n<\/span><\/span><span class='shcb-loc'><span>            $table-&gt;string(<span class=\"hljs-string\">'avatar'<\/span>)-&gt;nullable();\n<\/span><\/span><span class='shcb-loc'><span>            $table-&gt;json(<span class=\"hljs-string\">'social_meta'<\/span>)-&gt;nullable(); <span class=\"hljs-comment\">\/\/ optional for raw payload<\/span>\n<\/span><\/span><span class='shcb-loc'><span>        });\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">down<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">void<\/span> <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>        Schema::table(<span class=\"hljs-string\">'users'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">(Blueprint $table)<\/span> <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>            $table-&gt;dropColumn(&#91;<span class=\"hljs-string\">'google_id'<\/span>, <span class=\"hljs-string\">'avatar'<\/span>, <span class=\"hljs-string\">'social_meta'<\/span>]);\n<\/span><\/span><span class='shcb-loc'><span>        });\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>};\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">php artisan migrate<\/code><\/span><\/pre>\n\n\n<p><\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>6 &#8211; Build the controller<\/strong><\/h2>\n\n\n\n<p><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-code-table shcb-line-numbers shcb-wrap-lines\"><span class='shcb-loc'><span><span class=\"hljs-comment\">\/\/ app\/Http\/Controllers\/Auth\/GoogleController.php<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Controllers<\/span>\\<span class=\"hljs-title\">Auth<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Controllers<\/span>\\<span class=\"hljs-title\">Controller<\/span>;\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">User<\/span>;\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">Auth<\/span>;\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Str<\/span>;\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Laravel<\/span>\\<span class=\"hljs-title\">Socialite<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">Socialite<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">GoogleController<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Controller<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-class\"><\/span>{\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-comment\">\/\/ Step 1: send user to Google<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">redirect<\/span><span class=\"hljs-params\">()<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-function\">    <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-comment\">\/\/ Request minimal scopes; OpenID gives you a stable sub (id)<\/span>\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-keyword\">return<\/span> Socialite::driver(<span class=\"hljs-string\">'google'<\/span>)\n<\/span><\/span><span class='shcb-loc'><span>            -&gt;scopes(&#91;<span class=\"hljs-string\">'openid'<\/span>, <span class=\"hljs-string\">'profile'<\/span>, <span class=\"hljs-string\">'email'<\/span>])\n<\/span><\/span><span class='shcb-loc'><span>            -&gt;redirect();\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-comment\">\/\/ Step 2: handle Google callback<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">callback<\/span><span class=\"hljs-params\">()<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-function\">    <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-comment\">\/\/ Use stateless() only if you know session\/state won\u2019t persist (e.g., API)<\/span>\n<\/span><\/span><span class='shcb-loc'><span>        $googleUser = Socialite::driver(<span class=\"hljs-string\">'google'<\/span>)-&gt;user();\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-comment\">\/\/ Defensive: Google should always provide email when using 'email' scope<\/span>\n<\/span><\/span><span class='shcb-loc'><span>        $email = $googleUser-&gt;getEmail();\n<\/span><\/span><span class='shcb-loc'><span>        $googleId = $googleUser-&gt;getId();\n<\/span><\/span><span class='shcb-loc'><span>        $name = $googleUser-&gt;getName() ?: Str::before($email, <span class=\"hljs-string\">'@'<\/span>);\n<\/span><\/span><span class='shcb-loc'><span>        $avatar = $googleUser-&gt;getAvatar();\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-comment\">\/\/ 1) If a user has already linked Google, log them in<\/span>\n<\/span><\/span><span class='shcb-loc'><span>        $user = User::where(<span class=\"hljs-string\">'google_id'<\/span>, $googleId)-&gt;first();\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-keyword\">if<\/span> (! $user &amp;&amp; $email) {\n<\/span><\/span><span class='shcb-loc'><span>            <span class=\"hljs-comment\">\/\/ 2) If email exists but not linked yet, decide your policy:<\/span>\n<\/span><\/span><span class='shcb-loc'><span>            <span class=\"hljs-comment\">\/\/    Option A: link automatically (safer if email is verified by Google)<\/span>\n<\/span><\/span><span class='shcb-loc'><span>            <span class=\"hljs-comment\">\/\/    Option B: ask user to confirm linking via email step<\/span>\n<\/span><\/span><span class='shcb-loc'><span>            $user = User::where(<span class=\"hljs-string\">'email'<\/span>, $email)-&gt;first();\n<\/span><\/span><span class='shcb-loc'><span>            <span class=\"hljs-keyword\">if<\/span> ($user) {\n<\/span><\/span><span class='shcb-loc'><span>                <span class=\"hljs-comment\">\/\/ Link existing account<\/span>\n<\/span><\/span><span class='shcb-loc'><span>                $user-&gt;forceFill(&#91;\n<\/span><\/span><span class='shcb-loc'><span>                    <span class=\"hljs-string\">'google_id'<\/span>   =&gt; $googleId,\n<\/span><\/span><span class='shcb-loc'><span>                    <span class=\"hljs-string\">'avatar'<\/span>      =&gt; $user-&gt;avatar ?: $avatar,\n<\/span><\/span><span class='shcb-loc'><span>                    <span class=\"hljs-string\">'social_meta'<\/span> =&gt; json_encode(&#91;<span class=\"hljs-string\">'google'<\/span> =&gt; $googleUser-&gt;user]),\n<\/span><\/span><span class='shcb-loc'><span>                ])-&gt;save();\n<\/span><\/span><span class='shcb-loc'><span>            }\n<\/span><\/span><span class='shcb-loc'><span>        }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-keyword\">if<\/span> (! $user) {\n<\/span><\/span><span class='shcb-loc'><span>            <span class=\"hljs-comment\">\/\/ 3) Otherwise create a new user account<\/span>\n<\/span><\/span><span class='shcb-loc'><span>            $user = User::create(&#91;\n<\/span><\/span><span class='shcb-loc'><span>                <span class=\"hljs-string\">'name'<\/span>       =&gt; $name,\n<\/span><\/span><span class='shcb-loc'><span>                <span class=\"hljs-string\">'email'<\/span>      =&gt; $email,            <span class=\"hljs-comment\">\/\/ may be null if Google doesn\u2019t return it<\/span>\n<\/span><\/span><span class='shcb-loc'><span>                <span class=\"hljs-string\">'password'<\/span>   =&gt; bcrypt(Str::random(<span class=\"hljs-number\">32<\/span>)), <span class=\"hljs-comment\">\/\/ random placeholder<\/span>\n<\/span><\/span><span class='shcb-loc'><span>                <span class=\"hljs-string\">'google_id'<\/span>  =&gt; $googleId,\n<\/span><\/span><span class='shcb-loc'><span>                <span class=\"hljs-string\">'avatar'<\/span>     =&gt; $avatar,\n<\/span><\/span><span class='shcb-loc'><span>                <span class=\"hljs-string\">'social_meta'<\/span>=&gt; json_encode(&#91;<span class=\"hljs-string\">'google'<\/span> =&gt; $googleUser-&gt;user]),\n<\/span><\/span><span class='shcb-loc'><span>            ]);\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>            <span class=\"hljs-comment\">\/\/ (Optional) assign default role, seed profile, etc.<\/span>\n<\/span><\/span><span class='shcb-loc'><span>            <span class=\"hljs-comment\">\/\/ $user-&gt;assignRole('User');<\/span>\n<\/span><\/span><span class='shcb-loc'><span>        }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>        Auth::login($user, remember: <span class=\"hljs-keyword\">true<\/span>);\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-keyword\">return<\/span> redirect()-&gt;intended(<span class=\"hljs-string\">'\/'<\/span>); <span class=\"hljs-comment\">\/\/ or your dashboard route<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-comment\">\/\/ Optional disconnect (keep local password login)<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">disconnect<\/span><span class=\"hljs-params\">()<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-function\">    <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>        $user = auth()-&gt;user();\n<\/span><\/span><span class='shcb-loc'><span>        $user-&gt;forceFill(&#91;<span class=\"hljs-string\">'google_id'<\/span> =&gt; <span class=\"hljs-keyword\">null<\/span>])-&gt;save();\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-keyword\">return<\/span> back()-&gt;with(<span class=\"hljs-string\">'status'<\/span>, <span class=\"hljs-string\">'Google account disconnected.'<\/span>);\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>Security note:<\/strong> If you <em>don\u2019t<\/em> want automatic linking when emails match, replace that part with a \u201cconfirm link\u201d screen and send a verification email to the address first.<\/p>\n<\/blockquote>\n\n\n\n<p><\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>7 &#8211; Add the \u201cContinue with Google\u201d button<\/strong><\/h2>\n\n\n\n<p><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-code-table shcb-line-numbers shcb-wrap-lines\"><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">a<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"{{ route('google.redirect') }}\"<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"btn btn-outline-dark w-100\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">i<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"bi bi-google me-2\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">i<\/span>&gt;<\/span> Continue with Google\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/span>&gt;<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>8 &#8211; Common Errors<\/strong><\/h2>\n\n\n\n<p><\/p>\n\n\n\n<p><strong>Redirect URI mismatch<\/strong><br>Ensure the URI in Google Console matches your <code>.env<\/code> <code>GOOGLE_REDIRECT_URI<\/code> <strong>exactly<\/strong> (protocol, domain, path).<\/p>\n\n\n\n<p><strong>Invalid state \/ CSRF<\/strong><br>Make sure sessions work (correct <code>APP_URL<\/code>, <code>SESSION_DOMAIN<\/code>, cookies not blocked). Avoid <code>stateless()<\/code> unless you know why.<\/p>\n\n\n\n<p><strong>Email is null<\/strong><br>Request <code>email<\/code> scope and ensure the Google account actually has an email. If missing, consider prompting the user to add one after login.<\/p>\n\n\n\n<p><strong>Subdomains (demo.your-domain.com)<\/strong><br>Use a shared cookie domain (e.g., <code>SESSION_DOMAIN=.your-domain.com<\/code>) if you log in on one subdomain and return to another.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">9 &#8211;   \u201cLink Google\u201d from account settings<\/h2>\n\n\n\n<p><\/p>\n\n\n\n<p>Allow logged-in users to connect their Google account later:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-code-table shcb-line-numbers shcb-wrap-lines\"><span class='shcb-loc'><span>Route::middleware(<span class=\"hljs-string\">'auth'<\/span>)-&gt;get(<span class=\"hljs-string\">'\/account\/google\/connect'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">return<\/span> Socialite::driver(<span class=\"hljs-string\">'google'<\/span>)-&gt;redirect();\n<\/span><\/span><span class='shcb-loc'><span>})-&gt;name(<span class=\"hljs-string\">'google.connect'<\/span>);\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>In the callback, if <code>auth()-&gt;check()<\/code>, <strong>attach<\/strong> <code>google_id<\/code> to the current user instead of logging in a different user. This prevents account takeovers.<\/p>\n\n\n\n<div class=\"wp-block-cover alignwide is-light\" style=\"min-height:100vh;aspect-ratio:unset;\"><span aria-hidden=\"true\" class=\"wp-block-cover__background has-background-dim-100 has-background-dim\" style=\"background-color:#ffffff\"><\/span><div class=\"wp-block-cover__inner-container is-layout-constrained wp-block-cover-is-layout-constrained\">\n<h2 class=\"wp-block-heading\"><strong>Grab a production-ready implementation<\/strong><\/h2>\n\n\n\n<p class=\"is-style-default\">If you don\u2019t want to wire up all the edge cases (UI, roles, permissions, settings toggles, demos), <strong>Grab a production-ready implementation<\/strong>:<\/p>\n\n\n\n<div class=\"wp-block-media-text alignwide is-stacked-on-mobile is-vertically-aligned-center is-image-fill-element\" style=\"grid-template-columns:56% auto\"><figure class=\"wp-block-media-text__media\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"779\" src=\"https:\/\/1v0.net\/blog\/wp-content\/uploads\/2025\/08\/Screenshot-2025-08-19-010037-1024x779.png\" alt=\"\" class=\"wp-image-57 size-full\" style=\"object-position:0% 0%\" srcset=\"https:\/\/1v0.net\/blog\/wp-content\/uploads\/2025\/08\/Screenshot-2025-08-19-010037-1024x779.png 1024w, https:\/\/1v0.net\/blog\/wp-content\/uploads\/2025\/08\/Screenshot-2025-08-19-010037-300x228.png 300w, https:\/\/1v0.net\/blog\/wp-content\/uploads\/2025\/08\/Screenshot-2025-08-19-010037-768x584.png 768w, https:\/\/1v0.net\/blog\/wp-content\/uploads\/2025\/08\/Screenshot-2025-08-19-010037.png 1113w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure><div class=\"wp-block-media-text__content\">\n<h2 class=\"wp-block-heading has-text-color\" style=\"color:#000000;font-size:32px\"><strong>Laravel Authentication &amp; User Management Kit<\/strong><\/h2>\n\n\n\n<p class=\"has-text-color\" style=\"color:#000000;font-size:17px\">Production-ready auth (Fortify + Socialite), roles\/permissions (Spatie), settings UI, polished Bootstrap frontend, and full docs \u2014 so you can launch in minutes, not weeks.<\/p>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button color--theme btn\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/1v0.net\/product\/laravel-authentication-and-user-management\">Learn more<\/a><\/div>\n<\/div>\n<\/div><\/div>\n<\/div><\/div>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Google login removes password friction and boosts conversions. In Laravel, the easiest, most secure way to add it is with Socialite.<\/p>\n<p>What you\u2019ll build: a \u201cContinue with Google\u201d button that redirects users to Google, returns with their profile, and logs them in (or signs them up) safely.<\/p>\n","protected":false},"author":1,"featured_media":65,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":"[]"},"categories":[7],"tags":[12,13,20],"class_list":["post-1","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-laravel","tag-authentication","tag-login","tag-social-login"],"_links":{"self":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/1","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/comments?post=1"}],"version-history":[{"count":28,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/1\/revisions"}],"predecessor-version":[{"id":95,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/1\/revisions\/95"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media\/65"}],"wp:attachment":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media?parent=1"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/categories?post=1"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/tags?post=1"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}