اجازه دسترسی به اطلاعات کاربر Google را صادر کنید

احراز هویت مشخص می‌کند که شخص چه کسی است، و معمولاً به عنوان ثبت نام یا ورود به سیستم کاربر شناخته می‌شود. مجوز فرآیند اعطای یا رد دسترسی به داده ها یا منابع است. به عنوان مثال، برنامه شما رضایت کاربر شما را برای دسترسی به Google Drive کاربر درخواست می کند.

تماس‌های احراز هویت و مجوز باید دو جریان مجزا و مجزا بر اساس نیازهای برنامه باشند.

اگر برنامه شما دارای ویژگی‌هایی است که می‌تواند از داده‌های API Google استفاده کند، اما به عنوان بخشی از ویژگی‌های اصلی برنامه شما مورد نیاز نیستند، باید برنامه خود را طوری طراحی کنید که بتوانید مواردی را که داده‌های API در دسترس نیست به‌خوبی مدیریت کنید. برای مثال، زمانی که کاربر به Drive اجازه دسترسی نداده است، ممکن است فهرستی از فایل‌های اخیراً ذخیره شده را پنهان کنید.

فقط زمانی که کاربر اقدامی را انجام می‌دهد که نیاز به دسترسی به یک API خاص دارد، باید به دامنه‌هایی که برای دسترسی به Google API نیاز دارید، درخواست کنید. برای مثال، هر زمان که کاربر روی دکمه «ذخیره در Drive» ضربه می‌زند، باید اجازه دسترسی به Drive کاربر را درخواست کنید.

با جدا کردن مجوز از احراز هویت، می‌توانید از سرکوب کاربران جدید یا سردرگمی کاربران در مورد اینکه چرا از آنها مجوزهای خاص خواسته می‌شود جلوگیری کنید.

برای احراز هویت، توصیه می‌کنیم از Credential Manager API استفاده کنید. برای مجوز دادن به اقداماتی که نیاز به دسترسی به داده های کاربر ذخیره شده توسط Google دارند، توصیه می کنیم از AuthorizationClient استفاده کنید.

خود را تنظیم کنید پروژه

  1. پروژه خود را در ، یا اگر قبلا پروژه ای ندارید ایجاد کنید.
  2. در ، مطمئن شوید که همه اطلاعات کامل و دقیق هستند.
    1. مطمئن شوید که نام برنامه، نشان‌واره برنامه و صفحه اصلی برنامه به برنامه شما اختصاص داده شده است. این مقادیر در صفحه رضایت ورود به سیستم با Google هنگام ثبت نام و صفحه برنامه‌ها و خدمات شخص ثالث به کاربران ارائه می‌شوند.
    2. مطمئن شوید که نشانی‌های اینترنتی خط‌مشی رازداری و شرایط خدمات برنامه‌تان را مشخص کرده‌اید.
  3. در ، یک شناسه کلاینت Android برای برنامه خود ایجاد کنید، اگر قبلاً آن را ندارید. شما باید نام بسته برنامه و امضای SHA-1 را مشخص کنید.
    1. برو به .
    2. روی ایجاد مشتری کلیک کنید.
    3. نوع برنامه اندروید را انتخاب کنید.
  4. در ، شناسه مشتری «برنامه وب» جدید ایجاد کنید، اگر قبلاً این کار را نکرده اید. فعلاً می‌توانید از فیلدهای «اصالت‌های مجاز جاوا اسکریپت» و «URIs تغییر مسیر مجاز» چشم‌پوشی کنید. این شناسه مشتری برای شناسایی سرور پشتیبان شما هنگام برقراری ارتباط با سرویس‌های احراز هویت Google استفاده می‌شود.
    1. برو به .
    2. روی ایجاد مشتری کلیک کنید.
    3. نوع برنامه وب را انتخاب کنید.

وابستگی ها را اعلام کنید

در فایل build.gradle ماژول خود، وابستگی ها را با استفاده از آخرین نسخه کتابخانه Google Identity Services اعلام کنید.

