Este é o terceiro tutorial da série de tutoriais de complementos do Google Sala de Aula.
Neste tutorial, você vai gerenciar visitas repetidas ao complemento recuperando automaticamente as credenciais concedidas anteriormente por um usuário. Você então encaminha os usuários para páginas de onde eles podem emitir solicitações de API imediatamente. Esse é um comportamento obrigatório para os complementos do Google Sala de Aula.
Neste tutorial, você vai fazer o seguinte:
- Implementar um armazenamento permanente para as credenciais de usuário.
- Recupere e avalie o parâmetro de consulta do complemento
login_hint
. Esse é um número de ID exclusivo do Google do usuário conectado.
Depois de terminar, você pode autorizar totalmente os usuários no seu app da Web e emitir chamadas para as APIs do Google.
Entender os parâmetros de consulta do iframe
O Google Sala de Aula carrega o URI de configuração de anexos do complemento quando ele é aberto. O Google Sala de Aula anexa vários parâmetros de consulta GET
ao URI. Eles contêm informações contextuais úteis. Se, por exemplo, o URI de descoberta de anexos for https://example.com/addon
, o Google Sala de Aula criará o iframe com o URL de origem definido como https://example.com/addon?courseId=XXX&itemId=YYY&itemType=courseWork&addOnToken=ZZZ
, em que XXX
, YYY
e ZZZ
são IDs de string. Consulte o guia de iframes para ver uma descrição detalhada desse cenário.
Há cinco parâmetros de consulta possíveis para o URL de descoberta:
courseId
: o ID do curso atual do Google Sala de Aula.itemId
: o ID do item de stream que o usuário está editando ou criando.itemType
: o tipo de item de stream que o usuário está criando ou editando:courseWork
,courseWorkMaterial
ouannouncement
.addOnToken
: token usado para autorizar determinadas ações de complementos do Google Sala de Aula.login_hint
: o ID do Google do usuário atual.
Este tutorial aborda login_hint
. Os usuários são encaminhados com base no fornecimento desse parâmetro de consulta: para o fluxo de autorização, se ausente, ou para a página de descoberta do complemento, se houver.
Acessar os parâmetros de consulta
Os parâmetros de consulta são transmitidos ao aplicativo da Web na string URI. Armazene esses valores na sua sessão. Eles são usados no fluxo de autorização e para armazenar e recuperar informações sobre o usuário. Esses parâmetros de consulta só são transmitidos quando o complemento é aberto pela primeira vez.
Python
Navegue até as definições das rotas do Flask (routes.py
se você estiver
seguindo o exemplo fornecido). Na parte de cima da rota de destino do complemento
(/classroom-addon
no exemplo fornecido), recupere e armazene o
parâmetro de consulta 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")
Verifique se login_hint
(se presente) está armazenado na sessão. Esse é um local apropriado para armazenar esses valores. Eles são efêmeros e você recebe novos valores quando o complemento é aberto.
# 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
Navegue até a rota de destino do complemento na classe de controlador
(/addon-discovery
em AuthController.java
no exemplo fornecido). No início desse trajeto, recupere e armazene o parâmetro de consulta login_hint
.
/** Retrieve the login_hint or hd query parameters from the request URL. */
String login_hint = request.getParameter("login_hint");
String hd = request.getParameter("hd");
Verifique se login_hint
(se presente) está armazenado na sessão. Esse é um local apropriado para armazenar esses valores. Eles são efêmeros e você recebe novos valores quando o complemento é aberto.
/** 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);
}
Adicionar os parâmetros de consulta ao fluxo de autorização
O parâmetro login_hint
também precisa ser transmitido para os servidores de autenticação do Google. Isso facilita o processo de autenticação. Se o aplicativo souber
qual usuário está tentando se autenticar, o servidor usará a dica para simplificar o
fluxo de login preenchendo automaticamente o campo de e-mail no formulário de login.
Python
Navegue até a rota de autorização no arquivo de servidor Flask (/authorize
no exemplo fornecido). Adicione o argumento login_hint
à chamada 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
Navegue até o método authorize()
na classe AuthService.java
. Adicione login_hint
e hd
como parâmetros ao método e inclua os argumentos login_hint
e hd
no Criador de URLs de autorização.
String authUrl = flow
.newAuthorizationUrl()
.setState(state)
.set("login_hint", login_hint)
.set("hd", hd)
.setRedirectUri(REDIRECT_URI)
.build();
Adicionar armazenamento permanente para credenciais de usuário
Se você receber login_hint
como um parâmetro de consulta quando o complemento for carregado, isso indica que o usuário já concluiu o fluxo de autorização do nosso aplicativo. Em vez de forçá-los a fazer login novamente, recupere as credenciais anteriores deles.
Lembre-se de que você recebeu um token de atualização após a conclusão do fluxo de autorização. Salve esse token. Ele será reutilizado para receber um token de acesso, que é de curta duração e necessário para usar as APIs do Google. Você salvou essas credenciais na sessão, mas precisa armazená-las para processar visitas repetidas.
Definir o esquema do usuário e configurar o banco de dados
Configure um esquema de banco de dados para um User
.
Python
Definir o esquema do usuário
Uma User
contém os seguintes atributos:
id
: o ID do Google do usuário. Ele precisa corresponder aos valores fornecidos no parâmetro de consultalogin_hint
.display_name
: o nome e sobrenome do usuário, como "Alex Smith".email
: o endereço de e-mail do usuário.portrait_url
: o URL da foto do perfil do usuário.refresh_token
: o token de atualização adquirido anteriormente.
Este exemplo implementa o armazenamento usando o SQLite, que tem suporte nativo do
Python. Ele usa o módulo flask_sqlalchemy
para facilitar o gerenciamento do
banco de dados.
Configurar o banco de dados
Primeiro, especifique o local do arquivo do nosso banco de dados. Navegue até o arquivo de configuração do servidor (config.py
no exemplo fornecido) e adicione o seguinte:
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
Isso aponta o Flask para o arquivo data.sqlite
no mesmo diretório do
arquivo main.py
.
Em seguida, navegue até o diretório do módulo e crie um novo arquivo models.py
.
Se você estiver seguindo o exemplo fornecido, ele será webapp/models.py
. Adicione
o seguinte ao novo arquivo para definir a tabela User
, substituindo o
nome do módulo por webapp
, se for 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 fim, no arquivo __init__.py
do módulo, adicione o comando a seguir para importar
os novos modelos e criar o banco de dados.
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
Definir o esquema do usuário
Uma User
contém os seguintes atributos:
id
: o ID do Google do usuário. Ele precisa corresponder ao valor fornecido no parâmetro de consultalogin_hint
.email
: o endereço de e-mail do usuário.
Crie um arquivo schema.sql
no diretório resources
do módulo. O Spring
lê esse arquivo e gera um esquema para o banco de dados de acordo com ele.
Defina a tabela com um nome de tabela, users
, e colunas para representar
os atributos User
, id
e email
.
CREATE TABLE IF NOT EXISTS users (
id VARCHAR(255) PRIMARY KEY, -- user's unique Google ID
email VARCHAR(255), -- user's email address
);
Crie uma classe Java para definir o modelo User
para o banco de dados. É User.java
no exemplo fornecido.
Adicione a anotação @Entity
para indicar que esse é um POJO que pode ser salvo no banco de dados. Adicione a anotação @Table
com o nome da tabela correspondente que você configurou em schema.sql
.
O exemplo de código inclui construtores e setters para os dois atributos. O construtor e os setters são usados em AuthController.java
para criar ou atualizar um usuário no banco de dados. Você
também pode incluir getters e um método toString
conforme achar adequado, mas, para
este tutorial específico, esses métodos não são usados e são omitidos do
exemplo de código nesta página para agilizar.
/** 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; }
}
Crie uma interface chamada UserRepository.java
para processar operações CRUD
no banco de dados. Essa interface estende a interface CrudRepository
.
/** Provides CRUD operations for the User class by extending the
* CrudRepository interface. */
@Repository
public interface UserRepository extends CrudRepository<User, String> {
}
A classe de controladores facilita a comunicação entre o cliente e o repositório. Portanto, atualize o construtor da classe do controlador para injetar
a classe 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;
}
Configurar o banco de dados
Para armazenar informações relacionadas ao usuário, use um banco de dados H2 que seja intrinsecamente
compatível com o Spring Boot. Esse banco de dados também será usado em tutoriais posteriores para armazenar outras informações relacionadas ao Google Sala de Aula. A configuração do banco de dados H2 requer a inclusão da seguinte configuração em 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
A configuração spring.datasource.url
cria um diretório, chamado h2
, com o arquivo userdb
armazenado nele. Adicione o caminho ao banco de dados H2 ao
.gitignore
. Atualize spring.datasource.username
e spring.datasource.password
antes de executar o aplicativo para definir o banco de dados com um nome de usuário e uma senha de sua escolha. Para atualizar o
nome de usuário e a senha do banco de dados depois de executar o aplicativo,
exclua o diretório h2
gerado, atualize a configuração e
execute o aplicativo novamente.
Definir a configuração spring.jpa.hibernate.ddl-auto
como update
garante que
os dados armazenados no banco de dados sejam preservados quando o aplicativo for reiniciado.
Para limpar o banco de dados sempre que o aplicativo for reiniciado, defina esta configuração como create
.
Defina a configuração spring.jpa.open-in-view
como false
. Essa configuração é ativada por padrão e pode resultar em problemas de desempenho difíceis de diagnosticar na produção.
Conforme descrito anteriormente, você precisa conseguir recuperar as credenciais de um
usuário recorrente. Isso é facilitado pelo suporte ao armazenamento de credenciais integrado oferecido pelo GoogleAuthorizationCodeFlow
.
Na classe AuthService.java
, defina um caminho para o arquivo em que a
classe de credencial está armazenada. Neste exemplo, o arquivo é criado no
diretório /credentialStore
. Adicione o caminho para o armazenamento de credenciais ao .gitignore
. Esse diretório é gerado quando o usuário inicia o
fluxo de autorização.
private static final File dataDirectory = new File("credentialStore");
Em seguida, crie um método no arquivo AuthService.java
que crie e retorne um objeto FileDataStoreFactory
. Esse é o repositório de dados que armazena as credenciais.
/** 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;
}
Atualize o método getFlow()
em AuthService.java
para incluir
setDataStoreFactory
no método GoogleAuthorizationCodeFlow Builder()
e chame getCredentialDataStore()
para definir o repositório de dados.
GoogleAuthorizationCodeFlow authorizationCodeFlow =
new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT,
JSON_FACTORY,
getClientSecrets(),
getScopes())
.setAccessType("offline")
.setDataStoreFactory(getCredentialDataStore())
.build();
Em seguida, atualize o método getAndSaveCredentials(String authorizationCode)
.
Anteriormente, esse método recebia credenciais sem armazená-las em nenhum lugar. Atualize o método para armazenar as credenciais no armazenamento de dados
indexado pelo ID do usuário.
O ID do usuário pode ser obtido do objeto TokenResponse
usando o
id_token
, mas precisa ser verificado primeiro. Caso contrário, os aplicativos
clientes poderão se passar por usuários enviando IDs de usuário
modificados para o servidor. É recomendável usar as bibliotecas de cliente das APIs do
Google para validar o id_token
. Consulte a [página do Google Identity sobre
como verificar o token de ID do Google] para mais informações.
// 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.");
}
Depois que o id_token
for verificado, extraia o userId
para armazenar com
as credenciais recebidas.
// Obtain the user id from the id_token.
Payload payload = idToken.getPayload();
String userId = payload.getSubject();
Atualize a chamada para flow.createAndStoreCredential
para incluir o userId
.
// Save the user id and credentials to the configured FileDataStoreFactory.
Credential credential = flow.createAndStoreCredential(tokenResponse, userId);
Adicione um método à classe AuthService.java
que retorne as credenciais
de um usuário específico, se ele existir no armazenamento de dados.
/** 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;
}
}
Recuperar credenciais
Defina um método para buscar Users
. Você receberá um id
no parâmetro de consulta login_hint
, que pode ser usado para recuperar um registro de usuário específico.
Python
def get_credentials_from_storage(id):
"""
Retrieves credentials from the storage and returns them as a dictionary.
"""
return User.query.get(id)
Java
Na classe AuthController.java
, defina um método para recuperar um usuário do
banco de dados com base no ID do usuário.
/** 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;
}
Armazenar credenciais
Há dois cenários ao armazenar credenciais. Se o id
do usuário já estiver
no banco de dados, atualize o registro atual com os novos valores. Caso contrário,
crie um novo registro User
e adicione-o ao banco de dados.
Python
Primeiro, defina um método utilitário que implemente o comportamento de armazenamento ou atualização.
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()
Há duas instâncias em que é possível salvar credenciais em seu banco de dados: quando o usuário retorna ao seu aplicativo no final do fluxo de autorização e ao emitir uma chamada de API. É aqui
que definimos a chave credentials
da sessão.
Chame save_user_credentials
no final do seu trajeto callback
. Mantenha o objeto
user_info
em vez de apenas extrair o nome do usuário.
# 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)
Atualize também as credenciais após as chamadas para a API. Nesse
caso, forneça as credenciais atualizadas como argumentos para o
método save_user_credentials
.
# Save credentials in case access token was refreshed.
flask.session["credentials"] = credentials_to_dict(credentials)
save_user_credentials(credentials)
Java
Primeiro, defina um método que armazene ou atualize um objeto User
no banco de dados
H2.
/** 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);
}
}
Há duas instâncias em que é possível salvar credenciais em seu banco de dados: quando o usuário retorna ao seu aplicativo no final do fluxo de autorização e ao emitir uma chamada de API. É aqui
que definimos a chave credentials
da sessão.
Chame saveUser
no final da rota /callback
. Mantenha o objeto
user_info
em vez de apenas extrair o e-mail do usuário.
/** 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);
Atualize também as credenciais após as chamadas para a API. Nesse
caso, forneça as credenciais atualizadas como argumentos para o método
saveUser
.
/** Save credentials in case access token was refreshed. */
saveUser(credentials, null, session);
Credenciais expiradas
Há alguns motivos pelos quais os tokens de atualização podem se tornar inválidos. Veja alguns exemplos:
- O token de atualização não é usado há seis meses.
- O usuário revoga as permissões de acesso do seu app.
- O usuário muda as senhas.
- O usuário pertence a uma organização do Google Cloud com políticas de controle de sessão em vigor.
Adquira novos tokens enviando o usuário pelo fluxo de autorização novamente se as credenciais dele se tornarem inválidas.
Encaminhar o usuário automaticamente
Modifique a rota de destino do complemento para detectar se o usuário autorizou nosso aplicativo anteriormente. Se esse for o caso, encaminhe-os para nossa página principal de complementos. Caso contrário, solicite que eles façam login.
Python
Verifique se o arquivo do banco de dados foi criado quando o aplicativo
for iniciado. Insira o seguinte em um inicializador de módulo (como
webapp/__init__.py
no exemplo fornecido) ou no método principal que
inicia o servidor.
# Initialize the database file if not created.
if not os.path.exists(DATABASE_FILE_NAME):
db.create_all()
Seu método deve processar o parâmetro de consulta login_hint
conforme discutido acima. Em seguida, carregue as credenciais da loja se este for um visitante
repetido. Você sabe que é um visitante recorrente se recebeu login_hint
.
Recupere todas as credenciais armazenadas para esse usuário e carregue-as na sessão.
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 fim, encaminhe o usuário para a página de login se não tivermos as credenciais dele. Se isso acontecer, encaminhe-os para a página principal do 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
Navegue até a rota de destino do complemento (/addon-discovery
no exemplo
fornecido). Conforme discutido acima, é aqui que você processou o parâmetro de consulta login_hint
.
Primeiro, verifique se a sessão tem credenciais. Caso contrário, encaminhe o usuário pelo fluxo de autenticação chamando o 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);
}
Em seguida, carregue o usuário do banco de dados H2 se o visitante for recorrente. Se você receber o parâmetro de consulta login_hint
, o evento será um visitante repetido. Se o
usuário estiver no banco de dados H2, carregue as credenciais do repositório de
credenciais configurado anteriormente e defina-as na sessão. Se as
credenciais não tiverem sido recebidas do armazenamento de dados de credenciais, direcione o usuário
por meio do fluxo de autenticação chamando 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 fim, direcione o usuário para a página de destino do complemento.
/** Finally, if there are credentials in the session and in persistent
* storage, direct the user to the addon-discovery page. */
return "addon-discovery";
Testar o complemento
Faça login no Google Sala de Aula como um dos seus usuários de teste do Professor. Acesse a guia Atividades e crie uma Atividade. Clique no botão Complementos abaixo da área de texto e selecione o complemento. O iframe é aberto e o complemento carrega o URI de configuração de anexos que você especificou na página Configuração do app do SDK do Google Workspace Marketplace.
Parabéns! Está tudo pronto para a próxima etapa: criar anexos e identificar o papel do usuário.