update
This commit is contained in:
196
ts/bunq.classes.session.ts
Normal file
196
ts/bunq.classes.session.ts
Normal file
@@ -0,0 +1,196 @@
|
||||
import * as plugins from './bunq.plugins';
|
||||
import { BunqHttpClient } from './bunq.classes.httpclient';
|
||||
import { BunqCrypto } from './bunq.classes.crypto';
|
||||
import {
|
||||
IBunqApiContext,
|
||||
IBunqInstallationResponse,
|
||||
IBunqDeviceServerResponse,
|
||||
IBunqSessionServerResponse
|
||||
} from './bunq.interfaces';
|
||||
|
||||
export class BunqSession {
|
||||
private httpClient: BunqHttpClient;
|
||||
private crypto: BunqCrypto;
|
||||
private context: IBunqApiContext;
|
||||
private sessionExpiryTime: plugins.smarttime.TimeStamp;
|
||||
|
||||
constructor(crypto: BunqCrypto, context: IBunqApiContext) {
|
||||
this.crypto = crypto;
|
||||
this.context = context;
|
||||
this.httpClient = new BunqHttpClient(crypto, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a new bunq API session
|
||||
*/
|
||||
public async init(deviceDescription: string, permittedIps: string[] = []): Promise<void> {
|
||||
// Step 1: Installation
|
||||
await this.createInstallation();
|
||||
|
||||
// Step 2: Device registration
|
||||
await this.registerDevice(deviceDescription, permittedIps);
|
||||
|
||||
// Step 3: Session creation
|
||||
await this.createSession();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create installation and exchange keys
|
||||
*/
|
||||
private async createInstallation(): Promise<void> {
|
||||
// Generate RSA key pair if not already generated
|
||||
if (!this.crypto.getPublicKey()) {
|
||||
await this.crypto.generateKeyPair();
|
||||
}
|
||||
|
||||
const response = await this.httpClient.post<IBunqInstallationResponse>('/v1/installation', {
|
||||
client_public_key: this.crypto.getPublicKey()
|
||||
});
|
||||
|
||||
// Extract installation token and server public key
|
||||
let installationToken: string;
|
||||
let serverPublicKey: string;
|
||||
|
||||
for (const item of response.Response) {
|
||||
if (item.Token) {
|
||||
installationToken = item.Token.token;
|
||||
}
|
||||
if (item.ServerPublicKey) {
|
||||
serverPublicKey = item.ServerPublicKey.server_public_key;
|
||||
}
|
||||
}
|
||||
|
||||
if (!installationToken || !serverPublicKey) {
|
||||
throw new Error('Failed to get installation token or server public key');
|
||||
}
|
||||
|
||||
// Update context
|
||||
this.context.installationToken = installationToken;
|
||||
this.context.serverPublicKey = serverPublicKey;
|
||||
this.context.clientPrivateKey = this.crypto.getPrivateKey();
|
||||
this.context.clientPublicKey = this.crypto.getPublicKey();
|
||||
|
||||
// Update HTTP client context
|
||||
this.httpClient.updateContext({
|
||||
installationToken,
|
||||
serverPublicKey
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the device
|
||||
*/
|
||||
private async registerDevice(description: string, permittedIps: string[] = []): Promise<void> {
|
||||
const response = await this.httpClient.post<IBunqDeviceServerResponse>('/v1/device-server', {
|
||||
description,
|
||||
secret: this.context.apiKey,
|
||||
permitted_ips: permittedIps.length > 0 ? permittedIps : undefined
|
||||
});
|
||||
|
||||
// Device is now registered
|
||||
if (!response.Response || !response.Response[0] || !response.Response[0].Id) {
|
||||
throw new Error('Failed to register device');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new session
|
||||
*/
|
||||
private async createSession(): Promise<void> {
|
||||
const response = await this.httpClient.post<IBunqSessionServerResponse>('/v1/session-server', {
|
||||
secret: this.context.apiKey
|
||||
});
|
||||
|
||||
// Extract session token and user info
|
||||
let sessionToken: string;
|
||||
let userId: number;
|
||||
|
||||
for (const item of response.Response) {
|
||||
if (item.Token) {
|
||||
sessionToken = item.Token.token;
|
||||
}
|
||||
if (item.UserPerson) {
|
||||
userId = item.UserPerson.id;
|
||||
} else if (item.UserCompany) {
|
||||
userId = item.UserCompany.id;
|
||||
} else if (item.UserApiKey) {
|
||||
userId = item.UserApiKey.id;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sessionToken || !userId) {
|
||||
throw new Error('Failed to create session');
|
||||
}
|
||||
|
||||
// Update context
|
||||
this.context.sessionToken = sessionToken;
|
||||
|
||||
// Update HTTP client context
|
||||
this.httpClient.updateContext({
|
||||
sessionToken
|
||||
});
|
||||
|
||||
// Set session expiry (bunq sessions expire after 10 minutes of inactivity)
|
||||
this.sessionExpiryTime = plugins.smarttime.TimeStamp.fromMilliSeconds(Date.now() + 600000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if session is still valid
|
||||
*/
|
||||
public isSessionValid(): boolean {
|
||||
if (!this.sessionExpiryTime) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const now = new plugins.smarttime.TimeStamp();
|
||||
return this.sessionExpiryTime.isYoungerThanOtherTimeStamp(now);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the session if needed
|
||||
*/
|
||||
public async refreshSession(): Promise<void> {
|
||||
if (!this.isSessionValid()) {
|
||||
await this.createSession();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the current session
|
||||
*/
|
||||
public async destroySession(): Promise<void> {
|
||||
if (this.context.sessionToken) {
|
||||
try {
|
||||
await this.httpClient.delete('/v1/session/' + this.getSessionId());
|
||||
} catch (error) {
|
||||
// Ignore errors when destroying session
|
||||
}
|
||||
|
||||
this.context.sessionToken = null;
|
||||
this.httpClient.updateContext({ sessionToken: null });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current session ID from the token
|
||||
*/
|
||||
private getSessionId(): string {
|
||||
// In a real implementation, we would need to store the session ID
|
||||
// For now, return a placeholder
|
||||
return '0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTTP client for making API requests
|
||||
*/
|
||||
public getHttpClient(): BunqHttpClient {
|
||||
return this.httpClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current context
|
||||
*/
|
||||
public getContext(): IBunqApiContext {
|
||||
return this.context;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user