@pagopa/io-wallet-oauth2 0.3.0

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.
@@ -0,0 +1,171 @@
1
+ import { CallbackContext, AuthorizationServerMetadata, RequestDpopOptions } from '@openid4vc/oauth2';
2
+ import z from 'zod';
3
+
4
+ declare enum PkceCodeChallengeMethod {
5
+ Plain = "plain",
6
+ S256 = "S256"
7
+ }
8
+ interface CreatePkceOptions {
9
+ /**
10
+ * Also allows string values so it can be directly passed from the
11
+ * 'code_challenge_methods_supported' metadata parameter
12
+ */
13
+ allowedCodeChallengeMethods?: Array<string | PkceCodeChallengeMethod>;
14
+ /**
15
+ * Code verifier to use. If not provided a value will be generated.
16
+ */
17
+ codeVerifier?: string;
18
+ callbacks: Pick<CallbackContext, 'hash' | 'generateRandom'>;
19
+ }
20
+ interface CreatePkceReturn {
21
+ codeVerifier: string;
22
+ codeChallenge: string;
23
+ codeChallengeMethod: PkceCodeChallengeMethod;
24
+ }
25
+ declare function createPkce(options: CreatePkceOptions): Promise<CreatePkceReturn>;
26
+ interface VerifyPkceOptions {
27
+ /**
28
+ * secure random code verifier
29
+ */
30
+ codeVerifier: string;
31
+ codeChallenge: string;
32
+ codeChallengeMethod: PkceCodeChallengeMethod;
33
+ callbacks: Pick<CallbackContext, 'hash'>;
34
+ }
35
+ declare function verifyPkce(options: VerifyPkceOptions): Promise<void>;
36
+
37
+ declare const zAuthorizationRequest: z.ZodObject<{
38
+ response_type: z.ZodString;
39
+ response_mode: z.ZodString;
40
+ client_id: z.ZodString;
41
+ state: z.ZodString;
42
+ code_challenge: z.ZodString;
43
+ code_challenge_method: z.ZodString;
44
+ scope: z.ZodString;
45
+ authorization_details: z.ZodArray<z.ZodObject<{
46
+ type: z.ZodLiteral<"openid_credential">;
47
+ credential_configuration_id: z.ZodString;
48
+ }, "strip", z.ZodTypeAny, {
49
+ type?: "openid_credential";
50
+ credential_configuration_id?: string;
51
+ }, {
52
+ type?: "openid_credential";
53
+ credential_configuration_id?: string;
54
+ }>, "many">;
55
+ redirect_uri: z.ZodOptional<z.ZodString>;
56
+ issuer_state: z.ZodOptional<z.ZodString>;
57
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
58
+ response_type: z.ZodString;
59
+ response_mode: z.ZodString;
60
+ client_id: z.ZodString;
61
+ state: z.ZodString;
62
+ code_challenge: z.ZodString;
63
+ code_challenge_method: z.ZodString;
64
+ scope: z.ZodString;
65
+ authorization_details: z.ZodArray<z.ZodObject<{
66
+ type: z.ZodLiteral<"openid_credential">;
67
+ credential_configuration_id: z.ZodString;
68
+ }, "strip", z.ZodTypeAny, {
69
+ type?: "openid_credential";
70
+ credential_configuration_id?: string;
71
+ }, {
72
+ type?: "openid_credential";
73
+ credential_configuration_id?: string;
74
+ }>, "many">;
75
+ redirect_uri: z.ZodOptional<z.ZodString>;
76
+ issuer_state: z.ZodOptional<z.ZodString>;
77
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
78
+ response_type: z.ZodString;
79
+ response_mode: z.ZodString;
80
+ client_id: z.ZodString;
81
+ state: z.ZodString;
82
+ code_challenge: z.ZodString;
83
+ code_challenge_method: z.ZodString;
84
+ scope: z.ZodString;
85
+ authorization_details: z.ZodArray<z.ZodObject<{
86
+ type: z.ZodLiteral<"openid_credential">;
87
+ credential_configuration_id: z.ZodString;
88
+ }, "strip", z.ZodTypeAny, {
89
+ type?: "openid_credential";
90
+ credential_configuration_id?: string;
91
+ }, {
92
+ type?: "openid_credential";
93
+ credential_configuration_id?: string;
94
+ }>, "many">;
95
+ redirect_uri: z.ZodOptional<z.ZodString>;
96
+ issuer_state: z.ZodOptional<z.ZodString>;
97
+ }, z.ZodTypeAny, "passthrough">>;
98
+ type AuthorizationRequest = z.infer<typeof zAuthorizationRequest>;
99
+ declare const zPushedAuthorizationRequestSigned: z.ZodObject<{
100
+ request: z.ZodString;
101
+ client_id: z.ZodString;
102
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
103
+ request: z.ZodString;
104
+ client_id: z.ZodString;
105
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
106
+ request: z.ZodString;
107
+ client_id: z.ZodString;
108
+ }, z.ZodTypeAny, "passthrough">>;
109
+ type PushedAuthorizationRequestSigned = z.infer<typeof zPushedAuthorizationRequestSigned>;
110
+ declare const zPushedAuthorizationResponse: z.ZodObject<{
111
+ request_uri: z.ZodString;
112
+ expires_in: z.ZodNumber;
113
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
114
+ request_uri: z.ZodString;
115
+ expires_in: z.ZodNumber;
116
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
117
+ request_uri: z.ZodString;
118
+ expires_in: z.ZodNumber;
119
+ }, z.ZodTypeAny, "passthrough">>;
120
+ type PushedAuthorizationResponse = z.infer<typeof zPushedAuthorizationResponse>;
121
+
122
+ interface CreatePushedAuthorizationRequestOptions {
123
+ /**
124
+ * Callback context mostly for crypto related functionality
125
+ */
126
+ callbacks: Pick<CallbackContext, 'hash' | 'generateRandom' | 'signJwt'>;
127
+ codeChallengeMethodsSupported: AuthorizationServerMetadata["code_challenge_methods_supported"];
128
+ /**
129
+ * MUST be set to the thumbprint of the jwk value in the cnf parameter inside the Wallet Attestation.
130
+ */
131
+ clientId: string;
132
+ /**
133
+ * It MUST be set to the identifier of the Credential Issuer.
134
+ */
135
+ audience: string;
136
+ /**
137
+ * Scope to request for the authorization request
138
+ */
139
+ scope: string;
140
+ /**
141
+ * It MUST be one of the supported values (response_modes_supported) provided in the metadata of the Credential Issuer.
142
+ */
143
+ responseMode: string;
144
+ /**
145
+ * Redirect uri to include in the authorization request
146
+ */
147
+ redirectUri: string;
148
+ /**
149
+ * Allows clients to specify their fine-grained authorization requirements using the expressiveness of JSON data structures
150
+ */
151
+ authorization_details: Record<string, unknown>[];
152
+ /**
153
+ * state parameter to use for PAR. If not provided a value will generated automatically
154
+ */
155
+ state?: string;
156
+ /**
157
+ * jti parameter to use for PAR. If not provided a value will generated automatically
158
+ */
159
+ jti?: string;
160
+ /**
161
+ * Code verifier to use for pkce. If not provided a value will generated when pkce is supported
162
+ */
163
+ pkceCodeVerifier?: string;
164
+ /**
165
+ * DPoP options
166
+ */
167
+ dpop: RequestDpopOptions;
168
+ }
169
+ declare function createPushedAuthorizationRequest(options: CreatePushedAuthorizationRequestOptions): Promise<PushedAuthorizationRequestSigned>;
170
+
171
+ export { type AuthorizationRequest, type CreatePkceOptions, type CreatePkceReturn, type CreatePushedAuthorizationRequestOptions, PkceCodeChallengeMethod, type PushedAuthorizationRequestSigned, type PushedAuthorizationResponse, type VerifyPkceOptions, createPkce, createPushedAuthorizationRequest, verifyPkce, zAuthorizationRequest, zPushedAuthorizationRequestSigned, zPushedAuthorizationResponse };
@@ -0,0 +1,171 @@
1
+ import { CallbackContext, AuthorizationServerMetadata, RequestDpopOptions } from '@openid4vc/oauth2';
2
+ import z from 'zod';
3
+
4
+ declare enum PkceCodeChallengeMethod {
5
+ Plain = "plain",
6
+ S256 = "S256"
7
+ }
8
+ interface CreatePkceOptions {
9
+ /**
10
+ * Also allows string values so it can be directly passed from the
11
+ * 'code_challenge_methods_supported' metadata parameter
12
+ */
13
+ allowedCodeChallengeMethods?: Array<string | PkceCodeChallengeMethod>;
14
+ /**
15
+ * Code verifier to use. If not provided a value will be generated.
16
+ */
17
+ codeVerifier?: string;
18
+ callbacks: Pick<CallbackContext, 'hash' | 'generateRandom'>;
19
+ }
20
+ interface CreatePkceReturn {
21
+ codeVerifier: string;
22
+ codeChallenge: string;
23
+ codeChallengeMethod: PkceCodeChallengeMethod;
24
+ }
25
+ declare function createPkce(options: CreatePkceOptions): Promise<CreatePkceReturn>;
26
+ interface VerifyPkceOptions {
27
+ /**
28
+ * secure random code verifier
29
+ */
30
+ codeVerifier: string;
31
+ codeChallenge: string;
32
+ codeChallengeMethod: PkceCodeChallengeMethod;
33
+ callbacks: Pick<CallbackContext, 'hash'>;
34
+ }
35
+ declare function verifyPkce(options: VerifyPkceOptions): Promise<void>;
36
+
37
+ declare const zAuthorizationRequest: z.ZodObject<{
38
+ response_type: z.ZodString;
39
+ response_mode: z.ZodString;
40
+ client_id: z.ZodString;
41
+ state: z.ZodString;
42
+ code_challenge: z.ZodString;
43
+ code_challenge_method: z.ZodString;
44
+ scope: z.ZodString;
45
+ authorization_details: z.ZodArray<z.ZodObject<{
46
+ type: z.ZodLiteral<"openid_credential">;
47
+ credential_configuration_id: z.ZodString;
48
+ }, "strip", z.ZodTypeAny, {
49
+ type?: "openid_credential";
50
+ credential_configuration_id?: string;
51
+ }, {
52
+ type?: "openid_credential";
53
+ credential_configuration_id?: string;
54
+ }>, "many">;
55
+ redirect_uri: z.ZodOptional<z.ZodString>;
56
+ issuer_state: z.ZodOptional<z.ZodString>;
57
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
58
+ response_type: z.ZodString;
59
+ response_mode: z.ZodString;
60
+ client_id: z.ZodString;
61
+ state: z.ZodString;
62
+ code_challenge: z.ZodString;
63
+ code_challenge_method: z.ZodString;
64
+ scope: z.ZodString;
65
+ authorization_details: z.ZodArray<z.ZodObject<{
66
+ type: z.ZodLiteral<"openid_credential">;
67
+ credential_configuration_id: z.ZodString;
68
+ }, "strip", z.ZodTypeAny, {
69
+ type?: "openid_credential";
70
+ credential_configuration_id?: string;
71
+ }, {
72
+ type?: "openid_credential";
73
+ credential_configuration_id?: string;
74
+ }>, "many">;
75
+ redirect_uri: z.ZodOptional<z.ZodString>;
76
+ issuer_state: z.ZodOptional<z.ZodString>;
77
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
78
+ response_type: z.ZodString;
79
+ response_mode: z.ZodString;
80
+ client_id: z.ZodString;
81
+ state: z.ZodString;
82
+ code_challenge: z.ZodString;
83
+ code_challenge_method: z.ZodString;
84
+ scope: z.ZodString;
85
+ authorization_details: z.ZodArray<z.ZodObject<{
86
+ type: z.ZodLiteral<"openid_credential">;
87
+ credential_configuration_id: z.ZodString;
88
+ }, "strip", z.ZodTypeAny, {
89
+ type?: "openid_credential";
90
+ credential_configuration_id?: string;
91
+ }, {
92
+ type?: "openid_credential";
93
+ credential_configuration_id?: string;
94
+ }>, "many">;
95
+ redirect_uri: z.ZodOptional<z.ZodString>;
96
+ issuer_state: z.ZodOptional<z.ZodString>;
97
+ }, z.ZodTypeAny, "passthrough">>;
98
+ type AuthorizationRequest = z.infer<typeof zAuthorizationRequest>;
99
+ declare const zPushedAuthorizationRequestSigned: z.ZodObject<{
100
+ request: z.ZodString;
101
+ client_id: z.ZodString;
102
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
103
+ request: z.ZodString;
104
+ client_id: z.ZodString;
105
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
106
+ request: z.ZodString;
107
+ client_id: z.ZodString;
108
+ }, z.ZodTypeAny, "passthrough">>;
109
+ type PushedAuthorizationRequestSigned = z.infer<typeof zPushedAuthorizationRequestSigned>;
110
+ declare const zPushedAuthorizationResponse: z.ZodObject<{
111
+ request_uri: z.ZodString;
112
+ expires_in: z.ZodNumber;
113
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
114
+ request_uri: z.ZodString;
115
+ expires_in: z.ZodNumber;
116
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
117
+ request_uri: z.ZodString;
118
+ expires_in: z.ZodNumber;
119
+ }, z.ZodTypeAny, "passthrough">>;
120
+ type PushedAuthorizationResponse = z.infer<typeof zPushedAuthorizationResponse>;
121
+
122
+ interface CreatePushedAuthorizationRequestOptions {
123
+ /**
124
+ * Callback context mostly for crypto related functionality
125
+ */
126
+ callbacks: Pick<CallbackContext, 'hash' | 'generateRandom' | 'signJwt'>;
127
+ codeChallengeMethodsSupported: AuthorizationServerMetadata["code_challenge_methods_supported"];
128
+ /**
129
+ * MUST be set to the thumbprint of the jwk value in the cnf parameter inside the Wallet Attestation.
130
+ */
131
+ clientId: string;
132
+ /**
133
+ * It MUST be set to the identifier of the Credential Issuer.
134
+ */
135
+ audience: string;
136
+ /**
137
+ * Scope to request for the authorization request
138
+ */
139
+ scope: string;
140
+ /**
141
+ * It MUST be one of the supported values (response_modes_supported) provided in the metadata of the Credential Issuer.
142
+ */
143
+ responseMode: string;
144
+ /**
145
+ * Redirect uri to include in the authorization request
146
+ */
147
+ redirectUri: string;
148
+ /**
149
+ * Allows clients to specify their fine-grained authorization requirements using the expressiveness of JSON data structures
150
+ */
151
+ authorization_details: Record<string, unknown>[];
152
+ /**
153
+ * state parameter to use for PAR. If not provided a value will generated automatically
154
+ */
155
+ state?: string;
156
+ /**
157
+ * jti parameter to use for PAR. If not provided a value will generated automatically
158
+ */
159
+ jti?: string;
160
+ /**
161
+ * Code verifier to use for pkce. If not provided a value will generated when pkce is supported
162
+ */
163
+ pkceCodeVerifier?: string;
164
+ /**
165
+ * DPoP options
166
+ */
167
+ dpop: RequestDpopOptions;
168
+ }
169
+ declare function createPushedAuthorizationRequest(options: CreatePushedAuthorizationRequestOptions): Promise<PushedAuthorizationRequestSigned>;
170
+
171
+ export { type AuthorizationRequest, type CreatePkceOptions, type CreatePkceReturn, type CreatePushedAuthorizationRequestOptions, PkceCodeChallengeMethod, type PushedAuthorizationRequestSigned, type PushedAuthorizationResponse, type VerifyPkceOptions, createPkce, createPushedAuthorizationRequest, verifyPkce, zAuthorizationRequest, zPushedAuthorizationRequestSigned, zPushedAuthorizationResponse };
package/dist/index.js ADDED
@@ -0,0 +1,180 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+
29
+ // src/index.ts
30
+ var index_exports = {};
31
+ __export(index_exports, {
32
+ PkceCodeChallengeMethod: () => PkceCodeChallengeMethod,
33
+ createPkce: () => createPkce,
34
+ createPushedAuthorizationRequest: () => createPushedAuthorizationRequest,
35
+ verifyPkce: () => verifyPkce,
36
+ zAuthorizationRequest: () => zAuthorizationRequest,
37
+ zPushedAuthorizationRequestSigned: () => zPushedAuthorizationRequestSigned,
38
+ zPushedAuthorizationResponse: () => zPushedAuthorizationResponse
39
+ });
40
+ module.exports = __toCommonJS(index_exports);
41
+
42
+ // src/pkce.ts
43
+ var import_oauth2 = require("@openid4vc/oauth2");
44
+ var import_utils = require("@openid4vc/utils");
45
+ var PkceCodeChallengeMethod = /* @__PURE__ */ ((PkceCodeChallengeMethod2) => {
46
+ PkceCodeChallengeMethod2["Plain"] = "plain";
47
+ PkceCodeChallengeMethod2["S256"] = "S256";
48
+ return PkceCodeChallengeMethod2;
49
+ })(PkceCodeChallengeMethod || {});
50
+ async function createPkce(options) {
51
+ const allowedCodeChallengeMethods = options.allowedCodeChallengeMethods ?? [
52
+ "S256" /* S256 */,
53
+ "plain" /* Plain */
54
+ ];
55
+ if (allowedCodeChallengeMethods.length === 0) {
56
+ throw new import_oauth2.Oauth2Error(`Unable to create PKCE code verifier. 'allowedCodeChallengeMethods' is an empty array.`);
57
+ }
58
+ const codeChallengeMethod = allowedCodeChallengeMethods.includes("S256" /* S256 */) ? "S256" /* S256 */ : "plain" /* Plain */;
59
+ const codeVerifier = options.codeVerifier ?? (0, import_utils.encodeToBase64Url)(await options.callbacks.generateRandom(64));
60
+ return {
61
+ codeVerifier,
62
+ codeChallenge: await calculateCodeChallenge({
63
+ codeChallengeMethod,
64
+ codeVerifier,
65
+ hashCallback: options.callbacks.hash
66
+ }),
67
+ codeChallengeMethod
68
+ };
69
+ }
70
+ async function verifyPkce(options) {
71
+ const calculatedCodeChallenge = await calculateCodeChallenge({
72
+ codeChallengeMethod: options.codeChallengeMethod,
73
+ codeVerifier: options.codeVerifier,
74
+ hashCallback: options.callbacks.hash
75
+ });
76
+ if (options.codeChallenge !== calculatedCodeChallenge) {
77
+ throw new import_oauth2.Oauth2Error(
78
+ `Derived code challenge '${calculatedCodeChallenge}' from code_verifier '${options.codeVerifier}' using code challenge method '${options.codeChallengeMethod}' does not match the expected code challenge.`
79
+ );
80
+ }
81
+ }
82
+ async function calculateCodeChallenge(options) {
83
+ if (options.codeChallengeMethod === "plain" /* Plain */) {
84
+ return options.codeVerifier;
85
+ }
86
+ if (options.codeChallengeMethod === "S256" /* S256 */) {
87
+ return (0, import_utils.encodeToBase64Url)(await options.hashCallback((0, import_utils.decodeUtf8String)(options.codeVerifier), import_oauth2.HashAlgorithm.Sha256));
88
+ }
89
+ throw new import_oauth2.Oauth2Error(`Unsupported code challenge method ${options.codeChallengeMethod}`);
90
+ }
91
+
92
+ // src/authorization-request/z-authorization-request.ts
93
+ var import_zod = __toESM(require("zod"));
94
+ var zAuthorizationRequest = import_zod.default.object({
95
+ response_type: import_zod.default.string(),
96
+ response_mode: import_zod.default.string(),
97
+ client_id: import_zod.default.string(),
98
+ state: import_zod.default.string(),
99
+ code_challenge: import_zod.default.string(),
100
+ code_challenge_method: import_zod.default.string(),
101
+ scope: import_zod.default.string(),
102
+ authorization_details: import_zod.default.array(import_zod.default.object({
103
+ type: import_zod.default.literal("openid_credential"),
104
+ credential_configuration_id: import_zod.default.string()
105
+ })),
106
+ redirect_uri: import_zod.default.string().url().optional(),
107
+ issuer_state: import_zod.default.optional(import_zod.default.string())
108
+ }).passthrough();
109
+ var zPushedAuthorizationRequestSigned = import_zod.default.object({
110
+ /*
111
+ * It MUST be a signed JWT. The private key corresponding to the public one in the cnf parameter inside the Wallet Attestation MUST be used for signing the Request Object.
112
+ */
113
+ request: import_zod.default.string(),
114
+ /*
115
+ * MUST be set to the thumbprint of the jwk value in the cnf parameter inside the Wallet Attestation.
116
+ */
117
+ client_id: import_zod.default.string()
118
+ }).passthrough();
119
+ var zPushedAuthorizationResponse = import_zod.default.object({
120
+ request_uri: import_zod.default.string(),
121
+ expires_in: import_zod.default.number().int()
122
+ }).passthrough();
123
+
124
+ // src/authorization-request/create-authorization-request.ts
125
+ var import_utils2 = require("@openid4vc/utils");
126
+ var JWT_EXPIRY_SECONDS = 3600;
127
+ var RANDOM_BYTES_SIZE = 32;
128
+ async function createPushedAuthorizationRequest(options) {
129
+ const pkce = await createPkce({
130
+ allowedCodeChallengeMethods: options.codeChallengeMethodsSupported,
131
+ callbacks: options.callbacks,
132
+ codeVerifier: options.pkceCodeVerifier
133
+ });
134
+ const authorizationRequest = {
135
+ response_type: "code",
136
+ response_mode: options.responseMode,
137
+ state: options.state ?? (0, import_utils2.encodeToBase64Url)(await options.callbacks.generateRandom(RANDOM_BYTES_SIZE)),
138
+ client_id: options.clientId,
139
+ redirect_uri: options.redirectUri,
140
+ scope: options.scope,
141
+ authorization_details: options.authorization_details,
142
+ code_challenge: pkce.codeChallenge,
143
+ code_challenge_method: pkce.codeChallengeMethod
144
+ };
145
+ const { dpop } = options;
146
+ if (!dpop.signer.alg || !dpop.signer.publicJwk?.kid) {
147
+ throw new Error("DPoP signer must have alg and publicJwk.kid properties");
148
+ }
149
+ const iat = Math.floor(Date.now());
150
+ const requestJwt = await options.callbacks.signJwt(dpop.signer, {
151
+ header: {
152
+ alg: dpop.signer.alg,
153
+ kid: dpop.signer.publicJwk.kid,
154
+ typ: "jwt"
155
+ },
156
+ payload: {
157
+ aud: options.audience,
158
+ exp: iat + JWT_EXPIRY_SECONDS,
159
+ iat,
160
+ iss: dpop.signer.publicJwk.kid,
161
+ jti: options.jti ?? (0, import_utils2.encodeToBase64Url)(await options.callbacks.generateRandom(RANDOM_BYTES_SIZE)),
162
+ ...authorizationRequest
163
+ }
164
+ });
165
+ return {
166
+ client_id: options.clientId,
167
+ request: requestJwt.jwt
168
+ };
169
+ }
170
+ // Annotate the CommonJS export names for ESM import in node:
171
+ 0 && (module.exports = {
172
+ PkceCodeChallengeMethod,
173
+ createPkce,
174
+ createPushedAuthorizationRequest,
175
+ verifyPkce,
176
+ zAuthorizationRequest,
177
+ zPushedAuthorizationRequestSigned,
178
+ zPushedAuthorizationResponse
179
+ });
180
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/pkce.ts","../src/authorization-request/z-authorization-request.ts","../src/authorization-request/create-authorization-request.ts"],"sourcesContent":["export * from \"./pkce\";\nexport * from \"./authorization-request\"","import { CallbackContext, HashAlgorithm, HashCallback, Oauth2Error } from '@openid4vc/oauth2'\nimport { decodeUtf8String, encodeToBase64Url } from '@openid4vc/utils'\n\nexport enum PkceCodeChallengeMethod {\n Plain = 'plain',\n S256 = 'S256',\n}\n\nexport interface CreatePkceOptions {\n /**\n * Also allows string values so it can be directly passed from the\n * 'code_challenge_methods_supported' metadata parameter\n */\n allowedCodeChallengeMethods?: Array<string | PkceCodeChallengeMethod>\n\n /**\n * Code verifier to use. If not provided a value will be generated.\n */\n codeVerifier?: string\n\n callbacks: Pick<CallbackContext, 'hash' | 'generateRandom'>\n}\n\nexport interface CreatePkceReturn {\n codeVerifier: string\n codeChallenge: string\n codeChallengeMethod: PkceCodeChallengeMethod\n}\n\nexport async function createPkce(options: CreatePkceOptions): Promise<CreatePkceReturn> {\n const allowedCodeChallengeMethods = options.allowedCodeChallengeMethods ?? [\n PkceCodeChallengeMethod.S256,\n PkceCodeChallengeMethod.Plain,\n ]\n\n if (allowedCodeChallengeMethods.length === 0) {\n throw new Oauth2Error(`Unable to create PKCE code verifier. 'allowedCodeChallengeMethods' is an empty array.`)\n }\n\n const codeChallengeMethod = allowedCodeChallengeMethods.includes(PkceCodeChallengeMethod.S256)\n ? PkceCodeChallengeMethod.S256\n : PkceCodeChallengeMethod.Plain\n\n const codeVerifier = options.codeVerifier ?? encodeToBase64Url(await options.callbacks.generateRandom(64))\n return {\n codeVerifier,\n codeChallenge: await calculateCodeChallenge({\n codeChallengeMethod,\n codeVerifier,\n hashCallback: options.callbacks.hash,\n }),\n codeChallengeMethod,\n }\n}\n\nexport interface VerifyPkceOptions {\n /**\n * secure random code verifier\n */\n codeVerifier: string\n\n codeChallenge: string\n codeChallengeMethod: PkceCodeChallengeMethod\n\n callbacks: Pick<CallbackContext, 'hash'>\n}\n\nexport async function verifyPkce(options: VerifyPkceOptions) {\n const calculatedCodeChallenge = await calculateCodeChallenge({\n codeChallengeMethod: options.codeChallengeMethod,\n codeVerifier: options.codeVerifier,\n hashCallback: options.callbacks.hash,\n })\n\n if (options.codeChallenge !== calculatedCodeChallenge) {\n throw new Oauth2Error(\n `Derived code challenge '${calculatedCodeChallenge}' from code_verifier '${options.codeVerifier}' using code challenge method '${options.codeChallengeMethod}' does not match the expected code challenge.`\n )\n }\n}\n\nasync function calculateCodeChallenge(options: {\n codeVerifier: string\n codeChallengeMethod: PkceCodeChallengeMethod\n hashCallback: HashCallback\n}) {\n if (options.codeChallengeMethod === PkceCodeChallengeMethod.Plain) {\n return options.codeVerifier\n }\n\n if (options.codeChallengeMethod === PkceCodeChallengeMethod.S256) {\n return encodeToBase64Url(await options.hashCallback(decodeUtf8String(options.codeVerifier), HashAlgorithm.Sha256))\n }\n\n throw new Oauth2Error(`Unsupported code challenge method ${options.codeChallengeMethod}`)\n}\n","import z from 'zod'\n\nexport const zAuthorizationRequest = z\n .object({\n response_type: z.string(),\n response_mode: z.string(),\n client_id: z.string(),\n state: z.string(),\n code_challenge: z.string(),\n code_challenge_method: z.string(),\n scope: z.string(),\n authorization_details: z.array(z.object({\n type: z.literal(\"openid_credential\"),\n credential_configuration_id: z.string()\n })),\n redirect_uri: z.string().url().optional(),\n issuer_state: z.optional(z.string()),\n\n })\n .passthrough()\nexport type AuthorizationRequest = z.infer<typeof zAuthorizationRequest>\n\nexport const zPushedAuthorizationRequestSigned = z\n .object({\n /* \n * It MUST be a signed JWT. The private key corresponding to the public one in the cnf parameter inside the Wallet Attestation MUST be used for signing the Request Object.\n */\n request: z.string(),\n /* \n * MUST be set to the thumbprint of the jwk value in the cnf parameter inside the Wallet Attestation.\n */\n client_id: z.string(),\n })\n .passthrough()\nexport type PushedAuthorizationRequestSigned = z.infer<typeof zPushedAuthorizationRequestSigned>\n\nexport const zPushedAuthorizationResponse = z\n .object({\n request_uri: z.string(),\n expires_in: z.number().int(),\n })\n .passthrough()\nexport type PushedAuthorizationResponse = z.infer<typeof zPushedAuthorizationResponse>\n","import { encodeToBase64Url } from '@openid4vc/utils'\nimport { AuthorizationServerMetadata, CallbackContext, RequestDpopOptions } from \"@openid4vc/oauth2\";\nimport { createPkce } from '../pkce';\nimport { AuthorizationRequest, PushedAuthorizationRequestSigned } from './z-authorization-request';\n\nconst JWT_EXPIRY_SECONDS = 3600; // 1 hour\nconst RANDOM_BYTES_SIZE = 32;\n\nexport interface CreatePushedAuthorizationRequestOptions {\n /**\n * Callback context mostly for crypto related functionality\n */\n callbacks: Pick<CallbackContext, 'hash' | 'generateRandom' | 'signJwt' >\n\n codeChallengeMethodsSupported: AuthorizationServerMetadata[\"code_challenge_methods_supported\"]\n\n /**\n * MUST be set to the thumbprint of the jwk value in the cnf parameter inside the Wallet Attestation.\n */\n clientId: string\n\n /**\n * It MUST be set to the identifier of the Credential Issuer.\n */\n audience: string\n\n /**\n * Scope to request for the authorization request\n */\n scope: string\n\n /**\n * It MUST be one of the supported values (response_modes_supported) provided in the metadata of the Credential Issuer.\n */\n responseMode: string\n\n /**\n * Redirect uri to include in the authorization request\n */\n redirectUri: string\n\n /**\n * Allows clients to specify their fine-grained authorization requirements using the expressiveness of JSON data structures\n */\n authorization_details: Record<string, unknown>[]\n\n /**\n * state parameter to use for PAR. If not provided a value will generated automatically\n */\n state?: string\n\n /**\n * jti parameter to use for PAR. If not provided a value will generated automatically\n */\n jti?: string\n\n /**\n * Code verifier to use for pkce. If not provided a value will generated when pkce is supported\n */\n pkceCodeVerifier?: string\n\n /**\n * DPoP options\n */\n dpop: RequestDpopOptions\n}\n\nexport async function createPushedAuthorizationRequest(options: CreatePushedAuthorizationRequestOptions) : Promise<PushedAuthorizationRequestSigned> {\n\n // PKCE\n const pkce = await createPkce({\n allowedCodeChallengeMethods: options.codeChallengeMethodsSupported,\n callbacks: options.callbacks,\n codeVerifier: options.pkceCodeVerifier,\n });\n\n const authorizationRequest: AuthorizationRequest = {\n response_type: 'code',\n response_mode: options.responseMode,\n state: options.state ?? encodeToBase64Url( await options.callbacks.generateRandom(RANDOM_BYTES_SIZE)),\n client_id: options.clientId,\n redirect_uri: options.redirectUri,\n scope: options.scope,\n authorization_details: options.authorization_details,\n code_challenge: pkce.codeChallenge,\n code_challenge_method: pkce.codeChallengeMethod,\n }\n\n const { dpop } = options;\n if (!dpop.signer.alg || !dpop.signer.publicJwk?.kid) {\n throw new Error('DPoP signer must have alg and publicJwk.kid properties');\n }\n\n const iat = Math.floor(Date.now())\n const requestJwt = await options.callbacks.signJwt(dpop.signer, {\n header: {\n alg: dpop.signer.alg,\n kid: dpop.signer.publicJwk.kid,\n typ: \"jwt\",\n },\n payload: {\n aud: options.audience,\n exp: iat + JWT_EXPIRY_SECONDS,\n iat,\n iss: dpop.signer.publicJwk.kid,\n jti: options.jti ?? encodeToBase64Url(await options.callbacks.generateRandom(RANDOM_BYTES_SIZE)),\n ...authorizationRequest\n },\n });\n\n return {\n client_id: options.clientId,\n request: requestJwt.jwt\n }\n}\n"],"mappings":"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","names":["PkceCodeChallengeMethod","z","import_utils"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,138 @@
1
+ // src/pkce.ts
2
+ import { HashAlgorithm, Oauth2Error } from "@openid4vc/oauth2";
3
+ import { decodeUtf8String, encodeToBase64Url } from "@openid4vc/utils";
4
+ var PkceCodeChallengeMethod = /* @__PURE__ */ ((PkceCodeChallengeMethod2) => {
5
+ PkceCodeChallengeMethod2["Plain"] = "plain";
6
+ PkceCodeChallengeMethod2["S256"] = "S256";
7
+ return PkceCodeChallengeMethod2;
8
+ })(PkceCodeChallengeMethod || {});
9
+ async function createPkce(options) {
10
+ const allowedCodeChallengeMethods = options.allowedCodeChallengeMethods ?? [
11
+ "S256" /* S256 */,
12
+ "plain" /* Plain */
13
+ ];
14
+ if (allowedCodeChallengeMethods.length === 0) {
15
+ throw new Oauth2Error(`Unable to create PKCE code verifier. 'allowedCodeChallengeMethods' is an empty array.`);
16
+ }
17
+ const codeChallengeMethod = allowedCodeChallengeMethods.includes("S256" /* S256 */) ? "S256" /* S256 */ : "plain" /* Plain */;
18
+ const codeVerifier = options.codeVerifier ?? encodeToBase64Url(await options.callbacks.generateRandom(64));
19
+ return {
20
+ codeVerifier,
21
+ codeChallenge: await calculateCodeChallenge({
22
+ codeChallengeMethod,
23
+ codeVerifier,
24
+ hashCallback: options.callbacks.hash
25
+ }),
26
+ codeChallengeMethod
27
+ };
28
+ }
29
+ async function verifyPkce(options) {
30
+ const calculatedCodeChallenge = await calculateCodeChallenge({
31
+ codeChallengeMethod: options.codeChallengeMethod,
32
+ codeVerifier: options.codeVerifier,
33
+ hashCallback: options.callbacks.hash
34
+ });
35
+ if (options.codeChallenge !== calculatedCodeChallenge) {
36
+ throw new Oauth2Error(
37
+ `Derived code challenge '${calculatedCodeChallenge}' from code_verifier '${options.codeVerifier}' using code challenge method '${options.codeChallengeMethod}' does not match the expected code challenge.`
38
+ );
39
+ }
40
+ }
41
+ async function calculateCodeChallenge(options) {
42
+ if (options.codeChallengeMethod === "plain" /* Plain */) {
43
+ return options.codeVerifier;
44
+ }
45
+ if (options.codeChallengeMethod === "S256" /* S256 */) {
46
+ return encodeToBase64Url(await options.hashCallback(decodeUtf8String(options.codeVerifier), HashAlgorithm.Sha256));
47
+ }
48
+ throw new Oauth2Error(`Unsupported code challenge method ${options.codeChallengeMethod}`);
49
+ }
50
+
51
+ // src/authorization-request/z-authorization-request.ts
52
+ import z from "zod";
53
+ var zAuthorizationRequest = z.object({
54
+ response_type: z.string(),
55
+ response_mode: z.string(),
56
+ client_id: z.string(),
57
+ state: z.string(),
58
+ code_challenge: z.string(),
59
+ code_challenge_method: z.string(),
60
+ scope: z.string(),
61
+ authorization_details: z.array(z.object({
62
+ type: z.literal("openid_credential"),
63
+ credential_configuration_id: z.string()
64
+ })),
65
+ redirect_uri: z.string().url().optional(),
66
+ issuer_state: z.optional(z.string())
67
+ }).passthrough();
68
+ var zPushedAuthorizationRequestSigned = z.object({
69
+ /*
70
+ * It MUST be a signed JWT. The private key corresponding to the public one in the cnf parameter inside the Wallet Attestation MUST be used for signing the Request Object.
71
+ */
72
+ request: z.string(),
73
+ /*
74
+ * MUST be set to the thumbprint of the jwk value in the cnf parameter inside the Wallet Attestation.
75
+ */
76
+ client_id: z.string()
77
+ }).passthrough();
78
+ var zPushedAuthorizationResponse = z.object({
79
+ request_uri: z.string(),
80
+ expires_in: z.number().int()
81
+ }).passthrough();
82
+
83
+ // src/authorization-request/create-authorization-request.ts
84
+ import { encodeToBase64Url as encodeToBase64Url2 } from "@openid4vc/utils";
85
+ var JWT_EXPIRY_SECONDS = 3600;
86
+ var RANDOM_BYTES_SIZE = 32;
87
+ async function createPushedAuthorizationRequest(options) {
88
+ const pkce = await createPkce({
89
+ allowedCodeChallengeMethods: options.codeChallengeMethodsSupported,
90
+ callbacks: options.callbacks,
91
+ codeVerifier: options.pkceCodeVerifier
92
+ });
93
+ const authorizationRequest = {
94
+ response_type: "code",
95
+ response_mode: options.responseMode,
96
+ state: options.state ?? encodeToBase64Url2(await options.callbacks.generateRandom(RANDOM_BYTES_SIZE)),
97
+ client_id: options.clientId,
98
+ redirect_uri: options.redirectUri,
99
+ scope: options.scope,
100
+ authorization_details: options.authorization_details,
101
+ code_challenge: pkce.codeChallenge,
102
+ code_challenge_method: pkce.codeChallengeMethod
103
+ };
104
+ const { dpop } = options;
105
+ if (!dpop.signer.alg || !dpop.signer.publicJwk?.kid) {
106
+ throw new Error("DPoP signer must have alg and publicJwk.kid properties");
107
+ }
108
+ const iat = Math.floor(Date.now());
109
+ const requestJwt = await options.callbacks.signJwt(dpop.signer, {
110
+ header: {
111
+ alg: dpop.signer.alg,
112
+ kid: dpop.signer.publicJwk.kid,
113
+ typ: "jwt"
114
+ },
115
+ payload: {
116
+ aud: options.audience,
117
+ exp: iat + JWT_EXPIRY_SECONDS,
118
+ iat,
119
+ iss: dpop.signer.publicJwk.kid,
120
+ jti: options.jti ?? encodeToBase64Url2(await options.callbacks.generateRandom(RANDOM_BYTES_SIZE)),
121
+ ...authorizationRequest
122
+ }
123
+ });
124
+ return {
125
+ client_id: options.clientId,
126
+ request: requestJwt.jwt
127
+ };
128
+ }
129
+ export {
130
+ PkceCodeChallengeMethod,
131
+ createPkce,
132
+ createPushedAuthorizationRequest,
133
+ verifyPkce,
134
+ zAuthorizationRequest,
135
+ zPushedAuthorizationRequestSigned,
136
+ zPushedAuthorizationResponse
137
+ };
138
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/pkce.ts","../src/authorization-request/z-authorization-request.ts","../src/authorization-request/create-authorization-request.ts"],"sourcesContent":["import { CallbackContext, HashAlgorithm, HashCallback, Oauth2Error } from '@openid4vc/oauth2'\nimport { decodeUtf8String, encodeToBase64Url } from '@openid4vc/utils'\n\nexport enum PkceCodeChallengeMethod {\n Plain = 'plain',\n S256 = 'S256',\n}\n\nexport interface CreatePkceOptions {\n /**\n * Also allows string values so it can be directly passed from the\n * 'code_challenge_methods_supported' metadata parameter\n */\n allowedCodeChallengeMethods?: Array<string | PkceCodeChallengeMethod>\n\n /**\n * Code verifier to use. If not provided a value will be generated.\n */\n codeVerifier?: string\n\n callbacks: Pick<CallbackContext, 'hash' | 'generateRandom'>\n}\n\nexport interface CreatePkceReturn {\n codeVerifier: string\n codeChallenge: string\n codeChallengeMethod: PkceCodeChallengeMethod\n}\n\nexport async function createPkce(options: CreatePkceOptions): Promise<CreatePkceReturn> {\n const allowedCodeChallengeMethods = options.allowedCodeChallengeMethods ?? [\n PkceCodeChallengeMethod.S256,\n PkceCodeChallengeMethod.Plain,\n ]\n\n if (allowedCodeChallengeMethods.length === 0) {\n throw new Oauth2Error(`Unable to create PKCE code verifier. 'allowedCodeChallengeMethods' is an empty array.`)\n }\n\n const codeChallengeMethod = allowedCodeChallengeMethods.includes(PkceCodeChallengeMethod.S256)\n ? PkceCodeChallengeMethod.S256\n : PkceCodeChallengeMethod.Plain\n\n const codeVerifier = options.codeVerifier ?? encodeToBase64Url(await options.callbacks.generateRandom(64))\n return {\n codeVerifier,\n codeChallenge: await calculateCodeChallenge({\n codeChallengeMethod,\n codeVerifier,\n hashCallback: options.callbacks.hash,\n }),\n codeChallengeMethod,\n }\n}\n\nexport interface VerifyPkceOptions {\n /**\n * secure random code verifier\n */\n codeVerifier: string\n\n codeChallenge: string\n codeChallengeMethod: PkceCodeChallengeMethod\n\n callbacks: Pick<CallbackContext, 'hash'>\n}\n\nexport async function verifyPkce(options: VerifyPkceOptions) {\n const calculatedCodeChallenge = await calculateCodeChallenge({\n codeChallengeMethod: options.codeChallengeMethod,\n codeVerifier: options.codeVerifier,\n hashCallback: options.callbacks.hash,\n })\n\n if (options.codeChallenge !== calculatedCodeChallenge) {\n throw new Oauth2Error(\n `Derived code challenge '${calculatedCodeChallenge}' from code_verifier '${options.codeVerifier}' using code challenge method '${options.codeChallengeMethod}' does not match the expected code challenge.`\n )\n }\n}\n\nasync function calculateCodeChallenge(options: {\n codeVerifier: string\n codeChallengeMethod: PkceCodeChallengeMethod\n hashCallback: HashCallback\n}) {\n if (options.codeChallengeMethod === PkceCodeChallengeMethod.Plain) {\n return options.codeVerifier\n }\n\n if (options.codeChallengeMethod === PkceCodeChallengeMethod.S256) {\n return encodeToBase64Url(await options.hashCallback(decodeUtf8String(options.codeVerifier), HashAlgorithm.Sha256))\n }\n\n throw new Oauth2Error(`Unsupported code challenge method ${options.codeChallengeMethod}`)\n}\n","import z from 'zod'\n\nexport const zAuthorizationRequest = z\n .object({\n response_type: z.string(),\n response_mode: z.string(),\n client_id: z.string(),\n state: z.string(),\n code_challenge: z.string(),\n code_challenge_method: z.string(),\n scope: z.string(),\n authorization_details: z.array(z.object({\n type: z.literal(\"openid_credential\"),\n credential_configuration_id: z.string()\n })),\n redirect_uri: z.string().url().optional(),\n issuer_state: z.optional(z.string()),\n\n })\n .passthrough()\nexport type AuthorizationRequest = z.infer<typeof zAuthorizationRequest>\n\nexport const zPushedAuthorizationRequestSigned = z\n .object({\n /* \n * It MUST be a signed JWT. The private key corresponding to the public one in the cnf parameter inside the Wallet Attestation MUST be used for signing the Request Object.\n */\n request: z.string(),\n /* \n * MUST be set to the thumbprint of the jwk value in the cnf parameter inside the Wallet Attestation.\n */\n client_id: z.string(),\n })\n .passthrough()\nexport type PushedAuthorizationRequestSigned = z.infer<typeof zPushedAuthorizationRequestSigned>\n\nexport const zPushedAuthorizationResponse = z\n .object({\n request_uri: z.string(),\n expires_in: z.number().int(),\n })\n .passthrough()\nexport type PushedAuthorizationResponse = z.infer<typeof zPushedAuthorizationResponse>\n","import { encodeToBase64Url } from '@openid4vc/utils'\nimport { AuthorizationServerMetadata, CallbackContext, RequestDpopOptions } from \"@openid4vc/oauth2\";\nimport { createPkce } from '../pkce';\nimport { AuthorizationRequest, PushedAuthorizationRequestSigned } from './z-authorization-request';\n\nconst JWT_EXPIRY_SECONDS = 3600; // 1 hour\nconst RANDOM_BYTES_SIZE = 32;\n\nexport interface CreatePushedAuthorizationRequestOptions {\n /**\n * Callback context mostly for crypto related functionality\n */\n callbacks: Pick<CallbackContext, 'hash' | 'generateRandom' | 'signJwt' >\n\n codeChallengeMethodsSupported: AuthorizationServerMetadata[\"code_challenge_methods_supported\"]\n\n /**\n * MUST be set to the thumbprint of the jwk value in the cnf parameter inside the Wallet Attestation.\n */\n clientId: string\n\n /**\n * It MUST be set to the identifier of the Credential Issuer.\n */\n audience: string\n\n /**\n * Scope to request for the authorization request\n */\n scope: string\n\n /**\n * It MUST be one of the supported values (response_modes_supported) provided in the metadata of the Credential Issuer.\n */\n responseMode: string\n\n /**\n * Redirect uri to include in the authorization request\n */\n redirectUri: string\n\n /**\n * Allows clients to specify their fine-grained authorization requirements using the expressiveness of JSON data structures\n */\n authorization_details: Record<string, unknown>[]\n\n /**\n * state parameter to use for PAR. If not provided a value will generated automatically\n */\n state?: string\n\n /**\n * jti parameter to use for PAR. If not provided a value will generated automatically\n */\n jti?: string\n\n /**\n * Code verifier to use for pkce. If not provided a value will generated when pkce is supported\n */\n pkceCodeVerifier?: string\n\n /**\n * DPoP options\n */\n dpop: RequestDpopOptions\n}\n\nexport async function createPushedAuthorizationRequest(options: CreatePushedAuthorizationRequestOptions) : Promise<PushedAuthorizationRequestSigned> {\n\n // PKCE\n const pkce = await createPkce({\n allowedCodeChallengeMethods: options.codeChallengeMethodsSupported,\n callbacks: options.callbacks,\n codeVerifier: options.pkceCodeVerifier,\n });\n\n const authorizationRequest: AuthorizationRequest = {\n response_type: 'code',\n response_mode: options.responseMode,\n state: options.state ?? encodeToBase64Url( await options.callbacks.generateRandom(RANDOM_BYTES_SIZE)),\n client_id: options.clientId,\n redirect_uri: options.redirectUri,\n scope: options.scope,\n authorization_details: options.authorization_details,\n code_challenge: pkce.codeChallenge,\n code_challenge_method: pkce.codeChallengeMethod,\n }\n\n const { dpop } = options;\n if (!dpop.signer.alg || !dpop.signer.publicJwk?.kid) {\n throw new Error('DPoP signer must have alg and publicJwk.kid properties');\n }\n\n const iat = Math.floor(Date.now())\n const requestJwt = await options.callbacks.signJwt(dpop.signer, {\n header: {\n alg: dpop.signer.alg,\n kid: dpop.signer.publicJwk.kid,\n typ: \"jwt\",\n },\n payload: {\n aud: options.audience,\n exp: iat + JWT_EXPIRY_SECONDS,\n iat,\n iss: dpop.signer.publicJwk.kid,\n jti: options.jti ?? encodeToBase64Url(await options.callbacks.generateRandom(RANDOM_BYTES_SIZE)),\n ...authorizationRequest\n },\n });\n\n return {\n client_id: options.clientId,\n request: requestJwt.jwt\n }\n}\n"],"mappings":"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","names":["PkceCodeChallengeMethod","encodeToBase64Url","encodeToBase64Url"]}
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@pagopa/io-wallet-oauth2",
3
+ "version": "0.3.0",
4
+ "files": [
5
+ "dist"
6
+ ],
7
+ "license": "Apache-2.0",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.mjs",
11
+ "require": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ },
14
+ "./package.json": "./package.json"
15
+ },
16
+ "homepage": "https://github.com/pagopa/io-wallet-sdk/tree/main/packages/oauth2",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/pagopa/io-wallet-sdk",
20
+ "directory": "packages/oauth2"
21
+ },
22
+ "dependencies": {
23
+ "@openid4vc/oauth2": "^0.2.0",
24
+ "@openid4vc/utils": "^0.2.0",
25
+ "zod": "^3.24.2"
26
+ },
27
+ "scripts": {
28
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean --sourcemap",
29
+ "test": "vitest"
30
+ },
31
+ "main": "./dist/index.js",
32
+ "module": "./dist/index.mjs",
33
+ "types": "./dist/index.d.ts"
34
+ }