अनुमति और पुष्टि की मदद से, Secure Data Connect को सुरक्षित करना

Firebase Data Connect, क्लाइंट-साइड पर मज़बूत सुरक्षा उपलब्ध कराता है. इसके लिए, ये सुविधाएं दी जाती हैं:

  • मोबाइल और वेब क्लाइंट के लिए अनुमति
  • क्वेरी और म्यूटेशन के लेवल पर, अनुमति देने से जुड़े कंट्रोल
  • Firebase App Check की मदद से ऐप्लिकेशन की पुष्टि करना.

Data Connect इन सुविधाओं के ज़रिए, सुरक्षा को और बेहतर बनाता है:

  • सर्वर-साइड से अनुमति लेना
  • IAM की मदद से, Firebase प्रोजेक्ट और Cloud SQL उपयोगकर्ता की सुरक्षा.

क्लाइंट की क्वेरी और म्यूटेशन को अनुमति देना

Data Connect को Firebase Authentication के साथ पूरी तरह से इंटिग्रेट किया गया है. इसलिए, आपके पास उन उपयोगकर्ताओं के बारे में ज़्यादा जानकारी होती है जो आपके डेटा को ऐक्सेस कर रहे हैं (पुष्टि). इस जानकारी का इस्तेमाल करके, यह तय किया जा सकता है कि वे उपयोगकर्ता कौनसे डेटा को ऐक्सेस कर सकते हैं (अनुमति).

Data Connect, क्वेरी और म्यूटेशन के लिए @auth डायरेक्टिव उपलब्ध कराता है. इसकी मदद से, ऑपरेशन को अनुमति देने के लिए ज़रूरी पुष्टि का लेवल सेट किया जा सकता है. इस गाइड में, @auth डायरेक्टिव के बारे में बताया गया है. साथ ही, इसके उदाहरण भी दिए गए हैं.

इसके अलावा, Data Connect म्यूटेशन में एम्बेड की गई क्वेरी को लागू करने की सुविधा देता है. इससे, आपको अपने डेटाबेस में सेव किए गए अतिरिक्त अनुमति मानदंड वापस पाने में मदद मिलती है. साथ ही, @check के निर्देशों में उन मानदंडों का इस्तेमाल करके यह तय किया जा सकता है कि म्यूटेशन को शामिल करने की अनुमति है या नहीं. अनुमति देने के इस मामले में, @redact डायरेक्टिव की मदद से यह कंट्रोल किया जा सकता है कि क्वेरी के नतीजे, वायर प्रोटोकॉल में क्लाइंट को दिखाए जाएं या नहीं. साथ ही, जनरेट किए गए एसडीके में एम्बेड की गई क्वेरी को शामिल न किया जाए. इन डायरेक्टिव के बारे में जानकारी और उदाहरण देखें.

@auth डायरेक्टिव को समझना

@auth डायरेक्टिव को पैरामीटर के तौर पर इस्तेमाल किया जा सकता है. इससे, ऐक्सेस के कई सामान्य मामलों को कवर करने वाले, ऐक्सेस के कई प्रीसेट लेवल में से किसी एक को फ़ॉलो किया जा सकता है. ये लेवल, PUBLIC (जिसमें सभी क्लाइंट को क्वेरी और बदलाव करने की अनुमति होती है. इसके लिए, किसी भी तरह की पुष्टि करने की ज़रूरत नहीं होती) से लेकर NO_ACCESS (जिसमें Firebase Admin SDK का इस्तेमाल करने वाले, खास अधिकार वाले सर्वर एनवायरमेंट के बाहर क्वेरी और बदलाव करने की अनुमति नहीं होती) तक होते हैं. इनमें से हर लेवल, Firebase Authentication के दिए गए पुष्टि करने के फ़्लो से जुड़ा होता है.

लेवल परिभाषा
PUBLIC इस ऑपरेशन को कोई भी व्यक्ति, पुष्टि किए बिना या पुष्टि करके पूरा कर सकता है.
PUBLIC इस ऑपरेशन को कोई भी व्यक्ति, पुष्टि किए बिना या पुष्टि करके पूरा कर सकता है.
USER_ANON पहचाना गया कोई भी उपयोगकर्ता, क्वेरी या म्यूटेशन कर सकता है. इसमें वे उपयोगकर्ता भी शामिल हैं जिन्होंने Firebase Authentication के साथ गुमनाम तरीके से लॉग इन किया है.
USER Firebase Authentication से लॉग इन करने वाले किसी भी उपयोगकर्ता के पास क्वेरी या म्यूटेशन करने का अधिकार होता है. हालांकि, पहचान ज़ाहिर किए बिना साइन इन करने वाले उपयोगकर्ताओं के पास यह अधिकार नहीं होता.
USER_EMAIL_VERIFIED जिस उपयोगकर्ता ने पुष्टि किए गए ईमेल पते से Firebase Authentication में लॉग इन किया है उसके पास क्वेरी या म्यूटेशन करने की अनुमति होती है.
NO_ACCESS इस कार्रवाई को Admin SDK के संदर्भ के बाहर नहीं किया जा सकता.

इन प्रीसेट ऐक्सेस लेवल का इस्तेमाल करके, @auth डायरेक्टिव में जटिल और भरोसेमंद अनुमति की जांच तय की जा सकती है. इसके लिए, where फ़िल्टर और सर्वर पर जांचे गए Common Expression Language (CEL) एक्सप्रेशन का इस्तेमाल करें.

अनुमति देने से जुड़ी सामान्य स्थितियों को लागू करने के लिए, @auth डायरेक्टिव का इस्तेमाल करना

प्रीसेट ऐक्सेस लेवल, अनुमति देने के लिए शुरुआती बिंदु होते हैं.

USER ऐक्सेस लेवल, शुरुआत करने के लिए सबसे ज़्यादा इस्तेमाल किया जाने वाला बेसिक लेवल है.

पूरी तरह से सुरक्षित ऐक्सेस, USER लेवल के साथ-साथ फ़िल्टर और एक्सप्रेशन पर आधारित होगा. ये फ़िल्टर और एक्सप्रेशन, उपयोगकर्ता के एट्रिब्यूट, संसाधन के एट्रिब्यूट, भूमिकाओं, और अन्य जांचों की पुष्टि करते हैं. USER_ANON और USER_EMAIL_VERIFIED लेवल, USER केस के वैरिएशन हैं.

