A comprehensive Svelte authentication library with WebAuthn/passkey support, designed for whitelabel applications and Flow app projects.
This package is published to GitHub Packages. You'll need to configure your package manager to use the GitHub registry for @thepia
scoped packages.
Using npm:
# Configure registry for @thepia scope
npm config set @thepia:registry https://npm.pkg.github.com
# Set authentication token (required for GitHub Packages)
npm config set //npm.pkg.github.com/:_authToken YOUR_GITHUB_PERSONAL_ACCESS_TOKEN
# Install the package
npm install @thepia/flows-auth
Using pnpm:
# Configure registry in .npmrc file
echo "@thepia:registry=https://npm.pkg.github.com" >> .npmrc
echo "//npm.pkg.github.com/:_authToken=YOUR_GITHUB_PERSONAL_ACCESS_TOKEN" >> .npmrc
# Install the package
pnpm install @thepia/flows-auth
Using yarn:
# Configure registry
yarn config set @thepia:registry https://npm.pkg.github.com
# Install the package (you'll be prompted for authentication)
yarn add @thepia/flows-auth
Note: You need a GitHub Personal Access Token with
read:packages
scope to install from GitHub Packages. Set theNODE_AUTH_TOKEN
environment variable or configure it in your.npmrc
file.
<script>
import { SignInForm, createAuthStore } from '@thepia/flows-auth';
const authConfig = {
apiBaseUrl: 'https://api.yourapp.com',
clientId: 'your-client-id',
domain: 'yourapp.com',
enablePasskeys: true,
enableMagicLinks: true,
branding: {
companyName: 'Your Company',
logoUrl: '/logo.svg',
primaryColor: '#0066cc'
}
};
const auth = createAuthStore(authConfig);
function handleSuccess({ detail }) {
console.log('Signed in:', detail.user);
}
</script>
<SignInForm
config={authConfig}
on:success={handleSuccess}
/>
The main authentication component that handles the complete sign-in flow.
<SignInForm
{config}
showLogo={true}
compact={false}
initialEmail=""
on:success={handleSuccess}
on:error={handleError}
on:stateChange={handleStateChange}
/>
Props:
config
- Authentication configuration objectshowLogo
- Whether to display the company logocompact
- Use compact layout for modals/small spacesinitialEmail
- Pre-fill email fieldclassName
- Additional CSS classesEvents:
success
- User successfully authenticatederror
- Authentication error occurredstateChange
- Authentication step changedYou can also use individual step components for custom flows:
import {
EmailStep,
PasskeyStep,
PasswordStep,
MagicLinkStep
} from '@thepia/flows-auth/components';
The auth store manages authentication state and provides methods for sign-in/out.
import { createAuthStore } from '@thepia/flows-auth/stores';
const auth = createAuthStore(config);
// Subscribe to auth state
auth.subscribe($auth => {
console.log('Auth state:', $auth.state);
console.log('User:', $auth.user);
});
// Sign in methods
await auth.signIn('[email protected]');
await auth.signInWithPasskey('[email protected]');
await auth.signInWithPassword('[email protected]', 'password');
await auth.signInWithMagicLink('[email protected]');
// Other methods
await auth.signOut();
await auth.refreshTokens();
const isAuthenticated = auth.isAuthenticated();
const token = auth.getAccessToken();
interface AuthConfig {
// Required
apiBaseUrl: string;
clientId: string;
domain: string;
// Feature flags
enablePasskeys: boolean;
enableMagicLinks: boolean;
enablePasswordLogin: boolean;
enableSocialLogin: boolean;
// Optional
redirectUri?: string;
socialProviders?: SocialProvider[];
branding?: AuthBranding;
}
interface AuthBranding {
companyName: string;
logoUrl?: string;
primaryColor?: string;
secondaryColor?: string;
showPoweredBy?: boolean;
customCSS?: string;
}
The library supports complete visual customization through CSS custom properties:
:root {
/* Brand colors */
--auth-primary-color: #your-brand-color;
--auth-secondary-color: #your-secondary-color;
--auth-accent-color: #your-accent-color;
/* Typography */
--auth-font-family: 'Your Brand Font', sans-serif;
--auth-border-radius: 8px;
/* Spacing */
--auth-padding: 24px;
--auth-gap: 16px;
}
Or use the branding configuration:
const config = {
// ... other config
branding: {
companyName: 'ACME Corp',
logoUrl: '/acme-logo.svg',
primaryColor: '#ff0000',
secondaryColor: '#00ff00',
customCSS: `
.auth-form { border-radius: 0; }
.auth-button { text-transform: uppercase; }
`
}
};
The library expects your API to implement these endpoints:
POST /auth/signin
POST /auth/signin/passkey
POST /auth/signin/password
POST /auth/signin/magic-link
POST /auth/webauthn/challenge
POST /auth/refresh
POST /auth/signout
GET /auth/profile
See the API Documentation for detailed request/response formats.
A comprehensive demo application is included in src/demo-app/
that showcases all features of the flows-auth library. The demo includes:
# Navigate to demo app
cd src/demo-app
# Install dependencies
pnpm install
# Start development server
pnpm dev
# Build for production
pnpm build
The demo app is also deployed automatically to GitHub Pages: View Live Demo
<!-- examples/basic/App.svelte -->
<script>
import { SignInForm } from '@thepia/flows-auth';
const config = {
apiBaseUrl: 'https://api.example.com',
clientId: 'demo-client',
domain: 'example.com',
enablePasskeys: true,
enableMagicLinks: true,
branding: {
companyName: 'Demo Company'
}
};
</script>
<SignInForm {config} />
<!-- examples/whitelabel/App.svelte -->
<script>
import { SignInForm } from '@thepia/flows-auth';
const config = {
apiBaseUrl: process.env.VITE_API_URL,
clientId: process.env.VITE_CLIENT_ID,
domain: process.env.VITE_DOMAIN,
enablePasskeys: true,
branding: {
companyName: process.env.VITE_COMPANY_NAME,
logoUrl: process.env.VITE_LOGO_URL,
primaryColor: process.env.VITE_PRIMARY_COLOR,
customCSS: `
.auth-form {
--auth-border-radius: 0;
--auth-shadow: none;
border: 2px solid var(--auth-primary-color);
}
`
}
};
</script>
<SignInForm {config} compact />
The library includes comprehensive test utilities:
import { render, fireEvent } from '@testing-library/svelte';
import { createMockAuthConfig } from '@thepia/flows-auth/testing';
import SignInForm from '@thepia/flows-auth';
test('sign in flow', async () => {
const config = createMockAuthConfig();
const { getByLabelText, getByText } = render(SignInForm, { config });
await fireEvent.input(getByLabelText('Email'), {
target: { value: '[email protected]' }
});
await fireEvent.click(getByText('Continue'));
// Assert passkey step appears
expect(getByText('Use your passkey')).toBeInTheDocument();
});
If you're migrating from the React version:
on:event
syntax instead of onEvent
propsSee the Migration Guide for detailed instructions.
# Install dependencies
npm install
# Run tests
npm test
# Build library
npm run build
# Run examples
npm run example:basic
npm run example:whitelabel
MIT License - see LICENSE file for details.