Skip to main content
Version: 8

How To: Create and Manage API Security Metadata using Declarative Security Policies

Introduction​

The Ed-Fi API security metadata is managed by the configuration service and is logically organized into a taxonomy. The security metadata is authored as a JSON document that is loaded into the Configuration Service at startup or at runtime via the Configuration Service API.

Choosing an Approach​

The Configuration Service provides several mechanisms for managing claims, each suited to different scenarios:

ScenarioApproach
Create or replace a custom claim set with specific resource permissionsPOST /v3/claimSets/import
Override authorization strategies for a specific claim set on a resourcePOST /v3/claimSets/import
Change one resource claim's actions or overrides on an existing claim set/v3/claimSets/{id}/resourceClaimActions endpoints
Change the default authorization strategy for a resource or domain (dev/test)POST /management/upload-claims
Add extension resources to the claims taxonomy (dev/test)POST /management/upload-claims
Restructure domain groupings or remove claims from the taxonomy (dev/test)POST /management/upload-claims
Change the default authorization strategy for a resource or domain (production)Deployment-time loading
Add extension resources to the claims taxonomy (production)Deployment-time loading
Restructure domain groupings or remove claims from the taxonomy (production)Deployment-time loading

POST /v3/claimSets/import operates at the claim set level. It defines which resources a claim set can access and with what actions. If no claim set has the name, it creates one. If a claim set that isn't system-reserved already has the name, the import replaces its grants. Built-in claim sets such as SISVendor are system-reserved and can't be imported; copy one first. Only the imported claim set is affected, and the unrestricted claims loading flag isn't required. Resource claims that aren't in the taxonomy are skipped and listed in the response's warnings, so check them after every import. See How To: Configure Claim Sets for examples.

The /v3/claimSets/{id}/resourceClaimActions endpoints change one resource claim on an existing claim set that isn't system-reserved: grant or remove actions, or override or reset its authorization strategies. You don't need to re-import the whole claim set. See Managing a Claim Set's Resource Claims.

Claim set import and the resourceClaimActions endpoints accept only built-in authorization strategies. To use a custom view-based authorization strategy, change the claims document instead, with upload-claims or deployment-time loading.

POST /management/upload-claims replaces the entire claims hierarchy atomically. Use it when the change involves the taxonomy itself: default authorization strategies, adding or removing resource claims, or domain-level restructuring. This requires CONFIGURATION_SERVICE_DANGEROUSLY_ENABLE_UNRESTRICTED_CLAIMS_LOADING=true and should only be used in non-production environments.

Deployment-time loading is the production-safe way to make taxonomy-level changes. The Configuration Service loads claims from an embedded resource, a directory of fragment files, or a complete claims file — controlled by the CONFIGURATION_SERVICE_CLAIMS_SOURCE environment variable. No dangerous flag is required. Claims are loaded only when the Configuration Service database is first initialized; see When Claims Are Loaded before changing the claims of an existing deployment. See Loading Modes for details.

caution

Both API approaches write only to the Configuration Service database; the configured claims source (such as the embedded Claims.json resource) is not modified. Changes survive redeployment as long as the database persists, but will be lost if the database is recreated or cleared, since startup seeding restores only what the configured claims source contains. For changes that must survive a fresh database, update the claims source directly and use deployment-time loading.

Security Metadata Overview​

In this section, the building blocks for defining a security policy are introduced, along with their JSON representations.

The Security Metadata Format​

The claims hierarchy is a JSON array of domain claim objects. Each domain claim can contain child resource claims, default authorization metadata, and per-claim-set permission assignments:

[
{
"name": "http://ed-fi.org/identity/claims/domains/people",
"defaultAuthorization": { ... },
"claimSets": [ ... ],
"claims": [ ... ]
}
]

The examples throughout this section all use this array format. A complete claims document wraps the array as its claimsHierarchy property, next to a claimSets catalog; see Building a Security Policy. How the document is delivered to the Configuration Service depends on the loading mode; see Loading Your Security Policy.

Managing the Claims Taxonomy​

Individual API resources are represented as "leaf nodes" (aka resource claims) that are grouped under higher-level domain claims, as depicted in the table below.

peoplehttp://ed-fi.org/identity/claims/domains/people
⌊_ contacthttp://ed-fi.org/identity/claims/ed-fi/contact
⌊_ staffhttp://ed-fi.org/identity/claims/ed-fi/staff
⌊_ studenthttp://ed-fi.org/identity/claims/ed-fi/student

The JSON representation of this structure uses the claims array to nest resource claims under their parent domain claim:

