1. परिचय
यह कोडलैब, वेब के लिए 'Google से साइन इन करें' बटन कोडलैब पर आधारित है. इसलिए, इसे पहले पूरा करें.
इस कोडलैब में, एचटीएमएल और JavaScript API का इस्तेमाल करके, स्टैटिक और डाइनैमिक वेब पेजों पर उपयोगकर्ता के साइन-इन करने की सुविधा जोड़ने के लिए, Google Identity Services JavaScript लाइब्रेरी और One Tap प्रॉम्प्ट का इस्तेमाल किया जाएगा.

हम सर्वर साइड पर लॉगिन एंडपॉइंट भी सेट अप करेंगे, ताकि JWT आईडी टोकन की पुष्टि की जा सके.
आपको क्या सीखने को मिलेगा
- आईडी टोकन की पुष्टि करने के लिए, सर्वर-साइड लॉगिन एंडपॉइंट को सेट अप करने का तरीका
- किसी वेब पेज पर Google One Tap प्रॉम्प्ट जोड़ने का तरीका
- स्टैटिक एचटीएमएल एलिमेंट के तौर पर, और
- JavaScript का इस्तेमाल करके डाइनैमिक तरीके से किया जाता है.
- One Tap प्रॉम्प्ट कैसे काम करता है
आपको किन चीज़ों की ज़रूरत होगी
- एचटीएमएल, सीएसएस, JavaScript, और Chrome DevTools (या इसके जैसा कोई अन्य टूल) की बुनियादी जानकारी.
- एचटीएमएल और JavaScript फ़ाइलों में बदलाव करने और उन्हें होस्ट करने की जगह.
- पिछले कोडलैब में मिला क्लाइंट आईडी.
- ऐसा एनवायरमेंट जिसमें बुनियादी Python ऐप्लिकेशन चलाया जा सके.
चलिए, शुरू करते हैं!
2. लॉगिन एंडपॉइंट सेट अप करना
सबसे पहले, हम एक Python स्क्रिप्ट बनाएंगे. यह स्क्रिप्ट, बुनियादी वेब सर्वर के तौर पर काम करेगी. साथ ही, इसे चलाने के लिए ज़रूरी Python एनवायरमेंट सेट अप करेंगे.
लोकल तौर पर स्क्रिप्ट चलाने पर, यह ब्राउज़र को लैंडिंग पेज, स्टैटिक एचटीएमएल, और डाइनैमिक 'एक टैप से साइन इन करें' पेज उपलब्ध कराती है. यह POST अनुरोधों को स्वीकार करता है. साथ ही, क्रेडेंशियल पैरामीटर में मौजूद JWT को डिकोड करता है. इसके अलावा, यह पुष्टि करता है कि इसे Google Identity के OAuth प्लैटफ़ॉर्म ने जारी किया था.
जेडब्ल्यूटी को डिकोड करने और उसकी पुष्टि करने के बाद, स्क्रिप्ट नतीजों को दिखाने के लिए index.html लैंडिंग पेज पर रीडायरेक्ट करती है.
इसे simple-server.py नाम की फ़ाइल में कॉपी करें:
"""Very basic web server to handle GET and POST requests."""
from http.server import SimpleHTTPRequestHandler
import json
import socketserver
from typing import Dict, Optional, Tuple
import urllib.parse
from urllib.parse import parse_qs
from google.auth.transport import requests as google_auth_requests
from google.oauth2 import id_token
""" NOTE: You'll need to change this """
CLIENT_ID = (
"PUT_YOUR_WEB_CLIENT_ID_HERE"
)
""" these may change for a Cloud IDE, but good as-is for local termainals """
SERVER_ADDRESS = "0.0.0.0"
PORT = 3000
TARGET_HTML_PAGE_URL = f"http://localhost:{PORT}/"
""" and this is the end of constants you might need to change """
HTTP_STATUS_OK = 200
HTTP_STATUS_BAD_REQUEST = 400
HTTP_STATUS_UNAUTHORIZED = 401
HTTP_STATUS_INTERNAL_SERVER_ERROR = 500
HTTP_STATUS_FOUND = 303 # For redirection after decode and verify
OIDC_SERVER = "accounts.google.com"
class OIDCJWTReceiver(SimpleHTTPRequestHandler):
"""Request handler to securely process a Google ID token response."""
def _validate_csrf(self, request_parameters: Dict) -> Tuple[bool, str]:
"""Validates the g_csrf_token to protect against CSRF attacks."""
csrf_token_body = request_parameters.get("g_csrf_token")
if not csrf_token_body:
return False, "g_csrf_token not found in POST body."
csrf_token_cookie = None
cookie_header = self.headers.get("Cookie")
if cookie_header:
cookie_pairs = (c.split("=", 1) for c in cookie_header.split(";"))
cookies = {k.strip(): v.strip() for k, v in cookie_pairs}
csrf_token_cookie = cookies.get("g_csrf_token")
if not csrf_token_cookie:
return False, "g_csrf_token not found in cookie."
if csrf_token_body != csrf_token_cookie:
return False, "CSRF token mismatch."
return True, "CSRF token validated successfully."
def _parse_and_validate_credential(
self, request_parameters: Dict
) -> Optional[Tuple[Optional[Dict], str]]:
"""Parse POST data, extract, decode and validate user credential."""
credential = request_parameters.get("credential")
if not credential:
return None, "Credential not provided"
try:
id_info = id_token.verify_oauth2_token(
credential, google_auth_requests.Request(), CLIENT_ID
)
return id_info, ""
except ValueError as e:
return None, f"Error during JWT decode: {e}"
except Exception as e:
return None, f"Unexpected error during credential validation: {e}"
def _redirect_to_html(self, response_data: Dict) -> None:
"""Redirect to the target HTML page with data in the URL fragment."""
try:
json_data = json.dumps(response_data)
encoded_data = urllib.parse.quote(json_data)
redirect_url = f"http://localhost:{PORT}/#data={encoded_data}"
self.send_response(HTTP_STATUS_FOUND)
self.send_header("Location", redirect_url)
self.send_header("Connection", "close")
self.end_headers()
except Exception as e:
print(f"An error occurred during redirection: {e}")
self.send_response(HTTP_STATUS_INTERNAL_SERVER_ERROR)
self.send_header("Content-type", "text/plain")
self.send_header("Connection", "close")
self.end_headers()
self.wfile.write(f"A redirect error occurred: {e}".encode("utf-8"))
def _send_bad_request(self, message: str) -> None:
"""Sends a 400 Bad Request response."""
self.send_response(HTTP_STATUS_BAD_REQUEST)
self.send_header("Content-type", "text/plain")
self.send_header("Connection", "close")
self.end_headers()
self.wfile.write(message.encode("utf-8"))
def do_POST(self):
"""Handle POST requests for the /user-login path."""
if self.path != "/user-login":
self.send_error(404, "File not found")
return
try:
content_length = int(self.headers.get("Content-Length", 0))
post_data_bytes = self.rfile.read(content_length)
post_data_str = post_data_bytes.decode("utf-8")
request_parameters = {
key: val[0]
for key, val in parse_qs(post_data_str).items()
if len(val) == 1
}
csrf_valid, csrf_message = self._validate_csrf(request_parameters)
if not csrf_valid:
print(f"CSRF verify failure: {csrf_message}")
self._send_bad_request(f"CSRF verify failure: {csrf_message}")
return
decoded_id_token, error_message = self._parse_and_validate_credential(
request_parameters
)
response_data = {}
if decoded_id_token:
response_data["status"] = "success"
response_data["message"] = decoded_id_token
elif error_message:
response_data["status"] = "error"
response_data["message"] = error_message
else:
response_data["status"] = "error"
response_data["message"] = "Unknown error during JWT validation"
self._redirect_to_html(response_data)
except Exception as e:
self._redirect_to_html(
{"status": "error", "error_message": f"Internal server error: {e}"}
)
with socketserver.TCPServer(("", PORT), OIDCJWTReceiver) as httpd:
print(
f"Serving HTTP on {SERVER_ADDRESS} port"
f" {PORT} (http://{SERVER_ADDRESS}:{PORT}/)"
)
httpd.serve_forever()
हम आईडी टोकन के ऑडियंस (aud) फ़ील्ड का इस्तेमाल करके, यह पुष्टि करेंगे कि JWT आपके क्लाइंट को जारी किया गया था. इसलिए, आपके Python ऐप्लिकेशन को यह पता होना चाहिए कि किस क्लाइंट आईडी का इस्तेमाल किया जा रहा है. इसके लिए, PUT_YOUR_WEB_CLIENT_ID_HERE को उस क्लाइंट आईडी से बदलें जिसका इस्तेमाल आपने 'Google से साइन इन करें' बटन के पिछले कोडलैब में किया था.
Python एनवायरमेंट
इससे वेब सर्वर स्क्रिप्ट चलाने के लिए एनवायरमेंट सेट अप किया जा सकता है.
आपको JWT की पुष्टि करने और उसे डिकोड करने के लिए, Python 3.8 या इसके बाद का वर्शन और कुछ पैकेज की ज़रूरत होगी.
$ python3 --version
अगर आपके सिस्टम में Python 3.8 से पहले का वर्शन है, तो आपको अपने शेल का पाथ बदलना पड़ सकता है, ताकि सही वर्शन मिल सके. इसके अलावा, अपने सिस्टम पर Python का नया वर्शन इंस्टॉल किया जा सकता है.
इसके बाद, requirements.txt नाम की एक फ़ाइल बनाएं. इसमें JWT को डिकोड और उसकी पुष्टि करने के लिए ज़रूरी पैकेज की सूची दी गई हो:
google-auth
वर्चुअल एनवायरमेंट बनाने के लिए, simple-server.py और requirements.txt वाली डायरेक्ट्री में ये कमांड चलाएं. साथ ही, सिर्फ़ इस ऐप्लिकेशन के लिए पैकेज इंस्टॉल करें:
$ python3 -m venv env
$ source env/bin/activate
(env) $ pip install -r requirements.txt
अब सर्वर शुरू करें. अगर सब कुछ ठीक से काम कर रहा है, तो आपको यह दिखेगा:
(env) $ python3 ./simple-server.py
Serving HTTP on 0.0.0.0 port 3000 (http://0.0.0.0:3000/) ...
क्लाउड-आधारित आईडीई
इस कोडलैब को लोकल टर्मिनल और लोकल होस्ट पर चलाने के लिए डिज़ाइन किया गया था. हालांकि, कुछ बदलावों के साथ इसे Replit या Glitch जैसे प्लैटफ़ॉर्म पर इस्तेमाल किया जा सकता है. हर प्लैटफ़ॉर्म के लिए, सेटअप करने की अपनी ज़रूरतें होती हैं. साथ ही, Python एनवायरमेंट के डिफ़ॉल्ट सेटिंग भी अलग-अलग होती हैं. इसलिए, आपको TARGET_HTML_PAGE_URL और Python सेटअप जैसी कुछ चीज़ों में बदलाव करना पड़ सकता है.
उदाहरण के लिए, Glitch पर आपको अब भी requirements.txt जोड़ना होगा. हालांकि, Python सर्वर को अपने-आप शुरू करने के लिए, आपको start.sh नाम की फ़ाइल भी बनानी होगी:
python3 ./simple-server.py
Python स्क्रिप्ट और एचटीएमएल फ़ाइलों में इस्तेमाल किए गए यूआरएल को भी अपने Cloud IDE के बाहरी यूआरएल पर अपडेट करना होगा. इसलिए, हमारे पास कुछ ऐसा होगा: TARGET_HTML_PAGE_URL = f"https://your-project-name.glitch.me/". साथ ही, इस कोडलैब में मौजूद सभी एचटीएमएल फ़ाइलें डिफ़ॉल्ट रूप से लोकलहोस्ट का इस्तेमाल करती हैं. इसलिए, आपको उन्हें बाहरी Cloud IDE यूआरएल से अपडेट करना होगा: data-login_uri="https://your-project-name.glitch.me/user-login".
3. लैंडिंग पेज बनाना
इसके बाद, हम एक ऐसा लैंडिंग पेज बनाएंगे जिस पर One Tap की मदद से साइन इन करने के नतीजे दिखेंगे. पेज पर डिकोड किया गया JWT आईडी टोकन या गड़बड़ी दिखती है. पेज पर मौजूद फ़ॉर्म का इस्तेमाल करके, हमारे Python HTTP सर्वर पर मौजूद लॉगिन एंडपॉइंट को JWT भेजा जा सकता है. यहां इसे डिकोड और पुष्टि की जाती है. यह सीएसआरएफ़ डबल-सबमिट कुकी और POST अनुरोध पैरामीटर का इस्तेमाल करता है, ताकि यह codelab में gsi/client एचटीएमएल और JavaScript API के उदाहरणों की तरह ही user-login सर्वर एंडपॉइंट का फिर से इस्तेमाल कर सके.
अपने टर्मिनल में, इसे index.html नाम की फ़ाइल में सेव करें:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>JWT Verification</title>
<style>
body { font-family: sans-serif; margin: 0; }
.top-nav {
text-align: center; padding: 15px 0; background-color: #f8f9fa;
border-bottom: 1px solid #dee2e6; width: 100%;
}
.top-nav a {
margin: 0 15px; text-decoration: none; font-weight: bold;
color: #007bff; font-size: 1.1em;
}
.top-nav a:hover { text-decoration: underline; }
.page-container { padding: 20px; }
pre {
background-color: #f9f9f9; padding: 10px; overflow-x: auto;
white-space: pre-wrap; word-break: break-all;
}
.error { color: red; }
.success { color: green; }
#jwt-form { margin-bottom: 20px; flex: 1; }
fieldset {
border: 1px solid #ddd; padding: 10px; margin: 0;
min-width: 0; flex: 1; }
legend { font-weight: bold; margin-bottom: 5px; }
textarea { width: 100%; box-sizing: border-box; vertical-align: top; }
button[type="submit"] { padding: 8px 15px; margin-top: 10px; }
@media (min-width: 1024px) {
.main-content { display: flex; gap: 20px; }
.main-content > #jwt-form,
.main-content > .result-container {
flex-basis: 50%; /* Each item takes up 50% of the width */
margin-bottom: 0; /* Remove bottom margin when side-by-side */
display: flex; /* Make the result container a flex container */
flex-direction: column; /* Stack children vertically */
flex: 1; /* Allows the result container to grow and shrink */
}
}
</style>
</head>
<body>
<nav class="top-nav">
<a href="static-page.html">One Tap Static Page</a>
<a href="dynamic-page.html">One Tap Dynamic Page</a>
<a href="prompt-outcomes.html">Prompt behaviors</a>
</nav>
<div class="page-container">
<h1>JWT Verification</h1>
<div class="main-content">
<form id="jwt-form" action="/user-login" method="post">
<fieldset>
<legend>Encoded JWT ID Token</legend>
<textarea id="credential" name="credential" rows="5"
cols="50"></textarea>
<button type="submit">Verify JWT</button>
</fieldset>
</form>
<section class="result-container">
<fieldset>
<legend>Decode and verify result</legend>
<p id="status"></p>
<pre id="result"></pre>
</fieldset>
</section>
</div>
</div>
<script>
const statusElement = document.getElementById("status");
const resultElement = document.getElementById("result");
const handleResponse = (responseData) => {
const { status, message } = responseData;
const result = message
? status === "success"
? JSON.stringify(message, null, 2)
: message
: "";
statusElement.textContent = status;
resultElement.textContent = result;
statusElement.className = "";
if (status === "success") {
statusElement.classList.add("success");
} else if (status === "error") {
statusElement.classList.add("error");
}
};
const getEncodedDataFromHash = (hash) => {
const urlParams = new URLSearchParams(hash.substring(1));
return urlParams.get("data");
};
const processHashData = (hash) => {
const encodedData = getEncodedDataFromHash(hash);
if (encodedData) {
try {
const jsonData = JSON.parse(decodeURIComponent(encodedData));
handleResponse(jsonData);
history.pushState(
"",
document.title,
window.location.pathname + window.location.search,
);
} catch (error) {
handleResponse({
status: "error",
message: "Error parsing data from URL: " + error,
});
}
}
};
window.addEventListener("load",
() => processHashData(window.location.hash));
window.addEventListener("hashchange",
() => processHashData(window.location.hash));
</script>
<script>
document.addEventListener("DOMContentLoaded", () => {
const generateRandomString = (length) => {
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
"abcdefghijklmnopqrstuvwxyz" +
"0123456789";
let result = "";
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
};
const csrfToken = generateRandomString(12);
document.cookie = `g_csrf_token=${csrfToken};path=/;SameSite=Lax`;
const form = document.getElementById("jwt-form");
const hiddenInput = document.createElement("input");
hiddenInput.setAttribute("type", "hidden");
hiddenInput.setAttribute("name", "g_csrf_token");
hiddenInput.setAttribute("value", csrfToken);
form.appendChild(hiddenInput);
});
</script>
</body>
</html>
वेब सर्वर और JWT डिकोडिंग की जांच करना
One Tap का इस्तेमाल करने से पहले, हम यह पक्का करेंगे कि सर्वर एंडपॉइंट एनवायरमेंट सेटअप हो गया हो और काम कर रहा हो.
लैंडिंग पेज http://localhost:3000/ पर जाएं और Verify JWT बटन दबाएं.
आपको यह देखना चाहिए

बटन दबाने पर, एंट्री फ़ील्ड का कॉन्टेंट Python स्क्रिप्ट को POST के तौर पर भेजा जाता है. स्क्रिप्ट को एंट्री फ़ील्ड में एन्कोड किया गया JWT चाहिए. इसलिए, यह पेलोड को डिकोड करने और उसकी पुष्टि करने की कोशिश करती है. इसके बाद, यह नतीजों को दिखाने के लिए वापस लैंडिंग पेज पर रीडायरेक्ट हो जाता है.
लेकिन रुको, यहां कोई JWT नहीं है... क्या यह काम नहीं करेगा? हां, लेकिन शालीनता से!
फ़ील्ड खाली होने की वजह से, गड़बड़ी का मैसेज दिखता है. अब एंट्री फ़ील्ड में कोई टेक्स्ट (अपनी पसंद का कोई भी टेक्स्ट) डालें और बटन को फिर से दबाएं. डिकोड करने में गड़बड़ी की वजह से यह काम नहीं करता.
एंट्री फ़ील्ड में, Google से जारी किया गया एन्कोड किया गया JWT आईडी टोकन चिपकाया जा सकता है. इसके बाद, Python स्क्रिप्ट इसे डिकोड, पुष्टि, और आपके लिए दिखा सकती है... इसके अलावा, एन्कोड किए गए किसी भी JWT की जांच करने के लिए, https://jwt.io का इस्तेमाल किया जा सकता है.
4. स्टैटिक एचटीएमएल पेज
ठीक है, अब हम एचटीएमएल पेजों पर One Tap को सेट अप करेंगे. इसके लिए, किसी भी JavaScript का इस्तेमाल नहीं किया जाएगा. यह स्टैटिक साइटों या कैश मेमोरी सिस्टम और सीडीएन के लिए फ़ायदेमंद हो सकता है.
सबसे पहले, इस कोड सैंपल को static-page.html नाम की फ़ाइल में जोड़ें:
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://accounts.google.com/gsi/client" async></script>
<link rel="icon" href="data:," />
</head>
<body>
<h1>Google One Tap static HTML page</h1>
<div
id="g_id_onload"
data-client_id="PUT_YOUR_WEB_CLIENT_ID_HERE"
data-ux_mode="redirect"
data-login_uri="http://localhost:3000/user-login"
></div>
</body>
</html>
इसके बाद, static-page.html खोलें और PUT_YOUR_WEB_CLIENT_ID_HERE को उस क्लाइंट आईडी से बदलें जिसका इस्तेमाल आपने 'Google से साइन इन करें' बटन के पिछले कोडलैब में किया था.
इससे क्या होता है?
g_id_onload id वाला कोई भी एचटीएमएल एलिमेंट और उसके डेटा एट्रिब्यूट का इस्तेमाल, Google Identity Services लाइब्रेरी (gsi/client) को कॉन्फ़िगर करने के लिए किया जाता है. यह ब्राउज़र में दस्तावेज़ लोड होने पर, 'एक टैप करके साइन इन करें' प्रॉम्प्ट भी दिखाता है.
data-login_uri एट्रिब्यूट, वह यूआरआई होता है जिसे उपयोगकर्ता के साइन इन करने के बाद, ब्राउज़र से POST अनुरोध मिलेगा. इस अनुरोध में, Google की ओर से जारी किया गया एन्कोड किया गया JWT शामिल होता है.
One Tap के सभी विकल्पों की पूरी सूची देखने के लिए, एचटीएमएल कोड जनरेटर और एचटीएमएल एपीआई रेफ़रंस देखें.
साइन-इन करें
http://localhost:3000/static-page.html पर क्लिक करें.
आपको अपने ब्राउज़र में 'एक टैप करके साइन इन करें' प्रॉम्प्ट दिखेगा.

साइन इन करने के लिए, इसके तौर पर जारी रखें पर क्लिक करें.
साइन इन करने के बाद, Google आपके Python सर्वर के लॉगिन एंडपॉइंट को POST अनुरोध भेजता है. अनुरोध में, Google के हस्ताक्षर वाला एक एन्कोड किया गया JWT शामिल है.
इसके बाद, सर्वर Google की किसी सार्वजनिक साइनिंग कुंजी का इस्तेमाल करके यह पुष्टि करता है कि Google ने JWT बनाया है और उस पर हस्ताक्षर किए हैं. इसके बाद, यह ऑडियंस को डिकोड करता है और पुष्टि करता है कि ऑडियंस, आपके क्लाइंट आईडी से मेल खाती है. इसके बाद, सीएसआरएफ़ की जांच की जाती है. इससे यह पक्का किया जाता है कि कुकी की वैल्यू और POST बॉडी में मौजूद अनुरोध के पैरामीटर की वैल्यू बराबर है. अगर ऐसा नहीं होता है, तो यह समस्या का संकेत है.
आखिर में, लैंडिंग पेज पर पुष्टि किया गया JWT, JSON फ़ॉर्मैट वाले आईडी टोकन के उपयोगकर्ता क्रेडेंशियल के तौर पर दिखता है.

आम तौर पर होने वाली गड़बड़ियां
साइन इन करने की प्रोसेस पूरी न होने की कई वजहें हो सकती हैं. इसकी कुछ सामान्य वजहें ये हैं:
data-client_idमौजूद नहीं है या गलत है. इस मामले में, आपको DevTools कंसोल में एक गड़बड़ी दिखेगी और'एक टैप करके साइन इन करें' सुविधा काम नहीं करेगी.data-login_uriउपलब्ध नहीं है, क्योंकि गलत यूआरआई डाला गया है, वेब सर्वर शुरू नहीं किया गया है या गलत पोर्ट पर सुना जा रहा है. ऐसा होने पर, One Tap प्रॉम्प्ट काम करता हुआ दिखेगा. हालांकि, क्रेडेंशियल वापस मिलने पर, DevTools के नेटवर्क टैब में गड़बड़ी दिखेगी.- आपका वेब सर्वर जिस होस्टनेम या पोर्ट का इस्तेमाल कर रहा है वह आपके OAuth क्लाइंट आईडी के लिए, अनुमति वाले JavaScript ऑरिजिन की सूची में शामिल नहीं है. आपको कंसोल पर यह मैसेज दिखेगा: "आईडी असर्शन एंडपॉइंट फ़ेच करते समय, 400 एचटीटीपी रिस्पॉन्स कोड मिला.". अगर आपको इस कोडलैब के दौरान यह मैसेज दिखता है, तो पक्का करें कि
http://localhost/औरhttp://localhost:3000, दोनों लिस्ट किए गए हों.
5. डाइनैमिक पेज
अब हम JavaScript कॉल का इस्तेमाल करके, One Tap को दिखाएंगे. इस उदाहरण में, पेज लोड होने पर हम हमेशा एक टैप की सुविधा दिखाएंगे. हालांकि, आपके पास प्रॉम्प्ट को सिर्फ़ तब दिखाने का विकल्प होता है, जब इसकी ज़रूरत हो. उदाहरण के लिए, यह देखा जा सकता है कि उपयोगकर्ता का सेशन 28 दिनों से ज़्यादा पुराना है या नहीं. अगर ऐसा है, तो साइन इन करने का अनुरोध फिर से दिखाया जा सकता है.
इस कोड सैंपल को dynamic-page.html नाम की फ़ाइल में जोड़ें.
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://accounts.google.com/gsi/client" async></script>
<link rel="icon" href="data:," />
</head>
<body>
<h1>Google One Tap dynamic page</h1>
<script>
const generateRandomString = (length) => {
const array = new Uint8Array(length / 2);
window.crypto.getRandomValues(array);
return Array.from(array, (byte) =>
byte.toString(16).padStart(2, "0")
).join("");
};
const setCookie = (name, value) => {
document.cookie = '${name}=${value};path=/;SameSite=Lax';
};
const getCookie = (name) => {
const nameEQ = name + "=";
const ca = document.cookie.split(";");
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) == " ") c = c.substring(1, c.length);
if (c.indexOf(nameEQ) == 0)
return c.substring(nameEQ.length, c.length);
}
return null;
};
function handleResponse(rsp) {
console.log("ID Token received from Google: ", rsp.credential);
console.log("Submitting token to server via dynamic form POST.");
const form = document.createElement("form");
form.method = "POST";
form.action = "http://" + window.location.host + "/user-login";
// Add the credential and CSRF cookie value asa hidden fields
const hiddenField = document.createElement("input");
hiddenField.type = "hidden";
hiddenField.name = "credential";
hiddenField.value = rsp.credential;
form.appendChild(hiddenField);
const csrfToken = getCookie("g_csrf_token");
if (csrfToken) {
console.log("Found g_csrf_token cookie, adding to form.");
const csrfField = document.createElement("input");
csrfField.type = "hidden";
csrfField.name = "g_csrf_token";
csrfField.value = csrfToken;
form.appendChild(csrfField);
} else {
console.warn(
"Warning: g_csrf_token cookie not found. POSTing without it."
);
}
document.body.appendChild(form);
form.submit();
}
window.onload = function () {
const csrfToken = generateRandomString(12);
setCookie("g_csrf_token", csrfToken);
console.log("CSRF token cookie set on page load:", csrfToken);
google.accounts.id.initialize({
client_id: "PUT_YOUR_WEB_CLIENT_ID_HERE",
ux_mode: "popup",
callback: handleResponse,
});
google.accounts.id.prompt(); // Display the One Tap prompt
};
</script>
</body>
</html>
dynamic-page.html खोलें और PUT_YOUR_WEB_CLIENT_ID_HERE को उस क्लाइंट आईडी से बदलें जिसका इस्तेमाल आपने 'Google से साइन इन करें' बटन के पिछले कोडलैब में किया था.
यह कोड, एचटीएमएल और JavaScript का मिक्सचर है. इससे कई काम किए जा सकते हैं:
- यह
google.accounts.id.initialize()को कॉल करके, Google Identity Services लाइब्रेरी (gsi/client) को कॉन्फ़िगर करता है, - यह किसी दूसरी साइट से किए गए फ़र्ज़ी अनुरोध (CSRF) वाली कुकी जनरेट करती है,
- यह Google से एन्कोड किया गया JWT पाने के लिए, कॉलबैक हैंडलर जोड़ता है. साथ ही, इसे फ़ॉर्म POST का इस्तेमाल करके, हमारी Python स्क्रिप्ट
/user-loginएंडपॉइंट पर सबमिट करता है. google.accounts.id.prompt()का इस्तेमाल करके, One Tap प्रॉम्प्ट दिखाता है.
One Tap की सेटिंग की पूरी सूची, JavaScript API के रेफ़रंस में देखी जा सकती है.
चलिए, साइन-इन करते हैं!
अपने ब्राउज़र में http://localhost:3000/dynamic-page.html खोलें.
One Tap प्रॉम्प्ट का व्यवहार, स्टैटिक एचटीएमएल के जैसा ही होता है. हालांकि, यह पेज CSRF कुकी बनाने, Google से JWT पाने, और उसे Python सर्वर के user-login एंडपॉइंट पर पोस्ट करने के लिए, JavaScript कॉलबैक हैंडलर तय करता है. एचटीएमएल एपीआई, इन चरणों को आपके लिए अपने-आप पूरा करता है.

