Security checklist

What an app built on the SDK must get right. Token storage, content security policy, what never to log, server-side checks, untrusted text from posts, and what to do after a leak.

The SDK keeps the sign-in protocol safe for you: PKCE, state checks, one-time codes, a pinned issuer. What it cannot do is protect the rest of your page. This is a checklist of what is left to your app, in order of how often it goes wrong.

Tokens

  • Choose session storage unless you need more. It lives only in the tab. local keeps tokens across restarts but exposes them to any script on your origin. See Sessions and storage.
  • Never put a token in an address. Not in a query string, not in a link, not in a redirect. Addresses end up in logs, history and referrer headers. The SDK sends tokens only in headers.
  • Never log tokens. onTrace shortens tokens, codes and verifiers, but your own code does not. Do not log getAccessToken() results, Authorization headers or whole requests of your own.
  • Do not copy tokens into other storage, cookies of your own or analytics. Ask getAccessToken() when you need one.
  • Keep the App Secret out of the browser. A website app's secret is for servers. A page that needs no secret is a single-page app: it has none.

Transport and policy

  • Serve over HTTPS only. Sign-in cannot work on plain http outside localhost, and the SDK refuses an http issuer. Redirect http to https for the whole site.
  • Set a content security policy. A strict one is the best protection for tokens in web storage. Allow only what you use, and let the SDK reach UNET:
Content-Security-Policy: default-src 'self'; script-src 'self'; connect-src 'self' https://api.unet.ovh; img-src 'self' https://api.unet.ovh data:; frame-ancestors 'none'

Add blob: to media-src if you play audio through unet.api.media(). Avoid 'unsafe-inline' and 'unsafe-eval' in script-src.

  • Pin the SDK version and use a lockfile. The package has no dependencies of its own, which keeps the supply chain short.

Your server

  • Verify on the server whatever the browser claims. A button that is hidden in the browser is not access control. See Verify on your server.
  • Check that the token is for your app. Use verifyAccessToken (website apps) or verifyIdToken (apps without a secret), never only the profile endpoint.
  • Key your users by sub. It is the person's id for your app and cannot be used to recognise them on other sites. Do not use username, which can change.
  • Do not store more than you need. UNET gives you no email address; do not ask for one to link accounts across sites.

Anything another person wrote can be hostile: post text, display names, bios, hashtags, profile links, image descriptions, and the title and description of a link preview.

  • Render text as text. Use textContent or your framework's escaping. Never build HTML from these values with string concatenation or innerHTML.
  • Check the scheme of every link you build from UNET data. profile.website, profile.links[].url and preview.url can be anything the person typed. Only http: and https: addresses belong in an href:
function safeHref(value: string | null | undefined): string | undefined {
  try {
    const url = new URL(value ?? "");
    return url.protocol === "https:" || url.protocol === "http:" ? url.href : undefined;
  } catch {
    return undefined; // not an absolute address: no link
  }
}

splitLinkPreview already passes only http(s) addresses for the preview, but the other link fields are your responsibility.

  • Open outside links with rel="noopener noreferrer".
  • Treat returnTo from the address bar as untrusted. Accept only paths on your own site. See The sign-in flow.

Permissions

  • Ask for the least. Each permission is one more thing a stolen token can do. See Permissions.
  • Do not request offline_access by habit. It creates a refresh token that outlives the tab.
  • Treat write permissions with care. posts:write, follows:write and safety:manage act in the person's name; ask for them at the moment they are needed.

If you suspect a leak

  1. Sign out the affected sessions: unet.signOut() revokes the tokens at UNET.
  2. Website apps: rotate the App Secret in the console. The old one stops working at once.
  3. Turn the app off in the console if the leak is severe. Every token of the app stops working immediately, and every person must sign in again once you turn it back on with a fixed build.
  4. Tell the people affected. They can also remove your app themselves under Connected apps in their UNET settings, which revokes it for their account.
  5. Report a vulnerability in UNET or in the SDK to the address in the security policy.

What next