Manage Access Grants#

Upon receipt of an Access Request, resource owners can use an Access Management app to approve or deny the Access Request:

  • For an approved request, Inrupt’s Enterprise Solid Server (ESS) creates an Access Grant.

  • For a denied request, Inrupt’s Enterprise Solid Server (ESS) creates an Access Denial.

Access Requests and Grants

The following Inrupt products are available to support Access Requests and Grants:

  • solid-client-access-grants library for managing Access Requests and Grants

  • Inrupt’s Enterprise Solid Server (ESS) provides support for Access Requests and Grants. ESS serializes the Access Requests and Grants as Verifiable Credentials (VCs).

  • Inrupt’s Authorization Management Component supports Access Request management. New as part of ESS 2.2

solid-client-access-grants API#

Inrupt’s solid-client-access-grants library provides various functions for approving or denying Access Requests; for example:

approveAccessRequest

Approves the request and returns an approved Access Grant, serialized as a signed Verifiable Credential. The Access Grants may be used to get access to specified resources.

A server-side code can use the function to create the Grant.

Note

ESS uses ACP policy to enables the use of Access Grants for a resource. The approveAccessRequest function, by default, creates an ACP policy that enables the use of Access Grant.

Starting in ESS 2.2, new Pods are created with default policies that enable the use of Access Grants; for deployments upgrading to 2.2, this change does not affect existing Pods.

When using approveAccessRequest, you can specify updateAcr: false to prevent the function from creating separate ACP policy that enables the use of Access Grants.

Important

Starting in version 2.1 of ESS, an Access Request/Grant for a Container applies both to the Container and its descendants unless explicitly specified otherwise with an inherit: false.

Version 2.1.+ of @inrupt/solid-client-access-grants-js adds an inherit option to approveAccessRequest. For example:

  • You can include inherit: false as an override option to create an approved Access Grant for the Container only, regardless of the specification in the Access Request.

  • You can include inherit: true as an override option to create an approved Access Grant for both the Container and its descendants, regardless of the specification in the Access Request.

denyAccessRequest

Denies the request and returns an Access Denial, serialized as a signed Verifiable Credential.

A server-side code can use the function to deny the request.

Granting Access#

The following implements the role of the Access Management app in the example introduced in Access Requests and Grants. The role of the Access Management app is to act as a trusted third-party in the Access Request flow.

The access requestor (e.g., ExamplePrinter) sends the Resource Owner (e.g., snoringsue) to the Resource Owner’s Access Management app.

Note

When sending resource owner to the Access Management app, the requestor adds the following query parameters:

  • id of the Access Request (in the example, the id of the Access Request VC), and

  • redirectUrl, the URL where the requestor expects the Access Management app to redirect the Resource Owner after the request has been granted or denied.

In order to approve or deny an Access Request:

  1. The resource owner should log in to the Access Management app, if not already.

  2. The Access Management app displays the Access Request (found in the requestVc parameter) to the Resource Owner. [1]

  3. The Resource Owner approves or denies the Access Request.

    • If the Resource Owner approves the request, the Access Management app uses approveAccessRequest to return the id of the Access Grant VC. When calling the function, the Access Management application can also include an optional modifications object (such as to specify a subset of the requested resources or permissions or set the inherit flag).

      async function getApprovedGrantVC(accessRequestToApprove, resourceOwnerSession){
      
        // Call `approveAccessRequest` to acquire a Verifiable Credential
        // for the approved Access Grant
        const accessGrant = await approveAccessRequest(
          accessRequestToApprove,
          undefined,  // Optional modifications
          {
            updateAcr: false, // For Pods created on ESS 2.2+
            fetch: resourceOwnerSession.fetch, // From the resource owner's (i.e., snoringsue's) authenticated session
          }
        );
      }
      
    • If the Resource Owner denies the request, the Access Management app uses denyAccessRequest to return the id of the Access Denial VC.

      async function geAccessDenialVC(accessRequestToDeny, resourceOwnerSession){
      
        // Call `denyAccessRequest` to create an Access Denial
      
        const accessDenial = await denyAccessRequest(
          accessRequestToDeny,
          {
            fetch: resourceOwnerSession.fetch, // From the resource owner's (i.e., snoringsue's) authenticated session
          }
        );
      }
      
  4. Once the user acts on a request, the Access Management app redirects the user to the requesting app using the redirectUrl parameter received earlier. The id of the Access Grant/Denial is added to the redirect URL. The requesting app can use getAccessGrantFromRedirectUrl to get the Access Grant.

    The requesting app can pass the id of the VC to getAccessGrant to retrieve the Access Grant.

Adding custom fields to an Access Grant#

Similar to Access Requests (see Use Access Grants to Access Resources), it can be useful to add application-specific information into an Access Grant.

