# orca.tokens

> A module of morca's API.

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`

```mojo
struct Issued
```

Implements: `Movable`

Fields:

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

### `TokenRow`

```mojo
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`

```mojo
def new_token() -> String
```

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

### `is_token`

```mojo
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`

```mojo
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`

```mojo
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`

```mojo
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`

```mojo
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`

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

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

### `revoke_token`

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

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

### `user_tokens`

```mojo
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`

```mojo
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

### `TOKEN_PREFIX`

```mojo
comptime TOKEN_PREFIX = "orca_"
```

### `TOKEN_BYTES`

```mojo
comptime TOKEN_BYTES = 32
```

256 bits: as many as a session's.

### `DEFAULT_TOKEN_DAYS`

```mojo
comptime DEFAULT_TOKEN_DAYS = 30
```

### `MAX_TOKEN_DAYS`

```mojo
comptime MAX_TOKEN_DAYS = 3650
```

### `MAX_LABEL`

```mojo
comptime MAX_LABEL = 64
```