एक्सप्रेशन सिंटैक्स की मदद से, डेटा का आकलन किया जा सकता है. इसके लिए, auth ऑब्जेक्ट का इस्तेमाल किया जाता है. यह ऑब्जेक्ट, कार्रवाइयों के साथ पास किए गए पुष्टि करने के डेटा को दिखाता है. इसमें पुष्टि करने वाले टोकन में मौजूद स्टैंडर्ड डेटा और टोकन में मौजूद कस्टम डेटा, दोनों शामिल होते हैं. auth ऑब्जेक्ट में उपलब्ध फ़ील्ड की सूची के लिए, रेफ़रंस सेक्शन देखें.

हालांकि, कुछ मामलों में PUBLIC ऐक्सेस लेवल से शुरुआत करना सही होता है. फिर से बता दें कि ऐक्सेस लेवल हमेशा शुरुआती बिंदु होता है. साथ ही, बेहतर सुरक्षा के लिए अतिरिक्त फ़िल्टर और एक्सप्रेशन की ज़रूरत होती है.

इस गाइड में, USER और PUBLIC को बनाने के तरीके के उदाहरण दिए गए हैं.

उदाहरण के तौर पर दिया गया एक टेंप्लेट

सबसे सही तरीके के इन उदाहरणों में, ब्लॉगिंग प्लैटफ़ॉर्म के लिए इस स्कीमा का इस्तेमाल किया गया है. इस प्लैटफ़ॉर्म पर कुछ कॉन्टेंट को ऐक्सेस करने के लिए, पेमेंट प्लान लेना ज़रूरी है.

ऐसा प्लैटफ़ॉर्म, Users औरPosts को मॉडल कर सकता है.

type User @table(key: "uid") {
  uid: String!
  name: String
  birthday: Date
  createdAt: Timestamp! @default(expr: "request.time")
}

type Post @table {
  author: User!
  text: String!
  # "one of 'draft', 'public', or 'pro'"
  visibility: String! @default(value: "draft")
  # "the time at which the post should be considered published. defaults to
  # immediately"
  publishedAt: Timestamp! @default(expr: "request.time")
  createdAt: Timestamp! @default(expr: "request.time")
  updatedAt: Timestamp! @default(expr: "request.time")
}

उपयोगकर्ता के स्वामित्व वाले संसाधन

Firebase का सुझाव है कि आप ऐसे फ़िल्टर और एक्सप्रेशन लिखें जो किसी संसाधन पर उपयोगकर्ता के मालिकाना हक की जांच करें. इन मामलों में, Posts के मालिकाना हक की जांच करें.

यहां दिए गए उदाहरणों में, एक्सप्रेशन का इस्तेमाल करके, पुष्टि करने वाले टोकन से मिले डेटा को पढ़ा और उसकी तुलना की गई है. आम तौर पर, authorUid में सेव किए गए where: {authorUid: {eq_expr: "auth.uid"}} की तुलना, पुष्टि करने वाले टोकन में पास किए गए auth.uid (User-ID) से की जाती है.

बनाएं

अनुमति देने की इस प्रोसेस में, हर नए Post में auth.uid को auth टोकन से authorUid फ़ील्ड के तौर पर जोड़ा जाता है, ताकि अनुमति देने की बाद की जांचों में तुलना की जा सके.

# Create a new post as the current user
mutation CreatePost($text: String!, $visibility: String) @auth(level: USER) {
  post_insert(data: {
    # set the author's uid to the current user uid
    authorUid_expr: "auth.uid"
    text: $text
    visibility: $visibility
  })
}
अपडेट करें

जब कोई क्लाइंट किसी Post को अपडेट करने की कोशिश करता है, तब पास किए गए auth.uid की तुलना, सेव किए गए authorUid से की जा सकती है.

# Update one of the current user's posts
mutation UpdatePost($id: UUID!, $text: String, $visibility: String) @auth(level:USER) {
  post_update(
    # only update posts whose author is the current user
    first: { where: {
      id: {eq: $id}
      authorUid: {eq_expr: "auth.uid"}
    }}
    data: {
      text: $text
      visibility: $visibility
      # insert the current server time for updatedAt
      updatedAt_expr: "request.time"
    }
  )
}
मिटाएं

मिटाने की कार्रवाइयों को अनुमति देने के लिए, इसी तकनीक का इस्तेमाल किया जाता है.

# Delete one of the current user's posts
mutation DeletePost($id: UUID!) @auth(level: USER) {
  post_delete(
    # only delete posts whose author is the current user
    first: { where: {
      id: {eq: $id}
      authorUid: {eq_expr: "auth.uid"}
    }}
  )
}
# Common display information for a post
fragment DisplayPost on Post {
  id, text, createdAt, updatedAt
  author { uid, name }
}
सूची
# List all posts belonging to the current user
query ListMyPosts @auth(level: USER) {
  posts(where: {
    userUid: {eq_expr: "auth.uid"}
  }) {
    # See the fragment above
    ...DisplayPost
    # also show visibility since it is user-controlled
    visibility
  }
}
पाएं
# Get a post only if it belongs to the current user
query GetMyPost($id: UUID!) @auth(level: USER) {
  post(key: {id: $id},
    first: {where: {
      id: {eq: $id}
      authorUid: {eq_expr: "auth.uid"}}
      }}, {
      # See the fragment above
      ...DisplayPost
      # also show visibility since it is user-controlled
      visibility
  }
}

डेटा फ़िल्टर करना

Data Connect का अनुमति देने वाला सिस्टम, आपको बेहतर फ़िल्टर लिखने की सुविधा देता है. इन फ़िल्टर को, PUBLIC जैसे पहले से सेट किए गए ऐक्सेस लेवल के साथ-साथ, अनुमति देने वाले टोकन से मिले डेटा का इस्तेमाल करके भी लिखा जा सकता है.

अनुमति देने वाला सिस्टम, आपको सिर्फ़ एक्सप्रेशन इस्तेमाल करने की सुविधा भी देता है. इसके लिए, आपको बुनियादी ऐक्सेस लेवल की ज़रूरत नहीं होती. यहां दिए गए कुछ उदाहरणों में यह दिखाया गया है.

संसाधन एट्रिब्यूट के हिसाब से फ़िल्टर करना

