LinkStacked

Authentication

Linkstacked supports three authentication paths, each suited to a different integration shape:

PathUse it for
OAuth bearerServer-side apps and CLIs acting on behalf of a user.
Session cookieFirst-party browser sessions (the dashboard itself).
Service accountEnterprise back-office automation (planned, not GA yet).

OAuth bearer flow

The recommended path for any non-browser integration. The flow follows RFC 6749 with PKCE — most OAuth client libraries handle it without modification.

    1. Direct the user to https://api.linkstacked.com/oauth/authorize with your client_id, redirect_uri, and a generated PKCE pair.
    2. After they consent, Linkstacked redirects to your redirect_uri with an ?code= parameter.
    3. Exchange the code for a token by calling the GraphQL oauthLogin mutation (see below). Tokens come back as a short-lived access token plus a long-lived refresh token.
mutation OAuthLogin($input: OAuthInput!) {
  oauthLogin(input: $input) {
    accessToken
    refreshToken
    expiresAt
    user { _id email subscriptionTier }
  }
}
{
  "input": {
    "provider": "LINKSTACKED",
    "code": "ac_...",
    "codeVerifier": "...",
    "redirectUri": "https://your-app.example/cb"
  }
}

Refreshing tokens

Access tokens expire in 60 minutes. Use the refreshToken mutation to mint a new pair without sending the user through the consent screen again:

mutation Refresh($refreshToken: String!) {
  refreshToken(token: $refreshToken) {
    accessToken
    refreshToken
    expiresAt
  }
}

Refresh tokens rotate

Each call to refreshToken returns a new refresh token and invalidates the old one. Persist the latest token your client receives, and treat the rotation as the canonical "is the user still logged in?" signal.

Two-factor authentication

If a user enables 2FA, the regular login flow returns a partial response with requiresTwoFactor: true and a temporary twoFactorToken. Complete authentication with verifyTwoFactorLogin:

mutation Verify($input: VerifyTwoFactorInput!) {
  verifyTwoFactorLogin(input: $input) {
    accessToken
    refreshToken
    user { _id }
  }
}

Session management

The same auth context exposes a session-management surface so users can audit and revoke active sessions from your app:

  • activeSessions — list every session.
  • revokeSession(sessionId) — kill a single session.
  • revokeAllSessions — kill everything except the caller's current session (useful for "log out everywhere else").