تسجيل دخول المستخدم

هذه هي الجولة التفصيلية الثانية ضمن سلسلة التعليمات الإضافية لإضافات Classroom.

في هذه الجولة التفصيلية، عليك إضافة ميزة "تسجيل الدخول بحساب Google" إلى تطبيق الويب. هذا الإجراء مطلوب لإضافات Classroom. استخدم بيانات الاعتماد من تدفق التفويض هذا لجميع الطلبات المستقبلية لواجهة برمجة التطبيقات.

خلال هذه الجولة التفصيلية، تكمل ما يلي:

  • اضبط تطبيق الويب للحفاظ على بيانات الجلسات ضمن إطار iframe.
  • تنفيذ عملية تسجيل الدخول من خادم إلى خادم في Google OAuth 2.0.
  • إصدار اتصال بواجهة برمجة تطبيقات OAuth 2.0.
  • أنشئ مسارات إضافية لإتاحة التفويض والخروج واختبار طلبات بيانات من واجهة برمجة التطبيقات.

وبعد الانتهاء، يمكنك منح المستخدمين الإذن الكامل في تطبيق الويب وإصدار طلبات إلى Google APIs.

فهم مسار التفويض

تستخدم واجهات برمجة تطبيقات Google بروتوكول OAuth 2.0 للمصادقة والترخيص. يتوفّر الوصف الكامل لتنفيذ OAuth من Google في دليل OAuth لـ Google Identity.

تتم إدارة بيانات اعتماد تطبيقك في Google Cloud. بمجرد إنشاء هذه العناصر، قم بتنفيذ عملية من أربع خطوات لتفويض المستخدم ومصادقته:

  1. طلب تفويض. يُرجى تقديم عنوان URL لمعاودة الاتصال كجزء من هذا الطلب. عند اكتمال العملية، ستتلقّى عنوان URL للتفويض.
  2. أعِد توجيه المستخدم إلى عنوان URL للتفويض. تخبر الصفحة الناتجة المستخدم بالأذونات التي يتطلبها تطبيقك، وتطلب منه السماح بالوصول. عند اكتمال العملية، يتم توجيه المستخدم إلى عنوان URL لمعاودة الاتصال.
  3. تلقّي رمز تفويض في مسار معاودة الاتصال يمكنك استبدال رمز التفويض برمز دخول ورمز مميّز لإعادة التحميل.
  4. إجراء استدعاءات لواجهة Google API باستخدام الرموز المميزة

الحصول على بيانات اعتماد OAuth 2.0

تأكَّد من إنشاء بيانات اعتماد OAuth وتنزيلها كما هو موضّح في صفحة "نظرة عامة". يجب أن يستخدم مشروعك بيانات الاعتماد هذه لتسجيل دخول المستخدم.

تنفيذ مسار التفويض

أضِف المنطق والمسارات إلى تطبيق الويب لتحقيق التدفق الموضّح أعلاه، بما في ذلك الميزات التالية:

  • ابدأ مسار التفويض عند الوصول إلى الصفحة المقصودة.
  • اطلب التفويض وعالج استجابة خادم التفويض.
  • امحُ بيانات الاعتماد المُخزَّنة.
  • إبطال أذونات التطبيق
  • يمكنك اختبار طلب بيانات من واجهة برمجة التطبيقات.

بدء التفويض

عدِّل صفحتك المقصودة لبدء مسار التفويض إذا لزم الأمر. يمكن أن تكون الإضافة في حالتين محتملتين: إما وجود رموز مميزة محفوظة في الجلسة الحالية أو أنك تحتاج إلى الحصول على رموز مميزة من خادم OAuth 2.0. نفِّذ طلب اختبار من واجهة برمجة التطبيقات في حال كانت هناك رموز مميزة في الجلسة أو اطلب من المستخدم تسجيل الدخول.

Python

افتح ملف routes.py. عليك أولاً ضبط بعض الثوابت وضبط ملفات تعريف الارتباط وفقًا لاقتراحات الأمان الخاصة بإطار iframe.