यहां अनुमति, पुष्टि करने वाले टोकन के आधार पर नहीं दी जाती है, क्योंकि सुरक्षा का बुनियादी स्तर PUBLIC पर सेट है. हालांकि, हम अपने डेटाबेस में मौजूद रिकॉर्ड को सार्वजनिक तौर पर ऐक्सेस करने के लिए सेट कर सकते हैं. मान लें कि हमारे डेटाबेस में Post रिकॉर्ड हैं और उनमें से visibility को "सार्वजनिक" के तौर पर सेट किया गया है.

# List all posts marked as 'public' visibility
query ListPublicPosts @auth(level: PUBLIC) {
  posts(where: {
    # Test that visibility is "public"
    visibility: {eq: "public"}
    # Only display articles that are already published
    publishedAt: {lt_expr: "request.time"}
  }) {
    # see the fragment above
    ...DisplayPost
  }
}
उपयोगकर्ता के दावों के हिसाब से फ़िल्टर करना

यहां मान लें कि आपने कस्टम उपयोगकर्ता दावे सेट अप किए हैं. ये दावे, आपके ऐप्लिकेशन के "प्रो" प्लान में उपयोगकर्ताओं की पहचान करने के लिए, ऑथराइज़ेशन टोकन में पास होते हैं. इन्हें ऑथराइज़ेशन टोकन में auth.token.plan फ़ील्ड के साथ फ़्लैग किया जाता है. इस फ़ील्ड के हिसाब से, आपके एक्सप्रेशन की जांच की जा सकती है.

# List all public or pro posts, only permitted if user has "pro" plan claim
query ProListPosts @auth(expr: "auth.token.plan == 'pro'") {
  posts(where: {
    # display both public posts and "pro" posts
    visibility: {in: ['public', 'pro']},
    # only display articles that are already published
    publishedAt: {lt_expr: "request.time"},
  }) {
    # see the fragment above
    ...DisplayPost
    # show visibility so pro users can see which posts are pro\
    visibility
  }
}
क्रम और सीमा के हिसाब से फ़िल्टर करना

इसके अलावा, हो सकता है कि आपने Post रिकॉर्ड में visibility सेट किया हो, ताकि यह पता चल सके कि "प्रो" उपयोगकर्ताओं के लिए कौन-सा कॉन्टेंट उपलब्ध है. हालांकि, डेटा की झलक या टीज़र लिस्टिंग के लिए, दिखाए गए रिकॉर्ड की संख्या को और कम करें.

# Show 2 oldest Pro post as a preview
query ProTeaser @auth(level: USER) {
  posts(
    where: {
      # show only pro posts
      visibility: {eq: "pro"}
      # that have already been published more than 30 days ago
      publishedAt: {lt_time: {now: true, sub: {days: 30}}}
    },
    # order by publish time
    orderBy: [{publishedAt: DESC}],
    # only return two posts
    limit: 2
  ) {
    # See the fragment above
    ...DisplayPost
  }
}
भूमिका के हिसाब से फ़िल्टर करना

अगर आपके कस्टम दावे में admin भूमिका तय की गई है, तो इसके मुताबिक कार्रवाइयों की जांच की जा सकती है और उन्हें अनुमति दी जा सकती है.

# List all posts unconditionally iff the current user has an admin claim
query AdminListPosts @auth(expr: "auth.token.admin == true") {
  posts { ...DisplayPost }
}

अनुमति से जुड़ा डेटा देखने के लिए, @check और @redact डायरेक्टिव जोड़ें

अनुमति देने के सामान्य इस्तेमाल के उदाहरण में, आपकी अनुमति से जुड़ी कस्टम भूमिकाओं को आपके डेटाबेस में सेव करना शामिल है. उदाहरण के लिए, खास अनुमतियों वाली टेबल में. साथ ही, उन भूमिकाओं का इस्तेमाल करके, डेटा बनाने, अपडेट करने या मिटाने के लिए म्यूटेशन को अनुमति देना शामिल है.

अनुमति से जुड़े डेटा लुकअप का इस्तेमाल करके, userID के आधार पर भूमिकाओं के लिए क्वेरी की जा सकती है. साथ ही, सीईएल एक्सप्रेशन का इस्तेमाल करके यह तय किया जा सकता है कि म्यूटेशन को अनुमति मिली है या नहीं. उदाहरण के लिए, आपको ऐसा UpdateMovieTitle म्यूटेशन लिखना पड़ सकता है जिससे अनुमति पा चुका क्लाइंट, फ़िल्मों के टाइटल अपडेट कर सके.

इस चर्चा के बाकी हिस्से के लिए, मान लें कि फ़िल्म की समीक्षा करने वाले ऐप्लिकेशन का डेटाबेस, MoviePermission टेबल में अनुमति की भूमिका को सेव करता है.

# MoviePermission
# Suppose a user has an authorization role with respect to records in the Movie table
type MoviePermission @table(key: ["movie", "user"]) {
  movie: Movie! # implies another field: movieId: UUID!
  user: User!
  role: String!
}

म्यूटेशन में इस्तेमाल करना

लागू करने के इस उदाहरण में, UpdateMovieTitle म्यूटेशन में query फ़ील्ड शामिल है, ताकि MoviePermission से डेटा वापस पाया जा सके. साथ ही, यह पक्का करने के लिए कि ऑपरेशन सुरक्षित और मज़बूत है, यहां दिए गए डायरेक्टिव शामिल हैं:

  • @transaction डायरेक्टिव, यह पक्का करने के लिए कि अनुमति से जुड़ी सभी क्वेरी और जांचें पूरी हो जाएं या एक साथ फ़ेल हो जाएं.
  • @redact डायरेक्टिव का इस्तेमाल, जवाब में क्वेरी के नतीजों को शामिल न करने के लिए किया जाता है. इसका मतलब है कि अनुमति की जांच Data Connect सर्वर पर की जाती है, लेकिन संवेदनशील डेटा को क्लाइंट के साथ शेयर नहीं किया जाता.
  • @check डायरेक्टिव का एक पेयर, जिसका इस्तेमाल क्वेरी के नतीजों पर अनुमति देने के लॉजिक का आकलन करने के लिए किया जाता है. जैसे, यह जांच करना कि किसी दिए गए userID के पास बदलाव करने की सही भूमिका है या नहीं.

