+
node
+
+
+
nim
riot
mysql
ts
+
ray
protobuf
+
+
+
r
+
+
+
+
+
cypress
+
android
+
ansible
hapi
elm
+
+
+
ember
+
json
clj
+
#
swift
+
emacs
kali
+
+
linux
+
+
swift
kali
$
+
+
svelte
fauna
protobuf
+
c#
sklearn
+
+
redis
mongo
nvim
mvn
+
+
quarkus
windows
jest
xml
bundler
+
elm
axum
+
scipy
marko
+
mysql
yarn
+
+
yarn
influxdb
+
json
c#
oauth
Back to Blog
🔐 Setting Up OAuth2 Authentication: Simple Guide
Alpine Linux Security Beginner

🔐 Setting Up OAuth2 Authentication: Simple Guide

Published Jun 13, 2025

Easy tutorial on implementing OAuth2 authentication in Alpine Linux applications. Perfect for beginners to add secure login with Google, GitHub, and more.

9 min read
0 views
Table of Contents

Let me show you how to set up OAuth2 authentication on Alpine Linux! OAuth2 lets users log in to your app using their existing accounts from Google, GitHub, or other providers. It’s like having a universal key that works across many doors!

🤔 What is OAuth2?

OAuth2 is a secure way for users to log in without sharing passwords with your app. Instead of creating yet another username and password, users can click “Login with Google” and they’re in! It’s safer because your app never sees their actual password.

Why use OAuth2?

  • No password storage needed
  • Users trust big providers
  • Easier user onboarding
  • Better security
  • Social features access

🎯 What You Need

Before starting, you’ll need:

  • Alpine Linux installed
  • Web server running
  • Developer accounts (Google/GitHub)
  • Basic programming knowledge
  • About 30 minutes

📋 Step 1: Install Required Tools

Let’s get the tools we need:

# Update packages
apk update

# Install Node.js for our example app
apk add nodejs npm

# Install Python (alternative option)
apk add python3 py3-pip

# Install development tools
apk add git curl openssl

# Install Redis for sessions
apk add redis
rc-service redis start
rc-update add redis

# Create project directory
mkdir -p /var/www/oauth2-app
cd /var/www/oauth2-app

📋 Step 2: Set Up OAuth2 with Google

First, let’s configure Google OAuth2:

# 1. Go to Google Cloud Console
# https://console.cloud.google.com

# 2. Create new project or select existing

# 3. Enable Google+ API

# 4. Create OAuth2 credentials
# - Application type: Web application
# - Authorized redirect URIs: http://localhost:3000/auth/google/callback

# 5. Save your credentials
cat > .env << 'EOF'
# Google OAuth2 Credentials
GOOGLE_CLIENT_ID=your-client-id-here
GOOGLE_CLIENT_SECRET=your-client-secret-here
GOOGLE_CALLBACK_URL=http://localhost:3000/auth/google/callback
SESSION_SECRET=your-random-session-secret
EOF

# Protect credentials
chmod 600 .env

📋 Step 3: Create OAuth2 Application

Build a simple OAuth2 app:

# Initialize Node.js project
npm init -y

# Install dependencies
npm install express express-session passport passport-google-oauth20
npm install dotenv ejs

# Create main application file
cat > app.js << 'EOF'
const express = require('express');
const session = require('express-session');
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
require('dotenv').config();

const app = express();

// Session setup
app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: true,
  cookie: { secure: false } // Set true with HTTPS
}));

// Passport setup
app.use(passport.initialize());
app.use(passport.session());

// View engine
app.set('view engine', 'ejs');

// Passport serialization
passport.serializeUser((user, done) => {
  done(null, user);
});

passport.deserializeUser((user, done) => {
  done(null, user);
});

