diff options
| author | Simen A. W. Olsen <hello@simenandre.no> | 2026-11-03 22:08:49 +0100 |
|---|---|---|
| committer | Simen A. W. Olsen <hello@simenandre.no> | 2026-11-03 22:08:49 +0100 |
| commit | bef3ccf4521d4ed431f3cdfc66e618c619ce28e4 (patch) | |
| tree | ef8e6ff90fed4b27d4f2cfd082106f2efdb38ca5 /README.md | |
| parent | a5491c9dccdf4678bf3b4380d208cf2562afeee4 (diff) | |
| download | fiken-bef3ccf4521d4ed431f3cdfc66e618c619ce28e4.tar.gz fiken-bef3ccf4521d4ed431f3cdfc66e618c619ce28e4.zip | |
fikenconfig keeps an OAuth app and a grant per account in ~/.config/fiken, and finds the account that reaches a company. Renewing a grant takes a lock, so programs sharing it take turns.
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 63 |
1 files changed, 61 insertions, 2 deletions
@@ -7,7 +7,7 @@ gets a token for it. go get simenandre.no/fiken ``` -Two packages, and the split is the point: +Three packages, and the split is the point: - **`fiken`** knows how to put a bearer token in a header and how to read Fiken's answers. Where the token came from is not its business. @@ -15,6 +15,8 @@ Two packages, and the split is the point: loopback redirect, refreshing an expired grant, and a `0600` file to store it in. Refreshing is a side effect of asking for a token rather than a step the caller has to remember, which is the only way it gets done everywhere. +- **`fikenconfig`** is the login every program on the machine shares, so + logging in once, from any of them, logs in all of them. ```go store := fikenauth.NewStore(filepath.Join(dir, "fiken-token.json")) @@ -24,6 +26,60 @@ source.LoginCommand = "myapp fiken login" // named in the error if the grant die client, err := fiken.NewWithTokens(source) ``` +## One login for every program + +`fikenconfig` keeps one OAuth app and a grant per Fiken account in +`~/.config/fiken` (`$XDG_CONFIG_HOME/fiken`, or `$FIKEN_CONFIG_DIR`): + +``` +config.json the app, and each account with the companies it reaches +accounts/<email>.json each account's grant +``` + +```json +{ + "app": { + "client_id": "...", + "client_secret": "...", + "redirect_url": "http://localhost:8137/callback" + }, + "accounts": { + "simen@sokkel.io": { + "name": "Simen", + "companies": [{ "name": "Sokkel AS", "slug": "sokkel-as" }] + }, + "post@bjerk.io": { "companies": [...], "token": "a personal API token" } + } +} +``` + +A program names the company it works for and gets the account that reaches +it. Which company that is stays the program's business: + +```go +cfg, err := fikenconfig.Open("") +cfg.LoginCommand = "myapp login" +client, err := cfg.Client(ctx, "sokkel-as") +``` + +Logging in records the grant under the email of the Fiken user who gave it: + +```go +grant, err := cfg.Auth().Login(ctx, announce) +email, err := cfg.AddGrant(ctx, grant) +``` + +A company added in Fiken after the login is found without logging in again: +when no account has it, `Client` asks each account once more. + +Sharing the grant file is the only safe way to share a grant, because Fiken +rotates the refresh token on every renewal and two copies means the first to +renew silently logs out the other. Renewing takes a lock beside the file and +reads it again under the lock, so two programs renewing at once take turns. +`$FIKEN_CLIENT_ID` and `$FIKEN_CLIENT_SECRET` override the app in the file. + +## Without the shared login + A personal API token works too, with no OAuth at all: ```go @@ -48,7 +104,10 @@ that is already gone is usually the state the caller wanted, not a failure. again, the other means try again. **Retries are bounded and only for what is retryable.** A 5xx or 429 is retried; -a 4xx is not, because sending the same bad request again cannot help. +a 4xx is not, because sending the same bad request again cannot help. Nothing +that creates something is retried at all: a 5xx does not say whether Fiken +acted before failing, and a draft or contact made twice is worse than an +error. ## Status |