mutation UpdateMovieTitle($movieId: UUID!, $newTitle: String!) @auth(level: USER) @transaction {
  # Step 1: Query and check
  query @redact {
    moviePermission( # Look up a join table called MoviePermission with a compound key.
      key: {movieId: $movieId, userId_expr: "auth.uid"}
    # Step 1a: Use @check to test if the user has any role associated with the movie
    # Here the `this` binding refers the lookup result, i.e. a MoviePermission object or null
    # The `this != null` expression could be omitted since rejecting on null is default behavior
    ) @check(expr: "this != null", message: "You do not have access to this movie") {
      # Step 1b: Check if the user has the editor role for the movie
      # Next we execute another @check; now `this` refers to the contents of the `role` field
      role @check(expr: "this == 'editor'", message: "You must be an editor of this movie to update title")
    }
  }
  # Step 2: Act
  movie_update(id: $movieId, data: {
    title: $newTitle
  })
}

क्वेरी में इस्तेमाल करना

अनुमति से जुड़े डेटा को लुकअप करने की सुविधा, भूमिकाओं या अन्य पाबंदियों के आधार पर क्वेरी को सीमित करने के लिए भी काम आती है.

यहां दिए गए उदाहरण में, MoviePermission स्कीमा का भी इस्तेमाल किया गया है. इसमें क्वेरी यह जांच करती है कि अनुरोध करने वाले व्यक्ति के पास, उन उपयोगकर्ताओं को देखने के लिए "एडमिन" की भूमिका है या नहीं जो किसी फ़िल्म में बदलाव कर सकते हैं.

query GetMovieEditors($movieId: UUID!) @auth(level: PUBLIC) {
  moviePermission(key: { movieId: $movieId, userId_expr: "auth.uid" }) @redact {
    role @check(expr: "this == 'admin'", message: "You must be an admin to view all editors of a movie.")
  }
  moviePermissions(where: { movieId: { eq: $movieId }, role: { eq: "editor" } }) {
    user {
      id
      username
    }
  }
}

अनुमति देने की प्रोसेस में इन एंटीपैटर्न से बचें

पिछले सेक्शन में, @auth डायरेक्टिव का इस्तेमाल करते समय फ़ॉलो किए जाने वाले पैटर्न के बारे में बताया गया है.

आपको कुछ ऐसे एंटीपैटर्न के बारे में भी पता होना चाहिए जिनसे बचना ज़रूरी है.

क्वेरी और म्यूटेशन के तर्कों में, उपयोगकर्ता एट्रिब्यूट आईडी और auth token पैरामीटर पास करने से बचें

Firebase Authentication पुष्टि करने के फ़्लो दिखाने और पुष्टि करने से जुड़ा डेटा सुरक्षित तरीके से कैप्चर करने का एक असरदार टूल है. जैसे, रजिस्टर किए गए उपयोगकर्ता आईडी और पुष्टि करने वाले टोकन में सेव किए गए कई फ़ील्ड.

क्वेरी और म्यूटेशन आर्ग्युमेंट में उपयोगकर्ता आईडी और पुष्टि करने वाले टोकन का डेटा पास करने का सुझाव नहीं दिया जाता.

# Antipattern!
# This incorrectly allows any user to view any other user's posts
query AllMyPosts($userId: String!) @auth(level: USER) {
  posts(where: {authorUid: {eq: $userId}}) {
    id, text, createdAt
  }
}

बिना किसी फ़िल्टर के USER ऐक्सेस लेवल का इस्तेमाल न करें

गाइड में कई बार बताया गया है कि USER, USER_ANON, USER_EMAIL_VERIFIED जैसे मुख्य ऐक्सेस लेवल, अनुमति की जांच के लिए बेसलाइन और शुरुआती पॉइंट होते हैं. इन्हें फ़िल्टर और एक्सप्रेशन के साथ बेहतर बनाया जा सकता है. इन लेवल का इस्तेमाल, ऐसे फ़िल्टर या एक्सप्रेशन के बिना करना जो यह जांच करता है कि अनुरोध किस उपयोगकर्ता ने किया है, PUBLIC लेवल का इस्तेमाल करने के बराबर है.

# Antipattern!
# This incorrectly allows any user to view all documents
query ListDocuments @auth(level: USER) {
  documents {
    id
    title
    text
  }
}

प्रोटोटाइपिंग के लिए, PUBLIC या USER ऐक्सेस लेवल का इस्तेमाल न करें

डेवलपमेंट की प्रोसेस को तेज़ करने के लिए, सभी ऑपरेशनों को PUBLIC या USER ऐक्सेस लेवल पर सेट किया जा सकता है. इससे सभी ऑपरेशनों को अनुमति मिल जाती है और आपको अपने कोड को तुरंत टेस्ट करने का मौका मिलता है. हालांकि, ऐसा करने से पहले आपको कुछ और सुधार करने पड़ सकते हैं.

जब आपने इस तरीके से शुरुआती प्रोटोटाइपिंग कर ली हो, तो NO_ACCESS से PUBLIC और USER लेवल के साथ प्रोडक्शन के लिए तैयार ऑथराइज़ेशन पर स्विच करें. हालांकि, इस गाइड में दिखाए गए तरीके के मुताबिक, अतिरिक्त लॉजिक जोड़े बिना उन्हें PUBLIC या USER के तौर पर डिप्लॉय न करें.

# Antipattern!
# This incorrectly allows anyone to delete any post
mutation DeletePost($id: UUID!) @auth(level: PUBLIC) {
  post: post_delete(
    id: $id,
  )
}

पुष्टि नहीं किए गए ईमेल पतों के आधार पर अनुमति न दें

किसी डोमेन के उपयोगकर्ताओं को ऐक्सेस देना, ऐक्सेस को सीमित करने का एक बेहतरीन तरीका है. हालांकि, साइन-इन करते समय कोई भी व्यक्ति किसी ईमेल पते पर मालिकाना हक का दावा कर सकता है. पक्का करें कि आपने सिर्फ़ उन ईमेल पतों को ऐक्सेस दिया हो जिनकी पुष्टि Firebase Authentication के ज़रिए की गई है.

# Antipattern!
# Anyone can claim an email address during sign-in
mutation CreatePost($text: String!, $visibility: String) @auth(expr: "auth.token.email.endsWith('@example.com')") {
  post_insert(data: {
    # set the author's uid to the current user uid
    authorUid_expr: "auth.uid"
    text: $text
    visibility: $visibility
  })
}

