Cómo controlar los accesos repetidos

Esta es la tercera explicación de los complementos de Classroom .

En esta explicación, administras las visitas repetidas a nuestro complemento de forma automática recuperar las credenciales otorgadas anteriormente a un usuario Luego, enrutas a los usuarios páginas desde las que pueden emitir solicitudes a la API de inmediato. Este campo es obligatorio de los complementos de Classroom.

En esta explicación, completaste lo siguiente:

  • Implementar almacenamiento persistente para las credenciales de usuario
  • Recupera y evalúa el parámetro de consulta del complemento login_hint. Este es un el número de ID único de Google del usuario que accedió.

Una vez finalizado, puedes autorizar completamente a los usuarios en tu aplicación web y emitir llamadas a APIs de Google.

Comprende los parámetros de consulta de iframe

Classroom carga el URI de configuración de archivos adjuntos de tu complemento cuando abriendo. En el aula agrega varios parámetros de consulta GET al URI. contienen información útil información contextual. Si, por ejemplo, el URI de detección de adjuntos es https://example.com/addon, Classroom crea el iframe con la URL de origen configurada como https://example.com/addon?courseId=XXX&itemId=YYY&itemType=courseWork&addOnToken=ZZZ, En el ejemplo anterior, XXX, YYY y ZZZ son IDs de cadena. Consulta la guía de iframes para obtener descripción detallada de esta situación.

Existen cinco parámetros de búsqueda posibles para la URL de descubrimiento:

  • courseId: Es el ID del curso actual de Classroom.
  • itemId: Es el ID del elemento de flujo que el usuario está editando o creando.
  • itemType: Es el tipo de elemento de flujo que el usuario crea o edita, es decir, uno de courseWork, courseWorkMaterial o announcement.
  • addOnToken: Es un token que se usa para autorizar ciertos Acciones de complementos de Classroom.
  • login_hint: Es el ID de Google del usuario actual.

En esta explicación, se aborda login_hint. Los usuarios se enrutan según si este parámetro de consulta, ya sea al flujo de autorización si falta o al la página de descubrimiento del complemento, si está presente.

Accede a los parámetros de consulta

Los parámetros de consulta se pasan a tu aplicación web en la cadena del URI. Tienda estos valores en tu sesión; se usan en el flujo de autorización almacenar y recuperar información sobre el usuario. Estos parámetros de consulta solo son que se pasan cuando el complemento se abre por primera vez.

Python

Navega hasta las definiciones de las rutas de Flask (routes.py si estás con el ejemplo proporcionado). En la parte superior de la ruta de destino del complemento (/classroom-addon en el ejemplo proporcionado), recupera y almacena el Parámetro de consulta de login_hint:

# If the login_hint query parameter is available, we'll store it in the session.
if flask.request.args.get("login_hint"):
    flask.session["login_hint"] = flask.request.args.get("login_hint")

Asegúrate de que login_hint (si está presente) esté almacenado en la sesión. Este es un el lugar apropiado para almacenar estos valores; son efímeras y recibes valores nuevos cuando se abre el complemento.

# It's possible that we might return to this route later, in which case the
# parameters will not be passed in. Instead, use the values cached in the
# session.
login_hint = flask.session.get("login_hint")

# If there's still no login_hint query parameter, this must be their first
# time signing in, so send the user to the sign in page.
if login_hint is None:
    return start_auth_flow()

Java

Navega a la ruta de destino del complemento en tu clase de controlador. (/addon-discovery en AuthController.java en el ejemplo proporcionado). En al comienzo de esta ruta, recuperarás y almacenarás la consulta login_hint parámetro.

/** Retrieve the login_hint or hd query parameters from the request URL. */
String login_hint = request.getParameter("login_hint");
String hd = request.getParameter("hd");

Asegúrate de que login_hint (si está presente) esté almacenado en la sesión. Este es un el lugar apropiado para almacenar estos valores; son efímeras y recibes valores nuevos cuando se abre el complemento.

