Usa OAuth 2.0 para aplicaciones de servidor web

En este documento, se explica cómo las aplicaciones de servidor web usan bibliotecas cliente de la API de Google o extremos de Google OAuth 2.0 para implementar la autorización de OAuth 2.0 y acceder a la API de YouTube Data.

OAuth 2.0 permite que los usuarios compartan datos específicos con una aplicación y, al mismo tiempo, mantengan la privacidad de sus nombres de usuario, contraseñas y otra información. Por ejemplo, una aplicación puede usar OAuth 2.0 para obtener permiso para recuperar los datos de YouTube de un canal.

Este flujo de OAuth 2.0 es específico para la autorización del usuario. Está diseñado para aplicaciones que pueden almacenar información confidencial y mantener el estado. Una aplicación de servidor web correctamente autorizada puede acceder a una API mientras el usuario interactúa con ella o después de que el usuario la abandona.

Las aplicaciones de servidor web también suelen usar cuentas de servicio para autorizar solicitudes a la API, en particular, cuando se llaman a las APIs de Cloud para acceder a datos basados en el proyecto en lugar de datos específicos del usuario. Las aplicaciones de servidor web pueden usar cuentas de servicio junto con la autorización del usuario.

La API de YouTube Live Streaming no admite el flujo de cuentas de servicio. Dado que no hay forma de vincular una cuenta de servicio a una cuenta de YouTube, los intentos de autorizar solicitudes con este flujo generarán un error NoLinkedYouTubeAccount.

Bibliotecas cliente

Los ejemplos específicos de lenguaje de esta página usan bibliotecas cliente de la API de Google para implementar la autorización de OAuth 2.0. Para ejecutar las muestras de código, primero debes instalar la biblioteca cliente de tu lenguaje.

Cuando usas una biblioteca cliente de la API de Google para controlar el flujo de OAuth 2.0 de tu aplicación, la biblioteca cliente realiza muchas acciones que, de otro modo, la aplicación tendría que controlar por sí sola. Por ejemplo, determina cuándo la aplicación puede usar o actualizar los tokens de acceso almacenados, así como cuándo debe volver a adquirir el consentimiento. La biblioteca cliente también genera URLs de redireccionamiento correctas y ayuda a implementar controladores de redireccionamiento que intercambian códigos de autorización por tokens de acceso.

Las bibliotecas cliente de la API de Google para aplicaciones del servidor están disponibles para los siguientes lenguajes:

Requisitos previos

Habilita las API para tu proyecto.

Cualquier aplicación que llame a las APIs de Google debe habilitarlas en API Console.

Para habilitar una API en tu proyecto, sigue estos pasos:

  1. Open the API Library en Google API Console.
  2. If prompted, select a project, or create a new one.
  3. Usa la página Biblioteca para buscar y habilitar la API de YouTube Data. Busca otras APIs que usará tu aplicación y habilítala.

Crea credenciales de autorización

Cualquier aplicación que use OAuth 2.0 para acceder a las APIs de Google debe tener credenciales de autorización que identifiquen la aplicación para el servidor OAuth 2.0 de Google. En los siguientes pasos, se explica cómo crear credenciales para tu proyecto. Luego, tus aplicaciones podrán usar las credenciales para acceder a las APIs que habilitaste para ese proyecto.

  1. Go to the Credentials page.
  2. Haz clic en Crear credenciales > ID de cliente de OAuth.
  3. Selecciona el tipo de aplicación Aplicación web.
  4. Completa el formulario y haz clic en Crear. Las aplicaciones que usan lenguajes y frameworks como PHP, Java, Python, Ruby y .NET deben especificar URIs de redireccionamiento autorizados. Los URIs de redireccionamiento son los extremos a los que el servidor de OAuth 2.0 puede enviar respuestas. Estos extremos deben cumplir con las reglas de validación de Google.

    Para las pruebas, puedes especificar URIs que hagan referencia a la máquina local, como http://localhost:8080. Con esto en mente, ten en cuenta que todos los ejemplos de este documento usan http://localhost:8080 como el URI de redireccionamiento.

    Te recomendamos que diseñas los extremos de autenticación de tu app para que tu aplicación no exponga códigos de autorización a otros recursos de la página.

Después de crear tus credenciales, descarga el archivo client_secret.json de API Console. Almacena el archivo de forma segura en una ubicación a la que solo pueda acceder tu aplicación.

Identifica los permisos de acceso

Los alcances permiten que la aplicación solo solicite acceso a los recursos que necesita, al tiempo que permiten a los usuarios controlar el nivel de acceso que otorgan a tu aplicación. Por lo tanto, puede haber una relación inversa entre la cantidad de permisos solicitados y la probabilidad de obtener el consentimiento del usuario.

Antes de que comiences a implementar la autorización de OAuth 2.0, te recomendamos identificar los alcances para los que la aplicación necesitará permiso de acceso.

También recomendamos que tu aplicación solicite acceso a los alcances de autorización a través de un proceso de autorización incremental, en el que solicite acceso a los datos del usuario en contexto. Esta práctica recomendada ayuda a los usuarios a comprender con mayor facilidad por qué tu aplicación necesita el acceso que solicita.

La versión 3 de la API de YouTube Data usa los siguientes permisos:

Permisos
https://www.googleapis.com/auth/youtubeAdministrar tu cuenta de YouTube
https://www.googleapis.com/auth/youtube.channel-memberships.creatorVer una lista de los miembros actuales y activos de su canal, su nivel actual y el momento en que se hicieron miembros
https://www.googleapis.com/auth/youtube.force-sslVea, edite y borre de forma permanente sus videos, calificaciones, comentarios y subtítulos de YouTube
https://www.googleapis.com/auth/youtube.readonlyPermite ver tu cuenta de YouTube.
https://www.googleapis.com/auth/youtube.uploadPermite administrar tus videos de YouTube.
https://www.googleapis.com/auth/youtubepartnerVer y administrar sus elementos y el contenido asociado en YouTube
https://www.googleapis.com/auth/youtubepartner-channel-auditPermite ver información privada de tu canal de YouTube que sea relevante durante el proceso de auditoría con un socio de YouTube.

El documento Alcances de la API de OAuth 2.0 contiene una lista completa de los permisos que puedes usar para acceder a las APIs de Google.

Requisitos específicos del idioma

Para ejecutar cualquiera de los ejemplos de código de este documento, necesitarás una Cuenta de Google, acceso a Internet y un navegador web. Si usas una de las bibliotecas cliente de la API, consulta también los requisitos específicos del lenguaje a continuación.

PHP

Para ejecutar las muestras de código de PHP en este documento, necesitarás lo siguiente:

  • PHP 8.0 o una versión posterior con la interfaz de línea de comandos (CLI) y la extensión JSON instaladas
  • La herramienta de administración de dependencias Composer
  • La biblioteca cliente de las APIs de Google para PHP:

    composer require google/apiclient:^2.15.0

Consulta la biblioteca cliente de las APIs de Google para PHP para obtener más información.

Python

Para ejecutar las muestras de código de Python en este documento, necesitarás lo siguiente:

  • Python 3.7 o versiones posteriores
  • La herramienta de administración de paquetes pip
  • La versión 2.0 de la biblioteca cliente de las APIs de Google para Python incluye lo siguiente:
    pip install --upgrade google-api-python-client
  • google-auth, google-auth-oauthlib y google-auth-httplib2 para la autorización del usuario
    pip install --upgrade google-auth google-auth-oauthlib google-auth-httplib2
  • El framework de aplicaciones web de Flask en Python
    pip install --upgrade flask
  • La biblioteca HTTP requests
    pip install --upgrade requests

Revisa la nota de la versión de la biblioteca cliente de Python de la API de Google si no puedes actualizar Python y la guía de migración asociada.

Ruby

Para ejecutar las muestras de código de Ruby en este documento, necesitarás lo siguiente:

  • Ruby 2.6 o una versión posterior
  • La biblioteca de Google Auth para Ruby:

    gem install googleauth
  • Las bibliotecas cliente de las APIs de Google Drive y Calendario:

    gem install google-apis-drive_v3 google-apis-calendar_v3
  • El framework de aplicaciones web de Ruby Sinatra.

    gem install sinatra

Node.js

Para ejecutar las muestras de código de Node.js en este documento, necesitarás lo siguiente:

  • La versión LTS de mantenimiento, la LTS activa o la versión actual de Node.js
  • El cliente de Node.js de las APIs de Google:

    npm install googleapis crypto express express-session

HTTP/REST

No necesitas instalar ninguna biblioteca para poder llamar directamente a los extremos de OAuth 2.0.

Obtén tokens de acceso de OAuth 2.0

En los siguientes pasos, se muestra cómo tu aplicación interactúa con el servidor de OAuth 2.0 de Google para obtener el consentimiento de un usuario y realizar una solicitud a la API en su nombre. Tu aplicación debe tener ese consentimiento para poder ejecutar una solicitud a la API de Google que requiera autorización del usuario.

En la siguiente lista, se resumen rápidamente estos pasos:

  1. Tu aplicación identifica los permisos que necesita.
  2. Tu aplicación redirecciona al usuario a Google junto con la lista de permisos solicitados.
  3. El usuario decide si otorgar los permisos a tu aplicación.
  4. Tu aplicación descubre lo que decidió el usuario.
  5. Si el usuario otorgó los permisos solicitados, tu aplicación recupera los tokens necesarios para realizar solicitudes a la API en nombre del usuario.

Paso 1: Establece los parámetros de autorización

El primer paso es crear la solicitud de autorización. Esa solicitud establece parámetros que identifican tu aplicación y definen los permisos que se le pedirá al usuario que le otorgue.

  • Si usas una biblioteca cliente de Google para la autenticación y autorización de OAuth 2.0, debes crear y configurar un objeto que defina estos parámetros.
  • Si llamas al extremo de Google OAuth 2.0 directamente, generarás una URL y establecerás los parámetros en esa URL.

En las siguientes pestañas, se definen los parámetros de autorización admitidos para las aplicaciones de servidor web. En los ejemplos específicos de cada lenguaje, también se muestra cómo usar una biblioteca cliente o de autorización para configurar un objeto que establezca esos parámetros.

PHP

En el siguiente fragmento de código, se crea un objeto Google\Client(), que define los parámetros en la solicitud de autorización.

Ese objeto usa información de tu archivo client_secret.json para identificar tu aplicación. (Consulta Cómo crear credenciales de autorización para obtener más información sobre ese archivo). El objeto también identifica los permisos a los que tu aplicación solicita acceso y la URL al extremo de autenticación de tu aplicación, que controlará la respuesta del servidor de OAuth 2.0 de Google. Por último, el código establece los parámetros opcionales access_type y include_granted_scopes.

Por ejemplo, para solicitar acceso sin conexión para recuperar los datos de YouTube de un usuario, haz lo siguiente:

use Google\Client;

$client = new Client();

// Required, call the setAuthConfig function to load authorization credentials from
// client_secret.json file.
$client->setAuthConfig('client_secret.json');

// Required, to set the scope value, call the addScope function
$client->addScope(Google_Service_YouTube::YOUTUBE_FORCE_SSL);

// Required, call the setRedirectUri function to specify a valid redirect URI for the
// provided client_id
$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php');

// Recommended, offline access will give you both an access and refresh token so that
// your app can refresh the access token without user interaction.
$client->setAccessType('offline');

