
إذا كانت إضافة Google Workspace تتصل بخدمة أو واجهة برمجة تطبيقات (API) تابعة لجهة خارجية وتتطلّب الحصول على إذن، يمكن أن تطلب الإضافة من المستخدمين تسجيل الدخول ومنح الإذن بالوصول.
توضّح هذه الصفحة كيفية مصادقة المستخدمين باستخدام سير عمل تفويض (مثل OAuth)، والذي يتضمّن الخطوات التالية:
- الرصد عندما يكون التفويض مطلوبًا
- إرجاع واجهة بطاقة تطلب من المستخدمين تسجيل الدخول إلى الخدمة
- أعِد تحميل الإضافة ليتمكّن المستخدمون من الوصول إلى الخدمة أو المورد المحمي.
إذا كانت الإضافة تتطلّب هوية المستخدم فقط، يمكنك مصادقة المستخدمين مباشرةً باستخدام رقم تعريف حساباتهم على Google Workspace أو عناوين بريدهم الإلكتروني. لاستخدام عنوان البريد الإلكتروني للمصادقة، اطّلِع على التحقّق من صحة طلبات JSON. إذا أنشأت الإضافة باستخدام "برمجة تطبيقات Google"، يمكنك تسهيل هذه العملية باستخدام مكتبة OAuth2 لـ "برمجة تطبيقات Google" (يتوفّر أيضًا إصدار OAuth1).
رصد الحاجة إلى الحصول على إذن
عند استخدام الإضافة، قد لا يكون لدى المستخدمين إذن بالوصول إلى مورد محمي لأسباب مختلفة، مثل ما يلي:
- لم يتم إنشاء رمز دخول للربط بالخدمة التابعة لجهة خارجية بعد أو انتهت صلاحيته.
- لا يغطي رمز الدخول المورد المطلوب.
- لا يغطي رمز الدخول النطاقات المطلوبة للطلب.
يجب أن يرصد تطبيقك الإضافي هذه الحالات ليتمكّن المستخدمون من تسجيل الدخول إلى خدمتك والوصول إليها.
إذا كنت تنشئ في "برمجة تطبيقات Google"، يمكن أن تخبرك الدالة
hasAccess()
في مكتبة OAuth ما إذا كان لديك إذن الوصول إلى إحدى الخدمات.
بدلاً من ذلك، عند استخدام طلبات UrlFetchApp fetch()
، يمكنك ضبط المَعلمة muteHttpExceptions
على true
. يمنع ذلك الطلب من طرح استثناء عند تعذُّر تنفيذه، ويتيح لك فحص رمز استجابة الطلب والمحتوى في عنصر HttpResponse
الذي تم عرضه.
مطالبة المستخدمين بتسجيل الدخول إلى خدمتك
عندما يرصد التطبيق الإضافي أنّ هناك حاجة إلى تفويض، يجب أن يعرض التطبيق الإضافي واجهة بطاقة لتوجيه المستخدمين إلى تسجيل الدخول إلى الخدمة. يجب أن تعيد بطاقة تسجيل الدخول توجيه المستخدمين لإكمال عملية المصادقة والتفويض من خلال خدمة خارجية على بنيتك الأساسية.
عند إنشاء الإضافة باستخدام نقاط نهاية HTTP، ننصحك بحماية التطبيق الوجهة باستخدام تسجيل الدخول باستخدام حساب Google والحصول على معرّف المستخدم من خلال استخدام رمز التعريف الذي يتم إصداره أثناء تسجيل الدخول. يحتوي المطالبة الفرعية على المعرّف الفريد للمستخدم ويمكن ربطه بالمعرّف من الإضافة.
قبل قبول معرّف المستخدم الذي تم تأكيده.إنشاء بطاقة تسجيل الدخول وإرجاعها
بالنسبة إلى بطاقة تسجيل الدخول الخاصة بخدمتك، يمكنك استخدام بطاقة التفويض الأساسي من Google، أو يمكنك تخصيص بطاقة لعرض معلومات إضافية، مثل شعار مؤسستك. إذا كنت ستنشر الإضافة بشكل علني، عليك استخدام بطاقة مخصّصة.
بطاقة التفويض الأساسية
تعرض الصورة التالية مثالاً على بطاقة التفويض الأساسية من Google:

