승인 및 증명으로 데이터 연결 보호

Firebase Data Connect는 다음과 같은 강력한 클라이언트 측 보안을 제공합니다.

  • 모바일 및 웹 클라이언트 승인
  • 개별 쿼리 및 변형 수준 승인 제어
  • Firebase App Check를 통한 앱 증명

Data Connect는 다음과 같이 이 보안을 확장합니다.

  • 서버 측 승인
  • IAM을 사용한 Firebase 프로젝트 및 Cloud SQL 사용자 보안

클라이언트 쿼리 및 변형 승인

Data ConnectFirebase Authentication와 완전히 통합되어 있으므로 설계에서 데이터에 액세스하는 사용자 (인증)에 관한 풍부한 데이터를 사용하여 사용자가 액세스할 수 있는 데이터 (승인)를 지정할 수 있습니다.

Data Connect는 쿼리 및 변형에 @auth 디렉티브를 제공하여 작업을 승인하는 데 필요한 인증 수준을 설정할 수 있도록 합니다. 이 가이드에서는 예시를 통해 @auth 지시문을 소개합니다.

또한 Data Connect는 변형에 삽입된 쿼리의 실행을 지원하므로 데이터베이스에 저장한 추가 승인 기준을 검색하고 @check 디렉티브에서 이러한 기준을 사용하여 괄호로 묶인 변형이 승인되었는지 결정할 수 있습니다. 이 승인 사례의 경우 @redact 디렉티브를 사용하면 쿼리 결과가 전송 프로토콜에서 클라이언트에게 반환되는지 여부와 생성된 SDK에서 생략된 삽입된 쿼리를 제어할 수 있습니다. 예시가 포함된 이러한 지시문 소개를 확인하세요.

@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 컨텍스트 외부에서 실행할 수 없습니다.

이러한 사전 설정된 액세스 수준을 출발점으로 삼아 where 필터와 서버에서 평가되는 Common Expression Language (CEL) 표현식을 사용하여 @auth 디렉티브에서 복잡하고 강력한 승인 검사를 정의할 수 있습니다.

@auth 지시어를 사용하여 일반적인 승인 시나리오 구현

사전 설정된 액세스 수준은 승인의 출발점입니다.

USER 액세스 수준은 가장 광범위하게 유용한 시작용 기본 수준입니다.

완전히 안전한 액세스는 USER 수준과 사용자 속성, 리소스 속성, 역할, 기타 검사를 확인하는 필터 및 표현식을 기반으로 합니다. USER_ANONUSER_EMAIL_VERIFIED 수준은 USER 케이스의 변형입니다.

표현식 문법을 사용하면 작업과 함께 전달된 인증 데이터(인증 토큰의 표준 데이터와 토큰의 맞춤 데이터 모두)를 나타내는 auth 객체를 사용하여 데이터를 평가할 수 있습니다. auth 객체에서 사용할 수 있는 필드 목록은 참조 섹션을 참고하세요.

물론 PUBLIC가 시작에 적합한 액세스 수준인 사용 사례도 있습니다. 다시 한번 강조하지만 액세스 수준은 항상 출발점이며 강력한 보안을 위해서는 추가 필터와 표현식이 필요합니다.

이제 이 가이드에서는 USERPUBLIC에서 빌드하는 방법의 예를 제공합니다.

동기를 부여하는 예시

다음 권장사항 예시는 특정 콘텐츠가 결제 계획으로 잠겨 있는 블로깅 플랫폼의 다음 스키마를 참고합니다.

이러한 플랫폼은 UsersPosts를 모델링할 가능성이 높습니다.

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의 소유권)을 작성하는 것이 좋습니다.

다음 예에서는 표현식을 사용하여 인증 토큰의 데이터를 읽고 비교합니다. 일반적인 패턴은 where: {authorUid: {eq_expr: "auth.uid"}}와 같은 표현식을 사용하여 저장된 authorUid를 인증 토큰에 전달된 auth.uid(사용자 ID)와 비교하는 것입니다.

