Spaces:
Running
Running
| from __future__ import annotations | |
| from typing import Dict, List, Optional, Tuple | |
| # Friendly Google OAuth scope map. | |
| # | |
| # ``SCOPE_MAP`` groups every Google OAuth scope under a human-friendly category | |
| # and gives each scope a short alias. Clients can request scopes by alias in | |
| # POST /google/oauth/auth-url instead of pasting long scope URIs, e.g. | |
| # ``["openid", "email", "gmail_full"]``. | |
| # | |
| # Generic full scope URIs are also accepted verbatim (the map is a convenience, | |
| # not a restriction). Validation only rejects empty/unknown scope names. | |
| SCOPE_MAP: Dict[str, Dict[str, str]] = { | |
| "authentication": { | |
| "openid": "openid", | |
| "email": "https://www.googleapis.com/auth/userinfo.email", | |
| "profile": "https://www.googleapis.com/auth/userinfo.profile", | |
| "userinfo_email": "https://www.googleapis.com/auth/userinfo.email", | |
| "userinfo_profile": "https://www.googleapis.com/auth/userinfo.profile", | |
| }, | |
| "gmail": { | |
| "gmail_full": "https://www.googleapis.com/auth/gmail.modify", | |
| "gmail_readonly": "https://www.googleapis.com/auth/gmail.readonly", | |
| "gmail_send": "https://www.googleapis.com/auth/gmail.send", | |
| "gmail_compose": "https://www.googleapis.com/auth/gmail.compose", | |
| "gmail_metadata": "https://www.googleapis.com/auth/gmail.metadata", | |
| "gmail_insert": "https://www.googleapis.com/auth/gmail.insert", | |
| "gmail_labels": "https://www.googleapis.com/auth/gmail.labels", | |
| "gmail_settings_basic": "https://www.googleapis.com/auth/gmail.settings.basic", | |
| "gmail_settings_sharing": "https://www.googleapis.com/auth/gmail.settings.sharing", | |
| "gmail_permanent_delete": "https://mail.google.com/", | |
| "gmail_addons_current_action_compose": "https://www.googleapis.com/auth/gmail.addons.current.action.compose", | |
| "gmail_addons_current_message_action": "https://www.googleapis.com/auth/gmail.addons.current.message.action", | |
| "gmail_addons_current_message_metadata": "https://www.googleapis.com/auth/gmail.addons.current.message.metadata", | |
| "gmail_addons_current_message_readonly": "https://www.googleapis.com/auth/gmail.addons.current.message.readonly", | |
| }, | |
| "google_sheets": { | |
| "sheets_readwrite": "https://www.googleapis.com/auth/spreadsheets", | |
| "sheets_readonly": "https://www.googleapis.com/auth/spreadsheets.readonly", | |
| "sheets_drive_file": "https://www.googleapis.com/auth/drive.file", | |
| "sheets_drive_readonly": "https://www.googleapis.com/auth/drive.readonly", | |
| "sheets_drive_full": "https://www.googleapis.com/auth/drive", | |
| }, | |
| "google_drive": { | |
| "drive_file": "https://www.googleapis.com/auth/drive.file", | |
| "drive_metadata_readonly": "https://www.googleapis.com/auth/drive.metadata.readonly", | |
| "drive_readonly": "https://www.googleapis.com/auth/drive.readonly", | |
| "drive_full": "https://www.googleapis.com/auth/drive", | |
| "drive_metadata": "https://www.googleapis.com/auth/drive.metadata", | |
| "drive_appdata": "https://www.googleapis.com/auth/drive.appdata", | |
| "drive_apps_readonly": "https://www.googleapis.com/auth/drive.apps.readonly", | |
| "drive_scripts": "https://www.googleapis.com/auth/drive.scripts", | |
| "drive_install": "https://www.googleapis.com/auth/drive.install", | |
| "drive_activity": "https://www.googleapis.com/auth/drive.activity", | |
| "drive_activity_readonly": "https://www.googleapis.com/auth/drive.activity.readonly", | |
| "drive_photos_readonly": "https://www.googleapis.com/auth/drive.photos.readonly", | |
| "drive_meet_readonly": "https://www.googleapis.com/auth/drive.meet.readonly", | |
| }, | |
| "google_docs": { | |
| "docs_readwrite": "https://www.googleapis.com/auth/documents", | |
| "docs_readonly": "https://www.googleapis.com/auth/documents.readonly", | |
| "docs_drive_file": "https://www.googleapis.com/auth/drive.file", | |
| "docs_drive_readonly": "https://www.googleapis.com/auth/drive.readonly", | |
| "docs_drive_full": "https://www.googleapis.com/auth/drive", | |
| }, | |
| "google_slides": { | |
| "slides_readwrite": "https://www.googleapis.com/auth/presentations", | |
| "slides_readonly": "https://www.googleapis.com/auth/presentations.readonly", | |
| "slides_drive_file": "https://www.googleapis.com/auth/drive.file", | |
| "slides_drive_readonly": "https://www.googleapis.com/auth/drive.readonly", | |
| "slides_drive_full": "https://www.googleapis.com/auth/drive", | |
| }, | |
| "google_calendar": { | |
| "calendar_full": "https://www.googleapis.com/auth/calendar", | |
| "calendar_readonly": "https://www.googleapis.com/auth/calendar.readonly", | |
| "calendar_events": "https://www.googleapis.com/auth/calendar.events", | |
| "calendar_events_readonly": "https://www.googleapis.com/auth/calendar.events.readonly", | |
| "calendar_events_owned": "https://www.googleapis.com/auth/calendar.events.owned", | |
| "calendar_events_owned_readonly": "https://www.googleapis.com/auth/calendar.events.owned.readonly", | |
| "calendar_events_public_readonly": "https://www.googleapis.com/auth/calendar.events.public.readonly", | |
| "calendar_events_freebusy": "https://www.googleapis.com/auth/calendar.events.freebusy", | |
| "calendar_freebusy": "https://www.googleapis.com/auth/calendar.freebusy", | |
| "calendar_settings_readonly": "https://www.googleapis.com/auth/calendar.settings.readonly", | |
| "calendar_calendarlist": "https://www.googleapis.com/auth/calendar.calendarlist", | |
| "calendar_calendarlist_readonly": "https://www.googleapis.com/auth/calendar.calendarlist.readonly", | |
| "calendar_calendars": "https://www.googleapis.com/auth/calendar.calendars", | |
| "calendar_calendars_readonly": "https://www.googleapis.com/auth/calendar.calendars.readonly", | |
| "calendar_acls": "https://www.googleapis.com/auth/calendar.acls", | |
| "calendar_acls_readonly": "https://www.googleapis.com/auth/calendar.acls.readonly", | |
| "calendar_app_created": "https://www.googleapis.com/auth/calendar.app.created", | |
| "calendar_addons_execute": "https://www.googleapis.com/auth/calendar.addons.execute", | |
| "calendar_addons_current_event_read": "https://www.googleapis.com/auth/calendar.addons.current.event.read", | |
| "calendar_addons_current_event_write": "https://www.googleapis.com/auth/calendar.addons.current.event.write", | |
| }, | |
| "google_contacts": { | |
| "contacts_full": "https://www.googleapis.com/auth/contacts", | |
| "contacts_readonly": "https://www.googleapis.com/auth/contacts.readonly", | |
| "contacts_other_readonly": "https://www.googleapis.com/auth/contacts.other.readonly", | |
| "contacts_directory_readonly": "https://www.googleapis.com/auth/directory.readonly", | |
| }, | |
| "google_tasks": { | |
| "tasks_readwrite": "https://www.googleapis.com/auth/tasks", | |
| "tasks_readonly": "https://www.googleapis.com/auth/tasks.readonly", | |
| }, | |
| "google_photos": { | |
| "photos_library_full": "https://www.googleapis.com/auth/photoslibrary", | |
| "photos_library_readonly": "https://www.googleapis.com/auth/photoslibrary.readonly", | |
| "photos_library_appendonly": "https://www.googleapis.com/auth/photoslibrary.appendonly", | |
| "photos_library_edit_appcreated": "https://www.googleapis.com/auth/photoslibrary.edit.appcreateddata", | |
| "photos_library_readonly_appcreated": "https://www.googleapis.com/auth/photoslibrary.readonly.appcreateddata", | |
| "photos_library_sharing": "https://www.googleapis.com/auth/photoslibrary.sharing", | |
| }, | |
| "google_chat": { | |
| "chat_messages": "https://www.googleapis.com/auth/chat.messages", | |
| "chat_messages_readonly": "https://www.googleapis.com/auth/chat.messages.readonly", | |
| "chat_spaces": "https://www.googleapis.com/auth/chat.spaces", | |
| "chat_spaces_readonly": "https://www.googleapis.com/auth/chat.spaces.readonly", | |
| "chat_import": "https://www.googleapis.com/auth/chat.import", | |
| "chat_apps_configuration": "https://www.googleapis.com/auth/chat.apps.configuration", | |
| "chat_apps_configuration_readonly": "https://www.googleapis.com/auth/chat.apps.configuration.readonly", | |
| "chat_apps_readonly": "https://www.googleapis.com/auth/chat.apps.readonly", | |
| "chat_pushes": "https://www.googleapis.com/auth/chat.pushes", | |
| "chat_meetings": "https://www.googleapis.com/auth/chat.meetings", | |
| "chat_meetings_readonly": "https://www.googleapis.com/auth/chat.meetings.readonly", | |
| }, | |
| "google_meet": { | |
| "meet_recordings_readonly": "https://www.googleapis.com/auth/drive.meet.readonly", | |
| }, | |
| "google_forms": { | |
| "forms_full": "https://www.googleapis.com/auth/forms", | |
| "forms_responses_readonly": "https://www.googleapis.com/auth/forms.responses.readonly", | |
| }, | |
| "google_classroom": { | |
| "classroom_courses": "https://www.googleapis.com/auth/classroom.courses", | |
| "classroom_courses_readonly": "https://www.googleapis.com/auth/classroom.courses.readonly", | |
| "classroom_rosters": "https://www.googleapis.com/auth/classroom.rosters", | |
| "classroom_rosters_readonly": "https://www.googleapis.com/auth/classroom.rosters.readonly", | |
| "classroom_profile_emails": "https://www.googleapis.com/auth/classroom.profile.emails", | |
| "classroom_profile_photos": "https://www.googleapis.com/auth/classroom.profile.photos", | |
| "classroom_announcements": "https://www.googleapis.com/auth/classroom.announcements", | |
| "classroom_announcements_readonly": "https://www.googleapis.com/auth/classroom.announcements.readonly", | |
| "classroom_topics": "https://www.googleapis.com/auth/classroom.topics", | |
| "classroom_topics_readonly": "https://www.googleapis.com/auth/classroom.topics.readonly", | |
| "classroom_coursework_students": "https://www.googleapis.com/auth/classroom.coursework.students", | |
| "classroom_coursework_students_readonly": "https://www.googleapis.com/auth/classroom.coursework.students.readonly", | |
| "classroom_coursework_me": "https://www.googleapis.com/auth/classroom.coursework.me", | |
| "classroom_coursework_me_readonly": "https://www.googleapis.com/auth/classroom.coursework.me.readonly", | |
| "classroom_courseworkmaterials": "https://www.googleapis.com/auth/classroom.courseworkmaterials", | |
| "classroom_courseworkmaterials_readonly": "https://www.googleapis.com/auth/classroom.courseworkmaterials.readonly", | |
| "classroom_guardianlinks_students": "https://www.googleapis.com/auth/classroom.guardianlinks.students", | |
| "classroom_guardianlinks_students_readonly": "https://www.googleapis.com/auth/classroom.guardianlinks.students.readonly", | |
| "classroom_student_submissions_me_readonly": "https://www.googleapis.com/auth/classroom.student-submissions.me.readonly", | |
| "classroom_student_submissions_students_readonly": "https://www.googleapis.com/auth/classroom.student-submissions.students.readonly", | |
| }, | |
| "google_analytics": { | |
| "analytics_full": "https://www.googleapis.com/auth/analytics", | |
| "analytics_readonly": "https://www.googleapis.com/auth/analytics.readonly", | |
| "analytics_edit": "https://www.googleapis.com/auth/analytics.edit", | |
| "analytics_manage_users": "https://www.googleapis.com/auth/analytics.manage.users", | |
| "analytics_manage_users_readonly": "https://www.googleapis.com/auth/analytics.manage.users.readonly", | |
| "analytics_manage_edit": "https://www.googleapis.com/auth/analytics.manage.edit", | |
| "analytics_manage_partners": "https://www.googleapis.com/auth/analytics.manage.partners", | |
| "analytics_provision": "https://www.googleapis.com/auth/analytics.provision", | |
| }, | |
| "google_ads": { | |
| "google_ads_full": "https://www.googleapis.com/auth/adwords", | |
| "admanager_readonly": "https://www.googleapis.com/auth/admanager.readonly", | |
| }, | |
| "google_cloud": { | |
| "bigquery_readonly": "https://www.googleapis.com/auth/bigquery.readonly", | |
| "bigquery_full": "https://www.googleapis.com/auth/bigquery", | |
| "bigquery_insertdata": "https://www.googleapis.com/auth/bigquery.insertdata", | |
| "cloud_platform": "https://www.googleapis.com/auth/cloud-platform", | |
| "cloud_platform_readonly": "https://www.googleapis.com/auth/cloud-platform.read-only", | |
| "devstorage_read_only": "https://www.googleapis.com/auth/devstorage.read_only", | |
| "devstorage_read_write": "https://www.googleapis.com/auth/devstorage.read_write", | |
| "devstorage_full_control": "https://www.googleapis.com/auth/devstorage.full_control", | |
| "compute_readonly": "https://www.googleapis.com/auth/compute.readonly", | |
| "compute_full": "https://www.googleapis.com/auth/compute", | |
| "datastore": "https://www.googleapis.com/auth/datastore", | |
| "sqlservice_admin": "https://www.googleapis.com/auth/sqlservice.admin", | |
| "vision": "https://www.googleapis.com/auth/cloud-vision", | |
| "translate": "https://www.googleapis.com/auth/cloud-translation", | |
| "language": "https://www.googleapis.com/auth/cloud-language", | |
| "genai": "https://www.googleapis.com/auth/genai", | |
| }, | |
| "google_maps": {}, | |
| "youtube": { | |
| "youtube_readonly": "https://www.googleapis.com/auth/youtube.readonly", | |
| "youtube_full": "https://www.googleapis.com/auth/youtube", | |
| "youtube_upload": "https://www.googleapis.com/auth/youtube.upload", | |
| "youtube_force_ssl": "https://www.googleapis.com/auth/youtube.force-ssl", | |
| "youtube_channel_memberships_creator": "https://www.googleapis.com/auth/youtube.channel-memberships.creator", | |
| "youtube_partner": "https://www.googleapis.com/auth/youtubepartner", | |
| "youtube_partner_channel_audit": "https://www.googleapis.com/auth/youtubepartner-channel-audit", | |
| }, | |
| "google_play": { | |
| "play_androidpublisher": "https://www.googleapis.com/auth/androidpublisher", | |
| "play_games": "https://www.googleapis.com/auth/playgames", | |
| "play_games_readonly": "https://www.googleapis.com/auth/playgames.readonly", | |
| }, | |
| "firebase": { | |
| "firebase_full": "https://www.googleapis.com/auth/firebase", | |
| "firebase_readonly": "https://www.googleapis.com/auth/firebase.readonly", | |
| "firebase_database": "https://www.googleapis.com/auth/firebase.database", | |
| "firebase_database_readonly": "https://www.googleapis.com/auth/firebase.database.readonly", | |
| "firebase_messaging": "https://www.googleapis.com/auth/firebase.messaging", | |
| "firebase_rules": "https://www.googleapis.com/auth/firebase.rules", | |
| "firebase_rules_readonly": "https://www.googleapis.com/auth/firebase.rules.readonly", | |
| "firebase_analytics": "https://www.googleapis.com/auth/firebase.analytics", | |
| }, | |
| "admin_sdk": { | |
| "admin_directory_user_readonly": "https://www.googleapis.com/auth/admin.directory.user.readonly", | |
| "admin_directory_user": "https://www.googleapis.com/auth/admin.directory.user", | |
| "admin_directory_group_readonly": "https://www.googleapis.com/auth/admin.directory.group.readonly", | |
| "admin_directory_group": "https://www.googleapis.com/auth/admin.directory.group", | |
| "admin_directory_group_member_readonly": "https://www.googleapis.com/auth/admin.directory.group.member.readonly", | |
| "admin_directory_group_member": "https://www.googleapis.com/auth/admin.directory.group.member", | |
| "admin_directory_device_chromeos_readonly": "https://www.googleapis.com/auth/admin.directory.device.chromeos.readonly", | |
| "admin_directory_device_chromeos": "https://www.googleapis.com/auth/admin.directory.device.chromeos", | |
| "admin_directory_orgunit_readonly": "https://www.googleapis.com/auth/admin.directory.orgunit.readonly", | |
| "admin_directory_orgunit": "https://www.googleapis.com/auth/admin.directory.orgunit", | |
| "admin_directory_rolemanagement_readonly": "https://www.googleapis.com/auth/admin.directory.rolemanagement.readonly", | |
| "admin_directory_rolemanagement": "https://www.googleapis.com/auth/admin.directory.rolemanagement", | |
| "admin_directory_domain_readonly": "https://www.googleapis.com/auth/admin.directory.domain.readonly", | |
| "admin_directory_customer_readonly": "https://www.googleapis.com/auth/admin.directory.customer.readonly", | |
| "admin_directory_userschema_readonly": "https://www.googleapis.com/auth/admin.directory.userschema.readonly", | |
| "admin_reports_audit_readonly": "https://www.googleapis.com/auth/admin.reports.audit.readonly", | |
| "admin_reports_usage_readonly": "https://www.googleapis.com/auth/admin.reports.usage.readonly", | |
| "admin_datatransfer": "https://www.googleapis.com/auth/admin.datatransfer", | |
| }, | |
| "people_api": { | |
| "people_contacts_full": "https://www.googleapis.com/auth/contacts", | |
| "people_contacts_readonly": "https://www.googleapis.com/auth/contacts.readonly", | |
| "people_othercontacts": "https://www.googleapis.com/auth/people.otherContacts", | |
| "people_othercontacts_readonly": "https://www.googleapis.com/auth/people.otherContacts.readonly", | |
| "people_email_readonly": "https://www.googleapis.com/auth/people.emailAddresses.readonly", | |
| "people_phone_readonly": "https://www.googleapis.com/auth/people.phoneNumbers.readonly", | |
| "people_contactinfo": "https://www.googleapis.com/auth/people.contactInfo", | |
| "people_contactinfo_readonly": "https://www.googleapis.com/auth/people.contactInfo.readonly", | |
| "people_directory_readonly": "https://www.googleapis.com/auth/people.directory.readonly", | |
| "user_emails_readonly": "https://www.googleapis.com/auth/user.emails.readonly", | |
| "user_phonenumbers_readonly": "https://www.googleapis.com/auth/user.phonenumbers.readonly", | |
| }, | |
| "business_profile": { | |
| "business_manage": "https://www.googleapis.com/auth/business.manage", | |
| }, | |
| "other": { | |
| "openid": "openid", | |
| "email": "https://www.googleapis.com/auth/userinfo.email", | |
| "profile": "https://www.googleapis.com/auth/userinfo.profile", | |
| }, | |
| } | |
| CATEGORY_ORDER: List[str] = list(SCOPE_MAP.keys()) | |
| ALIAS_TO_URI: Dict[str, str] = { | |
| alias: uri | |
| for category in SCOPE_MAP.values() | |
| for alias, uri in category.items() | |
| } | |
| ALL_ALIASES: List[str] = sorted(ALIAS_TO_URI) | |
| # Scope containment for UX normalization. When a broader scope is already in | |
| # the request, narrower scopes of the same family are redundant and are dropped | |
| # so the Google consent screen stays minimal. Only documented supersets are | |
| # listed; generic/unknown scopes are never touched. | |
| _SCOPE_INCLUDED_BY: Dict[str, Tuple[str, ...]] = { | |
| # --- Gmail family --- | |
| "https://www.googleapis.com/auth/gmail.metadata": ( | |
| "https://www.googleapis.com/auth/gmail.readonly", | |
| "https://www.googleapis.com/auth/gmail.modify", | |
| "https://mail.google.com/", | |
| ), | |
| "https://www.googleapis.com/auth/gmail.readonly": ( | |
| "https://www.googleapis.com/auth/gmail.modify", | |
| "https://mail.google.com/", | |
| ), | |
| "https://www.googleapis.com/auth/gmail.labels": ( | |
| "https://www.googleapis.com/auth/gmail.modify", | |
| "https://mail.google.com/", | |
| ), | |
| "https://www.googleapis.com/auth/gmail.send": ( | |
| "https://www.googleapis.com/auth/gmail.compose", | |
| "https://www.googleapis.com/auth/gmail.modify", | |
| "https://mail.google.com/", | |
| ), | |
| "https://www.googleapis.com/auth/gmail.compose": ( | |
| "https://www.googleapis.com/auth/gmail.modify", | |
| "https://mail.google.com/", | |
| ), | |
| "https://www.googleapis.com/auth/gmail.modify": ( | |
| "https://mail.google.com/", | |
| ), | |
| # --- Sheets / Drive family --- | |
| "https://www.googleapis.com/auth/spreadsheets.readonly": ( | |
| "https://www.googleapis.com/auth/spreadsheets", | |
| ), | |
| "https://www.googleapis.com/auth/drive.metadata.readonly": ( | |
| "https://www.googleapis.com/auth/drive.readonly", | |
| "https://www.googleapis.com/auth/drive.metadata", | |
| "https://www.googleapis.com/auth/drive", | |
| ), | |
| "https://www.googleapis.com/auth/drive.metadata": ( | |
| "https://www.googleapis.com/auth/drive", | |
| ), | |
| "https://www.googleapis.com/auth/drive.readonly": ( | |
| "https://www.googleapis.com/auth/drive", | |
| ), | |
| "https://www.googleapis.com/auth/drive.file": ( | |
| "https://www.googleapis.com/auth/drive", | |
| ), | |
| "https://www.googleapis.com/auth/drive.appdata": ( | |
| "https://www.googleapis.com/auth/drive", | |
| ), | |
| "https://www.googleapis.com/auth/drive.scripts": ( | |
| "https://www.googleapis.com/auth/drive", | |
| ), | |
| "https://www.googleapis.com/auth/drive.activity.readonly": ( | |
| "https://www.googleapis.com/auth/drive.activity", | |
| "https://www.googleapis.com/auth/drive", | |
| ), | |
| "https://www.googleapis.com/auth/drive.activity": ( | |
| "https://www.googleapis.com/auth/drive", | |
| ), | |
| "https://www.googleapis.com/auth/drive.photos.readonly": ( | |
| "https://www.googleapis.com/auth/drive", | |
| ), | |
| "https://www.googleapis.com/auth/drive.meet.readonly": ( | |
| "https://www.googleapis.com/auth/drive", | |
| ), | |
| # --- Calendar family --- | |
| "https://www.googleapis.com/auth/calendar.events.readonly": ( | |
| "https://www.googleapis.com/auth/calendar.events", | |
| "https://www.googleapis.com/auth/calendar.readonly", | |
| "https://www.googleapis.com/auth/calendar", | |
| ), | |
| "https://www.googleapis.com/auth/calendar.events": ( | |
| "https://www.googleapis.com/auth/calendar", | |
| ), | |
| "https://www.googleapis.com/auth/calendar.events.owned.readonly": ( | |
| "https://www.googleapis.com/auth/calendar.events.owned", | |
| "https://www.googleapis.com/auth/calendar.events.readonly", | |
| "https://www.googleapis.com/auth/calendar.readonly", | |
| "https://www.googleapis.com/auth/calendar", | |
| ), | |
| "https://www.googleapis.com/auth/calendar.events.owned": ( | |
| "https://www.googleapis.com/auth/calendar.events", | |
| "https://www.googleapis.com/auth/calendar", | |
| ), | |
| "https://www.googleapis.com/auth/calendar.events.public.readonly": ( | |
| "https://www.googleapis.com/auth/calendar.readonly", | |
| "https://www.googleapis.com/auth/calendar", | |
| ), | |
| "https://www.googleapis.com/auth/calendar.events.freebusy": ( | |
| "https://www.googleapis.com/auth/calendar.freebusy", | |
| "https://www.googleapis.com/auth/calendar", | |
| ), | |
| "https://www.googleapis.com/auth/calendar.readonly": ( | |
| "https://www.googleapis.com/auth/calendar", | |
| ), | |
| "https://www.googleapis.com/auth/calendar.freebusy": ( | |
| "https://www.googleapis.com/auth/calendar", | |
| ), | |
| "https://www.googleapis.com/auth/calendar.settings.readonly": ( | |
| "https://www.googleapis.com/auth/calendar", | |
| ), | |
| "https://www.googleapis.com/auth/calendar.calendarlist.readonly": ( | |
| "https://www.googleapis.com/auth/calendar.calendarlist", | |
| "https://www.googleapis.com/auth/calendar", | |
| ), | |
| "https://www.googleapis.com/auth/calendar.calendarlist": ( | |
| "https://www.googleapis.com/auth/calendar", | |
| ), | |
| "https://www.googleapis.com/auth/calendar.calendars.readonly": ( | |
| "https://www.googleapis.com/auth/calendar.calendars", | |
| "https://www.googleapis.com/auth/calendar", | |
| ), | |
| "https://www.googleapis.com/auth/calendar.calendars": ( | |
| "https://www.googleapis.com/auth/calendar", | |
| ), | |
| "https://www.googleapis.com/auth/calendar.acls.readonly": ( | |
| "https://www.googleapis.com/auth/calendar.acls", | |
| "https://www.googleapis.com/auth/calendar", | |
| ), | |
| "https://www.googleapis.com/auth/calendar.acls": ( | |
| "https://www.googleapis.com/auth/calendar", | |
| ), | |
| # --- Contacts / People family --- | |
| "https://www.googleapis.com/auth/contacts.readonly": ( | |
| "https://www.googleapis.com/auth/contacts", | |
| ), | |
| "https://www.googleapis.com/auth/contacts.other.readonly": ( | |
| "https://www.googleapis.com/auth/contacts", | |
| ), | |
| # --- Tasks family --- | |
| "https://www.googleapis.com/auth/tasks.readonly": ( | |
| "https://www.googleapis.com/auth/tasks", | |
| ), | |
| # --- YouTube family --- | |
| "https://www.googleapis.com/auth/youtube.readonly": ( | |
| "https://www.googleapis.com/auth/youtube.force-ssl", | |
| "https://www.googleapis.com/auth/youtube", | |
| ), | |
| "https://www.googleapis.com/auth/youtube.upload": ( | |
| "https://www.googleapis.com/auth/youtube", | |
| ), | |
| "https://www.googleapis.com/auth/youtube.force-ssl": ( | |
| "https://www.googleapis.com/auth/youtube", | |
| ), | |
| "https://www.googleapis.com/auth/youtube.channel-memberships.creator": ( | |
| "https://www.googleapis.com/auth/youtube", | |
| ), | |
| "https://www.googleapis.com/auth/youtubepartner-channel-audit": ( | |
| "https://www.googleapis.com/auth/youtubepartner", | |
| ), | |
| # --- Forms family --- | |
| "https://www.googleapis.com/auth/forms.responses.readonly": ( | |
| "https://www.googleapis.com/auth/forms", | |
| ), | |
| # --- Analytics family --- | |
| "https://www.googleapis.com/auth/analytics.readonly": ( | |
| "https://www.googleapis.com/auth/analytics.edit", | |
| "https://www.googleapis.com/auth/analytics", | |
| ), | |
| "https://www.googleapis.com/auth/analytics.edit": ( | |
| "https://www.googleapis.com/auth/analytics", | |
| ), | |
| "https://www.googleapis.com/auth/analytics.manage.users.readonly": ( | |
| "https://www.googleapis.com/auth/analytics.manage.users", | |
| ), | |
| # --- BigQuery / Cloud family --- | |
| "https://www.googleapis.com/auth/bigquery.readonly": ( | |
| "https://www.googleapis.com/auth/bigquery", | |
| ), | |
| "https://www.googleapis.com/auth/bigquery.insertdata": ( | |
| "https://www.googleapis.com/auth/bigquery", | |
| ), | |
| "https://www.googleapis.com/auth/cloud-platform.read-only": ( | |
| "https://www.googleapis.com/auth/cloud-platform", | |
| ), | |
| "https://www.googleapis.com/auth/devstorage.read_only": ( | |
| "https://www.googleapis.com/auth/devstorage.read_write", | |
| "https://www.googleapis.com/auth/devstorage.full_control", | |
| ), | |
| "https://www.googleapis.com/auth/devstorage.read_write": ( | |
| "https://www.googleapis.com/auth/devstorage.full_control", | |
| ), | |
| # --- Admin SDK family --- | |
| "https://www.googleapis.com/auth/admin.directory.user.readonly": ( | |
| "https://www.googleapis.com/auth/admin.directory.user", | |
| ), | |
| "https://www.googleapis.com/auth/admin.directory.group.readonly": ( | |
| "https://www.googleapis.com/auth/admin.directory.group", | |
| ), | |
| "https://www.googleapis.com/auth/admin.directory.group.member.readonly": ( | |
| "https://www.googleapis.com/auth/admin.directory.group.member", | |
| ), | |
| "https://www.googleapis.com/auth/admin.directory.device.chromeos.readonly": ( | |
| "https://www.googleapis.com/auth/admin.directory.device.chromeos", | |
| ), | |
| "https://www.googleapis.com/auth/admin.directory.orgunit.readonly": ( | |
| "https://www.googleapis.com/auth/admin.directory.orgunit", | |
| ), | |
| "https://www.googleapis.com/auth/admin.directory.rolemanagement.readonly": ( | |
| "https://www.googleapis.com/auth/admin.directory.rolemanagement", | |
| ), | |
| } | |
| def is_generic_scope(value: str) -> bool: | |
| """True when the value is a full scope URI the client typed directly.""" | |
| return value.startswith(("http://", "https://", "mail.google.com/")) or value == "openid" | |
| def _normalize_scopes(resolved: List[str]) -> Tuple[List[str], List[str]]: | |
| """Drop scopes already covered by a broader scope in the same request. | |
| Returns ``(kept_scopes, dropped_scopes)``. Order of the remaining scopes is | |
| preserved. Unknown/generic scopes (not in the containment table) are never | |
| dropped. | |
| """ | |
| present = set(resolved) | |
| kept: List[str] = [] | |
| dropped: List[str] = [] | |
| for uri in resolved: | |
| supersets = _SCOPE_INCLUDED_BY.get(uri, ()) | |
| if any(superset in present for superset in supersets): | |
| dropped.append(uri) | |
| continue | |
| kept.append(uri) | |
| return kept, dropped | |
| def resolve_scope_list( | |
| scopes: Optional[List[str]], *, normalize: bool = True | |
| ) -> Tuple[List[str], List[str], List[str]]: | |
| """Resolve a list of scope aliases / URIs into a de-duplicated list of URIs. | |
| Returns ``(resolved_uris, errors, dropped_scopes)``. Generic full scope URIs | |
| are accepted verbatim; only empty or unknown scope names produce errors. | |
| When ``normalize`` is true, narrower scopes that are fully covered by a | |
| broader scope already present are dropped (best UX: minimal consent screen). | |
| """ | |
| resolved: List[str] = [] | |
| errors: List[str] = [] | |
| dropped: List[str] = [] | |
| seen = set() | |
| for raw in scopes or []: | |
| value = str(raw or "").strip() | |
| if not value: | |
| errors.append("Scope name is missing (empty entry).") | |
| continue | |
| if value in ALIAS_TO_URI: | |
| uri = ALIAS_TO_URI[value] | |
| elif is_generic_scope(value): | |
| uri = value | |
| else: | |
| errors.append( | |
| f"Unknown scope '{value}'. It is not a known alias and not a full scope URI. " | |
| f"Valid aliases: {', '.join(ALL_ALIASES)}" | |
| ) | |
| continue | |
| if uri not in seen: | |
| seen.add(uri) | |
| resolved.append(uri) | |
| if normalize: | |
| resolved, dropped = _normalize_scopes(resolved) | |
| return resolved, errors, dropped | |