// Google OAuth2 strategy
passport.use(new GoogleStrategy({
    clientID: process.env.GOOGLE_CLIENT_ID,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    callbackURL: process.env.GOOGLE_CALLBACK_URL
  },
  (accessToken, refreshToken, profile, done) => {
    // In production, save user to database
    const user = {
      id: profile.id,
      name: profile.displayName,
      email: profile.emails[0].value,
      photo: profile.photos[0].value
    };
    return done(null, user);
  }
));

// Routes
app.get('/', (req, res) => {
  res.render('index', { user: req.user });
});

app.get('/auth/google',
  passport.authenticate('google', { scope: ['profile', 'email'] })
);

app.get('/auth/google/callback',
  passport.authenticate('google', { failureRedirect: '/' }),
  (req, res) => {
    res.redirect('/dashboard');
  }
);

app.get('/dashboard', ensureAuth, (req, res) => {
  res.render('dashboard', { user: req.user });
});

app.get('/logout', (req, res) => {
  req.logout(() => {
    res.redirect('/');
  });
});

// Middleware
function ensureAuth(req, res, next) {
  if (req.isAuthenticated()) {
    return next();
  }
  res.redirect('/');
}

// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});
EOF

# Create views directory
mkdir views

# Create index page
cat > views/index.ejs << 'EOF'
<!DOCTYPE html>
<html>
<head>
  <title>OAuth2 Demo</title>
  <style>
    body { font-family: Arial; margin: 50px; text-align: center; }
    .login-btn { 
      background: #4285f4; 
      color: white; 
      padding: 10px 20px; 
      text-decoration: none; 
      border-radius: 5px; 
      display: inline-block;
      margin: 10px;
    }
    .user-info { margin: 20px 0; }
    img { border-radius: 50%; }
  </style>
</head>
<body>
  <h1>OAuth2 Authentication Demo</h1>
  
  <% if (!user) { %>
    <p>Please log in to continue</p>
    <a href="/auth/google" class="login-btn">Login with Google</a>
  <% } else { %>
    <div class="user-info">
      <img src="<%= user.photo %>" width="100" alt="Profile">
      <h2>Welcome, <%= user.name %>!</h2>
      <p>Email: <%= user.email %></p>
      <a href="/dashboard">Go to Dashboard</a> |
      <a href="/logout">Logout</a>
    </div>
  <% } %>
</body>
</html>
EOF

# Create dashboard page
cat > views/dashboard.ejs << 'EOF'
<!DOCTYPE html>
<html>
<head>
  <title>Dashboard</title>
  <style>
    body { font-family: Arial; margin: 50px; }
    .container { max-width: 800px; margin: 0 auto; }
    .user-card { 
      background: #f0f0f0; 
      padding: 20px; 
      border-radius: 10px; 
      margin: 20px 0;
    }
  </style>
</head>
<body>
  <div class="container">
    <h1>User Dashboard</h1>
    
    <div class="user-card">
      <h2>Profile Information</h2>
      <p><strong>Name:</strong> <%= user.name %></p>
      <p><strong>Email:</strong> <%= user.email %></p>
      <p><strong>ID:</strong> <%= user.id %></p>
    </div>
    
    <p>This is a protected page. Only authenticated users can see this!</p>
    
    <a href="/">Home</a> | <a href="/logout">Logout</a>
  </div>
</body>
</html>
EOF

📋 Step 4: Add GitHub OAuth2

Support multiple providers:

# Install GitHub strategy
npm install passport-github2

# Update .env with GitHub credentials
cat >> .env << 'EOF'

# GitHub OAuth2 Credentials
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
GITHUB_CALLBACK_URL=http://localhost:3000/auth/github/callback
EOF

# Add GitHub strategy to app.js
cat >> github-auth.js << 'EOF'
// Add this to your app.js file

const GitHubStrategy = require('passport-github2').Strategy;

