Skip to content

Commit 04018b8

Browse files
committed
added /debug/oidc
1 parent 32a15c2 commit 04018b8

6 files changed

Lines changed: 748 additions & 1 deletion

File tree

components.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ declare module 'vue' {
3838
ElMain: typeof import('element-plus/es')['ElMain']
3939
ElRow: typeof import('element-plus/es')['ElRow']
4040
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
41+
ElTable: typeof import('element-plus/es')['ElTable']
42+
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
4143
ElTag: typeof import('element-plus/es')['ElTag']
4244
ElTooltip: typeof import('element-plus/es')['ElTooltip']
4345
GlossarySearchNav: typeof import('./src/components/GlossarySearchNav.vue')['default']

server/routes/status.ts

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,4 +218,265 @@ router.get('/status/providers', (req: Request, res: Response) => {
218218
}
219219
})
220220

221+
/**
222+
* GET /status/oidc-debug
223+
* Get detailed OIDC discovery information for debugging
224+
* Shows the full discovery process and configuration for all providers
225+
*/
226+
router.get('/status/oidc-debug', async (req: Request, res: Response) => {
227+
try {
228+
console.log('OIDC Debug: Starting detailed discovery process...')
229+
230+
// Step 1: Get OBP API well-known endpoint info
231+
const obpApiHost = obpClientService.getOBPClientConfig().baseUri
232+
const wellKnownEndpoint = `${obpApiHost}/obp/v5.1.0/well-known`
233+
234+
const step1 = {
235+
description: 'Discovery of OIDC providers from OBP API',
236+
endpoint: wellKnownEndpoint,
237+
success: false,
238+
response: null as any,
239+
error: null as string | null,
240+
providers: [] as any[]
241+
}
242+
243+
try {
244+
console.log(`OIDC Debug: Fetching from ${wellKnownEndpoint}`)
245+
const wellKnownResponse = await obpClientService.get('/obp/v5.1.0/well-known', null)
246+
step1.response = wellKnownResponse
247+
step1.success = !!(wellKnownResponse && wellKnownResponse.well_known_uris)
248+
step1.providers = wellKnownResponse.well_known_uris || []
249+
console.log(`OIDC Debug: Found ${step1.providers.length} providers`)
250+
} catch (error) {
251+
step1.error = error instanceof Error ? error.message : 'Unknown error'
252+
console.error('OIDC Debug: Error fetching OBP well-known:', error)
253+
}
254+
255+
// Step 2: For each provider, fetch their OIDC configuration
256+
const providerDetails = []
257+
258+
for (const provider of step1.providers) {
259+
console.log(`OIDC Debug: Fetching OIDC config for ${provider.provider}`)
260+
const detail = {
261+
providerName: provider.provider,
262+
wellKnownUrl: provider.url,
263+
success: false,
264+
oidcConfiguration: null as any,
265+
error: null as string | null,
266+
endpoints: {
267+
authorization: null as string | null,
268+
token: null as string | null,
269+
userinfo: null as string | null,
270+
jwks: null as string | null
271+
},
272+
issuer: null as string | null,
273+
supportedFeatures: {
274+
pkce: false,
275+
scopes: [] as string[],
276+
responseTypes: [] as string[],
277+
grantTypes: [] as string[]
278+
}
279+
}
280+
281+
try {
282+
const response = await fetch(provider.url)
283+
284+
if (!response.ok) {
285+
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
286+
}
287+
288+
const config = await response.json()
289+
detail.oidcConfiguration = config
290+
detail.success = true
291+
detail.issuer = config.issuer
292+
293+
// Extract endpoints
294+
detail.endpoints.authorization = config.authorization_endpoint
295+
detail.endpoints.token = config.token_endpoint
296+
detail.endpoints.userinfo = config.userinfo_endpoint
297+
detail.endpoints.jwks = config.jwks_uri
298+
299+
// Extract supported features
300+
detail.supportedFeatures.pkce =
301+
config.code_challenge_methods_supported?.includes('S256') || false
302+
detail.supportedFeatures.scopes = config.scopes_supported || []
303+
detail.supportedFeatures.responseTypes = config.response_types_supported || []
304+
detail.supportedFeatures.grantTypes = config.grant_types_supported || []
305+
306+
console.log(`OIDC Debug: Successfully fetched config for ${provider.provider}`)
307+
} catch (error) {
308+
detail.error = error instanceof Error ? error.message : 'Unknown error'
309+
console.error(`OIDC Debug: Error fetching config for ${provider.provider}:`, error)
310+
}
311+
312+
providerDetails.push(detail)
313+
}
314+
315+
// Step 3: Get current provider status from manager
316+
const currentStatus = providerManager.getAllProviderStatus()
317+
const availableProviders = providerManager.getAvailableProviders()
318+
319+
// Step 4: Get environment configuration
320+
const maskCredential = (value: string | undefined): string => {
321+
if (!value || value.length < 6) {
322+
return value ? '***masked***' : 'not configured'
323+
}
324+
return `${value.substring(0, 2)}...${value.substring(value.length - 2)}`
325+
}
326+
327+
const envConfig = {
328+
obpOidc: {
329+
clientId: maskCredential(process.env.VITE_OBP_OIDC_CLIENT_ID),
330+
clientSecret: process.env.VITE_OBP_OIDC_CLIENT_SECRET ? 'configured' : 'not configured',
331+
configured: !!(
332+
process.env.VITE_OBP_OIDC_CLIENT_ID && process.env.VITE_OBP_OIDC_CLIENT_SECRET
333+
)
334+
},
335+
keycloak: {
336+
clientId: maskCredential(process.env.VITE_KEYCLOAK_CLIENT_ID),
337+
clientSecret: process.env.VITE_KEYCLOAK_CLIENT_SECRET ? 'configured' : 'not configured',
338+
configured: !!(
339+
process.env.VITE_KEYCLOAK_CLIENT_ID && process.env.VITE_KEYCLOAK_CLIENT_SECRET
340+
)
341+
},
342+
google: {
343+
clientId: maskCredential(process.env.VITE_GOOGLE_CLIENT_ID),
344+
clientSecret: process.env.VITE_GOOGLE_CLIENT_SECRET ? 'configured' : 'not configured',
345+
configured: !!(process.env.VITE_GOOGLE_CLIENT_ID && process.env.VITE_GOOGLE_CLIENT_SECRET)
346+
},
347+
github: {
348+
clientId: maskCredential(process.env.VITE_GITHUB_CLIENT_ID),
349+
clientSecret: process.env.VITE_GITHUB_CLIENT_SECRET ? 'configured' : 'not configured',
350+
configured: !!(process.env.VITE_GITHUB_CLIENT_ID && process.env.VITE_GITHUB_CLIENT_SECRET)
351+
},
352+
custom: {
353+
providerName: process.env.VITE_CUSTOM_OIDC_PROVIDER_NAME || 'not configured',
354+
clientId: maskCredential(process.env.VITE_CUSTOM_OIDC_CLIENT_ID),
355+
clientSecret: process.env.VITE_CUSTOM_OIDC_CLIENT_SECRET ? 'configured' : 'not configured',
356+
configured: !!(
357+
process.env.VITE_CUSTOM_OIDC_CLIENT_ID && process.env.VITE_CUSTOM_OIDC_CLIENT_SECRET
358+
)
359+
},
360+
shared: {
361+
redirectUrl: process.env.VITE_OAUTH2_REDIRECT_URL || 'not configured',
362+
obpApiHost: process.env.VITE_OBP_API_HOST || 'not configured'
363+
}
364+
}
365+
366+
// Compile summary
367+
const summary = {
368+
timestamp: new Date().toISOString(),
369+
obpApiReachable: step1.success,
370+
totalProvidersDiscovered: step1.providers.length,
371+
successfulConfigurations: providerDetails.filter((p) => p.success).length,
372+
failedConfigurations: providerDetails.filter((p) => !p.success).length,
373+
currentlyAvailable: availableProviders.length,
374+
configuredInEnvironment: Object.values(envConfig).filter(
375+
(c) => typeof c === 'object' && 'configured' in c && c.configured
376+
).length
377+
}
378+
379+
res.json({
380+
summary,
381+
discoveryProcess: {
382+
step1_obpApiDiscovery: step1,
383+
step2_providerConfigurations: providerDetails,
384+
step3_currentStatus: currentStatus
385+
},
386+
environment: envConfig,
387+
recommendations: generateRecommendations(step1, providerDetails, envConfig, currentStatus),
388+
note: 'This debug information shows the complete OIDC discovery process for troubleshooting'
389+
})
390+
391+
console.log('OIDC Debug: Response sent successfully')
392+
} catch (error) {
393+
console.error('OIDC Debug: Error generating debug info:', error)
394+
res.status(500).json({
395+
error: error instanceof Error ? error.message : 'Unknown error',
396+
stack: error instanceof Error ? error.stack : undefined
397+
})
398+
}
399+
})
400+
401+
/**
402+
* Generate troubleshooting recommendations based on the discovery results
403+
*/
404+
function generateRecommendations(
405+
step1: any,
406+
providerDetails: any[],
407+
envConfig: any,
408+
currentStatus: any[]
409+
): string[] {
410+
const recommendations: string[] = []
411+
412+
// Check if OBP API is reachable
413+
if (!step1.success) {
414+
recommendations.push(
415+
'❌ OBP API well-known endpoint is not reachable. Check that VITE_OBP_API_HOST is correct and the API server is running.'
416+
)
417+
recommendations.push(` Current endpoint: ${step1.endpoint}`)
418+
if (step1.error) {
419+
recommendations.push(` Error: ${step1.error}`)
420+
}
421+
} else {
422+
recommendations.push('✅ OBP API well-known endpoint is reachable')
423+
}
424+
425+
// Check if any providers were discovered
426+
if (step1.providers.length === 0) {
427+
recommendations.push(
428+
'⚠️ No OIDC providers found in OBP API response. The OBP API may not have any providers configured.'
429+
)
430+
} else {
431+
recommendations.push(`✅ Found ${step1.providers.length} provider(s) from OBP API`)
432+
}
433+
434+
// Check each provider's configuration
435+
providerDetails.forEach((provider) => {
436+
if (!provider.success) {
437+
recommendations.push(
438+
`❌ Provider '${provider.providerName}' OIDC configuration failed to load`
439+
)
440+
recommendations.push(` Well-known URL: ${provider.wellKnownUrl}`)
441+
if (provider.error) {
442+
recommendations.push(` Error: ${provider.error}`)
443+
}
444+
recommendations.push(
445+
` Check that the provider's well-known endpoint is accessible and returning valid JSON`
446+
)
447+
} else {
448+
recommendations.push(
449+
`✅ Provider '${provider.providerName}' OIDC configuration loaded successfully`
450+
)
451+
}
452+
453+
// Check if provider has environment credentials
454+
const envKey = provider.providerName.replace('-', '')
455+
const providerEnv = envConfig[envKey] || envConfig[provider.providerName]
456+
if (providerEnv && !providerEnv.configured) {
457+
recommendations.push(
458+
`⚠️ Provider '${provider.providerName}' is missing environment credentials`
459+
)
460+
const upperName = provider.providerName.toUpperCase().replace('-', '_')
461+
recommendations.push(` Set VITE_${upperName}_CLIENT_ID and VITE_${upperName}_CLIENT_SECRET`)
462+
}
463+
})
464+
465+
// Check current provider status
466+
currentStatus.forEach((status) => {
467+
if (!status.available) {
468+
recommendations.push(`⚠️ Provider '${status.name}' is currently unavailable`)
469+
if (status.error) {
470+
recommendations.push(` Error: ${status.error}`)
471+
}
472+
}
473+
})
474+
475+
if (recommendations.length === 0) {
476+
recommendations.push('✅ All checks passed! OIDC configuration looks good.')
477+
}
478+
479+
return recommendations
480+
}
481+
221482
export default router