auth.token.email_verified भी देखें

mutation CreatePost($text: String!, $visibility: String) @auth(expr: "auth.token.email_verified && auth.token.email.endsWith('@example.com')") {
  post_insert(data: {
    # set the author's uid to the current user uid
    authorUid_expr: "auth.uid"
    text: $text
    visibility: $visibility
  })
}

Firebase CLI की मदद से अनुमति की ऑडिट करना

जैसा कि पहले बताया गया है, प्रीसेट ऐक्सेस लेवल, जैसे कि PUBLIC और USER, मज़बूत अनुमति के लिए शुरुआती पॉइंट हैं. इनका इस्तेमाल, फ़िल्टर और एक्सप्रेशन पर आधारित अनुमति की अतिरिक्त जांचों के साथ किया जाना चाहिए. इनका इस्तेमाल, इस्तेमाल के उदाहरण पर ध्यान दिए बिना नहीं किया जाना चाहिए.

Data Connect, ऑथराइज़ेशन की रणनीति की ऑडिट करने में आपकी मदद करता है. इसके लिए, यह Firebase CLI से firebase deploy का इस्तेमाल करके सर्वर पर डिप्लॉय करते समय, आपके कनेक्टर कोड का विश्लेषण करता है. इस ऑडिट का इस्तेमाल करके, अपने कोडबेस की समीक्षा की जा सकती है.

कनेक्टर डिप्लॉय करने पर, सीएलआई आपके कनेक्टर में मौजूद, बदले गए, और नए ऑपरेशन कोड के लिए आकलन दिखाएगा.

बदलाव की गई और नई कार्रवाइयों के लिए, सीएलआई चेतावनियां जारी करता है. साथ ही, नई कार्रवाइयों में कुछ ऐक्सेस लेवल का इस्तेमाल करने पर या उन ऐक्सेस लेवल का इस्तेमाल करने के लिए मौजूदा कार्रवाइयों में बदलाव करने पर, आपसे पुष्टि करने के लिए कहता है.

चेतावनी और प्रॉम्प्ट हमेशा इन मामलों में दिखते हैं:

  • PUBLIC

साथ ही, auth.uid का इस्तेमाल करके फ़िल्टर नहीं जोड़ने पर, आपको इन ऐक्सेस लेवल पर चेतावनियां और प्रॉम्प्ट दिखेंगे:

  • USER
  • USER_ANON
  • USER_EMAIL_VERIFIED

@auth(insecureReason:) आर्ग्युमेंट का इस्तेमाल करके, असुरक्षित ऑपरेशन की चेतावनियों को छिपाना

कई मामलों में, आपको लगेगा कि PUBLIC और USER* ऐक्सेस लेवल का इस्तेमाल करना सही है.

जब आपके कनेक्टर में कई कार्रवाइयां शामिल हों, तो हो सकता है कि आपको सुरक्षा ऑडिट का ऐसा आउटपुट चाहिए हो जिसमें कार्रवाइयों के बारे में ज़्यादा साफ़ तौर पर और काम की जानकारी दी गई हो. साथ ही, उसमें उन कार्रवाइयों को शामिल न किया गया हो जिनसे आम तौर पर चेतावनी ट्रिगर होती है, लेकिन आपको पता है कि उनके लिए सही ऐक्सेस लेवल है.

@auth(insecureReason:) की मदद से, इस तरह की कार्रवाइयों के लिए चेतावनियों को छिपाया जा सकता है. उदाहरण के लिए:

query listItem @auth(level: PUBLIC, insecureReason: "This operation is safe to expose to the public.")
  {
    items {
      id name
    }
  }

ऐप्लिकेशन की पुष्टि करने के लिए Firebase App Check का इस्तेमाल करना

पुष्टि और अनुमति, Data Connect सुरक्षा के अहम कॉम्पोनेंट हैं. पुष्टि करने और अनुमति देने की प्रोसेस के साथ-साथ, ऐप्लिकेशन की पुष्टि करने की सुविधा को इस्तेमाल करने से, सुरक्षा से जुड़ी समस्याओं को हल करने में मदद मिलती है.

Firebase App Check के ज़रिए पुष्टि करने की सुविधा का इस्तेमाल करके, आपके ऐप्लिकेशन को चलाने वाले डिवाइस, ऐप्लिकेशन या डिवाइस की पुष्टि करने वाली सेवा का इस्तेमाल करेंगे. यह सेवा पुष्टि करती है कि Data Connect की कार्रवाइयां, आपके असली ऐप्लिकेशन से की गई हैं और अनुरोध, असली और छेड़छाड़ न किए गए डिवाइस से किए गए हैं. यह पुष्टि, Data Connect पर आपके ऐप्लिकेशन से किए गए हर अनुरोध के साथ जुड़ी होती है.

App Check के लिए Data Connect की सुविधा चालू करने और अपने ऐप्लिकेशन में इसके क्लाइंट एसडीके को शामिल करने का तरीका जानने के लिए, App Check की खास जानकारी देखें.

@auth(level) डायरेक्टिव के लिए पुष्टि के लेवल

इस टेबल में, ऐक्सेस के सभी स्टैंडर्ड लेवल और उनके CEL वर्शन की सूची दी गई है. पुष्टि के लेवल, ज़्यादा से कम उपयोगकर्ताओं के हिसाब से क्रम में दिए गए हैं. हर लेवल में, उन सभी उपयोगकर्ताओं को शामिल किया जाता है जो अगले लेवल से मेल खाते हैं.

लेवल परिभाषा
PUBLIC इस ऑपरेशन को कोई भी व्यक्ति, पुष्टि किए बिना या पुष्टि करके पूरा कर सकता है.

ध्यान दें: इस डेटा को कोई भी उपयोगकर्ता पढ़ सकता है या इसमें बदलाव कर सकता है. Firebase, सार्वजनिक तौर पर ब्राउज़ किए जा सकने वाले डेटा के लिए, इस लेवल की अनुमति देने का सुझाव देता है. जैसे, प्रॉडक्ट या मीडिया लिस्टिंग. सबसे सही तरीके के उदाहरण और विकल्प देखें.

@auth(expr: "true") के बराबर