[
{
"name": "http://ed-fi.org/identity/claims/domains/people",
"claims": [
{ "name": "http://ed-fi.org/identity/claims/ed-fi/contact" },
{ "name": "http://ed-fi.org/identity/claims/ed-fi/staff" },
{ "name": "http://ed-fi.org/identity/claims/ed-fi/student" }
]
}
]
info

In general, when adding new claims to the taxonomy, it is best to define domain claims that organize the resource claims according to their business-oriented domains rather than according to the similarities in the authorization approach.

note

Claim URIs in Ed-Fi API v8 use the pattern http://ed-fi.org/identity/claims/..., without the /ods/ segment used in previous ODS/API versions. Update any existing security policies accordingly.

Default Authorization Metadata​

Each claim may have default authorization metadata associated with it, which tells the API which authorization strategy (or strategies) to use when authorizing API requests that are performing specific actions (i.e. CRUD operations). This default authorization metadata applies to all descendants in the taxonomy unless overridden for a specific claim set (aka "role") and domain/resource claim.

The out-of-the-box default authorization metadata for the people domain claim is represented in JSON as follows:

[
{
"name": "http://ed-fi.org/identity/claims/domains/people",
"defaultAuthorization": {
"actions": [
{
"name": "Create",
"authorizationStrategies": [
{ "name": "NoFurtherAuthorizationRequired" }
]
},
{
"name": "Read",
"authorizationStrategies": [
{ "name": "RelationshipsWithEdOrgsAndPeople" }
]
},
{
"name": "Update",
"authorizationStrategies": [
{ "name": "RelationshipsWithEdOrgsAndPeople" }
]
},
{
"name": "Delete",
"authorizationStrategies": [
{ "name": "NoFurtherAuthorizationRequired" }
]
},
{
"name": "ReadChanges",
"authorizationStrategies": [
{ "name": "RelationshipsWithEdOrgsAndPeopleIncludingDeletes" }
]
}
]
}
}
]

This tells the API that unless otherwise specified, it should use the NoFurtherAuthorizationRequired strategy for Create and Delete operations, the RelationshipsWithEdOrgsAndPeople strategy for Read and Update operations, and RelationshipsWithEdOrgsAndPeopleIncludingDeletes for the Change Queries /deletes and /keyChanges endpoints (the ReadChanges action).

caution

Once established and in use by descendant resource claims, great care should be taken when modifying the default authorization strategies. Changes at this level will affect all descendant resource claims that haven't specified explicit authorization strategies. The intent of default authorization metadata is to reduce unnecessary redundancy, so you must be careful not to introduce unintended side-effects when changing defaults already in use.

Claim Set Permissions​

Claim sets are the "roles" of the API, to which API clients are assigned. You can assign permissions to claim sets on either domain or resource claims, but the goal is to organize the taxonomy so that most assignments are made to domain claims because those permissions will then apply to all the descendant resource claims as well.

The out-of-the-box SISVendor claim set is granted full CRUD permissions for all people (including the descendant resource claims of contact, staff, and student). This is represented in JSON as follows:

[
{
"name": "http://ed-fi.org/identity/claims/domains/people",
"claimSets": [
{
"name": "SISVendor",
"actions": [
{ "name": "Create" },
{ "name": "Read" },
{ "name": "Update" },
{ "name": "Delete" }
]
}
]
}
]

Claim Set Authorization Strategy Overrides​

In some cases, it may be necessary to override the authorization strategies used to authorize specific requests for API clients assigned to a particular claim set.

For example, consider an SEA-level implementation where multiple vendors may create data that is visible to other vendors, but only the creator should be able to update or delete it. In this case, the OwnershipBased authorization strategy can be used as an override on the Update and Delete actions.

The out-of-the-box default authorization metadata for the relationshipBasedData domain applies the general-purpose relationship-based strategy to the CRUD actions, and its IncludingDeletes variant to ReadChanges. The following JSON adds the SISVendor claim set with ownership-based overrides on Update and Delete:

[
{
"name": "http://ed-fi.org/identity/claims/domains/relationshipBasedData",
"defaultAuthorization": {
"actions": [
{
"name": "Create",
"authorizationStrategies": [
{ "name": "RelationshipsWithEdOrgsAndPeople" }
]
},
{
"name": "Read",
"authorizationStrategies": [
{ "name": "RelationshipsWithEdOrgsAndPeople" }
]
},
{
"name": "Update",
"authorizationStrategies": [
{ "name": "RelationshipsWithEdOrgsAndPeople" }
]
},
{
"name": "Delete",
"authorizationStrategies": [
{ "name": "RelationshipsWithEdOrgsAndPeople" }
]
},
{
"name": "ReadChanges",
"authorizationStrategies": [
{ "name": "RelationshipsWithEdOrgsAndPeopleIncludingDeletes" }
]
}
]
},
"claimSets": [
{
"name": "SISVendor",
"actions": [
{ "name": "Create" },
{ "name": "Read" },
{
"name": "Update",
"authorizationStrategyOverrides": [
{ "name": "RelationshipsWithEdOrgsAndPeople" },
{ "name": "OwnershipBased" }
]
},
{
"name": "Delete",
"authorizationStrategyOverrides": [
{ "name": "RelationshipsWithEdOrgsAndPeople" },
{ "name": "OwnershipBased" }
]
}
]
}
]
}
]