src/components/HeaderNav.vue

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ const logoffurl = ref('')
5252
const obpApiVersions = ref(inject(obpApiActiveVersionsKey) || [])
5353
const obpMessageDocs = ref(Object.keys(inject(obpGroupedMessageDocsKey) || {}))
5454
55+
// Debug menu items
56+
const debugMenuItems = ref(['/debug/providers-status', '/debug/oidc'])
57+
5558
// Split versions into main and other
5659
const mainVersions = ['BGv1.3', 'OBPv5.1.0', 'OBPv6.0.0', 'UKv3.1', 'dynamic-endpoints', 'dynamic-entities', 'OBPdynamic-endpoint', 'OBPdynamic-entity']
5760
const sortedVersions = computed(() => {
@@ -205,6 +208,9 @@ const handleMore = (command: string) => {
205208
} else if (command.includes('_')) {
206209
console.log('Navigating to message docs:', command)
207210
router.push({ name: 'message-docs', params: { id: command } })
211+
} else if (command.startsWith('/debug/')) {
212+
console.log('Navigating to debug page:', command)
213+
router.push(command)
208214
} else {
209215
console.log('Navigating to resource docs:', `/resource-docs/${command}`)
210216
console.log('Current route:', route.path)
@@ -291,6 +297,15 @@ const getCurrentPath = () => {
291297
:background-color="headerLinksBackgroundColor"
292298
@select="handleMore"
293299
/>
300+
<SvelteDropdown
301+
class="menu-right"
302+
id="header-nav-debug"
303+
label="Debug"
304+
:items="debugMenuItems"
305+
:hover-color="headerLinksHoverColor"
306+
:background-color="headerLinksBackgroundColor"
307+
@select="handleMore"
308+
/>
294309
<!--<span class="el-dropdown-link">
295310
<RouterLink class="router-link" id="header-nav-spaces" to="/spaces">{{
296311
$t('header.spaces')
@@ -494,7 +509,8 @@ button.login-button-disabled {
494509
495510
/* Custom dropdown containers */
496511
#header-nav-versions,
497-
#header-nav-message-docs {
512+
#header-nav-message-docs,
513+
#header-nav-debug {
498514
display: inline-block;
499515
vertical-align: middle;
500516
}

src/router/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import APIServerStatusView from '../views/APIServerStatusView.vue'
4040
import { isServerUp, OBP_API_DEFAULT_RESOURCE_DOC_VERSION } from '../obp'
4141
import MessageDocsContent from '@/components/CodeBlock.vue'
4242
import ProvidersStatusView from '../views/ProvidersStatusView.vue'
43+
import OIDCDebugView from '../views/OIDCDebugView.vue'
4344

4445
export default async function router(): Promise<any> {
4546
const isServerActive = await isServerUp()
@@ -60,6 +61,11 @@ export default async function router(): Promise<any> {
6061
name: 'providers-status',
6162
component: ProvidersStatusView
6263
},
64+
{
65+
path: '/debug/oidc',
66+
name: 'oidc-debug',
67+
component: OIDCDebugView
68+
},
6369
{
6470
path: '/glossary',
6571
name: 'glossary',

0 commit comments

Comments
 (0)