notesy::accounts

Signing the user in to a web service, the way desktop apps do: notesy opens the service's sign-in page in the browser, the user says yes there, and the browser comes back to notesy with a code that notesy trades for tokens (OAuth 2.0 for native apps, RFC 8252, with PKCE). Takes the accounts permission, and the service in the plugin's [[accounts]] (plugin.toml).

tomlpermissions = ["accounts"]

[net]
hosts = ["github.com", "api.github.com"]

[[accounts]]
id = "github"
name = "GitHub"
authorize_url = "https://github.com/login/oauth/authorize"
token_url = "https://github.com/login/oauth/access_token"
client_id = "Iv1.0123456789abcdef"
scopes = ["read:user"]
hosts = ["api.github.com"]
rustuse notesy::{accounts, commands, log, net};

pub fn ready() {
    commands::add("sign-in", "Sign in to GitHub", |command| {
        accounts::sign_in("github", |result| {
            let signed_in = result?;
            log::info(`signed in, with ${signed_in.scope}`);
        });
    });
    commands::add("whoami", "Who am I on GitHub", |command| {
        net::fetch(#{ url: "https://api.github.com/user", account: "github" }, |result| {
            log::info(result?.text);
        });
    });
}
Function What it does
accounts::sign_in(id, then) Opens the sign-in page for account id in the browser, then calls then(result) once the user's back: Ok(#{ account, scope, expires_at }), or Err(why) (they said no, or didn't finish within five minutes). One sign-in at a time.
accounts::signed_in(id) Whether there's a token for account id.
accounts::sign_out(id) Forgets account id's tokens.

expires_at is when the token runs out, in seconds since 1970, when the service said.

#Using the account

A request that names the account (account: "github" in net::fetch) goes with its token as Authorization, when it's to one of the account's own sites: its sign-in page's host, its token address's host, or one in its hosts (api.github.com above). Naming the account in a request to any other site is an error. When the token has run out and the service gave a refresh token, notesy refreshes it first. The script never sees a token: they're kept in the plugin's secrets, under names only notesy can read.

#What notesy checks

  • The sign-in page and the token address have to be on hosts the plugin names; notesy checks when it reads the manifest, and again when the script signs in.
  • The token goes only to the account's own sites, never to another site the plugin names, nor on to where a redirect sends it.
  • The browser comes back to a port on this computer only, open just for that sign-in, and counts only with the state notesy made for it.
  • The code is traded with the PKCE verifier notesy made, so a code taken on the way is no use to anyone else.
  • Removing the plugin with its data deletes its tokens.

Every page