/** If neither query parameter is sent, use the values in the session. */
if (login_hint == null && hd == null) {
    login_hint = (String) session.getAttribute("login_hint");
    hd = (String) session.getAttribute("hd");
}

/** If the hd query parameter is provided, add hd to the session and route
*   the user to the authorization page. */
else if (hd != null) {
    session.setAttribute("hd", hd);
    return startAuthFlow(model);
}

/** If the login_hint query parameter is provided, add it to the session. */
else if (login_hint != null) {
    session.setAttribute("login_hint", login_hint);
}

Agrega los parámetros de consulta al flujo de autorización

El parámetro login_hint debe pasarse a los servidores de autenticación de Google a tus conjuntos de datos. Esto facilita el proceso de autenticación. si tu aplicación sabe qué usuario intenta autenticarse, el servidor usa la sugerencia para simplificar la completando previamente el campo de correo electrónico en el formulario de acceso.

Python

Navega a la ruta de autorización en el archivo del servidor de Flask (/authorize) en el ejemplo proporcionado). Agrega el argumento login_hint a la llamada para flow.authorization_url

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",
    # The user will automatically be selected if we have the login_hint.
    login_hint=flask.session.get("login_hint"),

Java

Navega hasta el método authorize() en la clase AuthService.java. Agrega login_hint y hd como parámetros al método, y agrega login_hint y hd al compilador de URLs de autorización.

String authUrl = flow
    .newAuthorizationUrl()
    .setState(state)
    .set("login_hint", login_hint)
    .set("hd", hd)
    .setRedirectUri(REDIRECT_URI)
    .build();

Agrega almacenamiento continuo para las credenciales de los usuarios

Si recibes login_hint como parámetro de consulta cuando se carga el complemento, se trata de una indicación de que el usuario ya completó el flujo de autorización para nuestro y mantener la integridad de su aplicación. Debes recuperar sus credenciales anteriores en lugar de forzar que accedan de nuevo.

Recuerda que recibiste un token de actualización al completar el de autorización. Guarda este token. reutilizarlo para obtener un token de acceso; que es de corta duración y es necesaria para usar las APIs de Google. Ya guardaste estas credenciales en la sesión, pero debes almacenarlas para manejar las visitas repetidas.

Define el esquema de usuario y configura la base de datos

Configura un esquema de base de datos para un User.

Python

Define el esquema de usuario

Un User contiene los siguientes atributos:

  • id: Es el ID de Google del usuario. Debe coincidir con los valores proporcionados en Parámetro de consulta login_hint.
  • display_name: El nombre y apellido del usuario, como “Alejandro Pérez”.
  • email: La dirección de correo electrónico del usuario.
  • portrait_url: Es la URL de la foto de perfil del usuario.
  • refresh_token: El token de actualización que se adquirió anteriormente.

En este ejemplo, se implementa el almacenamiento con SQLite, que es compatible de forma nativa con en Python. Usa el módulo flask_sqlalchemy para facilitar nuestra base de datos y administración de posturas.

Configura la base de datos

Primero, especifica la ubicación de un archivo para nuestra base de datos. Navega a tu servidor de Terraform (config.py en el ejemplo proporcionado) y agrega el siguientes.

import os

# Point to a database file in the project root.
DATABASE_FILE_NAME = os.path.join(
    os.path.abspath(os.path.dirname(__file__)), 'data.sqlite')

class Config(object):
    SQLALCHEMY_DATABASE_URI = f"sqlite:///{DATABASE_FILE_NAME}"
    SQLALCHEMY_TRACK_MODIFICATIONS = False

Esto dirige Flask al archivo data.sqlite en el mismo directorio que tu main.py archivo.

