> Documentation index: [Saleor](/llms.txt) · [This section](/developer/llms.txt)
> Source: https://docs.saleor.io/developer/extending/apps/architecture/app-permissions

# App Permissions

[Permissions](/developer/permissions.md) are a way to control what operations can be performed by an app.

<a id="managing-permissions"></a>

## Managing permissions

When you install an app, Saleor grants it a set of permissions. The initial app permissions are drawn from [the app's manifest file](/developer/extending/apps/architecture/manifest.md) and stored in the [`App`](/api-reference/apps/objects/app.md) object under the `permissions` field. This is how you can obtain the permissions of an app:

```graphql
query AppPermissions($id: ID!) {
  app(id: $id) {
    permissions {
      name
      code
    }
  }
}
```

Permissions can also be modified after the app is installed. If you have the `MANAGE_APPS` permission, you can change them using the [appUpdate](/api-reference/apps/mutations/app-update.md) mutation:

```graphql
mutation UpdateAppPermissions($id: ID!) {
  appUpdate(id: $id, input: { permissions: [MANAGE_ORDERS] }) {
    app {
      permissions {
        name
        code
      }
    }
  }
}
```

Using the [`RequestPermissions`](/developer/extending/apps/developing-apps/app-sdk/app-bridge.md#available-actions) action from the `appBridge`, you can even request more permissions dynamically:

```jsx
import { actions, useAppBridge } from "@saleor/app-sdk/app-bridge";

const Component = () => {
  const { appBridge } = useAppBridge();

  const requestExtraPermission = () => {
    appBridge.dispatch(
      actions.RequestPermissions(["MANAGE_ORDERS"], "/success")
    );
  };

  return <button onClick={requestExtraPermission}>Request permission</button>;
};
```

Once the user approves the permission request, the app receives the granted permissions. The user is then redirected to the `appUrl` defined in the app's manifest, with a `redirectUrl` query parameter appended. This parameter's value corresponds to the `redirectUrl` provided in `actions.RequestPermissions` (e.g., `redirectUrl=/success`). If the operation encounters an error, the `?error=REASON` query parameter will be appended to the redirect URL.

<a id="managing-apps"></a>

## Managing apps

A user can open the installed app, but they won't be able to deactivate or delete it.

info

To install, deactivate, or delete an app, the user must have the `MANAGE_APPS` permission, as well as all the app permissions.

Consider an example of the Orders App, which sends orders to an external service. This app would require the `MANAGE_ORDERS` permission.

In this case, only the user who has both the `MANAGE_APPS` and `MANAGE_ORDERS` permissions can install, deactivate or delete the Orders App.

<a id="access-scopes"></a>

## Access scopes

When building an app, you must be aware of two access scopes: user scope and app scope. They determine where and how you can call the Saleor API.

To visualize them, let's go back to our Orders App example.

Imagine you were tasked with adding a table of orders to our app. The table can be used to determine which orders have been synchronized with the external service. We want to populate our table with the result of the [`orders` query](/api-reference/orders/queries/orders.md). This query requires the `MANAGE_ORDERS` permission.

We have to decide where to execute this query. We can do it on the client-side or the server-side.

On the client-side, we would use the user [accessToken](/developer/extending/apps/architecture/server-and-client-side-calls.md#accesstoken) to authenticate the API call. The token would include the user's permissions.

On the server-side, we would use the [appToken](/developer/extending/apps/architecture/server-and-client-side-calls.md#apptoken) stored in [APL](/developer/extending/apps/architecture/apl.md). The token would include the app's permissions.

info

You can dive deeper in [Server and client-side app calls](/developer/extending/apps/architecture/server-and-client-side-calls.md) in a separate article.

Let's see what are the implications of executing the `orders` query on the client-side and the server-side:

<a id="user-scope"></a>

### User scope

Opening the app requires no permissions, so you can not assume the presence of any of them. Luckily, we can check if the user has the required permissions before we execute the `orders` query.

In your React component, you can use the `useAppBridge` hook from [@saleor/app-sdk](/developer/extending/apps/developing-apps/app-sdk/overview.md) to check what permissions the user has.

We will compare the results with the required permission and render the table accordingly:

```jsx
import { useAppBridge } from "@saleor/app-sdk";
import { OrdersTable } from "./OrdersTable";

const Component = () => {
  const { appBridgeState } = useAppBridge();

  const hasPermission =
    appBridgeState.user.permissions.includes("MANAGE_ORDERS");

  return hasPermission ? (
    <OrdersTable />
  ) : (
    <p>You don't have the required permissions</p>
  );
};
```

Once you have confirmed that the user has the required permissions, you can execute the `orders` query in your `OrdersTable` component:

info

In the example below, you can see passing the `appBridgeState.token` right to the `Authorization` header in the query. This is for presentational purposes only. You should rather create a client with the token and pass it to the provider of your GraphQL client library.

See: [App Template](https://github.com/saleor/saleor-app-template/blob/main/src/providers/GraphQLProvider.tsx) for reference.

```jsx
import { useAppBridge } from "@saleor/app-sdk";
import { useQuery } from "graphql-client-library"; // made up library
import { ORDERS_QUERY } from "./queries";

export const OrdersTable = () => {
  const { appBridgeState } = useAppBridge();
  const { data } = useQuery(ORDERS_QUERY, {
    context: {
      headers: {
        Authorization: `Bearer ${appBridgeState.token}`, // this is the user token
      },
    },
  });

  // ...
};
```

Summing up, the user scope applies to the client-side of our app. In the case of the Orders App, it means that as long as the user has the `MANAGE_ORDERS` permission, they will see the orders table.

<a id="app-scope"></a>

### App scope

When you want to execute a query that requires permission that the user may not have, you should do it on the server-side.

The app scope applies to the server-side of our app. It means that as long as the app has the `MANAGE_ORDERS` permission, it can execute the `orders` query, regardless of the user's permissions.

In our case, you would execute the `orders` query on the server-side and pass the result to the client-side. In Next.js, it would mean creating an API route and executing the query there:

```jsx
// pages/api/orders.js
import { SALEOR_API_URL_HEADER } from "@saleor/app-sdk/const";
import { ordersQuery } from "./queries";
import { createClient } from "graphql-client-library"; // made up library

export default async function handler(req, res) {
  const saleorApiUrl = req.headers[SALEOR_API_URL_HEADER]; // assuming it's set
  const authData = await apl.get(saleorApiUrl);
  const appToken = authData.token;

  const client = createClient({
    headers: {
      Authorization: `Bearer ${appToken}`, // this is the app token
    },
  });

  const data = await client.query(ordersQuery);

  res.status(200).json(data);
}
```

danger

You must not expose the `appToken` to the client-side. It should be retrieved from the APL and used only on the server-side.

In your React component, you would fetch the data from the API route:

```jsx
import { useEffect, useState } from "react";

const Component = () => {
  const [orders, setOrders] = useState([]);

  useEffect(() => {
    fetch("/api/orders")
      .then((res) => res.json())
      .then((data) => setOrders(data));
  }, []);

  return <OrdersTable orders={orders} />;
};
```

As a result, all the users able to render the `OrdersTable` component will see the orders, regardless of their personal permissions.