// Recommended, call the setState function. Using a state value can increase your assurance that
// an incoming connection is the result of an authentication request.
$client->setState($sample_passthrough_value);

// Optional, if your application knows which user is trying to authenticate, it can use this
// parameter to provide a hint to the Google Authentication Server.
$client->setLoginHint('hint@example.com');

// Optional, call the setPrompt function to set "consent" will prompt the user for consent
$client->setPrompt('consent');

// Optional, call the setIncludeGrantedScopes function with true to enable incremental
// authorization
$client->setIncludeGrantedScopes(true);

Python

En el siguiente fragmento de código, se usa el módulo google-auth-oauthlib.flow para crear la solicitud de autorización.

El código crea un objeto Flow, que identifica tu aplicación con información del archivo client_secret.json que descargaste después de crear credenciales de autorización. Ese objeto también identifica los permisos a los que tu aplicación solicita permiso para acceder y la URL al extremo de autenticación de tu aplicación, que controlará la respuesta del servidor de OAuth 2.0 de Google. Por último, el código configura los parámetros opcionales access_type y include_granted_scopes.

Por ejemplo, para solicitar acceso sin conexión para recuperar los datos de YouTube de un usuario, haz lo siguiente:

import google.oauth2.credentials
import google_auth_oauthlib.flow

# Required, call the from_client_secrets_file method to retrieve the client ID from a
# client_secret.json file. The client ID (from that file) and access scopes are required. (You can
# also use the from_client_config method, which passes the client configuration as it originally
# appeared in a client secrets file but doesn't access the file itself.)
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file('client_secret.json',
    scopes=['https://www.googleapis.com/auth/youtube.force-ssl',
            'https://www.googleapis.com/auth/calendar.readonly'])

# Required, indicate where the API server will redirect the user after the user completes
# the authorization flow. The redirect URI is required. The value must exactly
# match one of the authorized redirect URIs for the OAuth 2.0 client, which you
# configured in the API Console. If this value doesn't match an authorized URI,
# you will get a 'redirect_uri_mismatch' error.
flow.redirect_uri = 'https://www.example.com/oauth2callback'

# Generate URL for request to Google's OAuth 2.0 server.
# Use kwargs to set optional request parameters.
authorization_url, state = flow.authorization_url(
    # Recommended, enable offline access so that you can refresh an access token without
    # re-prompting the user for permission. Recommended for web server apps.
    access_type='offline',
    # Optional, enable incremental authorization. Recommended as a best practice.
    include_granted_scopes='true',
    # Optional, if your application knows which user is trying to authenticate, it can use this
    # parameter to provide a hint to the Google Authentication Server.
    login_hint='hint@example.com',
    # Optional, set prompt to 'consent' will prompt the user for consent
    prompt='consent')

Ruby

Usa el archivo client_secrets.json que creaste para configurar un objeto cliente en tu aplicación. Cuando configuras un objeto cliente, especificas los permisos a los que tu aplicación necesita acceder, junto con la URL al extremo de autenticación de tu aplicación, que controlará la respuesta del servidor de OAuth 2.0.

Por ejemplo, para solicitar acceso sin conexión para recuperar los datos de YouTube de un usuario, haz lo siguiente:

require 'googleauth'
require 'googleauth/web_user_authorizer'
require 'googleauth/stores/redis_token_store'

require 'google/apis/youtube_v3'
require 'google/apis/calendar_v3'

# Required, call the from_file method to retrieve the client ID from a
# client_secret.json file.
client_id = Google::Auth::ClientId.from_file('/path/to/client_secret.json')

# Required, scope value 
# Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar.
scope = ['Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY',
         'Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY']

# Required, Authorizers require a storage instance to manage long term persistence of
# access and refresh tokens.
token_store = Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new)

# Required, indicate where the API server will redirect the user after the user completes
# the authorization flow. The redirect URI is required. The value must exactly
# match one of the authorized redirect URIs for the OAuth 2.0 client, which you
# configured in the API Console. If this value doesn't match an authorized URI,
# you will get a 'redirect_uri_mismatch' error.
callback_uri = '/oauth2callback'

# To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI
# from the client_secret.json file. To get these credentials for your application, visit
# https://console.cloud.google.com/apis/credentials.
authorizer = Google::Auth::WebUserAuthorizer.new(client_id, scope,
                                                token_store, callback_uri)

Tu aplicación usa el objeto cliente para realizar operaciones de OAuth 2.0, como generar URLs de solicitud de autorización y aplicar tokens de acceso a solicitudes HTTP.

Node.js

En el siguiente fragmento de código, se crea un objeto google.auth.OAuth2, que define los parámetros en la solicitud de autorización.

Ese objeto usa información de tu archivo client_secret.json para identificar tu aplicación. Para solicitarle permisos a un usuario y recuperar un token de acceso, redirecciona al usuario a una página de consentimiento. Para crear una URL de página de consentimiento, sigue estos pasos:

const {google} = require('googleapis');
const crypto = require('crypto');
const express = require('express');
const session = require('express-session');

/**
 * To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI
 * from the client_secret.json file. To get these credentials for your application, visit
 * https://console.cloud.google.com/apis/credentials.
 */
const oauth2Client = new google.auth.OAuth2(
  YOUR_CLIENT_ID,
  YOUR_CLIENT_SECRET,
  YOUR_REDIRECT_URL
);

// Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar.
const scopes = [
  'https://www.googleapis.com/auth/youtube.force-ssl',
  'https://www.googleapis.com/auth/calendar.readonly'
];

// Generate a secure random state value.
const state = crypto.randomBytes(32).toString('hex');

// Store state in the session
req.session.state = state;

// Generate a url that asks permissions for the Drive activity and Google Calendar scope
const authorizationUrl = oauth2Client.generateAuthUrl({
  // 'online' (default) or 'offline' (gets refresh_token)
  access_type: 'offline',
  /** Pass in the scopes array defined above.
    * Alternatively, if only one scope is needed, you can pass a scope URL as a string */
  scope: scopes,
  // Enable incremental authorization. Recommended as a best practice.
  include_granted_scopes: true,
  // Include the state parameter to reduce the risk of CSRF attacks.
  state: state
});

Nota importante: refresh_token solo se muestra en la primera autorización. Obtén más información aquí.

HTTP/REST

El extremo de OAuth 2.0 de Google se encuentra en https://accounts.google.com/o/oauth2/v2/auth. Solo se puede acceder a este extremo a través de HTTPS. Se rechazan las conexiones HTTP simples.

El servidor de autorización de Google admite los siguientes parámetros de cadena de consulta para aplicaciones de servidor web:

Parámetros
client_id Obligatorio

El ID de cliente de tu aplicación. Puedes encontrar este valor en el archivo API Console Credentials page.

redirect_uri Obligatorio

Determina a dónde redirecciona el servidor de la API al usuario después de que completa el flujo de autorización. El valor debe coincidir exactamente con uno de los URIs de redireccionamiento autorizados para el cliente de OAuth 2.0, que configuraste en el API Consolede tu cliente Credentials page. Si este valor no coincide con un URI de redireccionamiento autorizado para el client_id proporcionado, recibirás un error redirect_uri_mismatch.

Ten en cuenta que el esquema http o https, el caso y la barra final ('/') deben coincidir.

response_type Obligatorio

Determina si el extremo de Google OAuth 2.0 muestra un código de autorización.

Establece el valor del parámetro en code para las aplicaciones de servidor web.

scope Obligatorio

Es una lista de permisos delimitados por espacios que identifican los recursos a los que puede acceder tu aplicación en nombre del usuario. Estos valores informan la pantalla de consentimiento que Google muestra al usuario.

Los alcances permiten que la aplicación solo solicite acceso a los recursos que necesita, al tiempo que permiten a los usuarios controlar el nivel de acceso que otorgan a tu aplicación. Por lo tanto, existe una relación inversa entre la cantidad de permisos solicitados y la probabilidad de obtener el consentimiento del usuario.

La versión 3 de la API de YouTube Data usa los siguientes permisos:

Permisos
https://www.googleapis.com/auth/youtubeAdministrar tu cuenta de YouTube
https://www.googleapis.com/auth/youtube.channel-memberships.creatorVer una lista de los miembros actuales y activos de su canal, su nivel actual y el momento en que se hicieron miembros
https://www.googleapis.com/auth/youtube.force-sslVea, edite y borre de forma permanente sus videos, calificaciones, comentarios y subtítulos de YouTube
https://www.googleapis.com/auth/youtube.readonlyPermite ver tu cuenta de YouTube.
https://www.googleapis.com/auth/youtube.uploadPermite administrar tus videos de YouTube.
https://www.googleapis.com/auth/youtubepartnerVer y administrar sus elementos y el contenido asociado en YouTube
https://www.googleapis.com/auth/youtubepartner-channel-auditPermite ver información privada de tu canal de YouTube que sea relevante durante el proceso de auditoría con un socio de YouTube.

El documento Alcances de la API de OAuth 2.0 proporciona una lista completa de los permisos que puedes usar para acceder a las APIs de Google.

Te recomendamos que tu aplicación solicite acceso a los permisos de autorización en el contexto cada vez que sea posible. Cuando solicitas acceso a los datos del usuario en contexto, a través de la autorización incremental, ayudas a los usuarios a comprender con mayor facilidad por qué tu aplicación necesita el acceso que solicita.

access_type Recomendado

Indica si tu aplicación puede actualizar los tokens de acceso cuando el usuario no está presente en el navegador. Los valores de parámetros válidos son online, que es el valor predeterminada, y offline.

Establece el valor en offline si tu aplicación necesita actualizar los tokens de acceso cuando el usuario no está presente en el navegador. Este es el método para actualizar los tokens de acceso que se describe más adelante en este documento. Este valor le indica al servidor de autorización de Google que muestre un token de actualización y un token de acceso la primera vez que tu aplicación intercambie un código de autorización por tokens.

state Recomendado

Especifica cualquier valor de cadena que use tu aplicación para mantener el estado entre tu solicitud de autorización y la respuesta del servidor de autorización. El servidor muestra el valor exacto que envías como un par name=value en el componente de consulta de URL (?) de redirect_uri después de que el usuario acepta o rechaza la solicitud de acceso de tu aplicación.

Puedes usar este parámetro para varios fines, como dirigir al usuario al recurso correcto en tu aplicación, enviar nonces y mitigar la falsificación de solicitudes entre sitios. Dado que se puede adivinar tu redirect_uri, usar un valor state puede aumentar tu seguridad de que una conexión entrante es el resultado de una solicitud de autenticación. Si generas una cadena aleatoria o codificas el hash de una cookie o algún otro valor que capture el estado del cliente, puedes validar la respuesta para asegurarte además de que la solicitud y la respuesta se originen en el mismo navegador, lo que proporciona protección contra ataques como la falsificación de solicitudes entre sitios. Consulta la documentación de OpenID Connect para ver un ejemplo de cómo crear y confirmar un token state.

include_granted_scopes Opcional

Permite que las aplicaciones usen la autorización incremental para solicitar acceso a permisos adicionales en el contexto. Si estableces el valor de este parámetro en true y se otorga la solicitud de autorización, el nuevo token de acceso también abarcará cualquier permiso al que el usuario le haya otorgado acceso a la aplicación anteriormente. Consulta la sección sobre la autorización incremental para ver ejemplos.

