Creates, updates, lists and deletes tokens from code.
These are what beamlet tokens.* runs. Creating a token is the
only time its secret is visible:
{:ok, token} = Beamlet.Tokens.create(name: "laptop", policy: "explorer")
token.secret
#=> "wJalrXUtnFEMI_K7MDENG_bPxRfiCYEXAMPLEKEY_q0"A client sends the secret as a bearer token.
update/2 renames a cli token or changes its policy. It refuses
oauth tokens. delete/1 works on either kind, and the token's
next request fails.
The authenticate and rotate functions serve the MCP endpoint and the OAuth token endpoint. Most callers will not need them.
Summary
Functions
Turns a presented secret into its token.
Turns a presented refresh secret into its oauth token, for the
token endpoint.
Creates a token.
Deletes a token.
Finds a token by id.
Every token on the beamlet, oldest first.
Rotates an oauth token in place, with new secrets and expiries.
Updates a cli token's name or policy.
Functions
@spec authenticate(term()) :: {:ok, Beamlet.Token.t()} | {:error, :unknown_token | :expired_token}
Turns a presented secret into its token.
Anything that is not the secret of a stored token, including a
deleted token's secret or a value that never was one, is
{:error, :unknown_token}. The secret of an oauth token past its
expiry is {:error, :expired_token}.
@spec authenticate_refresh(term()) :: {:ok, Beamlet.Token.t()} | {:error, :unknown_token | :expired_token}
Turns a presented refresh secret into its oauth token, for the
token endpoint.
Anything that is not the refresh secret of a stored token, a cli
token's secret among them, is {:error, :unknown_token}. A refresh
secret past refresh_expires_at is {:error, :expired_token}.
@spec create(map() | keyword()) :: {:ok, Beamlet.Token.t()} | {:error, Ecto.Changeset.t()}
Creates a token.
kind is :cli unless given. A cli token takes name and an
optional policy. An oauth token takes client, policy,
expires_at and refresh_expires_at. The policy must be one the
beamlet declares.
The returned token carries its secret, and an oauth token its
refresh_secret too. Nothing else ever will.
@spec delete(Beamlet.Token.t()) :: {:ok, Beamlet.Token.t()} | {:error, Ecto.Changeset.t()}
Deletes a token.
Requests presenting its secret fail from then on.
@spec find(pos_integer()) :: {:ok, Beamlet.Token.t()} | {:error, :not_found}
Finds a token by id.
@spec list() :: [Beamlet.Token.t()]
Every token on the beamlet, oldest first.
@spec rotate(Beamlet.Token.t(), map() | keyword()) :: {:ok, Beamlet.Token.t()} | {:error, Ecto.Changeset.t() | :cli_token | :unknown_token}
Rotates an oauth token in place, with new secrets and expiries.
attrs carries expires_at and refresh_expires_at. The row and
its id stay, so provenance keeps pointing at the same token. The
old secrets stop working at once, and the returned token carries
the new secret and refresh_secret. A cli token answers
{:error, :cli_token}.
A refresh secret redeems once. The rotation only lands while the
row still holds the refresh secret token was loaded with, so of
two rotations from one secret the second answers
{:error, :unknown_token}, as a spent secret does.
@spec update(Beamlet.Token.t(), map() | keyword()) :: {:ok, Beamlet.Token.t()} | {:error, Ecto.Changeset.t() | :oauth_token}
Updates a cli token's name or policy.
The secret cannot change, so create a new token instead. An oauth
token cannot be updated and answers {:error, :oauth_token}.