// GitHub OAuth2 strategy
passport.use(new GitHubStrategy({
    clientID: process.env.GITHUB_CLIENT_ID,
    clientSecret: process.env.GITHUB_CLIENT_SECRET,
    callbackURL: process.env.GITHUB_CALLBACK_URL
  },
  (accessToken, refreshToken, profile, done) => {
    const user = {
      id: profile.id,
      name: profile.displayName || profile.username,
      email: profile.emails?.[0]?.value || 'No public email',
      photo: profile.photos[0].value,
      provider: 'github'
    };
    return done(null, user);
  }
));

// GitHub routes
app.get('/auth/github',
  passport.authenticate('github', { scope: ['user:email'] })
);

app.get('/auth/github/callback',
  passport.authenticate('github', { failureRedirect: '/' }),
  (req, res) => {
    res.redirect('/dashboard');
  }
);
EOF

# Update index.ejs to add GitHub button
# Add after Google button:
# <a href="/auth/github" class="login-btn" style="background: #333;">Login with GitHub</a>

📋 Step 5: Secure Your OAuth2 Setup

Implement security best practices:

# Create security middleware
cat > middleware/security.js << 'EOF'
// Security headers
exports.securityHeaders = (req, res, next) => {
  res.setHeader('X-Content-Type-Options', 'nosniff');
  res.setHeader('X-Frame-Options', 'DENY');
  res.setHeader('X-XSS-Protection', '1; mode=block');
  res.setHeader('Strict-Transport-Security', 'max-age=31536000');
  next();
};

// Rate limiting
const rateLimit = {};
exports.rateLimiter = (req, res, next) => {
  const ip = req.ip;
  const now = Date.now();
  
  if (!rateLimit[ip]) {
    rateLimit[ip] = { count: 1, resetTime: now + 60000 };
  } else if (now > rateLimit[ip].resetTime) {
    rateLimit[ip] = { count: 1, resetTime: now + 60000 };
  } else {
    rateLimit[ip].count++;
    if (rateLimit[ip].count > 10) {
      return res.status(429).send('Too many requests');
    }
  }
  next();
};

// CSRF protection
exports.csrfCheck = (req, res, next) => {
  if (req.method === 'POST') {
    const token = req.session.csrfToken;
    const submitted = req.body._csrf || req.headers['x-csrf-token'];
    
    if (!token || token !== submitted) {
      return res.status(403).send('CSRF token mismatch');
    }
  }
  next();
};
EOF

# Add HTTPS support
cat > setup-https.sh << 'EOF'
#!/bin/sh
# Generate self-signed certificate for testing
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes \
  -subj "/C=US/ST=State/L=City/O=Organization/CN=localhost"

# Update app to use HTTPS
echo "
const https = require('https');
const fs = require('fs');

const options = {
  key: fs.readFileSync('key.pem'),
  cert: fs.readFileSync('cert.pem')
};

https.createServer(options, app).listen(3443, () => {
  console.log('HTTPS Server running on https://localhost:3443');
});" >> app.js
EOF

chmod +x setup-https.sh

📋 Step 6: Handle OAuth2 Tokens

Manage access tokens properly:

# Create token manager
cat > utils/tokenManager.js << 'EOF'
const crypto = require('crypto');

class TokenManager {
  constructor() {
    this.tokens = new Map();
  }

  // Encrypt token
  encrypt(text) {
    const algorithm = 'aes-256-gcm';
    const key = crypto.scryptSync(process.env.SESSION_SECRET, 'salt', 32);
    const iv = crypto.randomBytes(16);
    const cipher = crypto.createCipheriv(algorithm, key, iv);
    
    let encrypted = cipher.update(text, 'utf8', 'hex');
    encrypted += cipher.final('hex');
    
    const authTag = cipher.getAuthTag();
    
    return {
      encrypted,
      authTag: authTag.toString('hex'),
      iv: iv.toString('hex')
    };
  }