login_hint Opcional

Si tu aplicación sabe qué usuario intenta autenticarse, puede usar este parámetro para proporcionar una sugerencia al servidor de autenticación de Google. El servidor usa la sugerencia para simplificar el flujo de acceso, ya sea completando previamente el campo de correo electrónico en el formulario de acceso o seleccionando la sesión de acceso múltiple adecuada.

Establece el valor del parámetro en una dirección de correo electrónico o un identificador sub, que es equivalente al ID de Google del usuario.

prompt Opcional

Es una lista de instrucciones delimitadas por espacios que distingue mayúsculas de minúsculas para presentarle al usuario. Si no especificas este parámetro, se le solicitará al usuario solo la primera vez que tu proyecto solicite acceso. Consulta Cómo solicitar el consentimiento nuevamente para obtener más información.

Los valores posibles son:

none No muestres ninguna pantalla de autenticación o consentimiento. No se debe especificar con otros valores.
consent Solicita el consentimiento del usuario.
select_account Pídele al usuario que seleccione una cuenta.

Paso 2: Redirecciona al servidor OAuth 2.0 de Google

Redirecciona al usuario al servidor de OAuth 2.0 de Google para iniciar el proceso de autenticación y autorización. Por lo general, esto ocurre cuando tu aplicación necesita acceder por primera vez a los datos del usuario. En el caso de la autorización incremental, este paso también ocurre cuando tu aplicación primero necesita acceder a recursos adicionales a los que aún no tiene permiso para acceder.

PHP

  1. Genera una URL para solicitar acceso desde el servidor de OAuth 2.0 de Google:
    $auth_url = $client->createAuthUrl();
  2. Redirecciona al usuario a $auth_url:
    header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));

Python

En este ejemplo, se muestra cómo redireccionar al usuario a la URL de autorización con el framework de aplicaciones web de Flask:

return flask.redirect(authorization_url)

Ruby

  1. Genera una URL para solicitar acceso desde el servidor de OAuth 2.0 de Google:
    auth_uri = authorizer.get_authorization_url(request: request)
  2. Redirecciona al usuario a auth_uri.

Node.js

  1. Usa la URL generada authorizationUrl del método generateAuthUrl del Paso 1 para solicitar acceso desde el servidor de OAuth 2.0 de Google.
  2. Redirecciona al usuario a authorizationUrl.
    res.redirect(authorizationUrl);

HTTP/REST

Ejemplo de redireccionamiento al servidor de autorización de Google

A continuación, se muestra una URL de ejemplo, con saltos de línea y espacios para facilitar la lectura. La URL solicita acceso a un permiso que permite recuperar los datos de YouTube del usuario. Usa la autorización incremental (include_granted_scopes=true) para garantizar que el nuevo token de acceso cubra cualquier ámbito al que el usuario le haya otorgado acceso a la aplicación anteriormente. En el ejemplo, también se configuran varios otros parámetros.

https://accounts.google.com/o/oauth2/v2/auth?
 scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.force-ssl&
 access_type=offline&
 include_granted_scopes=true&
 response_type=code&
 state=state_parameter_passthrough_value&
 redirect_uri=http%3A%2F%2Flocalhost%2Foauth2callback&
 client_id=client_id

Después de crear la URL de solicitud, redirecciona al usuario a ella.

El servidor OAuth 2.0 de Google autentica al usuario y obtiene su consentimiento para que tu aplicación acceda a los permisos solicitados. La respuesta se envía de vuelta a tu aplicación con la URL de redireccionamiento que especificaste.

Paso 3: Google le solicita consentimiento al usuario

En este paso, el usuario decide si otorgarle a tu aplicación el acceso solicitado. En esta etapa, Google muestra una ventana de consentimiento que muestra el nombre de tu aplicación y los servicios de la API de Google a los que solicita permiso para acceder con las credenciales de autorización del usuario, y un resumen de los permisos de acceso que se otorgarán. Luego, el usuario puede consentir otorgar acceso a uno o más permisos solicitados por tu aplicación o rechazar la solicitud.

Tu aplicación no necesita hacer nada en esta etapa, ya que espera la respuesta del servidor de OAuth 2.0 de Google que indique si se otorgó algún acceso. Esa respuesta se explica en el siguiente paso.

Errores

Es posible que las solicitudes al extremo de autorización de OAuth 2.0 de Google muestren mensajes de error para el usuario en lugar de los flujos de autenticación y autorización esperados. A continuación, se indican los códigos de error comunes y las resoluciones sugeridas.

admin_policy_enforced

La Cuenta de Google no puede autorizar uno o más permisos solicitados debido a las políticas de su administrador de Google Workspace. Consulta el artículo de ayuda del Administrador de Google Workspace Controla qué apps internas y de terceros acceden a los datos de Google Workspace para obtener más información sobre cómo un administrador puede restringir el acceso a todos los permisos o a los permisos sensibles y restringidos hasta que se otorgue acceso de forma explícita a tu ID de cliente de OAuth.

disallowed_useragent

El extremo de autorización se muestra dentro de un usuario-agente incorporado que no está permitido por las Políticas de OAuth 2.0 de Google.

Android

Es posible que los desarrolladores de Android encuentren este mensaje de error cuando abran solicitudes de autorización en android.webkit.WebView. En su lugar, los desarrolladores deben usar bibliotecas de Android, como Acceso con Google para Android o AppAuth para Android de la OpenID Foundation.

Es posible que los desarrolladores web encuentren este error cuando una app para Android abre un vínculo web general en un usuario-agente incorporado y un usuario navega al extremo de autorización de OAuth 2.0 de Google desde tu sitio. Los desarrolladores deben permitir que los vínculos generales se abran en el controlador de vínculos predeterminado del sistema operativo, que incluye los controladores de Android App Links o la app de navegador predeterminada. La biblioteca de Android Custom Tabs también es una opción compatible.

iOS

Es posible que los desarrolladores de iOS y macOS encuentren este error cuando abran solicitudes de autorización en WKWebView. En su lugar, los desarrolladores deben usar bibliotecas de iOS, como Acceso con Google para iOS o AppAuth para iOS de OpenID Foundation.

Es posible que los desarrolladores web encuentren este error cuando una app para iOS o macOS abra un vínculo web general en un usuario-agente incorporado y un usuario navegue al extremo de autorización de OAuth 2.0 de Google desde tu sitio. Los desarrolladores deben permitir que los vínculos generales se abran en el controlador de vínculos predeterminado del sistema operativo, que incluye los controladores de vínculos universales o la app de navegador predeterminada. La biblioteca SFSafariViewController también es una opción compatible.

org_internal

El ID de cliente de OAuth en la solicitud forma parte de un proyecto que limita el acceso a las Cuentas de Google en una organización de Google Cloud específica. Para obtener más información sobre esta opción de configuración, consulta la sección Tipo de usuario en el artículo de ayuda Cómo configurar tu pantalla de consentimiento de OAuth.

invalid_client

El secreto del cliente de OAuth es incorrecto. Revisa la configuración del cliente de OAuth, incluido el ID de cliente y el secreto que se usó para esta solicitud.

invalid_grant

Cuando se actualiza un token de acceso o se usa la autorización incremental, es posible que el token haya caducado o que se haya invalidado. Vuelve a autenticar al usuario y solicita su consentimiento para obtener tokens nuevos. Si sigues viendo este error, asegúrate de que tu aplicación esté configurada correctamente y de que uses los tokens y parámetros correctos en tu solicitud. De lo contrario, es posible que se haya borrado o inhabilitado la cuenta de usuario.

redirect_uri_mismatch

El redirect_uri que se pasó en la solicitud de autorización no coincide con un URI de redireccionamiento autorizado para el ID de cliente de OAuth. Revisa los URIs de redireccionamiento autorizados en Google API Console Credentials page.

El parámetro redirect_uri puede hacer referencia al flujo fuera de banda (OOB) de OAuth que dejó de estar disponible y ya no es compatible. Consulta la guía de migración para actualizar tu integración.

invalid_request

Se produjo un error con la solicitud que realizaste. Esto puede deberse a varios motivos:

  • La solicitud no tenía el formato correcto
  • Faltaban parámetros obligatorios en la solicitud
  • La solicitud usa un método de autorización que Google no admite. Verifica que tu integración de OAuth use un método de integración recomendado

Paso 4: Controla la respuesta del servidor de OAuth 2.0

El servidor de OAuth 2.0 responde a la solicitud de acceso de tu aplicación con la URL especificada en la solicitud.

Si el usuario aprueba la solicitud de acceso, la respuesta contendrá un código de autorización. Si el usuario no aprueba la solicitud, la respuesta contendrá un mensaje de error. El código de autorización o el mensaje de error que se muestra al servidor web aparece en la cadena de consulta, como se muestra a continuación:

Una respuesta de error:

https://oauth2.example.com/auth?error=access_denied

Una respuesta de código de autorización:

https://oauth2.example.com/auth?code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7

Ejemplo de respuesta del servidor de OAuth 2.0

Para probar este flujo, haz clic en la siguiente URL de ejemplo, que solicita acceso de solo lectura para ver los metadatos de los archivos de tu Google Drive y acceso de solo lectura para ver los eventos de tu Calendario de Google:

https://accounts.google.com/o/oauth2/v2/auth?
 scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.force-ssl&
 access_type=offline&
 include_granted_scopes=true&
 response_type=code&
 state=state_parameter_passthrough_value&
 redirect_uri=http%3A%2F%2Flocalhost%2Foauth2callback&
 client_id=client_id

Después de completar el flujo de OAuth 2.0, deberías redireccionarte a http://localhost/oauth2callback, lo que probablemente genere un error 404 NOT FOUND, a menos que tu máquina local entregue un archivo en esa dirección. En el siguiente paso, se proporcionan más detalles sobre la información que se muestra en el URI cuando se redirecciona al usuario a tu aplicación.

Paso 5: Intercambia el código de autorización por tokens de actualización y acceso

Una vez que el servidor web recibe el código de autorización, puede intercambiarlo por un token de acceso.

PHP

Para intercambiar un código de autorización por un token de acceso, usa el método fetchAccessTokenWithAuthCode:

$access_token = $client->fetchAccessTokenWithAuthCode($_GET['code']);

Python

En tu página de devolución de llamada, usa la biblioteca google-auth para verificar la respuesta del servidor de autorización. Luego, usa el método flow.fetch_token para intercambiar el código de autorización de esa respuesta por un token de acceso:

state = flask.session['state']
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
    'client_secret.json',
    scopes=['https://www.googleapis.com/auth/youtube.force-ssl'],
    state=state)
flow.redirect_uri = flask.url_for('oauth2callback', _external=True)

authorization_response = flask.request.url
flow.fetch_token(authorization_response=authorization_response)

# Store the credentials in the session.
# ACTION ITEM for developers:
#     Store user's access and refresh tokens in your data store if
#     incorporating this code into your real app.
credentials = flow.credentials
flask.session['credentials'] = {
    'token': credentials.token,
    'refresh_token': credentials.refresh_token,
    'token_uri': credentials.token_uri,
    'client_id': credentials.client_id,
    'client_secret': credentials.client_secret,
    'granted_scopes': credentials.granted_scopes}

Ruby

