- Add OAUTH2-README.md: Overview and navigation guide - Add OAUTH2-QUICK-START.md: 15-minute setup guide with code examples - Add OAUTH2-OIDC-INTEGRATION-PREP.md: Complete 60-page implementation guide Documentation covers: - 6-phase implementation plan (6 weeks) - Integration with OBP-OIDC provider - Reference implementation from OBP-Portal - Complete code examples for all components - Testing strategy and deployment guide - Backward compatibility with OAuth 1.0a
59 KiB
OAuth2/OIDC Integration Preparation Document
API Explorer II with OBP-OIDC
Version: 1.0
Date: 2024
Author: TESOBE Development Team
Table of Contents
- Executive Summary
- Current State Analysis
- Target Architecture
- OBP-OIDC Overview
- OBP-Portal Reference Implementation
- Implementation Phases
- Technical Requirements
- Configuration Changes
- Code Changes Required
- Testing Strategy
- Deployment Considerations
- Rollback Plan
- 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 (
oauthnpm 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 generationOauthAccessTokenMiddleware.ts- Handles access token exchange
- Services:
OauthInjectedService.ts- OAuth consumer managementOBPClientService.ts- OBP API client wrapper
- Controllers:
ConnectController.ts- Initiates OAuth flowCallbackController.ts- Handles OAuth callback
- Session Management: Express-session with Redis store
Frontend (Vue 3)
- Login Flow: Redirects to
/api/connect?redirect=<current_path> - User State: Fetched via
getCurrentUser()API call - Components:
HeaderNav.vue- Login/Logoff buttonsChatWidget.vue- Checks authentication for Opey accessPreview.vue- Shows login prompts for restricted features
Environment Configuration
VITE_OBP_CONSUMER_KEY=<consumer_key>
VITE_OBP_CONSUMER_SECRET=<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
- OAuth 1.0a is deprecated - Industry has moved to OAuth2
- No refresh token support - Users must re-authenticate frequently
- Single provider only - Cannot support multiple identity providers
- Complex signature generation - OAuth 1.0a requires HMAC-SHA1 signatures
- 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_clientsandv_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:
# 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=<secure_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=<random_state>
&code_challenge=<pkce_challenge>
&code_challenge_method=S256
Token Exchange:
POST http://localhost:9000/obp-oidc/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic <base64(client_id:client_secret)>
grant_type=authorization_code
&code=<auth_code>
&redirect_uri=http://localhost:5173/login/obp/callback
&code_verifier=<pkce_verifier>
UserInfo:
GET http://localhost:9000/obp-oidc/userinfo
Authorization: Bearer <access_token>
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-sessionswith Redis - Language: TypeScript
5.2 Key Components from OBP-Portal
5.2.1 OAuth2 Client (src/lib/oauth/client.ts)
import { OAuth2Client } from 'arctic'
export class OAuth2ClientWithConfig extends OAuth2Client {
OIDCConfig?: OpenIdConnectConfiguration
async initOIDCConfig(OIDCConfigUrl: string): Promise<void> {
// 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<TokenResponse> {
// 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)
class OAuth2ProviderManager {
async fetchWellKnownUris(): Promise<WellKnownUri[]> {
// 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)
class OAuth2ProviderFactory {
private strategies: Map<string, ProviderStrategy>
async initializeProvider(wellKnownUri: WellKnownUri): Promise<OAuth2ClientWithConfig | null> {
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)
export class SessionOAuthHelper {
static async handleCallback(
session: Session,
code: string,
state: string,
provider: string
): Promise<void> {
// Validate state
// Exchange code for tokens
// Store tokens in session
// Fetch user info
}
static async refreshAccessToken(session: Session, provider: string): Promise<void> {
// Use refresh token to get new access token
// Update session with new tokens
}
}
5.3 OBP-Portal Configuration (.env.example)
# 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:
// 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:
-
✅ Set up OBP-OIDC server locally
- Install dependencies (Java 11+, Maven, PostgreSQL)
- Configure database views (see OBP-API scripts)
- Copy
run-server.example.shtorun-server.sh - Update database credentials
- Start OIDC server:
./run-server.sh - Verify:
curl http://localhost:9000/obp-oidc/.well-known/openid-configuration
-
✅ Register API Explorer II client in OBP-OIDC
- Update
run-server.sh:export OIDC_CLIENT_EXPLORER_ID=obp-explorer-ii-client export OIDC_CLIENT_EXPLORER_SECRET=<generate_secure_secret> export OIDC_CLIENT_EXPLORER_REDIRECTS=http://localhost:5173/login/obp/callback - Restart OBP-OIDC
- Copy client credentials
- Update
-
✅ Study OBP-Portal implementation
- Review OAuth2 client implementation
- Understand provider manager pattern
- Study session management approach
- Document key differences from current implementation
-
⬜ Add OAuth2 dependencies
npm install arctic jsonwebtoken @types/jsonwebtokenarctic: Modern OAuth2/OIDC client libraryjsonwebtoken: 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
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<void> {
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<TokenResponse> {
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<TokenResponse> {
// Implement refresh token flow
}
async getUserInfo(accessToken: string): Promise<UserInfo> {
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
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
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<void> {
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
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<void> {
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
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
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:
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:
@Get('/user/current')
async getCurrentUser(@Session() session: any, @Res() response: Response): Promise<Response> {
// 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:
### 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=<your_secure_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_aiwith 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:
<template>
<a v-bind:href="loginUrl" v-show="isShowLoginButton" class="login-button router-link" id="login">
{{ $t('header.login') }}
</a>
</template>
<script setup lang="ts">
const useOAuth2 = import.meta.env.VITE_USE_OAUTH2 === 'true'
const loginUrl = computed(() => {
const currentPath = getCurrentPath()
return useOAuth2
? `/oauth2/connect?redirect=${encodeURIComponent(currentPath)}`
: `/api/connect?redirect=${encodeURIComponent(currentPath)}`
})
</script>
4.2 Update Logoff Handling
File: server/controllers/UserController.ts
@Get('/user/logoff')
async logoff(@Session() session: any, @Req() request: Request, @Res() response: Response): Promise<Response> {
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:
<!-- In HeaderNav.vue or a settings component -->
<el-tag v-if="import.meta.env.VITE_USE_OAUTH2 === 'true'" type="info" size="small">
OAuth2
</el-tag>
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
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
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:
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:
## 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=<your_consumer_key>
VITE_OBP_CONSUMER_SECRET=<your_consumer_secret>
VITE_OBP_REDIRECT_URL=http://localhost:5173/api/callback
```
OAuth2/OIDC (Recommended)
Modern OAuth2 with OpenID Connect support.
Configuration:
VITE_USE_OAUTH2=true
VITE_OBP_OAUTH2_CLIENT_ID=obp-explorer-ii-client
VITE_OBP_OAUTH2_CLIENT_SECRET=<your_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:
-
Clone and setup OBP-OIDC:
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 -
Copy client credentials from OBP-OIDC startup output
-
Update API Explorer II
.envfile -
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
-
Configure Environment Update
.env:VITE_USE_OAUTH2=true VITE_OBP_OAUTH2_CLIENT_ID=obp-explorer-ii-client VITE_OBP_OAUTH2_CLIENT_SECRET=<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 -
Test Authentication
- Start server:
npm run dev - Click login
- Verify redirect to OIDC provider
- Login with valid credentials
- Verify successful callback
- Start server:
-
Verify API Calls
- Test protected endpoints
- Verify Bearer token in requests
- Check token refresh on expiration
Rollback Procedure
If issues occur:
- Set
VITE_USE_OAUTH2=false - Restart server
- System reverts to OAuth 1.0a
For System Administrators
Production Deployment
-
Use Production OIDC Provider
- Don't use OBP-OIDC in production
- Recommended: Keycloak, Auth0, Okta
- Configure provider in OBP-API well-known endpoints
-
Security Requirements
- HTTPS only for all endpoints
- Secure Redis with password
- Rotate client secrets regularly
- Monitor for suspicious activity
-
Configuration
VITE_USE_OAUTH2=true VITE_OBP_OAUTH2_CLIENT_ID=<production_client_id> VITE_OBP_OAUTH2_CLIENT_SECRET=<production_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 -
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:
redis-cli
> KEYS sess:*
> GET sess:<session_id>
Testing OIDC Provider
Test discovery:
curl http://localhost:9000/obp-oidc/.well-known/openid-configuration
Test authorization (should return HTML login form):
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 authenticationv_oidc_clients- Client registrationv_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:
# 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=<secure_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):
# OAuth 1.0a (Legacy) - Still needed for backward compatibility
VITE_OBP_CONSUMER_KEY=<consumer_key>
VITE_OBP_CONSUMER_SECRET=<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=<session_secret>
# 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:
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:
export OIDC_CLIENT_EXPLORER_ID=obp-explorer-ii-client
export OIDC_CLIENT_EXPLORER_SECRET=<generate_secure_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=<url>- Start OAuth2 flowGET /oauth2/callback?code=<code>&state=<state>- OAuth2 callback
Modified Endpoints:
GET /user/current- Returns user info from OAuth2 or OAuth 1.0a sessionGET /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.tspkce.tsOAuth2AuthorizationMiddleware.tsOAuth2CallbackMiddleware.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:
- Full OAuth2 authentication flow
- Token refresh flow
- Logout flow
- Error cases (invalid code, state mismatch)
- Session persistence
- Concurrent user sessions
10.4 End-to-End Tests
Use Playwright for browser automation:
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:
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:
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:
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:
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:
// 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):
# 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:
-
Update environment variable:
export VITE_USE_OAUTH2=false -
Restart application:
pm2 restart api-explorer # OR systemctl restart api-explorer -
Verify:
- Login button points to
/api/connect - OAuth 1.0a flow works
- Existing sessions remain valid
- Login button points to
No code changes needed - feature flag handles rollback.
12.2 Partial Rollback (Specific Users)
If issues affect some users but not others:
-
Keep OAuth2 enabled globally
-
Add user-specific override in session:
if (session.forceOAuth1) { // Use OAuth 1.0a for this user } -
Or implement A/B testing:
const useOAuth2 = (userId) => { return userId % 10 < 5 // 50% of users }
12.3 Full Rollback (Remove OAuth2 Code)
If OAuth2 needs to be completely removed:
-
Delete new files:
rm server/services/OAuth2Service.ts rm server/middlewares/OAuth2*.ts rm server/controllers/OAuth2*.ts rm server/utils/pkce.ts -
Remove dependencies:
npm uninstall arctic jsonwebtoken -
Revert modified files:
git checkout server/app.ts git checkout server/controllers/UserController.ts git checkout src/components/HeaderNav.vue -
Remove environment variables from
.env -
Rebuild and deploy:
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:
-
Internal notification (immediate):
- Alert development team
- Document the issue
- Perform rollback
-
User notification (if service interruption):
- Status page update
- Email to affected users
- Estimated time to resolution
-
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
- RFC 7636: Proof Key for Code Exchange (PKCE)
- OpenID Connect Core 1.0
- OpenID Connect Discovery 1.0
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 wrappersrc/lib/oauth/providerManager.ts- Multi-provider supportsrc/lib/oauth/providerFactory.ts- Provider strategy patternsrc/lib/oauth/sessionHelper.ts- Session managementsrc/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:
OAuth 2.0 Security Best Current Practice:
Key Recommendations:
- Always use PKCE for authorization code flow
- Use state parameter to prevent CSRF
- Validate redirect URIs strictly
- Use HTTPS in production
- Store tokens securely (never in localStorage)
- Implement token refresh before expiration
- Use short-lived access tokens
- Rotate refresh tokens when possible
- Log authentication events for audit
- 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
# Feature Flag
VITE_USE_OAUTH2=true|false
# Client Credentials
VITE_OBP_OAUTH2_CLIENT_ID=<client_id>
VITE_OBP_OAUTH2_CLIENT_SECRET=<client_secret>
# Endpoints
VITE_OBP_OAUTH2_REDIRECT_URL=<callback_url>
VITE_OBP_OAUTH2_WELL_KNOWN_URL=<discovery_url>
# API Configuration
VITE_OBP_API_HOST=<obp_api_url>
VITE_OBP_API_EXPLORER_HOST=<explorer_url>
# Session
VITE_OBP_REDIS_URL=<redis_connection_string>
VITE_OPB_SERVER_SESSION_PASSWORD=<session_secret>
A.3 Common Commands
Start OBP-OIDC:
cd ~/Documents/workspace_2024/OBP-OIDC
./run-server.sh
Start API Explorer II:
cd ~/Documents/workspace_2024/API-Explorer-II
npm run dev
Test OIDC Discovery:
curl http://localhost:9000/obp-oidc/.well-known/openid-configuration | jq
Check Redis:
redis-cli ping
redis-cli KEYS "sess:*"
Run Tests:
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.tsserver/utils/pkce.tsserver/middlewares/OAuth2AuthorizationMiddleware.tsserver/middlewares/OAuth2CallbackMiddleware.tsserver/controllers/OAuth2ConnectController.tsserver/controllers/OAuth2CallbackController.tsserver/test/OAuth2Service.test.tsserver/test/pkce.test.tsdocs/OAUTH2_MIGRATION_GUIDE.mddocs/OAUTH2_TROUBLESHOOTING.md
Files to Modify:
server/app.tsserver/controllers/UserController.tssrc/components/HeaderNav.vuepackage.jsonenv_aiREADME.md
Files to Keep (Don't Delete):
server/services/OauthInjectedService.tsserver/services/OBPClientService.tsserver/middlewares/OauthRequestTokenMiddleware.tsserver/middlewares/OauthAccessTokenMiddleware.tsserver/controllers/ConnectController.tsserver/controllers/CallbackController.ts
Appendix B: Testing Scenarios
B.1 Happy Path Testing
-
New User Login
- User clicks login
- Redirects to OBP-OIDC
- User enters credentials
- Successfully redirected back
- User info displayed
- Session persists
-
Returning User
- User has valid session
- Page refresh maintains login
- API calls include Bearer token
- Token auto-refreshes before expiry
-
Logout
- User clicks logout
- Session cleared
- Redirected appropriately
- Cannot access protected resources
B.2 Error Scenarios
-
Invalid Credentials
- User enters wrong password
- OBP-OIDC shows error
- User can retry
-
State Mismatch
- Tampered state parameter
- Shows error message
- User can restart flow
-
Expired Code
- Authorization code expires
- Shows error message
- User can retry login
-
Network Errors
- OIDC provider unreachable
- Shows user-friendly error
- Option to retry
-
Token Expired
- Access token expires
- Refresh token used automatically
- User stays logged in
B.3 Edge Cases
-
Concurrent Logins
- User logs in from multiple browsers
- Each session independent
- Logout affects only current session
-
Session Timeout
- User inactive for extended period
- Session expires
- Prompted to log in again
-
Browser Refresh During Flow
- User refreshes during authorization
- Flow restarts gracefully
- No error shown
-
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