@auth फ़िल्टर और एक्सप्रेशन का इस्तेमाल, इस ऐक्सेस लेवल के साथ एक साथ नहीं किया जा सकता. इस तरह के एक्सप्रेशन के लिए, 400 गड़बड़ी वाला अनुरोध दिखेगा.
USER_ANON पहचाना गया कोई भी उपयोगकर्ता, क्वेरी या म्यूटेशन कर सकता है. इसमें वे उपयोगकर्ता भी शामिल हैं जिन्होंने Firebase Authentication के साथ गुमनाम तरीके से लॉग इन किया है.

ध्यान दें: USER_ANON, USER का सुपरसेट है.

ध्यान देने वाली बातें: ध्यान दें कि आपको इस लेवल की अनुमति के लिए, अपनी क्वेरी और म्यूटेशन को ध्यान से डिज़ाइन करना होगा. इस लेवल पर, उपयोगकर्ता को Authentication के साथ गुमनाम तौर पर लॉग इन करने की अनुमति मिलती है. इसका मतलब है कि उपयोगकर्ता के डिवाइस पर अपने-आप साइन-इन होने की सुविधा चालू होती है. हालांकि, इस लेवल पर यह जांच नहीं की जाती कि डेटा उपयोगकर्ता का है या नहीं. सबसे सही तरीके के उदाहरण और विकल्प देखें.

पहचान छिपाकर लॉगिन करने की सुविधा Authentication, uid जारी करती है. इसलिए, USER_ANON लेवल,
@auth(expr: "auth.uid != nil") के बराबर होता है
USER Firebase Authentication से लॉग इन करने वाले किसी भी उपयोगकर्ता के पास क्वेरी या म्यूटेशन करने का अधिकार होता है. हालांकि, पहचान ज़ाहिर किए बिना साइन इन करने वाले उपयोगकर्ताओं के पास यह अधिकार नहीं होता.

ध्यान देने वाली बातें: ध्यान दें कि आपको इस लेवल की अनुमति के लिए, अपनी क्वेरी और म्यूटेशन को ध्यान से डिज़ाइन करना होगा. इस लेवल पर सिर्फ़ यह जांच की जाती है कि उपयोगकर्ता ने Authentication से लॉग इन किया है या नहीं. यह अपने-आप अन्य जांच नहीं करता. उदाहरण के लिए, यह जांच नहीं करता कि डेटा उपयोगकर्ता का है या नहीं. सबसे सही तरीके के उदाहरण और विकल्प देखें.

@auth(expr: "auth.uid != nil && auth.token.firebase.sign_in_provider != 'anonymous'")" के बराबर
USER_EMAIL_VERIFIED जिस उपयोगकर्ता ने पुष्टि किए गए ईमेल पते से Firebase Authentication में लॉग इन किया है उसके पास क्वेरी या म्यूटेशन करने की अनुमति होती है.

ध्यान देने वाली बातें: ईमेल की पुष्टि Authentication का इस्तेमाल करके की जाती है. इसलिए, यह Authentication के ज़्यादा भरोसेमंद तरीके पर आधारित है. इस वजह से, यह लेवल USER या USER_ANON की तुलना में ज़्यादा सुरक्षा देता है. यह लेवल सिर्फ़ यह जांच करता है कि उपयोगकर्ता ने पुष्टि किए गए ईमेल पते से Authentication में लॉग इन किया है या नहीं. यह अपने-आप अन्य जांच नहीं करता. उदाहरण के लिए, यह जांच नहीं करता कि डेटा उपयोगकर्ता का है या नहीं. सबसे सही तरीके के उदाहरण और विकल्प देखें.

@auth(expr: "auth.uid != nil && auth.token.email_verified")" के बराबर
NO_ACCESS इस कार्रवाई को Admin SDK के संदर्भ के बाहर नहीं किया जा सकता.

@auth(expr: "false") के बराबर

@auth(expr) के लिए सीईएल रेफ़रंस

इस गाइड में दिए गए अन्य उदाहरणों में बताया गया है कि Data Connect, @auth(expr:), और @check डायरेक्टिव के लिए अनुमति को कंट्रोल करने के लिए, कॉमन एक्सप्रेशन लैंग्वेज (सीईएल) में तय किए गए एक्सप्रेशन का इस्तेमाल किया जा सकता है. आपको इनका इस्तेमाल करना चाहिए.

इस सेक्शन में, इन निर्देशों के लिए एक्सप्रेशन बनाने से जुड़े CEL सिंटैक्स के बारे में बताया गया है.

सीईएल के बारे में पूरी जानकारी, सीईएल स्पेसिफ़िकेशन में दी गई है.

क्वेरी और म्यूटेशन में पास किए गए वैरिएबल की जांच करना

@auth(expr) सिंटैक्स की मदद से, क्वेरी और म्यूटेशन से वैरिएबल ऐक्सेस किए जा सकते हैं और उनकी जांच की जा सकती है.

उदाहरण के लिए, vars.status का इस्तेमाल करके, $status जैसे ऑपरेशन वैरिएबल को शामिल किया जा सकता है.

mutation Update($id: UUID!, $status: Any) @auth(expr: "has(vars.status)")

एक्सप्रेशन के लिए उपलब्ध डेटा: request, response, this

डेटा का इस्तेमाल इन कामों के लिए किया जाता है:

  • @auth(expr:) और @check(expr:) डायरेक्टिव में CEL एक्सप्रेशन का इस्तेमाल करके आकलन करना
  • सर्वर एक्सप्रेशन का इस्तेमाल करके असाइनमेंट, <field>_expr.

@auth(expr:) और @check(expr:), दोनों सीईएल एक्सप्रेशन इनका आकलन कर सकते हैं:

  • request.operationName
  • vars (request.variables के लिए उपनाम)
  • auth (request.auth के लिए उपनाम)

म्यूटेशन में, इनके कॉन्टेंट को ऐक्सेस और असाइन किया जा सकता है:

  • response (कई चरणों वाले लॉजिक में आंशिक नतीजे देखने के लिए)

इसके अलावा, @check(expr:) एक्सप्रेशन इन चीज़ों का आकलन कर सकते हैं:

  • this (मौजूदा फ़ील्ड की वैल्यू)
  • response (कई चरणों वाले लॉजिक में आंशिक नतीजे देखने के लिए)

अनुरोध का ऑपरेशन नेम बाइंडिंग

request.operarationName बाइंडिंग, ऑपरेशन का टाइप सेव करती है. यह क्वेरी या म्यूटेशन हो सकता है.

vars बाइंडिंग (request.vars)