Notice that the Create and Read actions rely on the defaultAuthorization defined for the domain claim rather than repeating the strategy locally, which would be redundant.

The OwnershipBased override works only after the vendors' API clients are assigned ownership tokens. It denies access to records that have no owner, such as records created before the creating client had a creator token. See Ownership-Based Authorization for setup.

Building a Security Policy​

Each of the examples in the previous sections is a partial view. A complete policy is a claims document with two top-level properties:

{
"claimSets": [
{ "claimSetName": "SISVendor", "isSystemReserved": true }
],
"claimsHierarchy": [ ... ]
}
  • claimSets registers every claim set name and whether it is system-reserved (the claim set endpoints can't change or delete it). List every claim set that the hierarchy grants access to. A grant to a claim set that isn't listed here has no effect.
  • claimsHierarchy is the array described in The Security Metadata Format. It contains every domain and resource claim you want to be active, with all defaultAuthorization, claimSets, and claims entries in place.

Any claim, claim set assignment, or authorization strategy omitted from the document will be absent from the active configuration. The Filesystem loading mode and the runtime upload endpoint both expect this complete document; the embedded resource uses the same format.

There are two practical starting points for a complete policy:

If you are migrating from a previous Ed-Fi ODS/API deployment, the Ed-Fi API Security Metadata Tools converts ODS/API XML security metadata into a complete document with top-level claimSets and claimsHierarchy properties. Upload the generated file unchanged to POST /management/upload-claims, using the administrative token and unrestricted-claims-loading setting described in Runtime Upload via Configuration Service. The converter emits editable imported sets (isSystemReserved: false); an already-present reserved set with the same name retains its stored reserved status during upload.

To add extension resources without maintaining a complete document, use claims fragments instead; see Introducing Extension Resources into the Taxonomy.

To review the result of your fragments before you deploy them, build the complete document offline with the merge command of the Ed-Fi API Security Metadata Tools. It applies the fragments in a directory to the bundled base claims for a data standard version. Use the same version as the Configuration Service's CONFIGURATION_SERVICE_DATA_STANDARD_VERSION. The output folder must already exist:

New-Item -ItemType Directory -Force ./policy-output | Out-Null
security-metadata-tools merge --fragments ./fragments `
--data-standard-version 5.2 --output ./policy-output/Claims.json

Use the version of the tool that matches the Configuration Service you deploy. The result is a complete document that you can deploy with Filesystem loading or upload. Don't also deploy the fragments it already includes.

Adding New Claims​

When the hierarchy is loaded, the Configuration Service automatically derives a resource claim record for every claim name in the array and registers it in its internal catalog (insert-only, idempotent). No separate registration step is needed.

Adding Authorization Strategies and Actions​

Actions can't be defined through the policy document; use the actions built into the service. An authorization strategy must be either a built-in strategy or the name of a custom authorization view. The Ed-Fi API treats a name that isn't built in as a view in the auth schema. See How To: Use Custom View-Based Authorization.

Introducing Extension Resources into the Taxonomy​

To add a new resource under an existing domain claim, include it nested under its intended parent in the claims array:

[
{
"name": "http://ed-fi.org/identity/claims/domains/someDomain",
"claims": [
{
"name": "http://ed-fi.org/identity/claims/myext/myNewResource"
}
]
}
]

The new claim inherits the domain's defaultAuthorization unless you specify its own. Include any claimSets entries needed to grant access to it.

Alternatively, write a claims fragment and let the Configuration Service merge it into the base claims at startup using the Hybrid loading mode. You don't need to produce a merged document yourself. For complete worked examples, see Step 5d of How To: Extend the Ed-Fi API - Student Transcript Example and How To: Extend the Ed-Fi API - Alternative Education Program Example.

Omitting Claims and Claim Sets​

Because loading a policy fully replaces the active hierarchy, any claim, claim set assignment, or authorization strategy omitted from the final security metadata file will be removed from the active configuration. Ensure the array represents the complete intended state before loading it.

caution

The entire claims hierarchy is replaced atomically. Partial updates are not supported. Always start from the current hierarchy and modify it rather than authoring a document from scratch.

Claims Fragments​

A claims fragment is a small JSON file that adds to the claims the Configuration Service already has, rather than replacing them. Use fragments to add extension resources, add a new domain, or give a claim set access to existing claims, without maintaining a complete claims document. The Configuration Service merges fragments into its base claims when it loads claims in the Hybrid or Filesystem loading mode.

Before deployment, use the Security Metadata Tools' merge command to check your fragments against the bundled base for the same data standard. It fails on incomplete composition and policy that the service silently ignores, including policy on matched parent entries and retired *-claimset.json files. The Configuration Service's loading behavior is unchanged; these are additional offline checks.

Fragment Structure​

A fragment has a top-level name and a resourceClaims array. Each entry in resourceClaims is one of two kinds:

  • A parent entry has "isParent": true. It adds new claims to the hierarchy: resource claims under an existing domain, or a new domain.
  • A grant entry has no isParent (or "isParent": false). It gives the fragment's claim set access to a claim that already exists, for the actions listed in its authorizationStrategyOverridesForCRUD.

On a grant entry, actions and its enabled values are informational. The requested grants come only from authorizationStrategyOverridesForCRUD: enabling an action without an override doesn't grant it, and an override requests its action even if actions is absent or marks it disabled. These flags don't deny or revoke existing or inherited access. This rule applies to grant entries, not the claimSets[].actions on a new parent entry.

The top-level name is the claim set that the fragment's grant entries give access to. Always set it on a fragment with grant entries. If it's missing, the service uses the file name as the claim set name (for example, 003-sisvendor-claims) and creates a new system-reserved claim set with that name, which the claim set endpoints can't change or delete. In a fragment with only parent entries, name is just a label.

Fragments use different property names from the claims hierarchy described in The Security Metadata Format. Use the fragment names in a fragment file:

PurposeClaims hierarchyFragment
Child claimsclaimschildren
Default authorizationdefaultAuthorization.actions[].name_defaultAuthorizationStrategiesForCrud[].actionName
Claim set permissionsclaimSetsclaimSets on a parent entry, or a grant entry
Claim set strategy overridesauthorizationStrategyOverrides in claimSetsauthorizationStrategyOverrides in a parent entry's claimSets, or authorizationStrategies in a grant entry's authorizationStrategyOverridesForCRUD

File Names and Order​

The service applies every file whose name ends in -claims.json anywhere under the configured claims directory, including subdirectories, one at a time, sorted by file path. Start each file name with a number such as 001- or 002- to control the order, and keep all your fragments in one folder: a number prefix only orders files within the same folder. Order matters when one fragment refers to a claim that an earlier fragment adds.

The suffix must be lowercase. The Configuration Service runs on Linux, where the match is case-sensitive, so the Configuration Service skips a file ending in -Claims.json without a warning.

Before v8.1, fragment file names ended in -claimset.json. The service no longer reads those files, so rename each one to end in -claims.json. The service logs a warning for each -claimset.json file it finds, but only when it reads fragments (see When Claims Are Loaded), so an in-place upgrade of an existing database logs no warning at startup.

The Docker Compose files changed too. In v8.0 they read the fragment folder from DMS_CONFIG_CLAIMS_MOUNT_SOURCE, which is now CONFIGURATION_SERVICE_CLAIMS_MOUNT_SOURCE, and their default folder moved from Deploy/AdditionalClaimsets to Deploy/AdditionalClaims. If you set DMS_CONFIG_CLAIMS_MOUNT_SOURCE, rename it to CONFIGURATION_SERVICE_CLAIMS_MOUNT_SOURCE. Otherwise Compose mounts the default folder instead of yours, and the service logs no warning.

Renaming needs care for a fragment with grant entries but no top-level name. This applies only to fragments the Configuration Service reads directly, because prepare-dms-claims.ps1 rejects such fragments. The service attaches that fragment's grants to a claim set named after the file. In v8.0, applications could use those grants only if a claim set with that name was created separately through the claim set endpoints. Renaming the file moves the grants to a new name, so those applications lose their access. To keep their access, set the fragment's top-level name to the old file name without .json, for example "003-sisvendor-claimset", before you rename the file. From v8.1, that name becomes a system-reserved claim set (see Choosing Claim Set Names).

Referring to Existing Claims​

To point at a claim that already exists, use its short form: the last two segments of its URI, such as domains/systemDescriptors or ed-fi/student. Case doesn't matter. A full URI never matches an existing claim. Use full URIs only for claims you are adding, such as a new domain or the children you add under a domain.

Adding Resource Claims to an Existing Domain​

To add resource claims under an existing domain, write a parent entry that names the domain by its short form and lists the new resource claims as children. The following fragment adds an extension descriptor under the out-of-the-box systemDescriptors domain and an extension association under the relationshipBasedData domain:

001-my-extension-claims.json
{
"name": "MyExtensionClaims",
"resourceClaims": [
{
"isParent": true,
"name": "domains/systemDescriptors",
"children": [
{ "name": "http://ed-fi.org/identity/claims/my-extension/MyDescriptor" }
]
},
{
"isParent": true,
"name": "domains/relationshipBasedData",
"children": [
{
"name": "http://ed-fi.org/identity/claims/my-extension/MyAssociation"
}
]
}
]
}

This tells the Configuration Service to add MyDescriptor and MyAssociation as resource claims under those domains. They inherit each domain's default authorization and claim set permissions, so every claim set that can already access the domain gets the same access to the new resource claims.

Any claimSets or _defaultAuthorizationStrategiesForCrud on a parent entry that names an existing domain are ignored. To give different access to the new resource claims, put them in a new domain or use grant entries.

Adding a New Domain​

To add a domain, write a parent entry whose name is the full URI of the new domain, with its default authorization, its resource claims, and the claim sets that can access it:

002-my-extension-domain-claims.json
{
"name": "MyExtensionDomainClaims",
"resourceClaims": [
{
"isParent": true,
"name": "http://ed-fi.org/identity/claims/domains/myExtension",
"_defaultAuthorizationStrategiesForCrud": [
{
"actionName": "Create",
"authorizationStrategies": [
{ "name": "NoFurtherAuthorizationRequired" }
]
},
{
"actionName": "Read",
"authorizationStrategies": [
{ "name": "NoFurtherAuthorizationRequired" }
]
},
{
"actionName": "Update",
"authorizationStrategies": [
{ "name": "NoFurtherAuthorizationRequired" }
]
},
{
"actionName": "Delete",
"authorizationStrategies": [
{ "name": "NoFurtherAuthorizationRequired" }
]
}
],
"children": [
{
"name": "http://ed-fi.org/identity/claims/my-extension/MyNewResource"
}
],
"claimSets": [
{
"name": "EdFiSandbox",
"actions": [
{ "name": "Create" },
{ "name": "Read" },
{ "name": "Update" },
{ "name": "Delete" }
]
}
]
}
]
}

This tells the Configuration Service to add a top-level myExtension domain that contains MyNewResource, to use NoFurtherAuthorizationRequired for every action unless a claim set overrides it, and to give EdFiSandbox full CRUD access. No other claim set can access MyNewResource until it is granted.

Granting a Claim Set Access to an Existing Claim​

To give a claim set access to a claim that already exists, write a fragment named after that claim set, with a grant entry for each claim. List each granted action in authorizationStrategyOverridesForCRUD, optionally with authorization strategies that override the defaults for that action. The following fragment grants the out-of-the-box SISVendor claim set access to the resource claim added by the previous example:

003-sisvendor-claims.json
{
"name": "SISVendor",
"resourceClaims": [
{
"name": "my-extension/MyNewResource",
"authorizationStrategyOverridesForCRUD": [
{ "actionName": "Read" },
{
"actionName": "Update",
"authorizationStrategies": [{ "name": "NamespaceBased" }]
}
]
}
]
}

This tells the Configuration Service that SISVendor can read MyNewResource using the domain's default authorization strategy, and can update it using NamespaceBased instead. A grant entry can refer to a claim added by an earlier entry or an earlier fragment, which is why this file is numbered after the one that adds the domain.

Choosing Claim Set Names​

Use only claim sets that the base claims document already declares, both in a parent entry's claimSets and as the name of a fragment with grant entries. An undeclared claim set in a parent entry's claimSets is ignored. An undeclared name on a fragment with grant entries creates a new system-reserved claim set, which the claim set endpoints can't change or delete and which stays even if you remove the fragment. prepare-dms-claims.ps1 rejects both.

Rules and Common Mistakes​

  • Refer to an existing claim by its short form. If you write an existing domain's full URI in a parent entry, the service doesn't find the domain and adds a second, duplicate domain.
  • In a parent entry, name a domain, not a resource claim, and list only direct children. Children under a resource claim, or nested inside another child, are ignored.
  • claimSets belongs only on parent entries. On a grant entry the service ignores it, and prepare-dms-claims.ps1 rejects the file.
  • On an existing matched parent, claimSets and _defaultAuthorizationStrategiesForCrud are ignored. merge rejects these fields when they contain policy. Put grants in grant entries instead.
  • Use only a name on each child in a parent entry's children list. The service copies that name, not policy nested on the child; merge rejects unapplied child policy.
  • Put authorizationStrategyOverridesForCRUD on grant entries, not parent entries. The service ignores it on parents; merge rejects it when nonempty.
caution

Fragment mistakes don't always produce an error:

  • A grant entry whose claim isn't found is skipped.

  • If a fragment isn't valid JSON, the service logs only a warning and loads the base claims without any fragments, including the valid ones:

    • On a first start, only the base claims are stored. Fixing the file and restarting doesn't help, because claims load only into an empty database.
    • On a reload, the request succeeds but removes every claim that the fragments added.

    To recover, fix the file. In a non-production environment, then call POST /management/reload-claims (requires CONFIGURATION_SERVICE_DANGEROUSLY_ENABLE_UNRESTRICTED_CLAIMS_LOADING=true). In production, start with a new Configuration Service database.

  • Other mistakes, including a fragment file the service doesn't have permission to read, stop the claims from loading. On a first start, the service doesn't start. On a reload, the request fails with a 500 error and the stored claims don't change.

After deploying fragments, check the Configuration Service log and verify the result as described in Verifying the Active Policy.

Loading Your Security Policy​

Loading Modes​

The Configuration Service controls how claims are loaded at startup via the CONFIGURATION_SERVICE_CLAIMS_SOURCE environment variable. Three source modes are available: Embedded, Hybrid, and Filesystem. Claims can also be replaced at runtime via the management API, independently of the configured source mode.

Whichever mode you choose, the service seeds its database from that source only when the database holds no claims yet. See When Claims Are Loaded.

The variable names on this page are the ones used by the Docker Compose files that ship with the Ed-Fi API. Compose passes each one to the Configuration Service as a setting. If you run the Configuration Service without those Compose files, set the setting directly instead:

PurposeDocker Compose variableConfiguration Service setting
Claims sourceCONFIGURATION_SERVICE_CLAIMS_SOURCEClaimsOptions__ClaimsSource
Claims directoryCONFIGURATION_SERVICE_CLAIMS_DIRECTORYClaimsOptions__ClaimsDirectory
Data standard versionCONFIGURATION_SERVICE_DATA_STANDARD_VERSIONClaimsOptions__DataStandardVersion
Runtime claims changesCONFIGURATION_SERVICE_DANGEROUSLY_ENABLE_UNRESTRICTED_CLAIMS_LOADINGClaimsOptions__DangerouslyEnableUnrestrictedClaimsLoading
Database setup at startupCONFIGURATION_SERVICE_DEPLOY_DATABASEAppSettings__DeployDatabaseOnStartup
Folder on your machine to mount as the claims directoryCONFIGURATION_SERVICE_CLAIMS_MOUNT_SOURCENone. Compose mounts it at /app/additional-claims; without Compose, mount the folder yourself

Embedded (Production Default)​

CONFIGURATION_SERVICE_CLAIMS_SOURCE=Embedded

The Claims/Standards/ds{version}/Claims.json file in the EdFi.DmsConfigurationService.Claims project is compiled into the Configuration Service assembly as an embedded resource. On startup, if the database tables are empty, the service seeds the ClaimSet, ResourceClaim, and ClaimsHierarchy tables from that embedded file. The embedded file changes only when the service is rebuilt, and deploying a rebuilt service doesn't update the claims of an existing database (see When Claims Are Loaded). The data standard version to load is selected by CONFIGURATION_SERVICE_DATA_STANDARD_VERSION (default: 5.2).

This is the most secure option for production: no external files, no runtime modifications.

Embedded is what the service uses when no claims source is set. The sample environment files that ship with the Docker Compose setup (.env.example and .env.template) set Hybrid instead, so a local environment started from them uses Hybrid mode.

Hybrid​

CONFIGURATION_SERVICE_CLAIMS_SOURCE=Hybrid
CONFIGURATION_SERVICE_CLAIMS_DIRECTORY=/app/additional-claims

Uses the same embedded base as Embedded mode, and applies the claims fragments found in the configured directory on top of it. This is the recommended way to add extension resources and extension claim sets without maintaining a complete claims document.

The fragment directory must be readable by the Configuration Service. The Docker Compose files mount a folder from your machine into the container at /app/additional-claims. Set CONFIGURATION_SERVICE_CLAIMS_MOUNT_SOURCE to the folder that holds your fragments. If you deploy without those Compose files, mount your folder yourself and set the claims directory to the path inside the container. For a complete walkthrough, see Step 5d of the extension how-to guides linked in Introducing Extension Resources into the Taxonomy.

Filesystem​

CONFIGURATION_SERVICE_CLAIMS_SOURCE=Filesystem
CONFIGURATION_SERVICE_CLAIMS_DIRECTORY=/app/additional-claims

The embedded Claims.json is not used. Instead, the service reads a complete claims document (see Building a Security Policy) from a file named Claims.json in the configured directory. The service also looks in subdirectories, and it stops with an error if it finds no Claims.json or more than one. Any claims fragments in the directory are applied on top of it. This is for deployments that manage their own complete claims document outside the service binary.

When Claims Are Loaded​

At startup, the Configuration Service seeds its database from the configured claims source only when the database holds no claims yet, which is normally the first start against a new database. On every later start it logs Claims data already exists, skipping initial load and keeps the claims already stored in the database. Seeding also requires database setup at startup to be on (CONFIGURATION_SERVICE_DEPLOY_DATABASE, on by default).

This means that for an existing deployment, restarting the service after any of these changes does not change the active claims:

  • adding, editing, or removing claims fragments;
  • editing the Claims.json used by Filesystem mode;
  • changing CONFIGURATION_SERVICE_CLAIMS_SOURCE or CONFIGURATION_SERVICE_DATA_STANDARD_VERSION.

For a new deployment, put the claims source in place (embedded, fragments, or Claims.json) before the first start, and the service loads it automatically.

For an existing deployment, the stored claims can only be replaced through the management API: POST /management/reload-claims reloads from the configured source, including fragments, and POST /management/upload-claims replaces the claims with a document you supply. Both require CONFIGURATION_SERVICE_DANGEROUSLY_ENABLE_UNRESTRICTED_CLAIMS_LOADING=true; see Runtime Upload via Configuration Service. Changes limited to individual claim sets can be made with the claim set endpoints instead; see Choosing an Approach.

A reload or upload deletes every claim set that isn't system-reserved and isn't in the claims source, including claim sets created through the claim set endpoints. Before reloading or uploading, export those claim sets (GET /v3/claimSets/{id}/export) and re-import them afterward (POST /v3/claimSets/import).

In this release, production deployments have no supported way to change the full claims policy of an existing database. To apply a new full claims policy, provision a new Configuration Service database. A new database doesn't carry over vendors, applications, client credentials, or data store registrations, so plan to recreate them.

Runtime Upload via Configuration Service​

Runtime upload is a separate capability that can be combined with any of the three source modes above. It is enabled by:

CONFIGURATION_SERVICE_DANGEROUSLY_ENABLE_UNRESTRICTED_CLAIMS_LOADING=true

When enabled, the following management API endpoints become available:

  • GET /management/current-claims : retrieve the active claims document
  • POST /management/upload-claims : atomically replace the active claims
  • POST /management/reload-claims : reload from the configured source
caution

CONFIGURATION_SERVICE_DANGEROUSLY_ENABLE_UNRESTRICTED_CLAIMS_LOADING=true enables runtime modification of security policies and should never be enabled in production deployments.

The management API works with a container document that wraps the hierarchy array together with a claim set catalog:

{
"claimSets": [
{ "claimSetName": "SISVendor", "isSystemReserved": true }
],
"claimsHierarchy": [ ... ]
}

The claimSets array registers every claim set name and whether it is system-reserved (the claim set endpoints can't change or delete it). The claimsHierarchy array uses the same JSON array format described in The Security Metadata Format. Any new claim set referenced in the hierarchy must also appear in claimSets.

GET /management/current-claims returns this same two-section document, and POST /management/upload-claims accepts it unchanged, so a document retrieved from the service can be edited and posted back exactly as it was downloaded.

These management endpoints require a Configuration Service bearer token. Write operations (POST /management/upload-claims, POST /management/reload-claims) require the edfi_admin_api/full_access scope; the read operation (GET /management/current-claims) accepts edfi_admin_api/readonly_access or edfi_admin_api/full_access. Obtain a token:

$auth = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("<config-admin-key>:<config-admin-secret>"))
$token = Invoke-RestMethod -Method Post -Uri "http://localhost:8081/connect/token" `
-Headers @{ "Authorization" = "Basic $auth" } `
-ContentType "application/x-www-form-urlencoded" `
-Body @{
"grant_type" = "client_credentials"
"scope" = "edfi_admin_api/full_access"
}

Retrieve the current claims to use as a starting point:

Invoke-RestMethod -Uri "http://localhost:8081/management/current-claims" `
-Headers @{ Authorization = "Bearer $($token.access_token)" } |
ConvertTo-Json -Depth 100 |
Set-Content claims.json

Modify the claimsHierarchy array in the downloaded file. Add any new claim set names to the claimSets catalog as well.

Upload the modified document to replace the active claims hierarchy. The endpoint accepts the two-section document exactly as downloaded, so post the file contents directly as the request body:

Invoke-RestMethod -Method Post `
-Uri "http://localhost:8081/management/upload-claims" `
-Headers @{ Authorization = "Bearer $($token.access_token)" } `
-ContentType "application/json" `
-Body (Get-Content claims.json -Raw)

The uploaded document must include both the claimSets catalog and the claimsHierarchy array as top-level properties, as {"claimSets": [...], "claimsHierarchy": [...]}.

An upload or reload replaces the claims hierarchy, including the permissions of system-reserved claim sets. It is not a complete replacement of everything, though:

  • Claim sets that aren't system-reserved are replaced by the ones in the document. System-reserved claim sets are never removed, even if the document leaves them out. Their permissions still come only from the uploaded hierarchy, so a system-reserved claim set the document doesn't grant anything to has no access afterward.
  • The claim sets and the hierarchy are replaced together, in a single step.

Verifying the Active Policy​

To inspect the policy stored by the Configuration Service, call GET /v3/authorizationMetadata. It is the same data the Ed-Fi API loads to authorize requests, it reflects the claims stored in the Configuration Service database, and it is available in every loading mode without CONFIGURATION_SERVICE_DANGEROUSLY_ENABLE_UNRESTRICTED_CLAIMS_LOADING. It accepts a token with the edfi_admin_api/full_access, edfi_admin_api/readonly_access, or edfi_admin_api/authMetadata_readonly_access scope.

Invoke-RestMethod -Uri "http://localhost:8081/v3/authorizationMetadata?claimSetName=SISVendor" `
-Headers @{ Authorization = "Bearer $($token.access_token)" } |
ConvertTo-Json -Depth 10

Omit claimSetName to return every claim set. Check the spelling of the name: an unknown name returns 200 with an empty array ([]) rather than an error. An empty array means no claim set has that name. A claim set that exists but has no access is still listed, with an empty claims list.

The response doesn't repeat your policy document. Instead, for each claim set it lists every resource claim the claim set can access, with the effective actions and authorization strategies after inheritance and defaults are resolved. An abridged example:

[
{
"claimSetName": "SISVendor",
"claims": [
{
"name": "http://ed-fi.org/identity/claims/ed-fi/academicWeek",
"authorizationId": 1
}
],
"authorizations": [
{
"id": 1,
"actions": [
{
"name": "Read",
"authorizationStrategies": [
{ "name": "RelationshipsWithEdOrgsOnly" }
]
}
]
}
]
}
]

Use it to confirm that new resource claims appear for the claim sets you expected, with the actions and strategies you intended. Domain claims don't appear in the response, only the resource claims beneath them.

To compare a complete claims document with that stored metadata, use the verify command of the Ed-Fi API Security Metadata Tools. Use the tool from the matching Configuration Service release. Pass the complete claims document, not the inspection output from resolve. It reads GET /v3/authorizationMetadata and reports every claim set, claim, action, or strategy that differs from the document. Pass the name of an environment variable that holds a Configuration Service token, not the token itself:

$env:CMS_ACCESS_TOKEN = $token.access_token
security-metadata-tools verify --input ./policy-output/Claims.json `
--cms-url http://localhost:8081 --access-token-env CMS_ACCESS_TOKEN
Remove-Item Env:CMS_ACCESS_TOKEN

For a multi-tenant deployment, add --tenant <tenant-name>. verify compares every claim set, so system-reserved claim sets kept by an upload but missing from the document are reported as unexpected. It compares effective permissions, including the order of strategies within each action. Exit code 0 means all metadata matches, 2 means valid metadata differs, and 1 means the command failed, for example because authentication failed or a response was invalid.

A match confirms the Configuration Service metadata at the time of the request. It doesn't prove that the Ed-Fi API's authorization cache is current or that live API requests enforce the intended policy. Verify those requests separately; verify doesn't change policy or refresh caches.

note

GET /management/current-claims isn't a good substitute. It returns 404 unless CONFIGURATION_SERVICE_DANGEROUSLY_ENABLE_UNRESTRICTED_CLAIMS_LOADING is set. It also returns the claims document the service most recently loaded or received, which isn't guaranteed to match what is stored in the database. For example, changes made through the claim set endpoints aren't reflected in it. Use GET /v3/authorizationMetadata to inspect stored effective permissions.