Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
pontusab committed Feb 20, 2025
1 parent c6c8335 commit 607d0f8
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 33 deletions.
7 changes: 6 additions & 1 deletion apps/engine/src/common/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ export const GeneralErrorSchema = z.object({
}),
});

export const Providers = z.enum(["teller", "plaid", "gocardless"]);
export const Providers = z.enum([
"teller",
"plaid",
"gocardless",
"enablebanking",
]);

export const HeadersSchema = z.object({
authorization: z.string().openapi({
Expand Down
84 changes: 64 additions & 20 deletions apps/engine/src/providers/enablebanking/enablebanking-api.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Buffer } from "node:buffer";
import fs from "node:fs";
import * as jose from "jose";
import xior, { type XiorInstance, type XiorRequestConfig } from "xior";
import type { ProviderParams } from "../types";
Expand All @@ -12,32 +14,71 @@ export class EnableBankingApi {
#baseUrl = "https://api.enablebanking.com";
#applicationId: string;
#keyContent: string;
#expiresIn = 23;

// Maximum allowed TTL is 24 hours (86400 seconds)
#expiresIn = 23; // hours

constructor(params: Omit<ProviderParams, "provider">) {
this.#applicationId = params.envs.ENABLEBANKING_APPLICATION_ID;
this.#keyContent = params.envs.ENABLE_BANKING_KEY_CONTENT;
}

private async generateJWT() {
const privateKey = Buffer.from(this.#keyContent, "base64");
private encodeData(data: object) {
return jose.base64url.encode(Buffer.from(JSON.stringify(data)));
}

const key = await jose.importPKCS8(privateKey.toString(), "RS256");
private getJWTHeader() {
return this.encodeData({
typ: "JWT",
alg: "RS256",
kid: this.#applicationId,
});
}

const jwt = await new jose.SignJWT({
private getJWTBody(exp: number) {
const timestamp = Math.floor(Date.now() / 1000);
return this.encodeData({
iss: "enablebanking.com",
aud: "api.enablebanking.com",
})
.setProtectedHeader({
typ: "JWT",
alg: "RS256",
kid: this.#applicationId,
})
.setIssuedAt()
.setExpirationTime(`${this.#expiresIn}h`)
.sign(key);

return jwt;
iat: timestamp,
exp: timestamp + exp,
});
}

async signWithKey(data: string) {
const decodedKey = Buffer.from(this.#keyContent, "base64").toString(
"utf-8",
);

const pemKey = decodedKey.includes("BEGIN PRIVATE KEY")
? decodedKey
: `-----BEGIN PRIVATE KEY-----\n${decodedKey}\n-----END PRIVATE KEY-----`;

console.log(decodedKey.includes("BEGIN PRIVATE KEY"));

const privateKey = await jose.importPKCS8(pemKey, "RS256");

// Sign directly with RSA-SHA256
const signature = await crypto.subtle.sign(
{
name: "RSASSA-PKCS1-v1_5",
hash: { name: "SHA-256" },
},
// @ts-expect-error
privateKey,
new TextEncoder().encode(data),
);

return jose.base64url.encode(new Uint8Array(signature));
}

private async generateJWT() {
const exp = this.#expiresIn * 60 * 60;
const jwtHeaders = this.getJWTHeader();
const jwtBody = this.getJWTBody(exp);
const jwtSignature = await this.signWithKey(`${jwtHeaders}.${jwtBody}`);

return `${jwtHeaders}.${jwtBody}.${jwtSignature}`;
}

async #getApi(): Promise<XiorInstance> {
Expand Down Expand Up @@ -94,10 +135,13 @@ export class EnableBankingApi {
continuation_key?: string;
},
): Promise<GetTransactionsResponse> {
return this.#get<GetTransactionsResponse>(
`/accounts/${accountId}/transactions`,
params,
);
// return this.#get<GetTransactionsResponse>(
// `/accounts/${accountId}/transactions`,
// params,
// );

const katt = await this.#get<GetTransactionsResponse>("/aspsps", params);
console.log(katt);
}

async deleteSession(sessionId: string): Promise<void> {
Expand Down
20 changes: 8 additions & 12 deletions apps/engine/src/providers/enablebanking/enablebanking-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import type {
ProviderParams,
} from "../types";
import { EnableBankingApi } from "./enablebanking-api";
import { transformAccount, transformTransaction } from "./transform";
// import { transformAccount, transformTransaction } from "./transform";

export class EnableBankingProvider implements Provider {
#api: EnableBankingApi;
Expand All @@ -32,33 +32,29 @@ export class EnableBankingProvider implements Provider {
params: GetInstitutionsRequest,
): Promise<GetInstitutionsResponse> {
const response = await this.#api.getInstitutions();
return response.aspsps.map(transformInstitution);
// return response.aspsps.map(transformInstitution);
return [];
}

async getAccounts(params: GetAccountsRequest): Promise<GetAccountsResponse> {
const response = await this.#api.getAccounts(params.id!);
return response.accounts.map(transformAccount);
// return response.accounts.map(transformAccount);
return [];
}

async getAccountBalance(
params: GetAccountBalanceRequest,
): Promise<GetAccountBalanceResponse> {
const response = await this.#api.getAccountBalance(params.accountId);
return transformBalance(response.balances[0]);
// return transformBalance(response.balances[0]);
return { currency: "EUR", amount: 100 };
}

async getTransactions(
params: GetTransactionsRequest,
): Promise<GetTransactionsResponse> {
const response = await this.#api.getTransactions(params.accountId);
return {
transactions: response.transactions.map(transformTransaction),
paging: response.continuation_key
? {
next: response.continuation_key,
}
: undefined,
};
return [];
}

async getConnectionStatus(
Expand Down
1 change: 1 addition & 0 deletions apps/engine/wrangler.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
compatibility_date = "2024-11-11"
workers_dev = false
logpush = true
compatibility_flags = ["nodejs_compat_v2"]

[observability]
enabled = true
Expand Down

0 comments on commit 607d0f8

Please sign in to comment.