Code Examples
Complete, copy-paste examples for integrating Flowsta Auth into your application.
Quick Start (Any Framework)
The simplest integration - works with any JavaScript framework:
typescript
import { FlowstaAuth } from '@flowsta/auth';
const auth = new FlowstaAuth({
clientId: 'your_client_id',
redirectUri: window.location.origin + '/auth/callback'
});
// Login button click handler
document.getElementById('login-btn').onclick = () => auth.login();
// On your callback page (/auth/callback)
if (window.location.pathname === '/auth/callback') {
auth.handleCallback().then(user => {
console.log('Logged in:', user.displayName);
window.location.href = '/dashboard';
});
}React
Basic React App
tsx
// src/App.tsx
import { FlowstaAuthProvider, useFlowstaAuth } from '@flowsta/auth/react';
function App() {
return (
<FlowstaAuthProvider
clientId="your_client_id"
redirectUri={window.location.origin + '/auth/callback'}
>
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/auth/callback" element={<AuthCallback />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Router>
</FlowstaAuthProvider>
);
}Login Component
tsx
// src/components/LoginButton.tsx
import { useFlowstaAuth } from '@flowsta/auth/react';
export function LoginButton() {
const { isAuthenticated, user, login, logout, isLoading } = useFlowstaAuth();
if (isLoading) {
return <button disabled>Loading...</button>;
}
if (isAuthenticated) {
return (
<div className="user-menu">
<img src={user.profilePicture} alt={user.displayName} />
<span>{user.displayName}</span>
{user.username && <span className="username">@{user.username}</span>}
<button onClick={logout}>Logout</button>
</div>
);
}
return (
<button onClick={login} className="login-btn">
Sign in with Flowsta
</button>
);
}Auth Callback Page
tsx
// src/pages/AuthCallback.tsx
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useFlowstaAuth } from '@flowsta/auth/react';
export function AuthCallback() {
const { handleCallback, error } = useFlowstaAuth();
const navigate = useNavigate();
useEffect(() => {
handleCallback()
.then(() => navigate('/dashboard'))
.catch(err => console.error('Auth failed:', err));
}, []);
if (error) {
return (
<div className="error">
<h2>Authentication Failed</h2>
<p>{error}</p>
<a href="/">Go back home</a>
</div>
);
}
return <div>Completing login...</div>;
}Protected Route
tsx
// src/components/ProtectedRoute.tsx
import { Navigate } from 'react-router-dom';
import { useFlowstaAuth } from '@flowsta/auth/react';
export function ProtectedRoute({ children }: { children: React.ReactNode }) {
const { isAuthenticated, isLoading } = useFlowstaAuth();
if (isLoading) {
return <div>Loading...</div>;
}
if (!isAuthenticated) {
return <Navigate to="/" replace />;
}
return <>{children}</>;
}
// Usage:
<Route
path="/dashboard"
element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
}
/>User Profile Display
tsx
// src/components/UserProfile.tsx
import { useFlowstaAuth } from '@flowsta/auth/react';
export function UserProfile() {
const { user } = useFlowstaAuth();
if (!user) return null;
return (
<div className="profile">
<img
src={user.profilePicture}
alt={user.displayName}
className="avatar"
/>
<h2>{user.displayName}</h2>
{user.username && <p className="username">@{user.username}</p>}
{user.email && <p className="email">{user.email}</p>}
<p className="did" title={user.did}>
DID: {user.did?.substring(0, 20)}...
</p>
</div>
);
}Vue 3
Main App
vue
<!-- src/App.vue -->
<script setup lang="ts">
import { provide } from 'vue';
import { FlowstaAuth } from '@flowsta/auth';
import { RouterView } from 'vue-router';
const auth = new FlowstaAuth({
clientId: 'your_client_id',
redirectUri: window.location.origin + '/auth/callback'
});
provide('flowstaAuth', auth);
</script>
<template>
<RouterView />
</template>Composable Hook
typescript
// src/composables/useFlowstaAuth.ts
import { ref, inject, onMounted } from 'vue';
import type { FlowstaAuth, FlowstaUser } from '@flowsta/auth';
export function useFlowstaAuth() {
const auth = inject<FlowstaAuth>('flowstaAuth')!;
const user = ref<FlowstaUser | null>(null);
const isAuthenticated = ref(false);
const isLoading = ref(true);
onMounted(() => {
isAuthenticated.value = auth.isAuthenticated();
user.value = auth.getUser();
isLoading.value = false;
});
const login = () => auth.login();
const logout = () => {
auth.logout();
user.value = null;
isAuthenticated.value = false;
};
const handleCallback = async () => {
const result = await auth.handleCallback();
user.value = result;
isAuthenticated.value = true;
return result;
};
return {
user,
isAuthenticated,
isLoading,
login,
logout,
handleCallback
};
}Login Component
vue
<!-- src/components/LoginButton.vue -->
<script setup lang="ts">
import { useFlowstaAuth } from '@/composables/useFlowstaAuth';
const { isAuthenticated, user, login, logout, isLoading } = useFlowstaAuth();
</script>
<template>
<div v-if="isLoading">Loading...</div>
<div v-else-if="isAuthenticated" class="user-menu">
<img :src="user?.profilePicture" :alt="user?.displayName" />
<span>{{ user?.displayName }}</span>
<span v-if="user?.username" class="username">@{{ user.username }}</span>
<button @click="logout">Logout</button>
</div>
<button v-else @click="login" class="login-btn">
Sign in with Flowsta
</button>
</template>Auth Callback Page
vue
<!-- src/pages/AuthCallback.vue -->
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { useRouter } from 'vue-router';
import { useFlowstaAuth } from '@/composables/useFlowstaAuth';
const router = useRouter();
const { handleCallback } = useFlowstaAuth();
const error = ref<string | null>(null);
onMounted(async () => {
try {
await handleCallback();
router.push('/dashboard');
} catch (err) {
error.value = (err as Error).message;
}
});
</script>
<template>
<div v-if="error" class="error">
<h2>Authentication Failed</h2>
<p>{{ error }}</p>
<router-link to="/">Go back home</router-link>
</div>
<div v-else>Completing login...</div>
</template>Qwik
Root Layout
tsx
// src/routes/layout.tsx
import { component$, Slot, useContextProvider, createContextId } from '@builder.io/qwik';
import { FlowstaAuth } from '@flowsta/auth';
export const AuthContext = createContextId<FlowstaAuth>('flowsta-auth');
export default component$(() => {
const auth = new FlowstaAuth({
clientId: 'your_client_id',
redirectUri: window.location.origin + '/auth/callback'
});
useContextProvider(AuthContext, auth);
return <Slot />;
});Login Component
tsx
// src/components/LoginButton.tsx
import { component$, useContext, useSignal, useVisibleTask$ } from '@builder.io/qwik';
import { AuthContext } from '~/routes/layout';
import type { FlowstaUser } from '@flowsta/auth';
export const LoginButton = component$(() => {
const auth = useContext(AuthContext);
const user = useSignal<FlowstaUser | null>(null);
const isAuthenticated = useSignal(false);
useVisibleTask$(() => {
isAuthenticated.value = auth.isAuthenticated();
user.value = auth.getUser();
});
return (
<>
{isAuthenticated.value ? (
<div class="user-menu">
<img src={user.value?.profilePicture} alt={user.value?.displayName} />
<span>{user.value?.displayName}</span>
{user.value?.username && (
<span class="username">@{user.value.username}</span>
)}
<button onClick$={() => {
auth.logout();
window.location.href = '/';
}}>
Logout
</button>
</div>
) : (
<button onClick$={() => auth.login()} class="login-btn">
Sign in with Flowsta
</button>
)}
</>
);
});Auth Callback Page
tsx
// src/routes/auth/callback/index.tsx
import { component$, useVisibleTask$, useSignal, useContext } from '@builder.io/qwik';
import { useNavigate } from '@builder.io/qwik-city';
import { AuthContext } from '~/routes/layout';
export default component$(() => {
const auth = useContext(AuthContext);
const nav = useNavigate();
const error = useSignal<string | null>(null);
useVisibleTask$(async () => {
try {
await auth.handleCallback();
nav('/dashboard');
} catch (err) {
error.value = (err as Error).message;
}
});
return (
<div>
{error.value ? (
<div class="error">
<h2>Authentication Failed</h2>
<p>{error.value}</p>
<a href="/">Go back home</a>
</div>
) : (
<div>Completing login...</div>
)}
</div>
);
});Vanilla JavaScript
HTML Page
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My App</title>
<style>
.hidden { display: none; }
.user-menu { display: flex; align-items: center; gap: 10px; }
.user-menu img { width: 40px; height: 40px; border-radius: 50%; }
.username { color: #666; }
</style>
</head>
<body>
<header>
<h1>My App</h1>
<div id="auth-container">
<button id="login-btn" class="hidden">Sign in with Flowsta</button>
<div id="user-menu" class="hidden user-menu">
<img id="user-avatar" src="" alt="">
<span id="user-name"></span>
<span id="user-username" class="username"></span>
<button id="logout-btn">Logout</button>
</div>
</div>
</header>
<main id="content">
<p>Welcome! Please sign in to continue.</p>
</main>
<script type="module">
import { FlowstaAuth } from '@flowsta/auth';
const auth = new FlowstaAuth({
clientId: 'your_client_id',
redirectUri: window.location.origin + '/auth/callback'
});
// DOM elements
const loginBtn = document.getElementById('login-btn');
const userMenu = document.getElementById('user-menu');
const logoutBtn = document.getElementById('logout-btn');
const userAvatar = document.getElementById('user-avatar');
const userName = document.getElementById('user-name');
const userUsername = document.getElementById('user-username');
// Handle callback if on callback page
if (window.location.pathname === '/auth/callback') {
auth.handleCallback()
.then(user => {
console.log('Logged in:', user);
window.location.href = '/';
})
.catch(err => {
console.error('Auth failed:', err);
alert('Login failed: ' + err.message);
window.location.href = '/';
});
}
// Update UI based on auth state
function updateUI() {
if (auth.isAuthenticated()) {
const user = auth.getUser();
loginBtn.classList.add('hidden');
userMenu.classList.remove('hidden');
userAvatar.src = user.profilePicture || '';
userAvatar.alt = user.displayName || '';
userName.textContent = user.displayName || '';
userUsername.textContent = user.username ? `@${user.username}` : '';
} else {
loginBtn.classList.remove('hidden');
userMenu.classList.add('hidden');
}
}
// Event listeners
loginBtn.addEventListener('click', () => auth.login());
logoutBtn.addEventListener('click', () => {
auth.logout();
updateUI();
});
// Initialize
updateUI();
</script>
</body>
</html>Node.js Backend
Express Server with Token Verification
javascript
// server.js
import express from 'express';
import jwt from 'jsonwebtoken';
const app = express();
app.use(express.json());
// Middleware to verify Flowsta access tokens
async function verifyFlowstaToken(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'No token provided' });
}
const token = authHeader.split(' ')[1];
try {
// Verify token with Flowsta
const response = await fetch('https://auth-api.flowsta.com/oauth/userinfo', {
headers: { 'Authorization': `Bearer ${token}` }
});
if (!response.ok) {
return res.status(401).json({ error: 'Invalid token' });
}
const user = await response.json();
req.user = user;
next();
} catch (error) {
return res.status(401).json({ error: 'Token verification failed' });
}
}
// Public endpoint
app.get('/api/public', (req, res) => {
res.json({ message: 'This is public' });
});
// Protected endpoint
app.get('/api/profile', verifyFlowstaToken, (req, res) => {
res.json({
message: 'This is protected',
user: {
id: req.user.sub,
name: req.user.name,
username: req.user.preferred_username,
email: req.user.email,
did: req.user.did
}
});
});
// Protected endpoint - only users with username
app.get('/api/premium', verifyFlowstaToken, (req, res) => {
if (!req.user.preferred_username) {
return res.status(403).json({
error: 'Username required',
message: 'Please set a username in your Flowsta profile'
});
}
res.json({
message: 'Welcome premium user!',
username: req.user.preferred_username
});
});
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});Making Authenticated API Calls (Frontend)
typescript
// api.ts
import { FlowstaAuth } from '@flowsta/auth';
const auth = new FlowstaAuth({
clientId: 'your_client_id',
redirectUri: window.location.origin + '/auth/callback'
});
async function fetchWithAuth(url: string, options: RequestInit = {}) {
const token = auth.getAccessToken();
if (!token) {
throw new Error('Not authenticated');
}
const response = await fetch(url, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
if (response.status === 401) {
// Token expired - redirect to login
auth.logout();
auth.login();
throw new Error('Session expired');
}
return response;
}
// Usage
async function getProfile() {
const response = await fetchWithAuth('/api/profile');
return response.json();
}
async function updateSettings(settings: object) {
const response = await fetchWithAuth('/api/settings', {
method: 'POST',
body: JSON.stringify(settings)
});
return response.json();
}Next Steps
- SDK Documentation - Full SDK reference
- OAuth Guide - Complete OAuth flow documentation
- Security Best Practices - Secure your integration
- API Reference - Backend API endpoints