만들기

이 인증 관행은 후속 인증 테스트에서 비교할 수 있도록 인증 토큰의 auth.uid를 각 새 PostauthorUid 필드로 추가하는 것으로 시작합니다.

# 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
  })
}
Update

클라이언트가 Post를 업데이트하려고 하면 저장된 authorUid를 기준으로 전달된 auth.uid를 테스트할 수 있습니다.

# 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로 설정되어 있으므로 승인은 인증 토큰을 기반으로 하지 않습니다. 하지만 데이터베이스의 레코드를 공개 액세스에 적합하도록 명시적으로 설정할 수 있습니다. 데이터베이스에 visibility이 'public'으로 설정된 Post 레코드가 있다고 가정해 보겠습니다.

# 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 필드로 플래그가 지정된 앱의 'pro' 요금제 사용자를 식별하기 위해 인증 토큰을 전달하는 맞춤 사용자 클레임을 설정했다고 가정합니다. 표현식은 이 필드를 기준으로 테스트할 수 있습니다.

# 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 디렉티브 이해

@check 지시어는 지정된 필드가 쿼리 결과에 있는지 확인합니다. Common Expression Language (CEL) 표현식은 필드 값을 테스트하는 데 사용됩니다. 디렉티브의 기본 동작은 null 값 노드를 확인하고 거부하는 것입니다.

@redact 지시어는 클라이언트의 응답 일부를 삭제합니다. 삭제된 필드는 여전히 데이터 변경 및 @check를 비롯한 부작용에 대해 평가되며 결과는 CEL 표현식의 후속 단계에서 계속 사용할 수 있습니다.

Data Connect에서 @check@redact 디렉티브는 승인 확인 컨텍스트에서 가장 자주 사용됩니다. 승인 데이터 조회 관련 논의를 참고하세요.

승인 데이터를 조회하는 @check@redact 디렉티브 추가

일반적인 승인 사용 사례는 데이터베이스(예: 특수 권한 테이블)에 맞춤 승인 역할을 저장하고 이러한 역할을 사용하여 데이터를 생성, 업데이트 또는 삭제하는 변형을 승인하는 것입니다.

승인 데이터 조회를 사용하면 userID를 기반으로 역할을 쿼리하고 CEL 표현식을 사용하여 변형이 승인되었는지 결정할 수 있습니다. 예를 들어 승인된 클라이언트가 영화 제목을 업데이트할 수 있는 UpdateMovieTitle 변형을 작성할 수 있습니다.

이 섹션의 나머지 부분에서는 영화 리뷰 앱 데이터베이스가 MoviePermission 테이블에 승인 역할을 저장한다고 가정합니다.

# MoviePermission
# Suppose a user has an authorization role with respect to records in the Movie table
type MoviePermission @table(key: ["doc", "userId"]) {
  movie: Movie! # implies another field: movieId: UUID!
  userId: String! # Can also be a reference to a User table, doesn't matter
  role: String!
}

다음 구현 예에서 UpdateMovieTitle 변형에는 MoviePermission에서 데이터를 검색하는 query 필드와 작업이 안전하고 견고하도록 하는 다음 디렉티브가 포함됩니다.

  • 모든 승인 쿼리 및 검사가 자동으로 완료되거나 실패하도록 하는 @transaction 디렉티브입니다.
  • 응답에서 쿼리 결과를 생략하는 @redact 지시문: 즉, Google의 승인 확인은 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
  })
}

승인 시 피해야 할 안티패턴

이전 섹션에서는 @auth 디렉티브를 사용할 때 따라야 하는 패턴을 다룹니다.

피해야 할 중요한 역패턴도 알아야 합니다.

쿼리 및 변형 인수에 사용자 속성 ID 및 인증 토큰 매개변수를 전달하지 않음

