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

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

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

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

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

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

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

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

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

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

الحصول على بيانات اعتماد 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.

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 الإعدادات .

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