@@ -54,3 +54,221 @@ keycloak-status: ## Show Keycloak status and connection info
5454.PHONY : keycloak-logs
5555keycloak-logs : # # Tail Keycloak logs
5656 @kubectl logs -n $(KEYCLOAK_NAMESPACE ) -l app=keycloak -f --tail=100
57+
58+ .PHONY : keycloak-setup-realm
59+ keycloak-setup-realm : # # Setup OpenShift realm with token exchange support
60+ @echo " ========================================="
61+ @echo " Setting up OpenShift Realm for Token Exchange"
62+ @echo " ========================================="
63+ @echo " Using Keycloak at http://localhost:8090"
64+ @echo " (Ensure 'make keycloak-forward' is running in another terminal)"
65+ @echo " "
66+ @echo " Getting admin access token..."
67+ @TOKEN=$$(curl -s -X POST "http://localhost:8090/realms/master/protocol/openid-connect/token" \
68+ -H " Content-Type: application/x-www-form-urlencoded" \
69+ -d " username=$( KEYCLOAK_ADMIN_USER) " \
70+ -d " password=$( KEYCLOAK_ADMIN_PASSWORD) " \
71+ -d " grant_type=password" \
72+ -d " client_id=admin-cli" \
73+ 2> /dev/null | jq -r ' .access_token // empty' ); \
74+ if [ -z " $$ TOKEN" ] || [ " $$ TOKEN" = " null" ]; then \
75+ echo " ❌ Failed to get access token. Check if:" ; \
76+ echo " - Keycloak is running (make keycloak-install)" ; \
77+ echo " - Port forwarding is active (make keycloak-forward)" ; \
78+ echo " - Admin credentials are correct: $( KEYCLOAK_ADMIN_USER) /$( KEYCLOAK_ADMIN_PASSWORD) " ; \
79+ exit 1; \
80+ fi ; \
81+ echo " ✅ Successfully obtained access token" ; \
82+ echo " " ; \
83+ echo " Creating OpenShift realm..." ; \
84+ REALM_RESPONSE=$$(curl -s -w "%{http_code}" -X POST "http://localhost:8090/admin/realms" \
85+ -H " Authorization: Bearer $$ TOKEN" \
86+ -H " Content-Type: application/json" \
87+ -d ' {"realm":"openshift","enabled":true}' ); \
88+ REALM_CODE=$$(echo "$$REALM_RESPONSE" | tail -c 4 ) ; \
89+ if [ " $$ REALM_CODE" = " 201" ] || [ " $$ REALM_CODE" = " 409" ]; then \
90+ if [ " $$ REALM_CODE" = " 201" ]; then echo " ✅ OpenShift realm created" ; \
91+ else echo " ✅ OpenShift realm already exists" ; fi ; \
92+ else \
93+ echo " ❌ Failed to create OpenShift realm (HTTP $$ REALM_CODE)" ; \
94+ exit 1; \
95+ fi ; \
96+ echo " " ; \
97+ echo " Creating mcp:openshift client scope..." ; \
98+ SCOPE_RESPONSE=$$(curl -s -w "HTTPCODE:%{http_code}" -X POST "http://localhost:8090/admin/realms/openshift/client-scopes" \
99+ -H " Authorization: Bearer $$ TOKEN" \
100+ -H " Content-Type: application/json" \
101+ -d ' {"name":"mcp:openshift","protocol":"openid-connect","attributes":{"display.on.consent.screen":"false","include.in.token.scope":"true"}}' ); \
102+ SCOPE_CODE=$$(echo "$$SCOPE_RESPONSE" | grep -o "HTTPCODE:[0-9]*" | cut -d: -f2 ) ; \
103+ if [ " $$ SCOPE_CODE" = " 201" ] || [ " $$ SCOPE_CODE" = " 409" ]; then \
104+ if [ " $$ SCOPE_CODE" = " 201" ]; then echo " ✅ mcp:openshift client scope created" ; \
105+ else echo " ✅ mcp:openshift client scope already exists" ; fi ; \
106+ else \
107+ echo " ❌ Failed to create mcp:openshift scope (HTTP $$ SCOPE_CODE)" ; \
108+ exit 1; \
109+ fi ; \
110+ echo " " ; \
111+ echo " Adding audience mapper to mcp:openshift scope..." ; \
112+ SCOPES_LIST=$$(curl -s -X GET "http://localhost:8090/admin/realms/openshift/client-scopes" \
113+ -H " Authorization: Bearer $$ TOKEN" \
114+ -H " Accept: application/json" ); \
115+ SCOPE_ID=$$(echo "$$SCOPES_LIST" | jq -r '.[] | select(.name == "mcp:openshift" ) | .id // empty' 2>/dev/null); \
116+ if [ -z " $$ SCOPE_ID" ]; then \
117+ echo " ❌ Failed to find mcp:openshift scope" ; \
118+ exit 1; \
119+ fi ; \
120+ MAPPER_RESPONSE=$$(curl -s -w "HTTPCODE:%{http_code}" -X POST "http://localhost:8090/admin/realms/openshift/client-scopes/$$SCOPE_ID/protocol-mappers/models" \
121+ -H " Authorization: Bearer $$ TOKEN" \
122+ -H " Content-Type: application/json" \
123+ -d ' {"name":"openshift-audience","protocol":"openid-connect","protocolMapper":"oidc-audience-mapper","config":{"included.client.audience":"openshift","id.token.claim":"true","access.token.claim":"true"}}' ); \
124+ MAPPER_CODE=$$(echo "$$MAPPER_RESPONSE" | grep -o "HTTPCODE:[0-9]*" | cut -d: -f2 ) ; \
125+ if [ " $$ MAPPER_CODE" = " 201" ] || [ " $$ MAPPER_CODE" = " 409" ]; then \
126+ if [ " $$ MAPPER_CODE" = " 201" ]; then echo " ✅ Audience mapper added" ; \
127+ else echo " ✅ Audience mapper already exists" ; fi ; \
128+ else \
129+ echo " ❌ Failed to create audience mapper (HTTP $$ MAPPER_CODE)" ; \
130+ exit 1; \
131+ fi ; \
132+ echo " " ; \
133+ echo " Creating groups client scope..." ; \
134+ GROUPS_SCOPE_RESPONSE=$$(curl -s -w "HTTPCODE:%{http_code}" -X POST "http://localhost:8090/admin/realms/openshift/client-scopes" \
135+ -H " Authorization: Bearer $$ TOKEN" \
136+ -H " Content-Type: application/json" \
137+ -d ' {"name":"groups","protocol":"openid-connect","attributes":{"display.on.consent.screen":"false","include.in.token.scope":"true"}}' ); \
138+ GROUPS_SCOPE_CODE=$$(echo "$$GROUPS_SCOPE_RESPONSE" | grep -o "HTTPCODE:[0-9]*" | cut -d: -f2 ) ; \
139+ if [ " $$ GROUPS_SCOPE_CODE" = " 201" ] || [ " $$ GROUPS_SCOPE_CODE" = " 409" ]; then \
140+ if [ " $$ GROUPS_SCOPE_CODE" = " 201" ]; then echo " ✅ groups client scope created" ; \
141+ else echo " ✅ groups client scope already exists" ; fi ; \
142+ else \
143+ echo " ❌ Failed to create groups scope (HTTP $$ GROUPS_SCOPE_CODE)" ; \
144+ exit 1; \
145+ fi ; \
146+ echo " " ; \
147+ echo " Adding group membership mapper to groups scope..." ; \
148+ SCOPES_LIST=$$(curl -s -X GET "http://localhost:8090/admin/realms/openshift/client-scopes" \
149+ -H " Authorization: Bearer $$ TOKEN" \
150+ -H " Accept: application/json" ); \
151+ GROUPS_SCOPE_ID=$$(echo "$$SCOPES_LIST" | jq -r '.[] | select(.name == "groups" ) | .id // empty' 2>/dev/null); \
152+ if [ -z " $$ GROUPS_SCOPE_ID" ]; then \
153+ echo " ❌ Failed to find groups scope" ; \
154+ exit 1; \
155+ fi ; \
156+ GROUPS_MAPPER_RESPONSE=$$(curl -s -w "HTTPCODE:%{http_code}" -X POST "http://localhost:8090/admin/realms/openshift/client-scopes/$$GROUPS_SCOPE_ID/protocol-mappers/models" \
157+ -H " Authorization: Bearer $$ TOKEN" \
158+ -H " Content-Type: application/json" \
159+ -d ' {"name":"groups","protocol":"openid-connect","protocolMapper":"oidc-group-membership-mapper","config":{"claim.name":"groups","full.path":"false","id.token.claim":"true","access.token.claim":"true","userinfo.token.claim":"true"}}' ); \
160+ GROUPS_MAPPER_CODE=$$(echo "$$GROUPS_MAPPER_RESPONSE" | grep -o "HTTPCODE:[0-9]*" | cut -d: -f2 ) ; \
161+ if [ " $$ GROUPS_MAPPER_CODE" = " 201" ] || [ " $$ GROUPS_MAPPER_CODE" = " 409" ]; then \
162+ if [ " $$ GROUPS_MAPPER_CODE" = " 201" ]; then echo " ✅ Group membership mapper added" ; \
163+ else echo " ✅ Group membership mapper already exists" ; fi ; \
164+ else \
165+ echo " ❌ Failed to create group mapper (HTTP $$ GROUPS_MAPPER_CODE)" ; \
166+ exit 1; \
167+ fi ; \
168+ echo " " ; \
169+ echo " Creating openshift service client..." ; \
170+ OPENSHIFT_CLIENT_RESPONSE=$$(curl -s -w "HTTPCODE:%{http_code}" -X POST "http://localhost:8090/admin/realms/openshift/clients" \
171+ -H " Authorization: Bearer $$ TOKEN" \
172+ -H " Content-Type: application/json" \
173+ -d ' {"clientId":"openshift","enabled":true,"publicClient":false,"standardFlowEnabled":true,"directAccessGrantsEnabled":true,"serviceAccountsEnabled":true,"authorizationServicesEnabled":false,"redirectUris":["*"],"defaultClientScopes":["groups"],"optionalClientScopes":[]}' ); \
174+ OPENSHIFT_CLIENT_CODE=$$(echo "$$OPENSHIFT_CLIENT_RESPONSE" | grep -o "HTTPCODE:[0-9]*" | cut -d: -f2 ) ; \
175+ if [ " $$ OPENSHIFT_CLIENT_CODE" = " 201" ] || [ " $$ OPENSHIFT_CLIENT_CODE" = " 409" ]; then \
176+ if [ " $$ OPENSHIFT_CLIENT_CODE" = " 201" ]; then echo " ✅ openshift client created" ; \
177+ else echo " ✅ openshift client already exists" ; fi ; \
178+ else \
179+ echo " ❌ Failed to create openshift client (HTTP $$ OPENSHIFT_CLIENT_CODE)" ; \
180+ exit 1; \
181+ fi ; \
182+ echo " " ; \
183+ echo " Creating mcp-server client with token exchange..." ; \
184+ MCP_CLIENT_RESPONSE=$$(curl -s -w "HTTPCODE:%{http_code}" -X POST "http://localhost:8090/admin/realms/openshift/clients" \
185+ -H " Authorization: Bearer $$ TOKEN" \
186+ -H " Content-Type: application/json" \
187+ -d ' {"clientId":"mcp-server","enabled":true,"publicClient":false,"standardFlowEnabled":true,"directAccessGrantsEnabled":true,"serviceAccountsEnabled":true,"authorizationServicesEnabled":false,"redirectUris":["*"],"defaultClientScopes":["groups"],"optionalClientScopes":["mcp:openshift"],"attributes":{"oauth2.device.authorization.grant.enabled":"false","oidc.ciba.grant.enabled":"false","backchannel.logout.session.required":"true","backchannel.logout.revoke.offline.tokens":"false"}}' ); \
188+ MCP_CLIENT_CODE=$$(echo "$$MCP_CLIENT_RESPONSE" | grep -o "HTTPCODE:[0-9]*" | cut -d: -f2 ) ; \
189+ if [ " $$ MCP_CLIENT_CODE" = " 201" ] || [ " $$ MCP_CLIENT_CODE" = " 409" ]; then \
190+ if [ " $$ MCP_CLIENT_CODE" = " 201" ]; then echo " ✅ mcp-server client created" ; \
191+ else echo " ✅ mcp-server client already exists" ; fi ; \
192+ else \
193+ echo " ❌ Failed to create mcp-server client (HTTP $$ MCP_CLIENT_CODE)" ; \
194+ exit 1; \
195+ fi ; \
196+ echo " " ; \
197+ echo " Enabling token exchange for mcp-server..." ; \
198+ CLIENTS_LIST=$$(curl -s -X GET "http://localhost:8090/admin/realms/openshift/clients" \
199+ -H " Authorization: Bearer $$ TOKEN" \
200+ -H " Accept: application/json" ); \
201+ MCP_CLIENT_ID=$$(echo "$$CLIENTS_LIST" | jq -r '.[] | select(.clientId == "mcp-server" ) | .id // empty' 2>/dev/null); \
202+ if [ -z " $$ MCP_CLIENT_ID" ]; then \
203+ echo " ❌ Failed to find mcp-server client" ; \
204+ exit 1; \
205+ fi ; \
206+ PERMS_RESPONSE=$$(curl -s -w "HTTPCODE:%{http_code}" -X PUT "http://localhost:8090/admin/realms/openshift/clients/$$MCP_CLIENT_ID/management/permissions" \
207+ -H " Authorization: Bearer $$ TOKEN" \
208+ -H " Content-Type: application/json" \
209+ -d ' {"enabled":true}' ); \
210+ PERMS_CODE=$$(echo "$$PERMS_RESPONSE" | grep -o "HTTPCODE:[0-9]*" | cut -d: -f2 ) ; \
211+ if [ " $$ PERMS_CODE" = " 200" ]; then \
212+ echo " ✅ Token exchange permissions enabled" ; \
213+ else \
214+ echo " ⚠️ Could not enable permissions (HTTP $$ PERMS_CODE) - may need manual configuration" ; \
215+ fi ; \
216+ echo " " ; \
217+ echo " Getting mcp-server client secret..." ; \
218+ SECRET_RESPONSE=$$(curl -s -X GET "http://localhost:8090/admin/realms/openshift/clients/$$MCP_CLIENT_ID/client-secret" \
219+ -H " Authorization: Bearer $$ TOKEN" \
220+ -H " Accept: application/json" ); \
221+ CLIENT_SECRET=$$(echo "$$SECRET_RESPONSE" | jq -r '.value // empty' 2>/dev/null ) ; \
222+ if [ -z " $$ CLIENT_SECRET" ]; then \
223+ echo " ❌ Failed to get client secret" ; \
224+ else \
225+ echo " ✅ Client secret retrieved" ; \
226+ fi ; \
227+ echo " " ; \
228+ echo " Creating test user developer/developer..." ; \
229+ USER_RESPONSE=$$(curl -s -w "%{http_code}" -X POST "http://localhost:8090/admin/realms/openshift/users" \
230+ -H " Authorization: Bearer $$ TOKEN" \
231+ -H " Content-Type: application/json" \
232+ -d
' {"username":"developer","email":"[email protected] ","firstName":"Developer","lastName":"User","enabled":true,"emailVerified":true,"credentials":[{"type":"password","value":"developer","temporary":false}]}' )
; \ 233+ USER_CODE=$$(echo "$$USER_RESPONSE" | tail -c 4 ) ; \
234+ if [ " $$ USER_CODE" = " 201" ] || [ " $$ USER_CODE" = " 409" ]; then \
235+ if [ " $$ USER_CODE" = " 201" ]; then echo " ✅ developer user created" ; \
236+ else echo " ✅ developer user already exists" ; fi ; \
237+ else \
238+ echo " ❌ Failed to create developer user (HTTP $$ USER_CODE)" ; \
239+ exit 1; \
240+ fi ; \
241+ echo " " ; \
242+ echo " 🎉 OpenShift realm setup complete!" ; \
243+ echo " " ; \
244+ echo " ========================================" ; \
245+ echo " Configuration Summary" ; \
246+ echo " ========================================" ; \
247+ echo " Realm: openshift" ; \
248+ echo " Authorization URL: http://localhost:8090/realms/openshift" ; \
249+ echo " " ; \
250+ echo " Test User:" ; \
251+ echo " Username: developer" ; \
252+ echo " Password: developer" ; \
253+ echo " Email: [email protected] " ; \ 254+ echo " " ; \
255+ echo " Clients:" ; \
256+ echo " mcp-server (confidential, token exchange enabled)" ; \
257+ echo " Client ID: mcp-server" ; \
258+ echo " Client Secret: $$ CLIENT_SECRET" ; \
259+ echo " openshift (service account)" ; \
260+ echo " Client ID: openshift" ; \
261+ echo " " ; \
262+ echo " Client Scopes:" ; \
263+ echo " mcp:openshift (optional) - Audience: openshift" ; \
264+ echo " groups (default) - Group membership mapper" ; \
265+ echo " " ; \
266+ echo " TOML Configuration:" ; \
267+ echo " require_oauth = true" ; \
268+ echo " oauth_audience = \" mcp-server\" " ; \
269+ echo " authorization_url = \" http://localhost:8090/realms/openshift\" " ; \
270+ echo " sts_client_id = \" mcp-server\" " ; \
271+ echo " sts_client_secret = \" $$ CLIENT_SECRET\" " ; \
272+ echo " sts_audience = \" openshift\" " ; \
273+ echo " sts_scopes = [\" mcp:openshift\" ]" ; \
274+ echo " ========================================"
0 commit comments