abovetheborg34 downloadsPeriodically fetches an OAuth2 client-credentials token and stores it in Obsidian's SecretStorage so other plugins can use it.
Periodically fetches OAuth2 client_credentials access tokens from one or
more configurable token endpoints and stores them in Obsidian's
SecretStorage, so
other plugins can read them by secret name.
Connections are listed in a table with one row per OAuth connection (name, token URL, target secret, refresh interval, plugin to reload). Use Add connection to open a form, and the row icons to fetch now, edit, or delete. Fetch all now refreshes every connection. The form has:
grant_type sent with the request (default client_credentials).client_id.client_secret.app.secretStorage.getSecret(name).scope sent with the request.expires_in (seconds, RFC 6749) has elapsed. If the server omits
expires_in, or a fetch fails, the refresh interval is used. Waits are at
least 30 seconds, and the table shows "On expiry" for these connections.The command palette entry Fetch all OAuth tokens now refreshes every
connection. Settings saved by older versions (a single flat connection) are
migrated automatically into one connection named "Default".
Turn on Debug mode in the plugin settings (it is off again after every
restart), trigger a fetch, then use
View debug log (or the Show OAuth debug log command) and click Copy.
For each token request it records the URL, form fields, and the server's status,
response headers and body, which usually explains errors such as HTTP 401
(error_description, WWW-Authenticate). Values are logged as-is, including the
client secret and returned tokens, so treat copied logs as sensitive. Failed
fetches are always recorded (with or without debug mode); the request/response
detail needs debug mode on. The log is kept in memory only (last 200 entries)
and is cleared when Obsidian quits or the plugin is disabled.
The plugin sends a client_credentials request (client ID and client secret)
only to the Token URL you configure on each connection. Client secrets and
fetched tokens are stored in Obsidian's SecretStorage, never in the plugin's
data.json. No telemetry or other network requests are made. "Plugin to
reload" uses Obsidian's internal plugin manager (not a public API) to disable
and re-enable the chosen plugin.
npm install
npm run dev # watch build with esbuild
npm run build # type-check + production build
Commit your changes first so the working tree is clean, then run:
npm version patch # or minor / major
git push --follow-tags
npm version updates package.json, manifest.json, and versions.json, then
creates a commit and matching version tag (without a v prefix). Pushing the
commit and tag runs .github/workflows/release.yml, which tests, builds,
attests, and publishes a GitHub release with main.js and manifest.json.
For the first submission only, add the plugin through the
Obsidian Community directory account page.
Subsequent releases are scanned automatically.
All OAuth/network/secret-storage logic is behind small interfaces
(HttpClient, SecretStore, Scheduler) so tests run against in-memory fakes
instead of a real token endpoint or Obsidian's runtime — no real API usage is
burned and no obsidian module needs to be loaded:
test/fakes/FakeOAuthServer.ts — simulates the token endpoint in memory.test/fakes/InMemorySecretStore.ts — in-memory stand-in for SecretStorage.test/fakes/FakeScheduler.ts — manually-advanced clock instead of real timers.The settings tab (SettingsTab.ts) is tested the same way: test/mocks/obsidian.ts
provides minimal fakes of PluginSettingTab/Setting/SecretComponent backed by
real jsdom elements, so test/SettingsTab.test.ts renders the actual settings UI
and simulates real user input (typing, selecting a secret, clicking "Fetch now")
without launching Obsidian. This test file runs under the jsdom environment via
a @jest-environment jsdom docblock; the rest of the suite runs under node.
npm test
test-vault/ is a ready-made vault with this plugin and a demo consumer plugin
(demo/secret-target-demo) symlinked in. A local mock OAuth2 endpoint
(demo/mock-oauth-server.mjs) lets you try the full workflow without a real provider.
npm install && npm run dev (creates main.js, which the vault symlinks to).npm run mock-server
(override with PORT, CLIENT_ID, CLIENT_SECRET, SCOPES, REQUIRE_SCOPE=1, EXPIRES_IN seconds).
It follows RFC 6749 section 4.4: it requires grant_type=client_credentials,
accepts credentials in the form body or an HTTP Basic header, validates the
optional scope against SCOPES (default read write), and returns the
granted scope plus standard invalid_request / invalid_client /
unsupported_grant_type / invalid_scope errors.test-vault/ as a vault and enable both community plugins
(turn off Restricted Mode if prompted).http://localhost:8080/tokentest-clienttest-secrettt-secretread (optional)tt-secret.Fetch all OAuth tokens now).
To test the reload, set "Plugin to reload" to Secret Target Demo first; its
status bar loads: N counter increases after a confirmed reload. The mock server logs each
request, and the demo plugin's status bar shows the new mock-token-N-... value
within 5 seconds.