# The file that contains the OAuth 2.0 client_id and client_secret.
CLIENT_SECRETS_FILE = "client_secret.json"

# The OAuth 2.0 access scopes to request.
# These scopes must match the scopes in your Google Cloud project's OAuth Consent
# Screen: https://console.cloud.google.com/apis/credentials/consent
SCOPES = [
    "openid",
    "https://www.googleapis.com/auth/userinfo.profile",
    "https://www.googleapis.com/auth/userinfo.email",
    "https://www.googleapis.com/auth/classroom.addons.teacher",
    "https://www.googleapis.com/auth/classroom.addons.student"
]

# Flask cookie configurations.
app.config.update(
    SESSION_COOKIE_SECURE=True,
    SESSION_COOKIE_HTTPONLY=True,
    SESSION_COOKIE_SAMESITE="None",
)

انتقِل إلى المسار المقصود للإضافة (هذا هو /classroom-addon في نموذج الملف). أضِف منطقًا لعرض صفحة تسجيل الدخول في حال لم تحتوي الجلسة على المفتاح "بيانات الاعتماد".

@app.route("/classroom-addon")
def classroom_addon():
    if "credentials" not in flask.session:
        return flask.render_template("authorization.html")

    return flask.render_template(
        "addon-discovery.html",
        message="You've reached the addon discovery page.")

Java

يمكن العثور على رمز هذه الجولة التفصيلية في وحدة step_02_sign_in.

افتح ملف application.properties، وأضِف إعدادات الجلسة التي تتّبع اقتراحات أمان iframe.

# iFrame security recommendations call for cookies to have the HttpOnly and
# secure attribute set
server.servlet.session.cookie.http-only=true
server.servlet.session.cookie.secure=true

# Ensures that the session is maintained across the iframe and sign-in pop-up.
server.servlet.session.cookie.same-site=none

أنشِئ فئة خدمة (AuthService.java في وحدة step_02_sign_in) للتعامل مع الأسباب المنطقية لنقاط النهاية في ملف وحدة التحكّم وإعداد معرّف الموارد المنتظم (URI) لإعادة التوجيه وموقع ملف أسرار العملاء والنطاقات التي تتطلّبها الإضافة. يُستخدم معرِّف الموارد المنتظم (URI) لإعادة التوجيه لإعادة توجيه المستخدمين إلى معرّف موارد منتظم (URI) محدّد بعد منح الإذن لتطبيقك. يُرجى الاطّلاع على قسم "إعداد المشروع" في README.md في رمز المصدر للحصول على معلومات حول مكان وضع ملف client_secret.json.

@Service
public class AuthService {
    private static final String REDIRECT_URI = "https://localhost:5000/callback";
    private static final String CLIENT_SECRET_FILE = "client_secret.json";
    private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
    private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();

    private static final String[] REQUIRED_SCOPES = {
        "https://www.googleapis.com/auth/userinfo.profile",
        "https://www.googleapis.com/auth/userinfo.email",
        "https://www.googleapis.com/auth/classroom.addons.teacher",
        "https://www.googleapis.com/auth/classroom.addons.student"
    };

    /** Creates and returns a Collection object with all requested scopes.
    *   @return Collection of scopes requested by the application.
    */
    public static Collection<String> getScopes() {
        return new ArrayList<>(Arrays.asList(REQUIRED_SCOPES));
    }
}

افتح ملف وحدة التحكّم (AuthController.java في وحدة step_02_sign_in) وأضِف منطقًا إلى المسار المقصودة لعرض صفحة تسجيل الدخول إذا كانت الجلسة لا تحتوي على المفتاح credentials.

@GetMapping(value = {"/start-auth-flow"})
public String startAuthFlow(Model model) {
    try {
        return "authorization";
    } catch (Exception e) {
        return onError(e.getMessage(), model);
    }
}

@GetMapping(value = {"/addon-discovery"})
public String addon_discovery(HttpSession session, Model model) {
    try {
        if (session == null || session.getAttribute("credentials") == null) {
            return startAuthFlow(model);
        }
        return "addon-discovery";
    } catch (Exception e) {
        return onError(e.getMessage(), model);
    }
}

