skip to content

How does a BigQuery authorized view give a team query access without access to the source tables?

level: middleimportance: must knowfreq 60%

answer

  1. a plain view needs access to its own sources
  2. the view itself becomes a trusted principal
  3. it must live in a different dataset
  4. authorize a whole dataset once you have many

basics

~20 s

An authorized view sits in a separate dataset that you authorize on the source dataset. Readers are granted access to the view's dataset only; the view reads the base tables under its own authorization and exposes just the columns and rows it selects.

solid answer

~50 s

In BigQuery, permissions are granted on datasets and tables, so a view normally requires its readers to also have read access on everything it selects from — which defeats the point. An **authorized view** breaks that chain. You put the view in a second dataset, then add that view to the *source* dataset's list of authorized views. The source dataset now trusts the view itself as a principal, so the view can read the base tables even when the person running it cannot. Readers then need only `roles/bigquery.dataViewer` on the view's dataset plus the ability to create jobs in their own project. They see the view, not the tables behind it — the base tables do not even appear in their console. This is BigQuery's standard way to hand out a curated slice: hide sensitive columns, pre-filter rows, or join in reference data. Authorizing a whole dataset (an authorized dataset) does the same thing for every view it contains, which is what you want once you have dozens.

code

sql · 6 lines
sql
-- View lives in a serving dataset, never alongside the raw data
CREATE OR REPLACE VIEW `reporting.orders_public` AS
SELECT order_id, region, order_date, total_amount
FROM `raw.orders`
WHERE deleted_at IS NULL;
-- columns enumerated on purpose: SELECT * would publish new columns silently

go deeper

for a junior

Remember that a plain BigQuery view gives no protection — readers still need access to its source tables — and that the authorized-view mechanism is what removes that requirement.

for a middle

Be able to walk through the setup end to end: view in a separate dataset, view registered on the source dataset, readers granted on the serving dataset only.

for a senior

Discuss the operational edges: SELECT * leakage, who may edit the view, cost still billed on the underlying scan, and when to materialize the slice instead.

for a principal

Own the access architecture for the whole estate — authorized datasets versus per-view entries, where row-level and column-level controls belong instead, and how the pattern survives dozens of consuming teams.

## The problem it solves BigQuery access control is resource-scoped: you grant roles on a project, a dataset, or an individual table. A plain view stores only a query, so when a user selects from it, BigQuery evaluates that query as *them* — and they need read permission on every table it touches. That makes a plain view useless as a security boundary, because anyone who can query the view could equally query the raw table directly. ## How authorization works An authorized view inverts the check. The view is registered as a trusted principal on the dataset that holds the source data. From then on, the view's own identity carries the read permission, and the caller does not need any grant on the source dataset at all. In classic database terms this is definer's rights rather than invoker's rights: the query runs with the authority of the object, not the person. Two consequences follow directly. First, the view **must live in a different dataset from the data it reads** — if it sat in the same dataset, anyone who could read the view could already read the tables, and nothing would be gained. Second, whoever can *edit* the view can rewrite it to select anything in the source dataset, so edit rights on the view's dataset are as sensitive as read rights on the raw data. ## Setting one up Three moves: 1. Create the view in a serving dataset: `CREATE VIEW reporting.orders_public AS SELECT order_id, region, total FROM raw.orders WHERE deleted_at IS NULL;` 2. Add that view to the `raw` dataset's access list as an authorized view (console, Terraform, or `bq update --source` with the dataset's JSON). 3. Grant your consumers a reader role on `reporting` only, plus the ability to run jobs in whatever project pays for the queries. ## Authorized datasets and routines Managing a per-view entry on the source dataset does not scale past a handful. Authorizing an entire dataset makes every view inside it trusted, so adding a new curated view is a one-step operation. The same mechanism extends to routines — an authorized UDF or stored procedure can read source data that its caller cannot, which is how you expose a computation rather than a result set. ## What it does not solve **Cost.** Query jobs are billed to the project where the job runs — the reader's project — and the bytes billed are the bytes the underlying scan reads. A view is not a cache. Selecting three columns instead of thirty genuinely reduces bytes read because BigQuery is columnar, but the view itself adds no saving and adds no acceleration. **Freshness or performance.** A view is re-executed every time. If the exposed slice is expensive, materialize it — either as a scheduled table or a materialized view — and authorize that instead. **Per-user filtering.** One view exposes one fixed slice. If ten teams each need a different subset of rows, ten views is a maintenance problem; a row access policy on the base table expresses the same thing once. The historical trick is to filter inside the view with `SESSION_USER()` against a mapping table, which still works, but row-level security is the purpose-built answer. **Column-level governance.** Hiding a column by omitting it from the SELECT is only as good as your review process. Tagging the column in a taxonomy and requiring a fine-grained reader role enforces it wherever the column is read, including from tables you forget about. ## Pitfalls worth naming in an interview `SELECT *` in an authorized view is a slow-motion leak: add a sensitive column to the base table and it appears in the view without anyone deciding to publish it. Always enumerate columns. If the view's creator loses access to the source data, the view keeps working — the trust is on the view object, not the human — which is usually what you want but surprises people who expect it to break. Cross-project works fine; the view and the data can be in different projects as long as both are in the same location. Finally, an authorized view is a *read* surface. Users still cannot write, and anything that copies the view's output into a table under a broader identity re-opens whatever the view was hiding.

  • Why is an authorized dataset usually preferable to authorizing each view individually?
    Because the per-view entry lives on the source dataset's access list, every new curated view needs a change to a dataset you may not own, and the list grows without bound. Authorizing the serving dataset once makes every view it contains trusted, so publishing a new slice becomes a single CREATE VIEW in a dataset your team already controls.
  • Does querying through an authorized view make the query cheaper than querying the base table?
    Not by itself. The job is billed to the reader's project for the bytes the underlying scan reads, and the view is re-executed each time. You only save because the view reads fewer columns, or because its predicates hit partitioning and clustering on the base table. If the slice is expensive, materialize it and authorize the materialized result instead.
  • What stops a reader of an authorized view from simply reading the base table?
    They have no grant on the source dataset at all — the trust is attached to the view object, not to them. The real exposure is edit rights: anyone who can modify the view can rewrite its SELECT to pull anything in the source dataset, so write access on the view's dataset must be treated as sensitive as read access on the raw tables.

It works like a bank teller: customers have no key to the vault, but the teller does, and hands over only what the request entitles them to.

saying these in an interview costs you the question

  • Puts the authorized view in the same dataset as the source tables
  • Thinks readers still need dataViewer on the underlying tables
  • Uses SELECT * and assumes hidden columns stay hidden
  • Claims the view caches results and so cuts bytes billed
  • Ignores that edit rights on the view equal read rights on the source

context