@protected
The @protected
directive ensures that a user must be authenticated to access certain data.
directive @protected(
"""
Optional: A list of provider IDs that are required to access this field or type.
If omitted, authentication will be required from all providers.
To require access from specific providers, include multiple IDs.
"""
id: [String!]
) on OBJECT | FIELD_DEFINITION
The @protected
directive designates a type or field as protected, meaning that only authenticated users can access it.
Prerequisites
To use the @protected
directive, you must configure at least one authentication provider using the @link
directive, such as Htpasswd
or Jwks
.
schema
@server
@upstream
@link(id: "basic", type: Htpasswd, src: ".htpasswd_a")
@link(id: "jwt", type: Jwks, src: "jwks.json") {
query: Query
}
How It Works
The @protected
directive adds an authentication check to the resolver execution chain, ensuring that users meet the specified authentication criteria.
Key Features:
- Field-Level and Type-Level Protection:
- The directive can be applied to both object types and individual fields. for example
or
type Cat @protected {
meow: String
purr: String
}type Cat {
meow: String @protected
purr: String @protected
}
- Authentication Providers (
id
Argument):
- The optional
id
argument specifies the authentication providers required to access the data. - If multiple
id
values are provided, all listed providers are required to access the data. - If no
id
is provided, all configured providers are required.
- Query Planning Optimization:
- During query execution, the authentication requirements for all fields in a query are merged and moved to the top of the execution plan.
- This eliminates redundant authentication checks for each field, significantly improving performance.
Behavior with Multiple id
Values
When multiple id
values are provided in the @protected
directive, the system validates users against all the specified providers. For example:
type Cat {
meow: String @protected(id: ["a", "c"])
}
In this case:
- A user authenticated via provider
a
and providerc
is required to access themeow
field.
If no id
argument is provided:
- The system allows access to users authenticated via all configured providers.
Example Usage
Consider the following schema and authentication configuration:
Schema
schema
@server
@upstream
@link(id: "a", src: ".htpasswd_a", type: Htpasswd)
@link(id: "b", src: ".htpasswd_b", type: Htpasswd)
@link(id: "c", src: ".htpasswd_c", type: Htpasswd) {
query: Query
}
type Query {
animals: [Animal!]!
@expr(
body: [
{Dog: {bark: "woof"}}
{Cat: {meow: "meow"}}
{Bird: {tweet: "tweet"}}
]
)
}
union Animal = Dog | Cat | Bird | Fish | Snake
type Dog {
bark: String @protected(id: ["a"])
}
type Cat {
meow: String @protected(id: ["a", "c"])
}
type Bird {
tweet: String @protected
}
Authentication Files
.htpasswd_a
testuser1:$apr1$e3dp9qh2$fFIfHU9bilvVZBl8TxKzL/
testuser2:$2y$10$wJ/mZDURcAOBIrswCAKFsO0Nk7BpHmWl/XuhF7lNm3gBAFH3ofsuu
.htpasswd_b
testuser2:$2y$10$wJ/mZDURcAOBIrswCAKFsO0Nk7BpHmWl/XuhF7lNm3gBAFH3ofsuu
testuser3:{SHA}Y2fEjdGT1W6nsLqtJbGUVeUp9e4=
.htpasswd_c
testuser1:$apr1$e3dp9qh2$fFIfHU9bilvVZBl8TxKzL/
testuser3:{SHA}Y2fEjdGT1W6nsLqtJbGUVeUp9e4=
Scenarios
-
Accessing
Dog.bark
Field:- Authentication via provider
a
is required. - Allowed Users:
testuser1
,testuser2
.
- Authentication via provider
-
Accessing
Cat.meow
Field:- Authentication via providers
a
andc
is required. - Allowed Users:
testuser1
(froma
andc
).
- Authentication via providers
-
Accessing
Bird.tweet
Field:- Authentication via all configured providers is required.
- Allowed Users: None, as no user is present in all providers.
Type-Level vs Field-Level Protection
The @protected
directive can be applied at both the type and field levels. Here’s how they interact and the advantages of using each:
Type-Level Protection
Applying @protected
at the type level ensures all fields within the type are protected. This reduces redundancy, as you don’t need to annotate each field individually.
Field-Level Protection
If specific fields within a type require different authentication rules, you can apply @protected
at the field level. Field-level rules are merged with type-level rules.
Combined Use of Type and Field-Level Protection
When @protected
is applied at both the type and field levels, the rules are merged and moved to the top of the query execution plan. This ensures authentication checks are performed efficiently without redundant processing.
For example:
type Pet @protected(id: ["a"]) {
name: String
age: Int
breed: String @protected(id: ["c"])
}
Explanation:
-
Type-Level Rule (
id: ["a"]
):- Protects all fields (
name
,age
, andbreed
). - Requires authentication via provider
a
.
- Protects all fields (
-
Field-Level Rule (
breed
withid: ["c"]
):- Adds provider
c
for thebreed
field.
- Adds provider
-
Merged Authentication Rule:
- For fields like
name
andage
, authentication via providera
suffices. - For the
breed
field, authentication via botha
andc
is required.
- For fields like