  // Store token securely
  storeToken(userId, provider, accessToken, refreshToken) {
    const encrypted = this.encrypt(accessToken);
    
    this.tokens.set(`${userId}-${provider}`, {
      accessToken: encrypted,
      refreshToken: refreshToken ? this.encrypt(refreshToken) : null,
      expiresAt: Date.now() + 3600000 // 1 hour
    });
  }

  // Get token
  getToken(userId, provider) {
    const tokenData = this.tokens.get(`${userId}-${provider}`);
    
    if (!tokenData || Date.now() > tokenData.expiresAt) {
      return null;
    }
    
    return tokenData;
  }

  // Refresh token
  async refreshToken(userId, provider, refreshToken) {
    // Implement token refresh logic here
    // This depends on the OAuth2 provider
  }

  // Clean expired tokens
  cleanupTokens() {
    const now = Date.now();
    for (const [key, value] of this.tokens.entries()) {
      if (now > value.expiresAt) {
        this.tokens.delete(key);
      }
    }
  }
}

module.exports = new TokenManager();
EOF

# Run cleanup periodically
echo "*/30 * * * * cd /var/www/oauth2-app && node -e 'require(\"./utils/tokenManager\").cleanupTokens()'" | crontab -

📋 Step 7: Test OAuth2 Flow

Verify everything works:

# Start the application
node app.js

# Test endpoints with curl
# Check home page
curl http://localhost:3000/

# Check authentication required
curl -I http://localhost:3000/dashboard
# Should return 302 redirect

# Monitor OAuth2 flow
cat > monitor-oauth.js << 'EOF'
// OAuth2 Flow Monitor
const events = require('events');
const oauth2Monitor = new events.EventEmitter();

oauth2Monitor.on('auth_start', (provider, userId) => {
  console.log(`[${new Date().toISOString()}] Auth started: ${provider} - User: ${userId}`);
});

oauth2Monitor.on('auth_success', (provider, userId) => {
  console.log(`[${new Date().toISOString()}] Auth success: ${provider} - User: ${userId}`);
});

oauth2Monitor.on('auth_failure', (provider, error) => {
  console.log(`[${new Date().toISOString()}] Auth failed: ${provider} - Error: ${error}`);
});

module.exports = oauth2Monitor;
EOF

# Create test script
cat > test-oauth.sh << 'EOF'
#!/bin/sh
echo "🔐 Testing OAuth2 Setup"
echo "====================="

# Check if app is running
if curl -s http://localhost:3000 > /dev/null; then
  echo "✅ App is running"
else
  echo "❌ App is not running"
  exit 1
fi

# Check OAuth2 endpoints
for endpoint in "/auth/google" "/auth/github"; do
  response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000$endpoint)
  if [ "$response" = "302" ]; then
    echo "✅ $endpoint redirects correctly"
  else
    echo "❌ $endpoint not working (HTTP $response)"
  fi
done

echo ""
echo "📍 OAuth2 Providers Status:"
echo "  Google: Configured"
echo "  GitHub: Configured"
echo ""
echo "Visit http://localhost:3000 to test login"
EOF

chmod +x test-oauth.sh
./test-oauth.sh

🎮 Practice Exercise

Extend the OAuth2 implementation:

  1. Add Facebook OAuth2
  2. Implement user roles
  3. Add remember me option
  4. Create API endpoints
# Add Facebook OAuth2
npm install passport-facebook

# Add to .env:
# FACEBOOK_APP_ID=your-app-id
# FACEBOOK_APP_SECRET=your-app-secret
# FACEBOOK_CALLBACK_URL=http://localhost:3000/auth/facebook/callback

# Implement user roles
cat > models/user.js << 'EOF'
const users = new Map();

class User {
  constructor(profile, provider) {
    this.id = `${provider}-${profile.id}`;
    this.providerId = profile.id;
    this.provider = provider;
    this.name = profile.displayName;
    this.email = profile.emails?.[0]?.value;
    this.photo = profile.photos?.[0]?.value;
    this.role = 'user'; // Default role
    this.createdAt = new Date();
    this.lastLogin = new Date();
  }

