Custom Login Page for MCP OAuth
This guide explains how to create a custom login page layer for DreamFactory's MCP (Model Context Protocol) OAuth authentication flow. By following this guide, you can build a branded, self-hosted login experience while maintaining full OAuth 2.0 security.
Overview
What This Achieves
A custom login page layer allows you to:
- Brand the login experience with your company's look and feel
- Host authentication UI on your domain (e.g.,
login.yourcompany.com) - Maintain OAuth 2.0 + PKCE security through DreamFactory
- Support both credential-based and social login flows
How It Works
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Your Client │────>│ DreamFactory │────>│ Custom Login │
│ Application │ │ MCP Service │ │ Page (this) │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │ │
│ 1. OAuth Request │ │
│──────────────────────>│ │
│ │ 2. Redirect to │
│ │ custom login │
│ │───────────────────────>│
│ │ │
│ │ 3. User submits │
│ │ credentials │
│ │<───────────────────────│
│ │ │
│ 4. Authorization │ │
│ code returned │ │
│<──────────────────────│ │
│ │ │
│ 5. Exchange code │ │
│ for tokens │ │
│──────────────────────>│ │
│ │ │
│ 6. Access granted │ │
│<──────────────────────│ │
Prerequisites
- DreamFactory instance with MCP services configured
- Web server capable of serving static HTML (any will work)
- HTTPS certificate (required for production)
Step-by-Step Implementation
Step 1: Create the HTML Structure
Your login page needs:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login</title>
</head>
<body>
<!-- Login Form -->
<form id="loginForm" method="POST">
<!-- Hidden OAuth fields (populated by JavaScript) -->
<input type="hidden" name="client_id" id="client_id">
<input type="hidden" name="redirect_uri" id="redirect_uri">
<input type="hidden" name="state" id="state">
<input type="hidden" name="code_challenge" id="code_challenge">
<input type="hidden" name="code_challenge_method" id="code_challenge_method">
<input type="hidden" name="service" id="service">
<input type="hidden" name="original_state" id="original_state">
<!-- User input fields -->
<input type="email" name="email" required>
<input type="password" name="password" required>
<button type="submit">Sign In</button>
</form>
<!-- Optional: OAuth service buttons container -->
<div id="oauthServices"></div>
<script>
// JavaScript implementation here
</script>
</body>
</html>
Step 2: Extract OAuth Parameters from URL
DreamFactory passes OAuth parameters via query string. Extract them:
// Parse URL parameters
const urlParams = new URLSearchParams(window.location.search);
const oauthParams = {
client_id: urlParams.get('client_id'),
redirect_uri: urlParams.get('redirect_uri'),
state: urlParams.get('state'),
code_challenge: urlParams.get('code_challenge'),
code_challenge_method: urlParams.get('code_challenge_method'),
original_state: urlParams.get('original_state'),
login_url: urlParams.get('login_url'), // CRITICAL: form submission endpoint
service: urlParams.get('service'),
oauth_services: urlParams.get('oauth_services'),
oauth_callback_base: urlParams.get('oauth_callback_base')
};