# orca.web.auth

> A module of morca's API.

Who a request is from, and whether it may act: the session cookie of a browser, the API token of a script (`orca --remote`), and the checks that keep another site's page from posting to the UI. Also the endpoints that log in and out. The users, sessions and audit rows themselves are `orca.auth`; API tokens are `orca.tokens`; a worker's token is checked by `orca.api.worker` instead, and gets nothing here.

| Path | |
|---|---|
| `/login` | `GET`: the form; `POST` `name`, `password`, `next`: a session, as the cookie `orca_session` |
| `/logout` | ends the session and clears the cookie |
| `/api/login` | JSON `name`, `password`, `label`, `days`: an API token for `orca login` |
| `/api/logout` | revokes the API token it's sent with |

A script's request is one with an `Authorization` header (or a JSON login
for a token). A browser sends neither to another site without a CORS
preflight, which the UI never answers, so a script's `POST` with no
`Origin` needn't be the UI's own; the token, not a cookie, is what it acts
with. A request with an `Authorization` header is answered 401 unless it's
a live token, whatever cookie it has.

## Functions

### `bearer`

```mojo
def bearer(authorization: String) -> String
```

The token of `Authorization: Bearer <token>`, or empty for any other header.

### `cookie`

```mojo
def cookie(req: Request, name: StringSpan) -> String
```

A cookie the request sent, or empty.

### `sign_in`

```mojo
def sign_in(mut db: State, req: Request, mut call: Call) -> Bool
```

Fills in who `call` is from: the user of its API token (source "cli"), or of its session cookie (source "ui"), or nobody. False for an `Authorization` that isn't a live API token, whose request is refused (cookies are never looked at instead).

### `client`

```mojo
def client(req: Request) -> String
```

The address a request came from: the connection's peer, or behind a proxy (`ORCA_UI_TRUST_PROXY=1`) the first address of `X-Forwarded-For`, which only a proxy in front can be trusted to set.

### `content_type`

```mojo
def content_type(req: Request) -> String
```

The request's media type, lower case, without its parameters, which a cross-site page may set however it likes.

### `scripted`

```mojo
def scripted(req: Request, path: String) -> Bool
```

Whether `req` is a script's (`orca.remote`): it has an `Authorization` header, or is a JSON login for a token. `/login` and `/logout` are a browser's whatever they're sent with.

### `same_origin`

```mojo
def same_origin(req: Request) -> Bool
```

Whether a `POST` came from the UI's own pages: its `Origin`, or without one its `Referer`, is this server as the browser addressed it, by `Host` and the scheme `_scheme` gives. Host and scheme compare in any case.

### `cross_origin`

```mojo
def cross_origin() -> Page
```

### `login_needed`

```mojo
def login_needed(req: Request, json: Bool) -> Response
```

An action without a session: 401 for a script, to the login page for a browser, which comes back to the page the form was on.

### `json_form`

```mojo
def json_form(body: String) -> String
```

A JSON object of strings, numbers and booleans (`orca.remote`'s actions) as the form the UI's own pages post, so both are handled and audited alike: true is `1`, false and null are left out.

### `json_error`

```mojo
def json_error(status: Int, why: String) -> Page
```

An error as JSON only, for a script.

### `token_refused`

```mojo
def token_refused() -> Page
```

A request with a token that's no use: unknown, expired or revoked, or an `Authorization` that isn't a bearer token.

### `login_form`

```mojo
def login_form(mut db: State, call: Call) -> Page
```

`GET /login`: the form, which goes on to `?next=`.

### `login`

```mojo
def login(mut db: State, call: Call) -> Page
```

`POST /login`: a session for `name` and `password`, as a cookie, then on to `next`.

### `logout`

```mojo
def logout(mut db: State, call: Call) -> Page
```

`/logout`: ends the session, if there is one, and clears the cookie.

### `api_login`

```mojo
def api_login(mut db: State, call: Call) -> Page
```

`POST /api/login`: a new API token for a JSON `name` and `password`, labelled `label`, lasting `days` (at most `ORCA_API_TOKEN_DAYS`). Failures count against the same limit as `/login`'s, and the token is in this answer only. A token it was sent with isn't looked at.

### `api_logout`

```mojo
def api_logout(mut db: State, call: Call) -> Page
```

`POST /api/logout`: revokes the API token it's sent with; a session has none to revoke.

## Aliases

### `LOGIN`

```mojo
comptime LOGIN = "/login"
```

### `LOGOUT`

```mojo
comptime LOGOUT = "/logout"
```

### `API_LOGIN`

```mojo
comptime API_LOGIN = "/api/login"
```

### `API_LOGOUT`

```mojo
comptime API_LOGOUT = "/api/logout"
```