يجب أن تحتوي صفحة التفويض على رابط أو زر يتيح للمستخدم "تسجيل الدخول". من المفترض أن يؤدي النقر على هذا الرابط إلى إعادة توجيه المستخدم إلى المسار authorize.

طلب تفويض

لطلب التفويض، عليك إنشاء المستخدم وإعادة توجيهه إلى عنوان URL للمصادقة. يتضمّن عنوان URL هذا عدّة معلومات، مثل النطاقات المطلوبة والمسار الوجهة إلى بعد التفويض ومعرّف العميل لتطبيق الويب. يمكنك الاطّلاع على هذه المعلومات في نموذج عنوان URL للتفويض.

Python

أضِف عملية الاستيراد التالية إلى ملف routes.py.

import google_auth_oauthlib.flow

إنشاء مسار جديد /authorize أنشِئ مثيلاً للسمة google_auth_oauthlib.flow.Flow، وننصحك بشدة باستخدام طريقة from_client_secrets_file المضمَّنة لإجراء ذلك.

@app.route("/authorize")
def authorize():
    # Create flow instance to manage the OAuth 2.0 Authorization Grant Flow
    # steps.
    flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
        CLIENT_SECRETS_FILE, scopes=SCOPES)

يمكنك ضبط redirect_uri للسمة flow، وهي المسار الذي تريد من المستخدمين الرجوع إليه بعد منح الإذن لتطبيقك. ويمكنك الاطّلاع على الموضع /callback في المثال أدناه.

# The URI created here must exactly match one of the authorized redirect
# URIs for the OAuth 2.0 client, which you configured in the API Console. If
# this value doesn't match an authorized URI, you will get a
# "redirect_uri_mismatch" error.
flow.redirect_uri = flask.url_for("callback", _external=True)

استخدِم كائن التدفق لإنشاء authorization_url وstate. تخزين state في الجلسة، حيث يُستخدم للتحقّق من صحة استجابة الخادم لاحقًا وأخيرًا، أعِد توجيه المستخدم إلى authorization_url.

authorization_url, state = flow.authorization_url(
    # Enable offline access so that you can refresh an access token without
    # re-prompting the user for permission. Recommended for web server apps.
    access_type="offline",
    # Enable incremental authorization. Recommended as a best practice.
    include_granted_scopes="true")

# Store the state so the callback can verify the auth server response.
flask.session["state"] = state

# Redirect the user to the OAuth authorization URL.
return flask.redirect(authorization_url)

Java

أضِف الطرق التالية إلى ملف AuthService.java لإنشاء مثيل لكائن التدفق، ثم استخدِمه لاسترداد عنوان URL للتفويض:

  • getClientSecrets() تقرأ الملف السري للعميل وتنشئ كائن GoogleClientSecrets.
  • getFlow() تنشئ مثيلاً لـ GoogleAuthorizationCodeFlow.
  • authorize() الكائن GoogleAuthorizationCodeFlow والمَعلمة state ومعرّف الموارد المنتظم (URI) لإعادة التوجيه لاسترداد عنوان URL للتفويض. تُستخدم المعلمة state للتحقق من صحة الاستجابة من خادم التفويض. تُرجع الطريقة بعد ذلك خريطة تحتوي على عنوان URL للتفويض والمعلمة state.
/** Reads the client secret file downloaded from Google Cloud.
 *   @return GoogleClientSecrets read in from client secret file. */
public GoogleClientSecrets getClientSecrets() throws Exception {
    try {
        InputStream in = SignInApplication.class.getClassLoader()
            .getResourceAsStream(CLIENT_SECRET_FILE);
        if (in == null) {
            throw new FileNotFoundException("Client secret file not found: "
                +   CLIENT_SECRET_FILE);
        }
        GoogleClientSecrets clientSecrets = GoogleClientSecrets
            .load(JSON_FACTORY, new InputStreamReader(in));
        return clientSecrets;
    } catch (Exception e) {
        throw e;
    }
}

