diff --git a/OAUTH2-OIDC-INTEGRATION-PREP.md b/OAUTH2-OIDC-INTEGRATION-PREP.md new file mode 100644 index 0000000..8240ed7 --- /dev/null +++ b/OAUTH2-OIDC-INTEGRATION-PREP.md @@ -0,0 +1,2369 @@ +# OAuth2/OIDC Integration Preparation Document + +## API Explorer II with OBP-OIDC + +**Version:** 1.0 +**Date:** 2024 +**Author:** TESOBE Development Team + +--- + +## Table of Contents + +1. [Executive Summary](#executive-summary) +2. [Current State Analysis](#current-state-analysis) +3. [Target Architecture](#target-architecture) +4. [OBP-OIDC Overview](#obp-oidc-overview) +5. [OBP-Portal Reference Implementation](#obp-portal-reference-implementation) +6. [Implementation Phases](#implementation-phases) +7. [Technical Requirements](#technical-requirements) +8. [Configuration Changes](#configuration-changes) +9. [Code Changes Required](#code-changes-required) +10. [Testing Strategy](#testing-strategy) +11. [Deployment Considerations](#deployment-considerations) +12. [Rollback Plan](#rollback-plan) +13. [References](#references) + +--- + +## 1. Executive Summary + +This document outlines the preparation and implementation plan for migrating **API Explorer II** from OAuth 1.0a authentication to OAuth2/OpenID Connect (OIDC) using the **OBP-OIDC** provider. The OBP-Portal serves as the reference implementation for this integration. + +### Key Goals + +- Replace OAuth 1.0a (`oauth` npm package) with OAuth2/OIDC +- Integrate with OBP-OIDC provider for authentication +- Maintain backward compatibility during transition +- Follow OBP-Portal's proven implementation patterns +- Support multiple OIDC providers (OBP-OIDC, Keycloak, etc.) + +### Benefits + +- **Modern Authentication**: OAuth2/OIDC is the industry standard +- **Better Security**: Improved token management and refresh mechanisms +- **User Experience**: Single sign-on capabilities +- **Maintainability**: Aligns with OBP ecosystem (Portal, API) +- **Flexibility**: Support for multiple identity providers + +--- + +## 2. Current State Analysis + +### 2.1 Current Authentication Flow (OAuth 1.0a) + +API Explorer II currently uses OAuth 1.0a with the following flow: + +``` +User → /api/connect → OauthRequestTokenMiddleware + → OBP-API /oauth/authorize + → User Login/Authorization + → /api/callback → OauthAccessTokenMiddleware + → Session Storage → Authenticated State +``` + +### 2.2 Current Implementation Components + +#### Backend (Express/TypeScript) + +- **Package**: `oauth` (v0.10.0) - OAuth 1.0a library +- **Middlewares**: + - `OauthRequestTokenMiddleware.ts` - Handles request token generation + - `OauthAccessTokenMiddleware.ts` - Handles access token exchange +- **Services**: + - `OauthInjectedService.ts` - OAuth consumer management + - `OBPClientService.ts` - OBP API client wrapper +- **Controllers**: + - `ConnectController.ts` - Initiates OAuth flow + - `CallbackController.ts` - Handles OAuth callback +- **Session Management**: Express-session with Redis store + +#### Frontend (Vue 3) + +- **Login Flow**: Redirects to `/api/connect?redirect=` +- **User State**: Fetched via `getCurrentUser()` API call +- **Components**: + - `HeaderNav.vue` - Login/Logoff buttons + - `ChatWidget.vue` - Checks authentication for Opey access + - `Preview.vue` - Shows login prompts for restricted features + +#### Environment Configuration + +```bash +VITE_OBP_CONSUMER_KEY= +VITE_OBP_CONSUMER_SECRET= +VITE_OBP_REDIRECT_URL=http://localhost:5173/api/callback +VITE_OBP_API_HOST=http://127.0.0.1:8080 +``` + +### 2.3 Current Limitations + +1. **OAuth 1.0a is deprecated** - Industry has moved to OAuth2 +2. **No refresh token support** - Users must re-authenticate frequently +3. **Single provider only** - Cannot support multiple identity providers +4. **Complex signature generation** - OAuth 1.0a requires HMAC-SHA1 signatures +5. **Limited ecosystem support** - Most modern services use OAuth2/OIDC + +--- + +## 3. Target Architecture + +### 3.1 OAuth2/OIDC Flow + +``` +User → Login Button → /login/obp → OAuth2 Authorization Endpoint (OBP-OIDC) + → User Authentication (OBP-OIDC Login Page) + → Authorization Code → /login/obp/callback + → Token Exchange → Access Token + Refresh Token + ID Token + → Session Storage → Authenticated State +``` + +### 3.2 Key Differences from Current Implementation + +| Aspect | OAuth 1.0a (Current) | OAuth2/OIDC (Target) | +| ------------------ | --------------------- | --------------------------------------- | +| **Flow** | 3-legged OAuth | Authorization Code Flow with PKCE | +| **Tokens** | Access Token + Secret | Access Token + Refresh Token + ID Token | +| **Token Type** | Opaque strings | JWT (JSON Web Tokens) | +| **Signatures** | HMAC-SHA1 per request | Bearer token in header | +| **User Info** | From API calls | From ID token claims | +| **Refresh** | Not supported | Refresh token flow | +| **Discovery** | Manual configuration | `.well-known/openid-configuration` | +| **Multi-Provider** | No | Yes (via provider manager) | + +--- + +## 4. OBP-OIDC Overview + +### 4.1 About OBP-OIDC + +**OBP-OIDC** is a bare-bones OpenID Connect provider built with http4s and functional programming in Scala. It's designed specifically for the OBP ecosystem. + +- **Repository**: `~/Documents/workspace_2024/OBP-OIDC` +- **Technology**: Scala, http4s, PostgreSQL +- **Purpose**: Development/testing OIDC provider for OBP apps +- **Production Note**: For production, use Keycloak or Hydra + +### 4.2 OBP-OIDC Features + +✅ **Core OIDC Endpoints**: + +- Authorization endpoint: `/obp-oidc/auth` +- Token endpoint: `/obp-oidc/token` +- UserInfo endpoint: `/obp-oidc/userinfo` +- JWKS endpoint: `/obp-oidc/jwks` +- Discovery: `/obp-oidc/.well-known/openid-configuration` + +✅ **Supported Flows**: + +- Authorization Code Flow +- Client Credentials Flow +- Refresh Token Flow + +✅ **Database Integration**: + +- Reads from OBP database views +- Authenticates against OBP users (`v_oidc_users`) +- Manages clients via `v_oidc_clients` and `v_oidc_admin_clients` + +✅ **Security Features**: + +- RS256 JWT signing +- BCrypt password verification +- Configurable token expiration +- PKCE support (recommended) + +### 4.3 OBP-OIDC Configuration + +Default configuration from `run-server.example.sh`: + +```bash +# Server Configuration +OIDC_HOST=localhost +OIDC_PORT=9000 + +# Explorer II Client (Pre-configured) +OIDC_CLIENT_EXPLORER_ID=obp-explorer-ii-client +OIDC_CLIENT_EXPLORER_SECRET=CHANGE_THIS_TO_EXPLORER_SECRET_2024 +OIDC_CLIENT_EXPLORER_REDIRECTS=http://localhost:3001/callback,http://localhost:3001/oauth/callback + +# Database +DB_HOST=localhost +DB_PORT=5432 +DB_NAME=sandbox +OIDC_USER_USERNAME=oidc_user +OIDC_USER_PASSWORD= +``` + +### 4.4 OBP-OIDC Endpoints + +**Discovery Document**: + +``` +GET http://localhost:9000/obp-oidc/.well-known/openid-configuration +``` + +**Authorization**: + +``` +GET http://localhost:9000/obp-oidc/auth + ?response_type=code + &client_id=obp-explorer-ii-client + &redirect_uri=http://localhost:5173/login/obp/callback + &scope=openid%20profile%20email + &state= + &code_challenge= + &code_challenge_method=S256 +``` + +**Token Exchange**: + +``` +POST http://localhost:9000/obp-oidc/token +Content-Type: application/x-www-form-urlencoded +Authorization: Basic + +grant_type=authorization_code +&code= +&redirect_uri=http://localhost:5173/login/obp/callback +&code_verifier= +``` + +**UserInfo**: + +``` +GET http://localhost:9000/obp-oidc/userinfo +Authorization: Bearer +``` + +--- + +## 5. OBP-Portal Reference Implementation + +### 5.1 OBP-Portal Architecture + +The OBP-Portal (`~/Documents/workspace_2024/OBP-Portal`) provides the reference implementation using: + +- **Framework**: SvelteKit (but patterns are framework-agnostic) +- **OAuth2 Library**: `arctic` - Modern OAuth2/OIDC client +- **Session Management**: `svelte-kit-sessions` with Redis +- **Language**: TypeScript + +### 5.2 Key Components from OBP-Portal + +#### 5.2.1 OAuth2 Client (`src/lib/oauth/client.ts`) + +```typescript +import { OAuth2Client } from 'arctic' + +export class OAuth2ClientWithConfig extends OAuth2Client { + OIDCConfig?: OpenIdConnectConfiguration + + async initOIDCConfig(OIDCConfigUrl: string): Promise { + // Fetches .well-known/openid-configuration + const response = await fetch(OIDCConfigUrl) + const config = await response.json() + this.OIDCConfig = config + } + + async validateAuthorizationCode( + tokenEndpoint: string, + code: string, + codeVerifier: string | null + ): Promise { + // Handles token exchange with Basic Auth + // Falls back to credentials in body if needed + // Supports both OBP-OIDC and Keycloak + } +} +``` + +**Key Features**: + +- Automatic OIDC discovery document parsing +- Basic Authentication for client credentials (RFC 6749) +- Fallback to form-based credentials for compatibility +- Token expiration checking via JWT decoding + +#### 5.2.2 Provider Manager (`src/lib/oauth/providerManager.ts`) + +```typescript +class OAuth2ProviderManager { + async fetchWellKnownUris(): Promise { + // Fetches from OBP API: /obp/v5.1.0/well-known + const response = await obp_requests.get('/obp/v5.1.0/well-known') + return response.well_known_uris + } + + async initOauth2Providers() { + const wellKnownUris = await this.fetchWellKnownUris() + + for (const providerUri of wellKnownUris) { + const oauth2Client = await oauth2ProviderFactory.initializeProvider(providerUri) + // Track available/unavailable providers + } + } +} +``` + +**Key Features**: + +- Automatic provider discovery from OBP API +- Multi-provider support (OBP-OIDC, Keycloak, etc.) +- Provider status tracking (available/unavailable) +- Automatic retry on initialization failure +- Periodic health checks (60 second intervals) + +#### 5.2.3 Provider Factory (`src/lib/oauth/providerFactory.ts`) + +```typescript +class OAuth2ProviderFactory { + private strategies: Map + + async initializeProvider(wellKnownUri: WellKnownUri): Promise { + const strategy = this.strategies.get(wellKnownUri.provider) + if (!strategy) return null + + const client = new OAuth2ClientWithConfig( + strategy.clientId, + strategy.clientSecret, + strategy.redirectUri, + wellKnownUri.provider + ) + + await client.initOIDCConfig(wellKnownUri.url) + return client + } +} +``` + +**Key Features**: + +- Strategy pattern for different providers +- Supports: OBP-OIDC, Keycloak, Google, GitHub, etc. +- Extensible for new providers + +#### 5.2.4 Session Helper (`src/lib/oauth/sessionHelper.ts`) + +```typescript +export class SessionOAuthHelper { + static async handleCallback( + session: Session, + code: string, + state: string, + provider: string + ): Promise { + // Validate state + // Exchange code for tokens + // Store tokens in session + // Fetch user info + } + + static async refreshAccessToken(session: Session, provider: string): Promise { + // Use refresh token to get new access token + // Update session with new tokens + } +} +``` + +### 5.3 OBP-Portal Configuration (`.env.example`) + +```bash +# OIDC Configuration +OBP_OAUTH_CLIENT_ID=2a47cc56-0db1-409d-8f0b-131e4f94a212 +OBP_OAUTH_CLIENT_SECRET=OR04Ga8uQjdzFmNXDI5QrEXHN30Fk4u6lbtbrrMsj8g +OBP_OAUTH_WELL_KNOWN_URL=http://127.0.0.1:9000/obp-oidc/.well-known/openid-configuration +APP_CALLBACK_URL=http://localhost:5174/login/obp/callback + +# OBP API Configuration +PUBLIC_OBP_BASE_URL=http://localhost:8080 +OBP_API_HOST=localhost:8080 +OBP_API_URL=http://localhost:8080 + +# Redis Configuration +REDIS_HOST=localhost +REDIS_PORT=6379 +``` + +### 5.4 OBP-Portal Login Flow + +``` +1. User clicks "Login" → Navigate to /login/obp +2. Server-side: + - Generate PKCE challenge/verifier + - Generate state parameter + - Store in session + - Redirect to OBP-OIDC /auth endpoint +3. User authenticates on OBP-OIDC +4. OBP-OIDC redirects to /login/obp/callback?code=XXX&state=YYY +5. Server-side: + - Validate state + - Exchange code for tokens (with PKCE verifier) + - Store tokens in session + - Fetch user info from /userinfo + - Store user info in session + - Redirect to application +6. Client-side: Authenticated state +``` + +### 5.5 OBP-Portal Hooks Implementation + +From `src/hooks.server.ts`: + +```typescript +// Initialize OAuth providers on startup +await oauth2ProviderManager.start() + +// Check session validity for protected routes +const checkSessionValidity: Handle = async ({ event, resolve }) => { + const session = event.locals.session + + if (session.data.user) { + // Check if access token is expired + const isExpired = await checkAccessTokenExpiration(session.data.accessToken) + + if (isExpired && session.data.refreshToken) { + // Attempt to refresh + await SessionOAuthHelper.refreshAccessToken(session, session.data.provider) + } + } + + return resolve(event) +} +``` + +--- + +## 6. Implementation Phases + +### Phase 1: Preparation & Setup (Week 1) + +**Goals**: Set up OBP-OIDC and understand integration requirements + +**Tasks**: + +1. ✅ Set up OBP-OIDC server locally + + - Install dependencies (Java 11+, Maven, PostgreSQL) + - Configure database views (see OBP-API scripts) + - Copy `run-server.example.sh` to `run-server.sh` + - Update database credentials + - Start OIDC server: `./run-server.sh` + - Verify: `curl http://localhost:9000/obp-oidc/.well-known/openid-configuration` + +2. ✅ Register API Explorer II client in OBP-OIDC + + - Update `run-server.sh`: + ```bash + export OIDC_CLIENT_EXPLORER_ID=obp-explorer-ii-client + export OIDC_CLIENT_EXPLORER_SECRET= + export OIDC_CLIENT_EXPLORER_REDIRECTS=http://localhost:5173/login/obp/callback + ``` + - Restart OBP-OIDC + - Copy client credentials + +3. ✅ Study OBP-Portal implementation + + - Review OAuth2 client implementation + - Understand provider manager pattern + - Study session management approach + - Document key differences from current implementation + +4. ⬜ Add OAuth2 dependencies + ```bash + npm install arctic jsonwebtoken @types/jsonwebtoken + ``` + - `arctic`: Modern OAuth2/OIDC client library + - `jsonwebtoken`: JWT parsing and validation + +**Deliverables**: + +- Running OBP-OIDC instance +- API Explorer II client registered +- Dependencies added to `package.json` +- This preparation document + +--- + +### Phase 2: Backend OAuth2 Implementation (Week 2-3) + +**Goals**: Implement OAuth2/OIDC backend while maintaining OAuth 1.0a + +**Tasks**: + +#### 2.1 Create OAuth2 Service Layer + +**File**: `server/services/OAuth2Service.ts` + +```typescript +import { OAuth2Client } from 'arctic' +import { Service } from 'typedi' + +export interface OIDCConfiguration { + issuer: string + authorization_endpoint: string + token_endpoint: string + userinfo_endpoint: string + jwks_uri: string +} + +@Service() +export class OAuth2Service { + private client: OAuth2Client + private oidcConfig: OIDCConfiguration | null = null + + constructor() { + const clientId = process.env.VITE_OBP_OAUTH2_CLIENT_ID + const clientSecret = process.env.VITE_OBP_OAUTH2_CLIENT_SECRET + const redirectUri = process.env.VITE_OBP_OAUTH2_REDIRECT_URL + + this.client = new OAuth2Client(clientId, clientSecret, redirectUri) + } + + async initializeFromWellKnown(wellKnownUrl: string): Promise { + const response = await fetch(wellKnownUrl) + this.oidcConfig = await response.json() + } + + createAuthorizationURL(state: string, codeChallenge: string): URL { + if (!this.oidcConfig) { + throw new Error('OIDC configuration not initialized') + } + + return this.client.createAuthorizationURL(this.oidcConfig.authorization_endpoint, state, [ + 'openid', + 'profile', + 'email' + ]) + } + + async exchangeCodeForTokens(code: string, codeVerifier: string): Promise { + if (!this.oidcConfig) { + throw new Error('OIDC configuration not initialized') + } + + return await this.client.validateAuthorizationCode( + this.oidcConfig.token_endpoint, + code, + codeVerifier + ) + } + + async refreshAccessToken(refreshToken: string): Promise { + // Implement refresh token flow + } + + async getUserInfo(accessToken: string): Promise { + if (!this.oidcConfig) { + throw new Error('OIDC configuration not initialized') + } + + const response = await fetch(this.oidcConfig.userinfo_endpoint, { + headers: { + Authorization: `Bearer ${accessToken}` + } + }) + + return await response.json() + } +} +``` + +#### 2.2 Create PKCE Utility + +**File**: `server/utils/pkce.ts` + +```typescript +import crypto from 'crypto' + +export class PKCEUtils { + static generateCodeVerifier(): string { + return crypto.randomBytes(32).toString('base64url') + } + + static generateCodeChallenge(verifier: string): string { + return crypto.createHash('sha256').update(verifier).digest('base64url') + } + + static generateState(): string { + return crypto.randomBytes(32).toString('hex') + } +} +``` + +#### 2.3 Create OAuth2 Middlewares + +**File**: `server/middlewares/OAuth2AuthorizationMiddleware.ts` + +```typescript +import { ExpressMiddlewareInterface } from 'routing-controllers' +import { Request, Response } from 'express' +import { Service } from 'typedi' +import { OAuth2Service } from '../services/OAuth2Service' +import { PKCEUtils } from '../utils/pkce' + +@Service() +export default class OAuth2AuthorizationMiddleware implements ExpressMiddlewareInterface { + constructor(private oauth2Service: OAuth2Service) {} + + async use(request: Request, response: Response): Promise { + const session = request.session + const redirectPage = request.query.redirect + + // Store redirect page in session + if (redirectPage) { + session['redirectPage'] = redirectPage + } + + // Generate PKCE parameters + const codeVerifier = PKCEUtils.generateCodeVerifier() + const codeChallenge = PKCEUtils.generateCodeChallenge(codeVerifier) + const state = PKCEUtils.generateState() + + // Store in session for callback validation + session['oauth2_state'] = state + session['oauth2_code_verifier'] = codeVerifier + + // Create authorization URL + const authUrl = this.oauth2Service.createAuthorizationURL(state, codeChallenge) + + // Add PKCE challenge to URL + authUrl.searchParams.set('code_challenge', codeChallenge) + authUrl.searchParams.set('code_challenge_method', 'S256') + + console.log('Redirecting to OAuth2 authorization:', authUrl.toString()) + response.redirect(authUrl.toString()) + } +} +``` + +**File**: `server/middlewares/OAuth2CallbackMiddleware.ts` + +```typescript +import { ExpressMiddlewareInterface } from 'routing-controllers' +import { Request, Response } from 'express' +import { Service } from 'typedi' +import { OAuth2Service } from '../services/OAuth2Service' +import jwt from 'jsonwebtoken' + +@Service() +export default class OAuth2CallbackMiddleware implements ExpressMiddlewareInterface { + constructor(private oauth2Service: OAuth2Service) {} + + async use(request: Request, response: Response): Promise { + const session = request.session + const code = request.query.code as string + const state = request.query.state as string + + // Validate state + const storedState = session['oauth2_state'] + if (!state || state !== storedState) { + console.error('State validation failed') + return response.status(400).send('Invalid state parameter') + } + + // Get code verifier from session + const codeVerifier = session['oauth2_code_verifier'] + if (!codeVerifier) { + console.error('Code verifier not found in session') + return response.status(400).send('Invalid session state') + } + + try { + // Exchange authorization code for tokens + const tokens = await this.oauth2Service.exchangeCodeForTokens(code, codeVerifier) + + // Get user info + const userInfo = await this.oauth2Service.getUserInfo(tokens.accessToken) + + // Store in session + session['oauth2_access_token'] = tokens.accessToken + session['oauth2_refresh_token'] = tokens.refreshToken + session['oauth2_id_token'] = tokens.idToken + session['oauth2_user_info'] = userInfo + + // Decode ID token for user info + const idTokenPayload = jwt.decode(tokens.idToken) + session['oauth2_user'] = { + sub: idTokenPayload.sub, + email: idTokenPayload.email, + name: idTokenPayload.name, + username: idTokenPayload.preferred_username || idTokenPayload.sub + } + + // Clear OAuth2 flow parameters + delete session['oauth2_state'] + delete session['oauth2_code_verifier'] + + // Redirect to original page or home + const redirectPage = session['redirectPage'] || process.env.VITE_OBP_API_EXPLORER_HOST + delete session['redirectPage'] + + console.log('OAuth2 authentication successful, redirecting to:', redirectPage) + response.redirect(redirectPage) + } catch (error) { + console.error('OAuth2 token exchange failed:', error) + response.status(500).send('Authentication failed: ' + error.message) + } + } +} +``` + +#### 2.4 Create OAuth2 Controllers + +**File**: `server/controllers/OAuth2ConnectController.ts` + +```typescript +import { Controller, Req, Res, Get, UseBefore } from 'routing-controllers' +import { Request, Response } from 'express' +import { Service } from 'typedi' +import OAuth2AuthorizationMiddleware from '../middlewares/OAuth2AuthorizationMiddleware' + +@Service() +@Controller() +@UseBefore(OAuth2AuthorizationMiddleware) +export class OAuth2ConnectController { + @Get('/oauth2/connect') + connect(@Req() request: Request, @Res() response: Response): Response { + return response + } +} +``` + +**File**: `server/controllers/OAuth2CallbackController.ts` + +```typescript +import { Controller, Req, Res, Get, UseBefore } from 'routing-controllers' +import { Request, Response } from 'express' +import { Service } from 'typedi' +import OAuth2CallbackMiddleware from '../middlewares/OAuth2CallbackMiddleware' + +@Service() +@Controller() +@UseBefore(OAuth2CallbackMiddleware) +export class OAuth2CallbackController { + @Get('/oauth2/callback') + callback(@Req() request: Request, @Res() response: Response): Response { + return response + } +} +``` + +#### 2.5 Update Server Application + +**File**: `server/app.ts` + +Add OAuth2 service initialization: + +```typescript +import { OAuth2Service } from './services/OAuth2Service' +import Container from 'typedi' + +// Initialize OAuth2 service +const oauth2Service = Container.get(OAuth2Service) +const wellKnownUrl = process.env.VITE_OBP_OAUTH2_WELL_KNOWN_URL + +if (wellKnownUrl) { + oauth2Service + .initializeFromWellKnown(wellKnownUrl) + .then(() => { + console.log('OAuth2 service initialized from well-known URL:', wellKnownUrl) + }) + .catch((error) => { + console.error('Failed to initialize OAuth2 service:', error) + }) +} +``` + +#### 2.6 Add Current User Endpoint for OAuth2 + +**File**: `server/controllers/UserController.ts` + +Update to support both OAuth 1.0a and OAuth2: + +```typescript +@Get('/user/current') +async getCurrentUser(@Session() session: any, @Res() response: Response): Promise { + // Check OAuth2 session first + if (session['oauth2_user']) { + return response.json({ + username: session['oauth2_user'].username, + email: session['oauth2_user'].email, + name: session['oauth2_user'].name, + provider: 'oauth2' + }); + } + + // Fall back to OAuth 1.0a (existing code) + if (session['clientConfig']) { + // ... existing OAuth 1.0a logic + } + + return response.json({}); +} +``` + +**Deliverables**: + +- OAuth2Service with OIDC discovery +- PKCE utilities +- OAuth2 middlewares (authorization + callback) +- OAuth2 controllers +- Updated user endpoint supporting both auth methods + +--- + +### Phase 3: Environment Configuration (Week 3) + +**Goals**: Add OAuth2 configuration alongside OAuth 1.0a + +**File**: `env_ai` (update) + +Add new OAuth2 variables: + +```bash +### OAuth 1.0a Configuration (Legacy) ### +VITE_OBP_CONSUMER_KEY=0xzsimlrhdguiiuuj1ncykcxzjrogxibjff3dthl +VITE_OBP_CONSUMER_SECRET=ikf5wykke1oonykb33kmx3deh5ukbdak44ieg1l5 +VITE_OBP_REDIRECT_URL=http://localhost:5173/api/callback + +### OAuth2/OIDC Configuration (New) ### +# Set to 'true' to use OAuth2 instead of OAuth 1.0a +VITE_USE_OAUTH2=false + +# OAuth2 Client Credentials (from OBP-OIDC) +VITE_OBP_OAUTH2_CLIENT_ID=obp-explorer-ii-client +VITE_OBP_OAUTH2_CLIENT_SECRET= +VITE_OBP_OAUTH2_REDIRECT_URL=http://localhost:5173/oauth2/callback + +# OIDC Well-Known Configuration URL +VITE_OBP_OAUTH2_WELL_KNOWN_URL=http://127.0.0.1:9000/obp-oidc/.well-known/openid-configuration + +# Optional: Direct OIDC endpoints (if not using well-known) +# VITE_OBP_OAUTH2_AUTHORIZATION_URL=http://127.0.0.1:9000/obp-oidc/auth +# VITE_OBP_OAUTH2_TOKEN_URL=http://127.0.0.1:9000/obp-oidc/token +# VITE_OBP_OAUTH2_USERINFO_URL=http://127.0.0.1:9000/obp-oidc/userinfo + +### API Configuration ### +VITE_OBP_API_HOST=http://127.0.0.1:8080 +VITE_OBP_API_VERSION=v5.1.0 +VITE_OBP_API_EXPLORER_HOST=http://localhost:5173 +``` + +**Deliverables**: + +- Updated `env_ai` with OAuth2 configuration +- Documentation on environment variables + +--- + +### Phase 4: Frontend Updates (Week 4) + +**Goals**: Update frontend to support OAuth2 login flow + +#### 4.1 Update Login Links + +**File**: `src/components/HeaderNav.vue` + +Update login button to use OAuth2 when enabled: + +```vue + + + +``` + +#### 4.2 Update Logoff Handling + +**File**: `server/controllers/UserController.ts` + +```typescript +@Get('/user/logoff') +async logoff(@Session() session: any, @Req() request: Request, @Res() response: Response): Promise { + const redirectPage = request.query.redirect || process.env.VITE_OBP_API_EXPLORER_HOST; + + // Clear OAuth2 session + delete session['oauth2_access_token']; + delete session['oauth2_refresh_token']; + delete session['oauth2_id_token']; + delete session['oauth2_user_info']; + delete session['oauth2_user']; + + // Clear OAuth 1.0a session (backward compatibility) + delete session['clientConfig']; + + // Destroy session + session.destroy((err: any) => { + if (err) { + console.error('Error destroying session:', err); + } + }); + + return response.redirect(redirectPage); +} +``` + +#### 4.3 Add OAuth2 Status Indicator (Optional) + +Add visual indicator showing which auth method is active: + +```vue + + + OAuth2 + +``` + +**Deliverables**: + +- Updated login/logoff buttons +- OAuth2-aware user state management +- Optional status indicators + +--- + +### Phase 5: Testing (Week 5) + +**Goals**: Comprehensive testing of OAuth2 implementation + +#### 5.1 Unit Tests + +**File**: `server/test/OAuth2Service.test.ts` + +```typescript +import { OAuth2Service } from '../services/OAuth2Service' +import { PKCEUtils } from '../utils/pkce' + +describe('OAuth2Service', () => { + let service: OAuth2Service + + beforeEach(() => { + service = new OAuth2Service() + }) + + test('should initialize from well-known URL', async () => { + await service.initializeFromWellKnown( + 'http://localhost:9000/obp-oidc/.well-known/openid-configuration' + ) + // Assert configuration is loaded + }) + + test('should create valid authorization URL', () => { + const state = PKCEUtils.generateState() + const codeChallenge = PKCEUtils.generateCodeChallenge('test-verifier') + const authUrl = service.createAuthorizationURL(state, codeChallenge) + + expect(authUrl.searchParams.get('response_type')).toBe('code') + expect(authUrl.searchParams.get('state')).toBe(state) + }) +}) +``` + +**File**: `server/test/pkce.test.ts` + +```typescript +import { PKCEUtils } from '../utils/pkce' + +describe('PKCEUtils', () => { + test('should generate valid code verifier', () => { + const verifier = PKCEUtils.generateCodeVerifier() + expect(verifier).toHaveLength(43) // Base64url encoded 32 bytes + }) + + test('should generate valid code challenge', () => { + const verifier = 'test-verifier' + const challenge = PKCEUtils.generateCodeChallenge(verifier) + expect(challenge).toBeTruthy() + expect(typeof challenge).toBe('string') + }) + + test('should generate unique states', () => { + const state1 = PKCEUtils.generateState() + const state2 = PKCEUtils.generateState() + expect(state1).not.toBe(state2) + }) +}) +``` + +#### 5.2 Integration Tests + +Create integration test suite: + +```typescript +import request from 'supertest' +import { app } from '../app' + +describe('OAuth2 Integration Flow', () => { + test('GET /oauth2/connect should redirect to OIDC provider', async () => { + const response = await request(app).get('/oauth2/connect').expect(302) + + expect(response.header.location).toContain('obp-oidc/auth') + expect(response.header.location).toContain('code_challenge') + }) + + test('GET /oauth2/callback should exchange code for tokens', async () => { + // Mock OIDC server responses + // Test callback flow + }) +}) +``` + +#### 5.3 Manual Testing Checklist + +**OAuth2 Login Flow**: + +- [ ] User clicks login button +- [ ] Redirected to OBP-OIDC login page +- [ ] Enter valid credentials +- [ ] Successfully redirected back to API Explorer +- [ ] User info displayed in header +- [ ] Session persists across page refreshes +- [ ] Access token works for API calls + +**Token Refresh**: + +- [ ] Access token expires after configured time +- [ ] Refresh token automatically obtains new access token +- [ ] User remains logged in without re-authentication + +**Logout Flow**: + +- [ ] User clicks logout button +- [ ] Session cleared +- [ ] Redirected to appropriate page +- [ ] Cannot access protected resources + +**Error Handling**: + +- [ ] Invalid authorization code +- [ ] Expired authorization code +- [ ] State parameter mismatch +- [ ] OIDC provider unavailable +- [ ] Network errors during token exchange + +#### 5.4 Security Testing + +- [ ] PKCE challenge/verifier validation +- [ ] State parameter prevents CSRF attacks +- [ ] Tokens stored securely (not in localStorage) +- [ ] HTTPS required in production +- [ ] Token expiration enforced +- [ ] Refresh tokens rotated (if supported) + +**Deliverables**: + +- Unit test suite +- Integration test suite +- Manual testing report +- Security audit report + +--- + +### Phase 6: Documentation & Migration (Week 6) + +**Goals**: Document changes and prepare migration guide + +#### 6.1 Update README.md + +Add OAuth2 section: + +````markdown +## Authentication + +API Explorer II supports two authentication methods: + +### OAuth 1.0a (Legacy) + +Traditional OAuth 1.0a flow with OBP-API. + +**Configuration**: + +```bash +VITE_USE_OAUTH2=false +VITE_OBP_CONSUMER_KEY= +VITE_OBP_CONSUMER_SECRET= +VITE_OBP_REDIRECT_URL=http://localhost:5173/api/callback +``` +```` + +### OAuth2/OIDC (Recommended) + +Modern OAuth2 with OpenID Connect support. + +**Configuration**: + +```bash +VITE_USE_OAUTH2=true +VITE_OBP_OAUTH2_CLIENT_ID=obp-explorer-ii-client +VITE_OBP_OAUTH2_CLIENT_SECRET= +VITE_OBP_OAUTH2_REDIRECT_URL=http://localhost:5173/oauth2/callback +VITE_OBP_OAUTH2_WELL_KNOWN_URL=http://127.0.0.1:9000/obp-oidc/.well-known/openid-configuration +``` + +**Setting up OBP-OIDC**: + +1. Clone and setup OBP-OIDC: + + ```bash + cd ~/Documents/workspace_2024/OBP-OIDC + cp run-server.example.sh run-server.sh + # Edit run-server.sh with your database credentials + ./run-server.sh + ``` + +2. Copy client credentials from OBP-OIDC startup output + +3. Update API Explorer II `.env` file + +4. Restart API Explorer II + +```` + +#### 6.2 Create Migration Guide + +**File**: `docs/OAUTH2_MIGRATION_GUIDE.md` + +```markdown +# OAuth2 Migration Guide + +## For Developers + +### Prerequisites +- OBP-OIDC server running (or other OIDC provider) +- Client credentials (ID and Secret) +- Database views configured in OBP database + +### Step-by-Step Migration + +1. **Update Dependencies** + ```bash + npm install arctic jsonwebtoken @types/jsonwebtoken +```` + +2. **Configure Environment** + Update `.env`: + + ```bash + VITE_USE_OAUTH2=true + VITE_OBP_OAUTH2_CLIENT_ID=obp-explorer-ii-client + VITE_OBP_OAUTH2_CLIENT_SECRET= + VITE_OBP_OAUTH2_REDIRECT_URL=http://localhost:5173/oauth2/callback + VITE_OBP_OAUTH2_WELL_KNOWN_URL=http://127.0.0.1:9000/obp-oidc/.well-known/openid-configuration + ``` + +3. **Test Authentication** + + - Start server: `npm run dev` + - Click login + - Verify redirect to OIDC provider + - Login with valid credentials + - Verify successful callback + +4. **Verify API Calls** + - Test protected endpoints + - Verify Bearer token in requests + - Check token refresh on expiration + +### Rollback Procedure + +If issues occur: + +1. Set `VITE_USE_OAUTH2=false` +2. Restart server +3. System reverts to OAuth 1.0a + +## For System Administrators + +### Production Deployment + +1. **Use Production OIDC Provider** + + - Don't use OBP-OIDC in production + - Recommended: Keycloak, Auth0, Okta + - Configure provider in OBP-API well-known endpoints + +2. **Security Requirements** + + - HTTPS only for all endpoints + - Secure Redis with password + - Rotate client secrets regularly + - Monitor for suspicious activity + +3. **Configuration** + + ```bash + VITE_USE_OAUTH2=true + VITE_OBP_OAUTH2_CLIENT_ID= + VITE_OBP_OAUTH2_CLIENT_SECRET= + VITE_OBP_OAUTH2_REDIRECT_URL=https://explorer.yourdomain.com/oauth2/callback + VITE_OBP_OAUTH2_WELL_KNOWN_URL=https://auth.yourdomain.com/.well-known/openid-configuration + ``` + +4. **Monitoring** + - Track authentication success/failure rates + - Monitor token refresh failures + - Alert on OIDC provider downtime + +```` + +#### 6.3 Create Troubleshooting Guide + +**File**: `docs/OAUTH2_TROUBLESHOOTING.md` + +```markdown +# OAuth2 Troubleshooting Guide + +## Common Issues + +### 1. "State validation failed" + +**Cause**: State parameter mismatch or session timeout + +**Solution**: +- Clear browser cookies +- Check Redis connection +- Verify session configuration in `server/app.ts` + +### 2. "Code verifier not found in session" + +**Cause**: Session not persisting between requests + +**Solution**: +- Check Redis is running: `redis-cli ping` +- Verify `VITE_OBP_REDIS_URL` in `.env` +- Check session cookie settings (secure, httpOnly) + +### 3. "Token request failed: 401" + +**Cause**: Client credentials invalid + +**Solution**: +- Verify `VITE_OBP_OAUTH2_CLIENT_ID` and `VITE_OBP_OAUTH2_CLIENT_SECRET` +- Check client is registered in OBP-OIDC +- Verify redirect URI matches exactly + +### 4. "OIDC configuration not initialized" + +**Cause**: Well-known URL not accessible + +**Solution**: +- Check OBP-OIDC is running +- Verify `VITE_OBP_OAUTH2_WELL_KNOWN_URL` +- Test URL: `curl http://localhost:9000/obp-oidc/.well-known/openid-configuration` + +### 5. Redirect Loop + +**Cause**: Cookie not being set or read + +**Solution**: +- Check `X-Forwarded-Proto` header in nginx config +- Set `app.set('trust proxy', 1)` in production +- Verify cookie domain settings + +## Debug Mode + +Enable detailed logging: + +```bash +DEBUG=express-session npm run dev +```` + +Check session data in Redis: + +```bash +redis-cli +> KEYS sess:* +> GET sess: +``` + +## Testing OIDC Provider + +Test discovery: + +```bash +curl http://localhost:9000/obp-oidc/.well-known/openid-configuration +``` + +Test authorization (should return HTML login form): + +```bash +curl "http://localhost:9000/obp-oidc/auth?response_type=code&client_id=obp-explorer-ii-client&redirect_uri=http://localhost:5173/oauth2/callback&scope=openid&state=test123" +``` + +```` + +**Deliverables**: +- Updated README with OAuth2 documentation +- Migration guide for developers +- Troubleshooting guide +- Admin deployment guide + +--- + +## 7. Technical Requirements + +### 7.1 System Requirements + +**Development**: +- Node.js >= 16.14 +- npm >= 8.0.0 +- Redis >= 6.0 +- PostgreSQL >= 12 (for OBP database) +- Java 11+ (for OBP-OIDC) +- Maven 3.6+ (for OBP-OIDC) + +**Production**: +- Same as development, plus: +- HTTPS/TLS certificates +- Production-grade OIDC provider (Keycloak, Auth0, Okta) +- Load balancer with session affinity +- Monitoring and alerting + +### 7.2 Dependencies + +**New NPM Packages**: +```json +{ + "dependencies": { + "arctic": "^1.0.0", + "jsonwebtoken": "^9.0.2" + }, + "devDependencies": { + "@types/jsonwebtoken": "^9.0.6" + } +} +```` + +**Existing Dependencies** (no changes): + +- express +- express-session +- connect-redis +- redis +- routing-controllers +- typedi + +### 7.3 Database Requirements + +**OBP Database Views** (required for OBP-OIDC): + +- `v_oidc_users` - User authentication +- `v_oidc_clients` - Client registration +- `v_oidc_admin_clients` - Client management + +See: `OBP-API/obp-api/src/main/scripts/sql/OIDC/` + +### 7.4 Network Requirements + +**Development**: + +- API Explorer II: `http://localhost:5173` +- OBP-API: `http://localhost:8080` +- OBP-OIDC: `http://localhost:9000` +- Redis: `localhost:6379` + +**Production**: + +- HTTPS required for all endpoints +- CORS properly configured +- Firewall rules for service communication + +--- + +## 8. Configuration Changes + +### 8.1 Environment Variables + +**New Variables**: + +```bash +# OAuth2 Feature Flag +VITE_USE_OAUTH2=false # Set to 'true' to enable OAuth2 + +# OAuth2 Client Configuration +VITE_OBP_OAUTH2_CLIENT_ID=obp-explorer-ii-client +VITE_OBP_OAUTH2_CLIENT_SECRET= +VITE_OBP_OAUTH2_REDIRECT_URL=http://localhost:5173/oauth2/callback + +# OIDC Provider Configuration +VITE_OBP_OAUTH2_WELL_KNOWN_URL=http://127.0.0.1:9000/obp-oidc/.well-known/openid-configuration + +# Optional: Manual endpoint configuration (if not using well-known) +VITE_OBP_OAUTH2_AUTHORIZATION_URL=http://127.0.0.1:9000/obp-oidc/auth +VITE_OBP_OAUTH2_TOKEN_URL=http://127.0.0.1:9000/obp-oidc/token +VITE_OBP_OAUTH2_USERINFO_URL=http://127.0.0.1:9000/obp-oidc/userinfo +VITE_OBP_OAUTH2_JWKS_URL=http://127.0.0.1:9000/obp-oidc/jwks + +# Token Configuration +VITE_OBP_OAUTH2_TOKEN_REFRESH_THRESHOLD=300 # Refresh 5 minutes before expiry +``` + +**Existing Variables** (keep unchanged): + +```bash +# OAuth 1.0a (Legacy) - Still needed for backward compatibility +VITE_OBP_CONSUMER_KEY= +VITE_OBP_CONSUMER_SECRET= +VITE_OBP_REDIRECT_URL=http://localhost:5173/api/callback + +# API Configuration +VITE_OBP_API_HOST=http://127.0.0.1:8080 +VITE_OBP_API_VERSION=v5.1.0 +VITE_OBP_API_EXPLORER_HOST=http://localhost:5173 + +# Redis Configuration +VITE_OBP_REDIS_URL=redis://127.0.0.1:6379 +VITE_OPB_SERVER_SESSION_PASSWORD= + +# Opey Configuration +VITE_CHATBOT_ENABLED=true +VITE_CHATBOT_URL=http://localhost:5000 +``` + +### 8.2 Session Configuration + +No changes needed - existing Redis session store works with OAuth2. + +**Verify** in `server/app.ts`: + +```typescript +cookie: { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', // HTTPS only in production + maxAge: 24 * 60 * 60 * 1000 // 24 hours +} +``` + +### 8.3 OBP-OIDC Configuration + +**File**: `OBP-OIDC/run-server.sh` + +Update Explorer II client section: + +```bash +export OIDC_CLIENT_EXPLORER_ID=obp-explorer-ii-client +export OIDC_CLIENT_EXPLORER_SECRET= +export OIDC_CLIENT_EXPLORER_REDIRECTS=http://localhost:5173/oauth2/callback,https://production-domain.com/oauth2/callback +``` + +**Multiple Environments**: +Add comma-separated redirect URIs for dev, staging, production. + +--- + +## 9. Code Changes Required + +### 9.1 File Structure + +**New Files**: + +``` +server/ + ├── services/ + │ └── OAuth2Service.ts # OAuth2/OIDC client service + ├── middlewares/ + │ ├── OAuth2AuthorizationMiddleware.ts # Authorization flow + │ └── OAuth2CallbackMiddleware.ts # Callback handling + ├── controllers/ + │ ├── OAuth2ConnectController.ts # /oauth2/connect endpoint + │ └── OAuth2CallbackController.ts # /oauth2/callback endpoint + ├── utils/ + │ └── pkce.ts # PKCE helper functions + └── test/ + ├── OAuth2Service.test.ts # Service tests + └── pkce.test.ts # PKCE tests + +docs/ + ├── OAUTH2_MIGRATION_GUIDE.md # Migration documentation + └── OAUTH2_TROUBLESHOOTING.md # Troubleshooting guide +``` + +**Modified Files**: + +``` +server/ + ├── app.ts # Initialize OAuth2Service + └── controllers/ + └── UserController.ts # Support both auth methods + +src/ + └── components/ + └── HeaderNav.vue # Dual login button support +``` + +**Unchanged Files** (keep for backward compatibility): + +``` +server/ + ├── services/ + │ ├── OauthInjectedService.ts # OAuth 1.0a service + │ └── OBPClientService.ts # OBP API client + ├── middlewares/ + │ ├── OauthRequestTokenMiddleware.ts + │ └── OauthAccessTokenMiddleware.ts + └── controllers/ + ├── ConnectController.ts # /api/connect endpoint + └── CallbackController.ts # /api/callback endpoint +``` + +### 9.2 Breaking Changes + +**None** - Implementation is additive: + +- OAuth 1.0a code remains functional +- New OAuth2 code runs in parallel +- Feature flag controls which is active +- No breaking API changes + +### 9.3 API Changes + +**New Endpoints**: + +- `GET /oauth2/connect?redirect=` - Start OAuth2 flow +- `GET /oauth2/callback?code=&state=` - OAuth2 callback + +**Modified Endpoints**: + +- `GET /user/current` - Returns user info from OAuth2 or OAuth 1.0a session +- `GET /user/logoff` - Clears both OAuth2 and OAuth 1.0a sessions + +**Unchanged Endpoints**: + +- `GET /api/connect` - OAuth 1.0a flow (still works) +- `GET /api/callback` - OAuth 1.0a callback (still works) + +--- + +## 10. Testing Strategy + +### 10.1 Test Pyramid + +``` + /\ + / \ + / \ + / E2E \ + / Tests \ + /----------\ + / \ + / Integration \ + / Tests \ + /------------------\ + / \ + / Unit Tests \ + /________________________\ +``` + +### 10.2 Unit Tests (Target: 80% coverage) + +**Files to Test**: + +- `OAuth2Service.ts` +- `pkce.ts` +- `OAuth2AuthorizationMiddleware.ts` +- `OAuth2CallbackMiddleware.ts` + +**Test Cases**: + +- PKCE code verifier generation +- PKCE code challenge generation +- State parameter generation +- OIDC configuration initialization +- Authorization URL creation +- Token exchange logic +- Error handling + +### 10.3 Integration Tests + +**Test Scenarios**: + +1. Full OAuth2 authentication flow +2. Token refresh flow +3. Logout flow +4. Error cases (invalid code, state mismatch) +5. Session persistence +6. Concurrent user sessions + +### 10.4 End-to-End Tests + +Use Playwright for browser automation: + +```typescript +test('OAuth2 login flow', async ({ page }) => { + // Navigate to API Explorer + await page.goto('http://localhost:5173') + + // Click login + await page.click('#login') + + // Should redirect to OBP-OIDC + await page.waitForURL(/obp-oidc\/auth/) + + // Fill login form + await page.fill('[name="username"]', 'test@example.com') + await page.fill('[name="password"]', 'password123') + await page.click('[type="submit"]') + + // Should redirect back to Explorer + await page.waitForURL('http://localhost:5173') + + // Verify logged in + await expect(page.locator('.login-user')).toBeVisible() +}) +``` + +### 10.5 Security Tests + +**Test Cases**: + +- CSRF protection via state parameter +- PKCE validation +- Token expiration enforcement +- Secure cookie attributes +- HTTPS enforcement in production +- XSS prevention in redirect URLs + +### 10.6 Performance Tests + +**Metrics to Track**: + +- OAuth2 flow completion time (should be < 2 seconds) +- Token refresh time (should be < 500ms) +- Session lookup time (Redis) +- Memory usage with multiple sessions + +### 10.7 Compatibility Tests + +**Browsers**: + +- Chrome/Chromium +- Firefox +- Safari +- Edge + +**Platforms**: + +- Linux +- macOS +- Windows + +**OIDC Providers**: + +- OBP-OIDC +- Keycloak +- (Future: Auth0, Okta, etc.) + +--- + +## 11. Deployment Considerations + +### 11.1 Phased Rollout Strategy + +**Phase 1: Internal Testing** + +- Deploy to development environment +- Enable OAuth2 for internal team only +- Collect feedback +- Fix issues + +**Phase 2: Beta Testing** + +- Deploy to staging environment +- Enable OAuth2 for beta users +- Monitor metrics (login success rate, errors) +- Refine implementation + +**Phase 3: Production Rollout** + +- Deploy to production +- Keep OAuth 1.0a as default (`VITE_USE_OAUTH2=false`) +- Allow users to opt-in to OAuth2 +- Monitor metrics + +**Phase 4: Full Migration** + +- Make OAuth2 default (`VITE_USE_OAUTH2=true`) +- Keep OAuth 1.0a as fallback +- Announce deprecation timeline for OAuth 1.0a + +**Phase 5: OAuth 1.0a Removal** (6+ months later) + +- Remove OAuth 1.0a code +- Remove oauth npm package dependency +- Update documentation + +### 11.2 Production Configuration + +**Nginx Example**: + +```nginx +server { + listen 443 ssl http2; + server_name explorer.yourdomain.com; + + ssl_certificate /etc/ssl/certs/explorer.crt; + ssl_certificate_key /etc/ssl/private/explorer.key; + + # Frontend + location / { + root /var/www/api-explorer/dist; + try_files $uri $uri/ /index.html; + } + + # Backend API + location /api { + proxy_pass http://localhost:8085; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; # Important for secure cookies + } + + # OAuth2 endpoints + location /oauth2 { + proxy_pass http://localhost:8085; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; # Important for secure cookies + } +} +``` + +### 11.3 Environment-Specific Settings + +**Development**: + +```bash +VITE_USE_OAUTH2=true +VITE_OBP_OAUTH2_CLIENT_ID=obp-explorer-ii-client-dev +VITE_OBP_OAUTH2_REDIRECT_URL=http://localhost:5173/oauth2/callback +VITE_OBP_OAUTH2_WELL_KNOWN_URL=http://localhost:9000/obp-oidc/.well-known/openid-configuration +NODE_ENV=development +``` + +**Staging**: + +```bash +VITE_USE_OAUTH2=true +VITE_OBP_OAUTH2_CLIENT_ID=obp-explorer-ii-client-staging +VITE_OBP_OAUTH2_REDIRECT_URL=https://staging-explorer.yourdomain.com/oauth2/callback +VITE_OBP_OAUTH2_WELL_KNOWN_URL=https://staging-auth.yourdomain.com/.well-known/openid-configuration +NODE_ENV=production +``` + +**Production**: + +```bash +VITE_USE_OAUTH2=true +VITE_OBP_OAUTH2_CLIENT_ID=obp-explorer-ii-client-prod +VITE_OBP_OAUTH2_REDIRECT_URL=https://explorer.yourdomain.com/oauth2/callback +VITE_OBP_OAUTH2_WELL_KNOWN_URL=https://auth.yourdomain.com/.well-known/openid-configuration +NODE_ENV=production +``` + +### 11.4 Monitoring & Alerting + +**Metrics to Monitor**: + +- OAuth2 login success rate +- OAuth2 login failure rate (by error type) +- Token refresh success rate +- Token refresh failure rate +- Average login duration +- Session count (active users) +- OIDC provider response times +- Redis connection status + +**Alerts to Configure**: + +- OAuth2 failure rate > 5% +- OIDC provider unreachable +- Redis connection lost +- Token refresh failures spike +- Session store full + +**Logging**: + +```typescript +// Add structured logging +logger.info('OAuth2 login attempt', { + userId: session.oauth2_user?.sub, + provider: 'obp-oidc', + timestamp: new Date().toISOString() +}) + +logger.error('OAuth2 token exchange failed', { + error: error.message, + code: authCode, + provider: 'obp-oidc' +}) +``` + +### 11.5 Backup & Recovery + +**Session Backup** (Redis): + +```bash +# Enable RDB snapshots +save 900 1 +save 300 10 +save 60 10000 + +# Enable AOF for durability +appendonly yes +appendfsync everysec +``` + +**Configuration Backup**: + +- Store client secrets in secure vault (HashiCorp Vault, AWS Secrets Manager) +- Version control all configuration files +- Document manual configuration steps + +--- + +## 12. Rollback Plan + +### 12.1 Immediate Rollback (< 5 minutes) + +**If OAuth2 causes critical issues**: + +1. **Update environment variable**: + + ```bash + export VITE_USE_OAUTH2=false + ``` + +2. **Restart application**: + + ```bash + pm2 restart api-explorer + # OR + systemctl restart api-explorer + ``` + +3. **Verify**: + - Login button points to `/api/connect` + - OAuth 1.0a flow works + - Existing sessions remain valid + +**No code changes needed** - feature flag handles rollback. + +### 12.2 Partial Rollback (Specific Users) + +**If issues affect some users but not others**: + +1. Keep OAuth2 enabled globally +2. Add user-specific override in session: + + ```typescript + if (session.forceOAuth1) { + // Use OAuth 1.0a for this user + } + ``` + +3. Or implement A/B testing: + ```typescript + const useOAuth2 = (userId) => { + return userId % 10 < 5 // 50% of users + } + ``` + +### 12.3 Full Rollback (Remove OAuth2 Code) + +**If OAuth2 needs to be completely removed**: + +1. Delete new files: + + ```bash + rm server/services/OAuth2Service.ts + rm server/middlewares/OAuth2*.ts + rm server/controllers/OAuth2*.ts + rm server/utils/pkce.ts + ``` + +2. Remove dependencies: + + ```bash + npm uninstall arctic jsonwebtoken + ``` + +3. Revert modified files: + + ```bash + git checkout server/app.ts + git checkout server/controllers/UserController.ts + git checkout src/components/HeaderNav.vue + ``` + +4. Remove environment variables from `.env` + +5. Rebuild and deploy: + ```bash + npm run build + npm run build-server + # Deploy + ``` + +### 12.4 Data Migration Rollback + +**OAuth2 sessions → OAuth 1.0a sessions**: + +No data migration needed because: + +- OAuth 1.0a and OAuth2 use separate session keys +- Rolling back doesn't affect OAuth 1.0a sessions +- Users will need to re-authenticate (acceptable) + +### 12.5 Communication Plan + +**If rollback is needed**: + +1. **Internal notification** (immediate): + + - Alert development team + - Document the issue + - Perform rollback + +2. **User notification** (if service interruption): + + - Status page update + - Email to affected users + - Estimated time to resolution + +3. **Post-mortem** (after rollback): + - Root cause analysis + - Timeline of events + - Lessons learned + - Action items to prevent recurrence + +--- + +## 13. References + +### 13.1 OAuth2/OIDC Standards + +- **RFC 6749**: OAuth 2.0 Authorization Framework + - https://datatracker.ietf.org/doc/html/rfc6749 +- **RFC 7636**: Proof Key for Code Exchange (PKCE) + - https://datatracker.ietf.org/doc/html/rfc7636 +- **OpenID Connect Core 1.0** + - https://openid.net/specs/openid-connect-core-1_0.html +- **OpenID Connect Discovery 1.0** + - https://openid.net/specs/openid-connect-discovery-1_0.html + +### 13.2 Libraries & Tools + +**Arctic** (OAuth2/OIDC client): + +- Documentation: https://arctic.js.org/ +- GitHub: https://github.com/pilcrowonpaper/arctic +- Used by: OBP-Portal + +**jsonwebtoken** (JWT handling): + +- Documentation: https://github.com/auth0/node-jsonwebtoken +- NPM: https://www.npmjs.com/package/jsonwebtoken + +**Express Session**: + +- Documentation: https://github.com/expressjs/session +- Used for: Session management with Redis + +### 13.3 OBP Projects + +**OBP-OIDC**: + +- Location: `~/Documents/workspace_2024/OBP-OIDC` +- README: See project README.md for setup instructions +- GitHub: (Internal repository) + +**OBP-Portal** (Reference Implementation): + +- Location: `~/Documents/workspace_2024/OBP-Portal` +- Key files: + - `src/lib/oauth/client.ts` - OAuth2 client wrapper + - `src/lib/oauth/providerManager.ts` - Multi-provider support + - `src/lib/oauth/providerFactory.ts` - Provider strategy pattern + - `src/lib/oauth/sessionHelper.ts` - Session management + - `src/hooks.server.ts` - Server initialization + +**OBP-API**: + +- Database scripts: `OBP-API/obp-api/src/main/scripts/sql/OIDC/` +- Well-known endpoint: `/obp/v5.1.0/well-known` + +### 13.4 Additional Resources + +**OIDC Debugger**: + +- https://oidcdebugger.com/ +- Tool for testing OIDC flows + +**JWT.io**: + +- https://jwt.io/ +- Tool for decoding and inspecting JWTs + +**OAuth 2.0 Playground**: + +- https://www.oauth.com/playground/ +- Interactive OAuth2 flow demonstration + +### 13.5 Security Best Practices + +**OWASP OAuth 2.0 Security**: + +- https://cheatsheetseries.owasp.org/cheatsheets/OAuth2_Cheatsheet.html + +**OAuth 2.0 Security Best Current Practice**: + +- https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics + +**Key Recommendations**: + +1. Always use PKCE for authorization code flow +2. Use state parameter to prevent CSRF +3. Validate redirect URIs strictly +4. Use HTTPS in production +5. Store tokens securely (never in localStorage) +6. Implement token refresh before expiration +7. Use short-lived access tokens +8. Rotate refresh tokens when possible +9. Log authentication events for audit +10. Monitor for suspicious activity + +--- + +## Appendix A: Quick Reference + +### A.1 OAuth2 vs OAuth 1.0a Comparison + +| Feature | OAuth 1.0a | OAuth2/OIDC | +| ---------------------- | ----------------------- | --------------------------- | +| **Signature** | HMAC-SHA1 required | Bearer token (no signature) | +| **Request Complexity** | Complex (6+ parameters) | Simple (2-3 parameters) | +| **Token Types** | Access token + secret | Access + Refresh + ID token | +| **Token Format** | Opaque string | JWT (structured) | +| **User Info** | Separate API call | Embedded in ID token | +| **Refresh** | Not supported | Built-in refresh token | +| **Mobile Support** | Difficult | Native support | +| **Browser Support** | Limited | Full support | +| **Standard** | Deprecated | Current standard | + +### A.2 Key Environment Variables + +```bash +# Feature Flag +VITE_USE_OAUTH2=true|false + +# Client Credentials +VITE_OBP_OAUTH2_CLIENT_ID= +VITE_OBP_OAUTH2_CLIENT_SECRET= + +# Endpoints +VITE_OBP_OAUTH2_REDIRECT_URL= +VITE_OBP_OAUTH2_WELL_KNOWN_URL= + +# API Configuration +VITE_OBP_API_HOST= +VITE_OBP_API_EXPLORER_HOST= + +# Session +VITE_OBP_REDIS_URL= +VITE_OPB_SERVER_SESSION_PASSWORD= +``` + +### A.3 Common Commands + +**Start OBP-OIDC**: + +```bash +cd ~/Documents/workspace_2024/OBP-OIDC +./run-server.sh +``` + +**Start API Explorer II**: + +```bash +cd ~/Documents/workspace_2024/API-Explorer-II +npm run dev +``` + +**Test OIDC Discovery**: + +```bash +curl http://localhost:9000/obp-oidc/.well-known/openid-configuration | jq +``` + +**Check Redis**: + +```bash +redis-cli ping +redis-cli KEYS "sess:*" +``` + +**Run Tests**: + +```bash +npm test # Unit tests +npm run test:integration # Integration tests +npx playwright test # E2E tests +``` + +### A.4 Troubleshooting Checklist + +**Before Starting**: + +- [ ] OBP-OIDC is running +- [ ] Redis is running +- [ ] OBP-API is running +- [ ] Database views are created +- [ ] Client is registered in OBP-OIDC +- [ ] Environment variables are set + +**If Login Fails**: + +- [ ] Check browser console for errors +- [ ] Verify redirect URI matches exactly +- [ ] Check session cookie is set +- [ ] Verify OIDC provider is accessible +- [ ] Check server logs for errors +- [ ] Verify client credentials + +**If Tokens Fail**: + +- [ ] Check token expiration +- [ ] Verify refresh token exists +- [ ] Check token format (valid JWT) +- [ ] Verify signing algorithm (RS256) +- [ ] Check JWKS endpoint + +### A.5 File Checklist + +**New Files to Create**: + +- [ ] `server/services/OAuth2Service.ts` +- [ ] `server/utils/pkce.ts` +- [ ] `server/middlewares/OAuth2AuthorizationMiddleware.ts` +- [ ] `server/middlewares/OAuth2CallbackMiddleware.ts` +- [ ] `server/controllers/OAuth2ConnectController.ts` +- [ ] `server/controllers/OAuth2CallbackController.ts` +- [ ] `server/test/OAuth2Service.test.ts` +- [ ] `server/test/pkce.test.ts` +- [ ] `docs/OAUTH2_MIGRATION_GUIDE.md` +- [ ] `docs/OAUTH2_TROUBLESHOOTING.md` + +**Files to Modify**: + +- [ ] `server/app.ts` +- [ ] `server/controllers/UserController.ts` +- [ ] `src/components/HeaderNav.vue` +- [ ] `package.json` +- [ ] `env_ai` +- [ ] `README.md` + +**Files to Keep (Don't Delete)**: + +- [ ] `server/services/OauthInjectedService.ts` +- [ ] `server/services/OBPClientService.ts` +- [ ] `server/middlewares/OauthRequestTokenMiddleware.ts` +- [ ] `server/middlewares/OauthAccessTokenMiddleware.ts` +- [ ] `server/controllers/ConnectController.ts` +- [ ] `server/controllers/CallbackController.ts` + +--- + +## Appendix B: Testing Scenarios + +### B.1 Happy Path Testing + +1. **New User Login** + + - User clicks login + - Redirects to OBP-OIDC + - User enters credentials + - Successfully redirected back + - User info displayed + - Session persists + +2. **Returning User** + + - User has valid session + - Page refresh maintains login + - API calls include Bearer token + - Token auto-refreshes before expiry + +3. **Logout** + - User clicks logout + - Session cleared + - Redirected appropriately + - Cannot access protected resources + +### B.2 Error Scenarios + +1. **Invalid Credentials** + + - User enters wrong password + - OBP-OIDC shows error + - User can retry + +2. **State Mismatch** + + - Tampered state parameter + - Shows error message + - User can restart flow + +3. **Expired Code** + + - Authorization code expires + - Shows error message + - User can retry login + +4. **Network Errors** + + - OIDC provider unreachable + - Shows user-friendly error + - Option to retry + +5. **Token Expired** + - Access token expires + - Refresh token used automatically + - User stays logged in + +### B.3 Edge Cases + +1. **Concurrent Logins** + + - User logs in from multiple browsers + - Each session independent + - Logout affects only current session + +2. **Session Timeout** + + - User inactive for extended period + - Session expires + - Prompted to log in again + +3. **Browser Refresh During Flow** + + - User refreshes during authorization + - Flow restarts gracefully + - No error shown + +4. **Multiple Redirect URIs** + - Different environments + - Correct URI used per environment + - No cross-environment issues + +--- + +## Appendix C: Production Readiness Checklist + +### C.1 Security + +- [ ] HTTPS enabled for all endpoints +- [ ] Client secret stored securely (vault/secrets manager) +- [ ] Session secret is strong and rotated +- [ ] PKCE enabled and enforced +- [ ] State parameter validated +- [ ] CSRF protection enabled +- [ ] XSS protection in place +- [ ] Redirect URI whitelist enforced +- [ ] Token storage secure (httpOnly cookies) +- [ ] Rate limiting implemented +- [ ] Audit logging enabled + +### C.2 Performance + +- [ ] Redis configured for high availability +- [ ] Session expiration tuned +- [ ] Token refresh threshold optimized +- [ ] Connection pooling configured +- [ ] Caching strategy implemented +- [ ] Load testing completed +- [ ] Performance benchmarks met + +### C.3 Monitoring + +- [ ] Application monitoring (APM) +- [ ] Log aggregation configured +- [ ] Metrics dashboard created +- [ ] Alerts configured +- [ ] Health checks in place +- [ ] Uptime monitoring enabled +- [ ] Error tracking configured + +### C.4 Documentation + +- [ ] API documentation updated +- [ ] Admin guide created +- [ ] User guide updated +- [ ] Troubleshooting guide complete +- [ ] Runbook for common issues +- [ ] Architecture diagrams created +- [ ] Configuration documented + +### C.5 Operations + +- [ ] Deployment automation tested +- [ ] Rollback procedure documented +- [ ] Backup strategy implemented +- [ ] Disaster recovery plan +- [ ] On-call procedures defined +- [ ] Incident response plan +- [ ] Change management process + +### C.6 Testing + +- [ ] Unit tests passing (>80% coverage) +- [ ] Integration tests passing +- [ ] E2E tests passing +- [ ] Security tests completed +- [ ] Performance tests passed +- [ ] Load tests completed +- [ ] Penetration testing done (if required) + +--- + +## Document History + +| Version | Date | Author | Changes | +| ------- | ---- | --------------- | ------------------------- | +| 1.0 | 2024 | TESOBE Dev Team | Initial document creation | + +--- + +## Contact & Support + +**For Technical Questions**: + +- Development Team: dev@tesobe.com +- Internal Slack: #obp-development + +**For Security Issues**: + +- Security Team: security@tesobe.com +- Follow responsible disclosure guidelines + +**For Production Issues**: + +- On-call Team: oncall@tesobe.com +- Status Page: status.openbankproject.com + +--- + +**END OF DOCUMENT** diff --git a/OAUTH2-QUICK-START.md b/OAUTH2-QUICK-START.md new file mode 100644 index 0000000..59faf3c --- /dev/null +++ b/OAUTH2-QUICK-START.md @@ -0,0 +1,508 @@ +# OAuth2/OIDC Quick Start Guide +## API Explorer II Integration with OBP-OIDC + +**Quick reference for developers getting started with OAuth2/OIDC integration** + +--- + +## 🚀 Quick Setup (15 minutes) + +### Step 1: Set Up OBP-OIDC (5 minutes) + +```bash +# Navigate to OBP-OIDC directory +cd ~/Documents/workspace_2024/OBP-OIDC + +# Copy example configuration +cp run-server.example.sh run-server.sh + +# Edit database credentials (IMPORTANT!) +vim run-server.sh +# Update: DB_HOST, DB_PORT, DB_NAME, OIDC_USER_PASSWORD, OIDC_ADMIN_PASSWORD + +# Start the OIDC server +./run-server.sh +``` + +**Verify it's running:** +```bash +curl http://localhost:9000/obp-oidc/.well-known/openid-configuration +``` + +### Step 2: Configure API Explorer II (5 minutes) + +```bash +# Navigate to API Explorer II +cd ~/Documents/workspace_2024/API-Explorer-II + +# Install new dependencies +npm install arctic jsonwebtoken @types/jsonwebtoken + +# Update .env file +cat >> .env << EOF + +# OAuth2/OIDC Configuration +VITE_USE_OAUTH2=true +VITE_OBP_OAUTH2_CLIENT_ID=obp-explorer-ii-client +VITE_OBP_OAUTH2_CLIENT_SECRET=CHANGE_THIS_TO_EXPLORER_SECRET_2024 +VITE_OBP_OAUTH2_REDIRECT_URL=http://localhost:5173/oauth2/callback +VITE_OBP_OAUTH2_WELL_KNOWN_URL=http://127.0.0.1:9000/obp-oidc/.well-known/openid-configuration +EOF +``` + +**Note:** The client secret above matches the default in OBP-OIDC's `run-server.sh`. Change it to your actual secret. + +### Step 3: Verify Prerequisites (2 minutes) + +```bash +# Check Redis is running +redis-cli ping +# Expected output: PONG + +# Check OBP-API is running +curl http://localhost:8080/obp/v5.1.0/root +# Expected: JSON response + +# Check Node version +node --version +# Expected: v16.14.0 or higher +``` + +### Step 4: Test the Setup (3 minutes) + +```bash +# Start API Explorer II +npm run dev + +# Open browser to http://localhost:5173 +# Click "Login" button +# Should redirect to OBP-OIDC login page +``` + +**Test credentials** (default OBP-OIDC users): +- Username: `user@example.com` +- Password: (check your OBP database) + +--- + +## 📋 Implementation Checklist + +Use this checklist to track your implementation progress: + +### Phase 1: Backend Core +- [ ] Create `server/utils/pkce.ts` +- [ ] Create `server/services/OAuth2Service.ts` +- [ ] Create `server/middlewares/OAuth2AuthorizationMiddleware.ts` +- [ ] Create `server/middlewares/OAuth2CallbackMiddleware.ts` +- [ ] Create `server/controllers/OAuth2ConnectController.ts` +- [ ] Create `server/controllers/OAuth2CallbackController.ts` +- [ ] Update `server/app.ts` to initialize OAuth2Service + +### Phase 2: User Management +- [ ] Update `server/controllers/UserController.ts` getCurrentUser() +- [ ] Update `server/controllers/UserController.ts` logoff() +- [ ] Support both OAuth 1.0a and OAuth2 sessions + +### Phase 3: Frontend +- [ ] Update `src/components/HeaderNav.vue` login button +- [ ] Update `src/components/HeaderNav.vue` logout button +- [ ] Add OAuth2 status indicator (optional) + +### Phase 4: Testing +- [ ] Write unit tests for PKCE utilities +- [ ] Write unit tests for OAuth2Service +- [ ] Write integration tests for login flow +- [ ] Manual testing of full authentication flow + +### Phase 5: Documentation +- [ ] Update README.md +- [ ] Create migration guide +- [ ] Create troubleshooting guide + +--- + +## 🔑 Key Files to Create + +### 1. PKCE Utilities (`server/utils/pkce.ts`) + +```typescript +import crypto from 'crypto' + +export class PKCEUtils { + static generateCodeVerifier(): string { + return crypto.randomBytes(32).toString('base64url') + } + + static generateCodeChallenge(verifier: string): string { + return crypto.createHash('sha256').update(verifier).digest('base64url') + } + + static generateState(): string { + return crypto.randomBytes(32).toString('hex') + } +} +``` + +### 2. OAuth2 Service (`server/services/OAuth2Service.ts`) + +```typescript +import { OAuth2Client } from 'arctic' +import { Service } from 'typedi' + +export interface OIDCConfiguration { + issuer: string + authorization_endpoint: string + token_endpoint: string + userinfo_endpoint: string + jwks_uri: string +} + +@Service() +export class OAuth2Service { + private client: OAuth2Client + private oidcConfig: OIDCConfiguration | null = null + + constructor() { + const clientId = process.env.VITE_OBP_OAUTH2_CLIENT_ID + const clientSecret = process.env.VITE_OBP_OAUTH2_CLIENT_SECRET + const redirectUri = process.env.VITE_OBP_OAUTH2_REDIRECT_URL + + if (!clientId || !clientSecret || !redirectUri) { + throw new Error('OAuth2 configuration incomplete') + } + + this.client = new OAuth2Client(clientId, clientSecret, redirectUri) + } + + async initializeFromWellKnown(wellKnownUrl: string): Promise { + const response = await fetch(wellKnownUrl) + if (!response.ok) { + throw new Error(`Failed to fetch OIDC config: ${response.statusText}`) + } + this.oidcConfig = await response.json() + } + + createAuthorizationURL(state: string, scopes: string[] = ['openid', 'profile', 'email']): URL { + if (!this.oidcConfig) { + throw new Error('OIDC configuration not initialized') + } + return this.client.createAuthorizationURL(this.oidcConfig.authorization_endpoint, state, scopes) + } + + async exchangeCodeForTokens(code: string, codeVerifier: string): Promise { + if (!this.oidcConfig) { + throw new Error('OIDC configuration not initialized') + } + return await this.client.validateAuthorizationCode( + this.oidcConfig.token_endpoint, + code, + codeVerifier + ) + } + + async getUserInfo(accessToken: string): Promise { + if (!this.oidcConfig) { + throw new Error('OIDC configuration not initialized') + } + const response = await fetch(this.oidcConfig.userinfo_endpoint, { + headers: { Authorization: `Bearer ${accessToken}` } + }) + if (!response.ok) { + throw new Error(`UserInfo request failed: ${response.statusText}`) + } + return await response.json() + } +} +``` + +### 3. Authorization Middleware (`server/middlewares/OAuth2AuthorizationMiddleware.ts`) + +```typescript +import { ExpressMiddlewareInterface } from 'routing-controllers' +import { Request, Response } from 'express' +import { Service } from 'typedi' +import { OAuth2Service } from '../services/OAuth2Service' +import { PKCEUtils } from '../utils/pkce' + +@Service() +export default class OAuth2AuthorizationMiddleware implements ExpressMiddlewareInterface { + constructor(private oauth2Service: OAuth2Service) {} + + async use(request: Request, response: Response): Promise { + const session = request.session + const redirectPage = request.query.redirect + + if (redirectPage) { + session['redirectPage'] = redirectPage + } + + // Generate PKCE parameters + const codeVerifier = PKCEUtils.generateCodeVerifier() + const codeChallenge = PKCEUtils.generateCodeChallenge(codeVerifier) + const state = PKCEUtils.generateState() + + // Store in session + session['oauth2_state'] = state + session['oauth2_code_verifier'] = codeVerifier + + // Create authorization URL + const authUrl = this.oauth2Service.createAuthorizationURL(state) + authUrl.searchParams.set('code_challenge', codeChallenge) + authUrl.searchParams.set('code_challenge_method', 'S256') + + console.log('OAuth2: Redirecting to authorization endpoint') + response.redirect(authUrl.toString()) + } +} +``` + +### 4. Callback Middleware (`server/middlewares/OAuth2CallbackMiddleware.ts`) + +```typescript +import { ExpressMiddlewareInterface } from 'routing-controllers' +import { Request, Response } from 'express' +import { Service } from 'typedi' +import { OAuth2Service } from '../services/OAuth2Service' +import jwt from 'jsonwebtoken' + +@Service() +export default class OAuth2CallbackMiddleware implements ExpressMiddlewareInterface { + constructor(private oauth2Service: OAuth2Service) {} + + async use(request: Request, response: Response): Promise { + const session = request.session + const code = request.query.code as string + const state = request.query.state as string + + // Validate state + if (!state || state !== session['oauth2_state']) { + console.error('OAuth2: State validation failed') + return response.status(400).send('Invalid state parameter') + } + + // Get code verifier + const codeVerifier = session['oauth2_code_verifier'] + if (!codeVerifier) { + console.error('OAuth2: Code verifier not found') + return response.status(400).send('Invalid session state') + } + + try { + // Exchange code for tokens + const tokens = await this.oauth2Service.exchangeCodeForTokens(code, codeVerifier) + + // Get user info + const userInfo = await this.oauth2Service.getUserInfo(tokens.accessToken()) + + // Store in session + session['oauth2_access_token'] = tokens.accessToken() + session['oauth2_refresh_token'] = tokens.refreshToken?.() || null + session['oauth2_id_token'] = tokens.idToken?.() || null + session['oauth2_user_info'] = userInfo + + // Decode ID token + const idToken = tokens.idToken?.() + if (idToken) { + const decoded: any = jwt.decode(idToken) + session['oauth2_user'] = { + sub: decoded.sub, + email: decoded.email, + name: decoded.name, + username: decoded.preferred_username || decoded.sub + } + } + + // Clear flow parameters + delete session['oauth2_state'] + delete session['oauth2_code_verifier'] + + // Redirect + const redirectPage = session['redirectPage'] || process.env.VITE_OBP_API_EXPLORER_HOST + delete session['redirectPage'] + + console.log('OAuth2: Authentication successful') + response.redirect(redirectPage as string) + } catch (error: any) { + console.error('OAuth2: Token exchange failed:', error) + response.status(500).send('Authentication failed: ' + error.message) + } + } +} +``` + +--- + +## 🧪 Testing Your Implementation + +### Manual Testing Flow + +1. **Start all services:** + ```bash + # Terminal 1: OBP-OIDC + cd ~/Documents/workspace_2024/OBP-OIDC + ./run-server.sh + + # Terminal 2: Redis + redis-server + + # Terminal 3: API Explorer II + cd ~/Documents/workspace_2024/API-Explorer-II + npm run dev + ``` + +2. **Test login flow:** + - Open http://localhost:5173 + - Click "Login" button + - Should redirect to http://localhost:9000/obp-oidc/auth + - Enter credentials + - Should redirect back to http://localhost:5173 + - Username should appear in header + +3. **Test session persistence:** + - Refresh the page + - Should remain logged in + - Username still visible + +4. **Test logout:** + - Click "Logout" button + - Should redirect to home + - No longer authenticated + +### Debugging Tips + +**Enable debug logging:** +```bash +DEBUG=express-session npm run dev +``` + +**Check session in Redis:** +```bash +redis-cli +> KEYS sess:* +> GET sess: +``` + +**Check OIDC configuration:** +```bash +curl http://localhost:9000/obp-oidc/.well-known/openid-configuration | jq +``` + +**Monitor logs:** +- Watch server console for "OAuth2:" prefixed messages +- Watch browser console for errors +- Check OBP-OIDC terminal for authentication attempts + +--- + +## 🐛 Common Issues & Solutions + +### Issue: "OIDC configuration not initialized" +**Cause:** Well-known URL not reachable or OAuth2Service not initialized + +**Solution:** +```bash +# Check OBP-OIDC is running +curl http://localhost:9000/obp-oidc/.well-known/openid-configuration + +# Verify environment variable +echo $VITE_OBP_OAUTH2_WELL_KNOWN_URL + +# Check server logs for initialization error +``` + +### Issue: "State validation failed" +**Cause:** Session not persisting between requests + +**Solution:** +```bash +# Check Redis is running +redis-cli ping + +# Verify Redis connection in server logs +# Should see: "Connected to Redis instance: ..." + +# Check session cookie in browser DevTools (Application > Cookies) +``` + +### Issue: "Code verifier not found in session" +**Cause:** Session expired or cookie not set + +**Solution:** +- Clear browser cookies +- Check session timeout settings in `server/app.ts` +- Verify `VITE_OPB_SERVER_SESSION_PASSWORD` is set + +### Issue: "Token request failed: 401" +**Cause:** Invalid client credentials + +**Solution:** +```bash +# Verify client credentials match OBP-OIDC configuration +grep OIDC_CLIENT_EXPLORER ~/Documents/workspace_2024/OBP-OIDC/run-server.sh + +# Check credentials in .env +grep VITE_OBP_OAUTH2 .env +``` + +### Issue: Redirect loop +**Cause:** Cookies not being set properly + +**Solution:** +- Check cookie settings in `server/app.ts` +- If using nginx, verify `X-Forwarded-Proto` header +- Set `app.set('trust proxy', 1)` if behind reverse proxy + +--- + +## 📚 Additional Resources + +### Full Documentation +See `OAUTH2-OIDC-INTEGRATION-PREP.md` for: +- Complete implementation guide +- Architecture details +- Production deployment +- Security considerations +- Testing strategy + +### Reference Implementations +- **OBP-Portal**: `~/Documents/workspace_2024/OBP-Portal` + - `src/lib/oauth/` - OAuth2 implementation + - `src/hooks.server.ts` - Server initialization +- **OBP-OIDC**: `~/Documents/workspace_2024/OBP-OIDC` + - `README.md` - OIDC provider documentation + +### Standards & Specifications +- OAuth 2.0: https://oauth.net/2/ +- OpenID Connect: https://openid.net/connect/ +- PKCE: https://oauth.net/2/pkce/ + +--- + +## 🎯 Next Steps + +After completing the quick start: + +1. **Read the full preparation document** (`OAUTH2-OIDC-INTEGRATION-PREP.md`) +2. **Implement remaining phases** (see Phase 2-6 in main document) +3. **Write comprehensive tests** (unit, integration, E2E) +4. **Update documentation** (README, migration guide) +5. **Plan production deployment** (see deployment section in main doc) + +--- + +## 💡 Tips for Success + +1. **Keep OAuth 1.0a working** - Don't remove old code until OAuth2 is stable +2. **Use feature flags** - `VITE_USE_OAUTH2` allows easy rollback +3. **Test thoroughly** - OAuth2 flows have many edge cases +4. **Monitor closely** - Watch logs and metrics during rollout +5. **Document everything** - Future you will thank present you + +--- + +**Need Help?** +- Check `OAUTH2-OIDC-INTEGRATION-PREP.md` for detailed guidance +- Review OBP-Portal reference implementation +- Ask in #obp-development Slack channel + +**Good luck! 🚀** diff --git a/OAUTH2-README.md b/OAUTH2-README.md new file mode 100644 index 0000000..d7f7f3c --- /dev/null +++ b/OAUTH2-README.md @@ -0,0 +1,409 @@ +# OAuth2/OIDC Integration Documentation +## API Explorer II with OBP-OIDC + +Welcome! This directory contains comprehensive documentation for integrating OAuth2/OpenID Connect authentication into API Explorer II. + +--- + +## 📚 Documentation Overview + +This documentation set guides you through migrating API Explorer II from OAuth 1.0a to OAuth2/OIDC using OBP-OIDC as the identity provider. + +### Available Documents + +1. **[OAUTH2-QUICK-START.md](OAUTH2-QUICK-START.md)** ⭐ **START HERE** + - 15-minute setup guide + - Quick implementation checklist + - Key code snippets + - Common troubleshooting + - Perfect for: Developers getting started + +2. **[OAUTH2-OIDC-INTEGRATION-PREP.md](OAUTH2-OIDC-INTEGRATION-PREP.md)** 📖 **COMPLETE GUIDE** + - Full preparation document (60 pages) + - Architecture and design decisions + - Phase-by-phase implementation plan + - Testing strategy + - Production deployment guide + - Perfect for: Project planning and deep understanding + +--- + +## 🎯 Quick Navigation + +### For Developers + +**Just getting started?** +→ Read [OAUTH2-QUICK-START.md](OAUTH2-QUICK-START.md) + +**Need implementation details?** +→ See [OAUTH2-OIDC-INTEGRATION-PREP.md](OAUTH2-OIDC-INTEGRATION-PREP.md) Section 6 (Implementation Phases) + +**Having issues?** +→ Check [OAUTH2-QUICK-START.md](OAUTH2-QUICK-START.md) Common Issues section +→ Or [OAUTH2-OIDC-INTEGRATION-PREP.md](OAUTH2-OIDC-INTEGRATION-PREP.md) Appendix B (Troubleshooting) + +### For Project Managers + +**Need an overview?** +→ Read [OAUTH2-OIDC-INTEGRATION-PREP.md](OAUTH2-OIDC-INTEGRATION-PREP.md) Section 1 (Executive Summary) + +**Want timeline?** +→ See [OAUTH2-OIDC-INTEGRATION-PREP.md](OAUTH2-OIDC-INTEGRATION-PREP.md) Section 6 (Implementation Phases - 6 weeks) + +**Need risk assessment?** +→ Check [OAUTH2-OIDC-INTEGRATION-PREP.md](OAUTH2-OIDC-INTEGRATION-PREP.md) Section 12 (Rollback Plan) + +### For System Administrators + +**Production deployment?** +→ See [OAUTH2-OIDC-INTEGRATION-PREP.md](OAUTH2-OIDC-INTEGRATION-PREP.md) Section 11 (Deployment Considerations) + +**Configuration needed?** +→ Check [OAUTH2-OIDC-INTEGRATION-PREP.md](OAUTH2-OIDC-INTEGRATION-PREP.md) Section 8 (Configuration Changes) + +**Production readiness?** +→ Use [OAUTH2-OIDC-INTEGRATION-PREP.md](OAUTH2-OIDC-INTEGRATION-PREP.md) Appendix C (Production Readiness Checklist) + +--- + +## 🚀 Getting Started in 3 Steps + +### Step 1: Read the Quick Start (15 min) + +```bash +# Open the quick start guide +cat OAUTH2-QUICK-START.md +# Or open in your editor/browser +``` + +### Step 2: Set Up OBP-OIDC (5 min) + +```bash +cd ~/Documents/workspace_2024/OBP-OIDC +cp run-server.example.sh run-server.sh +# Edit run-server.sh with your database credentials +./run-server.sh +``` + +### Step 3: Configure API Explorer II (5 min) + +```bash +cd ~/Documents/workspace_2024/API-Explorer-II +npm install arctic jsonwebtoken @types/jsonwebtoken + +# Add to .env: +VITE_USE_OAUTH2=true +VITE_OBP_OAUTH2_CLIENT_ID=obp-explorer-ii-client +VITE_OBP_OAUTH2_CLIENT_SECRET=CHANGE_THIS_TO_EXPLORER_SECRET_2024 +VITE_OBP_OAUTH2_REDIRECT_URL=http://localhost:5173/oauth2/callback +VITE_OBP_OAUTH2_WELL_KNOWN_URL=http://127.0.0.1:9000/obp-oidc/.well-known/openid-configuration +``` + +--- + +## 📋 Implementation Timeline + +| Phase | Duration | Description | +|-------|----------|-------------| +| **Phase 1** | Week 1 | Preparation & Setup | +| **Phase 2** | Week 2-3 | Backend OAuth2 Implementation | +| **Phase 3** | Week 3 | Environment Configuration | +| **Phase 4** | Week 4 | Frontend Updates | +| **Phase 5** | Week 5 | Testing | +| **Phase 6** | Week 6 | Documentation & Migration | + +**Total:** 6 weeks for complete implementation and testing + +--- + +## 🎨 Architecture Overview + +### Current State (OAuth 1.0a) +``` +User → Login → OAuth 1.0a Flow → OBP-API → Callback → Session +``` + +### Target State (OAuth2/OIDC) +``` +User → Login → OAuth2 Flow → OBP-OIDC → Token Exchange → Session + ↓ + JWT Tokens (Access + Refresh + ID) +``` + +### Key Changes + +| Aspect | Before (OAuth 1.0a) | After (OAuth2/OIDC) | +|--------|---------------------|---------------------| +| **Auth Method** | HMAC-SHA1 signatures | Bearer tokens (JWT) | +| **Tokens** | Access token + secret | Access + Refresh + ID tokens | +| **User Info** | API calls | ID token claims | +| **Refresh** | Not supported | Automatic refresh | +| **Providers** | Single (OBP-API) | Multiple (OBP-OIDC, Keycloak, etc.) | + +--- + +## 🔑 Key Features + +### OAuth2/OIDC Benefits + +✅ **Modern Standard** - Industry-standard authentication +✅ **Better Security** - JWT tokens, PKCE flow, short-lived tokens +✅ **Auto Refresh** - Seamless token renewal +✅ **Multi-Provider** - Support for multiple identity providers +✅ **Better UX** - SSO capabilities, cleaner flow +✅ **Mobile Ready** - Native OAuth2 support in mobile apps + +### Backward Compatibility + +✅ **Feature Flag** - Switch between OAuth 1.0a and OAuth2 +✅ **Gradual Migration** - Both methods work simultaneously +✅ **Easy Rollback** - Revert with a single environment variable +✅ **No Breaking Changes** - Existing OAuth 1.0a code untouched + +--- + +## 🛠️ Key Components + +### Backend (Node.js/Express/TypeScript) + +**New Files:** +- `server/services/OAuth2Service.ts` - OAuth2/OIDC client +- `server/utils/pkce.ts` - PKCE helper functions +- `server/middlewares/OAuth2AuthorizationMiddleware.ts` +- `server/middlewares/OAuth2CallbackMiddleware.ts` +- `server/controllers/OAuth2ConnectController.ts` +- `server/controllers/OAuth2CallbackController.ts` + +**Modified Files:** +- `server/app.ts` - Initialize OAuth2Service +- `server/controllers/UserController.ts` - Support both auth methods + +### Frontend (Vue 3) + +**Modified Files:** +- `src/components/HeaderNav.vue` - Dual auth support + +### Dependencies + +**New:** +- `arctic` - Modern OAuth2/OIDC client library +- `jsonwebtoken` - JWT parsing and validation + +**Existing:** (no changes) +- `express`, `express-session`, `connect-redis`, `redis` + +--- + +## 🧪 Testing + +### Test Coverage + +- **Unit Tests** - PKCE, OAuth2Service, Middlewares +- **Integration Tests** - Full authentication flow +- **E2E Tests** - Browser automation with Playwright +- **Security Tests** - CSRF, XSS, token validation +- **Performance Tests** - Load testing, benchmarks + +### Manual Testing Checklist + +- [ ] Login flow (OAuth2) +- [ ] Logout flow +- [ ] Token refresh +- [ ] Session persistence +- [ ] Error handling +- [ ] Multiple browsers/devices +- [ ] Backward compatibility (OAuth 1.0a still works) + +--- + +## 📦 Dependencies + +### System Requirements + +- **Node.js** >= 16.14 +- **npm** >= 8.0.0 +- **Redis** >= 6.0 +- **PostgreSQL** >= 12 (for OBP database) +- **Java** 11+ (for OBP-OIDC) +- **Maven** 3.6+ (for OBP-OIDC) + +### NPM Packages (New) + +```json +{ + "dependencies": { + "arctic": "^1.0.0", + "jsonwebtoken": "^9.0.2" + }, + "devDependencies": { + "@types/jsonwebtoken": "^9.0.6" + } +} +``` + +--- + +## 🔒 Security Considerations + +### Implemented Security Measures + +✅ **PKCE** - Proof Key for Code Exchange (RFC 7636) +✅ **State Parameter** - CSRF protection +✅ **Secure Cookies** - httpOnly, secure flags +✅ **Token Validation** - JWT signature verification +✅ **HTTPS Required** - Production deployment +✅ **Short-lived Tokens** - Access token expiration +✅ **Refresh Rotation** - Refresh token rotation (if supported by provider) + +### Production Security Checklist + +- [ ] HTTPS enabled for all endpoints +- [ ] Client secrets in secure vault +- [ ] Session secrets strong and rotated +- [ ] Rate limiting implemented +- [ ] Audit logging enabled +- [ ] Security headers configured +- [ ] CORS properly set up + +--- + +## 🚨 Troubleshooting + +### Quick Fixes + +**"OIDC configuration not initialized"** +```bash +# Check OBP-OIDC is running +curl http://localhost:9000/obp-oidc/.well-known/openid-configuration +``` + +**"State validation failed"** +```bash +# Check Redis is running +redis-cli ping +# Clear browser cookies and retry +``` + +**"Code verifier not found"** +```bash +# Check session configuration in server/app.ts +# Verify Redis connection +DEBUG=express-session npm run dev +``` + +### Getting Help + +- 📖 Check [OAUTH2-QUICK-START.md](OAUTH2-QUICK-START.md) Common Issues +- 📖 Read [OAUTH2-OIDC-INTEGRATION-PREP.md](OAUTH2-OIDC-INTEGRATION-PREP.md) Troubleshooting section +- 💬 Ask in #obp-development Slack channel +- 📧 Email: dev@tesobe.com + +--- + +## 🎓 Learning Resources + +### OAuth2/OIDC Standards + +- **OAuth 2.0 Spec**: https://oauth.net/2/ +- **OpenID Connect**: https://openid.net/connect/ +- **PKCE (RFC 7636)**: https://oauth.net/2/pkce/ +- **OWASP OAuth Guide**: https://cheatsheetseries.owasp.org/cheatsheets/OAuth2_Cheatsheet.html + +### Tools + +- **OIDC Debugger**: https://oidcdebugger.com/ +- **JWT.io**: https://jwt.io/ (decode JWTs) +- **OAuth 2.0 Playground**: https://www.oauth.com/playground/ + +### Reference Implementations + +- **OBP-Portal** (`~/Documents/workspace_2024/OBP-Portal`) + - Production-ready OAuth2/OIDC implementation + - Multi-provider support + - SvelteKit-based (patterns are framework-agnostic) + +- **OBP-OIDC** (`~/Documents/workspace_2024/OBP-OIDC`) + - Minimal OIDC provider + - Perfect for development/testing + - Scala/http4s implementation + +--- + +## 📞 Support + +### For Technical Questions + +- **Development Team**: dev@tesobe.com +- **Internal Slack**: #obp-development +- **Documentation**: This directory + +### For Security Issues + +- **Security Team**: security@tesobe.com +- **Follow**: Responsible disclosure guidelines + +### For Production Issues + +- **On-call Team**: oncall@tesobe.com +- **Status Page**: status.openbankproject.com + +--- + +## 📝 Document History + +| Version | Date | Changes | +|---------|------|---------| +| 1.0 | 2024 | Initial documentation created | + +--- + +## ✅ Next Actions + +### For New Developers + +1. ✅ Read this overview (you're here!) +2. ⬜ Read [OAUTH2-QUICK-START.md](OAUTH2-QUICK-START.md) +3. ⬜ Set up local environment +4. ⬜ Test OAuth2 login flow +5. ⬜ Review [OAUTH2-OIDC-INTEGRATION-PREP.md](OAUTH2-OIDC-INTEGRATION-PREP.md) for details + +### For Implementation Team + +1. ✅ Review all documentation +2. ⬜ Set up OBP-OIDC server +3. ⬜ Create implementation branch +4. ⬜ Follow Phase 1 (Preparation) in main document +5. ⬜ Begin Phase 2 (Backend Implementation) + +### For Project Stakeholders + +1. ✅ Review Executive Summary (Section 1 of main doc) +2. ⬜ Review timeline (6 weeks) +3. ⬜ Approve implementation phases +4. ⬜ Schedule kickoff meeting +5. ⬜ Assign resources + +--- + +## 🎯 Success Criteria + +Implementation is complete when: + +- ✅ OAuth2 login flow works end-to-end +- ✅ Token refresh works automatically +- ✅ All tests passing (unit, integration, E2E) +- ✅ Documentation updated +- ✅ Backward compatibility maintained (OAuth 1.0a still works) +- ✅ Production deployment successful +- ✅ Monitoring and alerts configured +- ✅ Team trained on new system + +--- + +**Ready to begin? Start with [OAUTH2-QUICK-START.md](OAUTH2-QUICK-START.md)!** + +--- + +**License:** AGPL V3 +**Copyright:** 2024 TESOBE GmbH +**Project:** Open Bank Project - API Explorer II