mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 21:50:57 +00:00
MoR stamps systemNumber and systemType into the access token it issues for the authenticating credentials, which makes the token the authority on them. Registration now reads both from there instead of from configuration, so the SourceSystem block cannot drift from what the gateway believes we are. EimsAuthService decodes the token payload after login, requires both claims to be non-empty, and exposes them through getSessionContext(). The token is decoded but never verified -- it is MoR's, signed with MoR's key -- and is kept out of the log line, which names only the system it identified. EIMS_SYSTEM_NUMBER and EIMS_SYSTEM_TYPE become optional expectations rather than inputs: when set they are compared against the claims and a mismatch fails fast, so neither side silently wins. Neither is required to register any more. Registration and manual resolution both resolve the session before touching the state row, which is keyed by the system number: a login failure now costs nothing because no counter has been reserved yet. Test fixtures move to eims-test-fixtures.ts. They previously lived in eims-auth.service.spec.ts, which made jest execute that suite again inside every importing spec. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
76 lines
2.4 KiB
TypeScript
76 lines
2.4 KiB
TypeScript
import { EimsConfig, EimsInvoiceConfig } from "../../config/eims.config";
|
|
|
|
/**
|
|
* Fixtures shared by the EIMS specs.
|
|
*
|
|
* Deliberately not a `.spec.ts`: importing fixtures from a spec file makes jest execute that
|
|
* file's `describe` blocks inside every importing suite, so the same tests run — and report —
|
|
* twice.
|
|
*/
|
|
|
|
export const EIMS_SYSTEM_NUMBER = "B0360154BA";
|
|
export const EIMS_SYSTEM_TYPE = "SYS";
|
|
|
|
export const eimsInvoiceConfig = (over: Partial<EimsInvoiceConfig> = {}): EimsInvoiceConfig => ({
|
|
sellerLegalName: "Ethio-Djibouti Railway S.C.",
|
|
sellerVatNumber: "0000000000",
|
|
sellerPhone: "0911223344",
|
|
sellerEmail: "finance@example.et",
|
|
sellerRegion: "13",
|
|
sellerWereda: "574",
|
|
sellerCity: null,
|
|
sellerSubCity: null,
|
|
sellerHouseNumber: null,
|
|
sellerLocality: null,
|
|
taxCode: "VAT15",
|
|
taxRatePercent: 15,
|
|
exciseTaxValue: 0,
|
|
incomeWithholdValue: 0,
|
|
transactionWithholdValue: 0,
|
|
transactionType: "B2B",
|
|
natureOfSupplies: "Service",
|
|
paymentMode: "CASH",
|
|
paymentTerm: "IMMIDIATE",
|
|
unitDefault: "PCS",
|
|
buyerCountryCode: null,
|
|
cashierName: null,
|
|
salesPersonName: null,
|
|
...over,
|
|
});
|
|
|
|
export const eimsConfig = (over: Partial<EimsConfig> = {}): EimsConfig => ({
|
|
enabled: true,
|
|
baseUrl: "https://core.mor.gov.et",
|
|
clientId: "cid",
|
|
clientSecret: "super-secret-value",
|
|
apiKey: "super-secret-apikey",
|
|
tin: "0000034558",
|
|
systemNumber: EIMS_SYSTEM_NUMBER,
|
|
systemType: EIMS_SYSTEM_TYPE,
|
|
privateKeyPath: "/dev/null",
|
|
certificatePath: "/dev/null",
|
|
httpTimeoutMs: 30_000,
|
|
tokenSkewMs: 45_000,
|
|
invoice: eimsInvoiceConfig(),
|
|
...over,
|
|
});
|
|
|
|
/**
|
|
* A structurally real access token. MoR stamps the source-system identity into the JWT payload and
|
|
* `EimsAuthService` reads it from there; only the payload segment is meaningful, since the token is
|
|
* never verified locally — it is MoR's, signed with MoR's key.
|
|
*
|
|
* Pass a claim as `undefined` to omit it (spreading beats `delete`, which the defaults would undo).
|
|
*/
|
|
export const eimsToken = (claims: Record<string, unknown> = {}): string => {
|
|
const payload = { systemNumber: EIMS_SYSTEM_NUMBER, systemType: EIMS_SYSTEM_TYPE, ...claims };
|
|
for (const [key, value] of Object.entries(payload)) {
|
|
if (value === undefined) delete (payload as Record<string, unknown>)[key];
|
|
}
|
|
return [
|
|
"eyJhbGciOiJSUzI1NiJ9",
|
|
Buffer.from(JSON.stringify(payload)).toString("base64url"),
|
|
"signature",
|
|
].join(".");
|
|
};
|