A continuación, navega al directorio de tu módulo y crea un nuevo archivo models.py. Es webapp/models.py si sigues el ejemplo que te proporcionamos. Agrega lo siguiente al archivo nuevo para definir la tabla User, con lo que se sustituye el nombre de módulo para webapp si es diferente.

from webapp import db

# Database model to represent a user.
class User(db.Model):
    # The user's identifying information:
    id = db.Column(db.String(120), primary_key=True)
    display_name = db.Column(db.String(80))
    email = db.Column(db.String(120), unique=True)
    portrait_url = db.Column(db.Text())

    # The user's refresh token, which will be used to obtain an access token.
    # Note that refresh tokens will become invalid if:
    # - The refresh token has not been used for six months.
    # - The user revokes your app's access permissions.
    # - The user changes passwords.
    # - The user belongs to a Google Cloud organization
    #   that has session control policies in effect.
    refresh_token = db.Column(db.Text())

Por último, en el archivo __init__.py de tu módulo, agrega lo siguiente para importar los modelos nuevos y crear la base de datos.

from webapp import models
from os import path
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy(app)

# Initialize the database file if not created.
if not path.exists(config.DATABASE_FILE_NAME):
    db.create_all()

Java

Define el esquema de usuario

Un User contiene los siguientes atributos:

  • id: Es el ID de Google del usuario. Debe coincidir con el valor proporcionado en la Parámetro de consulta login_hint.
  • email: La dirección de correo electrónico del usuario.

Crea un archivo schema.sql en el directorio resources del módulo. Primavera lee este archivo y genera un esquema para la base de datos según corresponda. Define la tabla con un nombre de tabla, users y columnas para representar los atributos User, id y email

CREATE TABLE IF NOT EXISTS users (
    id VARCHAR(255) PRIMARY KEY, -- user's unique Google ID
    email VARCHAR(255), -- user's email address
);

Crea una clase de Java para definir el modelo User de la base de datos. Este es User.java en el ejemplo proporcionado.

Agrega la anotación @Entity para indicar que se trata de un POJO que se puede guardados en la base de datos. Agrega la anotación @Table con el nombre de la tabla correspondiente que configuraste en schema.sql.

Ten en cuenta que el ejemplo de código incluye constructores y establecedores para los dos atributos. El constructor y los métodos set se usan en AuthController.java para crear o actualizar un usuario en la base de datos. Tú también puedes incluir métodos get y un método toString, como creas conveniente, pero para esta explicación en particular, estos métodos no se usan y se omiten de el ejemplo de código de esta página para mayor brevedad.

/** An entity class that provides a model to store user information. */
@Entity
@Table(name = "users")
public class User {
    /** The user's unique Google ID. The @Id annotation specifies that this
     *   is the primary key. */
    @Id
    @Column
    private String id;

    /** The user's email address. */
    @Column
    private String email;

    /** Required User class no args constructor. */
    public User() {
    }

    /** The User class constructor that creates a User object with the
    *   specified parameters.
    *   @param id the user's unique Google ID
    *   @param email the user's email address
    */
    public User(String id, String email) {
        this.id = id;
        this.email = email;
    }

    public void setId(String id) { this.id = id; }

    public void setEmail(String email) { this.email = email; }
}

Crea una interfaz llamada UserRepository.java para controlar las operaciones de CRUD a la base de datos. Esta interfaz extiende la interfaz CrudRepository.

/** Provides CRUD operations for the User class by extending the
 *   CrudRepository interface. */
@Repository
public interface UserRepository extends CrudRepository<User, String> {
}

La clase del controlador facilita la comunicación entre el cliente y el en un repositorio de confianza. Por lo tanto, actualiza el constructor de la clase del controlador para insertar la clase UserRepository

/** Declare UserRepository to be used in the Controller class constructor. */
private final UserRepository userRepository;

/**
*   ...
*   @param userRepository the class that interacts with User objects stored in
*   persistent storage.
*/
public AuthController(AuthService authService, UserRepository userRepository) {
    this.authService = authService;
    this.userRepository = userRepository;
}