En tu página de devolución de llamada, usa la biblioteca googleauth para verificar la respuesta del servidor de autorización. Usa el método authorizer.handle_auth_callback_deferred para guardar el código de autorización y redireccionar a la URL que solicitó la autorización originalmente. Esto aplaza el intercambio del código, ya que oculta temporalmente los resultados en la sesión del usuario.

  target_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(request)
  redirect target_url

Node.js

Para intercambiar un código de autorización por un token de acceso, usa el método getToken:

const url = require('url');

// Receive the callback from Google's OAuth 2.0 server.
app.get('/oauth2callback', async (req, res) => {
  let q = url.parse(req.url, true).query;

  if (q.error) { // An error response e.g. error=access_denied
    console.log('Error:' + q.error);
  } else if (q.state !== req.session.state) { //check state value
    console.log('State mismatch. Possible CSRF attack');
    res.end('State mismatch. Possible CSRF attack');
  } else { // Get access and refresh tokens (if access_type is offline)

    let { tokens } = await oauth2Client.getToken(q.code);
    oauth2Client.setCredentials(tokens);
});

HTTP/REST

Para intercambiar un código de autorización por un token de acceso, llama al extremo https://oauth2.googleapis.com/token y establece los siguientes parámetros:

Campos
client_id El ID de cliente obtenido de API Console Credentials page.
client_secret El secreto de cliente obtenido de API Console Credentials page.
code El código de autorización que se muestra en la solicitud inicial.
grant_type Como se define en la especificación de OAuth 2.0, el valor de este campo debe establecerse en authorization_code.
redirect_uri Uno de los URIs de redireccionamiento que se enumeran para tu proyecto en el API Console Credentials page para el client_id determinado.

En el siguiente fragmento, se muestra una solicitud de muestra:

POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded

code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=your_client_id&
client_secret=your_client_secret&
redirect_uri=https%3A//oauth2.example.com/code&
grant_type=authorization_code

Google responde a esta solicitud mostrando un objeto JSON que contiene un token de acceso de corta duración y un token de actualización. Ten en cuenta que el token de actualización solo se muestra si tu aplicación configuró el parámetro access_type como offline en la solicitud inicial al servidor de autorización de Google.

La respuesta contiene los siguientes campos:

Campos
access_token Es el token que envía tu aplicación para autorizar una solicitud a la API de Google.
expires_in Es la vida útil restante del token de acceso en segundos.
refresh_token Es un token que puedes usar para obtener un nuevo token de acceso. Los tokens de actualización son válidos hasta que el usuario revoca el acceso. Una vez más, este campo solo está presente en esta respuesta si configuras el parámetro access_type como offline en la solicitud inicial al servidor de autorización de Google.
scope Los permisos de acceso que otorga access_token expresados como una lista de cadenas delimitadas por espacios y que distinguen mayúsculas de minúsculas.
token_type Es el tipo de token que se muestra. En este momento, el valor de este campo siempre se establece en Bearer.

En el siguiente fragmento, se muestra una respuesta de ejemplo:

{
  "access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
  "expires_in": 3920,
  "token_type": "Bearer",
  "scope": "https://www.googleapis.com/auth/youtube.force-ssl https://www.googleapis.com/auth/calendar.readonly",
  "refresh_token": "1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
}

Errores

Cuando intercambies el código de autorización por un token de acceso, es posible que encuentres el siguiente error en lugar de la respuesta esperada. A continuación, se indican los códigos de error comunes y las resoluciones sugeridas.

invalid_grant

El código de autorización proporcionado no es válido o tiene un formato incorrecto. Para solicitar un código nuevo, reinicia el proceso de OAuth y vuelve a solicitarle el consentimiento al usuario.

Paso 6: Verifica qué permisos otorgaron los usuarios

Cuando solicitas varios permisos a la vez, es posible que los usuarios no otorguen todos los permisos que solicita tu app. Tu app siempre debe verificar qué permisos otorgó el usuario y controlar cualquier denegación de permisos inhabilitando las funciones relevantes. Consulta Cómo controlar los permisos detallados para obtener más información.

PHP

Para verificar qué permisos otorgó el usuario, usa el método getGrantedScope():

// Space-separated string of granted scopes if it exists, otherwise null.
$granted_scopes = $client->getOAuth2Service()->getGrantedScope();

// Determine which scopes user granted and build a dictionary
$granted_scopes_dict = [
  'Drive' => str_contains($granted_scopes, Google\Service\Drive::DRIVE_METADATA_READONLY),
  'Calendar' => str_contains($granted_scopes, Google\Service\Calendar::CALENDAR_READONLY)
];

Python

El objeto credentials que se muestra tiene una propiedad granted_scopes, que es una lista de los permisos que el usuario otorgó a tu app.

credentials = flow.credentials
flask.session['credentials'] = {
    'token': credentials.token,
    'refresh_token': credentials.refresh_token,
    'token_uri': credentials.token_uri,
    'client_id': credentials.client_id,
    'client_secret': credentials.client_secret,
    'granted_scopes': credentials.granted_scopes}

La siguiente función verifica qué permisos otorgó el usuario a tu app.

def check_granted_scopes(credentials):
  features = {}
  if 'https://www.googleapis.com/auth/drive.metadata.readonly' in credentials['granted_scopes']:
    features['drive'] = True
  else:
    features['drive'] = False

  if 'https://www.googleapis.com/auth/calendar.readonly' in credentials['granted_scopes']:
    features['calendar'] = True
  else:
    features['calendar'] = False

  return features

Ruby

Cuando solicites varios permisos a la vez, verifica qué permisos se otorgaron a través de la propiedad scope del objeto credentials.

# User authorized the request. Now, check which scopes were granted.
if credentials.scope.include?(Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY)
  # User authorized read-only Drive activity permission.
  # Calling the APIs, etc
else
  # User didn't authorize read-only Drive activity permission.
  # Update UX and application accordingly
end

# Check if user authorized Calendar read permission.
if credentials.scope.include?(Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY)
  # User authorized Calendar read permission.
  # Calling the APIs, etc.
else
  # User didn't authorize Calendar read permission.
  # Update UX and application accordingly
end

Node.js

Cuando solicites varios permisos a la vez, verifica qué permisos se otorgaron a través de la propiedad scope del objeto tokens.

// User authorized the request. Now, check which scopes were granted.
if (tokens.scope.includes('https://www.googleapis.com/auth/youtube.force-ssl'))
{
  // User authorized read-only Drive activity permission.
  // Calling the APIs, etc.
}
else
{
  // User didn't authorize read-only Drive activity permission.
  // Update UX and application accordingly
}

// Check if user authorized Calendar read permission.
if (tokens.scope.includes('https://www.googleapis.com/auth/calendar.readonly'))
{
  // User authorized Calendar read permission.
  // Calling the APIs, etc.
}
else
{
  // User didn't authorize Calendar read permission.
  // Update UX and application accordingly
}

HTTP/REST

Para verificar si el usuario otorgó a tu aplicación acceso a un alcance en particular, examina el campo scope en la respuesta del token de acceso. Los permisos de acceso que otorga el access_token expresados como una lista de cadenas separadas por espacios y sensibles a mayúsculas y minúsculas.

Por ejemplo, la siguiente respuesta de muestra del token de acceso indica que el usuario le otorgó a tu aplicación acceso a los permisos de eventos de Calendario y actividad de solo lectura de Drive:

  {
    "access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
    "expires_in": 3920,
    "token_type": "Bearer",
    "scope": "https://www.googleapis.com/auth/youtube.force-ssl https://www.googleapis.com/auth/calendar.readonly",
    "refresh_token": "1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
  }

Llama a las API de Google

PHP

Para usar el token de acceso y llamar a las APIs de Google, completa los siguientes pasos:

  1. Si necesitas aplicar un token de acceso a un objeto Google\Client nuevo (por ejemplo, si almacenaste el token de acceso en una sesión del usuario), usa el método setAccessToken:
    $client->setAccessToken($access_token);
  2. Compila un objeto de servicio para la API a la que deseas llamar. Para compilar un objeto de servicio, proporciona un objeto Google\Client autorizado al constructor de la API a la que deseas llamar. Por ejemplo, para llamar a la API de YouTube Data, haz lo siguiente:
    $youtube = new Google_Service_YouTube($client);
  3. Realiza solicitudes al servicio de la API con la interfaz que proporciona el objeto de servicio. Por ejemplo, para usar la API de YouTube Data y recuperar una lista de transmisiones en vivo del canal del usuario autenticado, haz lo siguiente:
    $broadcasts = $youtube->liveBroadcasts->listLiveBroadcasts('id,snippet', [ 'mine' => true ]);

Python

Después de obtener un token de acceso, tu aplicación puede usarlo para autorizar solicitudes a la API en nombre de una cuenta de usuario o de servicio determinada. Usa las credenciales de autorización específicas del usuario para compilar un objeto de servicio para la API a la que deseas llamar y, luego, usa ese objeto para realizar solicitudes a la API autorizadas.

  1. Compila un objeto de servicio para la API a la que deseas llamar. Para compilar un objeto de servicio, llama al método build de la biblioteca googleapiclient.discovery con el nombre y la versión de la API, y las credenciales del usuario: Por ejemplo, para llamar a la versión 3 de la API de YouTube Data, haz lo siguiente:
    from googleapiclient.discovery import build
    
    youtube = build('youtube', 'v3', credentials=credentials)
  2. Realiza solicitudes al servicio de la API con la interfaz que proporciona el objeto de servicio. Por ejemplo, para usar la API de YouTube Data y recuperar una lista de transmisiones en vivo del canal del usuario autenticado, haz lo siguiente:
    broadcasts = youtube.liveBroadcasts().list(part='id,snippet', mine=True).execute()

Ruby

Después de obtener un token de acceso, tu aplicación puede usarlo para realizar solicitudes a la API en nombre de una cuenta de usuario o de servicio determinada. Usa las credenciales de autorización específicas del usuario para compilar un objeto de servicio para la API a la que deseas llamar y, luego, usa ese objeto para realizar solicitudes a la API autorizadas.

  1. Compila un objeto de servicio para la API a la que deseas llamar. Por ejemplo, para llamar a la versión 3 de la API de YouTube Data, haz lo siguiente:
    youtube = Google::Apis::YoutubeV3::YouTubeService.new
  2. Establece las credenciales en el servicio:
    youtube.authorization = credentials
  3. Realiza solicitudes al servicio de la API con la interfaz que proporciona el objeto de servicio. Por ejemplo, para usar la API de YouTube Data y recuperar una lista de transmisiones en vivo del canal del usuario autenticado, haz lo siguiente:
    broadcasts = youtube.list_liveBroadcasts('id,snippet', mine: true)

Como alternativa, se puede proporcionar la autorización por método si se proporciona el parámetro options a un método:

broadcasts = youtube.list_liveBroadcasts('id,snippet', mine: true)

Node.js

Después de obtener un token de acceso y configurarlo en el objeto OAuth2, úsalo para llamar a las APIs de Google. Tu aplicación puede usar ese token para autorizar solicitudes a la API en nombre de una cuenta de usuario o de servicio determinada. Compila un objeto de servicio para la API a la que deseas llamar. Por ejemplo, el siguiente código usa la API de Google Drive para mostrar una lista de nombres de archivos en el Drive del usuario.

const { google } = require('googleapis');

// Example of using Google Drive API to list filenames in user's Drive.
const drive = google.drive('v3');
drive.files.list({
  auth: oauth2Client,
  pageSize: 10,
  fields: 'nextPageToken, files(id, name)',
}, (err1, res1) => {
  if (err1) return console.log('The API returned an error: ' + err1);
  const files = res1.data.files;
  if (files.length) {
    console.log('Files:');
    files.map((file) => {
      console.log(`${file.name} (${file.id})`);
    });
  } else {
    console.log('No files found.');
  }
});

HTTP/REST

Una vez que tu aplicación obtenga un token de acceso, podrás usarlo para realizar llamadas a una API de Google en nombre de una cuenta de usuario determinada si se otorgaron los permisos de acceso que requiere la API. Para ello, incluye el token de acceso en una solicitud a la API con un parámetro de consulta access_token o un valor Bearer de encabezado HTTP Authorization. Cuando sea posible, se prefiere el encabezado HTTP, ya que las cadenas de consulta suelen ser visibles en los registros del servidor. En la mayoría de los casos, puedes usar una biblioteca cliente para configurar tus llamadas a las APIs de Google (por ejemplo, cuando llamas a la API de YouTube Live Streaming).

Ten en cuenta que la API de YouTube Live Streaming no admite el flujo de cuentas de servicio. Dado que no hay forma de vincular una cuenta de servicio a una cuenta de YouTube, los intentos de autorizar solicitudes con este flujo generarán un error NoLinkedYouTubeAccount.

Puedes probar todas las APIs de Google y ver sus permisos en OAuth 2.0 Playground.

Ejemplos de HTTP GET

Una llamada al extremo liveBroadcasts.list (la API de YouTube Live Streaming) con el encabezado HTTP Authorization: Bearer podría verse de la siguiente manera. Ten en cuenta que debes especificar tu propio token de acceso:

GET /youtube/v3/liveBroadcasts?part=id%2Csnippet&mine=true HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer access_token

Esta es una llamada a la misma API para el usuario autenticado con el parámetro de cadena de consulta access_token:

GET https://www.googleapis.com/youtube/v3/liveBroadcasts?access_token=access_token&part=id%2Csnippet&mine=true

Ejemplos de curl

Puedes probar estos comandos con la aplicación de línea de comandos curl. A continuación, se muestra un ejemplo que usa la opción de encabezado HTTP (preferida):

curl -H "Authorization: Bearer access_token" https://www.googleapis.com/youtube/v3/liveBroadcasts?part=id%2Csnippet&mine=true

O bien, la opción de parámetro de cadena de consulta:

curl https://www.googleapis.com/youtube/v3/liveBroadcasts?access_token=access_token&part=id%2Csnippet&mine=true

Ejemplo completo

En el siguiente ejemplo, se imprime un objeto con formato JSON que muestra las transmisiones en vivo del canal de YouTube del usuario autenticado después de que el usuario autoriza a la aplicación para que recupere esos datos.

PHP

Para ejecutar este ejemplo, haz lo siguiente:

  1. En API Console, agrega la URL de la máquina local a la lista de URLs de redireccionamiento. Por ejemplo, agrega http://localhost:8080.
  2. Crea un directorio nuevo y cámbialo. Por ejemplo:
    mkdir ~/php-oauth2-example
    cd ~/php-oauth2-example
  3. Instala la biblioteca cliente de la API de Google para PHP con Composer:
    composer require google/apiclient:^2.15.0
  4. Crea los archivos index.php y oauth2callback.php con el siguiente contenido.
  5. Ejecuta el ejemplo con el servidor web de prueba integrado de PHP:
    php -S localhost:8080 ~/php-oauth2-example

index.php

<?php
require_once __DIR__.'/vendor/autoload.php';

session_start();

$client = new Google\Client();
$client->setAuthConfig('client_secret.json');

// User granted permission as an access token is in the session.
if (isset($_SESSION['access_token']) && $_SESSION['access_token'])
{
  $client->setAccessToken($_SESSION['access_token']);
  
  // Check if user granted Drive permission
  if ($_SESSION['granted_scopes_dict']['Drive']) {
    echo "Drive feature is enabled.";
    echo "</br>";
    $drive = new Drive($client);
    $files = array();
    $response = $drive->files->listFiles(array());
    foreach ($response->files as $file) {
        echo "File: " . $file->name . " (" . $file->id . ")";
        echo "</br>";
    }
  } else {
    echo "Drive feature is NOT enabled.";
    echo "</br>";
  }

   // Check if user granted Calendar permission
  if ($_SESSION['granted_scopes_dict']['Calendar']) {
    echo "Calendar feature is enabled.";
    echo "</br>";
  } else {
    echo "Calendar feature is NOT enabled.";
    echo "</br>";
  }
}
else
{
  // Redirect users to outh2call.php which redirects users to Google OAuth 2.0
  $redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php';
  header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
?>

oauth2callback.php

<?php
require_once __DIR__.'/vendor/autoload.php';

session_start();

$client = new Google\Client();

// Required, call the setAuthConfig function to load authorization credentials from
// client_secret.json file.
$client->setAuthConfigFile('client_secret.json');
$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST']. $_SERVER['PHP_SELF']);

// Required, to set the scope value, call the addScope function.
$client->addScope(Google_Service_YouTube::YOUTUBE_FORCE_SSL);

// Enable incremental authorization. Recommended as a best practice.
$client->setIncludeGrantedScopes(true);

// Recommended, offline access will give you both an access and refresh token so that
// your app can refresh the access token without user interaction.
$client->setAccessType("offline");

// Generate a URL for authorization as it doesn't contain code and error
if (!isset($_GET['code']) && !isset($_GET['error']))
{
  // Generate and set state value
  $state = bin2hex(random_bytes(16));
  $client->setState($state);
  $_SESSION['state'] = $state;

  // Generate a url that asks permissions.
  $auth_url = $client->createAuthUrl();
  header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
}

// User authorized the request and authorization code is returned to exchange access and
// refresh tokens.
if (isset($_GET['code']))
{
  // Check the state value
  if (!isset($_GET['state']) || $_GET['state'] !== $_SESSION['state']) {
    die('State mismatch. Possible CSRF attack.');
  }

  // Get access and refresh tokens (if access_type is offline)
  $token = $client->fetchAccessTokenWithAuthCode($_GET['code']);

  /** Save access and refresh token to the session variables.
    * ACTION ITEM: In a production app, you likely want to save the
    *              refresh token in a secure persistent storage instead. */
  $_SESSION['access_token'] = $token;
  $_SESSION['refresh_token'] = $client->getRefreshToken();
  
  // Space-separated string of granted scopes if it exists, otherwise null.
  $granted_scopes = $client->getOAuth2Service()->getGrantedScope();

  // Determine which scopes user granted and build a dictionary
  $granted_scopes_dict = [
    'Drive' => str_contains($granted_scopes, Google\Service\Drive::DRIVE_METADATA_READONLY),
    'Calendar' => str_contains($granted_scopes, Google\Service\Calendar::CALENDAR_READONLY)
  ];
  $_SESSION['granted_scopes_dict'] = $granted_scopes_dict;
  
  $redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/';
  header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}

// An error response e.g. error=access_denied
if (isset($_GET['error']))
{
  echo "Error: ". $_GET['error'];
}
?>

Python

En este ejemplo, se usa el framework Flask. Ejecuta una aplicación web en http://localhost:8080 que te permite probar el flujo de OAuth 2.0. Si vas a esa URL, deberías ver cinco vínculos:

  • Llama a la API de Drive: Este vínculo dirige a una página que intenta ejecutar una solicitud de API de muestra si los usuarios otorgaron el permiso. Si es necesario, inicia el flujo de autorización. Si se realiza correctamente, la página muestra la respuesta de la API.
  • Página simulada para llamar a la API de Calendario: Este vínculo dirige a una página simulada que intenta ejecutar una solicitud de ejemplo a la API de Calendario si los usuarios otorgaron el permiso. Si es necesario, inicia el flujo de autorización. Si se realiza correctamente, la página muestra la respuesta de la API.
  • Probar el flujo de autenticación directamente: Este vínculo dirige a una página que intenta enviar al usuario por el flujo de autorización. La app solicita permiso para enviar solicitudes de API autorizadas en nombre del usuario.
  • Revocar credenciales actuales: Este vínculo dirige a una página que revoca los permisos que el usuario ya otorgó a la aplicación.
  • Borra las credenciales de la sesión de Flask: Este vínculo borra las credenciales de autorización que se almacenan en la sesión de Flask. Esto te permite ver qué sucedería si un usuario que ya otorgó permiso a tu app intentara ejecutar una solicitud a la API en una sesión nueva. También te permite ver la respuesta de la API que obtendría tu app si un usuario revocara los permisos otorgados a tu app y esta aún intentara autorizar una solicitud con un token de acceso revocado.
# -*- coding: utf-8 -*-

import os
import flask
import requests

import google.oauth2.credentials
import google_auth_oauthlib.flow
import googleapiclient.discovery

# This variable specifies the name of a file that contains the OAuth 2.0
# information for this application, including its client_id and client_secret.
CLIENT_SECRETS_FILE = "client_secret.json"

# The OAuth 2.0 access scope allows for access to the
# authenticated user's account and requires requests to use an SSL connection.
SCOPES = ['https://www.googleapis.com/auth/youtube.force-ssl',
          'https://www.googleapis.com/auth/calendar.readonly']
API_SERVICE_NAME = 'youtube'
API_VERSION = 'v3'

app = flask.Flask(__name__)
# Note: A secret key is included in the sample so that it works.
# If you use this code in your application, replace this with a truly secret
# key. See https://flask.palletsprojects.com/quickstart/#sessions.
app.secret_key = 'REPLACE ME - this value is here as a placeholder.'

@app.route('/')
def index():
  return print_index_table()

@app.route('/drive')
def drive_api_request():
  if 'credentials' not in flask.session:
    return flask.redirect('authorize')

  features = flask.session['features']

  if features['drive']:
    # Load credentials from the session.
    credentials = google.oauth2.credentials.Credentials(
        **flask.session['credentials'])

    youtube = googleapiclient.discovery.build(
        API_SERVICE_NAME, API_VERSION, credentials=credentials)

    broadcasts = youtube.liveBroadcasts().list(part='id,snippet', mine=True).execute()

    # Save credentials back to session in case access token was refreshed.
    # ACTION ITEM: In a production app, you likely want to save these
    #              credentials in a persistent database instead.
    flask.session['credentials'] = credentials_to_dict(credentials)

    return flask.jsonify(**broadcasts)
  else:
    # User didn't authorize read-only Drive activity permission.
    # Update UX and application accordingly
    return '<p>Drive feature is not enabled.</p>'

@app.route('/calendar')
    def calendar_api_request():
      if 'credentials' not in flask.session:
        return flask.redirect('authorize')

      features = flask.session['features']

      if features['calendar']:
        # User authorized Calendar read permission.
        # Calling the APIs, etc.
        return ('<p>User granted the Google Calendar read permission. '+
                'This sample code does not include code to call Calendar</p>')
      else:
        # User didn't authorize Calendar read permission.
        # Update UX and application accordingly
        return '<p>Calendar feature is not enabled.</p>'

@app.route('/authorize')
def authorize():
  # Create flow instance to manage the OAuth 2.0 Authorization Grant Flow steps.
  flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
      CLIENT_SECRETS_FILE, scopes=SCOPES)

  # The URI created here must exactly match one of the authorized redirect URIs
  # for the OAuth 2.0 client, which you configured in the API Console. If this
  # value doesn't match an authorized URI, you will get a 'redirect_uri_mismatch'
  # error.
  flow.redirect_uri = flask.url_for('oauth2callback', _external=True)

  authorization_url, state = flow.authorization_url(
      # Enable offline access so that you can refresh an access token without
      # re-prompting the user for permission. Recommended for web server apps.
      access_type='offline',
      # Enable incremental authorization. Recommended as a best practice.
      include_granted_scopes='true')

  # Store the state so the callback can verify the auth server response.
  flask.session['state'] = state

  return flask.redirect(authorization_url)

@app.route('/oauth2callback')
def oauth2callback():
  # Specify the state when creating the flow in the callback so that it can
  # verified in the authorization server response.
  state = flask.session['state']

  flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
      CLIENT_SECRETS_FILE, scopes=SCOPES, state=state)
  flow.redirect_uri = flask.url_for('oauth2callback', _external=True)

  # Use the authorization server's response to fetch the OAuth 2.0 tokens.
  authorization_response = flask.request.url
  flow.fetch_token(authorization_response=authorization_response)

  # Store credentials in the session.
  # ACTION ITEM: In a production app, you likely want to save these
  #              credentials in a persistent database instead.
  credentials = flow.credentials
  
  credentials = credentials_to_dict(credentials)
  flask.session['credentials'] = credentials

  # Check which scopes user granted
  features = check_granted_scopes(credentials)
  flask.session['features'] = features
  return flask.redirect('/')
  

