@tesouro/embedded-components-widget-token 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,135 @@
1
+ # @tesouro/embedded-components-widget-token
2
+
3
+ Server-side helpers for minting widget tokens used by Tesouro's embedded
4
+ components. Mints an [RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693)
5
+ token-exchange request and encrypts it as a JWE (`A256KW` + `A256GCM`) using
6
+ your shared widget secret.
7
+
8
+ > **Server-only.** This package handles your `clientSecret` and `widgetSecret`,
9
+ > so keep it on the server and never bundle it into client code. It runs in any
10
+ > server runtime — a Next.js Route Handler, an Express server, or a Lambda
11
+ > handler.
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ yarn add @tesouro/embedded-components-widget-token
17
+ # or
18
+ npm install @tesouro/embedded-components-widget-token
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ### One-shot
24
+
25
+ ```ts
26
+ import { createWidgetToken } from '@tesouro/embedded-components-widget-token';
27
+
28
+ const { widgetToken, exp } = await createWidgetToken({
29
+ clientId: process.env.TESOURO_CLIENT_ID!,
30
+ clientSecret: process.env.TESOURO_CLIENT_SECRET!,
31
+ widgetSecret: process.env.TESOURO_WIDGET_SECRET!,
32
+ userId: 'user_123',
33
+ userEmail: 'user@example.com',
34
+ });
35
+ ```
36
+
37
+ `exp` is the token's expiration time in seconds since epoch — return it to the
38
+ client so it can refresh before expiry.
39
+
40
+ ### Pre-configured factory
41
+
42
+ When the same credentials are reused across many requests, bind them once with
43
+ `configureCreateWidgetToken`:
44
+
45
+ ```ts
46
+ import { configureCreateWidgetToken } from '@tesouro/embedded-components-widget-token';
47
+
48
+ const mintWidgetToken = configureCreateWidgetToken({
49
+ clientId: process.env.TESOURO_CLIENT_ID!,
50
+ clientSecret: process.env.TESOURO_CLIENT_SECRET!,
51
+ widgetSecret: process.env.TESOURO_WIDGET_SECRET!,
52
+ });
53
+
54
+ const { widgetToken, exp } = await mintWidgetToken({
55
+ userId: 'user_123',
56
+ userEmail: 'user@example.com',
57
+ });
58
+ ```
59
+
60
+ ### Next.js Route Handler
61
+
62
+ ```ts
63
+ // app/api/widget-token/route.ts
64
+ import { NextRequest, NextResponse } from 'next/server';
65
+ import { configureCreateWidgetToken } from '@tesouro/embedded-components-widget-token';
66
+
67
+ const mintWidgetToken = configureCreateWidgetToken({
68
+ clientId: process.env.TESOURO_CLIENT_ID!,
69
+ clientSecret: process.env.TESOURO_CLIENT_SECRET!,
70
+ widgetSecret: process.env.TESOURO_WIDGET_SECRET!,
71
+ });
72
+
73
+ export async function POST(req: NextRequest) {
74
+ const { userId, userEmail } = await req.json();
75
+ const { widgetToken, exp } = await mintWidgetToken({ userId, userEmail });
76
+ return NextResponse.json({ widgetToken, exp });
77
+ }
78
+ ```
79
+
80
+ ## API
81
+
82
+ ### `createWidgetToken(params): Promise<WidgetTokenResult>`
83
+
84
+ | Param | Type | Required | Description |
85
+ | --------------------- | -------- | -------- | -------------------------------------------------------------------------------------------- |
86
+ | `clientId` | `string` | yes | Tesouro-issued client identifier. Used as JWE `kid` and as `iss`/`client_id` in the payload. |
87
+ | `clientSecret` | `string` | yes | Tesouro-issued client secret. Carried as `client_secret` inside the encrypted payload. |
88
+ | `widgetSecret` | `string` | yes | Shared symmetric key used to encrypt the JWE. |
89
+ | `userId` | `string` | yes | Subject (`sub`) of the inner identity token. |
90
+ | `userEmail` | `string` | yes | Email claim of the inner identity token. |
91
+ | `expirationInSeconds` | `number` | no | Token TTL in seconds. Defaults to `DEFAULT_EXPIRATION_IN_SECONDS` (480 — 8 minutes). |
92
+
93
+ Returns:
94
+
95
+ ```ts
96
+ interface WidgetTokenResult {
97
+ widgetToken: string; // compact-serialized JWE
98
+ exp: number; // expiration in seconds since epoch (UTC)
99
+ }
100
+ ```
101
+
102
+ Throws if `clientId`, `clientSecret`, or `widgetSecret` is missing or empty.
103
+
104
+ ### `configureCreateWidgetToken(creds)`
105
+
106
+ Binds `clientId`, `clientSecret`, `widgetSecret`, and an optional default
107
+ `expirationInSeconds`, and returns a function that only takes per-request
108
+ `userId` / `userEmail` (and an optional per-call `expirationInSeconds` that
109
+ overrides the bound default).
110
+
111
+ ### `DEFAULT_EXPIRATION_IN_SECONDS`
112
+
113
+ `480` — the default token lifetime in seconds (8 minutes).
114
+
115
+ ## Token format
116
+
117
+ `createWidgetToken` produces a JWE with the following protected header:
118
+
119
+ ```json
120
+ {
121
+ "alg": "A256KW",
122
+ "enc": "A256GCM",
123
+ "kid": "<clientId>",
124
+ "typ": "JWT",
125
+ "cty": "JWT"
126
+ }
127
+ ```
128
+
129
+ The encrypted payload is an RFC 8693 token-exchange request whose
130
+ `subject_token` is an unsigned identity JWT (`alg: none`) bearing the user's
131
+ `sub`, `email`, `iss`, `aud`, `iat`, and `exp` claims.
132
+
133
+ ## License
134
+
135
+ UNLICENSED — internal Tesouro package.
@@ -0,0 +1,2 @@
1
+ export * from './lib/createWidgetToken.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ import { DEFAULT_EXPIRATION_IN_SECONDS as r, configureCreateWidgetToken as t, createWidgetToken as T } from "./index2.js";
2
+ export {
3
+ r as DEFAULT_EXPIRATION_IN_SECONDS,
4
+ t as configureCreateWidgetToken,
5
+ T as createWidgetToken
6
+ };
package/dist/index2.js ADDED
@@ -0,0 +1,82 @@
1
+ import d from "crypto";
2
+ import { EncryptJWT as y } from "jose";
3
+ const k = 480;
4
+ async function T({
5
+ clientId: e,
6
+ clientSecret: o,
7
+ widgetSecret: r,
8
+ userId: i,
9
+ userEmail: s,
10
+ organizationReference: a,
11
+ expirationInSeconds: c = k
12
+ }) {
13
+ if (!r || !e || !o)
14
+ throw new Error(
15
+ "Missing required parameters for widget token generation: clientId, clientSecret, and widgetSecret are required"
16
+ );
17
+ const t = Math.floor(Date.now() / 1e3), n = t + c, u = new TextEncoder().encode(r), f = {
18
+ sub: i,
19
+ // Subject (user identifier)
20
+ email: s,
21
+ // Email claim
22
+ aud: "tesouro",
23
+ // Audience
24
+ iss: e,
25
+ // Issuer
26
+ exp: n,
27
+ // Expiration
28
+ iat: t
29
+ // Issued at
30
+ }, g = Buffer.from(JSON.stringify({ alg: "none", typ: "JWT" })).toString(
31
+ "base64url"
32
+ ) + "." + Buffer.from(JSON.stringify(f)).toString("base64url") + ".", p = {
33
+ aud: "tesouro",
34
+ client_id: e,
35
+ client_secret: o,
36
+ exp: n,
37
+ grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
38
+ iat: t,
39
+ iss: e,
40
+ jti: d.randomUUID(),
41
+ ...a ? { organization_reference: a } : {},
42
+ subject_token: g,
43
+ subject_token_type: "urn:ietf:params:oauth:token-type:access_token"
44
+ };
45
+ return { widgetToken: await new y(p).setProtectedHeader({
46
+ alg: "A256KW",
47
+ enc: "A256GCM",
48
+ kid: e,
49
+ typ: "JWT",
50
+ cty: "JWT"
51
+ }).encrypt(u), exp: n };
52
+ }
53
+ function b({
54
+ clientId: e,
55
+ clientSecret: o,
56
+ widgetSecret: r,
57
+ organizationReference: i,
58
+ expirationInSeconds: s
59
+ }) {
60
+ return ({
61
+ userId: a,
62
+ userEmail: c,
63
+ // Defaults to the partner-config value bound at configure time. A per-call
64
+ // override supports partners whose single client serves multiple
65
+ // organizations; it must still be resolved server-side, never from the browser.
66
+ organizationReference: t = i,
67
+ expirationInSeconds: n = s
68
+ }) => T({
69
+ clientId: e,
70
+ clientSecret: o,
71
+ widgetSecret: r,
72
+ userId: a,
73
+ userEmail: c,
74
+ organizationReference: t,
75
+ expirationInSeconds: n
76
+ });
77
+ }
78
+ export {
79
+ k as DEFAULT_EXPIRATION_IN_SECONDS,
80
+ b as configureCreateWidgetToken,
81
+ T as createWidgetToken
82
+ };
@@ -0,0 +1,26 @@
1
+ export declare const DEFAULT_EXPIRATION_IN_SECONDS = 480;
2
+ export interface CreateWidgetTokenParams {
3
+ clientId: string;
4
+ clientSecret: string;
5
+ widgetSecret: string;
6
+ userId: string;
7
+ userEmail: string;
8
+ /**
9
+ * The partner's stable, opaque identifier for the business/organization
10
+ * (SMB grouping) the applicant belongs to. Minted into the outer JWE payload
11
+ * as `organization_reference` so the widget proxy can call
12
+ * embedded-banking/v1/application-status to drive widget visibility.
13
+ *
14
+ * MUST be resolved server-side from the partner's organization config — never
15
+ * accept this value from the browser.
16
+ */
17
+ organizationReference?: string;
18
+ expirationInSeconds?: number;
19
+ }
20
+ export interface WidgetTokenResult {
21
+ widgetToken: string;
22
+ exp: number;
23
+ }
24
+ export declare function createWidgetToken({ clientId, clientSecret, widgetSecret, userId, userEmail, organizationReference, expirationInSeconds, }: CreateWidgetTokenParams): Promise<WidgetTokenResult>;
25
+ export declare function configureCreateWidgetToken({ clientId, clientSecret, widgetSecret, organizationReference: defaultOrganizationReference, expirationInSeconds: defaultExpiration, }: Pick<CreateWidgetTokenParams, 'clientId' | 'clientSecret' | 'widgetSecret' | 'organizationReference' | 'expirationInSeconds'>): ({ userId, userEmail, organizationReference, expirationInSeconds, }: Pick<CreateWidgetTokenParams, "userId" | "userEmail" | "organizationReference" | "expirationInSeconds">) => Promise<WidgetTokenResult>;
26
+ //# sourceMappingURL=createWidgetToken.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createWidgetToken.d.ts","sourceRoot":"","sources":["../../src/lib/createWidgetToken.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,6BAA6B,MAAM,CAAC;AAEjD,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;;;OAQG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAsB,iBAAiB,CAAC,EACtC,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,MAAM,EACN,SAAS,EACT,qBAAqB,EACrB,mBAAmD,GACpD,EAAE,uBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA6DtD;AAED,wBAAgB,0BAA0B,CAAC,EACzC,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,qBAAqB,EAAE,4BAA4B,EACnD,mBAAmB,EAAE,iBAAiB,GACvC,EAAE,IAAI,CACL,uBAAuB,EACrB,UAAU,GACV,cAAc,GACd,cAAc,GACd,uBAAuB,GACvB,qBAAqB,CACxB,IACS,oEAQL,IAAI,CACL,uBAAuB,EACvB,QAAQ,GAAG,WAAW,GAAG,uBAAuB,GAAG,qBAAqB,CACzE,gCAUF"}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@tesouro/embedded-components-widget-token",
3
+ "version": "0.0.1",
4
+ "license": "Apache-2.0",
5
+ "type": "module",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/tesourohq/Tesouro.Payments.Frontends.git",
9
+ "directory": "packages/embedded-components-widget-token"
10
+ },
11
+ "homepage": "https://github.com/tesourohq/Tesouro.Payments.Frontends/tree/main/packages/embedded-components-widget-token#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/tesourohq/Tesouro.Payments.Frontends/issues"
14
+ },
15
+ "publishConfig": {
16
+ "access": "public",
17
+ "provenance": false
18
+ },
19
+ "main": "./dist/index.js",
20
+ "module": "./dist/index.js",
21
+ "types": "./dist/index.d.ts",
22
+ "exports": {
23
+ "./package.json": "./package.json",
24
+ ".": {
25
+ "types": "./dist/index.d.ts",
26
+ "import": "./dist/index.js",
27
+ "default": "./dist/index.js"
28
+ }
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "!**/*.tsbuildinfo"
33
+ ],
34
+ "dependencies": {
35
+ "jose": "^6.1.3"
36
+ },
37
+ "nx": {
38
+ "name": "embedded-components-widget-token",
39
+ "tags": [
40
+ "type:feature",
41
+ "server-only"
42
+ ],
43
+ "targets": {
44
+ "verify-publish": {
45
+ "executor": "nx:run-commands",
46
+ "dependsOn": [
47
+ "build"
48
+ ],
49
+ "inputs": [
50
+ "default",
51
+ "{workspaceRoot}/tools/publishable-package/**"
52
+ ],
53
+ "options": {
54
+ "command": "node tools/publishable-package/verify-publish.mjs --config {projectRoot}/publish.config.mjs"
55
+ }
56
+ }
57
+ }
58
+ }
59
+ }