Device Sign-in: `banksync login --device` and the OAuth Device Grant
Sign a terminal or a machine without a browser into BankSync with the OAuth 2.0 device authorization grant (RFC 8628): the CLI flow, the endpoints, their errors and limits, and how to revoke it.
3 min read
On this page
Device sign-in signs a machine with no usable browser into BankSync: an SSH session, a container, a CI box you are logged in to. The machine shows a short code; you type it into BankSync in any browser where you're signed in, choose a workspace and approve. It is the standard OAuth 2.0 Device Authorization Grant, offered to BankSync's own CLI client (banksync-cli) only; the endpoints below document what the CLI does.
The result is an ordinary OAuth grant: scoped to the permissions you approved and to one workspace, refreshed like any other, listed under Settings, Developers, Connected apps, and revocable there.
Availability
From the CLI#
banksync login --devicebanksync login --device --scope transactions:read,feeds:readTo sign in, open https://app.banksync.io/deviceand enter the code BCDF-GHJK
Check that the code there matches this one before you approve.Waiting for approval (expires in 10 minutes)...Logged in to Acme Books (profile: default).The CLI stores the credential exactly as banksync login does. banksync logout forgets it on this machine; revoking it under Connected apps ends it everywhere.
The endpoints#
Discovery (https://auth.banksync.io/.well-known/oauth-authorization-server) advertises device_authorization_endpoint and the grant type urn:ietf:params:oauth:grant-type:device_code.
1. The client: banksync-cli#
The device grant belongs to BankSync's own command line. It uses the first-party public client banksync-cli, which BankSync defines itself, so there is nothing to register and no secret. Dynamic client registration refuses the device grant (invalid_client_metadata), and /device_authorization refuses any other client (unauthorized_client). That way the approval page only ever names a client BankSync vouches for. For your own tool, use banksync login --device and the CLI's stored credential, an API key, or the browser authorization-code flow.
2. Start: POST /device_authorization#
POST https://auth.banksync.io/device_authorizationContent-Type: application/x-www-form-urlencoded
client_id=banksync-cli&scope=banks:read feeds:read&platform=linuxplatform is a BankSync extension the approval page shows (web, windows, mac, linux, ios, android, other).
{ "device_code": "Q0hBTkdFLU1FLWl0J3MtYS0zMi1ieXRlLXJhbmRvbS12YWx1ZQ", "user_code": "BCDF-GHJK", "verification_uri": "https://app.banksync.io/device", "verification_uri_complete": "https://app.banksync.io/device?code=BCDF-GHJK", "expires_in": 600, "interval": 5}Show user_code and verification_uri, and ask the person to type the code. verification_uri_complete is for display (a QR code); the page still asks them to confirm the code against your screen. Keep device_code secret.
3. Poll: POST /token#
POST https://auth.banksync.io/tokenContent-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=Q0hBTk...&client_id=banksync-cliWait interval seconds between polls. Until approval the answer is HTTP 400:
error | Meaning | Do |
|---|---|---|
authorization_pending | Not approved yet | Poll again after interval seconds |
slow_down | You polled too soon | Add 5 seconds to interval for good |
access_denied | Denied, or the person was signed out or left the workspace | Stop |
expired_token | Ten minutes passed, or the grant was already issued | Stop; start again |
invalid_grant | The device code belongs to another client | Stop |
Once approved, one poll gets the standard token response (access_token, refresh_token, expires_in, scope). Refresh it with grant_type=refresh_token like any BankSync grant.
Limits#
/device_authorizationis rate limited per IP, and/tokenper IP and per client; a limited request is HTTP 429 withRetry-After.- The person approving has a budget of five wrong codes in fifteen minutes.
- While device sign-in is turned off,
/device_authorizationanswerstemporarily_unavailable(HTTP 503) and the approval page can't look up or approve a code, so a code issued before it was turned off ends inexpired_token./tokenitself isn't switched off: refreshing existing grants keeps working.
Revoking#
- One device: Settings, Developers, Connected apps, or
POST /revokewith its refresh token. - Sign out everywhere (
POST /api/device-signin/sessions/revoke-allfrom the app) ends signed-in browser and Excel add-in sessions only. It does not end OAuth grants, API keys or SimpleFIN access, and its response says so instill_active; revoke those where they live.
Security notes#
- Approving needs a sign-in from the last ten minutes that passed two-step verification when it's on, on a verified account. A session from the Excel add-in can never approve.
- Only BankSync's own CLI client can start a device sign-in, so the approval page never shows a name someone else chose. It names the client, the permissions, the platform, when and roughly where the device asked, and you choose the workspace and may grant fewer permissions.
- Never approve a code someone else sent you. BankSync staff never ask you to.
Use this page with your AI assistant
Every BankSync doc is available as plain Markdown for agents and LLMs.