Captive portals offer multiple ways for users to authenticate before accessing WiFi. Each method has different trade-offs in terms of user experience, data collection, security, and implementation complexity. Understanding these options helps administrators choose the right authentication strategy for their venue.
Overview of Authentication Methods
Method 1: Email Authentication
How It Works
-
User enters email on captive portal login page
-
Portal validates email format (regex check:
^[a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$) -
Portal checks database for existing email
-
If new: INSERT new user record
-
If existing: Update last login timestamp
-
Create session with user ID + MAC + IP + expiration
-
Update firewall ACL to grant internet access
Database Schema
CREATE TABLE users (
user_id INT PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(100) UNIQUE NOT NULL,
mac_address VARCHAR(17),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_login TIMESTAMP
);
CREATE TABLE sessions (
session_id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
mac_address VARCHAR(17),
ip_address VARCHAR(15),
start_time TIMESTAMP,
expiration TIMESTAMP,
status ENUM('active', 'expired')
);
User Experience
┌─────────────────────────────────┐
│ Welcome to CafeWiFi │
│ │
│ Enter your email to connect: │
│ ┌──────────────────────────┐ │
│ │ john@email.com │ │
│ └──────────────────────────┘ │
│ │
│ [Connect Button] │
│ │
│ Or login with: │
│ [Facebook] [Google] │
└─────────────────────────────────┘Steps:
-
User opens browser → Sees login page
-
Types email → Clicks “Connect”
-
Gets instant access (no email verification)
-
Pro: Fast, simple
-
Con: No verification (fake emails possible)
Pros & Cons
Implementation (PHP Example)
<?php
// Captive Portal Login Handler
$email = $_POST['email'];
// Validate email format
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
echo "Invalid email format";
exit;
}
// Check database for existing user
$stmt = $pdo->prepare("SELECT user_id FROM users WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch();
if (!$user) {
// New user: INSERT into database
$stmt = $pdo->prepare("INSERT INTO users (email, mac_address) VALUES (?, ?)");
$stmt->execute([$email, $_SERVER['MAC_ADDRESS']]);
$user_id = $pdo->lastInsertId();
} else {
// Existing user: Update last_login
$user_id = $user['user_id'];
$stmt = $pdo->prepare("UPDATE users SET last_login = NOW() WHERE user_id = ?");
$stmt->execute([$user_id]);
}
// Create session
$expiration = date('Y-m-d H:i:s', strtotime('+1 hour'));
$stmt = $pdo->prepare("INSERT INTO sessions (user_id, mac_address, ip_address, start_time, expiration) VALUES (?, ?, ?, NOW(), ?)");
$stmt->execute([$user_id, $_SERVER['MAC_ADDRESS'], $_SERVER['IP_ADDRESS'], $expiration]);
// Grant internet access
grant_internet_access($_SERVER['MAC_ADDRESS']);
echo "Login successful! You have 1 hour of access.";
?>
Best Use Cases
-
Cafes & coffee shops: Collect emails for newsletters
-
Retail stores: Build customer database for marketing
-
Small venues: Simple setup, no complex integration needed
Method 2: SMS Authentication
How It Works
-
User enters phone number on captive portal
-
Portal generates 6-digit code (random:
123456) -
Portal sends SMS via API (e.g., Twilio, AWS SNS)
-
User receives SMS on phone
-
User enters code on portal
-
Portal validates code against database
-
If valid: Create session → Grant access
-
If invalid: Show error → Retry
Database Schema
CREATE TABLE sms_verifications (
verification_id INT PRIMARY KEY AUTO_INCREMENT,
phone_number VARCHAR(15) NOT NULL,
code VARCHAR(6) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP,
verified BOOLEAN DEFAULT FALSE
);User Experience
┌─────────────────────────────────┐
│ Welcome to CafeWiFi │
│ │
│ Enter your phone number: │
│ ┌──────────────────────────┐ │
│ │ +254712345678 │ │
│ └──────────────────────────┘ │
│ │
│ [Send Code Button] │
│ │
│ ─────────────────────────────── │
│ │
│ Enter the code you received: │
│ ┌──────────────────────────┐ │
│ │ 123456 │ │
│ └──────────────────────────┘ │
│ │
│ [Verify Button] │
└─────────────────────────────────┘Steps:
-
User types phone → Clicks “Send Code”
-
Gets SMS: “Your CafeWiFi code is 123456”
-
Enters code → Clicks “Verify”
-
Gets instant access
-
Pro: High security (2FA)
-
Con: Slower (waiting for SMS)
SMS Flow Diagram
User Device → Captive Portal → Enter Phone → POST /send-code
Captive Portal → SMS API (Twilio) → POST phone + code
SMS API → Mobile Network → User Phone receives SMS
User Phone → User reads code → Enters on portal → POST /verify
Captive Portal → Database Check → Code valid? → YES
Captive Portal → Create Session → Grant internet accessSMS API Integration (Twilio Example)
from twilio.rest import Client
# Twilio credentials
client = Client("AC1234567890abcdef1234567890abcdef", "your_auth_token")
def send_verification_code(phone_number):
# Generate 6-digit code
code = str(random.randint(100000, 999999))
# Store code in database (expires in 5 minutes)
expires_at = datetime.now() + timedelta(minutes=5)
db.insert("sms_verifications", phone_number=phone_number, code=code, expires_at=expires_at)
# Send SMS via Twilio
message = client.messages.create(
body=f"Your CafeWiFi code is {code}",
from_="+1234567890", # Twilio number
to=phone_number
)
return message.sid
Verification Handler (Python Example)
def verify_code(phone_number, code):
# Check database for verification record
record = db.query("SELECT * FROM sms_verifications WHERE phone_number = ? AND code = ?", phone_number, code)
if not record:
return False, "Invalid code"
if record.expires_at < datetime.now():
return False, "Code expired"
if record.verified:
return False, "Code already used"
# Mark as verified
db.update("sms_verifications", verification_id=record.id, verified=True)
# Create session and grant access
create_session(phone_number=phone_number)
grant_internet_access(mac_address)
return True, "Success"
Pros & Cons
Best Use Cases
-
High-security venues: Airports, corporate offices
-
Payment-based WiFi: Verify phone before charging
-
Marketing campaigns: SMS promotions to verified users
Method 3: Social Login (Facebook, Google, LinkedIn)
How It Works (OAuth 2.0)
-
User clicks “Login with Facebook” on captive portal
-
Portal redirects to Facebook OAuth (
https://www.facebook.com/dialog/oauth) -
User authenticates with Facebook (if not already logged in)
-
Facebook returns access token to portal (
?access_token=abc123) -
Portal exchanges token for user data via Facebook API
-
Facebook API returns: Name, email, location, profile picture, interests
-
Portal stores data in database
-
Create session → Grant internet access
OAuth 2.0 Flow Diagram
User Device → Captive Portal → Click "Login with Facebook"
Captive Portal → Redirect to Facebook OAuth → https://facebook.com/dialog/oauth?client_id=...
Facebook → User logs in (if needed) → Ask permission: "Allow CafeWiFi to access your name, email, location?"
User → Click "Allow" → Facebook redirects back to portal
Facebook →Portal → ?access_token=abc123
Portal → Exchange token for user data → Facebook API GET /user?access_token=abc123
Facebook API → Returns: {name: "John Doe", email: "john@email.com", location: "Nairobi", picture: "..."}
Portal → Store in database → Create session → Grant internet accessDatabase Schema
CREATE TABLE social_users (
user_id INT PRIMARY KEY AUTO_INCREMENT,
social_provider VARCHAR(20) NOT NULL, -- 'facebook', 'google', 'linkedin'
social_id VARCHAR(50) UNIQUE NOT NULL, -- Facebook user ID
name VARCHAR(100),
email VARCHAR(100),
location VARCHAR(100),
profile_picture VARCHAR(200),
interests TEXT, -- JSON array: ["travel", "food", "technology"]
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);User Experience
┌─────────────────────────────────┐
│ Welcome to CafeWiFi │
│ │
│ Login to connect: │
│ │
│ [Login with Email] │
│ │
│ Or use social: │
│ [Facebook Logo] Login with Facebook │
│ [Google Logo] Login with Google │
│ [LinkedIn Logo] Login with LinkedIn │
│ │
│ By logging in, you agree to │
│ our Terms & Conditions. │
└─────────────────────────────────┘Steps:
-
User clicks “Login with Facebook”
-
Redirects to Facebook → Authorizes access
-
Returns to portal with data
-
Gets instant access (no typing)
-
Pro: Fastest (one-click)
-
Con: Privacy concerns (some users don’t want social login)
Facebook OAuth Integration (Python Example)
import requests
def facebook_login(access_token):
# Exchange token for user data
url = "https://graph.facebook.com/v18.0/me"
params = {
"fields": "id,name,email,location,picture,interests",
"access_token": access_token
}
response = requests.get(url, params=params)
user_data = response.json()
# Extract data
social_id = user_data['id']
name = user_data['name']
email = user_data['email']
location = user_data['location']['name']
picture = user_data['picture']['data']['url']
interests = user_data.get('interests', [])
# Store in database
db.insert("social_users",
social_provider="facebook",
social_id=social_id,
name=name,
email=email,
location=location,
profile_picture=picture,
interests=json.dumps(interests))
# Create session
create_session(user_id)
return True
Google OAuth Integration (Python Example)
def google_login(access_token):
# Exchange token for user data
url = "https://www.googleapis.com/oauth2/v2/userinfo"
headers = {"Authorization": f"Bearer {access_token}"}
response = requests.get(url, headers=headers)
user_data = response.json()
# Extract data
social_id = user_data['id']
name = user_data['name']
email = user_data['email']
picture = user_data['picture']
# Store in database
db.insert("social_users",
social_provider="google",
social_id=social_id,
name=name,
email=email,
profile_picture=picture)
# Create session
create_session(user_id)
return True
Pros & Cons
Data Collection Comparison
Best Use Cases
-
Marketing-focused venues: Collect rich data for campaigns
-
Cafes & restaurants: Social sharing drives engagement
-
Hotels: Build customer profiles for personalized service
-
Retail stores: Facebook Pixel tracks visitors for ad targeting
Method Comparison Table
Choosing the Right Method
For Marketing & CRM
-
Best: Social Login (Facebook/Google)
-
Why: Collect rich data (name, email, location, interests) for personalized campaigns
For Security & Verification
-
Best: SMS Authentication
-
Why: 2FA verification ensures real user, reduces fake accounts
For Simplicity & Speed
-
Best: Email Authentication
-
Why: Fastest setup, easiest for users (just type email)
For Paid WiFi
-
Best: Password/Voucher
-
Why: Pre-generated codes, no user data needed, secure
For Maximum Data Collection
-
Best: Social Login (Facebook)
-
Why: Most data points (name, email, location, interests, profile picture, friends)
Multi-Method Authentication
Most modern captive portals support multiple methods simultaneously:
┌─────────────────────────────────┐
│ Welcome to CafeWiFi │
│ │
│ Choose your login method: │
│ │
│ [Email Input] [Connect] │
│ │
│ Or use social: │
│ [Facebook] [Google] [LinkedIn] │
│ │
│ Or enter voucher: │
│ [Voucher Code Input] [Verify] │
│ │
│ Terms & Conditions: [✓ I agree] │
└─────────────────────────────────┘Benefits:
-
Flexibility: Users choose their preferred method
-
Data diversity: Collect emails, phones, and social profiles
-
Higher conversions: More options = more users authenticate
Bottom Line
Captive portal authentication offers email, SMS, and social login as the three primary methods. Email is simplest but has low security. SMS provides high security with 2FA but is slower. Social login (Facebook/Google) is fastest and collects the most data but raises privacy concerns.
Choose based on your goals:
-
Marketing: Social Login (rich data)
-
Security: SMS (2FA verification)
-
Simplicity: Email (fast setup)
-
Paid WiFi: Voucher (pre-generated codes)
Most venues use multi-method authentication to offer flexibility and maximize conversions.