A production-ready implementation of social login using SAP Customer Data Cloud (CDC) with the proxy-handled flow pattern and Ory Hydra for OAuth2.
This implementation demonstrates the CDC Proxy-Handled Flow where:
- Client-side: User authenticates with a social provider (Google) using their JavaScript SDK
- Client → Server: The ID token is sent to your proxy server
- Server → CDC: Your server searches for the user in CDC using
accounts.search - Server → Hydra: User is authenticated via Ory Hydra OAuth2 flow
- Client: Receives OAuth2 access and refresh tokens
- ✅ Modern client-side social login experience
- ✅ SAP CDC integration for user management
- ✅ Ory Hydra for OAuth2/OIDC token issuance
- ✅ Docker-based infrastructure setup
- ✅ Production-ready error handling
- ✅ Comprehensive logging
- Node.js 16+ installed
- Docker and Docker Compose installed
- A Google OAuth Client ID (Get one here)
- SAP CDC credentials (API Key and Secret Key)
git clone <repository-url>
cd approach-2-proxy-handled
npm installcp env.example .envEdit .env and add your credentials:
# SAP CDC Configuration
CDC_API_KEY=your_cdc_api_key
CDC_SECRET_KEY=your_cdc_secret_key
CDC_BASE_URL=https://accounts.us1.gigya.com # or eu1, au1, etc.
# Google OAuth
GOOGLE_CLIENT_ID=your-google-client-id.apps.googleusercontent.com
# Application URLs
APP_URL=http://localhost:3000
PORT=3000
# Ory Hydra URLs (default for Docker Compose setup)
HYDRA_ADMIN_URL=http://localhost:4445
HYDRA_PUBLIC_URL=http://localhost:4444
# OAuth Client (created automatically by start.sh)
CLIENT_ID=demo-client-id
CLIENT_SECRET=demo-client-secret- Go to Google Cloud Console
- Select or create a project
- Create OAuth 2.0 Client ID (Web application)
- Under Authorized JavaScript origins, add:
http://localhost:3000
- Copy the Client ID to your
.envfile
Note: No redirect URI is needed for the JavaScript SDK.
./start.shThis script will:
- Start Ory Hydra OAuth2 server (Docker)
- Wait for Hydra to be ready
- Create the OAuth2 client automatically
- Start the Node.js application server
Open in your browser: http://localhost:3000/test
Click "Start OAuth2 Flow" → Sign in with Google → Complete authentication
./stop.sh┌──────────┐ 1. Click ┌──────────┐ 2. Auth ┌────────┐
│ Browser │─────────────▶│ Google │─────────────▶│ Google │
│ │ │ SDK │ │ Auth │
└──────────┘ └──────────┘ └────────┘
│ │ │
│ │◀─── 3. ID Token ───────┘
│ │
│ 4. POST Token │
│─────────────────────────▶│
│ ┌───▼────┐
│ │ Your │ 5. Search
│ │ Server │────────────▶┌─────┐
│ └────────┘ │ CDC │
│ │ 6. User Info └─────┘
│ │◀─────────────────┘
│ │
│ │ 7. Accept Login
│ │─────────────────▶┌───────┐
│ 8. Redirect │ │ Hydra │
│◀─────────────────────────┤ │ │
│ │ 9. OAuth Tokens │ │
│◀─────────────────────────────────────────────┴───────┘
approach-2-proxy-handled/
├── src/
│ ├── server.js # Main Express application
│ ├── routes/
│ │ ├── auth.js # Login page with Google Sign-In button
│ │ ├── social-login.js # Receives token, searches CDC
│ │ ├── consent.js # Hydra consent handler
│ │ ├── hydra-callback.js # OAuth callback
│ │ └── test.js # Test interface
│ └── services/
│ └── cdcClient.js # CDC API client
├── docker-compose.yml # Hydra infrastructure
├── start.sh # Startup script
├── stop.sh # Shutdown script
├── package.json # Node.js dependencies
├── .env # Configuration (create from env.example)
└── env.example # Configuration template
The login page (src/routes/auth.js) loads the Google Sign-In SDK and renders the authentication button:
google.accounts.id.initialize({
client_id: 'YOUR_GOOGLE_CLIENT_ID',
callback: handleGoogleSignIn
});
google.accounts.id.renderButton(
document.getElementById('google-signin-btn'),
{ theme: 'outline', size: 'large' }
);When the user authenticates, the client sends the ID token to your server:
async function handleGoogleSignIn(response) {
const result = await fetch('/auth/social-login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
provider: 'google',
accessToken: response.credential, // Google ID token
login_challenge: login_challenge
})
});
const data = await result.json();
window.location.href = data.redirectTo;
}The server (src/routes/social-login.js) decodes the token and searches for the user:
// Decode Google ID token to get email
const decoded = jwt.decode(providerToken);
// Search CDC for existing user
const searchResult = await cdcClient.searchUser(decoded.email);Once the user is found/created, accept the Hydra login challenge:
await axios.put(
`${HYDRA_ADMIN_URL}/admin/oauth2/auth/requests/login/accept`,
{
subject: cdcResponse.UID,
context: {
email: cdcResponse.profile.email,
provider: 'google'
}
},
{ params: { login_challenge } }
);GET http://localhost:3000/healthReturns server status and available endpoints.
GET http://localhost:3000/testInteractive page to test the complete OAuth2 flow.
GET http://localhost:3000/login?login_challenge={challenge}Displays Google Sign-In button. Called by Hydra with a login challenge.
POST http://localhost:3000/auth/social-login
Content-Type: application/json
{
"provider": "google",
"accessToken": "google-id-token",
"login_challenge": "hydra-login-challenge"
}Processes social login, searches CDC, and accepts Hydra login.
| Variable | Description | Example |
|---|---|---|
CDC_API_KEY |
Your SAP CDC API key | 3_abc... |
CDC_SECRET_KEY |
Your SAP CDC secret key | xyz... |
CDC_BASE_URL |
CDC API base URL | https://accounts.us1.gigya.com |
GOOGLE_CLIENT_ID |
Google OAuth Client ID | xxx.apps.googleusercontent.com |
APP_URL |
Your application URL | http://localhost:3000 |
PORT |
Server port | 3000 |
HYDRA_ADMIN_URL |
Hydra admin API URL | http://localhost:4445 |
HYDRA_PUBLIC_URL |
Hydra public API URL | http://localhost:4444 |
CLIENT_ID |
OAuth client ID | demo-client-id |
CLIENT_SECRET |
OAuth client secret | demo-client-secret |
Problem: The Google Sign-In button doesn't appear on the login page.
Solutions:
- Verify
GOOGLE_CLIENT_IDis correctly set in.env - Check that
http://localhost:3000is added to "Authorized JavaScript origins" in Google Console - Open browser console and look for Google SDK errors
- Ensure the SDK script is loading:
https://accounts.google.com/gsi/client
Problem: Browser shows: The given origin is not allowed for the given client ID
Solution:
- Go to Google Cloud Console
- Edit your OAuth 2.0 Client ID
- Add
http://localhost:3000to Authorized JavaScript origins - Save and wait a few minutes for changes to propagate
Problem: User authenticates but CDC can't find their account.
Solutions:
- This is normal for first-time users - they need to be registered in CDC first
- Check CDC console to see if the user exists
- Verify the email in the Google token matches the email in CDC
- Consider implementing user registration flow for new users
Problem: Server can't connect to Hydra.
Solutions:
- Ensure Hydra is running:
docker ps - Verify Hydra URLs in
.envare correct - Check Hydra health:
curl http://localhost:4445/health/ready - Restart Hydra:
docker-compose restart hydra
# Use production URLs
APP_URL=https://auth.yourdomain.com
HYDRA_ADMIN_URL=https://hydra-admin.yourdomain.com
HYDRA_PUBLIC_URL=https://hydra.yourdomain.com
# Use production CDC datacenter
CDC_BASE_URL=https://accounts.eu1.gigya.com- Add production domain to Authorized JavaScript origins
- Remove or restrict localhost origins
- Generate new OAuth credentials for production (recommended)
- ✅ Always use HTTPS in production
- ✅ Store secrets in a secure vault (AWS Secrets Manager, etc.)
- ✅ Rotate API keys regularly
- ✅ Enable rate limiting
- ✅ Set up monitoring and alerting
- ✅ Review CORS configuration for production domains
# Install production dependencies only
npm install --production
# Start with PM2 or similar process manager
pm2 start src/server.js --name "cdc-social-login"- Start the application
- Visit http://localhost:3000/test
- Click "Start OAuth2 Flow"
- Sign in with Google
- Verify successful authentication
- Check CDC console for user account
# Health check
curl http://localhost:3000/health
# Hydra health
curl http://localhost:4445/health/readyFor issues or questions:
- Check the troubleshooting section above
- Review server logs for detailed error messages
- Verify all environment variables are set correctly
- Check CDC error codes in API responses
ISC
Status: ✅ Production-Ready
This implementation follows CDC best practices and is ready for production deployment with proper configuration.