@pafi-dev/issuer 0.38.1 → 0.39.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/dist/auth-client/index.d.cts +2 -140
- package/dist/auth-client/index.d.ts +2 -140
- package/dist/auth-client/index.js +5 -176
- package/dist/auth-client/index.js.map +1 -1
- package/dist/chunk-7VEYSL2C.js +180 -0
- package/dist/chunk-7VEYSL2C.js.map +1 -0
- package/dist/direct-auth/index.cjs +662 -0
- package/dist/direct-auth/index.cjs.map +1 -0
- package/dist/direct-auth/index.d.cts +421 -0
- package/dist/direct-auth/index.d.ts +421 -0
- package/dist/direct-auth/index.js +465 -0
- package/dist/direct-auth/index.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/pafi-auth-client-DzHd_Ts_.d.cts +142 -0
- package/dist/pafi-auth-client-DzHd_Ts_.d.ts +142 -0
- package/package.json +33 -1
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { JWK } from 'jose';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Constructor params for {@link PafiAuthClient}. One instance per
|
|
5
|
+
* issuer backend — wraps the issuer's gateway credentials (client_id +
|
|
6
|
+
* private JWK) plus the static config (gateway URL, audience).
|
|
7
|
+
*/
|
|
8
|
+
interface PafiAuthClientOptions {
|
|
9
|
+
/** Base URL of the PAFI gateway (e.g. `https://id-dev.pacificfinance.org`). */
|
|
10
|
+
gatewayUrl: string;
|
|
11
|
+
/** Issuer identifier registered with the gateway (e.g. `gg56`). */
|
|
12
|
+
issuerId: string;
|
|
13
|
+
/**
|
|
14
|
+
* Gateway client_id assigned at issuer onboarding. Also acts as the
|
|
15
|
+
* `iss`/`sub` of the client_assertion JWT (RFC 7523 §3).
|
|
16
|
+
*/
|
|
17
|
+
clientId: string;
|
|
18
|
+
/**
|
|
19
|
+
* Private JWK the issuer uses to sign client_assertion. MUST include
|
|
20
|
+
* `kid` — gateway looks up the matching public JWK by kid.
|
|
21
|
+
*/
|
|
22
|
+
clientPrivateJwk: JWK & {
|
|
23
|
+
kid: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Optional fetch override — useful for tests / Node env without
|
|
27
|
+
* global fetch (Node ≥ 18 has it built-in).
|
|
28
|
+
*/
|
|
29
|
+
fetchImpl?: typeof fetch;
|
|
30
|
+
/**
|
|
31
|
+
* Optional algorithm override for the client assertion JWT. Default
|
|
32
|
+
* `ES256`. Must match what the gateway expects for this client.
|
|
33
|
+
*/
|
|
34
|
+
alg?: "ES256" | "ES384" | "RS256" | "RS384" | "RS512" | "EdDSA";
|
|
35
|
+
}
|
|
36
|
+
interface AuthSuccess {
|
|
37
|
+
pafiSessionToken: string;
|
|
38
|
+
pafiJwt: string;
|
|
39
|
+
canonicalId: string;
|
|
40
|
+
expiresAt: number;
|
|
41
|
+
isFirstLogin: boolean;
|
|
42
|
+
verifiedEmail?: string;
|
|
43
|
+
}
|
|
44
|
+
interface EmailChallenge {
|
|
45
|
+
challengeId: string;
|
|
46
|
+
expiresInSec: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Thrown when the gateway rejects the call. `code` is the gateway's
|
|
50
|
+
* structured `error` field (e.g. `invalid_otp`, `too_many_attempts`,
|
|
51
|
+
* `expired`, `email_not_verified`) — issuers can branch on it to drive
|
|
52
|
+
* UX (e.g. show "Resend code" button on `expired`).
|
|
53
|
+
*/
|
|
54
|
+
declare class PafiAuthError extends Error {
|
|
55
|
+
readonly status: number;
|
|
56
|
+
readonly code: string;
|
|
57
|
+
readonly correlationId?: string | undefined;
|
|
58
|
+
constructor(message: string, status: number, code: string, correlationId?: string | undefined);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Issuer-side client for the PAFI gateway's direct-auth endpoints. Use
|
|
63
|
+
* one instance per issuer backend — the constructor binds gateway URL +
|
|
64
|
+
* issuer id + signing credentials, methods just route to specific
|
|
65
|
+
* endpoints.
|
|
66
|
+
*
|
|
67
|
+
* Each method signs a fresh client_assertion (RFC 7523, 60s TTL) so
|
|
68
|
+
* a leaked one is useless beyond the window + replay-protected by jti.
|
|
69
|
+
*
|
|
70
|
+
* The gateway endpoints invoked here are owned by PAFI — the issuer
|
|
71
|
+
* never sees OTP codes, never holds OAuth client_secrets, never
|
|
72
|
+
* verifies signatures itself. Gateway is the sole authority; this
|
|
73
|
+
* client is purely a transport.
|
|
74
|
+
*
|
|
75
|
+
* Usage in a NestJS issuer backend:
|
|
76
|
+
*
|
|
77
|
+
* @Injectable()
|
|
78
|
+
* export class PafiAuthClientProvider {
|
|
79
|
+
* readonly client: PafiAuthClient;
|
|
80
|
+
* constructor(config: ConfigService) {
|
|
81
|
+
* this.client = new PafiAuthClient({
|
|
82
|
+
* gatewayUrl: config.getOrThrow('PAFI_GATEWAY_URL'),
|
|
83
|
+
* issuerId: config.getOrThrow('PAFI_GATEWAY_ISSUER_ID'),
|
|
84
|
+
* clientId: config.getOrThrow('PAFI_GATEWAY_CLIENT_ID'),
|
|
85
|
+
* clientPrivateJwk: JSON.parse(
|
|
86
|
+
* config.getOrThrow('PAFI_GATEWAY_CLIENT_PRIVATE_JWK_JSON'),
|
|
87
|
+
* ),
|
|
88
|
+
* });
|
|
89
|
+
* }
|
|
90
|
+
* }
|
|
91
|
+
*/
|
|
92
|
+
declare class PafiAuthClient {
|
|
93
|
+
private readonly opts;
|
|
94
|
+
private readonly fetchImpl;
|
|
95
|
+
private readonly tokenExchangeAud;
|
|
96
|
+
constructor(opts: PafiAuthClientOptions);
|
|
97
|
+
/**
|
|
98
|
+
* Step 1: ask the gateway to send the user an OTP. Returns the
|
|
99
|
+
* `challengeId` to echo back on {@link verifyEmail}.
|
|
100
|
+
*/
|
|
101
|
+
startEmail(args: {
|
|
102
|
+
email: string;
|
|
103
|
+
correlationId?: string;
|
|
104
|
+
}): Promise<EmailChallenge>;
|
|
105
|
+
/**
|
|
106
|
+
* Step 2: submit the OTP the user received. On success returns
|
|
107
|
+
* {@link AuthSuccess} containing BOTH the long-lived
|
|
108
|
+
* pafi_session_token (issuer verifies via gateway JWKS) AND the
|
|
109
|
+
* short-lived pafi_jwt (issuer FE feeds to Privy).
|
|
110
|
+
*/
|
|
111
|
+
verifyEmail(args: {
|
|
112
|
+
challengeId: string;
|
|
113
|
+
otpCode: string;
|
|
114
|
+
correlationId?: string;
|
|
115
|
+
}): Promise<AuthSuccess>;
|
|
116
|
+
/**
|
|
117
|
+
* Hand the gateway an id_token the issuer FE obtained from Google
|
|
118
|
+
* Identity Services (using PAFI's shared client_id). Gateway verifies
|
|
119
|
+
* signature + audience + `email_verified` before resolving identity.
|
|
120
|
+
*/
|
|
121
|
+
exchangeGoogle(args: {
|
|
122
|
+
idToken: string;
|
|
123
|
+
correlationId?: string;
|
|
124
|
+
}): Promise<AuthSuccess>;
|
|
125
|
+
/**
|
|
126
|
+
* Hand the gateway the authorization code returned by Kakao's
|
|
127
|
+
* redirect. Gateway exchanges with Kakao (server-to-server using
|
|
128
|
+
* PAFI's client_secret), verifies id_token, resolves identity.
|
|
129
|
+
*
|
|
130
|
+
* `redirectUri` must match the URL the FE used when starting the
|
|
131
|
+
* Kakao flow. Falls back to the gateway's KAKAO_REDIRECT_URI when
|
|
132
|
+
* omitted — pass an explicit value for multi-environment FEs.
|
|
133
|
+
*/
|
|
134
|
+
exchangeKakao(args: {
|
|
135
|
+
code: string;
|
|
136
|
+
redirectUri?: string;
|
|
137
|
+
correlationId?: string;
|
|
138
|
+
}): Promise<AuthSuccess>;
|
|
139
|
+
private post;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export { type AuthSuccess as A, type EmailChallenge as E, PafiAuthClient as P, type PafiAuthClientOptions as a, PafiAuthError as b };
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { JWK } from 'jose';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Constructor params for {@link PafiAuthClient}. One instance per
|
|
5
|
+
* issuer backend — wraps the issuer's gateway credentials (client_id +
|
|
6
|
+
* private JWK) plus the static config (gateway URL, audience).
|
|
7
|
+
*/
|
|
8
|
+
interface PafiAuthClientOptions {
|
|
9
|
+
/** Base URL of the PAFI gateway (e.g. `https://id-dev.pacificfinance.org`). */
|
|
10
|
+
gatewayUrl: string;
|
|
11
|
+
/** Issuer identifier registered with the gateway (e.g. `gg56`). */
|
|
12
|
+
issuerId: string;
|
|
13
|
+
/**
|
|
14
|
+
* Gateway client_id assigned at issuer onboarding. Also acts as the
|
|
15
|
+
* `iss`/`sub` of the client_assertion JWT (RFC 7523 §3).
|
|
16
|
+
*/
|
|
17
|
+
clientId: string;
|
|
18
|
+
/**
|
|
19
|
+
* Private JWK the issuer uses to sign client_assertion. MUST include
|
|
20
|
+
* `kid` — gateway looks up the matching public JWK by kid.
|
|
21
|
+
*/
|
|
22
|
+
clientPrivateJwk: JWK & {
|
|
23
|
+
kid: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Optional fetch override — useful for tests / Node env without
|
|
27
|
+
* global fetch (Node ≥ 18 has it built-in).
|
|
28
|
+
*/
|
|
29
|
+
fetchImpl?: typeof fetch;
|
|
30
|
+
/**
|
|
31
|
+
* Optional algorithm override for the client assertion JWT. Default
|
|
32
|
+
* `ES256`. Must match what the gateway expects for this client.
|
|
33
|
+
*/
|
|
34
|
+
alg?: "ES256" | "ES384" | "RS256" | "RS384" | "RS512" | "EdDSA";
|
|
35
|
+
}
|
|
36
|
+
interface AuthSuccess {
|
|
37
|
+
pafiSessionToken: string;
|
|
38
|
+
pafiJwt: string;
|
|
39
|
+
canonicalId: string;
|
|
40
|
+
expiresAt: number;
|
|
41
|
+
isFirstLogin: boolean;
|
|
42
|
+
verifiedEmail?: string;
|
|
43
|
+
}
|
|
44
|
+
interface EmailChallenge {
|
|
45
|
+
challengeId: string;
|
|
46
|
+
expiresInSec: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Thrown when the gateway rejects the call. `code` is the gateway's
|
|
50
|
+
* structured `error` field (e.g. `invalid_otp`, `too_many_attempts`,
|
|
51
|
+
* `expired`, `email_not_verified`) — issuers can branch on it to drive
|
|
52
|
+
* UX (e.g. show "Resend code" button on `expired`).
|
|
53
|
+
*/
|
|
54
|
+
declare class PafiAuthError extends Error {
|
|
55
|
+
readonly status: number;
|
|
56
|
+
readonly code: string;
|
|
57
|
+
readonly correlationId?: string | undefined;
|
|
58
|
+
constructor(message: string, status: number, code: string, correlationId?: string | undefined);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Issuer-side client for the PAFI gateway's direct-auth endpoints. Use
|
|
63
|
+
* one instance per issuer backend — the constructor binds gateway URL +
|
|
64
|
+
* issuer id + signing credentials, methods just route to specific
|
|
65
|
+
* endpoints.
|
|
66
|
+
*
|
|
67
|
+
* Each method signs a fresh client_assertion (RFC 7523, 60s TTL) so
|
|
68
|
+
* a leaked one is useless beyond the window + replay-protected by jti.
|
|
69
|
+
*
|
|
70
|
+
* The gateway endpoints invoked here are owned by PAFI — the issuer
|
|
71
|
+
* never sees OTP codes, never holds OAuth client_secrets, never
|
|
72
|
+
* verifies signatures itself. Gateway is the sole authority; this
|
|
73
|
+
* client is purely a transport.
|
|
74
|
+
*
|
|
75
|
+
* Usage in a NestJS issuer backend:
|
|
76
|
+
*
|
|
77
|
+
* @Injectable()
|
|
78
|
+
* export class PafiAuthClientProvider {
|
|
79
|
+
* readonly client: PafiAuthClient;
|
|
80
|
+
* constructor(config: ConfigService) {
|
|
81
|
+
* this.client = new PafiAuthClient({
|
|
82
|
+
* gatewayUrl: config.getOrThrow('PAFI_GATEWAY_URL'),
|
|
83
|
+
* issuerId: config.getOrThrow('PAFI_GATEWAY_ISSUER_ID'),
|
|
84
|
+
* clientId: config.getOrThrow('PAFI_GATEWAY_CLIENT_ID'),
|
|
85
|
+
* clientPrivateJwk: JSON.parse(
|
|
86
|
+
* config.getOrThrow('PAFI_GATEWAY_CLIENT_PRIVATE_JWK_JSON'),
|
|
87
|
+
* ),
|
|
88
|
+
* });
|
|
89
|
+
* }
|
|
90
|
+
* }
|
|
91
|
+
*/
|
|
92
|
+
declare class PafiAuthClient {
|
|
93
|
+
private readonly opts;
|
|
94
|
+
private readonly fetchImpl;
|
|
95
|
+
private readonly tokenExchangeAud;
|
|
96
|
+
constructor(opts: PafiAuthClientOptions);
|
|
97
|
+
/**
|
|
98
|
+
* Step 1: ask the gateway to send the user an OTP. Returns the
|
|
99
|
+
* `challengeId` to echo back on {@link verifyEmail}.
|
|
100
|
+
*/
|
|
101
|
+
startEmail(args: {
|
|
102
|
+
email: string;
|
|
103
|
+
correlationId?: string;
|
|
104
|
+
}): Promise<EmailChallenge>;
|
|
105
|
+
/**
|
|
106
|
+
* Step 2: submit the OTP the user received. On success returns
|
|
107
|
+
* {@link AuthSuccess} containing BOTH the long-lived
|
|
108
|
+
* pafi_session_token (issuer verifies via gateway JWKS) AND the
|
|
109
|
+
* short-lived pafi_jwt (issuer FE feeds to Privy).
|
|
110
|
+
*/
|
|
111
|
+
verifyEmail(args: {
|
|
112
|
+
challengeId: string;
|
|
113
|
+
otpCode: string;
|
|
114
|
+
correlationId?: string;
|
|
115
|
+
}): Promise<AuthSuccess>;
|
|
116
|
+
/**
|
|
117
|
+
* Hand the gateway an id_token the issuer FE obtained from Google
|
|
118
|
+
* Identity Services (using PAFI's shared client_id). Gateway verifies
|
|
119
|
+
* signature + audience + `email_verified` before resolving identity.
|
|
120
|
+
*/
|
|
121
|
+
exchangeGoogle(args: {
|
|
122
|
+
idToken: string;
|
|
123
|
+
correlationId?: string;
|
|
124
|
+
}): Promise<AuthSuccess>;
|
|
125
|
+
/**
|
|
126
|
+
* Hand the gateway the authorization code returned by Kakao's
|
|
127
|
+
* redirect. Gateway exchanges with Kakao (server-to-server using
|
|
128
|
+
* PAFI's client_secret), verifies id_token, resolves identity.
|
|
129
|
+
*
|
|
130
|
+
* `redirectUri` must match the URL the FE used when starting the
|
|
131
|
+
* Kakao flow. Falls back to the gateway's KAKAO_REDIRECT_URI when
|
|
132
|
+
* omitted — pass an explicit value for multi-environment FEs.
|
|
133
|
+
*/
|
|
134
|
+
exchangeKakao(args: {
|
|
135
|
+
code: string;
|
|
136
|
+
redirectUri?: string;
|
|
137
|
+
correlationId?: string;
|
|
138
|
+
}): Promise<AuthSuccess>;
|
|
139
|
+
private post;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export { type AuthSuccess as A, type EmailChallenge as E, PafiAuthClient as P, type PafiAuthClientOptions as a, PafiAuthError as b };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pafi-dev/issuer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.1",
|
|
4
4
|
"description": "Issuer backend API and services for the PAFI point token system",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -65,6 +65,16 @@
|
|
|
65
65
|
"types": "./dist/auth-client/index.d.cts",
|
|
66
66
|
"default": "./dist/auth-client/index.cjs"
|
|
67
67
|
}
|
|
68
|
+
},
|
|
69
|
+
"./direct-auth": {
|
|
70
|
+
"import": {
|
|
71
|
+
"types": "./dist/direct-auth/index.d.ts",
|
|
72
|
+
"default": "./dist/direct-auth/index.js"
|
|
73
|
+
},
|
|
74
|
+
"require": {
|
|
75
|
+
"types": "./dist/direct-auth/index.d.cts",
|
|
76
|
+
"default": "./dist/direct-auth/index.cjs"
|
|
77
|
+
}
|
|
68
78
|
}
|
|
69
79
|
},
|
|
70
80
|
"typesVersions": {
|
|
@@ -80,6 +90,9 @@
|
|
|
80
90
|
],
|
|
81
91
|
"auth-client": [
|
|
82
92
|
"./dist/auth-client/index.d.ts"
|
|
93
|
+
],
|
|
94
|
+
"direct-auth": [
|
|
95
|
+
"./dist/direct-auth/index.d.ts"
|
|
83
96
|
]
|
|
84
97
|
}
|
|
85
98
|
},
|
|
@@ -92,16 +105,35 @@
|
|
|
92
105
|
},
|
|
93
106
|
"peerDependencies": {
|
|
94
107
|
"@nestjs/common": "^10.0.0",
|
|
108
|
+
"@nestjs/swagger": "^7.0.0 || ^8.0.0",
|
|
109
|
+
"class-validator": "^0.14.0",
|
|
110
|
+
"class-transformer": "^0.5.0",
|
|
111
|
+
"reflect-metadata": "^0.1.13 || ^0.2.0",
|
|
95
112
|
"viem": "^2.0.0"
|
|
96
113
|
},
|
|
97
114
|
"peerDependenciesMeta": {
|
|
98
115
|
"@nestjs/common": {
|
|
99
116
|
"optional": true
|
|
117
|
+
},
|
|
118
|
+
"@nestjs/swagger": {
|
|
119
|
+
"optional": true
|
|
120
|
+
},
|
|
121
|
+
"class-validator": {
|
|
122
|
+
"optional": true
|
|
123
|
+
},
|
|
124
|
+
"class-transformer": {
|
|
125
|
+
"optional": true
|
|
126
|
+
},
|
|
127
|
+
"reflect-metadata": {
|
|
128
|
+
"optional": true
|
|
100
129
|
}
|
|
101
130
|
},
|
|
102
131
|
"devDependencies": {
|
|
103
132
|
"@nestjs/common": "^10.0.0",
|
|
133
|
+
"@nestjs/swagger": "^7.0.0",
|
|
104
134
|
"@vitest/coverage-v8": "^2.1.0",
|
|
135
|
+
"class-validator": "^0.14.0",
|
|
136
|
+
"reflect-metadata": "^0.2.0",
|
|
105
137
|
"tsup": "^8.0.0",
|
|
106
138
|
"typescript": "^5.5.0",
|
|
107
139
|
"viem": "^2.21.0",
|