@lincs.project/auth-api-contract
oRPC contract for the apps/api Auth API service, with Zod schemas for inputs, outputs, and declared errors. The server implements this contract with @orpc/server; OpenAPI is generated from the same definitions.
It can be used to build a JavaScript helper to access the endpoints of the Auth-API Service App using a standardized fetcher. It allows for better endpoint discoverability and fully typed request parameters and responses. The API contract is also available to create a custom fetcher.
Setup
Use with @orpc/client and @orpc/openapi-client (OpenAPI link over fetch).
Install
pnpm add @lincs.project/auth-api-contract @orpc/client @orpc/openapi-client
Minimal client
The package exports contract, createLincsAuthConnector, and inferred helper types (LincsAuthConnector, LinkedAccountsBodyOutputs, …). The HTTP version prefix (/v1) lives on the contract router; client calls use providers and users, not a v1 namespace.
import { createLincsAuthConnector } from '@lincs.project/auth-api-contract';
const client = createLincsAuthConnector('https://auth-api.dev.lincsproject.ca');
const providers = await client.providers.getAll({});
To set default headers for every call, build the link yourself:
import { contract } from '@lincs.project/auth-api-contract';
import { createORPCClient } from '@orpc/client';
import { OpenAPILink } from '@orpc/openapi-client/fetch';
const link = new OpenAPILink(contract, {
url: 'https://auth-api.dev.lincsproject.ca',
headers: () => ({
authorization: `Bearer ${accessToken}`,
}),
});
const client = createORPCClient(link);
HTTP failures surface as ORPCError. Use isDefinedError from @orpc/client to narrow to contract-declared codes (for example NOT_FOUND, INTERNAL_SERVER_ERROR), depending on the procedure.
v1 procedures
Sources: src/v1/providers.ts, src/v1/users.ts. For live docs, open the deployed API or its OpenAPI JSON (see apps/api/README.md).
providers.getAll | GET /v1/providers | List identity providers; optional input and optional headers.authorization |
providers.getProvider | GET /v1/providers/{alias} | Single provider; optional headers.authorization |
users.getLinkedAccounts | GET /v1/users/{username}/linked-accounts | Federated identities; requires headers.authorization |
users.getLinkAccountUrl | GET /v1/users/{username}/link-account-url | Query provider, redirectUri; requires headers.authorization |
On the wire, provider routes do not use Keycloak protect('admin'). The service only checks whether an Authorization header is present to decide whether provider payloads are trimmed (the token is not validated for that behavior).
Adapter examples
const list = await client.providers.getAll({});
const listFull = await client.providers.getAll({
headers: { authorization: `Bearer ${token}` },
});
const one = await client.providers.getProvider({
params: { alias: 'orcid' },
});
const accounts = await client.users.getLinkedAccounts({
headers: { authorization: `Bearer ${token}` },
params: { username: 'jdoe' },
});
const { url } = await client.users.getLinkAccountUrl({
headers: { authorization: `Bearer ${token}` },
params: { username: 'jdoe' },
query: {
provider: 'github',
redirectUri: 'https://app.example/callback',
},
});
Development
Changes here are consumed by apps/api via the workspace protocol (workspace:*). Run builds from the monorepo root or this package:
pnpm run build
pnpm run dev
pnpm run typecheck
pnpm run lint
From the monorepo root, pnpm run typecheck runs Turbo typecheck (after ^build) across packages that define the task.
Versioning follows API semver: breaking HTTP or procedure shapes should introduce a new versioned subtree (for example v2) while keeping v1 available for migration.
See also