Configura la base de datos

Para almacenar información relacionada con el usuario, usa una base de datos H2 que sea intrínsecamente compatibles con Spring Boot. Esta base de datos también se usa en versiones posteriores explicaciones para almacenar otros archivos información. Para configurar la base de datos de H2 es necesario agregar lo siguiente: actual en application.properties.

# Enable configuration for persistent storage using an H2 database
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:file:./h2/userdb
spring.datasource.username=<USERNAME>
spring.datasource.password=<PASSWORD>
spring.jpa.hibernate.ddl-auto=update
spring.jpa.open-in-view=false

La configuración spring.datasource.url crea un directorio, llamado h2, con el archivo userdb almacenado en él. Agrega la ruta de acceso a la base de datos de H2 para .gitignore Debes actualizar spring.datasource.username y spring.datasource.password antes de ejecutar la aplicación para configurar en tu base de datos con un nombre de usuario y una contraseña. Para actualizar el nombre de usuario y contraseña para la base de datos después de ejecutar la aplicación, borrar el directorio h2 generado, actualizar la configuración y vuelve a ejecutar la aplicación.

Establecer la configuración de spring.jpa.hibernate.ddl-auto en update garantiza que Los datos almacenados en la base de datos se conservan cuando se reinicia la aplicación. Para borrar la base de datos cada vez que se reinicie la aplicación, configura esta opción config en create.

Establece la configuración de spring.jpa.open-in-view en false. Esta configuración está habilitada de forma predeterminada y causan problemas de rendimiento difíciles de diagnosticar en producción.

Como se describió anteriormente, debes poder recuperar las credenciales de un usuario recurrente. El almacenamiento de credenciales integrado asistencia que ofrece GoogleAuthorizationCodeFlow.

En la clase AuthService.java, define una ruta de acceso al archivo en la que de la clase de credencial de Google. En este ejemplo, se crea el archivo en la /credentialStore. Agrega la ruta de acceso al almacén de credenciales al .gitignore Este directorio se genera una vez que el usuario inicia la de autorización.

private static final File dataDirectory = new File("credentialStore");

A continuación, crea un método en el archivo AuthService.java que cree y muestra un objeto FileDataStoreFactory. Este es el almacén de datos almacena las credenciales.

/** Creates and returns FileDataStoreFactory object to store credentials.
 *   @return FileDataStoreFactory dataStore used to save and obtain users ids
 *   mapped to Credentials.
 *   @throws IOException if creating the dataStore is unsuccessful.
 */
public FileDataStoreFactory getCredentialDataStore() throws IOException {
    FileDataStoreFactory dataStore = new FileDataStoreFactory(dataDirectory);
    return dataStore;
}

Actualiza el método getFlow() en AuthService.java para incluir setDataStoreFactory en GoogleAuthorizationCodeFlow Builder() y llama a getCredentialDataStore() para configurar el almacén de datos.

GoogleAuthorizationCodeFlow authorizationCodeFlow =
    new GoogleAuthorizationCodeFlow.Builder(
        HTTP_TRANSPORT,
        JSON_FACTORY,
        getClientSecrets(),
        getScopes())
    .setAccessType("offline")
    .setDataStoreFactory(getCredentialDataStore())
    .build();

A continuación, actualiza el método getAndSaveCredentials(String authorizationCode). Anteriormente, este método obtenía credenciales sin almacenarlas desde cualquier parte. Actualiza el método para almacenar las credenciales en el almacén de datos se indexa según el ID del usuario.

El ID de usuario se puede obtener del objeto TokenResponse con el id_token, pero debe verificarse primero. De lo contrario, el cliente aplicaciones pueden suplantar la identidad de los usuarios enviando mensajes de usuario modificados IDs al servidor. se recomienda que uses el cliente de la API de Google bibliotecas para validar el objeto id_token. Consulta la [página de Google Identity en cómo verificar el token de ID de Google] para obtener más información.