/** Builds and returns authorization code flow.
*   @return GoogleAuthorizationCodeFlow object used to retrieve an access
*   token and refresh token for the application.
*   @throws Exception if reading client secrets or building code flow object
*   is unsuccessful.
*/
public GoogleAuthorizationCodeFlow getFlow() throws Exception {
    try {
        GoogleAuthorizationCodeFlow authorizationCodeFlow =
            new GoogleAuthorizationCodeFlow.Builder(
                HTTP_TRANSPORT,
                JSON_FACTORY,
                getClientSecrets(),
                getScopes())
                .setAccessType("offline")
                .build();
        return authorizationCodeFlow;
    } catch (Exception e) {
        throw e;
    }
}

/** Builds and returns a map with the authorization URL, which allows the
*   user to give the app permission to their account, and the state parameter,
*   which is used to prevent cross site request forgery.
*   @return map with authorization URL and state parameter.
*   @throws Exception if building the authorization URL is unsuccessful.
*/
public HashMap authorize() throws Exception {
    HashMap<String, String> authDataMap = new HashMap<>();
    try {
        String state = new BigInteger(130, new SecureRandom()).toString(32);
        authDataMap.put("state", state);

        GoogleAuthorizationCodeFlow flow = getFlow();
        String authUrl = flow
            .newAuthorizationUrl()
            .setState(state)
            .setRedirectUri(REDIRECT_URI)
            .build();
        String url = authUrl;
        authDataMap.put("url", url);

        return authDataMap;
    } catch (Exception e) {
        throw e;
    }
}

استخدم حقن الدالة الإنشائية لإنشاء مثيل لفئة الخدمة في فئة وحدة التحكم.

/** Declare AuthService to be used in the Controller class constructor. */
private final AuthService authService;

/** AuthController constructor. Uses constructor injection to instantiate
*   the AuthService and UserRepository classes.
*   @param authService the service class that handles the implementation logic
*   of requests.
*/
public AuthController(AuthService authService) {
    this.authService = authService;
}

أضِف نقطة النهاية /authorize إلى فئة وحدة التحكّم. تستدعي نقطة النهاية هذه طريقة AuthService authorize() لاسترداد المَعلمة state وعنوان URL للتفويض. بعد ذلك، تخزِّن نقطة النهاية المَعلمة state في الجلسة وتعيد توجيه المستخدمين إلى عنوان URL للتفويض.

/** Redirects the sign-in pop-up to the authorization URL.
*   @param response the current response to pass information to.
*   @param session the current session.
*   @throws Exception if redirection to the authorization URL is unsuccessful.
*/
@GetMapping(value = {"/authorize"})
public void authorize(HttpServletResponse response, HttpSession session)
    throws Exception {
    try {
        HashMap authDataMap = authService.authorize();
        String authUrl = authDataMap.get("url").toString();
        String state = authDataMap.get("state").toString();
        session.setAttribute("state", state);
        response.sendRedirect(authUrl);
    } catch (Exception e) {
        throw e;
    }
}

التعامل مع استجابة الخادم

بعد منح الإذن، يعود المستخدم إلى مسار "redirect_uri" من الخطوة السابقة. في المثال أعلاه، هذا المسار هو /callback.

ستتلقى code في الرد عندما يعود المستخدم من صفحة التفويض. بعد ذلك، استبدِل الرمز برمزَي الدخول وإعادة التحميل:

Python

أضف عمليات الاستيراد التالية إلى ملف خادم Flask.

import google.oauth2.credentials
import googleapiclient.discovery

أضِف المسار إلى خادمك. إنشاء مثيل آخر من google_auth_oauthlib.flow.Flow، ولكن هذه المرة إعادة استخدام الحالة المحفوظة في الخطوة السابقة.

@app.route("/callback")
def callback():
    state = flask.session["state"]

    flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
        CLIENT_SECRETS_FILE, scopes=SCOPES, state=state)
    flow.redirect_uri = flask.url_for("callback", _external=True)

بعد ذلك، اطلب رموز الدخول وإعادة التحميل. لحسن الحظ، يحتوي الكائن flow أيضًا على طريقة fetch_token لإنجاز ذلك. وتتوقّع الطريقة إما وسيطة code أو authorization_response. استخدِم authorization_response، لأنّه عنوان URL الكامل الوارد في الطلب.