Firebase Authentication는 인증 흐름을 표시하고 등록된 사용자 ID, 인증 토큰에 저장된 여러 필드와 같은 인증 데이터를 안전하게 캡처하는 강력한 도구입니다.

쿼리 및 변형 인수에 사용자 ID 및 인증 토큰 데이터를 전달하는 것은 권장되지 않습니다.

# 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에서 PUBLICUSER 수준으로 프로덕션 준비 완료 승인으로 전환합니다. 그러나 이 가이드에 설명된 대로 로직을 추가하지 않고 PUBLIC 또는 USER로 배포하지 마세요.

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

앱 증명에 Firebase App Check 사용

인증 및 승인은 Data Connect 보안의 중요한 구성요소입니다. 인증 및 승인을 앱 증명과 결합하면 매우 강력한 보안 솔루션을 만들 수 있습니다.

Firebase App Check를 통한 증명을 사용하면 앱을 실행하는 기기는 Data Connect 작업이 인증된 앱에서 시작되고 요청이 변조되지 않은 인증된 기기에서 시작되었음을 증명하는 앱 또는 기기 증명 제공자를 사용합니다. 이 증명은 앱이 Data Connect에 대해 수행하는 모든 요청에 연결됩니다.

Data ConnectApp Check를 사용 설정하고 앱에 클라이언트 SDK를 포함하는 방법을 알아보려면 App Check 개요를 살펴보세요.

@auth(level) 디렉티브의 인증 수준

다음 표에는 모든 표준 액세스 수준과 해당 CEL이 나와 있습니다. 인증 수준은 광범위에서 좁은 순으로 나열됩니다. 각 수준은 다음 수준과 일치하는 모든 사용자를 포함합니다.

수준 정의
PUBLIC 이 작업은 인증 여부와 관계없이 누구나 실행할 수 있습니다.

고려사항: 모든 사용자가 데이터를 읽거나 수정할 수 있습니다. Firebase에서는 제품 또는 미디어 등 공개적으로 탐색 가능한 데이터에 이 수준의 승인을 권장합니다. 권장사항 예시 및 대안을 참고하세요.

@auth(expr: "true")과 동일합니다.

@auth 필터 및 표현식은 이 액세스 수준과 함께 사용할 수 없습니다. 이러한 표현식은 400 Bad Request 오류와 함께 실패합니다.
USER_ANON Firebase Authentication로 익명으로 로그인한 사용자를 포함하여 식별된 모든 사용자는 쿼리 또는 변형을 실행할 권한이 있습니다.

참고: USER_ANONUSER의 상위 집합입니다.

고려사항: 이 수준의 승인에 맞게 쿼리와 변형을 신중하게 설계해야 합니다. 이 수준을 사용하면 사용자가 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)@check(expr)의 CEL 참조

이 가이드의 다른 예에서 볼 수 있듯이 Common Expression Language (CEL)에 정의된 표현식을 사용하여 @auth(expr:)@check 지시어를 사용하여 Data Connect의 승인을 제어할 수 있으며 제어해야 합니다.

이 섹션에서는 이러한 디렉티브의 표현식 생성과 관련된 CEL 문법을 설명합니다.

CEL에 관한 전체 참조 정보는 CEL 사양에 나와 있습니다.

쿼리 및 변형에 전달된 테스트 변수

@auth(expr) 구문을 사용하면 쿼리 및 변형에서 변수에 액세스하고 테스트할 수 있습니다.

예를 들어 vars.status를 사용하여 $status와 같은 작업 변수를 포함할 수 있습니다.

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

표현식에 사용할 수 있는 데이터

@auth(expr:)@check(expr:) CEL 표현식은 모두 다음을 평가할 수 있습니다.

  • request.operationName
  • vars (request.variables의 별칭)
  • auth (request.auth의 별칭)

또한 @check(expr:) 표현식은 다음을 평가할 수 있습니다.

  • this (현재 필드의 값)

request.operationName 객체

request.operarationName 객체는 작업 유형(쿼리 또는 변형)을 저장합니다.

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 객체

