Skip to main content
Glama
Raistlin82

SAP OData to MCP Server

by Raistlin82
login.html21.9 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>SAP MCP Server - Authentication</title> <!-- SAP 72 Font --> <link href="https://fonts.googleapis.com/css2?family=72:wght@300;400;500;600;700&display=swap" rel="stylesheet"> <style> * { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: '72', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, #0f2027 0%, #203a43 50%, #2c5364 100%); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px; } .container { background: white; border-radius: 10px; box-shadow: 0 15px 30px rgba(0, 0, 0, 0.2); padding: 40px; width: 100%; max-width: 450px; } .header { text-align: center; margin-bottom: 30px; } .logo { width: 60px; height: 60px; background: #0070f3; border-radius: 50%; margin: 0 auto 20px; display: flex; align-items: center; justify-content: center; font-size: 24px; color: white; font-weight: bold; } h1 { color: #333; font-size: 24px; margin-bottom: 8px; } .subtitle { color: #666; font-size: 14px; } .form-group { margin-bottom: 20px; } label { display: block; color: #333; font-weight: 500; margin-bottom: 5px; } input[type="text"], input[type="password"] { width: 100%; padding: 12px; border: 1px solid #ddd; border-radius: 5px; font-size: 16px; transition: border-color 0.3s ease; } input[type="text"]:focus, input[type="password"]:focus { outline: none; border-color: #0070f3; box-shadow: 0 0 0 3px rgba(0, 112, 243, 0.1); } .btn { width: 100%; padding: 12px; background: #0070f3; color: white; border: none; border-radius: 5px; font-size: 16px; font-weight: 600; cursor: pointer; transition: background-color 0.3s ease; margin-bottom: 10px; } .btn-secondary { background: #f5f5f5; color: #333; border: 1px solid #ddd; } .btn-secondary:hover { background: #e9ecef; } .auth-method-selection { text-align: center; } .method-info { margin-top: 15px; text-align: center; } .method-info small { color: #666; font-style: italic; } .btn:hover { background: #0051a2; } .btn:disabled { background: #ccc; cursor: not-allowed; } .error-card { background: #f8d7da; border: 1px solid #f5c6cb; border-radius: 8px; padding: 20px; text-align: center; color: #721c24; } .error-card h3 { margin-bottom: 10px; color: #721c24; } .error-card ul { background: rgba(255, 255, 255, 0.7); border-radius: 4px; padding: 10px; margin: 15px 0; } .error-card code { background: rgba(0, 0, 0, 0.1); padding: 2px 4px; border-radius: 3px; font-family: 'Monaco', 'Menlo', monospace; } .retry-btn { background: #dc3545; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; font-weight: 600; margin-top: 10px; } .retry-btn:hover { background: #c82333; } .loading { display: none; align-items: center; justify-content: center; gap: 10px; } .spinner { width: 20px; height: 20px; border: 2px solid #ffffff; border-top: 2px solid transparent; border-radius: 50%; animation: spin 1s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .error { color: #dc3545; font-size: 14px; margin-top: 10px; padding: 10px; background: #f8d7da; border: 1px solid #f5c6cb; border-radius: 5px; display: none; } .success { display: none; text-align: center; } .success-icon { width: 60px; height: 60px; background: #28a745; border-radius: 50%; margin: 0 auto 20px; display: flex; align-items: center; justify-content: center; font-size: 30px; color: white; } .section { background: #f8f9fa; border: 1px solid #e9ecef; border-radius: 5px; padding: 15px; margin: 20px 0; } .section h3 { color: #495057; margin-bottom: 10px; font-size: 16px; } .section p { color: #6c757d; font-size: 14px; line-height: 1.4; margin-bottom: 10px; } .download-btn, .copy-btn { display: inline-block; padding: 8px 16px; background: #28a745; color: white; text-decoration: none; border-radius: 3px; font-size: 14px; margin-right: 10px; border: none; cursor: pointer; } .download-btn:hover, .copy-btn:hover { background: #1e7e34; } .config-text { background: #f1f3f4; padding: 10px; border-radius: 3px; font-family: monospace; font-size: 12px; margin: 10px 0; overflow-x: auto; } .back-btn { background: #6c757d; margin-top: 15px; } .back-btn:hover { background: #545b62; } </style> </head> <body> <div class="container"> <!-- Login Form --> <div id="loginForm"> <div class="header"> <div class="logo">S</div> <h1>SAP MCP Server</h1> <p class="subtitle" id="subtitle">Redirecting to SAP Identity Authentication Service...</p> </div> <!-- Direct SAP IAS Redirect --> <div id="iasRedirectMessage" class="auth-method-selection"> <div style="text-align: center; padding: 20px;"> <div class="loading" style="display: flex; justify-content: center; margin-bottom: 20px;"> <div class="spinner"></div> </div> <p>Redirecting to SAP Identity Authentication Service...</p> <small>You will be redirected to the standard SAP login page in a moment.</small> </div> </div> </div> <!-- Success State --> <div id="successState" class="success"> <div class="success-icon">✓</div> <h2>Authentication Successful!</h2> <p>Welcome back! Your session is active and ready to use.</p> <div id="userInfoSection" class="section"> <h3>👤 Your Session</h3> <p><strong>User:</strong> <span id="userName">Loading...</span></p> <p><strong>Session ID:</strong> <span id="sessionIdDisplay">Loading...</span></p> </div> <div id="adminSection" class="section" style="display: none;"> <h3>🔧 Administration</h3> <p>You have admin privileges.</p> <a href="javascript:openAdminDashboard()" class="download-btn">🛠️ View Admin Dashboard</a> </div> </div> </div> <script> let sessionId = null; let tokenData = null; let currentAuthMethod = 'selection'; // OAuth 2.0 Flow Functions - Direct redirect to SAP IAS async function initiateOAuth2Flow() { console.log('initiateOAuth2Flow called!'); try { console.log('Setting loading state...'); setLoadingState('Redirecting to SAP IAS login page...'); // Use proper callback endpoint (configured in IAS) - force HTTPS for Cloud Foundry const protocol = window.location.host.includes('.cfapps.') || window.location.host.includes('.ondemand.com') ? 'https:' : window.location.protocol; const redirectUri = `${protocol}//${window.location.host}/auth/callback`; const state = generateRandomString(32); // Store state for validation when callback returns sessionStorage.setItem('oauth_state', state); sessionStorage.setItem('oauth_redirect_after_success', window.location.pathname); // Get IAS configuration and redirect directly to SAP IAS (standard OAuth flow) try { console.log('Fetching config from /auth/debug/config...'); const configResponse = await fetch('/auth/debug/config'); console.log('Config response status:', configResponse.status); const config = await configResponse.json(); console.log('Config loaded:', config); if (!config.authorizationEndpoint || !config.clientId) { throw new Error('IAS configuration not available'); } // Direct redirect to SAP IAS authorization endpoint (standard OAuth 2.0) const iasAuthUrl = config.authorizationEndpoint + '?' + new URLSearchParams({ client_id: config.clientId, response_type: 'code', redirect_uri: redirectUri, scope: 'openid profile email groups', state: state }).toString(); console.log('Redirecting to SAP IAS:', iasAuthUrl); console.log('About to set window.location.href...'); // Redirect to standard SAP IAS login page window.location.href = iasAuthUrl; console.log('window.location.href set to:', iasAuthUrl); } catch (configError) { console.error('Could not load IAS configuration:', configError); document.getElementById('loginContainer').innerHTML = ` <div class="error-card"> <h3>🔧 Configuration Error</h3> <p>SAP Identity Authentication Service is not properly configured.</p> <p>Please ensure the following environment variables are set:</p> <ul style="text-align: left; margin: 1rem 0;"> <li><code>SAP_IAS_URL</code> - Your SAP IAS tenant URL</li> <li><code>SAP_IAS_CLIENT_ID</code> - OAuth client ID</li> <li><code>SAP_IAS_CLIENT_SECRET</code> - OAuth client secret</li> </ul> <button onclick="location.reload()" class="retry-btn">🔄 Retry</button> </div> `; } } catch (error) { console.error('OAuth initiation error:', error); showError('Failed to initiate OAuth flow: ' + error.message); } } function generateRandomString(length) { const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let result = ''; for (let i = 0; i < length; i++) { result += charset.charAt(Math.floor(Math.random() * charset.length)); } return result; } // Legacy functions removed - direct SAP IAS redirect only function setLoadingState(message) { const loginForm = document.getElementById('loginForm'); loginForm.innerHTML = ` <div class="header"> <div class="logo">S</div> <h1>SAP MCP Server</h1> <p class="subtitle">${message}</p> </div> <div style="text-align: center; padding: 20px;"> <div class="loading" style="display: flex; justify-content: center;"> <div class="spinner"></div> </div> </div> `; } function showError(message) { const errorDiv = document.getElementById('errorMessage'); if (errorDiv) { errorDiv.textContent = message; errorDiv.style.display = 'block'; } else { alert(message); // Fallback } } // Event Listeners (removed - automatic redirect now) function showSuccessState() { document.getElementById('loginForm').style.display = 'none'; document.getElementById('successState').style.display = 'block'; updateSuccessDisplay(); } async function updateSuccessDisplay() { // Get user session information and update display try { const response = await fetch('/auth/status', { headers: { 'x-mcp-session-id': sessionId } }); if (response.ok) { const status = await response.json(); // Update user information display document.getElementById('userName').textContent = status.user || 'Unknown'; document.getElementById('sessionIdDisplay').textContent = sessionId || 'Unknown'; // Show admin sections if user has admin privileges if (status.scopes && status.scopes.some(scope => scope.includes('admin'))) { document.getElementById('adminSection').style.display = 'block'; document.getElementById('adminButton').style.display = 'inline-block'; } } else { // If status check fails, show generic info document.getElementById('userName').textContent = 'Authenticated User'; document.getElementById('sessionIdDisplay').textContent = sessionId || 'Unknown'; } } catch (error) { console.log('Could not get session info:', error); // Show what we know document.getElementById('userName').textContent = 'Authenticated User'; document.getElementById('sessionIdDisplay').textContent = sessionId || 'Unknown'; } // Check for redirect parameter and redirect after successful authentication const urlParams = new URLSearchParams(window.location.search); const redirectUrl = urlParams.get('redirect'); if (redirectUrl) { setTimeout(() => { window.location.href = redirectUrl; }, 3000); // Give user 3 seconds to see session info before redirecting } } function openAdminDashboard() { // Open admin dashboard with session ID parameter const adminUrl = `/auth/admin?session=${encodeURIComponent(sessionId)}`; window.open(adminUrl, '_blank'); } // Handle OAuth 2.0 callback async function handleOAuth2Callback() { const urlParams = new URLSearchParams(window.location.search); const code = urlParams.get('code'); const state = urlParams.get('state'); const error = urlParams.get('error'); const storedState = sessionStorage.getItem('oauth_state'); if (error) { showError(`OAuth error: ${error}`); return false; } if (!code) { return false; // Not an OAuth callback } // Validate state parameter if (!state || state !== storedState) { showError('OAuth state mismatch. Please try again.'); return true; // Handled as error } try { setLoadingState('Processing OAuth callback...'); // Exchange authorization code for tokens - force HTTPS for Cloud Foundry const protocol = window.location.host.includes('.cfapps.') || window.location.host.includes('.ondemand.com') ? 'https:' : window.location.protocol; const response = await fetch('/auth/token', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ grant_type: 'authorization_code', code: code, redirect_uri: `${protocol}//${window.location.host}/auth/callback` }) }); const result = await response.json(); if (response.ok && result.session_id) { sessionId = result.session_id; tokenData = { sessionId: result.session_id, user: 'OAuth User' }; // Clear OAuth parameters from URL sessionStorage.removeItem('oauth_state'); window.history.replaceState({}, document.title, window.location.pathname); showSuccessState(); } else { throw new Error(result.error_description || result.message || 'OAuth token exchange failed'); } } catch (error) { console.error('OAuth callback error:', error); showError('OAuth authentication failed: ' + error.message); } return true; // OAuth callback handled } // Check URL parameters and existing authentication on page load window.addEventListener('load', async () => { const urlParams = new URLSearchParams(window.location.search); const urlSessionId = urlParams.get('session'); const success = urlParams.get('success'); const error = urlParams.get('error'); const code = urlParams.get('code'); // OAuth authorization code console.log('Page load - URL params:', { success, error, code, session: urlSessionId }); // First check if this is an OAuth callback if (await handleOAuth2Callback()) { return; // OAuth callback was handled } if (error) { showError(decodeURIComponent(error)); return; // Stop here on error } else if (success && urlSessionId) { // Success callback from server sessionId = urlSessionId; tokenData = { sessionId: urlSessionId, user: 'OAuth User' }; showSuccessState(); // Clean up URL window.history.replaceState({}, document.title, window.location.pathname); return; } // Check for existing session before redirecting try { const response = await fetch('/auth/status'); if (response.ok) { const status = await response.json(); if (status.authenticated) { sessionId = status.sessionId; tokenData = status; showSuccessState(); return; } } } catch (error) { // No existing session, continue to redirect } // Only redirect if no callback parameters and no existing session console.log('No session found, redirecting to SAP IAS...'); console.log('Calling initiateOAuth2Flow immediately...'); try { await initiateOAuth2Flow(); } catch (error) { console.error('Error in initiateOAuth2Flow:', error); } }); </script> </body> </html>

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Raistlin82/btp-sap-odata-to-mcp-server-optimized'

If you have feedback or need assistance with the MCP directory API, please join our Discord server