@spacelr/auth-components
v0.4.2
Published
Spacelr Auth Web Components - login, register, 2FA, social providers, profile management
Downloads
1,325
Maintainers
Readme
@spacelr/auth-components
Drop-in web components for authentication flows — login, registration, 2FA, social providers, password reset, and profile management. Built with Stencil so they work in any framework (React, Angular, Vue, Svelte) or plain HTML.
Install
npm install @spacelr/auth-componentsSetup
Initialize once before using any component:
import { initAuthComponents } from '@spacelr/auth-components';
import { defineCustomElements } from '@spacelr/auth-components/loader';
initAuthComponents({
baseUrl: 'https://api.example.com/api/v1',
clientId: 'your-client-id',
projectId: 'your-project-id',
theme: {
primaryColor: '#3880ff',
borderRadius: '8px',
},
});
defineCustomElements();Bridging tokens to @spacelr/sdk (recommended)
If your app also uses @spacelr/sdk, bind the SDK client once at startup and the auth components will push tokens directly into its TokenManager after every successful login / register / 2FA-verify / OAuth code-exchange. No manual client.setTokens(...) bridge code in your app.
import { setSpacelrClient } from '@spacelr/auth-components';
import { createClient } from '@spacelr/sdk';
const client = createClient({
apiUrl: 'https://api.example.com/api/v1',
clientId: 'your-client-id',
projectId: 'your-project-id',
});
setSpacelrClient(client);After this, the SDK automatically refreshes tokens for the entire session — see the SDK Guide → Token Refresh for details.
If your app doesn't use @spacelr/sdk, skip this step. The components still emit LoginSuccessPayload events and you can wire those manually.
Tests / SSR / microfrontends: call clearSpacelrClient() on teardown to drop the reference, otherwise it persists for the lifetime of the module.
SSR note:
authConfigis a module-level singleton, so multiple concurrent requests share one bound client. Bind it once at server startup, not per-request — callingclearSpacelrClient()mid-request would silently drop the client for every other in-flight request.
Logout side-effect: if a client is bound, auth-logout-button (and any direct authApi.logout(...) call) also invokes client.clearTokens() so the SDK doesn't keep hitting the gateway with an invalidated session.
Components
<auth-login>
Email/password sign-in form with optional links to registration and password reset. Handles 2FA handoff automatically.
<auth-login
card-title="Sign In"
show-forgot-password="true"
show-register-link="true"
></auth-login>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| cardTitle | string | 'Sign In' | Form heading |
| submitLabel | string | 'Sign In' | Button text |
| showForgotPassword | boolean | true | Show forgot password link |
| showRegisterLink | boolean | true | Show create account link |
| emailPlaceholder | string | 'Email address' | Email field placeholder |
| passwordPlaceholder | string | 'Password' | Password field placeholder |
| disabled | boolean | false | Disable form inputs |
| Event | Payload | Description |
|-------|---------|-------------|
| authSuccess | LoginSuccessPayload | Login succeeded (contains user, tokens) |
| authError | AuthErrorPayload | Login failed |
| forgotPasswordClick | void | User clicked forgot password link |
| registerClick | void | User clicked create account link |
<auth-register>
Registration form with email, password, and configurable optional fields (username, first/last/display name). Each non-core field can be set to hidden, optional, or required. Email and password are always required. Password fields include a built-in show/hide toggle (ion-input-password-toggle). Supports email verification flows.
<auth-register
username-field="required"
first-name-field="optional"
last-name-field="optional"
display-name-field="hidden"
show-login-link="true"
></auth-register>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| cardTitle | string | 'Create Account' | Form heading |
| submitLabel | string | 'Create Account' | Button text |
| showLoginLink | boolean | true | Show back to login link |
| usernameField | 'hidden' \| 'optional' \| 'required' | 'required' | Username field visibility |
| firstNameField | 'hidden' \| 'optional' \| 'required' | 'optional' | First name field visibility |
| lastNameField | 'hidden' \| 'optional' \| 'required' | 'optional' | Last name field visibility |
| displayNameField | 'hidden' \| 'optional' \| 'required' | 'optional' | Display name field visibility |
| disabled | boolean | false | Disable form inputs |
| Event | Payload | Description |
|-------|---------|-------------|
| authSuccess | RegisterSuccessPayload | Registration succeeded |
| authError | AuthErrorPayload | Registration failed |
| emailVerificationRequired | EmailVerificationPayload | Email verification needed before login |
| loginClick | void | User clicked back to login link |
Breaking change (since this release): the old showNameFields boolean prop was removed. Replace show-name-fields="false" with first-name-field="hidden" last-name-field="hidden" display-name-field="hidden" (or omit just the ones you want hidden).
<auth-two-factor>
6-digit OTP verification form shown after initial login when 2FA is enabled.
<auth-two-factor
two-factor-token="token-from-login-response"
></auth-two-factor>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| twoFactorToken | string | required | Session token from login response |
| cardTitle | string | 'Verify Your Identity' | Form heading |
| submitLabel | string | 'Verify' | Button text |
| resendStatus | 'idle' \| 'sending' \| 'sent' \| 'error' | 'idle' | Status of resend attempt |
| disabled | boolean | false | Disable form inputs |
| Event | Payload | Description |
|-------|---------|-------------|
| authSuccess | LoginSuccessPayload | 2FA verification succeeded |
| authError | AuthErrorPayload | Verification failed |
| resendRequested | void | User requested code resend |
<auth-social-providers>
Social login buttons (Google, GitHub, Apple, Microsoft). Supports popup and redirect OAuth flows.
<auth-social-providers
ux-mode="popup"
></auth-social-providers>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| uxMode | 'popup' \| 'redirect' | 'popup' | OAuth flow mode |
| redirectUri | string | — | Required when uxMode='redirect' |
| scopes | string | 'openid email profile' | OAuth scopes |
| useNativeFlow | boolean | false | Skip web script loading, use native flow |
| disabled | boolean | false | Disable buttons |
| Event | Payload | Description |
|-------|---------|-------------|
| authSuccess | LoginSuccessPayload | Social login succeeded |
| authError | AuthErrorPayload | Social login failed |
| providersLoaded | { hasProviders: boolean } | Provider configuration loaded |
| googleLoginRequested | void | Google login requested (native flow) |
<auth-forgot-password>
Password reset request form. Sends a reset link to the provided email address.
<auth-forgot-password
show-back-to-login="true"
></auth-forgot-password>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| cardTitle | string | 'Reset Password' | Form heading |
| submitLabel | string | 'Reset Password' | Button text |
| description | string | 'Enter your email...' | Help text |
| emailPlaceholder | string | 'Email address' | Input placeholder |
| showBackToLogin | boolean | true | Show back link |
| disabled | boolean | false | Disable form inputs |
| Event | Payload | Description |
|-------|---------|-------------|
| resetRequestSuccess | ResetRequestSuccessPayload | Reset email sent |
| resetRequestError | AuthErrorPayload | Request failed |
| backToLoginClick | void | User clicked back to login |
<auth-reset-password>
New password form used after clicking the reset link from email. Requires the token from the URL.
<auth-reset-password
token="reset-token-from-url"
></auth-reset-password>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| token | string | '' | Reset token from the email link |
| cardTitle | string | 'Set New Password' | Form heading |
| submitLabel | string | 'Set New Password' | Button text |
| description | string | 'Enter your new password below.' | Help text |
| showBackToLogin | boolean | true | Show back link |
| disabled | boolean | false | Disable form inputs |
| Event | Payload | Description |
|-------|---------|-------------|
| resetPasswordSuccess | ResetPasswordSuccessPayload | Password changed |
| resetPasswordError | AuthErrorPayload | Reset failed |
| backToLoginClick | void | User clicked back to login |
<auth-profile-edit>
Profile editing form for authenticated users. Updates first name, last name, and display name.
<auth-profile-edit
.user=${currentUser}
access-token="jwt-token"
></auth-profile-edit>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| user | AuthUser | required | Current user data to prefill |
| accessToken | string | required | JWT for authenticated API calls |
| cardTitle | string | 'Edit Profile' | Form heading |
| submitLabel | string | 'Save Changes' | Button text |
| disabled | boolean | false | Disable form inputs |
| Event | Payload | Description |
|-------|---------|-------------|
| profileUpdateSuccess | ProfileUpdateSuccessPayload | Profile saved |
| profileUpdateError | AuthErrorPayload | Update failed |
<auth-logout-button>
Logout button with optional confirmation dialog.
<auth-logout-button
confirm-logout="true"
color="danger"
fill="clear"
></auth-logout-button>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| label | string | 'Logout' | Button text |
| accessToken | string | — | Token for logout API call |
| color | string | 'danger' | Ionic color name |
| fill | 'clear' \| 'outline' \| 'solid' | 'clear' | Button style |
| size | 'small' \| 'default' \| 'large' | 'default' | Button size |
| confirmLogout | boolean | false | Show confirmation dialog |
| confirmMessage | string | 'Are you sure...' | Dialog message |
| disabled | boolean | false | Disable button |
| Event | Payload | Description |
|-------|---------|-------------|
| logoutRequested | void | Logout confirmed |
| logoutCancelled | void | User cancelled logout |
<auth-connected-accounts>
Which social providers a signed-in account can sign in with, plus buttons to add or remove one. For an account-settings screen.
<auth-connected-accounts access-token="..."></auth-connected-accounts>The OAuth hop belongs to the host, not this component: obtaining an authorization code differs per app — a popup, or a redirect that leaves and re-enters the page with the app's own routing in between. So the component asks, and takes the code back:
const accounts = document.querySelector('auth-connected-accounts');
accounts.addEventListener('connectRequested', async (event) => {
const { provider } = event.detail;
const code = await yourOAuthFlow(provider); // e.g. <auth-social-providers use-native-flow>
await accounts.completeConnect(provider, code, redirectUri);
});Disconnecting needs no provider round trip and is handled entirely by the component.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| accessToken | string | — | Token of the signed-in user; every call here is authenticated |
| Method | Description |
|--------|-------------|
| completeConnect(provider, code, redirectUri?) | Finish a connection the host started |
| refresh() | Reload the list — after a redirect flow returns, for instance |
| Event | Payload | Description |
|-------|---------|-------------|
| connectRequested | { provider } | Host should run its OAuth flow for this provider |
| providerLinked | { provider } | Provider connected |
| providerUnlinked | { provider } | Provider disconnected |
| authError | AuthErrorPayload | Any failure, including the server refusing to remove the last provider from an account with an unverified email |
A provider that is connected but no longer offered by the project still appears, because it still signs the account in and must stay removable.
Configuration
Theme
Customize appearance via the theme option:
initAuthComponents({
baseUrl: 'https://api.example.com/api/v1',
theme: {
primaryColor: '#6200ee',
errorColor: '#cf6679',
successColor: '#03dac5',
borderRadius: '12px',
fontFamily: '"Inter", sans-serif',
},
});Custom Endpoints
Override default API paths:
initAuthComponents({
baseUrl: 'https://api.example.com/api/v1',
endpoints: {
login: '/auth/login', // default
register: '/auth/register', // default
forgotPassword: '/auth/forgot-password',
resetPassword: '/auth/reset-password',
profile: '/auth/me',
logout: '/auth/logout',
refresh: '/auth/refresh',
},
});Events
All components emit typed CustomEvents. Listen in any framework:
// Vanilla JS
document.querySelector('auth-login').addEventListener('authSuccess', (e) => {
const { user, accessToken } = e.detail;
console.log('Logged in as', user.email);
});
// React (via ref)
loginRef.current.addEventListener('authSuccess', handleLogin);
// Angular
<auth-login (authSuccess)="onLogin($event)"></auth-login>Validation
Built-in Zod schemas are available for custom form implementations:
import { loginSchema, registerSchema, getFirstError } from '@spacelr/auth-components';
const result = loginSchema.safeParse({ email, password });
if (!result.success) {
console.log(getFirstError(result.error));
}License
MIT
