Чтобы использовать службы Google от имени пользователя, когда пользователь находится в автономном режиме, необходимо использовать гибридный поток на стороне сервера, при котором пользователь авторизует ваше приложение на стороне клиента с помощью клиента API JavaScript, а вы отправляете специальный одноразовый код авторизации на ваш сервер. Ваш сервер обменивается этим одноразовым кодом для получения собственных токенов доступа и обновления от Google, чтобы сервер мог выполнять собственные вызовы API, что можно делать, пока пользователь находится в автономном режиме. Этот одноразовый поток кода имеет преимущества в безопасности как по сравнению с чистым потоком на стороне сервера, так и перед отправкой токенов доступа на ваш сервер.
Ниже показан процесс входа в систему для получения токена доступа для вашего серверного приложения.
Одноразовые коды имеют ряд преимуществ в плане безопасности. С помощью кодов Google предоставляет токены непосредственно на ваш сервер без каких-либо посредников. Хотя мы не рекомендуем разглашать коды, их очень сложно использовать без секрета вашего клиента. Держите тайну своего клиента в секрете!
Реализация потока одноразового кода
Кнопка входа в Google предоставляет как токен доступа , так и код авторизации . Код представляет собой одноразовый код, который ваш сервер может обменивать с серверами Google на токен доступа.
В следующем примере кода показано, как выполнить поток одноразового кода.
Для аутентификации входа в Google с помощью одноразового кода вам необходимо:
Шаг 1. Создайте идентификатор клиента и секрет клиента.
Чтобы создать идентификатор клиента и секрет клиента, создайте проект консоли Google API, настройте идентификатор клиента OAuth и зарегистрируйте свое происхождение JavaScript:
Перейдите в консоль Google API .
В раскрывающемся списке проектов выберите существующий проект или создайте новый, выбрав Создать новый проект .
На боковой панели в разделе «API и службы» выберите «Учетные данные» и нажмите « Настроить экран согласия» .
Выберите адрес электронной почты, укажите название продукта и нажмите «Сохранить» .
На вкладке «Учетные данные» выберите раскрывающийся список «Создать учетные данные» и выберите «Идентификатор клиента OAuth» .
В разделе «Тип приложения» выберите «Веб-приложение» .
Зарегистрируйте источники, из которых вашему приложению разрешен доступ к API Google, следующим образом. Источник — это уникальная комбинация протокола, имени хоста и порта.
В поле «Авторизованные источники JavaScript» введите источник вашего приложения. Вы можете указать несколько источников, чтобы ваше приложение могло работать на разных протоколах, доменах или поддоменах. Вы не можете использовать подстановочные знаки. В приведенном ниже примере второй URL-адрес может быть рабочим URL-адресом.
http://localhost:8080 https://myproductionurl.example.com
Поле URI авторизованного перенаправления не требует указания значения. URI перенаправления не используются с API JavaScript.
Нажмите кнопку Создать .
В появившемся диалоговом окне клиента OAuth скопируйте идентификатор клиента. Идентификатор клиента позволяет вашему приложению получать доступ к активированным API Google.
Шаг 2. Добавьте библиотеку платформы Google на свою страницу.
Включите следующие сценарии, демонстрирующие анонимную функцию, которая вставляет сценарий в DOM этой веб-страницы 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 -->
Шаг 3. Инициализируйте объект GoogleAuth.
Загрузите библиотеку auth2 и вызовитеgapi.auth2.init gapi.auth2.init()
чтобы инициализировать объект GoogleAuth
. Укажите свой идентификатор клиента и области, которые вы хотите запросить при вызове 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>
Шаг 4. Добавьте кнопку входа на свою страницу.
Добавьте кнопку входа на свою веб-страницу и прикрепите обработчик кликов для вызова grantOfflineAccess()
чтобы запустить поток одноразового кода.
<!-- 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>
Шаг 5. Войдите в систему.
Пользователь нажимает кнопку входа и предоставляет вашему приложению доступ к запрошенным вами разрешениям. Затем функции обратного вызова, указанной вами в grantOfflineAccess().then()
передается объект JSON с кодом авторизации. Например:
{"code":"4/yU4cQZTMnnMtetyFcIWNItG32eKxxxgXXX-Z4yyJJJo.4qHskT-UtugceFc0ZRONyF4z7U4UmAI"}
Шаг 6. Отправьте код авторизации на сервер.
code
— это ваш одноразовый код, который ваш сервер может обменять на свой собственный токен доступа и токен обновления. Вы можете получить токен обновления только после того, как пользователю будет представлен диалог авторизации с запросом автономного доступа. Если вы указали prompt
select-account
в OfflineAccessOptions
на шаге 4, вам необходимо сохранить полученный токен обновления для дальнейшего использования, поскольку последующие обмены будут возвращать null
для токена обновления. Этот поток обеспечивает повышенную безопасность по сравнению со стандартным потоком OAuth 2.0.
Токены доступа всегда возвращаются при обмене действительным кодом авторизации.
Следующий скрипт определяет функцию обратного вызова для кнопки входа. При успешном входе функция сохраняет токен доступа для использования на стороне клиента и отправляет одноразовый код на ваш сервер в том же домене.
<!-- 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>
Шаг 7. Обменяйте код авторизации на токен доступа.
На сервере замените код аутентификации на токены доступа и обновления. Используйте токен доступа для вызова API Google от имени пользователя и, при необходимости, сохраните токен обновления, чтобы получить новый токен доступа по истечении срока действия токена доступа.
Если вы запросили доступ к профилю, вы также получите токен идентификатора, содержащий основную информацию о профиле пользователя.
Например:
Ява
// (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");
Питон
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']