dependencies {
  // ... other dependencies

  implementation "com.google.android.gms:play-services-auth:21.4.0"
}

درخواست مجوزهای مورد نیاز اقدامات کاربر

هر زمان که کاربر عملی را انجام داد که به محدوده بیشتری نیاز دارد، AuthorizationClient.authorize() را فراخوانی کنید. به عنوان مثال، اگر کاربر عملی را انجام می‌دهد که نیاز به دسترسی به فضای ذخیره‌سازی برنامه Drive خود دارد، موارد زیر را انجام دهید:

کاتلین

val requestedScopes: List<Scope> = listOf(DriveScopes.DRIVE_FILE)
val authorizationRequest = AuthorizationRequest.builder()
    .setRequestedScopes(requestedScopes)
    .build()

Identity.getAuthorizationClient(activity)
    .authorize(authorizationRequestBuilder.build())
    .addOnSuccessListener { authorizationResult ->
        if (authorizationResult.hasResolution()) {
            val pendingIntent = authorizationResult.pendingIntent
            // Access needs to be granted by the user
            startAuthorizationIntent.launchIntentSenderRequest.Builder(pendingIntent!!.intentSender).build()
        } else {
            // Access was previously granted, continue with user action
            saveToDriveAppFolder(authorizationResult);
        }
    }
    .addOnFailureListener { e -> Log.e(TAG, "Failed to authorize", e) }

جاوا

List<Scopes> requestedScopes = Arrays.asList(DriveScopes.DRIVE_FILE);
AuthorizationRequest authorizationRequest = AuthorizationRequest.builder()
    .setRequestedScopes(requestedScopes)
    .build();

Identity.getAuthorizationClient(activity)
    .authorize(authorizationRequest)
    .addOnSuccessListener(authorizationResult -> {
        if (authorizationResult.hasResolution()) {
            // Access needs to be granted by the user
            startAuthorizationIntent.launch(
                new IntentSenderRequest.Builder(
                    authorizationResult.getPendingIntent().getIntentSender()
                ).build()
            );
        } else {
            // Access was previously granted, continue with user action
            saveToDriveAppFolder(authorizationResult);
        }
    })
    .addOnFailureListener(e -> Log.e(TAG, "Failed to authorize", e));

هنگام تعریف ActivityResultLauncher ، پاسخ را همانطور که در قطعه زیر نشان داده شده است، مدیریت کنید، جایی که فرض می کنیم در یک قطعه انجام می شود. کد بررسی می کند که مجوزهای مورد نیاز با موفقیت اعطا شده است و سپس اقدام کاربر را انجام می دهد.

کاتلین

private lateinit var startAuthorizationIntent: ActivityResultLauncher<IntentSenderRequest>

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?,
): View? {
    // ...
    startAuthorizationIntent =
        registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { activityResult ->
            try {
                // extract the result
                val authorizationResult = Identity.getAuthorizationClient(requireContext())
                    .getAuthorizationResultFromIntent(activityResult.data)
                // continue with user action
                saveToDriveAppFolder(authorizationResult);
            } catch (ApiException e) {
                // log exception
            }
        }
}

جاوا

private ActivityResultLauncher<IntentSenderRequest> startAuthorizationIntent;

@Override
public View onCreateView(
    @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// ...
startAuthorizationIntent =
    registerForActivityResult(
        new ActivityResultContracts.StartIntentSenderForResult(),
        activityResult -> {
            try {
            // extract the result
            AuthorizationResult authorizationResult =
                Identity.getAuthorizationClient(requireActivity())
                    .getAuthorizationResultFromIntent(activityResult.getData());
            // continue with user action
            saveToDriveAppFolder(authorizationResult);
            } catch (ApiException e) {
            // log exception
            }
        });
}