// Obtaining the id_token will help determine which user signed in to the application.
String idTokenString = tokenResponse.get("id_token").toString();

// Validate the id_token using the GoogleIdTokenVerifier object.
GoogleIdTokenVerifier googleIdTokenVerifier = new GoogleIdTokenVerifier.Builder(
        HTTP_TRANSPORT,
        JSON_FACTORY)
    .setAudience(Collections.singletonList(
        googleClientSecrets.getWeb().getClientId()))
    .build();

GoogleIdToken idToken = googleIdTokenVerifier.verify(idTokenString);

if (idToken == null) {
    throw new Exception("Invalid ID token.");
}

Una vez que se haya verificado el id_token, obtén el userId para almacenarlo junto con con las credenciales obtenidas.

// Obtain the user id from the id_token.
Payload payload = idToken.getPayload();
String userId = payload.getSubject();

Actualiza la llamada a flow.createAndStoreCredential para incluir userId.

// Save the user id and credentials to the configured FileDataStoreFactory.
Credential credential = flow.createAndStoreCredential(tokenResponse, userId);

Agrega un método a la clase AuthService.java que muestre las credenciales. para un usuario específico, si existe en el almacén de datos.

/** Find credentials in the datastore based on a specific user id.
*   @param userId key to find in the file datastore.
*   @return Credential object to be returned if a matching key is found in the datastore. Null if
*   the key doesn't exist.
*   @throws Exception if building flow object or checking for userId key is unsuccessful. */
public Credential loadFromCredentialDataStore(String userId) throws Exception {
    try {
        GoogleAuthorizationCodeFlow flow = getFlow();
        Credential credential = flow.loadCredential(userId);
        return credential;
    } catch (Exception e) {
        e.printStackTrace();
        throw e;
    }
}

Recupera credenciales

Define un método para recuperar Users. Se te proporciona un id en las Parámetro de consulta login_hint, que puedes usar para recuperar un usuario específico registro.

Python

def get_credentials_from_storage(id):
    """
    Retrieves credentials from the storage and returns them as a dictionary.
    """
    return User.query.get(id)

Java

En la clase AuthController.java, define un método desde el cual recuperar un usuario. la base de datos en función de su ID de usuario.

/** Retrieves stored credentials based on the user id.
*   @param id the id of the current user
*   @return User the database entry corresponding to the current user or null
*   if the user doesn't exist in the database.
*/
public User getUser(String id) {
    if (id != null) {
        Optional<User> user = userRepository.findById(id);
        if (user.isPresent()) {
            return user.get();
        }
    }
    return null;
}

Almacenar credenciales

Hay dos situaciones en las que se almacenan las credenciales. Si el id del usuario ya es en la base de datos y, luego, actualiza el registro existente con cualquier valor nuevo. De lo contrario, Crea un registro User nuevo y agrégalo a la base de datos.

Python

Primero, define un método de utilidad que implemente la configuración el comportamiento de los usuarios.

def save_user_credentials(credentials=None, user_info=None):
    """
    Updates or adds a User to the database. A new user is added only if both
    credentials and user_info are provided.

    Args:
        credentials: An optional Credentials object.
        user_info: An optional dict containing user info returned by the
            OAuth 2.0 API.
    """

    existing_user = get_credentials_from_storage(
        flask.session.get("login_hint"))

    if existing_user:
        if user_info:
            existing_user.id = user_info.get("id")
            existing_user.display_name = user_info.get("name")
            existing_user.email = user_info.get("email")
            existing_user.portrait_url = user_info.get("picture")

        if credentials and credentials.refresh_token is not None:
            existing_user.refresh_token = credentials.refresh_token

    elif credentials and user_info:
        new_user = User(id=user_info.get("id"),
                        display_name=user_info.get("name"),
                        email=user_info.get("email"),
                        portrait_url=user_info.get("picture"),
                        refresh_token=credentials.refresh_token)

        db.session.add(new_user)

    db.session.commit()

