Files
accounted/extensions/enable-banking/lib/jwt.ts
T
Jakob WennbergandClaude Opus 4.6 885f362a29 feat: add bank file import as core, move Enable Banking to extension
Replace PSD2 bank integration as the default with file-based bank
import (CSV/XML), which better suits Swedish sole traders and small
companies. Enable Banking is now an opt-in extension.

- Phase 1: Extract generic transaction ingestion service (ingest.ts)
  with dedup, auto-categorization, and OCR-based invoice matching
- Phase 2: Bank file parser library supporting Nordea, SEB, Swedbank,
  Handelsbanken CSV formats and ISO 20022 camt.053 XML
- Phase 3: Database migration adding import_source, reference columns
  and bank_file_imports tracking table
- Phase 4: Import wizard UI (5-step flow) and API routes for parse/execute
- Phase 5: Move Enable Banking to extensions/enable-banking/ with
  commented-out loader entry for opt-in activation
- Phase 6: 104 new tests (ingestion + all parser formats), fixing
  Nordea detection overlap and camt.053 XML tag collision bugs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 14:21:28 +01:00

92 lines
2.3 KiB
TypeScript

/**
* JWT generation for Enable Banking API authentication
*
* Enable Banking requires JWT tokens signed with RS256 using your private key.
* The JWT is included in the Authorization header for all API calls.
*/
import * as crypto from 'crypto'
const APP_ID = process.env.ENABLE_BANKING_APP_ID
const PRIVATE_KEY_BASE64 = process.env.ENABLE_BANKING_PRIVATE_KEY
interface JWTHeader {
typ: string
alg: string
kid: string
}
interface JWTPayload {
iss: string
aud: string
iat: number
exp: number
}
function base64UrlEncode(data: Buffer | string): string {
const str = typeof data === 'string' ? data : data.toString('base64')
return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
}
function getPrivateKey(): string {
if (!PRIVATE_KEY_BASE64) {
throw new Error('ENABLE_BANKING_PRIVATE_KEY environment variable is not set')
}
// Decode base64 to get PEM format private key
const privateKeyPem = Buffer.from(PRIVATE_KEY_BASE64, 'base64').toString('utf-8')
return privateKeyPem
}
/**
* Generate a JWT token for Enable Banking API authentication
*
* @param expiresInSeconds - Token validity in seconds (default: 3600 = 1 hour)
* @returns Signed JWT token
*/
export function generateJWT(expiresInSeconds: number = 3600): string {
if (!APP_ID) {
throw new Error('ENABLE_BANKING_APP_ID environment variable is not set')
}
const now = Math.floor(Date.now() / 1000)
const header: JWTHeader = {
typ: 'JWT',
alg: 'RS256',
kid: APP_ID
}
const payload: JWTPayload = {
iss: 'enablebanking.com',
aud: 'api.enablebanking.com',
iat: now,
exp: now + expiresInSeconds
}
// Encode header and payload
const headerBase64 = base64UrlEncode(Buffer.from(JSON.stringify(header)))
const payloadBase64 = base64UrlEncode(Buffer.from(JSON.stringify(payload)))
// Create signature
const signatureInput = `${headerBase64}.${payloadBase64}`
const privateKey = getPrivateKey()
const sign = crypto.createSign('RSA-SHA256')
sign.update(signatureInput)
sign.end()
const signature = sign.sign(privateKey)
const signatureBase64 = base64UrlEncode(signature)
return `${headerBase64}.${payloadBase64}.${signatureBase64}`
}
/**
* Get the Authorization header value for Enable Banking API
*/
export function getAuthorizationHeader(): string {
const token = generateJWT()
return `Bearer ${token}`
}