feat(oauth): add OAuth session caching to prevent multiple authentication attempts
This commit is contained in:
@@ -2,6 +2,7 @@ import * as plugins from './bunq.plugins.js';
|
||||
import { BunqApiContext } from './bunq.classes.apicontext.js';
|
||||
import { BunqMonetaryAccount } from './bunq.classes.monetaryaccount.js';
|
||||
import { BunqUser } from './bunq.classes.user.js';
|
||||
import { BunqApiError } from './bunq.classes.httpclient.js';
|
||||
import type { IBunqSessionServerResponse } from './bunq.interfaces.js';
|
||||
|
||||
export interface IBunqConstructorOptions {
|
||||
@@ -16,6 +17,9 @@ export interface IBunqConstructorOptions {
|
||||
* the main bunq account
|
||||
*/
|
||||
export class BunqAccount {
|
||||
// Static cache for OAuth token sessions to prevent multiple authentication attempts
|
||||
private static oauthSessionCache = new Map<string, BunqApiContext>();
|
||||
|
||||
public options: IBunqConstructorOptions;
|
||||
public apiContext: BunqApiContext;
|
||||
public userId: number;
|
||||
@@ -31,17 +35,60 @@ export class BunqAccount {
|
||||
* Initialize the bunq account
|
||||
*/
|
||||
public async init() {
|
||||
// Create API context
|
||||
this.apiContext = new BunqApiContext({
|
||||
apiKey: this.options.apiKey,
|
||||
environment: this.options.environment,
|
||||
deviceDescription: this.options.deviceName,
|
||||
permittedIps: this.options.permittedIps,
|
||||
isOAuthToken: this.options.isOAuthToken
|
||||
});
|
||||
// For OAuth tokens, check if we already have a cached session
|
||||
if (this.options.isOAuthToken) {
|
||||
const cacheKey = `${this.options.apiKey}_${this.options.environment}`;
|
||||
const cachedContext = BunqAccount.oauthSessionCache.get(cacheKey);
|
||||
|
||||
if (cachedContext && cachedContext.hasValidSession()) {
|
||||
// Reuse existing session
|
||||
this.apiContext = cachedContext;
|
||||
console.log('Reusing existing OAuth session from cache');
|
||||
} else {
|
||||
// Create new context and cache it
|
||||
this.apiContext = new BunqApiContext({
|
||||
apiKey: this.options.apiKey,
|
||||
environment: this.options.environment,
|
||||
deviceDescription: this.options.deviceName,
|
||||
permittedIps: this.options.permittedIps,
|
||||
isOAuthToken: this.options.isOAuthToken
|
||||
});
|
||||
|
||||
try {
|
||||
await this.apiContext.init();
|
||||
// Cache the successfully initialized context
|
||||
BunqAccount.oauthSessionCache.set(cacheKey, this.apiContext);
|
||||
} catch (error) {
|
||||
// Handle "Superfluous authentication" or "Authentication token already has a user session" errors
|
||||
if (error instanceof BunqApiError) {
|
||||
const errorMessages = error.errors.map(e => e.error_description).join(' ');
|
||||
if (errorMessages.includes('Superfluous authentication') ||
|
||||
errorMessages.includes('Authentication token already has a user session')) {
|
||||
console.log('OAuth token already has an active session, attempting to reuse...');
|
||||
// Try to use the token directly without creating new session
|
||||
await this.apiContext.initWithExistingSession();
|
||||
// Cache the context with existing session
|
||||
BunqAccount.oauthSessionCache.set(cacheKey, this.apiContext);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Regular API key flow
|
||||
this.apiContext = new BunqApiContext({
|
||||
apiKey: this.options.apiKey,
|
||||
environment: this.options.environment,
|
||||
deviceDescription: this.options.deviceName,
|
||||
permittedIps: this.options.permittedIps,
|
||||
isOAuthToken: this.options.isOAuthToken
|
||||
});
|
||||
|
||||
// Initialize API context (handles installation, device registration, session)
|
||||
await this.apiContext.init();
|
||||
await this.apiContext.init();
|
||||
}
|
||||
|
||||
// Create user instance
|
||||
this.bunqUser = new BunqUser(this.apiContext);
|
||||
@@ -160,4 +207,28 @@ export class BunqAccount {
|
||||
this.apiContext = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the OAuth session cache
|
||||
*/
|
||||
public static clearOAuthCache(): void {
|
||||
BunqAccount.oauthSessionCache.clear();
|
||||
console.log('OAuth session cache cleared');
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear a specific OAuth token from the cache
|
||||
*/
|
||||
public static clearOAuthCacheForToken(apiKey: string, environment: 'SANDBOX' | 'PRODUCTION'): void {
|
||||
const cacheKey = `${apiKey}_${environment}`;
|
||||
BunqAccount.oauthSessionCache.delete(cacheKey);
|
||||
console.log(`OAuth session cache cleared for token in ${environment} environment`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current size of the OAuth cache
|
||||
*/
|
||||
public static getOAuthCacheSize(): number {
|
||||
return BunqAccount.oauthSessionCache.size;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user