@bagelink/auth
v1.8.73
Published
Bagelink auth package
Readme
@bagelink/auth
Modern authentication library for Bagelink applications with built-in SSO support, automatic redirect handling, and Vue components.
Features
- 🔐 Complete auth flow - Login, signup, password reset, email verification
- 🚀 SSO ready - Google, GitHub, Microsoft, Apple, Okta, Facebook
- 🔄 Auto-redirect - Handles post-login navigation automatically
- 🛡️ Security built-in - Protection against open redirect attacks
- 🎨 Vue components - Pre-built forms and pages
- 📱 Type-safe - Full TypeScript support
- 🧩 Router integration - Smart guards for protected routes
Installation
npm install @bagelink/authQuick Start
1. Initialize Auth
// main.ts
import { createApp } from 'vue'
import { createAuth } from '@bagelink/auth'
import router from './router'
const auth = createAuth({
baseURL: 'https://api.your-app.com',
redirect: {
loginRoute: 'Login',
fallback: '/dashboard',
autoRedirect: true, // Automatic post-login redirect
}
})
// Connect router (automatically sets up auth guard)
auth.use(router)
const app = createApp(App)
app.use(router)
app.use(auth) // Optional: adds $auth globally
app.mount('#app')2. Define Routes
// router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/login',
name: 'Login',
component: () => import('@/views/Login.vue'),
},
{
path: '/dashboard',
name: 'Dashboard',
component: () => import('@/views/Dashboard.vue'),
meta: { auth: true }, // Protected route
},
]
export default createRouter({
history: createWebHistory(),
routes,
})3. Use in Components
<script setup lang="ts">
import { useAuth } from '@bagelink/auth'
const auth = useAuth()
async function handleLogin() {
await auth.login({
email: '[email protected]',
password: 'password'
})
// Auto-redirect happens automatically! 🎉
}
</script>
<template>
<div v-if="auth.user.value">
<p>Welcome, {{ auth.user.value.name }}!</p>
<button @click="auth.logout()">Logout</button>
</div>
</template>Documentation
- REDIRECT_GUIDE.md - Complete redirect configuration guide
- COMPLETE_EXAMPLE.md - Full working application example
- BREAKING_CHANGES.md - Migration guide for breaking changes
Core API
createAuth(config)
Initialize the auth module with redirect configuration:
createAuth({
baseURL: string, // Required: Your API base URL
redirect?: {
loginRoute?: string, // Login route name (default: 'Login')
fallback?: string, // Default redirect after login (default: '/')
queryKey?: string, // Query param for redirect URL (default: 'redirect')
autoRedirect?: boolean, // Auto-redirect after login (default: true)
preserveRedirect?: boolean, // Preserve original URL (default: true)
noAuthRoutes?: string[], // Routes requiring no auth (default: ['Login', 'Signup', ...])
allowedPaths?: RegExp[], // Optional: Restrict allowed redirects for security
}
})useAuth()
Returns auth composable with:
{
// State
user: Ref<User | null>
accountInfo: Ref<AccountInfo | null>
// Authentication
login(credentials): Promise<AuthenticationResponse>
signup(data): Promise<AuthenticationResponse>
logout(): Promise<void>
checkAuth(): Promise<boolean>
// SSO
sso: {
google: { redirect(), callback() }
github: { redirect(), callback() }
microsoft: { redirect(), callback() }
// ... more providers
}
// Password Management
forgotPassword(email): Promise<void>
resetPassword(token, newPassword): Promise<void>
changePassword(current, new): Promise<void>
// Profile
updateProfile(updates): Promise<void>
// Getters
getFullName(): string
getIsLoggedIn(): boolean
getEmail(): string
getRoles(): string[]
}Custom Guard Composition
For multi-tenant apps or advanced permission logic, disable the auto-guard and compose your own:
Option 1: Using composeGuards utility (recommended)
import { composeGuards } from '@bagelink/auth'
// Disable auto-guard
auth.use(router, { guard: false })
// Custom org access guard
const orgAccessGuard = () => async (to, from, next) => {
if (to.meta.requiresOrg) {
const hasAccess = await checkOrgAccess(to.params.orgId)
if (!hasAccess) return next('/no-access')
}
next()
}
// Compose all guards in sequence
router.beforeEach(composeGuards([
auth.routerGuard(), // Auth first
orgAccessGuard(), // Then org check
]))Option 2: Multiple beforeEach calls
auth.use(router, { guard: false })
// Guards run in order they're registered
router.beforeEach(auth.routerGuard())
router.beforeEach(async (to, from, next) => {
// Custom org/tenant check
if (to.meta.requiresOrg && !await checkOrgAccess(to.params.orgId)) {
return next('/no-access')
}
next()
})Option 3: Manual composition
auth.use(router, { guard: false })
router.beforeEach(async (to, from, next) => {
// Run auth guard first
const authPassed = await new Promise<boolean>((resolve) => {
auth.routerGuard()(to, from, (result?: any) => {
if (result !== undefined) {
next(result)
resolve(false)
} else {
resolve(true)
}
})
})
if (!authPassed) return
// Your custom logic
if (to.meta.requiresOrg && !await checkOrgAccess(to.params.orgId)) {
return next('/no-access')
}
next()
})Components
Pre-built Pages
<script setup>
import { LoginPage, SignupPage, ForgotPasswordPage } from '@bagelink/auth'
</script>
<template>
<LoginPage
:texts="{ title: 'Welcome Back' }"
card-width="400px"
/>
</template>Form Components
For custom layouts:
<script setup>
import { LoginForm, SignupForm } from '@bagelink/auth'
</script>
<template>
<div class="custom-layout">
<LoginForm
:texts="{ title: 'Sign In' }"
:show-google="true"
:show-github="true"
/>
</div>
</template>SSO Integration
Quick SSO Login
const { sso } = useAuth()
// Redirect to Google OAuth
await sso.google.redirect({
redirect_uri: `${window.location.origin}/auth/callback`
})SSO Callback Route
// router.ts
{
path: '/auth/callback',
name: 'AuthCallback',
component: () => import('@bagelink/auth').then(m => m.Callback),
}Available Providers
- GitHub
- Microsoft
- Apple
- Okta
SSO Redirect Preservation
When users access a protected route and authenticate via SSO, the original URL is automatically preserved:
// User tries to access /dashboard (protected)
// ↓ Redirected to /login?redirect=/dashboard
// ↓ User clicks "Login with Google"
// ↓ Goes to Google OAuth
// ↓ Returns to /auth/callback
// ↓ Automatically redirected to /dashboard ✅How it works:
- The
redirectquery param is stored insessionStoragebefore SSO redirect - After OAuth callback, it's restored to the URL
- Auto-redirect (or manual fallback) uses it to navigate to the original destination
Manual SSO with redirect:
// On login page with ?redirect=/dashboard
await sso.google.redirect()
// Redirect param is automatically preserved across the OAuth flowAdvanced Configuration
Security: Restrict Redirect Paths
createAuth({
baseURL: 'https://api.example.com',
redirect: {
allowedPaths: [
/^\/dashboard/,
/^\/profile/,
/^\/settings/,
],
// Only these paths can be redirect targets
}
})Disable Auto-Redirect
For custom logic:
createAuth({
redirect: {
autoRedirect: false, // Manual control
}
})
// Then in login handler:
import { performRedirect, getRedirectConfig } from '@bagelink/auth'
await auth.login(credentials)
await performRedirect(router, getRedirectConfig())Custom Route Meta Key
createAuth({
redirect: {
authMetaKey: 'requiresAuth', // Default: 'auth'
}
})
// Then in routes:
{
path: '/dashboard',
meta: { requiresAuth: true } // Custom meta key
}Event System
Listen to auth events:
import { createAuth, AuthState } from '@bagelink/auth'
const auth = createAuth({ ... })
auth.on(AuthState.LOGIN, () => {
console.log('User logged in')
})
auth.on(AuthState.LOGOUT, () => {
console.log('User logged out')
})
auth.on(AuthState.PROFILE_UPDATE, () => {
console.log('Profile updated')
})Available events:
LOGINLOGOUTSIGNUPPASSWORD_RESETPASSWORD_CHANGEPROFILE_UPDATEAUTH_CHECKEMAIL_VERIFIEDSESSION_REFRESH
TypeScript Support
Full type definitions included:
import type {
User,
AccountInfo,
AuthState,
SSOProvider,
RedirectConfig,
LoginTexts,
SignupTexts,
} from '@bagelink/auth'Examples
Protected Route Flow
- User visits
/dashboard(protected) - Not authenticated → redirected to
/login?redirect=/dashboard - User logs in
- Auto-redirected to
/dashboard
Custom Login Page
<script setup lang="ts">
import { ref } from 'vue'
import { useAuth } from '@bagelink/auth'
const auth = useAuth()
const email = ref('')
const password = ref('')
const error = ref('')
async function handleLogin() {
try {
await auth.login({ email: email.value, password: password.value })
// Auto-redirect happens automatically!
} catch (err: any) {
error.value = err.response?.data?.message || 'Login failed'
}
}
</script>
<template>
<form @submit.prevent="handleLogin">
<input v-model="email" type="email" placeholder="Email" />
<input v-model="password" type="password" placeholder="Password" />
<button type="submit">Login</button>
<p v-if="error" class="error">{{ error }}</p>
</form>
</template>Role-Based Access
import { useAuth } from '@bagelink/auth'
const auth = useAuth()
// Check if user has admin role
if (auth.getRoles().includes('admin')) {
// Allow access
}
// Or in route guard
router.beforeEach((to, from, next) => {
if (to.meta.requiresAdmin && !auth.getRoles().includes('admin')) {
next('/unauthorized')
} else {
next()
}
})Migration
See BREAKING_CHANGES.md for migration guide from previous versions.
License
MIT
