Self-hosted climate transparency for enterprises
OpenEco uses federated authentication - it does NOT manage user identities. Organizations use Keycloak as an open-source IdP bridge that connects to their existing identity providers (Azure AD, Okta, Google Workspace, etc.), and OpenEco trusts identity assertions from Keycloak.
You host one Keycloak instance, but each organization brings their own IdP.
User → OpenEco → Keycloak (IdP Bridge) → Organization's IdP (Azure AD/Okta/Google/etc.)
↓
User authenticates
↓
IdP returns token to Keycloak
↓
Keycloak issues OIDC token to OpenEco
↓
OpenEco verifies token → Maps roles → Creates session
┌─────────────────────────────────────────────────────────────┐
│ Organization Infrastructure │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ OpenEco │ │ Keycloak │ │ Org's IdP │ │
│ │ (Web App) │ │ (IdP Bridge) │ │ (Azure AD/ │ │
│ │ │ │ │ │ Okta/etc.) │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ └─────────┬───────┴─────────────────┘ │
│ │ │
│ ┌─────────▼─────────┐ │
│ │ PostgreSQL │ │
│ │ (OpenEco DB) │ │
│ └───────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Key Points:
OpenEco connects to Keycloak (open-source IdP bridge). Keycloak then federates to your organization’s existing IdP:
| Provider Type | Examples | Protocol | Status |
|---|---|---|---|
| Cloud IAM | Azure AD, Google Workspace, AWS IAM | OIDC/SAML → Keycloak → OpenEco | ✅ Supported |
| Enterprise SSO | Okta, Auth0 | OIDC/SAML → Keycloak → OpenEco | ✅ Supported |
| Enterprise Directory | Active Directory | LDAP → Keycloak → OpenEco | ✅ Supported |
| Self-Hosted | Keycloak (standalone) | OIDC | ✅ Supported |
| Local Auth | Email/password (dev only) | Credentials | ⚠️ Development only |
Keycloak Features:
Keycloak can be deployed in three ways:
Option A: Embedded (Same Pod/Container as OpenEco)
Option B: Sidecar (Same Namespace/Network)
Option C: External (Dedicated Instance)
Deployment Commands:
Docker Compose (Recommended for Pilots):
cd deploy/keycloak
# Create .env file
cat > .env << EOF
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=your-secure-password
KEYCLOAK_DB_PASSWORD=your-db-password
KEYCLOAK_HOSTNAME=keycloak.yourcompany.com
KEYCLOAK_PORT=8080
EOF
# Start Keycloak
docker-compose up -d
# or
podman-compose up -d
Kubernetes/OKD:
kubectl apply -f deploy/okd/keycloak/deployment.yaml
See deploy/keycloak/README.md for detailed instructions.
https://keycloak.yourcompany.comyour-company (or your organization name)Connect Your Organization’s IdP (Identity Provider)
For Azure AD:
azure-adhttps://login.microsoftonline.com/{tenant-id}/.well-known/openid-configuration[from Azure AD][from Azure AD]For Okta:
oktahttps://{your-domain}.okta.com/.well-known/openid-configuration[from Okta][from Okta]For Google Workspace:
[from Google Cloud Console][from Google Cloud Console]For Active Directory (LDAP):
openecoopenid-connectconfidentialhttps://climate.yourcompany.com/api/auth/oidc/callback?organizationId=*https://climate.yourcompany.comsustainability-team, admins, etc.https://keycloak.yourcompany.com/realms/your-companyopeneco[paste from Keycloak]openeco (if required by your IdP)@yourcompany.com → ORG_ADMIN (Priority: 10)sustainability-team → ORG_MEMBER (Priority: 5)ORG_MEMBER (Priority: 0)The form displays connection information you’ll need for your IdP:
Required Fields:
https://keycloak.example.com/realms/my-realm)Optional Fields:
Role mappings determine which role a user gets when they sign in. Mappings are checked in priority order (higher priority first).
Mapping Types:
email_domain)
@acme.com → ORG_ADMINgroup)
sustainability-team → ORG_MEMBERattribute) - Future
department=sustainability → ORG_MEMBERPriority:
ORG_MEMBERExample Configuration:
| Priority | Type | Match Value | Role |
|---|---|---|---|
| 10 | email_domain | @acme.com | ORG_ADMIN |
| 5 | group | sustainability-team | ORG_MEMBER |
| 5 | group | admins | ORG_ADMIN |
| 0 | (default) | - | ORG_MEMBER |
When you first deploy OpenEco, you’ll be guided through setup:
/setupFlow: Organization’s IdP → Keycloak → OpenEco
Step 1: Connect Your IdP to Keycloak
For Azure AD:
https://login.microsoftonline.com/{tenant-id}/.well-known/openid-configuration[from Azure AD App Registration][from Azure AD App Registration]For Okta:
https://{your-domain}.okta.com/.well-known/openid-configuration[from Okta Application][from Okta Application]For Google Workspace:
[from Google Cloud Console][from Google Cloud Console]Step 2: Configure Keycloak Client for OpenEco
Keycloak Client Configuration:
Client ID: openeco
Client Protocol: openid-connect
Access Type: confidential
Valid Redirect URIs: https://climate.yourcompany.com/api/auth/oidc/callback?organizationId=*
Web Origins: https://climate.yourcompany.com
Step 3: Configure OpenEco
OpenEco Configuration:
Issuer: https://keycloak.yourcompany.com/realms/your-company
Client ID: openeco
Client Secret: [from Keycloak]
Audience: openeco
Note: OpenEco only connects to Keycloak. Your organization’s IdP (Azure AD, Okta, etc.) connects to Keycloak, not directly to OpenEco.
Scenario: All users from your company domain should be admins.
Configuration:
Priority: 10
Type: email_domain
Match Value: @acme.com
Role: ORG_ADMIN
Result: Any user with email ending in @acme.com gets ORG_ADMIN role.
Scenario: Users in “sustainability-team” group get member access.
Configuration:
Priority: 5
Type: group
Match Value: sustainability-team
Role: ORG_MEMBER
Result: Users in the sustainability-team group get ORG_MEMBER role.
Scenario:
Configuration:
Priority: 10, Type: email_domain, Match: @acme.com, Role: ORG_ADMIN
Priority: 5, Type: group, Match: sustainability-team, Role: ORG_MEMBER
Priority: 0, Type: (default), Match: -, Role: READ_ONLY
Error: “Invalid issuer”
/realms/your-realm (for Keycloak)Error: “Client authentication failed”
Error: “Redirect URI mismatch”
https://climate.yourcompany.com/api/auth/oidc/callback?organizationId=** wildcard allows any organization IDUser redirected back to sign-in page
“Organization not found” error
User gets wrong role
User gets default role when they shouldn’t
@)Initiate OIDC Login:
GET /api/auth/oidc/authorize?organizationId=xxx
Redirects user to identity provider.
OIDC Callback:
GET /api/auth/oidc/callback?code=xxx&state=xxx&organizationId=xxx
Handles OIDC callback and creates session.
Admin Auth Config:
GET /api/admin/auth-config?organizationId=xxx
POST /api/admin/auth-config
POST /api/admin/auth-config/test
Role Mappings:
GET /api/admin/role-mappings?authConfigId=xxx
POST /api/admin/role-mappings
Need help? Open an issue on GitHub.