authorization_response = flask.request.url
flow.fetch_token(authorization_response=authorization_response)

لديك الآن بيانات اعتماد مكتملة. تخزينها في الجلسة بحيث يمكن استردادها بطرق أو مسارات أخرى، ثم إعادة توجيهها إلى الصفحة المقصودة للإضافة.

credentials = flow.credentials
flask.session["credentials"] = {
    "token": credentials.token,
    "refresh_token": credentials.refresh_token,
    "token_uri": credentials.token_uri,
    "client_id": credentials.client_id,
    "client_secret": credentials.client_secret,
    "scopes": credentials.scopes
}

# Close the pop-up by rendering an HTML page with a script that redirects
# the owner and closes itself. This can be done with a bit of JavaScript:
# <script>
#     window.opener.location.href = "{{ url_for('classroom_addon') }}";
#     window.close();
# </script>
return flask.render_template("close-me.html")

Java

أضِف طريقة إلى فئة الخدمة تعرض عنصر Credentials من خلال إدخال رمز التفويض الذي تم استرداده من عملية إعادة التوجيه التي تتم من خلال عنوان URL للتفويض. يُستخدم كائن Credentials هذا لاحقًا لاسترداد رمز الدخول ورمز التحديث

/** Returns the required credentials to access Google APIs.
*   @param authorizationCode the authorization code provided by the
*   authorization URL that's used to obtain credentials.
*   @return the credentials that were retrieved from the authorization flow.
*   @throws Exception if retrieving credentials is unsuccessful.
*/
public Credential getAndSaveCredentials(String authorizationCode) throws Exception {
    try {
        GoogleAuthorizationCodeFlow flow = getFlow();
        GoogleClientSecrets googleClientSecrets = getClientSecrets();
        TokenResponse tokenResponse = flow.newTokenRequest(authorizationCode)
            .setClientAuthentication(new ClientParametersAuthentication(
                googleClientSecrets.getWeb().getClientId(),
                googleClientSecrets.getWeb().getClientSecret()))
            .setRedirectUri(REDIRECT_URI)
            .execute();
        Credential credential = flow.createAndStoreCredential(tokenResponse, null);
        return credential;
    } catch (Exception e) {
        throw e;
    }
}

أضِف نقطة نهاية لعنوان URI لإعادة التوجيه إلى وحدة التحكّم. استرجع رمز التفويض والمعلمة state من الطلب. قارِن هذه المَعلمة state بسمة state المخزَّنة في الجلسة. في حال تطابق الرمز، يمكنك مواصلة عملية التفويض. إذا لم يتطابقا، فيعرض خطأ.

بعد ذلك، يمكنك استدعاء طريقة getAndSaveCredentials AuthService وتمرير رمز التفويض كمَعلمة. بعد استرداد كائن Credentials، يمكنك تخزينه في الجلسة. بعد ذلك، أغلِق مربع الحوار وأعِد توجيه المستخدم إلى الصفحة المقصودة للإضافة.

/** Handles the redirect URL to grant the application access to the user's
*   account.
*   @param request the current request used to obtain the authorization code
*   and state parameter from.
*   @param session the current session.
*   @param response the current response to pass information to.
*   @param model the Model interface to pass error information that's
*   displayed on the error page.
*   @return the close-pop-up template if authorization is successful, or the
*   onError method to handle and display the error message.
*/
@GetMapping(value = {"/callback"})
public String callback(HttpServletRequest request, HttpSession session,
    HttpServletResponse response, Model model) {
    try {
        String authCode = request.getParameter("code");
        String requestState = request.getParameter("state");
        String sessionState = session.getAttribute("state").toString();
        if (!requestState.equals(sessionState)) {
            response.setStatus(401);
            return onError("Invalid state parameter.", model);
        }
        Credential credentials = authService.getAndSaveCredentials(authCode);
        session.setAttribute("credentials", credentials);
        return "close-pop-up";
    } catch (Exception e) {
        return onError(e.getMessage(), model);
    }
}

