Questa è la seconda procedura dettagliata della serie di procedure dettagliate dei componenti aggiuntivi di Classroom.
In questa procedura dettagliata, aggiungi Accedi con Google all'applicazione web. Si tratta di un comportamento obbligatorio per i componenti aggiuntivi di Classroom. Utilizza le credenziali di questo flusso di autorizzazione per tutte le chiamate future all'API.
Nel corso di questa procedura dettagliata, dovrai completare quanto segue:
- Configura la tua app web in modo da mantenere i dati della sessione all'interno di un iframe.
- Implementa il flusso di accesso server-to-server di Google OAuth 2.0.
- Esegui una chiamata all'API OAuth 2.0.
- Crea route aggiuntivi per supportare l'autorizzazione, la disconnessione e il test delle chiamate API.
Al termine, puoi autorizzare completamente gli utenti nella tua app web ed eseguire chiamate alle API Google.
Informazioni sul flusso di autorizzazione
Le API di Google utilizzano il protocollo OAuth 2.0 per l'autenticazione e l'autorizzazione. La descrizione completa dell'implementazione di OAuth di Google è disponibile nella guida OAuth di Google Identity.
Le credenziali dell'applicazione vengono gestite in Google Cloud. Una volta creati, implementa una procedura in quattro passaggi per autenticare e autorizzare un utente:
- Richiedi l'autorizzazione. Fornisci un URL di callback nell'ambito di questa richiesta. Al termine, riceverai un URL di autorizzazione.
- Reindirizza l'utente all'URL di autorizzazione. La pagina risultante informa l'utente delle autorizzazioni richieste dalla tua app e gli chiede di consentire l'accesso. Al termine, l'utente viene indirizzato all'URL di callback.
- Ricevi un codice di autorizzazione nel percorso di callback. Scambia il codice di autorizzazione con un token di accesso e un token di aggiornamento.
- Effettua chiamate a un'API Google utilizzando i token.
Ottenere le credenziali OAuth 2.0
Assicurati di aver creato e scaricato le credenziali OAuth come descritto nella pagina Panoramica. Il progetto deve utilizzare queste credenziali per far accedere l'utente.
Implementa il flusso di autorizzazione
Aggiungi logica e percorsi alla nostra app web per realizzare il flusso descritto, tra cui queste funzionalità:
- Avvia il flusso di autorizzazione quando raggiungi la pagina di destinazione.
- Richiedi l'autorizzazione e gestisci la risposta del server di autorizzazione.
- Cancella le credenziali archiviate.
- Revoca le autorizzazioni dell'app.
- Testa una chiamata API.
Avvia autorizzazione
Se necessario, modifica la pagina di destinazione per avviare il flusso di autorizzazione. Il plug-in può trovarsi in due possibili stati: ci sono token salvati nella sessione corrente o devi ottenere i token dal server OAuth 2.0. Esegui una chiamata API di prova se sono presenti token nella sessione oppure invita l'utente a eseguire l'accesso.
Python
Apri il file routes.py
. Innanzitutto, imposta un paio di costanti e la nostra configurazione dei cookie in base ai consigli per la sicurezza degli iframe.
# The file that contains the OAuth 2.0 client_id and client_secret.
CLIENT_SECRETS_FILE = "client_secret.json"
# The OAuth 2.0 access scopes to request.
# These scopes must match the scopes in your Google Cloud project's OAuth Consent
# Screen: https://console.cloud.google.com/apis/credentials/consent
SCOPES = [
"openid",
"https://www.googleapis.com/auth/userinfo.profile",
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/classroom.addons.teacher",
"https://www.googleapis.com/auth/classroom.addons.student"
]
# Flask cookie configurations.
app.config.update(
SESSION_COOKIE_SECURE=True,
SESSION_COOKIE_HTTPONLY=True,
SESSION_COOKIE_SAMESITE="None",
)
Vai al percorso di destinazione del componente aggiuntivo (/classroom-addon
nel
file di esempio). Aggiungi la logica per visualizzare una pagina di accesso se la sessione non contiene la chiave "credentials".
@app.route("/classroom-addon")
def classroom_addon():
if "credentials" not in flask.session:
return flask.render_template("authorization.html")
return flask.render_template(
"addon-discovery.html",
message="You've reached the addon discovery page.")
Java
Il codice di questa procedura dettagliata è disponibile nel modulo step_02_sign_in
.
Apri il file application.properties
e aggiungi la configurazione della sessione che segue i consigli per la sicurezza degli iframe.
# iFrame security recommendations call for cookies to have the HttpOnly and
# secure attribute set
server.servlet.session.cookie.http-only=true
server.servlet.session.cookie.secure=true
# Ensures that the session is maintained across the iframe and sign-in pop-up.
server.servlet.session.cookie.same-site=none
Crea una classe di servizio (AuthService.java
nel modulo step_02_sign_in
) per gestire la logica alla base degli endpoint nel file del controller e configurare l'URI di reindirizzamento, la posizione del file dei secret client e gli ambiti richiesti dal componente aggiuntivo. L'URI di reindirizzamento viene utilizzato per reindirizzare gli utenti a un URI specifico
dopo che hanno autorizzato la tua app. Consulta la sezione Configurazione del progetto del
README.md
nel codice sorgente per informazioni su dove posizionare il
file client_secret.json
.
@Service
public class AuthService {
private static final String REDIRECT_URI = "https://localhost:5000/callback";
private static final String CLIENT_SECRET_FILE = "client_secret.json";
private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();
private static final String[] REQUIRED_SCOPES = {
"https://www.googleapis.com/auth/userinfo.profile",
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/classroom.addons.teacher",
"https://www.googleapis.com/auth/classroom.addons.student"
};
/** Creates and returns a Collection object with all requested scopes.
* @return Collection of scopes requested by the application.
*/
public static Collection<String> getScopes() {
return new ArrayList<>(Arrays.asList(REQUIRED_SCOPES));
}
}
Apri il file del controller (AuthController.java
nel modulo step_02_sign_in
) e aggiungi la logica al percorso di destinazione per visualizzare la pagina di accesso se la sessione non contiene la chiave credentials
.
@GetMapping(value = {"/start-auth-flow"})
public String startAuthFlow(Model model) {
try {
return "authorization";
} catch (Exception e) {
return onError(e.getMessage(), model);
}
}
@GetMapping(value = {"/addon-discovery"})
public String addon_discovery(HttpSession session, Model model) {
try {
if (session == null || session.getAttribute("credentials") == null) {
return startAuthFlow(model);
}
return "addon-discovery";
} catch (Exception e) {
return onError(e.getMessage(), model);
}
}
La pagina di autorizzazione deve contenere un link o un pulsante per consentire all'utente di "accedere". Se l'utente fa clic su questo link, dovrebbe essere reindirizzato al percorso authorize
.
Richiesta autorizzazione
Per richiedere l'autorizzazione, crea e reindirizza l'utente a un URL di autenticazione. Questo URL include diverse informazioni, ad esempio gli ambiti richiesti, il percorso di destinazione per l'autorizzazione dopo e l'ID cliente dell'app web. Puoi visualizzarli in questo URL di autorizzazione di esempio.
Python
Aggiungi la seguente importazione al file routes.py
.
import google_auth_oauthlib.flow
Crea un nuovo percorso /authorize
. Crea un'istanza di
google_auth_oauthlib.flow.Flow
. Ti consigliamo vivamente di utilizzare il metodo
from_client_secrets_file
incluso per farlo.
@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)
Imposta il redirect_uri
di flow
, ovvero il percorso a cui vuoi che gli utenti
ritornino dopo aver autorizzato la tua app. Si tratta di /callback
nell'esempio seguente.
# 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("callback", _external=True)
Utilizza l'oggetto flow per creare authorization_url
e state
. Memorizza
state
nella sessione; verrà utilizzato per verificare l'autenticità della risposta del
server in un secondo momento. Infine, reindirizza l'utente a
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")
# Store the state so the callback can verify the auth server response.
flask.session["state"] = state
# Redirect the user to the OAuth authorization URL.
return flask.redirect(authorization_url)
Java
Aggiungi i seguenti metodi al file AuthService.java
per creare un'istanza dell'oggetto
flow e utilizzarlo per recuperare l'URL di autorizzazione:
- Il metodo
getClientSecrets()
legge il file del secret client e crea un oggettoGoogleClientSecrets
. - Il metodo
getFlow()
crea un'istanza diGoogleAuthorizationCodeFlow
. - Il metodo
authorize()
utilizza l'oggettoGoogleAuthorizationCodeFlow
, il parametrostate
e l'URI di reindirizzamento per recuperare l'URL di autorizzazione. Il parametrostate
viene utilizzato per verificare l'autenticità della risposta del server di autorizzazione. Il metodo restituisce quindi una mappa con l'URL di autorizzazione e il parametrostate
.
/** Reads the client secret file downloaded from Google Cloud.
* @return GoogleClientSecrets read in from client secret file. */
public GoogleClientSecrets getClientSecrets() throws Exception {
try {
InputStream in = SignInApplication.class.getClassLoader()
.getResourceAsStream(CLIENT_SECRET_FILE);
if (in == null) {
throw new FileNotFoundException("Client secret file not found: "
+ CLIENT_SECRET_FILE);
}
GoogleClientSecrets clientSecrets = GoogleClientSecrets
.load(JSON_FACTORY, new InputStreamReader(in));
return clientSecrets;
} catch (Exception e) {
throw e;
}
}
/** Builds and returns authorization code flow.
* @return GoogleAuthorizationCodeFlow object used to retrieve an access
* token and refresh token for the application.
* @throws Exception if reading client secrets or building code flow object
* is unsuccessful.
*/
public GoogleAuthorizationCodeFlow getFlow() throws Exception {
try {
GoogleAuthorizationCodeFlow authorizationCodeFlow =
new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT,
JSON_FACTORY,
getClientSecrets(),
getScopes())
.setAccessType("offline")
.build();
return authorizationCodeFlow;
} catch (Exception e) {
throw e;
}
}
/** Builds and returns a map with the authorization URL, which allows the
* user to give the app permission to their account, and the state parameter,
* which is used to prevent cross site request forgery.
* @return map with authorization URL and state parameter.
* @throws Exception if building the authorization URL is unsuccessful.
*/
public HashMap authorize() throws Exception {
HashMap<String, String> authDataMap = new HashMap<>();
try {
String state = new BigInteger(130, new SecureRandom()).toString(32);
authDataMap.put("state", state);
GoogleAuthorizationCodeFlow flow = getFlow();
String authUrl = flow
.newAuthorizationUrl()
.setState(state)
.setRedirectUri(REDIRECT_URI)
.build();
String url = authUrl;
authDataMap.put("url", url);
return authDataMap;
} catch (Exception e) {
throw e;
}
}
Utilizza l'iniezione del costruttore per creare un'istanza della classe di servizio nella classe del controller.
/** Declare AuthService to be used in the Controller class constructor. */
private final AuthService authService;
/** AuthController constructor. Uses constructor injection to instantiate
* the AuthService and UserRepository classes.
* @param authService the service class that handles the implementation logic
* of requests.
*/
public AuthController(AuthService authService) {
this.authService = authService;
}
Aggiungi l'endpoint /authorize
alla classe del controller. Questo endpoint chiama il metodo authorize()
di AuthService per recuperare il parametro state
e l'URL di autorizzazione. L'endpoint memorizza quindi il parametro state
nella sessione e reindirizza gli utenti all'URL di autorizzazione.
/** Redirects the sign-in pop-up to the authorization URL.
* @param response the current response to pass information to.
* @param session the current session.
* @throws Exception if redirection to the authorization URL is unsuccessful.
*/
@GetMapping(value = {"/authorize"})
public void authorize(HttpServletResponse response, HttpSession session)
throws Exception {
try {
HashMap authDataMap = authService.authorize();
String authUrl = authDataMap.get("url").toString();
String state = authDataMap.get("state").toString();
session.setAttribute("state", state);
response.sendRedirect(authUrl);
} catch (Exception e) {
throw e;
}
}
Gestire la risposta del server
Dopo l'autorizzazione, l'utente torna al percorso redirect_uri
del
passaggio precedente. Nell'esempio precedente, questo percorso è /callback
.
Riceverai un code
nella risposta quando l'utente torna dalla pagina di autorizzazione. Quindi scambia il codice con i token di accesso e di aggiornamento:
Python
Aggiungi le seguenti importazioni al file del server Flask.
import google.oauth2.credentials
import googleapiclient.discovery
Aggiungi il percorso al server. Costruisci un'altra istanza di
google_auth_oauthlib.flow.Flow
, ma questa volta riutilizza lo stato salvato nel
passaggio precedente.
@app.route("/callback")
def callback():
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("callback", _external=True)
Successivamente, richiedi i token di accesso e di aggiornamento. Fortunatamente, l'oggetto flow
contiene anche il metodo fetch_token
per farlo. Il metodo si aspetta
gli argomenti code
o authorization_response
. Utilizza authorization_response
, poiché si tratta dell'URL completo della richiesta.
authorization_response = flask.request.url
flow.fetch_token(authorization_response=authorization_response)
Ora hai le credenziali complete. Memorizzali nella sessione in modo che possano essere recuperati in altri metodi o percorsi, quindi reindirizza a una pagina di destinazione del componente aggiuntivo.
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,
"scopes": credentials.scopes
}
# Close the pop-up by rendering an HTML page with a script that redirects
# the owner and closes itself. This can be done with a bit of JavaScript:
# <script>
# window.opener.location.href = "{{ url_for('classroom_addon') }}";
# window.close();
# </script>
return flask.render_template("close-me.html")
Java
Aggiungi alla classe di servizio un metodo che restituisca l'oggetto Credentials
passando il codice di autorizzazione recuperato dal reindirizzamento eseguito dall'URL di autorizzazione. Questo oggetto Credentials
viene utilizzato in un secondo momento per recuperare il token di accesso e il token di aggiornamento.
/** Returns the required credentials to access Google APIs.
* @param authorizationCode the authorization code provided by the
* authorization URL that's used to obtain credentials.
* @return the credentials that were retrieved from the authorization flow.
* @throws Exception if retrieving credentials is unsuccessful.
*/
public Credential getAndSaveCredentials(String authorizationCode) throws Exception {
try {
GoogleAuthorizationCodeFlow flow = getFlow();
GoogleClientSecrets googleClientSecrets = getClientSecrets();
TokenResponse tokenResponse = flow.newTokenRequest(authorizationCode)
.setClientAuthentication(new ClientParametersAuthentication(
googleClientSecrets.getWeb().getClientId(),
googleClientSecrets.getWeb().getClientSecret()))
.setRedirectUri(REDIRECT_URI)
.execute();
Credential credential = flow.createAndStoreCredential(tokenResponse, null);
return credential;
} catch (Exception e) {
throw e;
}
}
Aggiungi un endpoint per l'URI di reindirizzamento al controller. Recupera il codice di autorizzazione e il parametro state
dalla richiesta. Confronta questo
state
parametro con l'attributo state
memorizzato nella sessione. Se c'è corrispondenza, continua con il flusso di autorizzazione. Se non corrispondono,
restituisce un errore.
Quindi, chiama il metodo AuthService
getAndSaveCredentials
e passa il codice di autorizzazione come parametro. Dopo aver recuperato l'oggetto Credentials
, memorizzalo nella sessione. Quindi, chiudi la finestra di dialogo e reindirizza l'utente alla pagina di destinazione del componente aggiuntivo.
/** Handles the redirect URL to grant the application access to the user's
* account.
* @param request the current request used to obtain the authorization code
* and state parameter from.
* @param session the current session.
* @param response the current response to pass information to.
* @param model the Model interface to pass error information that's
* displayed on the error page.
* @return the close-pop-up template if authorization is successful, or the
* onError method to handle and display the error message.
*/
@GetMapping(value = {"/callback"})
public String callback(HttpServletRequest request, HttpSession session,
HttpServletResponse response, Model model) {
try {
String authCode = request.getParameter("code");
String requestState = request.getParameter("state");
String sessionState = session.getAttribute("state").toString();
if (!requestState.equals(sessionState)) {
response.setStatus(401);
return onError("Invalid state parameter.", model);
}
Credential credentials = authService.getAndSaveCredentials(authCode);
session.setAttribute("credentials", credentials);
return "close-pop-up";
} catch (Exception e) {
return onError(e.getMessage(), model);
}
}
Testare una chiamata API
Una volta completato il flusso, puoi effettuare chiamate alle API di Google.
Ad esempio, puoi richiedere le informazioni del profilo dell'utente. Puoi richiedere le informazioni dell'utente dall'API OAuth 2.0.
Python
Leggi la documentazione dell'API di rilevamento OAuth 2.0 e utilizzala per ottenere un oggetto UserInfo compilato.
# Retrieve the credentials from the session data and construct a
# Credentials instance.
credentials = google.oauth2.credentials.Credentials(
**flask.session["credentials"])
# Construct the OAuth 2.0 v2 discovery API library.
user_info_service = googleapiclient.discovery.build(
serviceName="oauth2", version="v2", credentials=credentials)
# Request and store the username in the session.
# This allows it to be used in other methods or in an HTML template.
flask.session["username"] = (
user_info_service.userinfo().get().execute().get("name"))
Java
Crea un metodo nella classe di servizio che crea un oggetto UserInfo
utilizzando
Credentials
come parametro.
/** Obtains the Userinfo object by passing in the required credentials.
* @param credentials retrieved from the authorization flow.
* @return the Userinfo object for the currently signed-in user.
* @throws IOException if creating UserInfo service or obtaining the
* Userinfo object is unsuccessful.
*/
public Userinfo getUserInfo(Credential credentials) throws IOException {
try {
Oauth2 userInfoService = new Oauth2.Builder(
new NetHttpTransport(),
new GsonFactory(),
credentials).build();
Userinfo userinfo = userInfoService.userinfo().get().execute();
return userinfo;
} catch (Exception e) {
throw e;
}
}
Aggiungi l'endpoint /test
al controller che mostra l'email dell'utente.
/** Returns the test request page with the user's email.
* @param session the current session.
* @param model the Model interface to pass error information that's
* displayed on the error page.
* @return the test page that displays the current user's email or the
* onError method to handle and display the error message.
*/
@GetMapping(value = {"/test"})
public String test(HttpSession session, Model model) {
try {
Credential credentials = (Credential) session.getAttribute("credentials");
Userinfo userInfo = authService.getUserInfo(credentials);
String userInfoEmail = userInfo.getEmail();
if (userInfoEmail != null) {
model.addAttribute("userEmail", userInfoEmail);
} else {
return onError("Could not get user email.", model);
}
return "test";
} catch (Exception e) {
return onError(e.getMessage(), model);
}
}
Cancella credenziali
Puoi "cancellare" le credenziali di un utente rimuovendole dalla sessione corrente. In questo modo puoi testare il routing nella pagina di destinazione del componente aggiuntivo.
Ti consigliamo di mostrare un'indicazione che l'utente ha eseguito la disconnessione prima di indirizzarlo alla pagina di destinazione del componente aggiuntivo. L'app deve seguire il flusso di autorizzazione per ottenere nuove credenziali, ma agli utenti non viene chiesto di autorizzare nuovamente l'app.
Python
@app.route("/clear")
def clear_credentials():
if "credentials" in flask.session:
del flask.session["credentials"]
del flask.session["username"]
return flask.render_template("signed-out.html")
In alternativa, utilizza flask.session.clear()
, ma questo potrebbe avere effetti indesiderati se nella sessione sono memorizzati altri valori.
Java
Nel controller, aggiungi un endpoint /clear
.
/** Clears the credentials in the session and returns the sign-out
* confirmation page.
* @param session the current session.
* @return the sign-out confirmation page.
*/
@GetMapping(value = {"/clear"})
public String clear(HttpSession session) {
try {
if (session != null && session.getAttribute("credentials") != null) {
session.removeAttribute("credentials");
}
return "sign-out";
} catch (Exception e) {
return onError(e.getMessage(), model);
}
}
Revocare l'autorizzazione dell'app
Un utente può revocare l'autorizzazione della tua app inviando una richiesta POST
a
https://oauth2.googleapis.com/revoke
. La richiesta deve contenere il
token di accesso dell'utente.
Python
import requests
@app.route("/revoke")
def revoke():
if "credentials" not in flask.session:
return flask.render_template("addon-discovery.html",
message="You need to authorize before " +
"attempting 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"})
if "credentials" in flask.session:
del flask.session["credentials"]
del flask.session["username"]
status_code = getattr(revoke, "status_code")
if status_code == 200:
return flask.render_template("authorization.html")
else:
return flask.render_template(
"index.html", message="An error occurred during revocation!")
Java
Aggiungi un metodo alla classe di servizio che effettui una chiamata all'endpoint di revoca.
/** Revokes the app's permissions to the user's account.
* @param credentials retrieved from the authorization flow.
* @return response entity returned from the HTTP call to obtain response
* information.
* @throws RestClientException if the POST request to the revoke endpoint is
* unsuccessful.
*/
public ResponseEntity<String> revokeCredentials(Credential credentials) throws RestClientException {
try {
String accessToken = credentials.getAccessToken();
String url = "https://oauth2.googleapis.com/revoke?token=" + accessToken;
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE);
HttpEntity<Object> httpEntity = new HttpEntity<Object>(httpHeaders);
ResponseEntity<String> responseEntity = new RestTemplate().exchange(
url,
HttpMethod.POST,
httpEntity,
String.class);
return responseEntity;
} catch (RestClientException e) {
throw e;
}
}
Aggiungi un endpoint, /revoke
, al controller che cancella la sessione e reindirizza l'utente alla pagina di autorizzazione se la revoca è andata a buon fine.
/** Revokes the app's permissions and returns the authorization page.
* @param session the current session.
* @return the authorization page.
* @throws Exception if revoking access is unsuccessful.
*/
@GetMapping(value = {"/revoke"})
public String revoke(HttpSession session) throws Exception {
try {
if (session != null && session.getAttribute("credentials") != null) {
Credential credentials = (Credential) session.getAttribute("credentials");
ResponseEntity responseEntity = authService.revokeCredentials(credentials);
Integer httpStatusCode = responseEntity.getStatusCodeValue();
if (httpStatusCode != 200) {
return onError("There was an issue revoking access: " +
responseEntity.getStatusCode(), model);
}
session.removeAttribute("credentials");
}
return startAuthFlow(model);
} catch (Exception e) {
return onError(e.getMessage(), model);
}
}
Testare il componente aggiuntivo
Accedi a Google Classroom come uno dei tuoi utenti di test insegnante. Vai alla scheda Lavori del corso e crea un nuovo Compito. Fai clic sul pulsante Componenti aggiuntivi sotto l'area di testo, quindi seleziona il componente aggiuntivo. L'iframe si apre e il componente aggiuntivo carica l'URI di configurazione dell'allegato specificato nella pagina Configurazione dell'app dell'SDK GWM.
Complimenti! Ora puoi passare al passaggio successivo: gestire le visite ripetute al tuo componente aggiuntivo.