1. Introducción
Este codelab se basa en el codelab Botón de acceso con Google para la Web, así que asegúrate de completarlo primero.
En este codelab, usarás la biblioteca de JavaScript de Google Identity Services y las indicaciones de One Tap para agregar el acceso de usuarios a páginas web estáticas y dinámicas con las APIs de HTML y JavaScript.
También configuraremos un extremo de acceso del servidor para verificar los tokens de ID de JWT.
Qué aprenderás
- Cómo configurar un extremo de acceso del servidor para verificar tokens de ID
- Cómo agregar un mensaje de One Tap de Google a una página web
- como un elemento HTML estático
- de forma dinámica con JavaScript.
- Cómo se comporta el mensaje de One Tap
Requisitos
- Conocimientos básicos de HTML, CSS, JavaScript y las Herramientas para desarrolladores de Chrome (o herramientas equivalentes)
- Un lugar para editar y alojar archivos HTML y JavaScript
- El ID de cliente que obtuviste en el codelab anterior
- Un entorno capaz de ejecutar una app básica de Python
¡Comencemos!
2. Configura un extremo de acceso
Primero, crearemos una secuencia de comandos de Python que actúe como un servidor web básico y configuraremos el entorno de Python necesario para ejecutarlo.
Cuando se ejecuta de forma local, la secuencia de comandos entrega la página de destino y las páginas HTML estáticas y dinámicas de One Tap al navegador. Acepta solicitudes POST, decodifica el JWT que se incluye en el parámetro de credenciales y valida que se haya emitido a través de la plataforma de OAuth de Google Identity.
Después de decodificar y verificar un JWT, la secuencia de comandos redirecciona a la página de destino index.html
para mostrar los resultados.
Copia lo siguiente en un archivo llamado 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()
Dado que verificaremos que el JWT se haya emitido para tu cliente con el campo de público (aud) del token de ID, tu app de Python debe saber qué ID de cliente se está usando. Para ello, reemplaza PUT_YOUR_WEB_CLIENT_ID_HERE
por el ID de cliente que usaste en el codelab anterior del botón de Acceder con Google.
Entorno de Python
Configuraremos el entorno para ejecutar la secuencia de comandos del servidor web.
Necesitarás Python 3.8 o una versión posterior, junto con algunos paquetes para ayudar con la verificación y la decodificación de JWT.
$ python3 --version
Si tu versión de python3 es inferior a 3.8, es posible que debas cambiar la RUTA de acceso de tu shell para que se encuentre la versión esperada o instalar una versión más reciente de Python en tu sistema.
A continuación, crea un archivo llamado requirements.txt
que enumere los paquetes que necesitamos para la decodificación y verificación de JWT:
google-auth
Ejecuta estos comandos en el mismo directorio que simple-server.py
y requirements.txt
para crear un entorno virtual y, luego, instalar paquetes solo para esta app:
$ python3 -m venv env
$ source env/bin/activate
(env) $ pip install -r requirements.txt
Ahora inicia el servidor y, si todo funciona correctamente, verás lo siguiente:
(env) $ python3 ./simple-server.py
Serving HTTP on 0.0.0.0 port 3000 (http://0.0.0.0:3000/) ...
IDE basados en la nube
Este codelab se diseñó para ejecutarse en una terminal local y en localhost, pero, con algunos cambios, se puede usar en plataformas como Replit o Glitch. Cada plataforma tiene sus propios requisitos de configuración y valores predeterminados del entorno de Python, por lo que es probable que debas cambiar algunas cosas, como TARGET_HTML_PAGE_URL
y la configuración de Python.
Por ejemplo, en Glitch, aún agregarías un requirements.txt
, pero también crearías un archivo llamado start.sh
para iniciar automáticamente el servidor de Python:
python3 ./simple-server.py
Las URLs que usan la secuencia de comandos de Python y los archivos HTML también deben actualizarse a la URL externa de tu IDE de Cloud. Por lo tanto, tendríamos algo como TARGET_HTML_PAGE_URL = f"https://your-project-name.glitch.me/"
y, como los archivos HTML de este codelab también usan localhost de forma predeterminada, deberás actualizarlos con la URL externa del IDE de Cloud: data-login_uri="https://your-project-name.glitch.me/user-login"
.
3. Cree una página de destino
A continuación, crearemos una página de destino que muestre los resultados del acceso con Un toque. En la página, se muestra el token de ID de JWT decodificado o un error. También se puede usar un formulario en la página para enviar un JWT al extremo de acceso en nuestro servidor HTTP de Python, donde se decodifica y verifica. Utiliza una cookie de doble envío de CSRF y un parámetro de solicitud POST para poder reutilizar el mismo extremo del servidor user-login
que los ejemplos de la API de gsi/client
HTML y JavaScript en el codelab.
En tu terminal, guarda esto en un archivo llamado 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>
Prueba el servidor web y la decodificación de JWT
Antes de intentar trabajar con One Tap, nos aseguraremos de que el entorno del extremo del servidor esté configurado y funcionando.
Ve a la página de destino, http://localhost:3000/, y presiona el botón Verify JWT.
Deberías ver lo siguiente:
Cuando se presiona el botón, se envía una solicitud POST con el contenido del campo de entrada a la secuencia de comandos de Python. El script espera que haya un JWT codificado en el campo de entrada, por lo que intenta decodificar y verificar la carga útil. Luego, se redirecciona a la página de destino para mostrar los resultados.
Pero espera, no había un JWT… ¿no debería haber fallado? Sí, pero con gracia.
Como el campo estaba vacío, se muestra un error. Ahora intenta ingresar texto (el que quieras) en el campo de entrada y presiona el botón de nuevo. Falla con un error de decodificación diferente.
Puedes pegar un token de ID de JWT emitido por Google codificado en el campo de entrada y hacer que el script de Python lo decodifique, verifique y muestre por ti… o puedes usar https://jwt.io para inspeccionar cualquier JWT codificado.
4. Página HTML estática
De acuerdo. Ahora configuraremos One Tap para que funcione en páginas HTML sin usar JavaScript. Esto puede ser útil para sitios estáticos o para sistemas de almacenamiento en caché y CDN.
Para comenzar, agrega este código de muestra a un archivo llamado 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>
A continuación, abre static-page.html
y reemplaza PUT_YOUR_WEB_CLIENT_ID_HERE
por el ID de cliente que usaste en el codelab anterior del botón Acceder con Google.
¿Qué hace?
Cualquier elemento HTML con un id
de g_id_onload
y sus atributos de datos se usan para configurar la biblioteca de Google Identity Services (gsi/client
). También muestra el mensaje de One Tap cuando se carga el documento en el navegador.
El atributo data-login_uri
es el URI que recibirá una solicitud POST del navegador después de que el usuario acceda. Esta solicitud contiene el JWT codificado que emitió Google.
Consulta el generador de código HTML y la referencia de la API de HTML para obtener una lista completa de las opciones de Un toque.
Acceso
Haz clic en http://localhost:3000/static-page.html.
Deberías ver la instrucción de Un toque en tu navegador.
Presiona Continuar como para acceder.
Después de acceder, Google envía una solicitud POST al extremo de acceso de tu servidor de Python. La solicitud contiene un JWT codificado y firmado por Google.
A partir de ahí, el servidor usa una de las claves de firma públicas de Google para verificar que Google creó y firmó el JWT. Luego, decodifica y verifica si el público coincide con tu ID de cliente. A continuación, se realiza una verificación de CSRF para asegurarse de que el valor de la cookie y el valor del parámetro de la solicitud en el cuerpo de POST sean iguales. Si no lo están, es una señal segura de problemas.
Por último, la página de destino muestra el JWT verificado correctamente como una credencial de usuario de token de ID con formato JSON.
Errores comunes
Existen varias formas en que puede fallar el flujo de acceso. Estos son algunos de los motivos más comunes:
- Falta
data-client_id
o es incorrecto. En este caso, verás un error en la consola de Herramientas para desarrolladores y no funcionará el mensaje de One Tap. data-login_uri
no está disponible porque se ingresó un URI incorrecto, no se inició el servidor web o está escuchando en el puerto incorrecto. Si esto sucede, parecerá que la solicitud de Un toque funciona, pero se mostrará un error en la pestaña de red de Herramientas para desarrolladores cuando se devuelva la credencial.- El nombre de host o el puerto que usa tu servidor web no se encuentran en los Orígenes de JavaScript autorizados de tu ID de cliente de OAuth. Verás el siguiente mensaje en la consola: "When fetching the ID assertion endpoint, a 400 HTTP response code was received.". Si ves esto durante este codelab, verifica que aparezcan
http://localhost/
yhttp://localhost:3000
.
5. Página dinámica
Ahora mostraremos One Tap con una llamada de JavaScript. En este ejemplo, siempre mostraremos One Tap cuando se cargue la página, pero puedes optar por mostrar el mensaje solo cuando sea necesario. Por ejemplo, puedes verificar si la sesión del usuario tiene más de 28 días y volver a mostrar el mensaje de acceso.
Agrega este código de muestra a un archivo llamado 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>
Abre dynamic-page.html
y reemplaza PUT_YOUR_WEB_CLIENT_ID_HERE
por el ID de cliente que usaste en el codelab anterior del botón Sign In With Google.
Este código es una combinación de HTML y JavaScript, y realiza varias acciones:
- Configura la biblioteca de Google Identity Services (
gsi/client
) llamando agoogle.accounts.id.initialize()
. - Genera una cookie de falsificación de solicitudes de sitios cruzados (CSRF).
- agrega un controlador de devolución de llamada para recibir el JWT codificado de Google y enviarlo con un POST de formulario a nuestro extremo
/user-login
de la secuencia de comandos de Python - muestra el mensaje de One Tap con
google.accounts.id.prompt()
.
Puedes encontrar una lista completa de los parámetros de configuración de One Tap en la referencia de la API de JavaScript.
¡Accedamos!
Abre http://localhost:3000/dynamic-page.html en tu navegador.
El comportamiento de la ventana emergente de Acceder con un toque es el mismo que en el caso del HTML estático, excepto que esta página define un controlador de devolución de llamada de JavaScript para crear una cookie de CSRF, recibir el JWT de Google y enviarlo con POST al extremo user-login
del servidor de Python. La API de HTML realiza estos pasos automáticamente por ti.
6. Comportamientos de las instrucciones
Así que probemos algunas cosas con One Tap, ya que, a diferencia del botón, el mensaje no siempre se muestra. El navegador y el usuario pueden descartarla, cerrarla o inhabilitarla.
Primero, guarda lo siguiente en un archivo llamado 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>
A continuación, abre prompt-outcomes.html
, reemplaza PUT_YOUR_WEB_CLIENT_ID_HERE
por tu ID de cliente y, luego, guarda el archivo.
En tu navegador, abre http://localhost:3000/prompt-outcomes.html.
Clics en la página
Para comenzar, haz clic en cualquier lugar fuera de la instrucción de One Tap. Deberías ver el mensaje "Se anuló la solicitud." registrado en la página y en la consola.
con Google
Luego, accede normalmente. Verás actualizaciones de registro y notificaciones que se pueden usar para activar acciones como establecer o actualizar una sesión del usuario.
Cerrar la instrucción
Ahora, vuelve a cargar la página y, después de que se muestre One Tap, presiona la "X" en la barra de título. Este mensaje se debe registrar en la consola:
- "El usuario rechazó o descartó el mensaje. Se activó el enfriamiento exponencial de la API."
Durante la prueba, activarás el enfriamiento. Durante el período de inactividad, no se muestra el mensaje de One Tap. Durante las pruebas, probablemente querrás restablecerlo en lugar de esperar a que se restablezca automáticamente… a menos que realmente quieras ir a tomar un café o irte a casa a dormir. Para restablecer el tiempo de espera, haz lo siguiente:
- Haz clic en el ícono de "información del sitio" en el lado izquierdo de la barra de direcciones del navegador.
- presiona el botón “Restablecer permisos”.
- volver a cargar la página.
Después de restablecer el tiempo de espera y volver a cargar la página, se mostrará el mensaje de One Tap.
7. Conclusión
En este codelab, aprendiste algunas cosas, como mostrar One Tap solo con HTML estático o de forma dinámica con JavaScript.
Configuraste un servidor web de Python muy básico para realizar pruebas locales y aprendiste los pasos necesarios para decodificar y validar tokens de ID.
Probaste las formas más comunes en que los usuarios interactúan con el mensaje de Un toque y lo descartan, y tienes una página web que se puede usar para depurar el comportamiento del mensaje.
¡Felicitaciones!
Para obtener puntos adicionales, vuelve a intentarlo y usa One Tap en los diferentes navegadores que admite.
Estos vínculos pueden ayudarte con los próximos pasos:
- API de HTML de Google Identity Services
- API de JavaScript de Google Identity Services
- Cómo configurar Acceder con Google para la Web
- Cómo verificar un token de ID de Google
- Más información sobre los proyectos de Google Cloud
- Métodos de autenticación de la Identidad de Google