Files
zeekay 8986f49be0 the client carries the credential, because the document declares it
cloud's openapi.yaml now has a `bearer` securityScheme and a top-level
`security`, so regenerating turns 2498 operations from "imports
setBearerAuthToObject and calls it nowhere" into a real
`Authorization: Bearer` write from `configuration.accessToken`. The four
operations marked `security: []` — /v1/models, /v1/models/providers,
/v1/commands, /v1/openapi.json — generate no auth call, so commands-api.ts
is the one api file of 192 without one.

That retires `baseOptions: { headers: { Authorization } }`, which existed
only because a document with no scheme made `accessToken` inert. One field,
one header, one way; examples/client.ts and the docs say so.

examples/models is new and needs no credential, so a reader can run the
client against the live API before holding a key. Measured, not assumed:
models prints 112 models anonymously, hello prints
`sub admin/hanzo-cloud in org hanzo` with an IAM client-credentials token
and 401 invalid_token with a bad one, and money reads the org's own balance
where anonymous reads account "anonymous".

.spec-lock: hanzoai/cloud@113cf7d6, sha256 a50fc96c.
2026-08-14 17:21:07 -07:00

39 lines
1.4 KiB
TypeScript

// hello — who am I?
//
// GET /v1/iam/oauth/userinfo (get_iam_oauth_userinfo), the OIDC identity
// endpoint of the one IAM.
//
// This flow's job is to FAIL on a bad key, so the route has to be one that
// refuses. Two identity-shaped neighbours do not: /v1/iam/whoami answers 200
// with {"status":"error"} and /v1/ai/account answers 200 type="anonymous-user"
// to a request carrying no header at all. Either would print a cheerful
// identity for a key that is refused everywhere else. userinfo answers 401
// {"error":"invalid_token"}.
//
// The body is read through a cast because this operation declares no response
// schema — one of 891 in the document that state the route and not its shape.
import { IamApi } from 'hanzoai';
import { config, basePath, fail } from '../client';
type UserInfo = {
sub?: string;
name?: string;
preferred_username?: string;
email?: string;
organization?: string;
iss?: string;
};
async function main() {
const iam = new IamApi(config());
const { data } = await iam.getIamOauthUserinfo();
const me = data as unknown as UserInfo;
console.log(`hello from ${basePath}`);
console.log(` sub ${me.sub ?? '(none)'} in org ${me.organization ?? '(none)'}`);
console.log(` ${me.name ?? me.preferred_username ?? '(unnamed)'} <${me.email ?? 'no email'}>`);
console.log(` issued by ${me.iss ?? '(unknown issuer)'}`);
}
main().catch(fail);