6. प्रॉम्प्ट के व्यवहार
इसलिए, आइए One Tap की कुछ सुविधाओं को आज़माएं. ऐसा इसलिए, क्योंकि बटन के उलट, प्रॉम्प्ट हमेशा नहीं दिखता. इसे ब्राउज़र और उपयोगकर्ता खारिज कर सकते हैं, बंद कर सकते हैं या बंद कर सकते हैं.
सबसे पहले, इसे prompt-outcomes.html नाम की फ़ाइल में सेव करें:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Google One Tap Prompt behaviors</title>
<style>
body { font-family: sans-serif; padding: 20px; }
#log {
border: 1px solid #ccc; background-color: #f0f0f0;
padding: 15px; margin-top: 20px;
white-space: pre-wrap; font-family: monospace;
}
.success { color: green; }
.error { color: red; }
.info { color: blue; }
.warning { color: orange; }
</style>
</head>
<body>
<h1>One Tap behaviors</h1>
<p>Open the developer console to see detailed logs.</p>
<div id="log">Awaiting events...</div>
<script src="https://accounts.google.com/gsi/client" async defer></script>
<script>
// logging utility to display event and notification info
const logElement = document.getElementById("log");
function log(message, type = "info") {
const timestamp = new Date().toLocaleTimeString();
logElement.innerHTML +=
`\n<span class="${type}">[${timestamp}] ${message}</span>`;
console.log(`[${type.toUpperCase()}] ${message}`);
}
function decodeJwt(jwt) {
try {
const parts = jwt.split(".");
if (parts.length !== 3) {
throw new Error("Invalid JWT structure");
}
const base64Url = parts[1];
const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
const jsonPayload = decodeURIComponent(
atob(base64)
.split("")
.map(function (c) {
return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
})
.join(""),
);
return JSON.parse(jsonPayload);
} catch (e) {
log(`Error decoding JWT: ${e.message}`, "error");
return null;
}
}
/* Handles the credential response after a user signs in. */
function handleCredentialResponse(credentialResponse) {
log("Credential Response received.", "success");
const credential = credentialResponse.credential;
log(`Credential JWT: ${credential.substring(0, 30)}...`);
// For demonstration, we decode the JWT on the client side.
// REMEMBER: Always verify the token on your backend server!
const payload = decodeJwt(credential);
if (payload) {
log(`Welcome, ${payload.name}! (Email: ${payload.email})`);
log("Decoded JWT Payload: " + JSON.stringify(payload, null, 2));
}
}
/* Handles notifications about the One-Tap prompt's UI status. */
function handlePromptMomentNotification(notification) {
log(`Prompt Moment Notification received.`, "info");
if (notification.isNotDisplayed()) {
const reason = notification.getNotDisplayedReason();
log(`Prompt not displayed. Reason: <strong>${reason}</strong>`,
"error");
}
if (notification.isSkippedMoment()) {
const reason = notification.getSkippedReason();
log(`Prompt was skipped. Reason: <strong>${reason}</strong>`,
"warning");
if (reason === "auto_cancel") {
log("may have called prompt() multiple times in a row.");
} else if (reason === "user_cancel") {
log("The user manually closed the prompt.");
}
}
if (notification.isDismissedMoment()) {
const reason = notification.getDismissedReason();
log(`Prompt dismissed. Reason: <strong>${reason}</strong>`, "info");
if (reason === "credential_returned") {
log("Expected, credential sent to the JS handler.");
} else if (reason === "cancel_called") {
log("programmatic call to google.accounts.id.cancel().");
}
}
}
window.onload = function () {
try {
google.accounts.id.initialize({
client_id: "PUT_YOUR_WEB_CLIENT_ID_HERE",
callback: handleCredentialResponse,
ux_mode: "popup",
});
google.accounts.id.prompt(handlePromptMomentNotification);
log("One Tap initialized. Waiting for prompt...");
} catch (e) {
log(`Initialization Error: ${e.message}`, "error");
}
};
</script>
</body>
</html>
इसके बाद, prompt-outcomes.html खोलें. PUT_YOUR_WEB_CLIENT_ID_HERE को अपने क्लाइंट आईडी से बदलें. इसके बाद, फ़ाइल सेव करें.
अपने ब्राउज़र में, http://localhost:3000/prompt-outcomes.html खोलें
पेज पर मिले क्लिक
शुरू करने के लिए, 'एक टैप से साइन इन करें' प्रॉम्प्ट के बाहर कहीं भी क्लिक करें. आपको पेज और कंसोल, दोनों पर "अनुरोध रद्द कर दिया गया है." लॉग किया हुआ दिखेगा.
साइन इन करें
इसके बाद, सामान्य तरीके से साइन इन करें. आपको लॉगिंग और सूचना से जुड़े अपडेट दिखेंगे. इनका इस्तेमाल, उपयोगकर्ता सेशन को शुरू करने या रीफ़्रेश करने जैसे कामों के लिए किया जा सकता है.
प्रॉम्प्ट बंद करें
अब पेज को फिर से लोड करें. इसके बाद, जब One Tap दिखे, तो उसके टाइटल बार में मौजूद ‘X' पर क्लिक करें. यह मैसेज कंसोल में लॉग किया जाना चाहिए:
- "उपयोगकर्ता ने प्रॉम्प्ट को खारिज कर दिया या अस्वीकार कर दिया. एपीआई के लिए, एक्स्पोनेंशियल कूल डाउन ट्रिगर किया गया."
टेस्टिंग के दौरान, कूल डाउन की सुविधा चालू की जाएगी. कूल डाउन पीरियड के दौरान, 'एक टैप करके साइन इन करें' प्रॉम्प्ट नहीं दिखता है. जांच के दौरान, आपको शायद इसे अपने-आप रीसेट होने का इंतज़ार करने के बजाय, इसे रीसेट करना पड़े... ऐसा तब तक होगा, जब तक आपको कॉफ़ी पीने या घर जाकर सोने का मन न हो. कूलडाउन को रीसेट करने के लिए:
- ब्राउज़र के पता बार की बाईं ओर मौजूद, "साइट की जानकारी" आइकॉन पर क्लिक करें,
- "अनुमतियां रीसेट करें" बटन दबाएं.
- पेज को फिर से लोड करें.
कूलडाउन को रीसेट करने और पेज को फिर से लोड करने के बाद, 'एक टैप करके साइन इन करें' प्रॉम्प्ट दिखेगा.
7. नतीजा
इसलिए, इस कोडलैब में आपने कुछ चीज़ें सीखी हैं. जैसे, सिर्फ़ स्टैटिक एचटीएमएल का इस्तेमाल करके या JavaScript की मदद से डाइनैमिक तरीके से, 'एक टैप से साइन इन करें' सुविधा को कैसे दिखाया जाता है.
आपने स्थानीय टेस्टिंग के लिए, एक बुनियादी Python वेब सर्वर सेट अप किया. साथ ही, आईडी टोकन को डिकोड और पुष्टि करने के लिए ज़रूरी चरणों के बारे में जाना.
आपने उन सबसे सामान्य तरीकों का इस्तेमाल किया हो जिनसे उपयोगकर्ता, 'एक टैप करके साइन इन करें' प्रॉम्प्ट के साथ इंटरैक्ट करते हैं और उसे खारिज करते हैं. साथ ही, आपके पास एक ऐसा वेब पेज हो जिसका इस्तेमाल, प्रॉम्प्ट के व्यवहार को डीबग करने के लिए किया जा सकता हो.
बधाई हो!
ज़्यादा क्रेडिट पाने के लिए, उन ब्राउज़र में जाकर One Tap का इस्तेमाल करें जिन पर यह काम करता है.
इन लिंक से, आपको अगले चरणों के बारे में जानकारी मिल सकती है:
- Google Identity Services HTML API
- Google Identity Services JavaScript API
- वेब के लिए, 'Google से साइन इन करें' सुविधा को सेटअप करने का तरीका
- Google आईडी टोकन की पुष्टि करना
- Google Cloud के प्रोजेक्ट के बारे में ज़्यादा जानें
- Google Identity के पुष्टि करने के तरीके