@app.route('/revoke')
def revoke():
  if 'credentials' not in flask.session:
    return ('You need to <a href="/authorize">authorize</a> before ' +
            'testing the code to revoke credentials.')

  credentials = google.oauth2.credentials.Credentials(
    **flask.session['credentials'])

  revoke = requests.post('https://oauth2.googleapis.com/revoke',
      params={'token': credentials.token},
      headers = {'content-type': 'application/x-www-form-urlencoded'})

  status_code = getattr(revoke, 'status_code')
  if status_code == 200:
    return('Credentials successfully revoked.' + print_index_table())
  else:
    return('An error occurred.' + print_index_table())

@app.route('/clear')
def clear_credentials():
  if 'credentials' in flask.session:
    del flask.session['credentials']
  return ('Credentials have been cleared.<br><br>' +
          print_index_table())

def credentials_to_dict(credentials):
  return {'token': credentials.token,
          'refresh_token': credentials.refresh_token,
          'token_uri': credentials.token_uri,
          'client_id': credentials.client_id,
          'client_secret': credentials.client_secret,
          'granted_scopes': credentials.granted_scopes}

def check_granted_scopes(credentials):
  features = {}
  if 'https://www.googleapis.com/auth/youtube.force-ssl' in credentials['granted_scopes']:
    features['drive'] = True
  else:
    features['drive'] = False

  if 'https://www.googleapis.com/auth/calendar.readonly' in credentials['granted_scopes']:
    features['calendar'] = True
  else:
    features['calendar'] = False

  return features