اختبار طلب بيانات من واجهة برمجة التطبيقات

بعد اكتمال التدفق، يمكنك الآن إصدار استدعاءات لواجهات Google APIs!

على سبيل المثال، يمكنك طلب معلومات الملف الشخصي للمستخدم. يمكنك طلب معلومات المستخدم من خلال OAuth 2.0 API.

Python

اقرأ وثائق واجهة برمجة تطبيقات اكتشاف OAuth 2.0 استخدمها للحصول على كائن UserInfo الذي تمت تعبئته.

# Retrieve the credentials from the session data and construct a
# Credentials instance.
credentials = google.oauth2.credentials.Credentials(
    **flask.session["credentials"])

# Construct the OAuth 2.0 v2 discovery API library.
user_info_service = googleapiclient.discovery.build(
    serviceName="oauth2", version="v2", credentials=credentials)

# Request and store the username in the session.
# This allows it to be used in other methods or in an HTML template.
flask.session["username"] = (
    user_info_service.userinfo().get().execute().get("name"))

Java

في فئة الخدمة، يمكنك إنشاء طريقة تؤدي إلى إنشاء عنصر UserInfo باستخدام Credentials كمَعلمة.

/** Obtains the Userinfo object by passing in the required credentials.
*   @param credentials retrieved from the authorization flow.
*   @return the Userinfo object for the currently signed-in user.
*   @throws IOException if creating UserInfo service or obtaining the
*   Userinfo object is unsuccessful.
*/
public Userinfo getUserInfo(Credential credentials) throws IOException {
    try {
        Oauth2 userInfoService = new Oauth2.Builder(
            new NetHttpTransport(),
            new GsonFactory(),
            credentials).build();
        Userinfo userinfo = userInfoService.userinfo().get().execute();
        return userinfo;
    } catch (Exception e) {
        throw e;
    }
}

أضِف نقطة النهاية /test إلى وحدة التحكّم التي تعرض البريد الإلكتروني للمستخدم.

/** Returns the test request page with the user's email.
*   @param session the current session.
*   @param model the Model interface to pass error information that's
*   displayed on the error page.
*   @return the test page that displays the current user's email or the
*   onError method to handle and display the error message.
*/
@GetMapping(value = {"/test"})
public String test(HttpSession session, Model model) {
    try {
        Credential credentials = (Credential) session.getAttribute("credentials");
        Userinfo userInfo = authService.getUserInfo(credentials);
        String userInfoEmail = userInfo.getEmail();
        if (userInfoEmail != null) {
            model.addAttribute("userEmail", userInfoEmail);
        } else {
            return onError("Could not get user email.", model);
        }
        return "test";
    } catch (Exception e) {
        return onError(e.getMessage(), model);
    }
}

محو بيانات الاعتماد

يمكنك "محو" بيانات اعتماد المستخدم عن طريق إزالتها من الجلسة الحالية. يتيح لك ذلك اختبار التوجيه على الصفحة المقصودة للإضافة.

وننصحك بإظهار إشارة إلى أنّ المستخدم قد سجّل الخروج قبل إعادة توجيهه إلى الصفحة المقصودة للإضافة. يجب أن يخضع تطبيقك لمسار التفويض للحصول على بيانات الاعتماد الجديدة، ولكن لن يُطلب من المستخدمين إعادة تفويض تطبيقك.

Python

@app.route("/clear")
def clear_credentials():
    if "credentials" in flask.session:
        del flask.session["credentials"]
        del flask.session["username"]

    return flask.render_template("signed-out.html")

بدلاً من ذلك، يمكنك استخدام flask.session.clear()، ولكن قد يكون لذلك تأثيرات غير مقصودة إذا كانت الجلسة لديك قيم أخرى مخزّنة.

Java

في وحدة التحكّم، أضِف نقطة نهاية /clear.

