morca · API

orca.tokens

API tokens: what orca login gets from orca ui, so the orca command on another machine can act as a user of it (orca.remote).

orca user tokens alice          her tokens: label, created, last used, expires
orca user revoke alice 3        or @all

A token is orca_ and 64 hex digits, 32 random bytes from the system's generator; the prefix lets a secret scanner recognise one that leaked. The UI gives it out once, to POST /api/login with the right password, and keeps only its SHA-256 digest in orca.api_tokens, with its user, a label (the machine that logged in), when it was made and last used, and when it expires: ORCA_API_TOKEN_DAYS (default 30) days on, or fewer if the login asked for fewer. A request sends it as Authorization: Bearer <token>; one that's unknown, expired or revoked is answered 401. orca logout revokes its own (POST /api/logout), orca user revoke any of a user's, and changing a user's password or removing the user revokes all of hers, as it ends her sessions (orca.auth).

Structs

Issued

struct Issued

Implements Movable

Fields
token: String
expires: String: When it expires, in UTC (YYYY-MM-DDTHH:MM:SSZ).

TokenRow

struct TokenRow

Implements Copyable, Movable

Fields
id: Int
label: String
created: String
last_used: String: Empty if it hasn't been.
expires: String
expired: Bool

Functions

new_token

def new_token() -> String

A new token: orca_ and TOKEN_BYTES random bytes in hex.

is_token

def is_token(s: String) -> Bool

Whether s has a token's form, which anything else sent as one is refused for before the database is asked.

token_hash

def token_hash(token: String) -> String

What orca.api_tokens keeps of a token: its SHA-256, in hex. A token is 256 random bits, so a fast digest is enough: there's nothing to guess that a slow one would protect.

token_days

def token_days(asked: Int = Int(0)) -> Int

How many days a new token lasts: asked if it's from 1 to the most allowed, ORCA_API_TOKEN_DAYS (default 30, at most 3650), else that most. Raises for a setting that isn't a count of days.

clean_label

def clean_label(label: String) -> String

A token's label as it's kept: printable ASCII, at most MAX_LABEL bytes, - for anything else, and cli for none.

issue_token

def issue_token(mut db: State, user: String, label: String, days: Int) -> Issued

A new token for user, which only this answer ever holds.

token_user

def token_user(mut db: State, token: String) -> String

The user whose live token token is, or empty; marks it used.

revoke_token

def revoke_token(mut db: State, token: String) -> Bool

Revokes token; False if it wasn't a live one.

user_tokens

def user_tokens(mut db: State, user: String) -> List[TokenRow]

user's tokens, newest first, expired ones too until they're removed.

revoke_user_tokens

def revoke_user_tokens(mut db: State, user: String, id: Int) -> Int

Revokes user's token id, or all of them for 0; how many.

Aliases

comptime TOKEN_PREFIX = "orca_"
comptime TOKEN_BYTES = 32

256 bits: as many as a session's.

comptime DEFAULT_TOKEN_DAYS = 30
comptime MAX_TOKEN_DAYS = 3650
comptime MAX_LABEL = 64