react-google-one-tap-extention 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitattributes +2 -0
- package/LICENSE +21 -0
- package/README.md +279 -0
- package/index.js +65 -0
- package/package.json +15 -0
package/.gitattributes
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 danny7777777268
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# React Google One Tap
|
|
2
|
+
|
|
3
|
+
Minimal, dependency-free **Google One Tap** sign-in component for React applications.
|
|
4
|
+
|
|
5
|
+
Designed for **startups, SaaS products, and production systems** that need fast, secure Google authentication with minimal setup.
|
|
6
|
+
|
|
7
|
+
✔ Automatically loads Google Identity Services
|
|
8
|
+
✔ Works with **React**,**CRA**, **Vite**, **Next.js (client-side)**
|
|
9
|
+
✔ No build step, no bundler required
|
|
10
|
+
✔ Backend-agnostic (works with any stack)
|
|
11
|
+
✔ Lightweight, auditable, and secure
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## Repository
|
|
17
|
+
|
|
18
|
+
Source code and issues are available on GitHub:
|
|
19
|
+
|
|
20
|
+
🔗 https://github.com/danny7777777268/react-google-one-tap
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Why This Package
|
|
25
|
+
|
|
26
|
+
Modern products need **frictionless authentication**. Google One Tap significantly improves sign-in conversion, especially on mobile and returning users.
|
|
27
|
+
|
|
28
|
+
This package focuses on:
|
|
29
|
+
- **Performance** – zero extra dependencies
|
|
30
|
+
- **Security** – tokens are never stored on the client
|
|
31
|
+
- **Simplicity** – 1 component, 2 required props
|
|
32
|
+
- **Control** – backend verification is fully yours
|
|
33
|
+
|
|
34
|
+
Ideal for:
|
|
35
|
+
- SaaS platforms
|
|
36
|
+
- AI & fintech startups
|
|
37
|
+
- Marketplaces
|
|
38
|
+
- Consumer apps
|
|
39
|
+
- Enterprise dashboards
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
Using npm:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm install react-google-one-tap
|
|
49
|
+
|
|
50
|
+
or
|
|
51
|
+
|
|
52
|
+
yarn add react-google-one-tap
|
|
53
|
+
|
|
54
|
+
Usage
|
|
55
|
+
|
|
56
|
+
import GoogleOneTap from "react-google-one-tap";
|
|
57
|
+
|
|
58
|
+
export default function App() {
|
|
59
|
+
return (
|
|
60
|
+
<>
|
|
61
|
+
<GoogleOneTap
|
|
62
|
+
clientId="YOUR_GOOGLE_CLIENT_ID"
|
|
63
|
+
authUrl="https://yourdomain.com/api/auth/google"
|
|
64
|
+
onSignedIn={(data) => {
|
|
65
|
+
console.log("Signed in user:", data.user);
|
|
66
|
+
}}
|
|
67
|
+
/>
|
|
68
|
+
</>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
Pros
|
|
73
|
+
|
|
74
|
+
| Prop | Type | Required | Description |
|
|
75
|
+
| -------------------- | ---------- | -------- | ------------------------------------------ |
|
|
76
|
+
| `clientId` | `string` | ✅ | Google OAuth Client ID |
|
|
77
|
+
| `authUrl` | `string` | ✅ | Backend endpoint to verify Google ID token |
|
|
78
|
+
| `onSignedIn` | `function` | ❌ | Callback after successful authentication |
|
|
79
|
+
| `headers` | `object` | ❌ | Custom headers for the auth request |
|
|
80
|
+
| `autoSelect` | `boolean` | ❌ | Automatically sign in returning users |
|
|
81
|
+
| `cancelOnTapOutside` | `boolean` | ❌ | Prevent dismissal when clicking outside |
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
use this backend example case for node js
|
|
85
|
+
|
|
86
|
+
Backend Example (Node.js / Express)
|
|
87
|
+
|
|
88
|
+
const { OAuth2Client } = require("google-auth-library");
|
|
89
|
+
|
|
90
|
+
const googleClient = new OAuth2Client(GOOGLE_CLIENT_ID);
|
|
91
|
+
|
|
92
|
+
app.post("/api/auth/google", async (req, res) => {
|
|
93
|
+
try {
|
|
94
|
+
const { credential } = req.body;
|
|
95
|
+
if (!credential) return res.status(400).json({ error: "Missing credential" });
|
|
96
|
+
|
|
97
|
+
// Verify Google ID token on server
|
|
98
|
+
const ticket = await googleClient.verifyIdToken({
|
|
99
|
+
idToken: credential,
|
|
100
|
+
audience: process.env.GOOGLE_CLIENT_ID, // must match your web client id
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const payload = ticket.getPayload();
|
|
104
|
+
if (!payload) return res.status(401).json({ error: "Invalid token payload" });
|
|
105
|
+
|
|
106
|
+
// Typical fields:
|
|
107
|
+
// payload.sub (unique user id), payload.email, payload.name, payload.picture, payload.email_verified
|
|
108
|
+
const user = {
|
|
109
|
+
googleId: payload.sub,
|
|
110
|
+
email: payload.email,
|
|
111
|
+
name: payload.name,
|
|
112
|
+
picture: payload.picture,
|
|
113
|
+
emailVerified: payload.email_verified,
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// TODO: find-or-create user in your DB using payload.sub (recommended unique id)
|
|
117
|
+
// Then create your own session/JWT.
|
|
118
|
+
|
|
119
|
+
// Example: set a cookie session token (replace with real session/JWT)
|
|
120
|
+
// res.cookie("session", yourAppJwt, {
|
|
121
|
+
// httpOnly: true,
|
|
122
|
+
// secure: process.env.NODE_ENV === "production",
|
|
123
|
+
// sameSite: "lax",
|
|
124
|
+
// });
|
|
125
|
+
|
|
126
|
+
return res.json({ ok: true, user });
|
|
127
|
+
} catch (err) {
|
|
128
|
+
console.error(err);
|
|
129
|
+
return res.status(401).json({ error: "Token verification failed" });
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
Security Model
|
|
137
|
+
|
|
138
|
+
No tokens are stored in localStorage or sessionStorage
|
|
139
|
+
|
|
140
|
+
Google ID token is sent directly to your backend
|
|
141
|
+
|
|
142
|
+
You fully control:
|
|
143
|
+
|
|
144
|
+
Verification
|
|
145
|
+
|
|
146
|
+
Sessions
|
|
147
|
+
|
|
148
|
+
Cookies
|
|
149
|
+
|
|
150
|
+
User lifecycle
|
|
151
|
+
|
|
152
|
+
This aligns with OAuth best practices and modern security requirements.
|
|
153
|
+
|
|
154
|
+
Performance Notes
|
|
155
|
+
|
|
156
|
+
Loads Google script only once
|
|
157
|
+
|
|
158
|
+
No re-renders
|
|
159
|
+
|
|
160
|
+
No layout shifts
|
|
161
|
+
|
|
162
|
+
No UI blocking
|
|
163
|
+
|
|
164
|
+
Zero runtime dependencies
|
|
165
|
+
|
|
166
|
+
Framework Notes
|
|
167
|
+
Next.js
|
|
168
|
+
|
|
169
|
+
Use inside a client component:
|
|
170
|
+
|
|
171
|
+
"use client";
|
|
172
|
+
|
|
173
|
+
React Strict Mode
|
|
174
|
+
|
|
175
|
+
Safe to use — initialization is guarded internally.
|
|
176
|
+
|
|
177
|
+
SEO Keywords
|
|
178
|
+
|
|
179
|
+
google one tap
|
|
180
|
+
react google one tap
|
|
181
|
+
google sign in react
|
|
182
|
+
google oauth react
|
|
183
|
+
react authentication
|
|
184
|
+
oauth login react
|
|
185
|
+
google identity services
|
|
186
|
+
react auth component
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
License
|
|
190
|
+
|
|
191
|
+
MIT
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
### Why this helps SEO & credibility
|
|
198
|
+
|
|
199
|
+
- npm search indexes README text
|
|
200
|
+
- Keywords section improves discovery
|
|
201
|
+
- “Why This Package” speaks to CTOs & founders
|
|
202
|
+
- Sponsor section builds trust + brand recall
|
|
203
|
+
|
|
204
|
+
If you want next:
|
|
205
|
+
- npm **badge section**
|
|
206
|
+
- GitHub **Open Graph preview**
|
|
207
|
+
- short **landing page copy**
|
|
208
|
+
- or auto-generated **TypeScript types**
|
|
209
|
+
|
|
210
|
+
Just tell me 🚀
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## CORS (Common Issue & How to Fix It)
|
|
216
|
+
|
|
217
|
+
Google One Tap itself **does not cause CORS issues**.
|
|
218
|
+
CORS problems usually occur when your **frontend sends the Google ID token to your backend** (`authUrl`).
|
|
219
|
+
|
|
220
|
+
### Typical CORS Error
|
|
221
|
+
|
|
222
|
+
```text
|
|
223
|
+
Access to fetch at 'https://api.yourdomain.com/api/auth/google'
|
|
224
|
+
from origin 'https://yourfrontend.com'
|
|
225
|
+
has been blocked by CORS policy
|
|
226
|
+
This means your backend is not allowing requests from your frontend origin.
|
|
227
|
+
|
|
228
|
+
Recommended Backend CORS Setup (Node.js / Express)
|
|
229
|
+
Basic Example (most common)
|
|
230
|
+
|
|
231
|
+
import cors from "cors";
|
|
232
|
+
|
|
233
|
+
app.use(cors({
|
|
234
|
+
origin: [
|
|
235
|
+
"http://localhost:3000",
|
|
236
|
+
"https://yourfrontend.com"
|
|
237
|
+
],
|
|
238
|
+
methods: ["GET", "POST"],
|
|
239
|
+
credentials: true
|
|
240
|
+
}));
|
|
241
|
+
⚠️ Important
|
|
242
|
+
If you use credentials: true, you CANNOT use origin: "*".
|
|
243
|
+
Browsers will block it.
|
|
244
|
+
|
|
245
|
+
If You Need to Allow Multiple Environments
|
|
246
|
+
|
|
247
|
+
const allowedOrigins = [
|
|
248
|
+
"http://localhost:3000",
|
|
249
|
+
"http://localhost:5173",
|
|
250
|
+
"https://yourfrontend.com"
|
|
251
|
+
];
|
|
252
|
+
|
|
253
|
+
app.use(cors({
|
|
254
|
+
origin: function (origin, callback) {
|
|
255
|
+
if (!origin || allowedOrigins.includes(origin)) {
|
|
256
|
+
callback(null, true);
|
|
257
|
+
} else {
|
|
258
|
+
callback(new Error("CORS not allowed"));
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
credentials: true
|
|
262
|
+
}));
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
### Why this section is valuable
|
|
266
|
+
- Solves the **#1 real issue devs hit**
|
|
267
|
+
- Improves npm search relevance (CORS + Google auth)
|
|
268
|
+
- Reduces GitHub issues/questions
|
|
269
|
+
- Signals **senior-level engineering maturity**
|
|
270
|
+
|
|
271
|
+
If you want next, I can:
|
|
272
|
+
- add **Vercel / Netlify specific CORS notes**
|
|
273
|
+
- add **Cloudflare / Nginx CORS config**
|
|
274
|
+
- add **Next.js API route example**
|
|
275
|
+
- add **production hardening tips**
|
|
276
|
+
|
|
277
|
+
Just say the word 🚀
|
|
278
|
+
|
|
279
|
+
|
package/index.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
|
|
3
|
+
export default function GoogleOneTap({
|
|
4
|
+
clientId,
|
|
5
|
+
authUrl,
|
|
6
|
+
onSignedIn,
|
|
7
|
+
headers = { "Content-Type": "application/json" },
|
|
8
|
+
autoSelect = true,
|
|
9
|
+
cancelOnTapOutside = false,
|
|
10
|
+
}) {
|
|
11
|
+
const initializedRef = useRef(false);
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (!clientId || !authUrl) {
|
|
15
|
+
console.error("GoogleOneTap: clientId and authUrl are required");
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (initializedRef.current) return;
|
|
20
|
+
initializedRef.current = true;
|
|
21
|
+
|
|
22
|
+
// Load Google Identity Services script
|
|
23
|
+
const script = document.createElement("script");
|
|
24
|
+
script.src = "https://accounts.google.com/gsi/client";
|
|
25
|
+
script.async = true;
|
|
26
|
+
script.defer = true;
|
|
27
|
+
|
|
28
|
+
script.onload = () => {
|
|
29
|
+
if (!window.google?.accounts?.id) return;
|
|
30
|
+
|
|
31
|
+
window.google.accounts.id.initialize({
|
|
32
|
+
client_id: clientId,
|
|
33
|
+
callback: async (response) => {
|
|
34
|
+
try {
|
|
35
|
+
const idToken = response.credential;
|
|
36
|
+
|
|
37
|
+
const res = await fetch(authUrl, {
|
|
38
|
+
method: "POST",
|
|
39
|
+
headers,
|
|
40
|
+
body: JSON.stringify({ credential: idToken }),
|
|
41
|
+
credentials: "include",
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const data = await res.json();
|
|
45
|
+
onSignedIn?.(data);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
console.error("GoogleOneTap error:", err);
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
auto_select: autoSelect,
|
|
51
|
+
cancel_on_tap_outside: cancelOnTapOutside,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
window.google.accounts.id.prompt();
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
document.body.appendChild(script);
|
|
58
|
+
|
|
59
|
+
return () => {
|
|
60
|
+
document.body.removeChild(script);
|
|
61
|
+
};
|
|
62
|
+
}, []);
|
|
63
|
+
|
|
64
|
+
return null;
|
|
65
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-google-one-tap-extention",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"main": "index.js",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
7
|
+
},
|
|
8
|
+
"keywords": [],
|
|
9
|
+
"author": "",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"description": "Minimal Google One Tap component for React",
|
|
12
|
+
"peerDependencies": {
|
|
13
|
+
"react": ">=16.8"
|
|
14
|
+
}
|
|
15
|
+
}
|