aboutsummaryrefslogtreecommitdiffstats
path: root/README.md
diff options
context:
space:
mode:
authorSimen A. W. Olsen <hello@simenandre.no>2026-11-03 22:08:49 +0100
committerSimen A. W. Olsen <hello@simenandre.no>2026-11-03 22:08:49 +0100
commitbef3ccf4521d4ed431f3cdfc66e618c619ce28e4 (patch)
treeef8e6ff90fed4b27d4f2cfd082106f2efdb38ca5 /README.md
parenta5491c9dccdf4678bf3b4380d208cf2562afeee4 (diff)
downloadfiken-bef3ccf4521d4ed431f3cdfc66e618c619ce28e4.tar.gz
fiken-bef3ccf4521d4ed431f3cdfc66e618c619ce28e4.zip
Share one Fiken login between programsHEADv0.4.0main
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.md63
1 files changed, 61 insertions, 2 deletions
diff --git a/README.md b/README.md
index 7e59e41..1c6c2ad 100644
--- a/README.md
+++ b/README.md
@@ -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