Nuvaka › Developer docs › Sign in with Nuvaka

Sign in with Nuvaka

Your desktop program, website or command-line tool can recognise users by their Nuvaka account. If the user allows it, your app can send them notifications and read and write inside one My Files folder they choose. It is standard OAuth 2.1 and OpenID Connect, so existing libraries work.

How it works

  • No password is ever typed. The user never enters their Nuvaka password in a browser or in your app. Approval happens in Nuvaka General App, where they are already signed in.
  • The approval page in the browser shows a short approval code (XXXX-XXXX) and an "Approve in General App" button (nuvaka://oauth?code=…). The app opens and shows your app's name, return address and requested permissions; the user approves or denies. The code can also be typed in Profile › Connected apps.
  • Users see their connected apps in the same place and can Remove access at any time; all tokens stop working immediately. Every sign-in sends the user an "X signed in with your account" notification.

Registering a client

For now the Nuvaka team registers clients: write to support with your app's name, logo, client type, redirect URIs and the scopes you need. You get a client_id (and, for server-side apps, a nvcs_… secret shown only once).

TypeForAuthentication
publicdesktop, mobile, command line (can't keep a secret)client_id + PKCE only
confidentialserver-side web appclient_secret_post or client_secret_basic

The redirect_uri must match the registration exactly. Accepted forms: https://…; loopback http://127.0.0.1:port/… (the port is registered too); a reverse-domain custom scheme (com.example.app:/cb).

Scopes

scopeWhat it gives
openidsign-in; sub is the user's stable id. The user can't turn it off
profilepreferred_username
emailemail, email_verified. The user can turn it off during approval, so be ready to work without it
notifications.sendnotifications to the user (20 per hour per connection)
storage.folderthe one My Files folder the user picks during approval, with its subfolders: list, download, upload, delete. Secure Space and system folders can't be chosen
offline_accessrefresh token

You can only request scopes allowed for your client; unknown or disallowed scopes return invalid_scope. Users can switch off everything except openid; the scope field of the token response says what was actually granted.

Endpoints

Issuer: https://generalappapi.nuvaka.com. The discovery document lists every address; pointing your library at it is enough.

EndpointDescription
GET /.well-known/openid-configurationdiscovery
GET /oauth/jwksid_token signing key (RS256, kid)
GET /oauth/authorizeapproval page (opened in the browser)
POST /oauth/devicestart the device flow
POST /oauth/tokenauthorization_code, refresh_token, urn:ietf:params:oauth:grant-type:device_code
GET|POST /oauth/userinfoBearer; needs openid
POST /oauth/revoketoken revocation (RFC 7009, always 200)

Errors follow RFC 6749: { error, error_description }; a bad client gets 401 invalid_client. Endpoints are limited to 60 requests per minute per IP (429).

Authorization code + PKCE (desktop and web)

PKCE is required and only S256 is accepted. state (CSRF) and nonce are recommended.

  1. Create PKCE

    A random code_verifier of 43–128 characters; code_challenge = BASE64URL(SHA256(code_verifier)).

  2. Open the browser

    Send the user to the approval page. On desktop use a loopback listener (http://127.0.0.1:PORT/cb) or a custom scheme.

  3. The user approves in General App

    The page polls every 2 seconds. On approval it returns to redirect_uri?code=nvac_…&state=…&iss=…, on denial to redirect_uri?error=access_denied&state=…&iss=…. Check state and iss.

  4. Exchange the code

    The code is single-use and valid for 5 minutes; send the same redirect_uri. The request (approval page) is valid for 10 minutes.

# 2) Address to open in the browser
https://generalappapi.nuvaka.com/oauth/authorize?response_type=code
  &client_id=example-desktop
  &redirect_uri=http%3A%2F%2F127.0.0.1%3A53682%2Fcb
  &scope=openid%20profile%20storage.folder%20offline_access
  &state=RANDOM&nonce=RANDOM
  &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
  &code_challenge_method=S256

# 4) Code exchange
curl -X POST https://generalappapi.nuvaka.com/oauth/token \
  -d grant_type=authorization_code -d code=nvac_… \
  -d redirect_uri=http://127.0.0.1:53682/cb \
  -d code_verifier=… -d client_id=example-desktop
{ "access_token": "nvo_…", "token_type": "Bearer", "expires_in": 3600,
  "scope": "openid profile storage.folder offline_access",
  "refresh_token": "nvr_…", "id_token": "eyJ…" }

Redirected errors (after the redirect URI is verified): unsupported_response_type, invalid_request (no PKCE or not S256), invalid_scope. An unknown client or an unregistered redirect URI is never redirected to; an error page is shown instead.

Device flow (TV, console, command line)

For tools without a browser or a redirect address (RFC 8628).

curl -X POST https://generalappapi.nuvaka.com/oauth/device -d client_id=example-cli -d scope="openid profile"
# → { device_code, user_code: "WDJB-MJHT", verification_uri, verification_uri_complete, expires_in: 600, interval: 5 }

# Show the user the user_code and verification_uri (or a QR of verification_uri_complete).
# Then poll every interval seconds:
curl -X POST https://generalappapi.nuvaka.com/oauth/token \
  -d grant_type=urn:ietf:params:oauth:grant-type:device_code -d device_code=… -d client_id=example-cli

Poll results: authorization_pending (wait), slow_down (increase the interval), access_denied, expired_token, or the tokens on success.

Tokens

TokenPrefixLifetime
accessnvo_1 hour
refreshnvr_30 days, only with offline_access
id_tokenJWT (RS256)1 hour
  • Refresh tokens rotate: grant_type=refresh_token returns a new access token and a new refresh token every time; the old one is revoked immediately. If a revoked refresh token is used again (a sign of leakage), every token of the connection is revoked and invalid_grant is returned. Store the new token right away.
  • id_token: iss, aud (= client_id), sub, iat, exp, nonce if given; preferred_username with profile, email and email_verified with email. Verify the signature with /oauth/jwks.
  • Tokens are opaque; don't rely on their contents. If access is refused, refresh or ask the user to sign in again.

App API

Base address https://generalappapi.nuvaka.com/api/oauth-api/, header Authorization: Bearer nvo_….

EndpointscopeDescription
GET me—{ sub, username?, email?, language, scopes, folderId? }
POST notificationsnotifications.send{ title (1–100), message (≤ 500) }; shown to the user as "App name: title", and the user can mute it. 20 per hour; more returns 429 rate_limited
GET storage/items?folderId=storage.folderfolder contents (default: the chosen root)
GET storage/files/{fileId}storage.folderdownload, supports Range
POST storage/upload?folderId=&name=storage.folderraw body ≤ 100 MB; the user's quota applies
DELETE storage/files/{fileId}storage.foldermoves to the trash

Errors: invalid or expired token 401 invalid_token; missing scope 403 insufficient_scope; outside the chosen folder 403 out_of_scope. If the account is deleted or blocked, the client is disabled or the user removes access, the token stops working immediately.

Security recommendations

  • Generate a random state for every request and compare it on return; verify the iss parameter (RFC 9207).
  • Request only the scopes you actually use; email may be switched off.
  • Keep tokens in the operating system's key store; never log them.
  • Show the approval code in your app too; the Nuvaka approval screen warns "Don't approve if you didn't start this request".

Nuvaka Apps API v1 · last updated 2026-09-28