@atcute/oauth-crypto 0.1.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.
- package/LICENSE +14 -0
- package/README.md +7 -0
- package/dist/client-assertion/create-client-assertion.d.ts +19 -0
- package/dist/client-assertion/create-client-assertion.d.ts.map +1 -0
- package/dist/client-assertion/create-client-assertion.js +34 -0
- package/dist/client-assertion/create-client-assertion.js.map +1 -0
- package/dist/client-assertion/generate-key.d.ts +11 -0
- package/dist/client-assertion/generate-key.d.ts.map +1 -0
- package/dist/client-assertion/generate-key.js +18 -0
- package/dist/client-assertion/generate-key.js.map +1 -0
- package/dist/client-assertion/index.d.ts +5 -0
- package/dist/client-assertion/index.d.ts.map +1 -0
- package/dist/client-assertion/index.js +4 -0
- package/dist/client-assertion/index.js.map +1 -0
- package/dist/client-assertion/keys.d.ts +14 -0
- package/dist/client-assertion/keys.d.ts.map +1 -0
- package/dist/client-assertion/keys.js +18 -0
- package/dist/client-assertion/keys.js.map +1 -0
- package/dist/client-assertion/types.d.ts +9 -0
- package/dist/client-assertion/types.d.ts.map +1 -0
- package/dist/client-assertion/types.js +2 -0
- package/dist/client-assertion/types.js.map +1 -0
- package/dist/dpop/fetch.d.ts +24 -0
- package/dist/dpop/fetch.d.ts.map +1 -0
- package/dist/dpop/fetch.js +102 -0
- package/dist/dpop/fetch.js.map +1 -0
- package/dist/dpop/generate-key.d.ts +9 -0
- package/dist/dpop/generate-key.d.ts.map +1 -0
- package/dist/dpop/generate-key.js +61 -0
- package/dist/dpop/generate-key.js.map +1 -0
- package/dist/dpop/index.d.ts +6 -0
- package/dist/dpop/index.d.ts.map +1 -0
- package/dist/dpop/index.js +5 -0
- package/dist/dpop/index.js.map +1 -0
- package/dist/dpop/proof.d.ts +9 -0
- package/dist/dpop/proof.d.ts.map +1 -0
- package/dist/dpop/proof.js +36 -0
- package/dist/dpop/proof.js.map +1 -0
- package/dist/dpop/types.d.ts +17 -0
- package/dist/dpop/types.d.ts.map +1 -0
- package/dist/dpop/types.js +2 -0
- package/dist/dpop/types.js.map +1 -0
- package/dist/dpop/verify.d.ts +42 -0
- package/dist/dpop/verify.d.ts.map +1 -0
- package/dist/dpop/verify.js +124 -0
- package/dist/dpop/verify.js.map +1 -0
- package/dist/hash/index.d.ts +3 -0
- package/dist/hash/index.d.ts.map +1 -0
- package/dist/hash/index.js +3 -0
- package/dist/hash/index.js.map +1 -0
- package/dist/hash/pkce.d.ts +12 -0
- package/dist/hash/pkce.d.ts.map +1 -0
- package/dist/hash/pkce.js +14 -0
- package/dist/hash/pkce.js.map +1 -0
- package/dist/hash/sha256.d.ts +8 -0
- package/dist/hash/sha256.d.ts.map +1 -0
- package/dist/hash/sha256.js +14 -0
- package/dist/hash/sha256.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/crypto.d.ts +7 -0
- package/dist/internal/crypto.d.ts.map +1 -0
- package/dist/internal/crypto.js +78 -0
- package/dist/internal/crypto.js.map +1 -0
- package/dist/internal/jwk.d.ts +10 -0
- package/dist/internal/jwk.d.ts.map +1 -0
- package/dist/internal/jwk.js +121 -0
- package/dist/internal/jwk.js.map +1 -0
- package/dist/internal/key-cache.d.ts +24 -0
- package/dist/internal/key-cache.d.ts.map +1 -0
- package/dist/internal/key-cache.js +36 -0
- package/dist/internal/key-cache.js.map +1 -0
- package/dist/jwk/compute-jkt.d.ts +9 -0
- package/dist/jwk/compute-jkt.d.ts.map +1 -0
- package/dist/jwk/compute-jkt.js +23 -0
- package/dist/jwk/compute-jkt.js.map +1 -0
- package/dist/jwk/index.d.ts +5 -0
- package/dist/jwk/index.d.ts.map +1 -0
- package/dist/jwk/index.js +4 -0
- package/dist/jwk/index.js.map +1 -0
- package/dist/jwk/keys.d.ts +9 -0
- package/dist/jwk/keys.d.ts.map +1 -0
- package/dist/jwk/keys.js +13 -0
- package/dist/jwk/keys.js.map +1 -0
- package/dist/jwk/types.d.ts +37 -0
- package/dist/jwk/types.d.ts.map +1 -0
- package/dist/jwk/types.js +2 -0
- package/dist/jwk/types.js.map +1 -0
- package/dist/jwt/index.d.ts +26 -0
- package/dist/jwt/index.d.ts.map +1 -0
- package/dist/jwt/index.js +56 -0
- package/dist/jwt/index.js.map +1 -0
- package/lib/client-assertion/create-client-assertion.ts +50 -0
- package/lib/client-assertion/generate-key.ts +26 -0
- package/lib/client-assertion/index.ts +4 -0
- package/lib/client-assertion/keys.ts +26 -0
- package/lib/client-assertion/types.ts +9 -0
- package/lib/dpop/fetch.ts +140 -0
- package/lib/dpop/generate-key.ts +72 -0
- package/lib/dpop/index.ts +11 -0
- package/lib/dpop/proof.ts +46 -0
- package/lib/dpop/types.ts +19 -0
- package/lib/dpop/verify.ts +169 -0
- package/lib/hash/index.ts +2 -0
- package/lib/hash/pkce.ts +18 -0
- package/lib/hash/sha256.ts +14 -0
- package/lib/index.ts +5 -0
- package/lib/internal/crypto.ts +92 -0
- package/lib/internal/jwk.ts +157 -0
- package/lib/internal/key-cache.ts +51 -0
- package/lib/jwk/compute-jkt.ts +27 -0
- package/lib/jwk/index.ts +12 -0
- package/lib/jwk/keys.ts +15 -0
- package/lib/jwk/types.ts +51 -0
- package/lib/jwt/index.ts +86 -0
- package/package.json +38 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
BSD Zero Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mary
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted.
|
|
7
|
+
|
|
8
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
9
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
10
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
11
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
12
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
13
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
14
|
+
PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ClientAssertionPrivateJwk } from './types.js';
|
|
2
|
+
export interface CreateClientAssertionOptions {
|
|
3
|
+
/** client id */
|
|
4
|
+
client_id: string;
|
|
5
|
+
/** authorization server issuer */
|
|
6
|
+
aud: string;
|
|
7
|
+
/** JWK thumbprint of the DPoP key to bind to (for CAB pattern) */
|
|
8
|
+
jkt?: string;
|
|
9
|
+
/** client assertion signing key */
|
|
10
|
+
key: ClientAssertionPrivateJwk;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* creates a DPoP-bound client assertion per RFC 7523.
|
|
14
|
+
*
|
|
15
|
+
* @param options creation options
|
|
16
|
+
* @returns signed client assertion JWT
|
|
17
|
+
*/
|
|
18
|
+
export declare const createClientAssertion: (options: CreateClientAssertionOptions) => Promise<string>;
|
|
19
|
+
//# sourceMappingURL=create-client-assertion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-client-assertion.d.ts","sourceRoot":"","sources":["../../lib/client-assertion/create-client-assertion.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAE5D,MAAM,WAAW,4BAA4B;IAC5C,gBAAgB;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,kEAAkE;IAClE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,GAAG,EAAE,yBAAyB,CAAC;CAC/B;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,4DAyBjC,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { nanoid } from 'nanoid';
|
|
2
|
+
import { getCachedKeyMaterial } from '../internal/key-cache.js';
|
|
3
|
+
import { signJwt } from '../jwt/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* creates a DPoP-bound client assertion per RFC 7523.
|
|
6
|
+
*
|
|
7
|
+
* @param options creation options
|
|
8
|
+
* @returns signed client assertion JWT
|
|
9
|
+
*/
|
|
10
|
+
export const createClientAssertion = async (options) => {
|
|
11
|
+
const { client_id, aud, jkt, key } = options;
|
|
12
|
+
const { kid, alg } = key;
|
|
13
|
+
const { cryptoKey } = await getCachedKeyMaterial(key);
|
|
14
|
+
const now = Math.floor(Date.now() / 1000);
|
|
15
|
+
const cnf = jkt ? { jkt } : undefined;
|
|
16
|
+
return signJwt({
|
|
17
|
+
header: {
|
|
18
|
+
alg,
|
|
19
|
+
kid,
|
|
20
|
+
},
|
|
21
|
+
payload: {
|
|
22
|
+
iss: client_id,
|
|
23
|
+
sub: client_id,
|
|
24
|
+
aud: aud,
|
|
25
|
+
jti: nanoid(24),
|
|
26
|
+
iat: now,
|
|
27
|
+
exp: now + 60,
|
|
28
|
+
cnf,
|
|
29
|
+
},
|
|
30
|
+
key: cryptoKey,
|
|
31
|
+
alg,
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=create-client-assertion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-client-assertion.js","sourceRoot":"","sources":["../../lib/client-assertion/create-client-assertion.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAe1C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,EAAE,OAAqC,EAAmB,EAAE,CAAC;IACtG,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;IAC7C,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC;IACzB,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,oBAAoB,CAAC,GAAG,CAAC,CAAC;IAEtD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAC1C,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAEtC,OAAO,OAAO,CAAC;QACd,MAAM,EAAE;YACP,GAAG;YACH,GAAG;SACH;QACD,OAAO,EAAE;YACR,GAAG,EAAE,SAAS;YACd,GAAG,EAAE,SAAS;YACd,GAAG,EAAE,GAAG;YACR,GAAG,EAAE,MAAM,CAAC,EAAE,CAAC;YACf,GAAG,EAAE,GAAG;YACR,GAAG,EAAE,GAAG,GAAG,EAAE;YACb,GAAG;SACH;QACD,GAAG,EAAE,SAAS;QACd,GAAG;KACH,CAAC,CAAC;AAAA,CACH,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SigningAlgorithm } from '../jwk/types.js';
|
|
2
|
+
import type { ClientAssertionPrivateJwk } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* generates a new client assertion private key.
|
|
5
|
+
*
|
|
6
|
+
* @param kid key id to assign
|
|
7
|
+
* @param alg signing algorithm (defaults to es256)
|
|
8
|
+
* @returns client assertion private JWK (with cache pre-warmed)
|
|
9
|
+
*/
|
|
10
|
+
export declare const generateClientAssertionKey: (kid: string, alg?: SigningAlgorithm) => Promise<ClientAssertionPrivateJwk>;
|
|
11
|
+
//# sourceMappingURL=generate-key.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-key.d.ts","sourceRoot":"","sources":["../../lib/client-assertion/generate-key.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAExD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAE5D;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,6EAWtC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { getGenerateAlgorithm } from '../internal/crypto.js';
|
|
2
|
+
import { exportPrivateJwkFromKey } from '../internal/jwk.js';
|
|
3
|
+
import { setCachedKeyMaterial } from '../internal/key-cache.js';
|
|
4
|
+
/**
|
|
5
|
+
* generates a new client assertion private key.
|
|
6
|
+
*
|
|
7
|
+
* @param kid key id to assign
|
|
8
|
+
* @param alg signing algorithm (defaults to es256)
|
|
9
|
+
* @returns client assertion private JWK (with cache pre-warmed)
|
|
10
|
+
*/
|
|
11
|
+
export const generateClientAssertionKey = async (kid, alg = 'ES256') => {
|
|
12
|
+
const pair = await crypto.subtle.generateKey(getGenerateAlgorithm(alg), true, ['sign', 'verify']);
|
|
13
|
+
const jwk = (await exportPrivateJwkFromKey(pair.privateKey, alg, kid));
|
|
14
|
+
// pre-populate cache so we don't re-import
|
|
15
|
+
setCachedKeyMaterial(jwk, pair.privateKey);
|
|
16
|
+
return jwk;
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=generate-key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-key.js","sourceRoot":"","sources":["../../lib/client-assertion/generate-key.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAKhE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,KAAK,EAC9C,GAAW,EACX,GAAG,GAAqB,OAAO,EACM,EAAE,CAAC;IACxC,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,oBAAoB,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IAClG,MAAM,GAAG,GAAG,CAAC,MAAM,uBAAuB,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,EAAE,GAAG,CAAC,CAA8B,CAAC;IAEpG,2CAA2C;IAC3C,oBAAoB,CAAC,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IAE3C,OAAO,GAAG,CAAC;AAAA,CACX,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createClientAssertion } from './create-client-assertion.js';
|
|
2
|
+
export { generateClientAssertionKey } from './generate-key.js';
|
|
3
|
+
export { importClientAssertionPkcs8 } from './keys.js';
|
|
4
|
+
export type { ClientAssertionPrivateJwk } from './types.js';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/client-assertion/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC;AACvD,YAAY,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/client-assertion/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SigningAlgorithm } from '../jwk/types.js';
|
|
2
|
+
import type { ClientAssertionPrivateJwk } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* imports a client assertion private key from a pkcs8 pem string.
|
|
5
|
+
*
|
|
6
|
+
* @param pem pkcs8 pem string
|
|
7
|
+
* @param options import options (kid + alg)
|
|
8
|
+
* @returns client assertion private JWK (with cache pre-warmed)
|
|
9
|
+
*/
|
|
10
|
+
export declare const importClientAssertionPkcs8: (pem: string, options: {
|
|
11
|
+
kid: string;
|
|
12
|
+
alg: SigningAlgorithm;
|
|
13
|
+
}) => Promise<ClientAssertionPrivateJwk>;
|
|
14
|
+
//# sourceMappingURL=keys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../lib/client-assertion/keys.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAExD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAE5D;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B;;;wCAYtC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { exportPrivateJwkFromKey, importPkcs8PrivateKey } from '../internal/jwk.js';
|
|
2
|
+
import { setCachedKeyMaterial } from '../internal/key-cache.js';
|
|
3
|
+
/**
|
|
4
|
+
* imports a client assertion private key from a pkcs8 pem string.
|
|
5
|
+
*
|
|
6
|
+
* @param pem pkcs8 pem string
|
|
7
|
+
* @param options import options (kid + alg)
|
|
8
|
+
* @returns client assertion private JWK (with cache pre-warmed)
|
|
9
|
+
*/
|
|
10
|
+
export const importClientAssertionPkcs8 = async (pem, options) => {
|
|
11
|
+
const { kid, alg } = options;
|
|
12
|
+
const cryptoKey = await importPkcs8PrivateKey(pem, alg);
|
|
13
|
+
const jwk = (await exportPrivateJwkFromKey(cryptoKey, alg, kid));
|
|
14
|
+
// pre-populate cache so we don't re-import
|
|
15
|
+
setCachedKeyMaterial(jwk, cryptoKey);
|
|
16
|
+
return jwk;
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=keys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../lib/client-assertion/keys.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AACpF,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAKhE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,KAAK,EAC9C,GAAW,EACX,OAA+C,EACV,EAAE,CAAC;IACxC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;IAC7B,MAAM,SAAS,GAAG,MAAM,qBAAqB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACxD,MAAM,GAAG,GAAG,CAAC,MAAM,uBAAuB,CAAC,SAAS,EAAE,GAAG,EAAE,GAAG,CAAC,CAA8B,CAAC;IAE9F,2CAA2C;IAC3C,oBAAoB,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IAErC,OAAO,GAAG,CAAC;AAAA,CACX,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PrivateJwk, SigningAlgorithm } from '../jwk/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* private jwk for client assertion signing.
|
|
4
|
+
*/
|
|
5
|
+
export type ClientAssertionPrivateJwk = PrivateJwk & {
|
|
6
|
+
alg: SigningAlgorithm;
|
|
7
|
+
kid: string;
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../lib/client-assertion/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEpE;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG,UAAU,GAAG;IACpD,GAAG,EAAE,gBAAgB,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;CACZ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../lib/client-assertion/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { DpopPrivateJwk, DpopNonceCache } from './types.js';
|
|
2
|
+
export interface CreateDpopFetchOptions {
|
|
3
|
+
/** DPoP private key (JWK with `alg` set) */
|
|
4
|
+
key: DpopPrivateJwk;
|
|
5
|
+
/** nonce store, keyed by origin */
|
|
6
|
+
nonces: DpopNonceCache;
|
|
7
|
+
/** server's supported DPoP signing algorithms */
|
|
8
|
+
supportedAlgs?: readonly string[];
|
|
9
|
+
/**
|
|
10
|
+
* is the target an authorization server (true) or resource server (false)?
|
|
11
|
+
* affects how `use_dpop_nonce` errors are detected.
|
|
12
|
+
*/
|
|
13
|
+
isAuthServer?: boolean;
|
|
14
|
+
/** custom fetch implementation */
|
|
15
|
+
fetch?: typeof globalThis.fetch;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* creates a fetch wrapper that adds DPoP proofs to requests.
|
|
19
|
+
*
|
|
20
|
+
* @param options DPoP configuration
|
|
21
|
+
* @returns fetch function with DPoP support
|
|
22
|
+
*/
|
|
23
|
+
export declare const createDpopFetch: (options: CreateDpopFetchOptions) => typeof fetch;
|
|
24
|
+
//# sourceMappingURL=fetch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../lib/dpop/fetch.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjE,MAAM,WAAW,sBAAsB;IACtC,4CAA4C;IAC5C,GAAG,EAAE,cAAc,CAAC;IACpB,mCAAmC;IACnC,MAAM,EAAE,cAAc,CAAC;IACvB,iDAAiD;IACjD,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,kCAAkC;IAClC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CAChC;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,mDAmE3B,CAAC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { sha256Base64Url } from '../hash/sha256.js';
|
|
2
|
+
import { createDpopProofSigner } from './proof.js';
|
|
3
|
+
/**
|
|
4
|
+
* creates a fetch wrapper that adds DPoP proofs to requests.
|
|
5
|
+
*
|
|
6
|
+
* @param options DPoP configuration
|
|
7
|
+
* @returns fetch function with DPoP support
|
|
8
|
+
*/
|
|
9
|
+
export const createDpopFetch = (options) => {
|
|
10
|
+
const { key, nonces, supportedAlgs, isAuthServer, fetch = globalThis.fetch } = options;
|
|
11
|
+
negotiateAlg(key, supportedAlgs);
|
|
12
|
+
const sign = createDpopProofSigner(key);
|
|
13
|
+
return async (input, init) => {
|
|
14
|
+
const request = init == null && input instanceof Request ? input : new Request(input, init);
|
|
15
|
+
const authHeader = request.headers.get('Authorization');
|
|
16
|
+
const ath = authHeader?.startsWith('DPoP ') ? await sha256Base64Url(authHeader.slice(5)) : undefined;
|
|
17
|
+
const { origin } = new URL(request.url);
|
|
18
|
+
const htm = request.method;
|
|
19
|
+
const htu = buildHtu(request.url);
|
|
20
|
+
let initNonce;
|
|
21
|
+
try {
|
|
22
|
+
initNonce = await nonces.get(origin);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// ignore get errors
|
|
26
|
+
}
|
|
27
|
+
const initProof = await sign(htm, htu, initNonce, ath);
|
|
28
|
+
request.headers.set('DPoP', initProof);
|
|
29
|
+
const initResponse = await fetch(request);
|
|
30
|
+
const nextNonce = initResponse.headers.get('DPoP-Nonce');
|
|
31
|
+
if (!nextNonce || nextNonce === initNonce) {
|
|
32
|
+
return initResponse;
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
await nonces.set(origin, nextNonce);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// ignore set errors
|
|
39
|
+
}
|
|
40
|
+
const shouldRetry = await isUseDpopNonceError(initResponse, isAuthServer);
|
|
41
|
+
if (!shouldRetry) {
|
|
42
|
+
return initResponse;
|
|
43
|
+
}
|
|
44
|
+
if (input === request || init?.body instanceof ReadableStream) {
|
|
45
|
+
return initResponse;
|
|
46
|
+
}
|
|
47
|
+
await initResponse.body?.cancel();
|
|
48
|
+
const nextProof = await sign(htm, htu, nextNonce, ath);
|
|
49
|
+
const nextRequest = new Request(input, init);
|
|
50
|
+
nextRequest.headers.set('DPoP', nextProof);
|
|
51
|
+
const retryResponse = await fetch(nextRequest);
|
|
52
|
+
const retryNonce = retryResponse.headers.get('DPoP-Nonce');
|
|
53
|
+
if (retryNonce && retryNonce !== nextNonce) {
|
|
54
|
+
try {
|
|
55
|
+
await nonces.set(origin, retryNonce);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// ignore set errors
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return retryResponse;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
const buildHtu = (url) => {
|
|
65
|
+
const fragmentIdx = url.indexOf('#');
|
|
66
|
+
const queryIdx = url.indexOf('?');
|
|
67
|
+
const end = fragmentIdx === -1 ? queryIdx : queryIdx === -1 ? fragmentIdx : Math.min(fragmentIdx, queryIdx);
|
|
68
|
+
return end === -1 ? url : url.slice(0, end);
|
|
69
|
+
};
|
|
70
|
+
const negotiateAlg = (key, supportedAlgs) => {
|
|
71
|
+
const keyAlg = key.alg;
|
|
72
|
+
if (supportedAlgs?.length) {
|
|
73
|
+
if (supportedAlgs.includes(keyAlg)) {
|
|
74
|
+
return keyAlg;
|
|
75
|
+
}
|
|
76
|
+
throw new Error(`DPoP key algorithm ${keyAlg} not supported by server: ${supportedAlgs.join(', ')}`);
|
|
77
|
+
}
|
|
78
|
+
return keyAlg;
|
|
79
|
+
};
|
|
80
|
+
const isUseDpopNonceError = async (response, isAuthServer) => {
|
|
81
|
+
if (isAuthServer === undefined || isAuthServer === false) {
|
|
82
|
+
if (response.status === 401) {
|
|
83
|
+
const wwwAuth = response.headers.get('WWW-Authenticate');
|
|
84
|
+
if (wwwAuth?.startsWith('DPoP')) {
|
|
85
|
+
return wwwAuth.includes('error="use_dpop_nonce"');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (isAuthServer === undefined || isAuthServer === true) {
|
|
90
|
+
if (response.status === 400) {
|
|
91
|
+
try {
|
|
92
|
+
const json = await response.clone().json();
|
|
93
|
+
return typeof json === 'object' && json?.error === 'use_dpop_nonce';
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
};
|
|
102
|
+
//# sourceMappingURL=fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../lib/dpop/fetch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DpopPrivateJwk } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* generates a new DPoP private JWK with `alg` set.
|
|
4
|
+
*
|
|
5
|
+
* @param supportedAlgs server supported algorithms (optional)
|
|
6
|
+
* @returns private JWK (with cache pre-warmed)
|
|
7
|
+
*/
|
|
8
|
+
export declare const generateDpopKey: (supportedAlgs?: readonly string[] | undefined) => Promise<DpopPrivateJwk>;
|
|
9
|
+
//# sourceMappingURL=generate-key.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-key.d.ts","sourceRoot":"","sources":["../../lib/dpop/generate-key.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAoCjD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,4EAwB3B,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { getGenerateAlgorithm } from '../internal/crypto.js';
|
|
2
|
+
import { exportPrivateJwkFromKey, isSigningAlgorithm } from '../internal/jwk.js';
|
|
3
|
+
import { setCachedKeyMaterial } from '../internal/key-cache.js';
|
|
4
|
+
/**
|
|
5
|
+
* preferred algorithm order for DPoP key generation.
|
|
6
|
+
*/
|
|
7
|
+
const PREFERRED_ALGORITHMS = [
|
|
8
|
+
'ES256',
|
|
9
|
+
'ES384',
|
|
10
|
+
'ES512',
|
|
11
|
+
'PS256',
|
|
12
|
+
'PS384',
|
|
13
|
+
'PS512',
|
|
14
|
+
'RS256',
|
|
15
|
+
'RS384',
|
|
16
|
+
'RS512',
|
|
17
|
+
];
|
|
18
|
+
const sortAlgorithms = (algs) => {
|
|
19
|
+
return [...algs].sort((a, b) => {
|
|
20
|
+
const aIdx = PREFERRED_ALGORITHMS.indexOf(a);
|
|
21
|
+
const bIdx = PREFERRED_ALGORITHMS.indexOf(b);
|
|
22
|
+
if (aIdx === -1 && bIdx === -1) {
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
25
|
+
if (aIdx === -1) {
|
|
26
|
+
return 1;
|
|
27
|
+
}
|
|
28
|
+
if (bIdx === -1) {
|
|
29
|
+
return -1;
|
|
30
|
+
}
|
|
31
|
+
return aIdx - bIdx;
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* generates a new DPoP private JWK with `alg` set.
|
|
36
|
+
*
|
|
37
|
+
* @param supportedAlgs server supported algorithms (optional)
|
|
38
|
+
* @returns private JWK (with cache pre-warmed)
|
|
39
|
+
*/
|
|
40
|
+
export const generateDpopKey = async (supportedAlgs) => {
|
|
41
|
+
const normalized = supportedAlgs?.filter(isSigningAlgorithm) ?? [];
|
|
42
|
+
if (supportedAlgs?.length && normalized.length === 0) {
|
|
43
|
+
throw new Error(`no supported algorithms provided`);
|
|
44
|
+
}
|
|
45
|
+
const algs = normalized.length ? sortAlgorithms(normalized) : ['ES256'];
|
|
46
|
+
const errors = [];
|
|
47
|
+
for (const alg of algs) {
|
|
48
|
+
try {
|
|
49
|
+
const pair = await crypto.subtle.generateKey(getGenerateAlgorithm(alg), true, ['sign', 'verify']);
|
|
50
|
+
const jwk = (await exportPrivateJwkFromKey(pair.privateKey, alg));
|
|
51
|
+
// pre-populate cache so we don't re-import
|
|
52
|
+
setCachedKeyMaterial(jwk, pair.privateKey);
|
|
53
|
+
return jwk;
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
errors.push(err);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
throw new AggregateError(errors, `failed to generate DPoP key for any of: ${algs.join(', ')}`);
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=generate-key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-key.js","sourceRoot":"","sources":["../../lib/dpop/generate-key.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAKhE;;GAEG;AACH,MAAM,oBAAoB,GAAgC;IACzD,OAAO;IACP,OAAO;IACP,OAAO;IACP,OAAO;IACP,OAAO;IACP,OAAO;IACP,OAAO;IACP,OAAO;IACP,OAAO;CACP,CAAC;AAEF,MAAM,cAAc,GAAG,CAAC,IAAiC,EAAsB,EAAE,CAAC;IACjF,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAE7C,IAAI,IAAI,KAAK,CAAC,CAAC,IAAI,IAAI,KAAK,CAAC,CAAC,EAAE,CAAC;YAChC,OAAO,CAAC,CAAC;QACV,CAAC;QACD,IAAI,IAAI,KAAK,CAAC,CAAC,EAAE,CAAC;YACjB,OAAO,CAAC,CAAC;QACV,CAAC;QACD,IAAI,IAAI,KAAK,CAAC,CAAC,EAAE,CAAC;YACjB,OAAO,CAAC,CAAC,CAAC;QACX,CAAC;QAED,OAAO,IAAI,GAAG,IAAI,CAAC;IAAA,CACnB,CAAC,CAAC;AAAA,CACH,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAAE,aAAiC,EAA2B,EAAE,CAAC;IACpG,MAAM,UAAU,GAAG,aAAa,EAAE,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,CAAC;IACnE,IAAI,aAAa,EAAE,MAAM,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACrD,CAAC;IAED,MAAM,IAAI,GAAuB,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAC5F,MAAM,MAAM,GAAc,EAAE,CAAC;IAE7B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACxB,IAAI,CAAC;YACJ,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,oBAAoB,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;YAClG,MAAM,GAAG,GAAG,CAAC,MAAM,uBAAuB,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,CAAC,CAAmB,CAAC;YAEpF,2CAA2C;YAC3C,oBAAoB,CAAC,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;YAE3C,OAAO,GAAG,CAAC;QACZ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClB,CAAC;IACF,CAAC;IAED,MAAM,IAAI,cAAc,CAAC,MAAM,EAAE,2CAA2C,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAAA,CAC/F,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { createDpopFetch } from './fetch.js';
|
|
2
|
+
export { generateDpopKey } from './generate-key.js';
|
|
3
|
+
export { createDpopProofSigner } from './proof.js';
|
|
4
|
+
export type { DpopNonceCache, DpopPrivateJwk } from './types.js';
|
|
5
|
+
export { DpopVerifyError, verifyDpopProof, type DpopClaims, type DpopVerifyOptions, type DpopVerifyResult, } from './verify.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/dpop/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACnD,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EACN,eAAe,EACf,eAAe,EACf,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACrB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/dpop/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAEnD,OAAO,EACN,eAAe,EACf,eAAe,GAIf,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DpopPrivateJwk } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* creates a DPoP proof signer.
|
|
4
|
+
*
|
|
5
|
+
* @param jwk DPoP private JWK (with `alg` set)
|
|
6
|
+
* @returns signing function for DPoP proofs
|
|
7
|
+
*/
|
|
8
|
+
export declare const createDpopProofSigner: (jwk: DpopPrivateJwk) => (htm: string, htu: string, nonce?: string | undefined, ath?: string | undefined) => Promise<string>;
|
|
9
|
+
//# sourceMappingURL=proof.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proof.d.ts","sourceRoot":"","sources":["../../lib/dpop/proof.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,8HA+BjC,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { nanoid } from 'nanoid';
|
|
2
|
+
import { getCachedKeyMaterial } from '../internal/key-cache.js';
|
|
3
|
+
import { signJwt } from '../jwt/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* creates a DPoP proof signer.
|
|
6
|
+
*
|
|
7
|
+
* @param jwk DPoP private JWK (with `alg` set)
|
|
8
|
+
* @returns signing function for DPoP proofs
|
|
9
|
+
*/
|
|
10
|
+
export const createDpopProofSigner = (jwk) => {
|
|
11
|
+
const alg = jwk.alg;
|
|
12
|
+
// lazily resolve key material on first sign
|
|
13
|
+
let materialPromise;
|
|
14
|
+
return async (htm, htu, nonce, ath) => {
|
|
15
|
+
materialPromise ||= getCachedKeyMaterial(jwk);
|
|
16
|
+
const { cryptoKey, publicJwk } = await materialPromise;
|
|
17
|
+
const now = Math.floor(Date.now() / 1_000);
|
|
18
|
+
return signJwt({
|
|
19
|
+
header: {
|
|
20
|
+
typ: 'dpop+jwt',
|
|
21
|
+
jwk: publicJwk,
|
|
22
|
+
},
|
|
23
|
+
payload: {
|
|
24
|
+
htm,
|
|
25
|
+
htu,
|
|
26
|
+
iat: now,
|
|
27
|
+
jti: nanoid(24),
|
|
28
|
+
nonce,
|
|
29
|
+
ath,
|
|
30
|
+
},
|
|
31
|
+
key: cryptoKey,
|
|
32
|
+
alg,
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=proof.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proof.js","sourceRoot":"","sources":["../../lib/dpop/proof.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGhC,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAI1C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACpC,GAAmB,EAC6D,EAAE,CAAC;IACnF,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IAEpB,4CAA4C;IAC5C,IAAI,eAAuD,CAAC;IAE5D,OAAO,KAAK,EAAE,GAAW,EAAE,GAAW,EAAE,KAAc,EAAE,GAAY,EAAE,EAAE,CAAC;QACxE,eAAe,KAAK,oBAAoB,CAAC,GAAG,CAAC,CAAC;QAC9C,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,MAAM,eAAe,CAAC;QAEvD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC;QAE3C,OAAO,OAAO,CAAC;YACd,MAAM,EAAE;gBACP,GAAG,EAAE,UAAU;gBACf,GAAG,EAAE,SAAS;aACd;YACD,OAAO,EAAE;gBACR,GAAG;gBACH,GAAG;gBACH,GAAG,EAAE,GAAG;gBACR,GAAG,EAAE,MAAM,CAAC,EAAE,CAAC;gBACf,KAAK;gBACL,GAAG;aACH;YACD,GAAG,EAAE,SAAS;YACd,GAAG;SACH,CAAC,CAAC;IAAA,CACH,CAAC;AAAA,CACF,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PrivateJwk, SigningAlgorithm } from '../jwk/types.js';
|
|
2
|
+
export type Awaitable<T> = T | Promise<T>;
|
|
3
|
+
/**
|
|
4
|
+
* private JWK for DPoP proofs.
|
|
5
|
+
*/
|
|
6
|
+
export type DpopPrivateJwk = PrivateJwk & {
|
|
7
|
+
alg: SigningAlgorithm;
|
|
8
|
+
kid?: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* nonce cache for DPoP fetch.
|
|
12
|
+
*/
|
|
13
|
+
export interface DpopNonceCache {
|
|
14
|
+
get(key: string): Awaitable<string | undefined>;
|
|
15
|
+
set(key: string, value: string): Awaitable<void>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../lib/dpop/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEpE,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAE1C;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG;IACzC,GAAG,EAAE,gBAAgB,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;CACb,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAChD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;CACjD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../lib/dpop/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as v from '@badrap/valita';
|
|
2
|
+
import type { PublicJwk } from '../jwk/types.js';
|
|
3
|
+
import type { Awaitable } from './types.js';
|
|
4
|
+
declare const dpopPayloadSchema: v.ObjectType<{
|
|
5
|
+
htm: v.Type<string>;
|
|
6
|
+
htu: v.Type<string>;
|
|
7
|
+
iat: v.Type<number>;
|
|
8
|
+
jti: v.Type<string>;
|
|
9
|
+
nonce: v.Optional<string>;
|
|
10
|
+
}, undefined>;
|
|
11
|
+
export type DpopClaims = v.Infer<typeof dpopPayloadSchema>;
|
|
12
|
+
export interface DpopVerifyResult {
|
|
13
|
+
claims: DpopClaims;
|
|
14
|
+
jkt: string;
|
|
15
|
+
jwk: PublicJwk;
|
|
16
|
+
}
|
|
17
|
+
export interface DpopVerifyOptions {
|
|
18
|
+
method: string;
|
|
19
|
+
url: string;
|
|
20
|
+
nonce?: {
|
|
21
|
+
check(nonce: string): Awaitable<boolean>;
|
|
22
|
+
};
|
|
23
|
+
maxClockSkew?: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* error thrown when dpop verification fails.
|
|
27
|
+
*/
|
|
28
|
+
export declare class DpopVerifyError extends Error {
|
|
29
|
+
code: 'missing' | 'invalid' | 'expired' | 'nonce_required';
|
|
30
|
+
constructor(message: string, code: 'missing' | 'invalid' | 'expired' | 'nonce_required');
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* verifies a dpop proof from a request header.
|
|
34
|
+
*
|
|
35
|
+
* @param dpopHeader dpop header value
|
|
36
|
+
* @param options verification options
|
|
37
|
+
* @returns verification result with claims and jwk thumbprint
|
|
38
|
+
* @throws {DpopVerifyError} if verification fails
|
|
39
|
+
*/
|
|
40
|
+
export declare const verifyDpopProof: (dpopHeader: string | null | undefined, options: DpopVerifyOptions) => Promise<DpopVerifyResult>;
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=verify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../lib/dpop/verify.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,CAAC,MAAM,gBAAgB,CAAC;AAIpC,OAAO,KAAK,EAAE,SAAS,EAAoB,MAAM,iBAAiB,CAAC;AAGnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAsB5C,QAAA,MAAM,iBAAiB;;;;;;aAMrB,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAG3D,MAAM,WAAW,gBAAgB;IAChC,MAAM,EAAE,UAAU,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,SAAS,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE;QAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,CAAA;KAAE,CAAC;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,KAAK;IAGjC,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,gBAAgB;IAFlE,YACC,OAAO,EAAE,MAAM,EACR,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,gBAAgB,EAIjE;CACD;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,kGA8D3B,CAAC"}
|