Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

All notable changes to this project will be documented here. The format roughly follows [Keep a Changelog](https://keepachangelog.com/) and the project uses [Semantic Versioning](https://semver.org/).

## [0.5.1] - 2026-05-11

### Fixed

- **Auto-refresh of the access token now actually works.** The refresh endpoint reads the refresh JWT from a `refresh-token` HTTP header, not a JSON body — sending it as a body returned a pydantic 422 silently swallowed by `tryRefresh()`, which then propagated as an unauthenticated state to the caller. With this fix the CLI / library stays signed in for as long as the refresh token is valid (currently ~30 days), instead of forcing a manual `flappie login` after the ~12-hour access-token expiry.

### Changed

- `CLOUD_API.md` and `openapi.yaml` updated to document the refresh endpoint's actual header-based contract.

## [0.5.0] - 2026-05-11

Docs-and-DX release: same wire surface as 0.4.0, but the package is meaningfully easier to discover and use.
Expand Down Expand Up @@ -39,5 +49,6 @@ First public release on npm.
- `RE.md` reverse-engineering playbook for when the vendor's mobile app updates.
- Unit tests for pure CLI helpers (`parseBool`, `normalizePolicy`, `parseWeekdays`, `summarizeSettings`).

[0.5.1]: https://github.com/ooswald/flappie-api/releases/tag/v0.5.1
[0.5.0]: https://github.com/ooswald/flappie-api/releases/tag/v0.5.0
[0.4.0]: https://github.com/ooswald/flappie-api/releases/tag/v0.4.0
2 changes: 1 addition & 1 deletion CLOUD_API.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Do not "fix" this — it matches what the backend actually accepts.
| Method | Path | Body | CLI |
|--------|------|------|-----|
| POST | `/api/v1/users/login` | `{ email, password }` | ✅ `flappie login` |
| POST | `/api/v1/users/refresh` | `{ refresh_token }` | ✅ (auto on 401) |
| POST | `/api/v1/users/refresh` | (header `refresh-token: <token>`, no body) | ✅ (auto on 401) |
| POST | `/api/v1/users/validate-email` | `{ email }` | 🟡 |
| POST | `/api/v1/users/reset-password` | `{ email }` | 🟡 |
| POST | `/api/v1/users/reset-password/confirm-code` | `{ email, code }` | 🟡 |
Expand Down
18 changes: 9 additions & 9 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,16 @@ paths:
post:
tags: [auth]
summary: Trade a refresh token for a new access token
description: |
The refresh token is sent as a request **header** (`refresh-token: <token>`),
not in the JSON body. Sending it as a body field returns a pydantic 422.
security: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [refresh_token]
properties:
refresh_token: { type: string }
parameters:
- in: header
name: refresh-token
required: true
schema: { type: string }
description: The refresh JWT from a prior login.
responses:
"200":
description: New token pair
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "flappie-api",
"version": "0.5.0",
"version": "0.5.1",
"description": "Control your Flappie cat door from Node or the terminal. Typed TypeScript client with full CRUD for time plans, settings, and prey events. Unofficial, not associated with Flappie Technologies AG.",
"type": "module",
"license": "MIT",
Expand Down
14 changes: 11 additions & 3 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,14 +153,22 @@ export class FlappieClient {
return data as T;
}

/** Try to refresh the access token. Returns true on success. */
/**
* Try to refresh the access token. Returns true on success.
*
* The Flappie backend reads the refresh token from the `refresh-token`
* HTTP header (not a JSON body) - sending it in the body returns a
* pydantic 422 about a missing header field.
*/
async tryRefresh(): Promise<boolean> {
if (!this.auth.refresh_token) return false;
try {
const res = await this.fetchFn(`${this.baseUrl}/api/v1/users/refresh`, {
method: "POST",
headers: { "Content-Type": "application/json", "Accept": "application/json" },
body: JSON.stringify({ refresh_token: this.auth.refresh_token }),
headers: {
"Accept": "application/json",
"refresh-token": this.auth.refresh_token,
},
});
if (!res.ok) return false;
const data = (await res.json()) as Partial<TokenPair> & { accessToken?: string; refreshToken?: string };
Expand Down
Loading