Authentication는 데이터 액세스를 요청하는 사용자를 식별하고 해당 정보를 표현식에서 빌드할 수 있는 객체로 제공합니다.

필터와 표현식에서 authrequest.auth의 별칭으로 사용할 수 있습니다.

auth 객체에는 다음 정보가 포함됩니다.

  • uid: 요청하는 사용자에게 할당된 순 사용자 ID입니다.
  • token: Authentication에서 수집한 값의 맵입니다.

auth.token의 콘텐츠에 관한 자세한 내용은 인증 토큰의 데이터를 참고하세요.

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 평가가 건너뜁니다. 즉, thisnull이거나 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')")

다음 섹션에서는 사용 가능한 모든 연산자를 설명합니다.

연산자 및 연산자 우선순위

다음 표를 통해 연산자와 연산자 우선순위를 참고하세요.

ab, 필드 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, float, number, string, list, map, timestamp, duration일 수 있습니다. 왼쪽에서 오른쪽으로
a==b a!=b 비교 연산자 왼쪽에서 오른쪽으로
a && b 조건부 AND 왼쪽에서 오른쪽으로
a || b 조건부 OR 왼쪽에서 오른쪽으로
a ? true_value : false_value 3항 표현식 왼쪽에서 오른쪽으로

인증 토큰의 데이터

auth.token 객체는 다음 값을 포함할 수 있습니다.

필드 설명
email 계정과 연결된 이메일 주소(있는 경우)입니다.
email_verified true는 사용자가 email 주소에 대한 액세스 권한이 있는지 확인한 경우입니다. 일부 제공업체는 자동으로 자체 이메일 주소를 확인합니다.
phone_number 계정과 연결된 전화번호(있는 경우)입니다.
name 사용자의 표시 이름(설정된 경우)입니다.
sub 사용자의 Firebase UID입니다. 프로젝트 내에서 고유합니다.
firebase.identities 사용자 계정과 연결된 모든 ID의 사전입니다. 사전의 키는 email, phone, google.com, facebook.com, github.com, twitter.com일 수 있습니다. 사전의 값은 계정과 연결된 각 ID 공급업체의 고유한 식별자 배열입니다. 예를 들어 auth.token.firebase.identities["google.com"][0]에는 계정과 연결된 첫 번째 Google 사용자 ID가 포함됩니다.
firebase.sign_in_provider 토큰을 얻기 위해 사용된 로그인 제공업체입니다. 문자열 custom, password, phone, anonymous, google.com, facebook.com, github.com, twitter.com 중 하나일 수 있습니다.
firebase.tenant 계정과 연결된 tenantId(있는 경우)입니다. 예를 들면 tenant2-m6tyz입니다.

JWT ID 토큰의 추가 필드

다음 auth.token 필드에도 액세스할 수 있습니다.

커스텀 토큰 클레임
alg 알고리즘 "RS256"
iss 발급자 프로젝트의 서비스 계정 이메일 주소
sub 제목 프로젝트의 서비스 계정 이메일 주소
aud 잠재고객 "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat 발급 시간 Unix epoch를 기준으로 하는 현재 시간(초)
exp 만료 시간 Unix epoch를 기준으로 하는 토큰 만료 시간(초). iat보다 최대 3,600초 길어질 수 있습니다.
참고: 이 항목은 커스텀 토큰 자체의 만료 시간만 제어합니다. signInWithCustomToken()으로 사용자가 로그인한 후에는 세션이 무효화되거나 사용자가 로그아웃할 때까지 기기에서 로그인 상태가 유지됩니다.
<claims>(선택사항) 토큰에 포함할 선택적 맞춤 클레임으로, 표현식에서 auth.token (또는 request.auth.token)를 통해 액세스할 수 있습니다. 예를 들어 맞춤 소유권 주장 adminClaim을 만드는 경우 auth.token.adminClaim로 액세스할 수 있습니다.

다음 단계