Starting in version 3.2.0, @inrupt/solid-client-access-grants supports adding custom fields to an Access Grant. approveAccessRequest has a new customFields entry in its requestOverride argument, accepting a set of CustomField. A CustomField is a key/value entry where the key MUST be a URL, and the value a literal (string, boolean or number). The provided values are embedded within the issued Access Grant. The custom fields from the Access Request being approved are also propagated to the issued Access Grant, if not overridden by one of the provided CustomField overrides. A CustomField override which value is undefined will result in the custom field not being present in the Access Grant, even if it was part of the Access Request.

If an incorrect CustomField value is provided, AccessGrantError is thrown. Invalid CustomField definitions include not using a URL as a key, or not using a literal as a value. The TypeScript typing is provided as a guideline.

async function getApprovedGrantVC(accessRequestToApprove, resourceOwnerSession){

  // Call `approveAccessRequest` to acquire a Verifiable Credential
  // for the approved Access Grant
  const accessGrant = await approveAccessRequest(
    accessRequestToApprove,
    {
      // This adds a custom field to the issued Access Grant.
      // Custom fields from the Access Request will also be
      // present in the issued Grant.
      customFields: new Set([{
          key: new URL("https://example.com/printer/confirmationId"),
          value: "my-confirmation-id"
      }]),
    },
    {
      updateAcr: false, // For Pods created on ESS 2.2+
      fetch: resourceOwnerSession.fetch
    }
  );
}

Querying for Access Grants#

Starting in @inrupt/solid-client-access-grants@v3.2.0, the application can use the query function to query for active (i.e., current and not expired) Access Grants. To use query for Access Grants, you can pass in a AccessGrantFilter object that specifies the query filter values (i.e., a combination of the resource, creator, recipient, purpose, and type).

The AccessGrantFilter object has the following fields:

Key

Descriptions

type

The Access Credential type (in this case, SolidAccessGrant).

status

Optional. Include a credential status in the query object.

The following values are supported for Access Grants:

  • Active returns all active access grants: those that have not expired and have not been revoked.

  • Expired returns all access grants that have expired.

  • Revoked returns all access grants that have been revoked by the resource owner.

fromAgent

Optional. Include the creator of the Access Request in the query filter. This is the requestor. In the example, the creator value is the ExamplePrinter’s WebID https://id.example.com/examplePrinter.

toAgent

Optional. Include the recipient of the Access Request in the query filter. This is the resource owner.

resource

Optional. Include the resource in the query object. Use this filter to return Access Requests bound to a specific resource.

purpose

Optional. Include a purpose in the query object. Use this filter to return Access Requests bound to a specific purpose.

issuedWithin

Optional. Include a time constraint on the issuance date in the query object. All matched credentials will have been issued within the provided duration value. Certain time constraints are available for use with this method, including:

  • P1D One day (DURATION.ONE_DAY)

  • P7D Seven days (DURATION.ONE_WEEK)

  • P1M One month (DURATION.ONE_MONTH)

  • P3M Three months (DURATION.THREE_MONTH)

revokedWithin

Optional. Include a time constraint on the revocation date in the query object. All matched credentials will have been revoked or canceled within the provided duration value. Certain time constraints are available for use with this method, including:

  • P1D One day (DURATION.ONE_DAY)

  • P7D Seven days (DURATION.ONE_WEEK)

  • P1M One month (DURATION.ONE_MONTH)

  • P3M Three months (DURATION.THREE_MONTH)

The result is paginated, so the agent may need to make multiple calls to the query function to get all of the Access Requests matching the provided filter.

The following example queries for active access grants, given to ExamplePrinter, that provide access for a specific resource for the purpose of photo printing.

const page1 = await query({
  type: "SolidAccessGrant",
  status: "Active",
  resource: new URL("https://storage.example.com/some/resource"),
  purpose: new URL("https://purpose.example.com/PhotoPrinting"),
}, {
  fetch: session.fetch,
  queryEndpoint: new URL("https://vc.example.org/query"),
});
if (page1.next !== undefined) {
  const page2 = await query(page1.next, {
    fetch: session.fetch,
    queryEndpoint: new URL("https://vc.example.org/query"),
  });
}

paginatedQuery is a utility provided for iterating through the result pages:

const pages = paginatedQuery({
  type: "SolidAccessGrant",
  status: "Active",
  resource: new URL("https://storage.example.com/some/resource"),
  purpose: new URL("https://purpose.example.com/PhotoPrinting"),
}, {
    fetch: session.fetch,
    queryEndpoint: new URL("https://vc.example.org/query"),
});
let i = 0;
for await (const page of pages) {
  console.log(`Page ${i} has ${page.items.length} items.`)
  i += 1;
}