Skip to content

Sign in with Flowsta

Add "Sign in with Flowsta" to your application and let users sign in with their Flowsta account.

Sign in with Flowsta works like "Login with Google" or "Sign in with Apple" from your side of the integration: standard OAuth 2.0 with PKCE, a pre-built button, and a /oauth/userinfo endpoint. The difference is what happens on Flowsta's side - there is no one in between. A Flowsta account lives on the user's own device, secured by a 24-word recovery phrase and the Flowsta Vault app. The user approves every sign-in with keys only they hold.

How Flowsta Accounts Work

  • Device-hosted identity - the user's identity is a keypair on their device, backed by a 24-word recovery phrase. Flowsta never holds the keys.
  • No passwords - there is no password login. When a user signs in on login.flowsta.com, their Flowsta Vault signs a one-time challenge to prove they hold their keys.
  • Standard OAuth for you - your app never touches any of this. You implement ordinary OAuth 2.0 Authorization Code + PKCE; Flowsta handles the Vault interaction on its login page.

Why Sign in with Flowsta?

For Your Users

  • One account, everywhere - sign in across all Flowsta partner sites
  • Keys only they hold - no password to phish, no credential database to breach
  • Every sign-in approved - the user's own Vault signs each login challenge
  • W3C DIDs - self-sovereign, portable identity

For Developers

  • Quick integration - add a button, configure OAuth, done
  • Standard OAuth 2.0 - industry-standard protocol with PKCE
  • Multiple frameworks - React, Vue, Qwik, Vanilla JS support
  • Beautiful UI - pre-built login buttons and hosted auth pages
  • Free tier - get started without a credit card

How It Works

OAuth 2.0 sequence diagram showing the full login flow: User clicks Sign in with Flowsta, app redirects to login page, user approves the sign-in with their Flowsta Vault and grants consent, Flowsta redirects with auth code, app exchanges code for access token, fetches user info, and user is logged in

OAuth 2.0 Flow

Sign in with Flowsta implements the OAuth 2.0 Authorization Code Flow with PKCE:

  1. User clicks "Sign in with Flowsta" button
  2. Your app redirects to login.flowsta.com (a full-page redirect)
  3. User approves the sign-in with their Flowsta Vault (or creates an account)
  4. User grants consent for the scopes you requested
  5. Flowsta redirects back to your redirect URI with an authorization code
  6. Your app exchanges the code for an access token
  7. Your app fetches user profile data from /oauth/userinfo
  8. User is logged in to your application

Features

Security

  • OAuth 2.0 with PKCE - protection against authorization code interception
  • Refresh tokens - long-lived sessions (30 days)
  • Token revocation - instant logout
  • Rate limiting - protection against abuse
  • Audit logging - full security tracking

Email Stays With the User

Flowsta accounts are device-hosted, and the server keeps only a hash of the user's email - Flowsta has no email database to hand out.

  • The email scope means asking the user to share their address: at the consent screen, the user explicitly provides the email to share (pre-filled from their Vault when available), and Flowsta verifies it against the account's hash before accepting it.
  • Once shared, /oauth/userinfo returns email and email_verified for your app only. Only verified addresses can be shared, so the claim is trustworthy.
  • The shared copy is held per-app for delivery and deleted when the user revokes your app from their dashboard - design for the possibility that it goes away.

Customization

  • Pre-built buttons - responsive designs (light/dark/neutral)
  • Hosted pages - professional login and consent screens
  • App branding - add your logo, privacy policy, terms
  • Custom scopes - request only what you need

Available Scopes

Flowsta uses granular scopes so users only consent to the specific data your app needs:

ScopeDescriptionUser Data
openidUser identifier (auto-included)sub (stable user UUID)
display_nameDisplay namename
usernameUsernamepreferred_username
emailEmail addressemail, email_verified - only after the user explicitly shares it at consent (verified addresses only; revocable)
profile_pictureProfile pictureprofile_picture, has_custom_picture
didDecentralized IDdid (W3C DID)
public_keyPublic keyagent_pub_key (Holochain)
holochainHolochain identity accessagent_pub_key in /userinfo
signSign It signingGates Sign It signing endpoints (no extra /userinfo fields)
verifySign It verificationGates Sign It verification endpoints (no extra /userinfo fields)

Request Only What You Need

Instead of requesting all profile data, choose only the scopes your app actually needs. This builds user trust and simplifies the consent screen.

Email Is a Gift, Not a Given