لعرض بطاقة تفويض أساسية للمستخدمين، عليك عرض العنصر
AuthorizationError
. يوضّح الرمز التالي مثالاً على عنصر AuthorizationError
:
برمجة التطبيقات
CardService.newAuthorizationException() .setAuthorizationUrl('AUTHORIZATION_URL') .setResourceDisplayName('RESOURCE_DISPLAY_NAME') .throwException();
JSON
أرسِل استجابة JSON التالية:
{
"basic_authorization_prompt": {
"authorization_url": "AUTHORIZATION_URL",
"resource": "RESOURCE_DISPLAY_NAME"
}
}
غيِّر القيم في السلسلة على الشكل التالي:
AUTHORIZATION_URL
: عنوان URL لتطبيق الويب الذي يتعامل مع التفويض.RESOURCE_DISPLAY_NAME
: الاسم المعروض للمورد أو الخدمة المحمية. يظهر هذا الاسم للمستخدم في طلب التفويض. على سبيل المثال، إذا كان حسابك علىRESOURCE_DISPLAY_NAME
هوExample Account
، ستظهر الرسالة "تريد هذه الإضافة عرض معلومات إضافية، ولكنها تحتاج إلى موافقة للوصول إلى حسابك Example Account".
بعد إكمال عملية التفويض، سيُطلب من المستخدم إعادة تحميل الإضافة للوصول إلى المورد المحمي.
عرض بطاقات تفويض الإرجاع في Google Chat
إذا كانت الإضافة توسّع نطاق Google Chat ونفّذها المستخدم ضمن Google Chat، يمكن للمستخدم إكمال عملية التفويض بدون إعادة تحميل الصفحة يدويًا. يتيح Google Chat إعادة محاولة التنفيذ السابق تلقائيًا إذا كان عامل التشغيل هو رسالة أو تمت الإضافة إلى مساحة أو أمر التطبيق. بالنسبة إلى عمليات التشغيل هذه، يتلقّى التطبيق الإضافي completeRedirectUri
في حمولة الحدث.
يجب ترميز completeRedirectUri
في عنوان URL الخاص بالإعداد لتفعيل إعادة المحاولة تلقائيًا. يشير إعادة التوجيه إلى عنوان URL هذا إلى Google Chat بأنّه تم تنفيذ طلب الإعداد، ويتيح لـ Google Chat إعادة محاولة التنفيذ السابق.
عندما تتم إعادة توجيه المستخدم بنجاح إلى configCompleteRedirectUrl
المقدَّم في الرسالة الأصلية، يتّخذ Google Chat الخطوات التالية:
- يمحو هذا الإجراء الطلب المعروض للمستخدم الذي بدأ المحادثة.
- يرسل عنصر الحدث الأصلي إلى الإضافة نفسها مرة ثانية.
إذا لم يتم ترميز completeRedirectUri
في عنوان URL الخاص بالإعداد، سيظل بإمكان المستخدم إكمال مسار التفويض. ومع ذلك، لا يعيد Google Chat محاولة التنفيذ السابق، ويجب أن يستدعي المستخدم الإضافة يدويًا مرة أخرى.
يوضّح نموذج الرمز البرمجي التالي كيف يمكن لتطبيق Chat طلب بيانات اعتماد OAuth2 بلا إنترنت وتخزينها في قاعدة بيانات واستخدامها لإجراء طلبات إلى واجهة برمجة التطبيقات مع مصادقة المستخدم.
بطاقة التفويض المخصّصة
لتعديل طلب التفويض، يمكنك إنشاء بطاقة مخصّصة لتجربة تسجيل الدخول إلى خدمتك.
إذا كنت تنشر الإضافة بشكل علني، عليك استخدام بطاقة تفويض مخصّصة لجميع تطبيقات Google Workspace المضيفة باستثناء Chat. لمزيد من المعلومات حول متطلبات النشر على Google Workspace Marketplace، يُرجى الاطّلاع على لمحة عن مراجعة التطبيقات.
يجب أن تستوفي البطاقة التي تم إرجاعها الشروط التالية:
- يجب توضيح للمستخدم أنّ الإضافة تطلب الإذن بالوصول إلى خدمة غير تابعة لـ Google نيابةً عنه.
- يجب توضيح الإجراءات التي يمكن أن تتخذها الإضافة في حال منحها الإذن.
- أن تحتوي على زر أو أداة مشابهة تنقل المستخدم إلى عنوان URL الخاص بالتفويض في الخدمة تأكَّد من أنّ وظيفة هذه الأداة واضحة للمستخدم.
- يجب أن تستخدم الأداة أعلاه الإعداد
OnClose.RELOAD
في العنصرOpenLink
لضمان إعادة تحميل الإضافة بعد تلقّي إذن الوصول. - يجب أن تستخدم بروتوكول HTTPS جميع الروابط التي يتم فتحها من طلب التفويض.
تعرض الصورة التالية مثالاً على بطاقة تفويض مخصّصة لصفحة إضافة الرئيسية. تتضمّن البطاقة شعارًا ووصفًا وزر تسجيل الدخول:

