@flowsta/login-button
Pre-built "Sign in with Flowsta" button components.
Official login button with built-in PKCE generation and OAuth redirect. Available for React, Vue, Qwik, and vanilla JavaScript.
Current version: 0.1.5.
How the flow works
Clicking the button performs a full-page redirect to Flowsta's login page. After the user signs in, Flowsta redirects the browser back to your redirectUri with the authorization code in the query string (?code=...&state=...). Your callback page handles it from there - the button itself never sees the result, because your page has already navigated away.
Installation
npm install @flowsta/login-buttonFramework Components
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', 'email']}
variant="dark-pill"
onClick={() => console.log('Redirecting to Flowsta...')}
onError={(error) => console.error('Could not start login:', error)}
/>
);
}2
3
4
5
6
7
8
9
10
11
12
13
14
<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', 'email']"
variant="dark-pill"
@error="handleError"
/>
</template>2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { FlowstaLoginButton } from '@flowsta/login-button/qwik';
import { $, component$ } from '@builder.io/qwik';
export default component$(() => {
const handleError = $((error: { error: string; errorDescription?: string }) => {
console.error('Could not start login:', error);
});
return (
<FlowstaLoginButton
clientId="your_client_id"
redirectUri="https://yourapp.com/auth/callback"
scopes={['openid', 'display_name', 'email']}
variant="dark-pill"
onError$={handleError}
/>
);
});2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<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', 'email'],
variant: 'dark-pill',
onError: (error) => console.error('Could not start login:', error),
});
</script>2
3
4
5
6
7
8
9
10
11
12
13
Handling the callback
The authorization code arrives at your redirectUri as query parameters. The package exports helpers to read and validate them:
// On your callback page
import { handleCallback, validateState, retrieveCodeVerifier } from '@flowsta/login-button';
const { code, state, error, errorDescription } = handleCallback();
if (error) {
showError(errorDescription || error);
} else if (!state || !validateState(state)) {
showError('State mismatch - possible CSRF, start over');
} else {
// Exchange the code for tokens at POST /oauth/token,
// sending the PKCE verifier stored when the button was clicked:
const codeVerifier = retrieveCodeVerifier(state);
// ...POST { grant_type, code, redirect_uri, client_id, code_verifier }
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
Want session management too?
If you'd rather not hand-roll the token exchange, use @flowsta/auth (v2.3.1+) - call auth.handleCallback() on your callback page and it completes the flow, whether the login was started by this button or by auth.login(). It handles PKCE, the token exchange, user info, and session storage in one call.
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
clientId | string | Yes | - | Your app's client ID |
redirectUri | string | Yes | - | OAuth callback URL |
scopes | FlowstaScope[] | No | ['openid', 'display_name'] | OAuth scopes array |
variant | ButtonVariant | No | 'dark-pill' | Button style variant |
loginUrl | string | No | 'https://login.flowsta.com' | Flowsta login URL |
className | string | No | '' | Custom CSS class |
disabled | boolean | No | false | Disable the button |
Framework extras:
- React -
childrenreplaces the default button image with your own content. - Vue - the default slot replaces the default button image.
- Qwik -
classis accepted as an alias forclassName.
Requesting email? Don't count on getting one
The email scope asks the user to share their address at the consent screen - they can decline (by denying) or revoke it later. If shared, userinfo includes email and email_verified (verified addresses only). Design your app to work without one.
FlowstaScope
type FlowstaScope =
| 'openid' | 'email' | 'display_name'
| 'username' | 'did' | 'public_key'
| 'profile_picture' | 'holochain';2
3
4
holochain supersedes public_key
The public_key scope is still in the type for backward compatibility, but new apps should request holochain instead - it covers the agent public key.
Callbacks
| React | Vue | Qwik | Payload | When it fires |
|---|---|---|---|---|
onClick | @click | onClick$ | - | Button clicked, before the redirect |
onError | @error | onError$ | { error: 'authorization_failed', errorDescription?: string } | Building the authorization URL failed - the redirect never happened |
There is no success callback: on success the browser has navigated away to Flowsta's login page, and the result arrives at your redirectUri (see Handling the callback).
Button Variants
| Variant | Best For |
|---|---|
dark-pill | Light backgrounds (recommended) |
dark-rectangle | Light backgrounds, square corners |
light-pill | Dark backgrounds |
light-rectangle | Dark backgrounds, square corners |
neutral-pill | Any background |
neutral-rectangle | Any background, square corners |
View all button styles and download SVGs
Vanilla JS API
The vanilla JS export provides two functions:
// Create a button element and append it to a container
initFlowstaLoginButton(selector: string | HTMLElement, options): HTMLButtonElement;
// Create a button element (manual DOM management)
createFlowstaLoginButton(options): HTMLButtonElement;2
3
4
5
Without npm
Use buttons without a package manager:
<a href="YOUR_OAUTH_URL">
<img src="https://docs.flowsta.com/buttons/svg/flowsta_signin_web_dark_pill.svg"
alt="Sign in with Flowsta"
width="175" height="40">
</a>2
3
4
5
See the Vanilla JS guide for a complete implementation without npm.
Next Steps
- Button Downloads - Download SVG/PNG button assets
- @flowsta/auth SDK - Core authentication SDK
- Auth Overview - OAuth flow documentation
- Vanilla JS Guide - Complete implementation without npm