# Ser un Resource Server

> Cómo tu backend se convierte en un proveedor de recursos que otras apps pueden pedir autorización para usar.


Hasta ahora esta documentación asume que tu app **consume** Andco: pide autorización, guarda una sesión, llama a la API de Andco. Esta página es al revés — tu backend expone su propia API, y otras apps (incluida la tuya) piden permiso para usarla a través de Andco. [`@andco/bank-sdk`](/docs/reference/andco-bank-sdk) y `@andco/openfactura-sdk` son ejemplos reales de esto mismo.

## 1. Crea un proyecto

En el panel de Andco, ve a tu organización → **Proyectos** → **Nuevo proyecto**: [`https://bank.localhost/o/[organization_slug]/projects/new`](https://bank.localhost/o/[organization_slug]/projects/new) (`bank.localhost` es el dominio en desarrollo). Solo pide un nombre; las credenciales OAuth se configuran después, como un paso aparte.

## 2. Crea una credencial OAuth

Dentro del proyecto, en **Credenciales** → **Nueva credencial**: [`https://bank.localhost/o/[organization_slug]/p/[project_slug]/credentials/new`](https://bank.localhost/o/[organization_slug]/p/[project_slug]/credentials/new). Con esto obtienes un `client_id` y, si tu app corre en un servidor, un `client_secret` — es la misma credencial que usarías como cliente Andco en cualquier otra guía de esta documentación.

## 3. Expón scopes y `authorization_details`

Hoy esto no es autoservicio: los scopes propios y los tipos de `authorization_details` (RFC 9396) que tu Resource Server expone se coordinan directamente con Andco, que los declara y los revisa antes de que queden disponibles para que otras apps los soliciten. Lo mismo aplica al manifest de tu Miniapp si vas a publicar una. No es instantáneo — considera este paso al planificar tu integración.

## 4. Verifica un token entrante

Cuando otra app llama a tu API con un token que Andco emitió, tu backend necesita saber qué Grant hay detrás: qué scopes tiene, qué `authorization_details` concedió la persona. El JWT por sí solo no alcanza — no lleva el detalle de la autorización, solo lo esencial (`sub`, `scope`, `client_id`, `org_id`). Para el resto, tu backend introspecta el token contra Andco con el mismo SDK que usarías como cliente:

```ts title="src/andco.ts"
import { createAndcoInstanceForServer } from "@andco/sdk/server";

export const andco = createAndcoInstanceForServer({
  clientId: process.env.ANDCO_CLIENT_ID!,
  clientSecret: process.env.ANDCO_CLIENT_SECRET!,
});
```

```ts title="src/routes/dealerships.ts"
import { Hono } from "hono";
import { andco } from "../andco.js";

const CAR_DEALERSHIPS_AUTHORIZATION_TYPE = "https://carmarket.example/authorization-details/car-dealerships";

export const dealerships = new Hono();

dealerships.get("/dealerships", async (c) => {
  const token = c.req.header("Authorization")?.replace("Bearer ", "");
  if (!token) return c.json({ error: "missing_token" }, 401);

  const { data: grant, error } = await andco.with(token).rest.http.GET("/authorization");
  if (error) return c.json({ error: error.code }, error.status ?? 401);

  const canList = grant.authorizationDetails.some((detail) => detail["type"] === CAR_DEALERSHIPS_AUTHORIZATION_TYPE);
  if (!canList) return c.json({ error: "insufficient_authorization" }, 403);

  return c.json(await loadDealerships());
});
```

`GET /authorization` devuelve el Grant activo detrás del token que mandaste: `scopes`, `authorizationDetails`, `orgId`, `subject`, `consumerInstallationId`. Es la misma llamada que usarías para depurar un permiso, solo que aquí decide si respondes 200 o 403.

## 5. Empaqueta tu propia librería

Con el proyecto y la credencial listos, lo natural es dar a quien integre tu recurso una librería tan chica como `@andco/openfactura-sdk`: una definición de Resource Server con `authorization()` para construir la contribución y `use()` para ligarse a un cliente ya autenticado. Como tu API es la tuya —no la de Andco—, el cliente que devuelve `use()` no es un `AndcoRest` (ese solo tiene sentido cuando el Resource Server *es* la API de Andco, como en [`bank-sdk`](/docs/reference/andco-bank-sdk)): es tu propio cliente de [`openapi-fetch`](/docs/getting-started/calling-the-api#tu-propio-cliente-sin-el-sdk), generado desde tu propio contrato, con el token que Andco resolvió puesto por un middleware.

```ts title="packages/car-market-sdk/src/index.ts"
import type { AndcoBoundClient, AndcoResourceAuthorization, AndcoResourceServer } from "@andco/sdk";
import createClient from "openapi-fetch";

/** Generado por openapi-typescript desde el contrato de CarMarket. */
type CarMarketPaths = {
  "/dealerships": {
    get: {
      responses: { 200: { content: { "application/json": { data: { id: string; name: string }[] } } } };
    };
  };
};

const CAR_MARKET_RESOURCE = "https://carmarket.example/api";
const CAR_MARKET_RESOURCE_SERVER_ID = "car-market"; // el id que Andco te asigna al aprobar tu Resource Server
const CAR_DEALERSHIPS_AUTHORIZATION_TYPE = "https://carmarket.example/authorization-details/car-dealerships";

export class CarMarket implements AndcoResourceServer<ReturnType<typeof carMarketClient>> {
  public readonly id = CAR_MARKET_RESOURCE_SERVER_ID;
  public readonly resource = CAR_MARKET_RESOURCE;

  /** Convención, no parte de la interfaz: cada Resource Server decide su propia forma de pedirla. */
  public authorization(): AndcoResourceAuthorization {
    return {
      resourceServerId: this.id,
      resource: this.resource,
      scopes: [],
      authorizationDetails: [{ type: CAR_DEALERSHIPS_AUTHORIZATION_TYPE, actions: ["list_car_dealerships"] }],
    };
  }

  public use(client: AndcoBoundClient) {
    return carMarketClient(client, this.resource);
  }
}

function carMarketClient(bound: AndcoBoundClient, resource: string) {
  const client = createClient<CarMarketPaths>({ baseUrl: resource, fetch: bound.fetch });
  client.use({
    async onRequest({ request }) {
      const accessToken = await bound.accessTokenFor(resource);
      if (accessToken) request.headers.set("Authorization", `Bearer ${accessToken}`);
      return request;
    },
  });
  return client;
}
```

Quien la use nunca arma la petición a mano — pide permiso igual que con [`bank-sdk`](/docs/reference/andco-bank-sdk):

```ts
const carMarket = new CarMarket();

const { data: session, error } = await andco.auth.signIn({
  authorizations: [carMarket.authorization()],
  presentation: "popup",
});
```

Y con la sesión lista, hace la petición autenticada con `use()` — `andco` ya es un `AndcoBoundClient`, así que ligarlo es una línea:

```ts
const { data, error } = await carMarket.use(andco).GET("/dealerships");
```

En un servidor, donde `andco` no guarda sesión, es `carMarket.use(andco.with(session)).GET("/dealerships")` — igual que en [Autenticación en el servidor](/docs/getting-started/server-authentication#llamar-a-la-api-por-cuenta-de-una-persona).

`use()` va en la definición y no en el cliente Andco, por lo que agregar tu Resource Server nunca requiere tocar `@andco/sdk` — es la misma razón por la que [`bank-sdk`](/docs/reference/andco-bank-sdk) y `openfactura-sdk` conviven sin conocerse entre sí.
