Skip to content

Commit 3dadca8

Browse files
committed
Add multi-OIDC provider backend services
- Add TypeScript interfaces for multi-provider OAuth2 support - Create OAuth2ClientWithConfig extending arctic OAuth2Client with OIDC discovery - Create OAuth2ProviderFactory with strategy pattern for different providers - Create OAuth2ProviderManager for managing multiple providers with health checks - Support for OBP-OIDC, Keycloak, Google, GitHub, and custom providers
1 parent 100a79c commit 3dadca8

7 files changed

Lines changed: 3904 additions & 0 deletions

MULTI-OIDC-FLOW-DIAGRAM.md

Lines changed: 577 additions & 0 deletions
Large diffs are not rendered by default.

MULTI-OIDC-PROVIDER-IMPLEMENTATION.md

Lines changed: 1917 additions & 0 deletions
Large diffs are not rendered by default.

MULTI-OIDC-PROVIDER-SUMMARY.md

Lines changed: 372 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,372 @@
1+
# Multi-OIDC Provider Implementation - Executive Summary
2+
3+
## Overview
4+
5+
This document provides a high-level summary of implementing multiple OIDC provider support in API Explorer II, based on the proven architecture from OBP-Portal.
6+
7+
---
8+
9+
## Current State
10+
11+
**API Explorer II** currently supports OAuth2/OIDC authentication with a **single provider** configured via environment variables:
12+
13+
```bash
14+
VITE_OBP_OAUTH2_WELL_KNOWN_URL=http://localhost:9000/obp-oidc/.well-known/openid-configuration
15+
VITE_OBP_OAUTH2_CLIENT_ID=<client-id>
16+
VITE_OBP_OAUTH2_CLIENT_SECRET=<client-secret>
17+
```
18+
19+
**Limitations:**
20+
- Only one OIDC provider supported at a time
21+
- No user choice of authentication method
22+
- Requires redeployment to switch providers
23+
- No fallback if provider is unavailable
24+
25+
---
26+
27+
## Target State
28+
29+
**Multi-Provider Support** allows users to choose from multiple identity providers at login:
30+
31+
- **OBP-OIDC** - Open Bank Project's identity provider
32+
- **Keycloak** - Enterprise identity management
33+
- **Google** - Consumer identity (optional)
34+
- **GitHub** - Developer identity (optional)
35+
- **Custom** - Any OpenID Connect provider
36+
37+
---
38+
39+
## How OBP-Portal Does It
40+
41+
### 1. Dynamic Provider Discovery
42+
43+
OBP-Portal fetches available OIDC providers from the **OBP API**:
44+
45+
```
46+
GET /obp/v5.1.0/well-known
47+
```
48+
49+
**Response:**
50+
```json
51+
{
52+
"well_known_uris": [
53+
{
54+
"provider": "obp-oidc",
55+
"url": "http://localhost:9000/obp-oidc/.well-known/openid-configuration"
56+
},
57+
{
58+
"provider": "keycloak",
59+
"url": "http://localhost:8180/realms/obp/.well-known/openid-configuration"
60+
}
61+
]
62+
}
63+
```
64+
65+
### 2. Provider Manager
66+
67+
**Key Component:** `OAuth2ProviderManager`
68+
69+
**Responsibilities:**
70+
- Fetch well-known URIs from OBP API
71+
- Initialize OAuth2 client for each provider
72+
- Track provider health (available/unavailable)
73+
- Perform periodic health checks (60s intervals)
74+
- Provide access to specific providers
75+
76+
### 3. Provider Factory
77+
78+
**Key Component:** `OAuth2ProviderFactory`
79+
80+
**Responsibilities:**
81+
- Strategy pattern for provider-specific configuration
82+
- Load credentials from environment variables
83+
- Create OAuth2 clients with OIDC discovery
84+
- Support multiple provider types
85+
86+
**Strategy Pattern:**
87+
```typescript
88+
strategies.set('obp-oidc', {
89+
clientId: process.env.VITE_OBP_OAUTH2_CLIENT_ID,
90+
clientSecret: process.env.VITE_OBP_OAUTH2_CLIENT_SECRET,
91+
redirectUri: process.env.VITE_OBP_OAUTH2_REDIRECT_URL
92+
})
93+
94+
strategies.set('keycloak', {
95+
clientId: process.env.VITE_KEYCLOAK_CLIENT_ID,
96+
clientSecret: process.env.VITE_KEYCLOAK_CLIENT_SECRET,
97+
redirectUri: process.env.VITE_KEYCLOAK_REDIRECT_URL
98+
})
99+
```
100+
101+
### 4. User Flow
102+
103+
```
104+
1. User clicks "Login"
105+
→ Shows provider selection dialog
106+
107+
2. User selects provider (e.g., "OBP-OIDC")
108+
→ GET /api/oauth2/connect?provider=obp-oidc
109+
110+
3. Server:
111+
- Retrieves OAuth2 client for "obp-oidc"
112+
- Generates PKCE parameters
113+
- Stores provider name in session
114+
- Redirects to provider's authorization endpoint
115+
116+
4. User authenticates on selected OIDC provider
117+
118+
5. Provider redirects back:
119+
→ GET /api/oauth2/callback?code=xxx&state=yyy
120+
121+
6. Server:
122+
- Retrieves provider from session ("obp-oidc")
123+
- Gets corresponding OAuth2 client
124+
- Exchanges code for tokens
125+
- Stores tokens with provider name
126+
127+
7. User authenticated with selected provider
128+
```
129+
130+
---
131+
132+
## Implementation Architecture for API Explorer II
133+
134+
### New Services
135+
136+
#### 1. **OAuth2ClientWithConfig** (extends `OAuth2Client` from arctic)
137+
```typescript
138+
class OAuth2ClientWithConfig extends OAuth2Client {
139+
public OIDCConfig?: OIDCConfiguration
140+
public provider: string
141+
142+
async initOIDCConfig(oidcConfigUrl: string): Promise<void>
143+
getAuthorizationEndpoint(): string
144+
getTokenEndpoint(): string
145+
getUserInfoEndpoint(): string
146+
}
147+
```
148+
149+
#### 2. **OAuth2ProviderFactory**
150+
```typescript
151+
class OAuth2ProviderFactory {
152+
private strategies: Map<string, ProviderStrategy>
153+
154+
async initializeProvider(wellKnownUri: WellKnownUri): Promise<OAuth2ClientWithConfig>
155+
getConfiguredProviders(): string[]
156+
}
157+
```
158+
159+
#### 3. **OAuth2ProviderManager**
160+
```typescript
161+
class OAuth2ProviderManager {
162+
private providers: Map<string, OAuth2ClientWithConfig>
163+
164+
async fetchWellKnownUris(): Promise<WellKnownUri[]>
165+
async initializeProviders(): Promise<boolean>
166+
getProvider(providerName: string): OAuth2ClientWithConfig
167+
getAvailableProviders(): string[]
168+
startHealthCheck(intervalMs: number): void
169+
}
170+
```
171+
172+
### Updated Controllers
173+
174+
#### 1. **OAuth2ProvidersController** (NEW)
175+
```typescript
176+
GET /api/oauth2/providers
177+
Returns: { providers: [...], count: 2, availableCount: 1 }
178+
```
179+
180+
#### 2. **OAuth2ConnectController** (UPDATED)
181+
```typescript
182+
GET /api/oauth2/connect?provider=obp-oidc&redirect=/resource-docs
183+
Redirects to selected provider's authorization endpoint
184+
```
185+
186+
#### 3. **OAuth2CallbackController** (UPDATED)
187+
```typescript
188+
GET /api/oauth2/callback?code=xxx&state=yyy
189+
Uses provider from session to exchange code for tokens
190+
```
191+
192+
### Frontend Updates
193+
194+
#### **HeaderNav.vue** (UPDATED)
195+
196+
**Before:**
197+
```vue
198+
<a href="/api/oauth2/connect">Login</a>
199+
```
200+
201+
**After:**
202+
```vue
203+
<button @click="handleLoginClick">
204+
Login
205+
<span v-if="availableProviders.length > 1"></span>
206+
</button>
207+
208+
<!-- Provider Selection Dialog -->
209+
<el-dialog v-model="showProviderSelector">
210+
<div v-for="provider in availableProviders">
211+
<div @click="loginWithProvider(provider.name)">
212+
{{ provider.name }}
213+
</div>
214+
</div>
215+
</el-dialog>
216+
```
217+
218+
---
219+
220+
## Configuration
221+
222+
### Environment Variables
223+
224+
```bash
225+
# OBP-OIDC Provider
226+
VITE_OBP_OAUTH2_CLIENT_ID=48ac28e9-9ee3-47fd-8448-69a62764b779
227+
VITE_OBP_OAUTH2_CLIENT_SECRET=fOTQF7jfg8C74u7ZhSjVQpoBYvD0KpWfM5UsEZBSFFM
228+
VITE_OBP_OAUTH2_REDIRECT_URL=http://localhost:5173/api/oauth2/callback
229+
230+
# Keycloak Provider
231+
VITE_KEYCLOAK_CLIENT_ID=obp-api-explorer
232+
VITE_KEYCLOAK_CLIENT_SECRET=your-keycloak-secret
233+
VITE_KEYCLOAK_REDIRECT_URL=http://localhost:5173/api/oauth2/callback
234+
235+
# Google Provider (Optional)
236+
VITE_GOOGLE_CLIENT_ID=your-google-client-id
237+
VITE_GOOGLE_CLIENT_SECRET=your-google-client-secret
238+
VITE_GOOGLE_REDIRECT_URL=http://localhost:5173/api/oauth2/callback
239+
```
240+
241+
**Note:** No need to specify well-known URLs - they are fetched from OBP API!
242+
243+
---
244+
245+
## Key Benefits
246+
247+
### 1. **Dynamic Discovery**
248+
- Providers are discovered from OBP API at runtime
249+
- No hardcoded provider list
250+
- Easy to add new providers without code changes
251+
252+
### 2. **User Choice**
253+
- Users select their preferred authentication method
254+
- Better user experience
255+
- Support for organizational identity preferences
256+
257+
### 3. **Resilience**
258+
- Health monitoring detects provider outages
259+
- Can fallback to alternative providers
260+
- Automatic retry for failed initializations
261+
262+
### 4. **Extensibility**
263+
- Strategy pattern makes adding providers trivial
264+
- Just add environment variables
265+
- No code changes needed
266+
267+
### 5. **Backward Compatibility**
268+
- Existing single-provider mode still works
269+
- Gradual migration path
270+
- No breaking changes
271+
272+
---
273+
274+
## Implementation Phases
275+
276+
### **Phase 1: Backend Services** (Week 1)
277+
- [ ] Create `OAuth2ClientWithConfig`
278+
- [ ] Create `OAuth2ProviderFactory`
279+
- [ ] Create `OAuth2ProviderManager`
280+
- [ ] Create TypeScript interfaces
281+
282+
### **Phase 2: Backend Controllers** (Week 1-2)
283+
- [ ] Create `OAuth2ProvidersController`
284+
- [ ] Update `OAuth2ConnectController` with provider parameter
285+
- [ ] Update `OAuth2CallbackController` to use provider from session
286+
287+
### **Phase 3: Frontend** (Week 2)
288+
- [ ] Update `HeaderNav.vue` to fetch providers
289+
- [ ] Add provider selection UI (dialog/dropdown)
290+
- [ ] Update login flow to include provider selection
291+
292+
### **Phase 4: Configuration & Testing** (Week 2-3)
293+
- [ ] Configure environment variables for multiple providers
294+
- [ ] Write unit tests
295+
- [ ] Write integration tests
296+
- [ ] Manual testing with OBP-OIDC and Keycloak
297+
- [ ] Update documentation
298+
299+
---
300+
301+
## Migration Path
302+
303+
### **Step 1: Deploy with Backward Compatibility**
304+
- Implement new services
305+
- Keep existing single-provider mode working
306+
- Test thoroughly
307+
308+
### **Step 2: Enable Multi-Provider**
309+
- Add provider environment variables
310+
- Enable provider selection UI
311+
- Monitor for issues
312+
313+
### **Step 3: Deprecate Single-Provider**
314+
- Update documentation
315+
- Remove `VITE_OBP_OAUTH2_WELL_KNOWN_URL` env variable
316+
- Use OBP API well-known endpoint by default
317+
318+
---
319+
320+
## Testing Strategy
321+
322+
### Unit Tests
323+
- `OAuth2ProviderFactory.test.ts` - Strategy creation
324+
- `OAuth2ProviderManager.test.ts` - Provider initialization
325+
- `OAuth2ClientWithConfig.test.ts` - OIDC config loading
326+
327+
### Integration Tests
328+
- Multi-provider login flow
329+
- Provider selection
330+
- Token exchange with different providers
331+
- Callback handling
332+
333+
### Manual Testing
334+
- Login with OBP-OIDC
335+
- Login with Keycloak
336+
- Provider unavailable scenarios
337+
- Network error handling
338+
- User cancellation
339+
340+
---
341+
342+
## Success Criteria
343+
344+
- ✅ Users can choose from multiple OIDC providers
345+
- ✅ Providers are discovered from OBP API automatically
346+
- ✅ Health monitoring detects provider outages
347+
- ✅ Backward compatible with single-provider mode
348+
- ✅ No code changes needed to add new providers (only env vars)
349+
- ✅ Comprehensive test coverage (>80%)
350+
- ✅ Documentation updated
351+
352+
---
353+
354+
## References
355+
356+
- **Full Implementation Guide:** `MULTI-OIDC-PROVIDER-IMPLEMENTATION.md`
357+
- **OBP-Portal Reference:** `~/Documents/workspace_2024/OBP-Portal`
358+
- **OBP API Well-Known Endpoint:** `/obp/v5.1.0/well-known`
359+
- **Current OAuth2 Docs:** `OAUTH2-README.md`, `OAUTH2-OIDC-INTEGRATION-PREP.md`
360+
- **Arctic OAuth2 Library:** https://github.com/pilcrowOnPaper/arctic
361+
- **OpenID Connect Discovery:** https://openid.net/specs/openid-connect-discovery-1_0.html
362+
363+
---
364+
365+
## Questions?
366+
367+
For detailed implementation instructions, see **MULTI-OIDC-PROVIDER-IMPLEMENTATION.md**
368+
369+
For OBP-Portal reference implementation, see:
370+
- `OBP-Portal/src/lib/oauth/providerManager.ts`
371+
- `OBP-Portal/src/lib/oauth/providerFactory.ts`
372+
- `OBP-Portal/src/lib/oauth/client.ts`

0 commit comments

Comments
 (0)