إذا كانت إضافة Google Workspace تتصل بخدمة أو واجهة برمجة تطبيقات تابعة لجهة خارجية وتتطلّب الحصول على إذن، يمكن أن تطلب الإضافة من المستخدمين تسجيل الدخول ومنح إذن الوصول.
توضّح هذه الصفحة كيفية مصادقة المستخدمين باستخدام سير عمل تفويض (مثل 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 بلا إنترنت وتخزينها في قاعدة بيانات واستخدامها لإجراء طلبات البيانات من واجهة برمجة التطبيقات مع مصادقة المستخدم.
برمجة التطبيقات
Node.js
Python
جافا
بطاقة التفويض المخصّصة
لتعديل طلب التفويض، يمكنك إنشاء بطاقة مخصّصة لتجربة تسجيل الدخول إلى خدمتك.
إذا كنت تنشر الإضافة بشكل علني، عليك استخدام بطاقة تفويض مخصّصة لجميع تطبيقات 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 LogoDESCRIPTION: عبارة تحث المستخدمين على اتخاذ إجراء لتسجيل الدخول، مثل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.
إذا كنت تنشئ في برمجة تطبيقات، يمكنك منع طلبات تسجيل الدخول المتكرّرة باستخدام خاصيات المستخدم أو رموز التعريف. ويتم توضيحها في الأقسام التالية.
خصائص المستخدمين
يمكنك تخزين بيانات تسجيل الدخول الخاصة بالمستخدم في خصائص المستخدم في برمجة تطبيقات. على سبيل المثال، يمكنك إنشاء رمز JSON المميّز للويب (JWT) الخاص بك من خدمة تسجيل الدخول الخاصة بهم وتسجيله في إحدى سمات المستخدم، أو تسجيل اسم المستخدم وكلمة المرور لخدمتهم.
يتم تحديد نطاق خصائص المستخدمين بحيث لا يمكن الوصول إليها إلا من خلال هذا المستخدم
ضمن النص البرمجي للإضافة. لا يمكن للمستخدمين الآخرين والبرامج النصية الأخرى الوصول إلى هذه الخصائص. لمزيد من التفاصيل، يُرجى الاطّلاع على
PropertiesService.
رموز التعريف
يمكنك استخدام رمز مميّز لمعرّف Google كبيانات تسجيل الدخول إلى خدمتك. وهذه إحدى طرق تحقيق الدخول المُوحَّد. يكون المستخدمون قد سجّلوا الدخول إلى Google لأنّهم يستخدمون تطبيقًا مضيفًا من Google.
مثال على إعدادات OAuth غير تابعة لـ Google
يوضّح نموذج الرمز التالي في "برمجة تطبيقات Google" كيفية ضبط إضافة لاستخدام واجهة برمجة تطبيقات غير تابعة لـ Google تتطلّب مصادقة OAuth. يستفيد هذا النموذج من مكتبة OAuth2 لخدمة 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.
*/
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();
}