Nếu tiện ích bổ sung Google Workspace của bạn kết nối với một dịch vụ hoặc API bên thứ ba yêu cầu quyền truy cập, thì tiện ích bổ sung có thể nhắc người dùng đăng nhập và cho phép truy cập.
Trang này giải thích cách xác thực người dùng bằng luồng uỷ quyền (chẳng hạn như OAuth), bao gồm các bước sau:
- Phát hiện thời điểm cần có sự uỷ quyền.
- Trả về giao diện thẻ nhắc người dùng đăng nhập vào dịch vụ.
- Làm mới tiện ích bổ sung để người dùng có thể truy cập vào dịch vụ hoặc tài nguyên được bảo vệ.
Nếu tiện ích bổ sung của bạn chỉ yêu cầu danh tính người dùng, thì bạn có thể xác thực trực tiếp người dùng bằng cách sử dụng mã nhận dạng Google Workspace hoặc địa chỉ email của họ. Để sử dụng địa chỉ email để xác thực, hãy xem phần xác thực các yêu cầu JSON. Nếu đã tạo tiện ích bổ sung bằng Google Apps Script, bạn có thể đơn giản hoá quy trình này bằng cách sử dụng OAuth2 cho thư viện Google Apps Script (cũng có phiên bản OAuth1).
Phát hiện cần phải có quyền uỷ quyền
Khi sử dụng tiện ích bổ sung, người dùng có thể không được uỷ quyền truy cập vào tài nguyên được bảo vệ vì nhiều lý do, chẳng hạn như:
- Mã thông báo truy cập để kết nối với dịch vụ bên thứ ba chưa được tạo hoặc đã hết hạn.
- Mã truy cập không bao gồm tài nguyên đã yêu cầu.
- Mã truy cập không bao gồm các phạm vi bắt buộc của yêu cầu.
Tiện ích bổ sung của bạn sẽ phát hiện những trường hợp này để người dùng có thể đăng nhập và truy cập vào dịch vụ của bạn.
Nếu bạn đang xây dựng trong Apps Script, hàm hasAccess()
của thư viện OAuth có thể cho bạn biết liệu bạn có quyền truy cập vào một dịch vụ hay không.
Ngoài ra, khi sử dụng các yêu cầu UrlFetchApp fetch()
, bạn có thể đặt thông số muteHttpExceptions
thành true
. Điều này giúp yêu cầu không gửi ngoại lệ khi yêu cầu không thành công và cho phép bạn kiểm tra mã phản hồi yêu cầu và nội dung trong đối tượng HttpResponse
được trả về.
Nhắc người dùng đăng nhập vào dịch vụ của bạn
Khi phát hiện cần phải uỷ quyền, tiện ích bổ sung phải trả về giao diện thẻ để nhắc người dùng đăng nhập vào dịch vụ. Thẻ đăng nhập phải chuyển hướng người dùng để hoàn tất quy trình xác thực và uỷ quyền của bên thứ ba trên cơ sở hạ tầng của bạn.
Khi tạo tiện ích bổ sung bằng các điểm cuối HTTP, bạn nên bảo vệ ứng dụng đích bằng tính năng Đăng nhập bằng Google và lấy mã nhận dạng người dùng bằng cách sử dụng mã thông báo nhận dạng được phát hành trong quá trình đăng nhập. Tuyên bố phụ chứa mã nhận dạng duy nhất của người dùng và có thể được liên kết với mã nhận dạng từ tiện ích bổ sung của bạn.
Tạo và trả về thẻ đăng nhập
Đối với thẻ đăng nhập của dịch vụ, bạn có thể sử dụng thẻ uỷ quyền cơ bản của Google hoặc tuỳ chỉnh thẻ để hiển thị thêm thông tin, chẳng hạn như biểu trưng của tổ chức. Nếu phát hành tiện ích bổ sung công khai, bạn phải sử dụng thẻ tuỳ chỉnh.
Thẻ uỷ quyền cơ bản
Hình ảnh sau đây là ví dụ về thẻ uỷ quyền cơ bản của Google:
Mã sau đây cho thấy ví dụ về cách sử dụng thẻ uỷ quyền cơ bản của Google:
Apps Script
CardService.newAuthorizationException() .setAuthorizationUrl('AUTHORIZATION_URL') .setResourceDisplayName('RESOURCE_DISPLAY_NAME') .throwException();
JSON
Trả về phản hồi JSON sau:
{
"basic_authorization_prompt": {
"authorization_url": "AUTHORIZATION_URL",
"resource": "RESOURCE_DISPLAY_NAME"
}
}
Thay thế nội dung sau:
AUTHORIZATION_URL
: URL của ứng dụng web xử lý việc uỷ quyền.RESOURCE_DISPLAY_NAME
: Tên hiển thị cho tài nguyên hoặc dịch vụ được bảo vệ. Tên này sẽ hiển thị cho người dùng trên lời nhắc uỷ quyền. Ví dụ: nếuRESOURCE_DISPLAY_NAME
của bạn làExample Account
, thì lời nhắc sẽ là "Tiện ích bổ sung này muốn hiển thị thêm thông tin, nhưng cần được phê duyệt để truy cập vào Tài khoản mẫu của bạn".
Sau khi hoàn tất quá trình uỷ quyền, người dùng sẽ được nhắc làm mới tiện ích bổ sung để truy cập vào tài nguyên được bảo vệ.
Thẻ uỷ quyền tuỳ chỉnh
Để sửa đổi lời nhắc uỷ quyền, bạn có thể tạo một thẻ tuỳ chỉnh cho trải nghiệm đăng nhập của dịch vụ.
Nếu phát hành tiện ích bổ sung công khai, bạn phải sử dụng thẻ uỷ quyền tuỳ chỉnh. Để tìm hiểu thêm về các yêu cầu phát hành cho Google Workspace Marketplace, hãy xem bài viết Giới thiệu về quy trình đánh giá ứng dụng.
Thẻ được trả về phải thực hiện những việc sau:
- Cho người dùng biết rõ rằng tiện ích bổ sung đang yêu cầu quyền thay mặt họ truy cập vào một dịch vụ không phải của Google.
- Nêu rõ những việc mà tiện ích bổ sung có thể làm nếu được uỷ quyền.
- Chứa một nút hoặc tiện ích tương tự đưa người dùng đến URL uỷ quyền của dịch vụ. Đảm bảo người dùng thấy rõ chức năng của tiện ích này.
- Tiện ích trên phải sử dụng chế độ cài đặt
OnClose.RELOAD
trong đối tượngOpenLink
để đảm bảo rằng tiện ích bổ sung sẽ tải lại sau khi nhận được quyền. - Tất cả đường liên kết được mở từ lời nhắc uỷ quyền phải sử dụng HTTPS.
Hình ảnh sau đây cho thấy một ví dụ về thẻ uỷ quyền tuỳ chỉnh cho trang chủ của một tiện ích bổ sung. Thẻ này bao gồm biểu trưng, nội dung mô tả và nút đăng nhập:
Mã sau đây cho thấy cách sử dụng ví dụ về thẻ tuỳ chỉnh này:
Apps Script
function customAuthorizationCard() {
let cardSection1Image1 = CardService.newImage()
.setImageUrl('LOGO_URL')
.setAltText('LOGO_ALT_TEXT');
let cardSection1Divider1 = CardService.newDivider();
let cardSection1TextParagraph1 = CardService.newTextParagraph()
.setText('DESCRIPTION');
let cardSection1ButtonList1Button1 = CardService.newTextButton()
.setText('Sign in')
.setBackgroundColor('#0055ff')
.setTextButtonStyle(CardService.TextButtonStyle.FILLED)
.setAuthorizationAction(CardService.newAuthorizationAction()
.setAuthorizationUrl('AUTHORIZATION_URL'));
let cardSection1ButtonList1 = CardService.newButtonSet()
.addButton(cardSection1ButtonList1Button1);
let cardSection1TextParagraph2 = CardService.newTextParagraph()
.setText('TEXT_SIGN_UP');
let cardSection1 = CardService.newCardSection()
.addWidget(cardSection1Image1)
.addWidget(cardSection1Divider1)
.addWidget(cardSection1TextParagraph1)
.addWidget(cardSection1ButtonList1)
.addWidget(cardSection1TextParagraph2);
let card = CardService.newCardBuilder()
.addSection(cardSection1)
.build();
return [card];
}
function startNonGoogleAuth() {
CardService.newAuthorizationException()
.setAuthorizationUrl('AUTHORIZATION_URL')
.setResourceDisplayName('RESOURCE_DISPLAY_NAME')
.setCustomUiCallback('customAuthorizationCard')
.throwException();
}
JSON
Trả về phản hồi JSON sau:
{
"custom_authorization_prompt": {
"action": {
"navigations": [
{
"pushCard": {
"sections": [
{
"widgets": [
{
"image": {
"imageUrl": "LOGO_URL",
"altText": "LOGO_ALT_TEXT"
}
},
{
"divider": {}
},
{
"textParagraph": {
"text": "DESCRIPTION"
}
},
{
"buttonList": {
"buttons": [
{
"text": "Sign in",
"onClick": {
"openLink": {
"url": "AUTHORIZATION_URL",
"onClose": "RELOAD",
"openAs": "OVERLAY"
}
},
"color": {
"red": 0,
"green": 0,
"blue": 1,
"alpha": 1,
}
}
]
}
},
{
"textParagraph": {
"text": "TEXT_SIGN_UP"
}
}
]
}
]
}
}
]
}
}
}
Thay thế nội dung sau:
LOGO_URL
: URL của biểu trưng hoặc hình ảnh. Phải là một URL công khai.LOGO_ALT_TEXT
: Văn bản thay thế cho biểu trưng hoặc hình ảnh, chẳng hạn nhưCymbal Labs Logo
.DESCRIPTION
: Lời kêu gọi hành động để người dùng đăng nhập, chẳng hạn nhưSign in to get started
.- Cách cập nhật nút đăng nhập:
AUTHORIZATION_URL
: URL của ứng dụng web xử lý việc uỷ quyền.- Không bắt buộc: Để thay đổi màu nút, hãy cập nhật các giá trị float RGBA của trường
color
. Đối với Apps Script, hãy cập nhật phương thứcsetBackgroundColor()
bằng các giá trị thập lục phân.
TEXT_SIGN_UP
: Văn bản nhắc người dùng tạo tài khoản nếu họ chưa có. Ví dụ:New to Cymbal Labs? <a href=\"https://www.example.com/signup\">Sign up</a> here
.
Quản lý hoạt động đăng nhập qua bên thứ ba trên các ứng dụng của Google Workspace
Một ứng dụng phổ biến cho tiện ích bổ sung của Google Workspace là cung cấp giao diện để tương tác với hệ thống bên thứ ba từ trong ứng dụng lưu trữ của Google Workspace.
Các hệ thống bên thứ ba thường yêu cầu người dùng đăng nhập bằng mã nhận dạng người dùng, mật khẩu hoặc thông tin xác thực khác. Khi người dùng đăng nhập vào dịch vụ bên thứ ba của bạn trong khi đang sử dụng một máy chủ lưu trữ Google Workspace, bạn phải đảm bảo rằng họ không phải đăng nhập lại khi chuyển sang một máy chủ lưu trữ Google Workspace khác.
Nếu đang xây dựng trong Apps Script, bạn có thể ngăn các yêu cầu đăng nhập lặp lại bằng thuộc tính người dùng hoặc mã thông báo nhận dạng. Các loại này được giải thích trong các phần sau.
Thuộc tính người dùng
Bạn có thể lưu trữ dữ liệu đăng nhập của người dùng trong các thuộc tính người dùng của Apps Script. Ví dụ: bạn có thể tạo Mã thông báo web JSON (JWT) của riêng mình từ dịch vụ đăng nhập của họ và ghi lại mã đó trong một thuộc tính người dùng hoặc ghi lại tên người dùng và mật khẩu cho dịch vụ của họ.
Thuộc tính người dùng được đặt trong phạm vi để chỉ người dùng đó mới có thể truy cập vào các thuộc tính đó trong tập lệnh của tiện ích bổ sung. Người dùng khác và các tập lệnh khác không thể truy cập vào các thuộc tính này. Hãy xem PropertiesService
để biết thêm thông tin chi tiết.
Mã thông báo nhận dạng
Bạn có thể sử dụng mã thông báo giá trị nhận dạng của Google làm thông tin đăng nhập cho dịch vụ của mình. Đây là một cách để thực hiện tính năng đăng nhập một lần. Người dùng đã đăng nhập vào Google vì họ đang ở trong một ứng dụng lưu trữ của Google.
Ví dụ về cấu hình OAuth không phải của Google
Mã mẫu Apps Script sau đây cho biết cách định cấu hình một tiện ích bổ sung để sử dụng một API không phải của Google yêu cầu OAuth. Mẫu này sử dụng thư viện OAuth2 cho Apps Script để tạo một dịch vụ truy cập API.
Apps Script
/**
* Attempts to access a non-Google API using a constructed service
* object.
*
* If your add-on needs access to non-Google APIs that require OAuth,
* you need to implement this method. You can use the OAuth1 and
* OAuth2 Apps Script libraries to help implement it.
*
* @param {String} url The URL to access.
* @param {String} method_opt The HTTP method. Defaults to GET.
* @param {Object} headers_opt The HTTP headers. Defaults to an empty
* object. The Authorization field is added
* to the headers in this method.
* @return {HttpResponse} the result from the UrlFetchApp.fetch() call.
*/
function accessProtectedResource(url, method_opt, headers_opt) {
var service = getOAuthService();
var maybeAuthorized = service.hasAccess();
if (maybeAuthorized) {
// A token is present, but it may be expired or invalid. Make a
// request and check the response code to be sure.
// Make the UrlFetch request and return the result.
var accessToken = service.getAccessToken();
var method = method_opt || 'get';
var headers = headers_opt || {};
headers['Authorization'] =
Utilities.formatString('Bearer %s', accessToken);
var resp = UrlFetchApp.fetch(url, {
'headers': headers,
'method' : method,
'muteHttpExceptions': true, // Prevents thrown HTTP exceptions.
});
var code = resp.getResponseCode();
if (code >= 200 && code < 300) {
return resp.getContentText("utf-8"); // Success
} else if (code == 401 || code == 403) {
// Not fully authorized for this action.
maybeAuthorized = false;
} else {
// Handle other response codes by logging them and throwing an
// exception.
console.error("Backend server error (%s): %s", code.toString(),
resp.getContentText("utf-8"));
throw ("Backend server error: " + code);
}
}
if (!maybeAuthorized) {
// Invoke the authorization flow using the default authorization
// prompt card.
CardService.newAuthorizationException()
.setAuthorizationUrl(service.getAuthorizationUrl())
.setResourceDisplayName("Display name to show to the user")
.throwException();
}
}
/**
* Create a new OAuth service to facilitate accessing an API.
* This example assumes there is a single service that the add-on needs to
* access. Its name is used when persisting the authorized token, so ensure
* it is unique within the scope of the property store. You must set the
* client secret and client ID, which are obtained when registering your
* add-on with the API.
*
* See the Apps Script OAuth2 Library documentation for more
* information:
* https://github.com/googlesamples/apps-script-oauth2#1-create-the-oauth2-service
*
* @return A configured OAuth2 service object.
*/
function getOAuthService() {
return OAuth2.createService('SERVICE_NAME')
.setAuthorizationBaseUrl('SERVICE_AUTH_URL')
.setTokenUrl('SERVICE_AUTH_TOKEN_URL')
.setClientId('CLIENT_ID')
.setClientSecret('CLIENT_SECRET')
.setScope('SERVICE_SCOPE_REQUESTS')
.setCallbackFunction('authCallback')
.setCache(CacheService.getUserCache())
.setPropertyStore(PropertiesService.getUserProperties());
}
/**
* Boilerplate code to determine if a request is authorized and returns
* a corresponding HTML message. When the user completes the OAuth2 flow
* on the service provider's website, this function is invoked from the
* service. In order for authorization to succeed you must make sure that
* the service knows how to call this function by setting the correct
* redirect URL.
*
* The redirect URL to enter is:
* https://script.google.com/macros/d/<Apps Script ID>/usercallback
*
* See the Apps Script OAuth2 Library documentation for more
* information:
* https://github.com/googlesamples/apps-script-oauth2#1-create-the-oauth2-service
*
* @param {Object} callbackRequest The request data received from the
* callback function. Pass it to the service's
* handleCallback() method to complete the
* authorization process.
* @return {HtmlOutput} a success or denied HTML message to display to
* the user. Also sets a timer to close the window
* automatically.
*/
function authCallback(callbackRequest) {
var authorized = getOAuthService().handleCallback(callbackRequest);
if (authorized) {
return HtmlService.createHtmlOutput(
'Success! <script>setTimeout(function() { top.window.close() }, 1);</script>');
} else {
return HtmlService.createHtmlOutput('Denied');
}
}
/**
* Unauthorizes the non-Google service. This is useful for OAuth
* development/testing. Run this method (Run > resetOAuth in the script
* editor) to reset OAuth to re-prompt the user for OAuth.
*/
function resetOAuth() {
getOAuthService().reset();
}