Spaces:
Sleeping
Getting Started with SwiftOps Authentication
For Frontend Developers | Estimated Time: 30 minutes
π Welcome!
This guide will help you integrate SwiftOps authentication into your frontend application. By the end, you'll have:
- β Working login/logout functionality
- β User profile display
- β Protected routes
- β Error handling
π Documentation Overview
You have access to three documents:
- This Guide (Getting Started) - Start here (30 min read)
- Quick Reference - For quick lookups later
- Complete API Guide - For deep dives and advanced features
Read them in order: Getting Started β Quick Reference β Complete Guide
π― What You'll Build
Step 1: Login Page
User enters email/password β System returns token β Store token β Redirect to dashboard
Step 2: Dashboard
Check if token exists β Fetch user profile β Display user info
Step 3: Protected Routes
Before showing any page β Check token β If invalid, redirect to login
Step 4: Logout
User clicks logout β Call logout API β Clear token β Redirect to login
π Let's Start!
Prerequisites
- Basic JavaScript/React knowledge
- Fetch API or Axios experience
- Understanding of localStorage
Step 1: Create Auth Service (10 minutes)
Create a file: src/services/auth.js
// auth.js - Your authentication service
const API_BASE = 'https://your-api-domain.com/api/v1';
const TOKEN_KEY = 'swiftops_token';
class AuthService {
// Get token from localStorage
getToken() {
return localStorage.getItem(TOKEN_KEY);
}
// Save token to localStorage
setToken(token) {
localStorage.setItem(TOKEN_KEY, token);
}
// Remove token from localStorage
clearToken() {
localStorage.removeItem(TOKEN_KEY);
}
// Check if user is logged in
isAuthenticated() {
return !!this.getToken();
}
// Get headers for authenticated requests
getHeaders() {
const token = this.getToken();
return {
'Content-Type': 'application/json',
...(token && { 'Authorization': `Bearer ${token}` })
};
}
// Login user
async login(email, password) {
const response = await fetch(`${API_BASE}/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Login failed');
}
const data = await response.json();
this.setToken(data.access_token);
return data.user;
}
// Get current user profile
async getCurrentUser() {
const response = await fetch(`${API_BASE}/auth/me`, {
method: 'GET',
headers: this.getHeaders()
});
if (!response.ok) {
if (response.status === 401) {
// Token expired or invalid
this.clearToken();
}
throw new Error('Failed to fetch user profile');
}
return await response.json();
}
// Logout user
async logout() {
try {
// Call logout endpoint for audit
await fetch(`${API_BASE}/auth/logout`, {
method: 'POST',
headers: this.getHeaders()
});
} catch (error) {
console.error('Logout error:', error);
} finally {
// Always clear token
this.clearToken();
}
}
}
// Export singleton instance
export default new AuthService();
What this does:
- Stores/retrieves JWT token in localStorage
- Provides methods for login, logout, and getting user
- Handles token expiration automatically
Step 2: Create Login Page (15 minutes)
Create a file: src/pages/LoginPage.jsx
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import AuthService from '../services/auth';
function LoginPage() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
const navigate = useNavigate();
const handleSubmit = async (e) => {
e.preventDefault();
setError('');
setLoading(true);
try {
// Call login API
const user = await AuthService.login(email, password);
// Success! Redirect to dashboard
console.log('Logged in as:', user.email);
navigate('/dashboard');
} catch (err) {
// Show error to user
setError(err.message);
setLoading(false);
}
};
return (
<div className="login-container">
<div className="login-box">
<h1>Login to SwiftOps</h1>
{error && (
<div className="error-message">
{error}
</div>
)}
<form onSubmit={handleSubmit}>
<div className="form-group">
<label>Email</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="user@example.com"
required
disabled={loading}
/>
</div>
<div className="form-group">
<label>Password</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Enter your password"
required
disabled={loading}
/>
</div>
<button
type="submit"
className="btn-primary"
disabled={loading}
>
{loading ? 'Logging in...' : 'Login'}
</button>
</form>
<div className="login-footer">
<a href="/forgot-password">Forgot password?</a>
</div>
</div>
</div>
);
}
export default LoginPage;
CSS (optional):
.login-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: #f5f5f5;
}
.login-box {
background: white;
padding: 40px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
width: 100%;
max-width: 400px;
}
.error-message {
background: #fee;
color: #c00;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 600;
}
.form-group input {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
}
.btn-primary {
width: 100%;
padding: 12px;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
}
.btn-primary:hover {
background: #0056b3;
}
.btn-primary:disabled {
background: #ccc;
cursor: not-allowed;
}
.login-footer {
margin-top: 20px;
text-align: center;
}
Step 3: Create Dashboard (5 minutes)
Create a file: src/pages/Dashboard.jsx
import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import AuthService from '../services/auth';
function Dashboard() {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const navigate = useNavigate();
useEffect(() => {
loadUser();
}, []);
const loadUser = async () => {
try {
const userData = await AuthService.getCurrentUser();
setUser(userData);
} catch (err) {
setError(err.message);
// If token invalid, redirect to login
navigate('/login');
} finally {
setLoading(false);
}
};
const handleLogout = async () => {
await AuthService.logout();
navigate('/login');
};
if (loading) {
return <div>Loading...</div>;
}
if (error) {
return <div>Error: {error}</div>;
}
return (
<div className="dashboard">
<header>
<h1>Welcome, {user.name}!</h1>
<button onClick={handleLogout}>Logout</button>
</header>
<div className="user-info">
<h2>Your Profile</h2>
<p><strong>Email:</strong> {user.email}</p>
<p><strong>Role:</strong> {user.role}</p>
<p><strong>Phone:</strong> {user.phone || 'Not set'}</p>
<p><strong>Status:</strong> {user.status}</p>
</div>
</div>
);
}
export default Dashboard;
Step 4: Set Up Routes (5 minutes)
Update your src/App.jsx:
import React from 'react';
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
import LoginPage from './pages/LoginPage';
import Dashboard from './pages/Dashboard';
import AuthService from './services/auth';
// Protected Route component
function ProtectedRoute({ children }) {
if (!AuthService.isAuthenticated()) {
return <Navigate to="/login" replace />;
}
return children;
}
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route
path="/dashboard"
element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
}
/>
<Route path="/" element={<Navigate to="/dashboard" replace />} />
</Routes>
</BrowserRouter>
);
}
export default App;
π You're Done! Test It Out
Test Login
- Start your dev server:
npm start - Go to
http://localhost:3000/login - Enter credentials:
- Email:
user@example.com - Password:
SecurePass123!
- Email:
- Click "Login"
- Should redirect to dashboard
Test Protected Route
- Go to
http://localhost:3000/dashboardwithout logging in - Should redirect to
/login
Test Logout
- On dashboard, click "Logout"
- Should redirect to
/login - Try accessing
/dashboardagain - Should redirect to
/login
π Common Issues & Solutions
Issue: "Login failed"
Solution: Check if API URL is correct. Verify email/password.
Issue: "Failed to fetch user profile"
Solution: Token might be expired. Clear localStorage and login again.
Issue: Infinite redirect loop
Solution: Check if token is being stored correctly in localStorage.
Issue: CORS error
Solution: Backend needs to allow your frontend domain in CORS settings.
π What You Just Built
β
Login System - Users can login with email/password
β
Token Management - JWT token stored in localStorage
β
Protected Routes - Dashboard only accessible when logged in
β
Auto-Logout - Expired tokens handled automatically
β
User Profile - Display current user information
β
Logout - Users can logout
π Next Steps
Phase 2: Profile Management
Add ability to update user profile:
// In Dashboard.jsx
const updateProfile = async () => {
const response = await fetch(`${API_BASE}/auth/me`, {
method: 'PUT',
headers: AuthService.getHeaders(),
body: JSON.stringify({
first_name: 'Jane',
phone: '+254712345678'
})
});
const updatedUser = await response.json();
setUser(updatedUser);
};
Phase 3: Invitation System
Handle invitation links when users are invited:
// InvitationAcceptPage.jsx
const acceptInvitation = async (token, password, name) => {
const response = await fetch(`${API_BASE}/invitations/accept`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
token,
password,
name,
accept_terms: true
})
});
const data = await response.json();
AuthService.setToken(data.access_token);
navigate('/dashboard');
};
Phase 4: Password Reset
Add forgot password and reset password flows.
π Where to Go Next
- Quick Reference - For quick endpoint lookups
- Complete API Guide - For advanced features:
- Password management
- Invitation system
- Error handling patterns
- Security best practices
π¬ Need Help?
Common Questions
Q: How long does the token last?
A: 24 hours. After that, user must login again.
Q: Can I use axios instead of fetch?
A: Yes! Just replace fetch calls with axios. Example:
const response = await axios.post(`${API_BASE}/auth/login`, {
email, password
});
const { access_token, user } = response.data;
Q: How do I show loading spinners?
A: Use the loading state variable like in the examples.
Q: What if I need to call other APIs?
A: Use AuthService.getHeaders() to include the auth token:
fetch(`${API_BASE}/users`, {
headers: AuthService.getHeaders()
});
Q: Mobile app development?
A: Use SecureStore instead of localStorage:
import * as SecureStore from 'expo-secure-store';
await SecureStore.setItemAsync('token', token);
β Checklist
Before moving to production:
- Login page works
- Dashboard shows user info
- Logout works
- Protected routes redirect to login
- Error messages display properly
- Token stored in localStorage
- 401 errors handled (auto-logout)
- Loading states shown
- Password validation on client side
π― Summary
What you learned:
- How to integrate SwiftOps authentication
- Token-based authentication flow
- Protected routes implementation
- Error handling patterns
Time invested: 30-60 minutes
Result: Working auth system
Next steps: Read the Quick Reference for other features like password reset, invitation system, and profile management.
Good luck with your implementation! π
Need detailed information? See AUTH_API_COMPLETE.md
Need quick lookup? See AUTH_API_QUICK_REFERENCE.md