vars बाइंडिंग की मदद से, आपके एक्सप्रेशन उन सभी वैरिएबल को ऐक्सेस कर सकते हैं जिन्हें आपकी क्वेरी या म्यूटेशन में पास किया गया है.

एक्सप्रेशन में vars.<variablename> का इस्तेमाल, पूरी तरह से क्वालिफ़ाइड request.variables.<variablename> के लिए एलियास के तौर पर किया जा सकता है:

# The following are equivalent
mutation StringType($v: String!) @auth(expr: "vars.v == 'hello'")
mutation StringType($v: String!) @auth(expr: "request.variables.v == 'hello'")

auth बाइंडिंग (request.auth)

Authentication, आपके डेटा को ऐक्सेस करने का अनुरोध करने वाले उपयोगकर्ताओं की पहचान करता है. साथ ही, यह जानकारी एक बाइंडिंग के तौर पर उपलब्ध कराता है, जिसका इस्तेमाल एक्सप्रेशन बनाने के लिए किया जा सकता है.

अपने फ़िल्टर और एक्सप्रेशन में, auth को request.auth के एलियास के तौर पर इस्तेमाल किया जा सकता है.

ऑथ बाइंडिंग में यह जानकारी शामिल होती है:

  • uid: अनुरोध करने वाले उपयोगकर्ता को असाइन किया गया यूनीक यूज़र आईडी.
  • token: Authentication से इकट्ठा की गई वैल्यू का मैप.

auth.token के कॉन्टेंट के बारे में ज़्यादा जानने के लिए, Auth टोकन में मौजूद डेटा देखें

response बाइंडिंग

response बाइंडिंग में, सर्वर से इकट्ठा किया गया डेटा होता है. यह डेटा, क्वेरी या म्यूटेशन के जवाब में इकट्ठा किया जाता है.

जैसे-जैसे कार्रवाई आगे बढ़ती है और हर चरण पूरा होता जाता है वैसे-वैसे response में, पूरे हो चुके चरणों का जवाब डेटा शामिल होता जाता है.

response बाइंडिंग को, उससे जुड़े ऑपरेशन के हिसाब से स्ट्रक्चर किया जाता है. इसमें नेस्ट किए गए (कई) फ़ील्ड और (अगर लागू हो) एम्बेड की गई क्वेरी शामिल होती हैं.

ध्यान दें कि एम्बेड की गई क्वेरी के जवाब का डेटा ऐक्सेस करते समय, फ़ील्ड में किसी भी तरह का डेटा टाइप हो सकता है. यह एम्बेड की गई क्वेरी में अनुरोध किए गए डेटा पर निर्भर करता है. म्यूटेशन फ़ील्ड, जैसे कि _insert और _delete से मिले डेटा को ऐक्सेस करते समय, उनमें यूयूआईडी कुंजियां, मिटाए गए आइटम की संख्या, और शून्य हो सकते हैं. इसके बारे में जानने के लिए, म्यूटेशन का रेफ़रंस देखें.

उदाहरण के लिए:

  • एम्बेड की गई क्वेरी वाले म्यूटेशन में, response बाइंडिंग में response.query.<fieldName>.<fieldName>.... पर लुकअप डेटा होता है. इस मामले में, response.query.todoList और response.query.todoList.priority.
mutation CheckTodoPriority(
  $uniqueListName: String!
) {
  # This query is identified as `response.query`
  query @check(expr: "response.query.todoList.priority == 'high'", message: "This list is not for high priority items!") {
    # This field is identified as `response.query.todoList`
    todoList(where: { name: $uniqueListName }) {
      # This field is identified as `response.query.todoList.priority`
      priority
    }
  }
}
  • एक से ज़्यादा चरणों वाले म्यूटेशन में, उदाहरण के लिए कई _insert फ़ील्ड के साथ, response बाइंडिंग में response.<fieldName>.<fieldName>.... पर आंशिक डेटा होता है. इस मामले में, response.todoList_insert.id.
mutation CreateTodoListWithFirstItem(
  $listName: String!,
  $itemContent: String!
) @transaction {
  # Step 1
  todoList_insert(data: {
    id_expr: "uuidV4()",
    name: $listName,
  })
  # Step 2:
  todo_insert(data: {
    listId_expr: "response.todoList_insert.id" # <-- Grab the newly generated ID from the partial response so far.
    content: $itemContent,
  })
}

this बाइंडिंग

बाइंडिंग this, उस फ़ील्ड का आकलन करती है जिससे @check डायरेक्टिव जुड़ा होता है. सामान्य मामले में, आपको सिंगल वैल्यू वाली क्वेरी के नतीजों का आकलन करना पड़ सकता है.

mutation UpdateMovieTitle (
  $movieId: UUID!,
  $newTitle: String!)
  @auth(level: USER)
  @transaction {
  # Step 1: Query and check
  query @redact {
    moviePermission( # Look up a join table called MoviePermission with a compound key.
      key: {movieId: $movieId, userId_expr: "auth.uid"}
    ) {
      # Check if the user has the editor role for the movie. `this` is the string value of `role`.
      # If the parent moviePermission is null, the @check will also fail automatically.
      role @check(expr: "this == 'editor'", message: "You must be an editor of this movie to update title")
    }
  }
  # Step 2: Act
  movie_update(id: $movieId, data: {
    title: $newTitle
  })
}

अगर किसी पूर्वज के तौर पर सूची का इस्तेमाल किया गया है, तो लौटाया गया फ़ील्ड कई बार दिखता है. ऐसे में, हर बार this का इस्तेमाल करके, हर वैल्यू की जांच की जाती है.

किसी भी पाथ के लिए, अगर पूर्वज null या [] है, तो फ़ील्ड तक नहीं पहुंचा जा सकेगा. साथ ही, उस पाथ के लिए CEL का आकलन नहीं किया जाएगा. दूसरे शब्दों में, आकलन सिर्फ़ तब किया जाता है, जब this null या गैर-null हो. हालांकि, यह कभी भी undefined नहीं होता.

जब फ़ील्ड खुद एक सूची या ऑब्जेक्ट होता है, तो this उसी स्ट्रक्चर को फ़ॉलो करता है. इसमें ऑब्जेक्ट के मामले में चुने गए सभी डिसेंडेंट शामिल होते हैं. इसे यहां दिए गए उदाहरण में दिखाया गया है.