Existen dos instancias en las que puedes guardar las credenciales en tu base de datos: cuando el usuario vuelve a tu aplicación al final de la de autorización y cuando se emite una llamada a la API. Aquí es donde se configuró antes la clave credentials de la sesión.

Llama a save_user_credentials al final de la ruta callback. Mantén la user_info en lugar de solo extraer el nombre del usuario.

# The flow is complete! We'll use the credentials to fetch the user's info.
user_info_service = googleapiclient.discovery.build(
    serviceName="oauth2", version="v2", credentials=credentials)

user_info = user_info_service.userinfo().get().execute()

flask.session["username"] = user_info.get("name")

save_user_credentials(credentials, user_info)

También debes actualizar las credenciales después de las llamadas a la API. En este En este caso, puedes proporcionar las credenciales actualizadas como argumentos al save_user_credentials.

# Save credentials in case access token was refreshed.
flask.session["credentials"] = credentials_to_dict(credentials)
save_user_credentials(credentials)

Java

Primero, define un método que almacene o actualice un objeto User en H2 en la base de datos.

/** Adds or updates a user in the database.
*   @param credential the credentials object to save or update in the database.
*   @param userinfo the userinfo object to save or update in the database.
*   @param session the current session.
*/
public void saveUser(Credential credential, Userinfo userinfo, HttpSession session) {
    User storedUser = null;
    if (session != null && session.getAttribute("login_hint") != null) {
        storedUser = getUser(session.getAttribute("login_hint").toString());
    }

    if (storedUser != null) {
        if (userinfo != null) {
            storedUser.setId(userinfo.getId());
            storedUser.setEmail(userinfo.getEmail());
        }
        userRepository.save(storedUser);
    } else if (credential != null && userinfo != null) {
        User newUser = new User(
            userinfo.getId(),
            userinfo.getEmail(),
        );
        userRepository.save(newUser);
    }
}

Existen dos instancias en las que puedes guardar las credenciales en tu base de datos: cuando el usuario vuelve a tu aplicación al final de la de autorización y cuando se emite una llamada a la API. Aquí es donde se configuró antes la clave credentials de la sesión.

Llama a saveUser al final de la ruta /callback. Debes mantener la user_info en lugar de solo extraer el correo electrónico del usuario.

/** This is the end of the auth flow. We should save user info to the database. */
Userinfo userinfo = authService.getUserInfo(credentials);
saveUser(credentials, userinfo, session);

También debes actualizar las credenciales después de las llamadas a la API. En este En este caso, puedes proporcionar las credenciales actualizadas como argumentos al saveUser. .

/** Save credentials in case access token was refreshed. */
saveUser(credentials, null, session);

Credenciales vencidas

Ten en cuenta que hay algunos motivos por los que los tokens de actualización pueden no ser válidos. Estos son algunos de ellos:

  • El token de actualización no se usó durante seis meses.
  • El usuario revoca los permisos de acceso de tu app.
  • El usuario cambia las contraseñas.
  • El usuario pertenece a una organización de Google Cloud que tiene control de sesiones. políticas vigentes.

Adquiere tokens nuevos enviando al usuario nuevamente a través del flujo de autorización en los siguientes casos: sus credenciales ya no son válidas.

Dirigir automáticamente al usuario

Modifica la ruta de destino del complemento para detectar si el usuario autorizó anteriormente nuestra aplicación. Si es así, dirígelos a nuestra página principal de complementos. De lo contrario, solicita para que accedan.

Python

Asegúrate de que el archivo de base de datos se haya creado cuando la aplicación lanzamientos de software. Inserta lo siguiente en un inicializador de módulo (como webapp/__init__.py en el ejemplo proporcionado) o en el método principal que inicia el servidor.

