@manta-eu/auth
v0.1.7
Published
Keycloak login for the SDK CLIs: browser and device-code login, silent token renewal, and credential storage; plus the default deployment the SDK targets
Downloads
2,180
Maintainers
Keywords
Readme
@manta-eu/auth
Keycloak login for the SDK CLIs (@manta-eu/codegen, @manta-eu/upload): browser and device-code
login, silent token renewal, and credential storage. It knows nothing about either CLI's own
purpose — it only mints and renews tokens, and gives each CLI login/logout/status commands to
register on its own commander program.
Log in
MANTA_CLIENT_SECRET decides the path.
No secret — a person. userTokenSource() builds a token source that logs in once and renews
silently after that:
import { publicClientFrom, userTokenSource } from "@manta-eu/auth";
const token = userTokenSource(publicClientFrom({ keycloakUrl, keycloakRealm }), { label: "my-cli" });It opens the browser and listens on 127.0.0.1 for Keycloak to hand the login back (the
authorization code flow with PKCE). Over SSH, or on Linux without a display, it prints a link and a
code for a browser on another machine instead; useDeviceCode: true asks for that anywhere. The
token is the person's, so a caller reads only what that person can open.
The login is kept, so later runs renew it silently. It asks for offline_access, so it lasts until
it goes unused for the realm's offline idle limit (30 days by default) or the person logs out.
userTokenSource logs in by itself when nothing is stored and a terminal is attached; without one it
throws and names the login command instead of waiting for a browser.
registerAuthCommands(program, { label }) wires login, status and logout into a commander
program, all reading and writing the same store:
| Command | What it does |
| -------- | ------------------------------------------------------------------- |
| login | Logs in and stores the login. |
| status | Says whether a login is stored, and for whom. Exits 1 when none is. |
| logout | Revokes the login at Keycloak and deletes the stored copy. |
Each takes the same --keycloak-url and --keycloak-realm, because a login belongs to one
Keycloak, realm and client. Both default to hiremanta.com production, from defaultDeployment.
The client defaults to the realm's <realm>-cli, a public client made for this; --client-id is
only needed for another one. Every CLI that registers these commands reads and writes the same
store, so logging in with one serves all of them.
The login lives in the macOS Keychain (service manta-cli), or in ~/.manta/credentials.json with
mode 0600 on Linux, on Windows, and on a Mac whose Keychain refuses it: while locked, e.g. over
SSH, or for a refresh token over about 2.9 KB, the most security takes in one line. The CLI says
on stderr when the file takes over. Only the refresh token is kept. MANTA_CONFIG_DIR moves the file.
A secret — a machine. mintWithClientCredentials() asks for a service account token directly,
with no stored login involved:
import { mintWithClientCredentials } from "@manta-eu/auth";
const accessToken = await mintWithClientCredentials({ baseUrl, realm, clientId, clientSecret, scope });Renewing on refusal
withRenewal(token, call) calls call with a fresh access token, and on a 401 specifically calls
the token source's renew() and retries once — the same pattern Claude Code uses for MCP OAuth. Wrap
any authenticated request with it instead of minting a token by hand.
