Mini apps
Ver como MarkdownSer 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 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 (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. 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:
import { createAndcoInstanceForServer } from "@andco/sdk/server";
export const andco = createAndcoInstanceForServer({
clientId: process.env.ANDCO_CLIENT_ID!,
clientSecret: process.env.ANDCO_CLIENT_SECRET!,
});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): es tu propio cliente de openapi-fetch, generado desde tu propio contrato, con el token que Andco resolvió puesto por un middleware.
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:
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:
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.
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 y openfactura-sdk conviven sin conocerse entre sí.
