Để thay mặt người dùng sử dụng các dịch vụ của Google khi người dùng không có mạng, bạn phải sử dụng quy trình phía máy chủ kết hợp, trong đó người dùng uỷ quyền cho ứng dụng của bạn ở phía máy khách bằng cách sử dụng ứng dụng API JavaScript và bạn gửi một mã uỷ quyền một lần đặc biệt đến máy chủ của mình. Máy chủ của bạn sẽ trao đổi mã sử dụng một lần này để lấy mã truy cập và mã làm mới của riêng mình từ Google để máy chủ có thể thực hiện các lệnh gọi API của riêng mình, điều này có thể được thực hiện khi người dùng không có mạng. Quy trình mã một lần này có các lợi thế bảo mật so với cả quy trình thuần tuý phía máy chủ và việc gửi mã thông báo truy cập đến máy chủ của bạn.
Quy trình đăng nhập để lấy mã truy cập cho ứng dụng phía máy chủ được minh hoạ bên dưới.
Mã một lần có một số ưu điểm về bảo mật. Với mã, Google sẽ cung cấp mã thông báo trực tiếp cho máy chủ của bạn mà không cần bất kỳ bên trung gian nào. Mặc dù bạn không nên rò rỉ mã, nhưng rất khó sử dụng mã nếu không có khoá bí mật của ứng dụng. Hãy giữ bí mật mật khẩu ứng dụng khách!
Triển khai quy trình mã một lần
Nút Đăng nhập bằng Google cung cấp cả mã thông báo truy cập và mã uỷ quyền. Mã này là mã một lần mà máy chủ của bạn có thể trao đổi với máy chủ của Google để lấy mã truy cập.
Mã mẫu sau đây minh hoạ cách thực hiện quy trình mã một lần.
Để xác thực quy trình Đăng nhập bằng Google bằng mã một lần, bạn cần:
Bước 1: Tạo mã ứng dụng và mã xác thực ứng dụng
Để tạo mã ứng dụng khách và khoá ứng dụng khách, hãy tạo một dự án trên Bảng điều khiển API của Google, thiết lập mã ứng dụng khách OAuth và đăng ký nguồn gốc JavaScript:
Chuyển đến Google API Console.
Trong trình đơn thả xuống về dự án, hãy chọn một dự án hiện có hoặc tạo một dự án mới bằng cách chọn Tạo dự án mới.
Trong thanh bên trong mục "API và dịch vụ", hãy chọn Thông tin xác thực, sau đó nhấp vào Định cấu hình màn hình yêu cầu đồng ý.
Chọn Địa chỉ email, chỉ định Tên sản phẩm rồi nhấn vào Lưu.
Trong thẻ Thông tin xác thực, hãy chọn danh sách thả xuống Tạo thông tin xác thực rồi chọn Mã ứng dụng khách OAuth.
Trong mục Loại ứng dụng, hãy chọn Ứng dụng web.
Đăng ký nguồn gốc mà ứng dụng của bạn được phép truy cập vào API của Google, như sau. Nguồn gốc là một tổ hợp duy nhất gồm giao thức, tên máy chủ và cổng.
Trong trường Nguồn gốc JavaScript được uỷ quyền, hãy nhập nguồn gốc cho ứng dụng của bạn. Bạn có thể nhập nhiều nguồn gốc để cho phép ứng dụng chạy trên nhiều giao thức, miền hoặc miền con. Bạn không thể sử dụng ký tự đại diện. Trong ví dụ bên dưới, URL thứ hai có thể là URL chính thức.
http://localhost:8080 https://myproductionurl.example.com
Trường URI chuyển hướng được uỷ quyền không yêu cầu giá trị. URI chuyển hướng không được dùng với API JavaScript.
Nhấn vào nút Tạo.
Trong hộp thoại OAuth client (Ứng dụng khách OAuth) xuất hiện, hãy sao chép mã ứng dụng khách. Mã ứng dụng cho phép ứng dụng của bạn truy cập vào các API đã bật của Google.
Bước 2: Thêm thư viện nền tảng Google vào trang
Thêm các tập lệnh sau đây để minh hoạ một hàm ẩn danh chèn một tập lệnh vào DOM của trang web index.html
này.
<!-- 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 -->
Bước 3: Khởi chạy đối tượng GoogleAuth
Tải thư viện auth2 và gọi gapi.auth2.init()
để khởi chạy đối tượng GoogleAuth
. Chỉ định mã ứng dụng và các phạm vi mà bạn muốn yêu cầu khi gọi 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>
Bước 4: Thêm nút đăng nhập vào trang
Thêm nút đăng nhập vào trang web và đính kèm trình xử lý lượt nhấp để gọi grantOfflineAccess()
nhằm bắt đầu quy trình mã một lần.
<!-- 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>
Bước 5: Đăng nhập người dùng
Người dùng nhấp vào nút đăng nhập và cấp cho ứng dụng của bạn quyền truy cập vào các quyền mà bạn đã yêu cầu. Sau đó, hàm gọi lại mà bạn chỉ định trong phương thức grantOfflineAccess().then()
sẽ được truyền một đối tượng JSON có mã uỷ quyền. Ví dụ:
{"code":"4/yU4cQZTMnnMtetyFcIWNItG32eKxxxgXXX-Z4yyJJJo.4qHskT-UtugceFc0ZRONyF4z7U4UmAI"}
Bước 6: Gửi mã uỷ quyền đến máy chủ
code
là mã một lần mà máy chủ của bạn có thể trao đổi lấy mã truy cập và mã làm mới của riêng máy chủ. Bạn chỉ có thể lấy mã thông báo làm mới sau khi người dùng thấy hộp thoại uỷ quyền yêu cầu quyền truy cập ngoại tuyến.
Nếu đã chỉ định select-account
prompt
trong OfflineAccessOptions
ở bước 4, bạn phải lưu trữ mã thông báo làm mới mà bạn truy xuất để sử dụng sau này vì các lượt trao đổi tiếp theo sẽ trả về null
cho mã thông báo làm mới. Quy trình này tăng cường bảo mật so với quy trình OAuth 2.0 tiêu chuẩn.
Mã thông báo truy cập luôn được trả về cùng với mã uỷ quyền hợp lệ.
Tập lệnh sau đây xác định một hàm gọi lại cho nút đăng nhập. Khi đăng nhập thành công, hàm này sẽ lưu trữ mã thông báo truy cập để sử dụng phía máy khách và gửi mã một lần đến máy chủ của bạn trên cùng một miền.
<!-- 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>
Bước 7: Trao đổi mã uỷ quyền để lấy mã truy cập
Trên máy chủ, hãy trao đổi mã xác thực để lấy mã truy cập và mã làm mới. Sử dụng mã truy cập để thay mặt người dùng gọi các API của Google, đồng thời tuỳ ý lưu trữ mã làm mới để lấy mã truy cập mới khi mã truy cập hết hạn.
Nếu đã yêu cầu quyền truy cập vào hồ sơ, bạn cũng sẽ nhận được một mã nhận dạng chứa thông tin cơ bản về hồ sơ của người dùng.
Ví dụ:
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']