# Initialize the database file if not created.
if not os.path.exists(DATABASE_FILE_NAME):
    db.create_all()

Entonces, tu método debería controlar el parámetro de consulta login_hint como explicado anteriormente. Luego, carga las credenciales de la tienda si esto es una repetición. visitante. Sabrás que fue un visitante recurrente si recibiste login_hint. Recupera todas las credenciales almacenadas para este usuario y cárgalas en la sesión.

stored_credentials = get_credentials_from_storage(login_hint)

# If we have stored credentials, store them in the session.
if stored_credentials:
    # Load the client secrets file contents.
    client_secrets_dict = json.load(
        open(CLIENT_SECRETS_FILE)).get("web")

    # Update the credentials in the session.
    if not flask.session.get("credentials"):
        flask.session["credentials"] = {}

    flask.session["credentials"] = {
        "token": stored_credentials.access_token,
        "refresh_token": stored_credentials.refresh_token,
        "token_uri": client_secrets_dict["token_uri"],
        "client_id": client_secrets_dict["client_id"],
        "client_secret": client_secrets_dict["client_secret"],
        "scopes": SCOPES
    }

    # Set the username in the session.
    flask.session["username"] = stored_credentials.display_name

Por último, redirige al usuario a la página de acceso si no tenemos su credenciales. De ser así, dirígelos a la página principal del complemento.

if "credentials" not in flask.session or \
    flask.session["credentials"]["refresh_token"] is None:
    return flask.render_template("authorization.html")

return flask.render_template(
    "addon-discovery.html",
    message="You've reached the addon discovery page.")

Java

Navega a la ruta de destino del complemento (/addon-discovery en las ejemplo). Como se mencionó anteriormente, aquí es donde manejaste el login_hint parámetro de consulta.

Primero, comprueba si existen credenciales en la sesión. De lo contrario, enruta el usuario a través del flujo de Auth llamando al método startAuthFlow.

/** Check if the credentials exist in the session. The session could have
 *   been cleared when the user clicked the Sign-Out button, and the expected
 *   behavior after sign-out would be to display the sign-in page when the
 *   iframe is opened again. */
if (session.getAttribute("credentials") == null) {
    return startAuthFlow(model);
}

Luego, carga el usuario desde la base de datos de H2 si se trata de un visitante recurrente. Es un visitante recurrente si recibes el parámetro de consulta login_hint Si el botón el usuario existe en la base de datos de H2, carga las credenciales de la credencial configurado previamente y establecer las credenciales en la sesión. Si el botón no se obtuvieron del almacén de datos, enruta al usuario a través del flujo de Auth llamando a startAuthFlow.

/** At this point, we know that credentials exist in the session, but we
 *   should update the session credentials with the credentials in persistent
 *   storage in case they were refreshed. If the credentials in persistent
 *   storage are null, we should navigate the user to the authorization flow
 *   to obtain persisted credentials. */

User storedUser = getUser(login_hint);

if (storedUser != null) {
    Credential credential = authService.loadFromCredentialDataStore(login_hint);
    if (credential != null) {
        session.setAttribute("credentials", credential);
    } else {
        return startAuthFlow(model);
    }
}

Por último, dirija al usuario a la página de destino del complemento.

/** Finally, if there are credentials in the session and in persistent
 *   storage, direct the user to the addon-discovery page. */
return "addon-discovery";

Prueba el complemento

Accede a Google Classroom como uno de tus exámenes de Profesor. usuarios. Navega a la pestaña Trabajo en clase y crea una nueva Tarea. Haz clic en haz clic en el botón Complementos debajo del área de texto y, luego, selecciona tu complemento. El iframe se abre y el complemento carga la El URI de configuración de archivos adjuntos que especificaste en el Página Configuración de la app del SDK de Google Workspace Marketplace.

¡Felicitaciones! Ya puedes continuar con el siguiente paso: crear archivos adjuntos identificar el rol del usuario.