/** Clears the credentials in the session and returns the sign-out
*   confirmation page.
*   @param session the current session.
*   @return the sign-out confirmation page.
*/
@GetMapping(value = {"/clear"})
public String clear(HttpSession session) {
    try {
        if (session != null && session.getAttribute("credentials") != null) {
            session.removeAttribute("credentials");
        }
        return "sign-out";
    } catch (Exception e) {
        return onError(e.getMessage(), model);
    }
}

إبطال إذن التطبيق

يمكن للمستخدم إبطال إذن تطبيقك عن طريق إرسال طلب POST إلى https://oauth2.googleapis.com/revoke. يجب أن يحتوي الطلب على رمز الدخول الخاص بالمستخدم.

Python

import requests

@app.route("/revoke")
def revoke():
    if "credentials" not in flask.session:
        return flask.render_template("addon-discovery.html",
                            message="You need to authorize before " +
                            "attempting to revoke credentials.")

    credentials = google.oauth2.credentials.Credentials(
        **flask.session["credentials"])

    revoke = requests.post(
        "https://oauth2.googleapis.com/revoke",
        params={"token": credentials.token},
        headers={"content-type": "application/x-www-form-urlencoded"})

    if "credentials" in flask.session:
        del flask.session["credentials"]
        del flask.session["username"]

    status_code = getattr(revoke, "status_code")
    if status_code == 200:
        return flask.render_template("authorization.html")
    else:
        return flask.render_template(
            "index.html", message="An error occurred during revocation!")

Java

يمكنك إضافة طريقة إلى فئة الخدمة التي تُجري استدعاءًا لنقطة نهاية الإبطال.

/** Revokes the app's permissions to the user's account.
*   @param credentials retrieved from the authorization flow.
*   @return response entity returned from the HTTP call to obtain response
*   information.
*   @throws RestClientException if the POST request to the revoke endpoint is
*   unsuccessful.
*/
public ResponseEntity<String> revokeCredentials(Credential credentials) throws RestClientException {
    try {
        String accessToken = credentials.getAccessToken();
        String url = "https://oauth2.googleapis.com/revoke?token=" + accessToken;

        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE);
        HttpEntity<Object> httpEntity = new HttpEntity<Object>(httpHeaders);
        ResponseEntity<String> responseEntity = new RestTemplate().exchange(
            url,
            HttpMethod.POST,
            httpEntity,
            String.class);
        return responseEntity;
    } catch (RestClientException e) {
        throw e;
    }
}

أضِف نقطة نهاية، /revoke، إلى وحدة التحكّم التي تمحو الجلسة وتعيد توجيه المستخدم إلى صفحة التفويض في حال نجاح عملية الإبطال.

/** Revokes the app's permissions and returns the authorization page.
*   @param session the current session.
*   @return the authorization page.
*   @throws Exception if revoking access is unsuccessful.
*/
@GetMapping(value = {"/revoke"})
public String revoke(HttpSession session) throws Exception {
    try {
        if (session != null && session.getAttribute("credentials") != null) {
            Credential credentials = (Credential) session.getAttribute("credentials");
            ResponseEntity responseEntity = authService.revokeCredentials(credentials);
            Integer httpStatusCode = responseEntity.getStatusCodeValue();

            if (httpStatusCode != 200) {
                return onError("There was an issue revoking access: " +
                    responseEntity.getStatusCode(), model);
            }
            session.removeAttribute("credentials");
        }
        return startAuthFlow(model);
    } catch (Exception e) {
        return onError(e.getMessage(), model);
    }
}

اختبار الإضافة

سجِّل الدخول إلى Google Classroom بصفتك أحد المستخدمين المختبِرين للمعلّم. انتقِل إلى علامة التبويب الواجب الدراسي وأنشئ مهمة جديدة. انقر على زر الإضافات أسفل مربّع النص، ثم اختَر الإضافة. يتم فتح إطار iframe وتحمِّل الإضافة معرّف الموارد المنتظم (URI) لإعداد المرفقات الذي حدّدته في صفحة إعدادات التطبيق في حزمة تطوير البرامج (SDK) لأداة GWM.

تهانينا أنت جاهز للمتابعة إلى الخطوة التالية: التعامل مع الزيارات المتكرّرة إلى الإضافة.