Per utilizzare i servizi Google per conto di un utente quando quest'ultimo è offline, devi utilizzare un flusso lato server ibrido in cui un utente autorizza la tua app lato client utilizzando il client API JavaScript e invii un codice di autorizzazione una tantum speciale al tuo server. Il server scambia questo codice monouso per acquisire i propri token di accesso e aggiornamento da Google in modo da poter effettuare le proprie chiamate API, che possono essere eseguite quando l'utente è offline. Questo flusso di codice una tantum offre vantaggi in termini di sicurezza rispetto a un flusso puramente lato server e all'invio di token di accesso al server.
Il flusso di accesso per ottenere un token di accesso per la tua applicazione lato server è illustrato di seguito.
I codici monouso offrono diversi vantaggi in termini di sicurezza. Con i codici, Google fornisce i token direttamente al tuo server senza intermediari. Sebbene non sia consigliabile divulgare i codici, sono molto difficili da utilizzare senza il tuo segreto cliente. Mantieni segreto il client secret.
Implementazione del flusso con codice monouso
Il pulsante Accedi con Google fornisce sia un token di accesso sia un codice di autorizzazione. Si tratta di un codice una tantum che il tuo server può scambiare con i server di Google per ottenere un token di accesso.
Il seguente esempio di codice mostra come eseguire il flusso di codice una tantum.
Per autenticare l'accesso con Google con il flusso di codice monouso devi:
Passaggio 1: crea un ID client e un secret client
Per creare un ID client e un client secret, crea un progetto nella console API di Google, configura un ID client OAuth e registra le origini JavaScript:
Vai alla console dell'API di Google.
Dal menu a discesa del progetto, seleziona un progetto esistente o creane uno nuovo selezionando Crea un nuovo progetto.
Nella barra laterale, in "API e servizi", seleziona Credenziali e poi fai clic su Configura schermata del consenso.
Scegli un indirizzo email, specifica un nome prodotto e premi Salva.
Nella scheda Credenziali, seleziona l'elenco a discesa Crea credenziali e scegli ID client OAuth.
In Tipo di applicazione, seleziona Applicazione web.
Registra le origini da cui la tua app è autorizzata ad accedere alle API di Google, come segue. Un'origine è una combinazione univoca di protocollo, nome host e porta.
Nel campo Origini JavaScript autorizzate, inserisci l'origine per la tua app. Puoi inserire più origini per consentire l'esecuzione dell'app su protocolli, domini o sottodomini diversi. Non puoi utilizzare caratteri jolly. Nell'esempio seguente, il secondo URL potrebbe essere un URL di produzione.
http://localhost:8080 https://myproductionurl.example.com
Il campo URI di reindirizzamento autorizzato non richiede un valore. Gli URI di reindirizzamento non vengono utilizzati con le API JavaScript.
Premi il pulsante Crea.
Dalla finestra di dialogo Client OAuth risultante, copia l'ID client. L'ID client consente alla tua app di accedere alle API Google abilitate.
Passaggio 2: includi la libreria della piattaforma Google nella tua pagina
Includi i seguenti script che mostrano una funzione anonima che inserisce uno script nel DOM di questa pagina web index.html
.
<!-- The top of file index.html -->
<html itemscope itemtype="http://schema.org/Article">
<head>
<!-- BEGIN Pre-requisites -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js">
</script>
<script src="https://apis.google.com/js/client:platform.js?onload=start" async defer>
</script>
<!-- END Pre-requisites -->
Passaggio 3: inizializza l'oggetto GoogleAuth
Carica la libreria auth2 e chiama gapi.auth2.init()
per inizializzare l'oggetto GoogleAuth
. Specifica il tuo ID client e gli ambiti che vuoi richiedere
quando chiami init()
.
<!-- Continuing the <head> section -->
<script>
function start() {
gapi.load('auth2', function() {
auth2 = gapi.auth2.init({
client_id: 'YOUR_CLIENT_ID.apps.googleusercontent.com',
// Scopes to request in addition to 'profile' and 'email'
//scope: 'additional_scope'
});
});
}
</script>
</head>
<body>
<!-- ... -->
</body>
</html>
Passaggio 4: aggiungi il pulsante di accesso alla pagina
Aggiungi il pulsante di accesso alla tua pagina web e allega un gestore dei clic per chiamare
grantOfflineAccess()
per avviare il flusso del codice una tantum.
<!-- Add where you want your sign-in button to render -->
<!-- Use an image that follows the branding guidelines in a real app -->
<button id="signinButton">Sign in with Google</button>
<script>
$('#signinButton').click(function() {
// signInCallback defined in step 6.
auth2.grantOfflineAccess().then(signInCallback);
});
</script>
Passaggio 5: consenti l'accesso all'utente
L'utente fa clic sul pulsante di accesso e concede alla tua app l'accesso alle autorizzazioni
che hai richiesto. Quindi, alla funzione di callback specificata nel metodo grantOfflineAccess().then()
viene passato un oggetto JSON con un codice di autorizzazione. Ad esempio:
{"code":"4/yU4cQZTMnnMtetyFcIWNItG32eKxxxgXXX-Z4yyJJJo.4qHskT-UtugceFc0ZRONyF4z7U4UmAI"}
Passaggio 6: invia il codice di autorizzazione al server
code
è il tuo codice una tantum che il server può scambiare con il proprio
token di accesso e token di aggiornamento. Puoi ottenere un token di aggiornamento solo dopo che all'utente è stata presentata una finestra di dialogo di autorizzazione che richiede l'accesso offline.
Se hai specificato select-account
prompt
in OfflineAccessOptions
nel passaggio 4, devi memorizzare il token di aggiornamento recuperato per utilizzarlo in un secondo momento, poiché gli scambi successivi restituiranno null
per il token di aggiornamento. Questo flusso offre una maggiore sicurezza rispetto al flusso OAuth 2.0 standard.
I token di accesso vengono sempre restituiti con lo scambio di un codice di autorizzazione valido.
Lo script seguente definisce una funzione di callback per il pulsante di accesso. Quando un accesso è andato a buon fine, la funzione memorizza il token di accesso per l'utilizzo lato client e invia il codice una tantum al tuo server nello stesso dominio.
<!-- Last part of BODY element in file index.html -->
<script>
function signInCallback(authResult) {
if (authResult['code']) {
// Hide the sign-in button now that the user is authorized, for example:
$('#signinButton').attr('style', 'display: none');
// Send the code to the server
$.ajax({
type: 'POST',
url: 'http://example.com/storeauthcode',
// Always include an `X-Requested-With` header in every AJAX request,
// to protect against CSRF attacks.
headers: {
'X-Requested-With': 'XMLHttpRequest'
},
contentType: 'application/octet-stream; charset=utf-8',
success: function(result) {
// Handle or verify the server response.
},
processData: false,
data: authResult['code']
});
} else {
// There was an error.
}
}
</script>
Passaggio 7: scambia il codice di autorizzazione con un token di accesso
Sul server, scambia il codice di autenticazione con i token di accesso e di aggiornamento. Utilizza il token di accesso per chiamare le API Google per conto dell'utente e, facoltativamente, memorizza il token di aggiornamento per acquisire un nuovo token di accesso quando quello esistente scade.
Se hai richiesto l'accesso al profilo, ricevi anche un token ID contenente informazioni di base sul profilo dell'utente.
Ad esempio:
Java
// (Receive authCode via HTTPS POST) if (request.getHeader("X-Requested-With") == null) { // Without the `X-Requested-With` header, this request could be forged. Aborts. } // Set path to the Web application client_secret_*.json file you downloaded from the // Google API Console: https://console.cloud.google.com/apis/credentials // You can also find your Web application client ID and client secret from the // console and specify them directly when you create the GoogleAuthorizationCodeTokenRequest // object. String CLIENT_SECRET_FILE = "/path/to/client_secret.json"; // Exchange auth code for access token GoogleClientSecrets clientSecrets = GoogleClientSecrets.load( JacksonFactory.getDefaultInstance(), new FileReader(CLIENT_SECRET_FILE)); GoogleTokenResponse tokenResponse = new GoogleAuthorizationCodeTokenRequest( new NetHttpTransport(), JacksonFactory.getDefaultInstance(), "https://oauth2.googleapis.com/token", clientSecrets.getDetails().getClientId(), clientSecrets.getDetails().getClientSecret(), authCode, REDIRECT_URI) // Specify the same redirect URI that you use with your web // app. If you don't have a web version of your app, you can // specify an empty string. .execute(); String accessToken = tokenResponse.getAccessToken(); // Use access token to call API GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken); Drive drive = new Drive.Builder(new NetHttpTransport(), JacksonFactory.getDefaultInstance(), credential) .setApplicationName("Auth Code Exchange Demo") .build(); File file = drive.files().get("appfolder").execute(); // Get profile info from ID token GoogleIdToken idToken = tokenResponse.parseIdToken(); GoogleIdToken.Payload payload = idToken.getPayload(); String userId = payload.getSubject(); // Use this value as a key to identify a user. String email = payload.getEmail(); boolean emailVerified = Boolean.valueOf(payload.getEmailVerified()); String name = (String) payload.get("name"); String pictureUrl = (String) payload.get("picture"); String locale = (String) payload.get("locale"); String familyName = (String) payload.get("family_name"); String givenName = (String) payload.get("given_name");
Python
from apiclient import discovery import httplib2 from oauth2client import client # (Receive auth_code by HTTPS POST) # If this request does not have `X-Requested-With` header, this could be a CSRF if not request.headers.get('X-Requested-With'): abort(403) # Set path to the Web application client_secret_*.json file you downloaded from the # Google API Console: https://console.cloud.google.com/apis/credentials CLIENT_SECRET_FILE = '/path/to/client_secret.json' # Exchange auth code for access token, refresh token, and ID token credentials = client.credentials_from_clientsecrets_and_code( CLIENT_SECRET_FILE, ['https://www.googleapis.com/auth/drive.appdata', 'profile', 'email'], auth_code) # Call Google API http_auth = credentials.authorize(httplib2.Http()) drive_service = discovery.build('drive', 'v3', http=http_auth) appfolder = drive_service.files().get(fileId='appfolder').execute() # Get profile info from ID token userid = credentials.id_token['sub'] email = credentials.id_token['email']