  static findOrCreate(profile, provider) {
    const id = `${provider}-${profile.id}`;
    let user = users.get(id);
    
    if (!user) {
      user = new User(profile, provider);
      users.set(id, user);
    } else {
      user.lastLogin = new Date();
    }
    
    return user;
  }

  static findById(id) {
    return users.get(id);
  }

  isAdmin() {
    return this.role === 'admin';
  }
}

module.exports = User;
EOF

🚨 Troubleshooting Common Issues

Redirect URI Mismatch

Fix OAuth2 redirect issues:

# Check exact redirect URI in provider console
# Must match exactly including:
# - Protocol (http/https)
# - Domain
# - Port
# - Path

# Common fixes:
# 1. Update provider settings
# 2. Check .env file
# 3. Verify callback routes

Session Not Persisting

Fix session problems:

# Use Redis for sessions
npm install connect-redis redis

# Update session config:
const RedisStore = require('connect-redis')(session);
const redis = require('redis');
const redisClient = redis.createClient();

app.use(session({
  store: new RedisStore({ client: redisClient }),
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: { 
    secure: true, // With HTTPS
    httpOnly: true,
    maxAge: 86400000 // 24 hours
  }
}));

Provider Errors

Debug provider-specific issues:

# Enable debug mode
DEBUG=passport:* node app.js

# Log all OAuth2 events
passport.use(new GoogleStrategy({
    // ... config
  },
  async (accessToken, refreshToken, profile, done) => {
    try {
      console.log('Profile received:', profile);
      // Process user
    } catch (error) {
      console.error('OAuth2 error:', error);
      done(error);
    }
  }
));

💡 Pro Tips

Tip 1: Multiple Accounts

Link multiple providers:

# Allow users to link accounts
function linkAccount(userId, provider, profile) {
  const user = User.findById(userId);
  if (user) {
    user.linkedAccounts = user.linkedAccounts || {};
    user.linkedAccounts[provider] = {
      id: profile.id,
      email: profile.emails?.[0]?.value
    };
  }
}

Tip 2: Scope Management

Request only needed permissions:

# Minimal scopes
passport.authenticate('google', { 
  scope: ['profile'] // Just basic profile
});

# Extended scopes
passport.authenticate('google', { 
  scope: ['profile', 'email', 'https://www.googleapis.com/auth/calendar']
});

Tip 3: Token Storage

Store tokens securely:

# Never store in:
# - Cookies (without encryption)
# - LocalStorage
# - URL parameters

# Good options:
# - Server session
# - Encrypted database
# - Secure token service

✅ Best Practices

  1. Always use HTTPS

    # Force HTTPS in production
    app.use((req, res, next) => {
      if (req.header('x-forwarded-proto') !== 'https') {
        res.redirect(`https://${req.header('host')}${req.url}`);
      } else {
        next();
      }
    });
  2. Validate tokens

    // Verify token hasn't expired
    // Check token signature
    // Validate issuer
  3. Implement logout properly

    app.get('/logout', (req, res) => {
      req.logout();
      req.session.destroy();
      res.redirect('/');
    });
  4. Rate limit auth endpoints

    # Prevent brute force
    npm install express-rate-limit
  5. Log authentication events

    // Track login attempts
    // Monitor suspicious activity
    // Alert on anomalies

🏆 What You Learned

Fantastic work! You can now:

  • ✅ Implement OAuth2 authentication
  • ✅ Support multiple providers
  • ✅ Handle tokens securely
  • ✅ Protect routes properly
  • ✅ Debug OAuth2 issues

Your app now has secure social login!

🎯 What’s Next?

Now that you have OAuth2, explore:

  • OpenID Connect (OIDC)
  • SAML authentication
  • Multi-factor authentication
  • JWT token management

Keep building secure apps! 🔐