def print_index_table():
  return ('<table>' +
          '<tr><td><a href="/test">Test an API request</a></td>' +
          '<td>Submit an API request and see a formatted JSON response. ' +
          '    Go through the authorization flow if there are no stored ' +
          '    credentials for the user.</td></tr>' +
          '<tr><td><a href="/authorize">Test the auth flow directly</a></td>' +
          '<td>Go directly to the authorization flow. If there are stored ' +
          '    credentials, you still might not be prompted to reauthorize ' +
          '    the application.</td></tr>' +
          '<tr><td><a href="/revoke">Revoke current credentials</a></td>' +
          '<td>Revoke the access token associated with the current user ' +
          '    session. After revoking credentials, if you go to the test ' +
          '    page, you should see an <code>invalid_grant</code> error.' +
          '</td></tr>' +
          '<tr><td><a href="/clear">Clear Flask session credentials</a></td>' +
          '<td>Clear the access token currently stored in the user session. ' +
          '    After clearing the token, if you <a href="/test">test the ' +
          '    API request</a> again, you should go back to the auth flow.' +
          '</td></tr></table>')

if __name__ == '__main__':
  # When running locally, disable OAuthlib's HTTPs verification.
  # ACTION ITEM for developers:
  #     When running in production *do not* leave this option enabled.
  os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'

  # This disables the requested scopes and granted scopes check.
  # If users only grant partial request, the warning would not be thrown.
  os.environ['OAUTHLIB_RELAX_TOKEN_SCOPE'] = '1'

  # Specify a hostname and port that are set as a valid redirect URI
  # for your API project in the Google API Console.
  app.run('localhost', 8080, debug=True)

Ruby

En este ejemplo, se usa el framework Sinatra.

require 'googleauth'
require 'googleauth/web_user_authorizer'
require 'googleauth/stores/redis_token_store'

require 'google/apis/youtube_v3'
require 'google/apis/calendar_v3'

require 'sinatra'

configure do
  enable :sessions

  # Required, call the from_file method to retrieve the client ID from a
  # client_secret.json file.
  set :client_id, Google::Auth::ClientId.from_file('/path/to/client_secret.json')

  # Required, scope value
  # Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar.
  scope = ['Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY',
           'Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY']

  # Required, Authorizers require a storage instance to manage long term persistence of
  # access and refresh tokens.
  set :token_store, Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new)

  # Required, indicate where the API server will redirect the user after the user completes
  # the authorization flow. The redirect URI is required. The value must exactly
  # match one of the authorized redirect URIs for the OAuth 2.0 client, which you
  # configured in the API Console. If this value doesn't match an authorized URI,
  # you will get a 'redirect_uri_mismatch' error.
  set :callback_uri, '/oauth2callback'

  # To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI
  # from the client_secret.json file. To get these credentials for your application, visit
  # https://console.cloud.google.com/apis/credentials.
  set :authorizer, Google::Auth::WebUserAuthorizer.new(settings.client_id, settings.scope,
                          settings.token_store, callback_uri: settings.callback_uri)
end

get '/' do
  # NOTE: Assumes the user is already authenticated to the app
  user_id = request.session['user_id']

  # Fetch stored credentials for the user from the given request session.
  # nil if none present
  credentials = settings.authorizer.get_credentials(user_id, request)

  if credentials.nil?
    # Generate a url that asks the user to authorize requested scope(s).
    # Then, redirect user to the url.
    redirect settings.authorizer.get_authorization_url(request: request)
  end
  
  # User authorized the request. Now, check which scopes were granted.
  if credentials.scope.include?(Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY)
    # User authorized read-only Drive activity permission.
    # Example of using Google Drive API to list filenames in user's Drive.
    youtube = Google::Apis::YoutubeV3::YouTubeService.new
    broadcasts = youtube.list_liveBroadcasts('id,snippet', mine: true)
    
    "<pre>#{JSON.pretty_generate(broadcasts.to_h)}</pre>"
  else
    # User didn't authorize read-only Drive activity permission.
    # Update UX and application accordingly
  end

  # Check if user authorized Calendar read permission.
  if credentials.scope.include?(Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY)
    # User authorized Calendar read permission.
    # Calling the APIs, etc.
  else
    # User didn't authorize Calendar read permission.
    # Update UX and application accordingly
  end
end

# Receive the callback from Google's OAuth 2.0 server.
get '/oauth2callback' do
  # Handle the result of the oauth callback. Defers the exchange of the code by
  # temporarily stashing the results in the user's session.
  target_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(request)
  redirect target_url
end

Node.js

Para ejecutar este ejemplo, haz lo siguiente:

  1. En API Console, agrega la URL de la máquina local a la lista de URLs de redireccionamiento. Por ejemplo, agrega http://localhost.
  2. Asegúrate de tener instalada la versión LTS de mantenimiento, la LTS activa o la versión actual de Node.js.
  3. Crea un directorio nuevo y cámbialo. Por ejemplo:
    mkdir ~/nodejs-oauth2-example
    cd ~/nodejs-oauth2-example
  4. Instala la biblioteca cliente de la API de Google para Node.js con npm:
    npm install googleapis
  5. Crea los archivos main.js con el siguiente contenido.
  6. Ejecuta el ejemplo:
    node .\main.js

main.js

const http = require('http');
const https = require('https');
const url = require('url');
const { google } = require('googleapis');
const crypto = require('crypto');
const express = require('express');
const session = require('express-session');

/**
 * To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI.
 * To get these credentials for your application, visit
 * https://console.cloud.google.com/apis/credentials.
 */
const oauth2Client = new google.auth.OAuth2(
  YOUR_CLIENT_ID,
  YOUR_CLIENT_SECRET,
  YOUR_REDIRECT_URL
);

// Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar.
const scopes = [
  'https://www.googleapis.com/auth/youtube.force-ssl',
  'https://www.googleapis.com/auth/calendar.readonly'
];

/* Global variable that stores user credential in this code example.
 * ACTION ITEM for developers:
 *   Store user's refresh token in your data store if
 *   incorporating this code into your real app.
 *   For more information on handling refresh tokens,
 *   see https://github.com/googleapis/google-api-nodejs-client#handling-refresh-tokens
 */
let userCredential = null;

async function main() {
  const app = express();

  app.use(session({
    secret: 'your_secure_secret_key', // Replace with a strong secret
    resave: false,
    saveUninitialized: false,
  }));

  // Example on redirecting user to Google's OAuth 2.0 server.
  app.get('/', async (req, res) => {
    // Generate a secure random state value.
    const state = crypto.randomBytes(32).toString('hex');
    // Store state in the session
    req.session.state = state;

    // Generate a url that asks permissions for the Drive activity and Google Calendar scope
    const authorizationUrl = oauth2Client.generateAuthUrl({
      // 'online' (default) or 'offline' (gets refresh_token)
      access_type: 'offline',
      /** Pass in the scopes array defined above.
        * Alternatively, if only one scope is needed, you can pass a scope URL as a string */
      scope: scopes,
      // Enable incremental authorization. Recommended as a best practice.
      include_granted_scopes: true,
      // Include the state parameter to reduce the risk of CSRF attacks.
      state: state
    });

    res.redirect(authorizationUrl);
  });

  // Receive the callback from Google's OAuth 2.0 server.
  app.get('/oauth2callback', async (req, res) => {
    // Handle the OAuth 2.0 server response
    let q = url.parse(req.url, true).query;

    if (q.error) { // An error response e.g. error=access_denied
      console.log('Error:' + q.error);
    } else if (q.state !== req.session.state) { //check state value
      console.log('State mismatch. Possible CSRF attack');
      res.end('State mismatch. Possible CSRF attack');
    } else { // Get access and refresh tokens (if access_type is offline)
      let { tokens } = await oauth2Client.getToken(q.code);
      oauth2Client.setCredentials(tokens);

      /** Save credential to the global variable in case access token was refreshed.
        * ACTION ITEM: In a production app, you likely want to save the refresh token
        *              in a secure persistent database instead. */
      userCredential = tokens;
      
      // User authorized the request. Now, check which scopes were granted.
      if (tokens.scope.includes('https://www.googleapis.com/auth/youtube.force-ssl'))
      {
        // User authorized read-only Drive activity permission.
        // Example of using Google Drive API to list filenames in user's Drive.
        const drive = google.drive('v3');
        drive.files.list({
          auth: oauth2Client,
          pageSize: 10,
          fields: 'nextPageToken, files(id, name)',
        }, (err1, res1) => {
          if (err1) return console.log('The API returned an error: ' + err1);
          const files = res1.data.files;
          if (files.length) {
            console.log('Files:');
            files.map((file) => {
              console.log(`${file.name} (${file.id})`);
            });
          } else {
            console.log('No files found.');
          }
        });
      }
      else
      {
        // User didn't authorize read-only Drive activity permission.
        // Update UX and application accordingly
      }

      // Check if user authorized Calendar read permission.
      if (tokens.scope.includes('https://www.googleapis.com/auth/calendar.readonly'))
      {
        // User authorized Calendar read permission.
        // Calling the APIs, etc.
      }
      else
      {
        // User didn't authorize Calendar read permission.
        // Update UX and application accordingly
      }
    }
  });

  // Example on revoking a token
  app.get('/revoke', async (req, res) => {
    // Build the string for the POST request
    let postData = "token=" + userCredential.access_token;

    // Options for POST request to Google's OAuth 2.0 server to revoke a token
    let postOptions = {
      host: 'oauth2.googleapis.com',
      port: '443',
      path: '/revoke',
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Length': Buffer.byteLength(postData)
      }
    };

    // Set up the request
    const postReq = https.request(postOptions, function (res) {
      res.setEncoding('utf8');
      res.on('data', d => {
        console.log('Response: ' + d);
      });
    });

    postReq.on('error', error => {
      console.log(error)
    });

    // Post the request with data
    postReq.write(postData);
    postReq.end();
  });


  const server = http.createServer(app);
  server.listen(8080);
}
main().catch(console.error);