اگر به APIهای Google در سمت سرور دسترسی دارید، با متد getServerAuthCode() از AuthorizationResult تماس بگیرید تا کد مجوزی را دریافت کنید که به پشتیبان خود ارسال می‌کنید تا برای یک توکن دسترسی و بازخوانی مبادله شود. برای کسب اطلاعات بیشتر، به حفظ دسترسی مداوم به داده‌های کاربر مراجعه کنید.

مجوزهای داده ها یا منابع کاربر را لغو کنید

برای لغو دسترسی قبلی، با AuthorizationClient.revokeAccess() تماس بگیرید. به عنوان مثال، اگر کاربر در حال حذف حساب خود از برنامه شما است و برنامه شما قبلاً به DriveScopes.DRIVE_FILE دسترسی داشته است.DRIVE_FILE، از کد زیر برای لغو دسترسی استفاده کنید:

کاتلین

val requestedScopes: MutableList<Scope> = mutableListOf(DriveScopes.DRIVE_FILE)
RevokeAccessRequest revokeAccessRequest = RevokeAccessRequest.builder()
    .setAccount(account)
    .setScopes(requestedScopes)
    .build()

Identity.getAuthorizationClient(activity)
    .revokeAccess(revokeAccessRequest)
    .addOnSuccessListener { Log.i(TAG, "Successfully revoked access") }
    .addOnFailureListener { e -> Log.e(TAG, "Failed to revoke access", e) }

جاوا

List<Scopes> requestedScopes = Arrays.asList(DriveScopes.DRIVE_FILE);
RevokeAccessRequest revokeAccessRequest = RevokeAccessRequest.builder()
    .setAccount(account)
    .setScopes(requestedScopes)
    .build();

Identity.getAuthorizationClient(activity)
    .revokeAccess(revokeAccessRequest)
    .addOnSuccessListener(unused -> Log.i(TAG, "Successfully revoked access"))
    .addOnFailureListener(e -> Log.e(TAG, "Failed to revoke access", e));

کش توکن را پاک کنید

نشانه های دسترسی OAuth به صورت محلی پس از دریافت از سرور ذخیره می شوند و سرعت دسترسی را افزایش داده و تماس های شبکه را کاهش می دهند. این توکن ها پس از انقضا به طور خودکار از حافظه پنهان حذف می شوند، اما ممکن است به دلایل دیگر نیز نامعتبر شوند. اگر هنگام استفاده از رمز، یک IllegalStateException دریافت کردید، کش محلی را پاک کنید تا مطمئن شوید که درخواست مجوز بعدی برای یک نشانه دسترسی به سرور OAuth می رود. قطعه زیر invalidAccessToken را از کش محلی حذف می کند:

کاتلین

Identity.getAuthorizationClient(activity)
    .clearToken(ClearTokenRequest.builder().setToken(invalidAccessToken).build())
    .addOnSuccessListener { Log.i(TAG, "Successfully removed the token from the cache") }
    .addOnFailureListener{ e -> Log.e(TAG, "Failed to clear token", e) }

جاوا

Identity.getAuthorizationClient(activity)
    .clearToken(ClearTokenRequest.builder().setToken(invalidAccessToken).build())
    .addOnSuccessListener(unused -> Log.i(TAG, "Successfully removed the token from the cache"))
    .addOnFailureListener(e -> Log.e(TAG, "Failed to clear the token cache", e));

دریافت اطلاعات کاربر در طول مجوز