The email scope asks the user to share their address at the consent screen - they can decline by denying, and they can revoke it later (which deletes Flowsta's shared copy). Design your app to work without an email, and treat one you receive as revocable.

Quick Start

Get Sign in with Flowsta running in 5 minutes:

1. Create a Flowsta Developer Account

  1. Go to dev.flowsta.com
  2. Sign up or log in
  3. Create a new application

2. Configure OAuth Settings

In your app's dashboard:

  1. Add redirect URIs (where users return after login)

    https://yourapp.com/auth/callback
  2. Select scopes your app needs

    • openid (auto-included, returns user ID)
    • display_name, username, profile_picture (profile data)
    • did, public_key (technical identifiers)
  3. Add branding (optional)

    • Logo URL
    • Privacy Policy URL
    • Terms of Service URL

3. Install the Button Widget

bash
npm install @flowsta/login-button

4. Add the Button

The button performs a full-page redirect to login.flowsta.com. There is no success callback in your app - the authorization code arrives at your redirect URI on a fresh page load, and you handle it there (step 5).

tsx
import { FlowstaLoginButton } from '@flowsta/login-button/react';

function App() {
  return (
    <FlowstaLoginButton
      clientId="your_client_id"
      redirectUri="https://yourapp.com/auth/callback"
      scopes={['openid', 'display_name']}
      variant="dark-pill"
      onError={(error) => console.error('Could not start login:', error)}
    />
  );
}
vue
<script setup>
import { FlowstaLoginButton } from '@flowsta/login-button/vue';

const handleError = (error) => {
  console.error('Could not start login:', error);
};
</script>

<template>
  <FlowstaLoginButton
    client-id="your_client_id"
    redirect-uri="https://yourapp.com/auth/callback"
    :scopes="['openid', 'display_name']"
    variant="dark-pill"
    @error="handleError"
  />
</template>
tsx
import { FlowstaLoginButton } from '@flowsta/login-button/qwik';
import { $, component$ } from '@builder.io/qwik';

export default component$(() => {
  const handleError = $((error: any) => {
    console.error('Could not start login:', error);
  });

  return (
    <FlowstaLoginButton
      clientId="your_client_id"
      redirectUri="https://yourapp.com/auth/callback"
      scopes={['openid', 'display_name']}
      variant="dark-pill"
      onError$={handleError}
    />
  );
});
html
<div id="flowsta-login"></div>

<script type="module">
  import { initFlowstaLoginButton } from '@flowsta/login-button/vanilla';

  initFlowstaLoginButton('#flowsta-login', {
    clientId: 'your_client_id',
    redirectUri: 'https://yourapp.com/auth/callback',
    scopes: ['openid', 'display_name'],
    variant: 'dark-pill',
    onError: (error) => console.error('Could not start login:', error),
  });
</script>

5. Handle the Callback

After the user approves the sign-in, Flowsta redirects to your redirect URI with ?code=...&state=.... On that page, validate the state, retrieve the PKCE verifier the button stored, and exchange the code for tokens:

javascript
// On your redirect URI page (e.g. /auth/callback)
import {
  handleCallback,
  validateState,
  retrieveCodeVerifier,
} from '@flowsta/login-button';

const { code, state, error, errorDescription } = handleCallback();

if (error) throw new Error(errorDescription || error);
if (!state || !validateState(state)) throw new Error('State mismatch');

const codeVerifier = retrieveCodeVerifier(state);

// Exchange code for tokens (PKCE - no client secret needed!)
const response = await fetch('https://auth-api.flowsta.com/oauth/token', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    grant_type: 'authorization_code',
    code,
    redirect_uri: 'https://yourapp.com/auth/callback',
    client_id: 'your_client_id',
    code_verifier: codeVerifier,
  }),
});

const { access_token, refresh_token } = await response.json();

No Client Secret Required

With PKCE, you don't need a client secret for browser or mobile apps. The code_verifier proves you initiated the request.

6. Fetch User Data

javascript
const userResponse = await fetch('https://auth-api.flowsta.com/oauth/userinfo', {
  headers: {
    'Authorization': `Bearer ${access_token}`,
  },
});

const user = await userResponse.json();
// { sub, name, preferred_username, did, agent_pub_key, profile_picture, has_custom_picture }
// Note: no email - device-hosted accounts never return one.

That's it! Your users can now sign in with Flowsta.

Next Steps

Need Help?

Documentation licensed under CC BY-SA 4.0.