HTTP/REST

En este ejemplo de Python, se usa el framework Flask y la biblioteca Requests para demostrar el flujo web de OAuth 2.0. Para este flujo, te recomendamos que uses la biblioteca cliente de la API de Google para Python. (El ejemplo de la pestaña Python usa la biblioteca cliente).

import json
import flask
import requests

app = flask.Flask(__name__)

# To get these credentials (CLIENT_ID CLIENT_SECRET) and for your application, visit
# https://console.cloud.google.com/apis/credentials.
CLIENT_ID = '123456789.apps.googleusercontent.com'
CLIENT_SECRET = 'abc123'  # Read from a file or environmental variable in a real app

# Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar.
SCOPE = 'https://www.googleapis.com/auth/youtube.force-ssl https://www.googleapis.com/auth/calendar.readonly'

# Indicate where the API server will redirect the user after the user completes
# the authorization flow. The redirect URI is required. The value must exactly
# match one of the authorized redirect URIs for the OAuth 2.0 client, which you
# configured in the API Console. If this value doesn't match an authorized URI,
# you will get a 'redirect_uri_mismatch' error.
REDIRECT_URI = 'http://example.com/oauth2callback'

@app.route('/')
def index():
  if 'credentials' not in flask.session:
    return flask.redirect(flask.url_for('oauth2callback'))

  credentials = json.loads(flask.session['credentials'])

  if credentials['expires_in'] <= 0:
    return flask.redirect(flask.url_for('oauth2callback'))
  else: 
    # User authorized the request. Now, check which scopes were granted.
    if 'https://www.googleapis.com/auth/drive.metadata.readonly' in credentials['scope']:
      # User authorized read-only Drive activity permission.
      # Example of using Google Drive API to list filenames in user's Drive.
      headers = {'Authorization': 'Bearer {}'.format(credentials['access_token'])}
      req_uri = 'https://youtube.googleapis.com/youtube/v3/liveBroadcasts'
      r = requests.get(req_uri, headers=headers).text
    else:
      # User didn't authorize read-only Drive activity permission.
      # Update UX and application accordingly
      r = 'User did not authorize Drive permission.'

    # Check if user authorized Calendar read permission.
    if 'https://www.googleapis.com/auth/calendar.readonly' in credentials['scope']:
      # User authorized Calendar read permission.
      # Calling the APIs, etc.
      r += 'User authorized Calendar permission.'
    else:
      # User didn't authorize Calendar read permission.
      # Update UX and application accordingly
      r += 'User did not authorize Calendar permission.'

  return r

@app.route('/oauth2callback')
def oauth2callback():
  if 'code' not in flask.request.args:
    state = str(uuid.uuid4())
    flask.session['state'] = state
    # Generate a url that asks permissions for the Drive activity
    # and Google Calendar scope. Then, redirect user to the url.
    auth_uri = ('https://accounts.google.com/o/oauth2/v2/auth?response_type=code'
                '&client_id={}&redirect_uri={}&scope={}&state={}').format(CLIENT_ID, REDIRECT_URI,
                                                                          SCOPE, state)
    return flask.redirect(auth_uri)
  else:
    if 'state' not in flask.request.args or flask.request.args['state'] != flask.session['state']:
      return 'State mismatch. Possible CSRF attack.', 400

    auth_code = flask.request.args.get('code')
    data = {'code': auth_code,
            'client_id': CLIENT_ID,
            'client_secret': CLIENT_SECRET,
            'redirect_uri': REDIRECT_URI,
            'grant_type': 'authorization_code'}

    # Exchange authorization code for access and refresh tokens (if access_type is offline)
    r = requests.post('https://oauth2.googleapis.com/token', data=data)
    flask.session['credentials'] = r.text
    return flask.redirect(flask.url_for('index'))

if __name__ == '__main__':
  import uuid
  app.secret_key = str(uuid.uuid4())
  app.debug = False
  app.run()

Reglas de validación de URIs de redireccionamiento

Google aplica las siguientes reglas de validación a los URIs de redireccionamiento para ayudar a los desarrolladores a mantener la seguridad de sus aplicaciones. Tus URIs de redireccionamiento deben cumplir con estas reglas. Consulta la sección 3 de la RFC 3986 para obtener la definición de dominio, host, ruta de acceso, consulta, esquema y userinfo, que se mencionan a continuación.

Reglas de validación
Esquema

Los URIs de redireccionamiento deben usar el esquema HTTPS, no el HTTP simple. Los URIs de localhost (incluidos los URIs de direcciones IP de localhost) están exentos de esta regla.

Host

Los hosts no pueden ser direcciones IP sin procesar. Las direcciones IP de localhost están exentas de esta regla.