يوضّح الرمز التالي كيفية استخدام مثال البطاقة المخصّصة هذا:
برمجة التطبيقات
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
أرسِل استجابة JSON التالية:
{
"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"
}
}
]
}
]
}
}
]
}
}
}
غيِّر القيم في السلسلة على الشكل التالي:
-
LOGO_URL
: تمثّل عنوان URL لشعار أو صورة. يجب أن يكون عنوان URL عامًا. LOGO_ALT_TEXT
: النص البديل للشعار أو الصورة، مثلCymbal Labs Logo
DESCRIPTION
: عبارة تحث المستخدمين على اتخاذ إجراء لتسجيل الدخول، مثلSign in to get started
- لتعديل زر تسجيل الدخول، اتّبِع الخطوات التالية:
AUTHORIZATION_URL
: عنوان URL لتطبيق الويب الذي يتعامل مع التفويض.- اختياري: لتغيير لون الزر، عدِّل قيم RGBA العائمة في الحقل
color
. بالنسبة إلى Apps Script، عدِّل طريقةsetBackgroundColor()
باستخدام قيم سداسية عشرية.
-
TEXT_SIGN_UP
: نص يحث المستخدمين على إنشاء حساب إذا لم يكن لديهم حساب. مثلاً:New to Cymbal Labs? <a href=\"https://www.example.com/signup\">Sign up</a> here
إدارة عمليات تسجيل الدخول باستخدام خدمات تابعة لجهات خارجية في تطبيقات Google Workspace
أحد الاستخدامات الشائعة لإضافات Google Workspace هو توفير واجهة للتفاعل مع نظام تابع لجهة خارجية من داخل تطبيق مضيف في Google Workspace.
تتطلّب الأنظمة التابعة لجهات خارجية غالبًا أن يسجّل المستخدم الدخول باستخدام معرّف مستخدم أو كلمة مرور أو بيانات اعتماد أخرى. عندما يسجّل مستخدم الدخول إلى خدمتك التابعة لجهة خارجية أثناء استخدامه أحد مضيفي Google Workspace، عليك التأكّد من أنّه لن يُطلب منه تسجيل الدخول مرة أخرى عند التبديل إلى مضيف آخر في Google Workspace.
إذا كنت تعمل على إنشاء تطبيق في "برمجة التطبيقات"، يمكنك منع طلبات تسجيل الدخول المتكرّرة باستخدام خصائص المستخدم أو رموز التعريف. يتم توضيح هذه الحالات في الأقسام التالية.
خصائص المستخدمين
يمكنك تخزين بيانات تسجيل الدخول الخاصة بالمستخدم في خصائص المستخدم في Apps Script. على سبيل المثال، يمكنك إنشاء رمز JSON المميّز للويب (JWT) الخاص بك من خدمة تسجيل الدخول الخاصة بهم وتسجيل ذلك في خاصية مستخدم، أو تسجيل اسم المستخدم وكلمة المرور لخدمتهم.
يتم تحديد نطاق خصائص المستخدمين بحيث لا يمكن الوصول إليها إلا من خلال هذا المستخدم
ضمن النص البرمجي للإضافة. ولا يمكن للمستخدمين الآخرين أو البرامج النصية الأخرى الوصول إلى هذه الخصائص. يمكنك الاطّلاع على
PropertiesService
لمزيد من التفاصيل.
رموز التعريف
يمكنك استخدام رمز مميّز لمعرّف Google كبيانات اعتماد تسجيل الدخول إلى خدمتك. وهذه إحدى طرق تحقيق الدخول الموحّد. يكون المستخدمون قد سجّلوا الدخول إلى Google لأنّهم يستخدمون تطبيقًا مضيفًا من Google.
مثال على إعدادات OAuth غير التابعة لـ Google
يوضّح نموذج الرمز البرمجي التالي في Apps Script كيفية ضبط إضافة لاستخدام واجهة برمجة تطبيقات غير تابعة لـ Google تتطلّب مصادقة OAuth. يستفيد هذا النموذج من مكتبة OAuth2 لبرمجة التطبيقات لإنشاء خدمة للوصول إلى واجهة برمجة التطبيقات.
برمجة التطبيقات
/**
* 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.
*/
function authCallback(callbackRequest) {
var authorized = getOAuthService().handleCallback(callbackRequest);
if (authorized) {
return HtmlService.createHtmlOutput(
'Success!');
} 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();
}