پاسخ مجوز حاوی هیچ اطلاعاتی در مورد حساب کاربری استفاده شده نیست. پاسخ فقط حاوی یک نشانه برای محدوده های درخواستی است. به عنوان مثال، پاسخ برای دریافت رمز دسترسی برای دسترسی به Google Drive کاربر، هویت حسابی را که توسط کاربر انتخاب شده است، آشکار نمی کند، حتی اگر می توان از آن برای دسترسی به فایل های موجود در درایو کاربر استفاده کرد. برای دریافت اطلاعاتی مانند نام یا ایمیل کاربر، گزینه های زیر را دارید:

  • قبل از درخواست مجوز، کاربر را با حساب Google خود با استفاده از APIهای Credential Manager وارد کنید. پاسخ احراز هویت از Credential Manager شامل اطلاعات کاربر مانند آدرس ایمیل است و همچنین حساب پیش‌فرض برنامه را روی حساب انتخابی تنظیم می‌کند. در صورت نیاز، می توانید این حساب را در برنامه خود پیگیری کنید. درخواست مجوز بعدی از حساب به عنوان پیش فرض استفاده می کند و از مرحله انتخاب حساب در جریان مجوز عبور می کند. برای استفاده از یک حساب دیگر برای مجوز، به مجوز از یک حساب غیر پیش‌فرض مراجعه کنید.

  • در درخواست مجوز خود، علاوه بر دامنه‌هایی که می‌خواهید (مثلاً Drive scopeuserinfo ، profile و محدوده‌های openid را نیز بخواهید. پس از بازگرداندن رمز دسترسی، اطلاعات کاربر را با درخواست GET HTTP به نقطه پایانی اطلاعات کاربر OAuth (https://www.googleapis.com/oauth2/v3/userinfo) با استفاده از کتابخانه HTTP ترجیحی خود و شامل نشانه دسترسی که در هدر دریافت کرده بودید، دریافت کنید، معادل دستور curl زیر:

    curl -X GET \ "https://www.googleapis.com/oauth2/v1/userinfo?alt=json" \ -H "Authorization: Bearer $TOKEN"
    

    پاسخ، UserInfo است، محدود به محدوده‌های درخواستی، قالب‌بندی شده در JSON.

مجوز از یک حساب غیر پیش فرض

اگر از Credential Manager برای احراز هویت و اجرای AuthorizationClient.authorize() استفاده می‌کنید، حساب پیش‌فرض برنامه شما روی حسابی که کاربر شما انتخاب کرده است تنظیم می‌شود. این بدان معنی است که هر تماس بعدی برای مجوز از این حساب پیش فرض استفاده می کند. برای نمایش اجباری انتخابگر حساب، کاربر را از برنامه با استفاده از API clearCredentialState() از Credential Manager خارج کنید.

دسترسی مداوم به داده های کاربر را حفظ کنید

اگر نیاز به دسترسی به داده های کاربر از برنامه خود دارید، یک بار با AuthorizationClient.authorize() تماس بگیرید. در جلسات بعدی، و تا زمانی که مجوزهای اعطا شده توسط کاربر حذف نشده است، با همان روش تماس بگیرید تا برای دستیابی به اهداف خود، بدون هیچ گونه تعامل با کاربر، یک نشانه دسترسی دریافت کنید. از طرف دیگر، اگر شما نیاز به دسترسی به داده های کاربر در حالت آفلاین، از سرور باطن خود دارید، باید نوع دیگری از توکن به نام "Refresh Token" را درخواست کنید.

توکن های دسترسی عمداً به گونه ای طراحی شده اند که عمر کوتاهی داشته باشند و طول عمر آنها یک ساعت است. اگر یک نشانه دسترسی رهگیری یا به خطر بیفتد، پنجره اعتبار محدود آن سوء استفاده احتمالی را به حداقل می رساند. پس از انقضای آن، توکن نامعتبر می شود و هرگونه تلاش برای استفاده از آن توسط سرور منبع رد می شود. از آنجایی که توکن‌های دسترسی کوتاه‌مدت هستند، سرورها از نشانه‌های تازه‌سازی برای حفظ دسترسی مداوم به داده‌های کاربر استفاده می‌کنند. توکن‌های Refresh توکن‌هایی با طول عمر طولانی هستند که توسط یک کلاینت برای درخواست توکن دسترسی کوتاه مدت از سرور مجوز، زمانی که نشانه دسترسی قدیمی منقضی شده است، بدون هیچ گونه تعامل کاربر استفاده می‌شوند.

برای به دست آوردن یک نشانه بازخوانی، ابتدا باید یک کد تأیید اعتبار (یا کد مجوز) را در طول مرحله مجوز در برنامه خود با درخواست «دسترسی آفلاین» دریافت کنید، و سپس کد تأیید را با یک نشانه تازه سازی در سرور خود مبادله کنید. بسیار مهم است که توکن‌های تازه‌سازی با عمر طولانی را به صورت ایمن روی سرور خود ذخیره کنید، زیرا می‌توان از آن‌ها مکرراً برای به دست آوردن نشانه‌های دسترسی جدید استفاده کرد. بنابراین، به دلیل نگرانی های امنیتی، به شدت از ذخیره توکن های تازه سازی روی دستگاه خودداری می شود. در عوض، آنها باید در سرورهای پشتیبان برنامه ذخیره شوند، جایی که تبادل توکن دسترسی انجام می شود.

پس از ارسال کد احراز هویت به سرور پشتیبان برنامه شما، می‌توانید با دنبال کردن مراحل راهنمای مجوز حساب، آن را با یک نشانه دسترسی کوتاه مدت در سرور و یک توکن تازه‌سازی طولانی مدت تعویض کنید. این تبادل فقط باید در قسمت پشتی برنامه شما انجام شود.

کاتلین

// Ask for offline access during the first authorization request
val authorizationRequest = AuthorizationRequest.builder()
    .setRequestedScopes(requestedScopes)
    .requestOfflineAccess(serverClientId)
    .build()

Identity.getAuthorizationClient(activity)
    .authorize(authorizationRequest)
    .addOnSuccessListener { authorizationResult ->
        startAuthorizationIntent.launchIntentSenderRequest.Builder(
            pendingIntent!!.intentSender
        ).build()
    }
    .addOnFailureListener { e -> Log.e(TAG, "Failed to authorize", e) }

جاوا

// Ask for offline access during the first authorization request
AuthorizationRequest authorizationRequest = AuthorizationRequest.builder()
    .setRequestedScopes(requestedScopes)
    .requestOfflineAccess(serverClientId)
    .build();

Identity.getAuthorizationClient(getContext())
    .authorize(authorizationRequest)
    .addOnSuccessListener(authorizationResult -> {
        startAuthorizationIntent.launch(
            new IntentSenderRequest.Builder(
                authorizationResult.getPendingIntent().getIntentSender()
            ).build()
        );
    })
    .addOnFailureListener(e -> Log.e(TAG, "Failed to authorize"));

قطعه زیر فرض می کند که مجوز از یک قطعه شروع شده است.

کاتلین

private lateinit var startAuthorizationIntent: ActivityResultLauncher<IntentSenderRequest>

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?,
): View? {
    // ...
    startAuthorizationIntent =
        registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { activityResult ->
            try {
                val authorizationResult = Identity.getAuthorizationClient(requireContext())
                    .getAuthorizationResultFromIntent(activityResult.data)
                // short-lived access token
                accessToken = authorizationResult.accessToken
                // store the authorization code used for getting a refresh token safely to your app's backend server
                val authCode: String = authorizationResult.serverAuthCode
                storeAuthCodeSafely(authCode)
            } catch (e: ApiException) {
                // log exception
            }
        }
}

جاوا

private ActivityResultLauncher<IntentSenderRequest> startAuthorizationIntent;

@Override
public View onCreateView(
    @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    // ...
    startAuthorizationIntent =
        registerForActivityResult(
            new ActivityResultContracts.StartIntentSenderForResult(),
            activityResult -> {
                try {
                    AuthorizationResult authorizationResult =
                        Identity.getAuthorizationClient(requireActivity())
                            .getAuthorizationResultFromIntent(activityResult.getData());
                    // short-lived access token
                    accessToken = authorizationResult.getAccessToken();
                    // store the authorization code used for getting a refresh token safely to your app's backend server
                    String authCode = authorizationResult.getServerAuthCode()
                    storeAuthCodeSafely(authCode);
                } catch (ApiException e) {
                    // log exception
                }
            });
}