Dominio
  • Los TLD del host (dominios de nivel superior) deben pertenecer a la lista de sufijos públicos.
  • Los dominios de host no pueden ser “googleusercontent.com”.
  • Los URIs de redireccionamiento no pueden contener dominios de acortadores de URL (p.ej., goo.gl) a menos que la app sea propietaria del dominio. Además, si una app que posee un dominio de acortador elige redireccionar a ese dominio, ese URI de redireccionamiento debe contener “/google-callback/” en su ruta de acceso o terminar con “/google-callback”.
  • Userinfo

    Los URIs de redireccionamiento no pueden contener el subcomponente userinfo.

    Ruta de acceso

    Los URIs de redireccionamiento no pueden contener un salto de directorio (también llamado retroceso de directorio), que se representa con un “/..” o “\..”, o su codificación de URL.

    Consulta

    Los URIs de redireccionamiento no pueden contener redireccionamientos abiertos.

    Fragmento

    Los URIs de redireccionamiento no pueden contener el componente de fragmento.

    Caracteres Los URIs de redireccionamiento no pueden contener ciertos caracteres, incluidos los siguientes:
    • Caracteres comodín ('*')
    • Caracteres ASCII no imprimibles
    • Codificación de porcentaje no válida (cualquier codificación de porcentaje que no siga el formato de codificación de URL de un signo de porcentaje seguido de dos dígitos hexadecimales)
    • Caracteres nulos (un carácter NULL codificado, p.ej., %00, %C0%80)

    Autorización incremental

    En el protocolo OAuth 2.0, tu app solicita autorización para acceder a los recursos, que se identifican por permisos. Se considera una práctica recomendada de experiencia del usuario solicitar la autorización para los recursos en el momento en que los necesitas. Para habilitar esa práctica, el servidor de autorización de Google admite la autorización incremental. Esta función te permite solicitar ámbitos a medida que sean necesarios y, si el usuario otorga permiso para el ámbito nuevo, muestra un código de autorización que se puede intercambiar por un token que contiene todos los ámbitos que el usuario otorgó al proyecto.

    Por ejemplo, supongamos que una app recupera datos del canal de YouTube del usuario autenticado y también le permite recuperar datos de YouTube Analytics a través de un flujo especial. En este caso, en el momento del acceso, la app podría solicitar acceso solo al permiso https://www.googleapis.com/auth/youtube.force-ssl. Sin embargo, si el usuario intentara acceder a los datos de Analytics de su canal, la app también podría solicitar acceso al alcance https://www.googleapis.com/auth/yt-analytics.readonly.

    Para implementar la autorización incremental, debes completar el flujo normal para solicitar un token de acceso, pero asegúrate de que la solicitud de autorización incluya los permisos otorgados anteriormente. Este enfoque permite que tu app evite tener que administrar varios tokens de acceso.

    Las siguientes reglas se aplican a un token de acceso obtenido de una autorización incremental:

    • El token se puede usar para acceder a los recursos correspondientes a cualquiera de los permisos integrados en la nueva autorización combinada.
    • Cuando usas el token de actualización para la autorización combinada para obtener un token de acceso, este representa la autorización combinada y se puede usar para cualquiera de los valores de scope incluidos en la respuesta.
    • La autorización combinada incluye todos los permisos que el usuario otorgó al proyecto de la API, incluso si los permisos se solicitaron desde diferentes clientes. Por ejemplo, si un usuario otorga acceso a un permiso con el cliente para computadoras de una aplicación y, luego, otorga otro permiso a la misma aplicación a través de un cliente para dispositivos móviles, la autorización combinada incluirá ambos permisos.
    • Si revocas un token que representa una autorización combinada, se revocará de forma simultánea el acceso a todos los ámbitos de esa autorización en nombre del usuario asociado.

    Las muestras de código específicas de lenguaje en el Paso 1: Configura los parámetros de autorización y la URL de redireccionamiento HTTP/REST de muestra en el Paso 2: Redirecciona al servidor de OAuth 2.0 de Google usan autorización incremental. En las siguientes muestras de código, también se muestra el código que debes agregar para usar la autorización incremental.

    PHP

    $client->setIncludeGrantedScopes(true);

    Python

    En Python, establece el argumento de palabra clave include_granted_scopes en true para asegurarte de que una solicitud de autorización incluya los permisos otorgados anteriormente. Es muy probable que include_granted_scopes no sea el único argumento de palabra clave que establezcas, como se muestra en el siguiente ejemplo.

    authorization_url, state = flow.authorization_url(
        # Enable offline access so that you can refresh an access token without
        # re-prompting the user for permission. Recommended for web server apps.
        access_type='offline',
        # Enable incremental authorization. Recommended as a best practice.
        include_granted_scopes='true')

    Ruby

    auth_client.update!(
      :additional_parameters => {"include_granted_scopes" => "true"}
    )

    Node.js

    const authorizationUrl = oauth2Client.generateAuthUrl({
      // 'online' (default) or 'offline' (gets refresh_token)
      access_type: 'offline',
      /** Pass in the scopes array defined above.
        * Alternatively, if only one scope is needed, you can pass a scope URL as a string */
      scope: scopes,
      // Enable incremental authorization. Recommended as a best practice.
      include_granted_scopes: true
    });

    HTTP/REST

    En este ejemplo, la aplicación que realiza la llamada solicita acceso para recuperar los datos de YouTube del usuario, además de cualquier otro acceso que el usuario ya le haya otorgado a la aplicación.

    GET https://accounts.google.com/o/oauth2/v2/auth?
      scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.force-ssl&
      access_type=offline&
      state=security_token%3D138rk%3Btarget_url%3Dhttp...index&
      redirect_uri=http%3A%2F%2Flocalhost%2Foauth2callback&
      response_type=code&
      client_id=client_id&
      include_granted_scopes=true
    
          

    Refreshing an access token (offline access)

    Access tokens periodically expire and become invalid credentials for a related API request. You can refresh an access token without prompting the user for permission (including when the user is not present) if you requested offline access to the scopes associated with the token.

    • If you use a Google API Client Library, the client object refreshes the access token as needed as long as you configure that object for offline access.
    • If you are not using a client library, you need to set the access_type HTTP query parameter to offline when redirecting the user to Google's OAuth 2.0 server. In that case, Google's authorization server returns a refresh token when you exchange an authorization code for an access token. Then, if the access token expires (or at any other time), you can use a refresh token to obtain a new access token.

    Requesting offline access is a requirement for any application that needs to access a Google API when the user is not present. For example, an app that performs backup services or executes actions at predetermined times needs to be able to refresh its access token when the user is not present. The default style of access is called online.

    Server-side web applications, installed applications, and devices all obtain refresh tokens during the authorization process. Refresh tokens are not typically used in client-side (JavaScript) web applications.

    PHP

    If your application needs offline access to a Google API, set the API client's access type to offline:

    $client->setAccessType("offline");

    Después de que un usuario otorga acceso sin conexión a los permisos solicitados, puedes seguir usando el cliente de la API para acceder a las APIs de Google en nombre del usuario cuando este no tiene conexión. El objeto cliente actualizará el token de acceso según sea necesario.

    Python

    En Python, establece el argumento de palabra clave access_type en offline para asegurarte de que puedas actualizar el token de acceso sin tener que volver a solicitarle permiso al usuario. Es muy probable que access_type no sea el único argumento de palabra clave que establezcas, como se muestra en el siguiente ejemplo.

    authorization_url, state = flow.authorization_url(
        # Enable offline access so that you can refresh an access token without
        # re-prompting the user for permission. Recommended for web server apps.
        access_type='offline',
        # Enable incremental authorization. Recommended as a best practice.
        include_granted_scopes='true')

    Después de que un usuario otorga acceso sin conexión a los permisos solicitados, puedes seguir usando el cliente de la API para acceder a las APIs de Google en nombre del usuario cuando este no tiene conexión. El objeto cliente actualizará el token de acceso según sea necesario.

    Ruby

    Si tu aplicación necesita acceso sin conexión a una API de Google, establece el tipo de acceso del cliente de la API en offline:

    auth_client.update!(
      :additional_parameters => {"access_type" => "offline"}
    )

    Después de que un usuario otorga acceso sin conexión a los permisos solicitados, puedes seguir usando el cliente de la API para acceder a las APIs de Google en nombre del usuario cuando este no tiene conexión. El objeto cliente actualizará el token de acceso según sea necesario.

    Node.js

    Si tu aplicación necesita acceso sin conexión a una API de Google, establece el tipo de acceso del cliente de la API en offline:

    const authorizationUrl = oauth2Client.generateAuthUrl({
      // 'online' (default) or 'offline' (gets refresh_token)
      access_type: 'offline',
      /** Pass in the scopes array defined above.
        * Alternatively, if only one scope is needed, you can pass a scope URL as a string */
      scope: scopes,
      // Enable incremental authorization. Recommended as a best practice.
      include_granted_scopes: true
    });

    Después de que un usuario otorga acceso sin conexión a los permisos solicitados, puedes seguir usando el cliente de la API para acceder a las APIs de Google en nombre del usuario cuando este no tiene conexión. El objeto cliente actualizará el token de acceso según sea necesario.

    Los tokens de acceso vencen. Esta biblioteca usará automáticamente un token de actualización para obtener un nuevo token de acceso si está por vencer. Una forma sencilla de asegurarte de almacenar siempre los tokens más recientes es usar el evento de tokens:

    oauth2Client.on('tokens', (tokens) => {
      if (tokens.refresh_token) {
        // store the refresh_token in your secure persistent database
        console.log(tokens.refresh_token);
      }
      console.log(tokens.access_token);
    });

    Este evento de tokens solo ocurre en la primera autorización, y debes haber configurado tu access_type como offline cuando llames al método generateAuthUrl para recibir el token de actualización. Si ya le otorgaste a tu app los permisos necesarios sin configurar las restricciones adecuadas para recibir un token de actualización, deberás volver a autorizar la aplicación para que reciba un token de actualización nuevo.

    Para configurar el refresh_token más adelante, puedes usar el método setCredentials:

    oauth2Client.setCredentials({
      refresh_token: `STORED_REFRESH_TOKEN`
    });

    Una vez que el cliente tenga un token de actualización, los tokens de acceso se adquirirán y actualizarán automáticamente en la próxima llamada a la API.

    HTTP/REST

    Para actualizar un token de acceso, tu aplicación envía una solicitud POST HTTPS al servidor de autorización de Google (https://oauth2.googleapis.com/token) que incluye los siguientes parámetros:

    Campos
    client_id El ID de cliente obtenido de API Console.
    client_secret El secreto de cliente obtenido de API Console
    grant_type Como se define en la especificación de OAuth 2.0, el valor de este campo debe establecerse en refresh_token.
    refresh_token El token de actualización que se muestra después del intercambio de códigos de autorización.

    En el siguiente fragmento, se muestra una solicitud de muestra:

    POST /token HTTP/1.1
    Host: oauth2.googleapis.com
    Content-Type: application/x-www-form-urlencoded
    
    client_id=your_client_id&
    client_secret=your_client_secret&
    refresh_token=refresh_token&
    grant_type=refresh_token

    Siempre que el usuario no haya revocado el acceso otorgado a la aplicación, el servidor de tokens muestra un objeto JSON que contiene un token de acceso nuevo. En el siguiente fragmento, se muestra una respuesta de ejemplo:

    {
      "access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
      "expires_in": 3920,
      "scope": "https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly",
      "token_type": "Bearer"
    }

    Ten en cuenta que hay límites en la cantidad de tokens de actualización que se emitirán: un límite por combinación de cliente/usuario y otro por usuario en todos los clientes. Debes guardar los tokens de actualización en el almacenamiento a largo plazo y seguir usándolos mientras sigan siendo válidos. Si tu aplicación solicita demasiados tokens de actualización, es posible que se encuentre con estos límites, en cuyo caso los tokens de actualización más antiguos dejarán de funcionar.

    Cómo revocar un token

    En algunos casos, es posible que un usuario desee revocar el acceso otorgado a una aplicación. Un usuario puede revocar el acceso en Configuración de la cuenta. Consulta la sección Cómo quitar el acceso de un sitio o una app del documento de asistencia Sitios y apps de terceros con acceso a tu cuenta para obtener más información.

    También es posible que una aplicación revoque de forma programática el acceso que se le otorgó. La revocación programática es importante en los casos en que un usuario cancela su suscripción, quita una aplicación o los recursos de la API que requiere una app cambiaron de forma significativa. En otras palabras, parte del proceso de eliminación puede incluir una solicitud a la API para garantizar que se quiten los permisos otorgados anteriormente a la aplicación.

    PHP

    Para revocar un token de manera programática, llama a revokeToken():

    $client->revokeToken();

    Python

    Para revocar un token de forma programática, realiza una solicitud a https://oauth2.googleapis.com/revoke que incluya el token como parámetro y establezca el encabezado Content-Type:

    requests.post('https://oauth2.googleapis.com/revoke',
        params={'token': credentials.token},
        headers = {'content-type': 'application/x-www-form-urlencoded'})

    Ruby

    Para revocar un token de forma programática, realiza una solicitud HTTP al extremo oauth2.revoke:

    uri = URI('https://oauth2.googleapis.com/revoke')
    response = Net::HTTP.post_form(uri, 'token' => auth_client.access_token)

    El token puede ser un token de acceso o un token de actualización. Si el token es un token de acceso y tiene un token de actualización correspondiente, este también se revocará.

    Si la revocación se procesa correctamente, el código de estado de la respuesta es 200. En el caso de las condiciones de error, se muestra un código de estado 400 junto con un código de error.

    Node.js

    Para revocar un token de forma programática, realiza una solicitud HTTPS POST al extremo /revoke:

    const https = require('https');
    
    // Build the string for the POST request
    let postData = "token=" + userCredential.access_token;
    
    // Options for POST request to Google's OAuth 2.0 server to revoke a token
    let postOptions = {
      host: 'oauth2.googleapis.com',
      port: '443',
      path: '/revoke',
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Length': Buffer.byteLength(postData)
      }
    };
    
    // Set up the request
    const postReq = https.request(postOptions, function (res) {
      res.setEncoding('utf8');
      res.on('data', d => {
        console.log('Response: ' + d);
      });
    });
    
    postReq.on('error', error => {
      console.log(error)
    });
    
    // Post the request with data
    postReq.write(postData);
    postReq.end();

    El parámetro de token puede ser un token de acceso o un token de actualización. Si el token es un token de acceso y tiene un token de actualización correspondiente, este también se revocará.

    Si la revocación se procesa correctamente, el código de estado de la respuesta es 200. En el caso de las condiciones de error, se muestra un código de estado 400 junto con un código de error.

    HTTP/REST

    Para revocar un token de manera programática, tu aplicación envía una solicitud a https://oauth2.googleapis.com/revoke y, además, incluye el token como parámetro:

    curl -d -X -POST --header "Content-type:application/x-www-form-urlencoded" \
            https://oauth2.googleapis.com/revoke?token={token}

    El token puede ser un token de acceso o un token de actualización. Si el token es un token de acceso y tiene un token de actualización correspondiente, este también se revocará.

    Si la revocación se procesa correctamente, el código de estado HTTP de la respuesta es 200. Para las condiciones de error, se muestra un código de estado HTTP 400 junto con un código de error.

    Implementa la Protección integral de la cuenta

    Un paso adicional que debes seguir para proteger las cuentas de tus usuarios es implementar la Protección entre cuentas con el servicio de Protección entre cuentas de Google. Este servicio te permite suscribirte a notificaciones de eventos de seguridad que proporcionan información a tu aplicación sobre cambios importantes en la cuenta de usuario. Luego, puedes usar la información para tomar medidas según cómo decidas responder a los eventos.

    Estos son algunos ejemplos de los tipos de eventos que el servicio de Protección entre cuentas de Google envía a tu app:

    • https://schemas.openid.net/secevent/risc/event-type/sessions-revoked
    • https://schemas.openid.net/secevent/oauth/event-type/token-revoked
    • https://schemas.openid.net/secevent/risc/event-type/account-disabled

    Consulta la página Protege las cuentas de usuario con la Protección integral de la cuenta para obtener más información sobre cómo implementar la Protección integral de la cuenta y ver la lista completa de eventos disponibles.