mutation UpdateMovieTitle2($movieId: UUID!, $newTitle: String!) @auth(level: USER) @transaction {
  # Step 1: Query and check
  query {
    moviePermissions( # Now we query for a list of all matching MoviePermissions.
      where: {movieId: {eq: $movieId}, userId: {eq_expr: "auth.uid"}}
    # This time we execute the @check on the list, so `this` is the list of objects.
    # We can use the `.exists` macro to check if there is at least one matching entry.
    ) @check(expr: "this.exists(p, p.role == 'editor')", message: "You must be an editor of this movie to update title") {
      role
    }
  }
  # Step 2: Act
  movie_update(id: $movieId, data: {
    title: $newTitle
  })
}

कॉम्प्लेक्स एक्सप्रेशन सिंटैक्स

&& और || ऑपरेटर के साथ मिलाकर, ज़्यादा जटिल एक्सप्रेशन लिखे जा सकते हैं.

mutation UpsertUser($username: String!) @auth(expr: "(auth != null) && (vars.username == 'joe')")

यहां दिए गए सेक्शन में, सभी उपलब्ध ऑपरेटर के बारे में बताया गया है.

ऑपरेटर और ऑपरेटर प्रेसिडेंस

ऑपरेटर और उनकी प्राथमिकता के बारे में जानने के लिए, इस टेबल का इस्तेमाल करें.

यहां a और b कोई भी एक्सप्रेशन, f कोई फ़ील्ड, और i कोई इंडेक्स है.

ऑपरेटर ब्यौरा एसोसिएटिविटी
a[i] a() a.f इंडेक्स, कॉल, फ़ील्ड ऐक्सेस बाएं से दाएं
!a -a यूनरी नेगेशन दाएं से बाएं
a/b a%b a*b गुणा करने वाले ऑपरेटर बाएं से दाएं
a+b a-b ऐडिटिव ऑपरेटर बाएं से दाएं
a>b a>=b a<b a<=b रिलेशनल ऑपरेटर बाएं से दाएं
a in b सूची या मैप में मौजूद होना बाएं से दाएं
type(a) == t टाइप की तुलना, जहां t bool, int, फ़्लोट, संख्या, स्ट्रिंग, सूची, मैप, टाइमस्टैंप या अवधि हो सकती है बाएं से दाएं
a==b a!=b कंपैरिज़न ऑपरेटर बाएं से दाएं
a && b कंडिशनल AND बाएं से दाएं
a || b कंडिशनल OR बाएं से दाएं
a ? true_value : false_value टर्नरी एक्सप्रेशन बाएं से दाएं

पुष्टि करने वाले टोकन में मौजूद डेटा

auth.token ऑब्जेक्ट में ये वैल्यू शामिल हो सकती हैं:

फ़ील्ड ब्यौरा
email अगर खाता मौजूद है, तो उससे जुड़ा ईमेल पता.
email_verified true अगर उपयोगकर्ता ने पुष्टि कर दी है कि उसके पास email पते का ऐक्सेस है. ईमेल की सेवा देने वाली कुछ कंपनियां, अपने ईमेल पतों की पुष्टि अपने-आप कर लेती हैं.
phone_number खाते से जुड़ा फ़ोन नंबर, अगर मौजूद हो.
name अगर उपयोगकर्ता का डिसप्ले नेम सेट है, तो यह उसका डिसप्ले नेम होता है.
sub उपयोगकर्ता का Firebase यूआईडी. यह किसी प्रोजेक्ट में यूनीक होता है.
firebase.identities इस उपयोगकर्ता के खाते से जुड़ी सभी पहचानों की डिक्शनरी. डिक्शनरी की कुंजियां इनमें से कोई भी हो सकती हैं: email, phone, google.com, facebook.com, github.com, twitter.com. डिक्शनरी की वैल्यू, खाते से जुड़े हर आइडेंटिटी प्रोवाइडर के लिए यूनीक आइडेंटिफ़ायर के ऐरे होते हैं. उदाहरण के लिए, auth.token.firebase.identities["google.com"][0] में खाते से जुड़ा पहला Google उपयोगकर्ता आईडी होता है.
firebase.sign_in_provider इस टोकन को पाने के लिए, साइन-इन की सुविधा देने वाली कंपनी का इस्तेमाल किया गया था. यह इनमें से कोई भी स्ट्रिंग हो सकती है: custom, password, phone, anonymous, google.com, facebook.com, github.com, twitter.com.
firebase.tenant अगर खाते से जुड़ा tenantId मौजूद है, तो उसे दिखाता है. उदाहरण के लिए, tenant2-m6tyz

JWT आईडी टोकन में अतिरिक्त फ़ील्ड

इन auth.token फ़ील्ड को भी ऐक्सेस किया जा सकता है:

कस्टम टोकन के दावे
alg एल्‍गोरि‍दम "RS256"
iss जारी करने वाला आपके प्रोजेक्ट के सेवा खाते का ईमेल पता
sub विषय आपके प्रोजेक्ट के सेवा खाते का ईमेल पता
aud ऑडियंस "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat जारी करने का समय UNIX epoch के बाद से मौजूदा समय, सेकंड में
exp समाप्ति समय यह टोकन की समयसीमा खत्म होने का समय है. इसे UNIX epoch के बाद से सेकंड में दिखाया जाता है. यह iat के ज़्यादा से ज़्यादा 3600 सेकंड बाद का हो सकता है.
ध्यान दें: इससे सिर्फ़ उस समय को कंट्रोल किया जाता है जब कस्टम टोकन की समयसीमा खत्म होती है. हालांकि, signInWithCustomToken() का इस्तेमाल करके किसी उपयोगकर्ता को साइन इन कराने के बाद, वह डिवाइस में तब तक साइन इन रहेगा, जब तक उसका सेशन अमान्य नहीं हो जाता या वह साइन आउट नहीं कर लेता.
<claims> (ज़रूरी नहीं) टोकन में शामिल करने के लिए, पसंद के मुताबिक बनाए गए दावे. इन्हें एक्सप्रेशन में auth.token (या request.auth.token) के ज़रिए ऐक्सेस किया जा सकता है. उदाहरण के लिए, अगर आपने कस्टम दावा adminClaim बनाया है, तो इसे auth.token.adminClaim की मदद से ऐक्सेस किया जा सकता है.

आगे क्या करना है?