@atproto/oauth-types 0.1.5 → 0.2.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/CHANGELOG.md +28 -0
- package/dist/atproto-loopback-client-metadata.d.ts +4 -1
- package/dist/atproto-loopback-client-metadata.d.ts.map +1 -1
- package/dist/atproto-loopback-client-metadata.js +1 -2
- package/dist/atproto-loopback-client-metadata.js.map +1 -1
- package/dist/constants.d.ts +0 -6
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +1 -17
- package/dist/constants.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/oauth-authorization-code-grant-token-request.d.ts +2 -2
- package/dist/oauth-authorization-code-grant-token-request.d.ts.map +1 -1
- package/dist/oauth-authorization-code-grant-token-request.js +2 -1
- package/dist/oauth-authorization-code-grant-token-request.js.map +1 -1
- package/dist/oauth-authorization-details.d.ts +42 -4
- package/dist/oauth-authorization-details.d.ts.map +1 -1
- package/dist/oauth-authorization-details.js +21 -1
- package/dist/oauth-authorization-details.js.map +1 -1
- package/dist/oauth-authorization-request-jar.d.ts +1 -1
- package/dist/oauth-authorization-request-par.d.ts +11 -11
- package/dist/oauth-authorization-request-parameters.d.ts +10 -10
- package/dist/oauth-authorization-request-parameters.d.ts.map +1 -1
- package/dist/oauth-authorization-request-parameters.js +3 -2
- package/dist/oauth-authorization-request-parameters.js.map +1 -1
- package/dist/oauth-authorization-request-query.d.ts +11 -11
- package/dist/oauth-authorization-server-metadata.d.ts +69 -66
- package/dist/oauth-authorization-server-metadata.d.ts.map +1 -1
- package/dist/oauth-authorization-server-metadata.js +14 -10
- package/dist/oauth-authorization-server-metadata.js.map +1 -1
- package/dist/oauth-client-id-discoverable.d.ts +3 -2
- package/dist/oauth-client-id-discoverable.d.ts.map +1 -1
- package/dist/oauth-client-id-discoverable.js +54 -31
- package/dist/oauth-client-id-discoverable.js.map +1 -1
- package/dist/oauth-client-id-loopback.d.ts +5 -5
- package/dist/oauth-client-id-loopback.d.ts.map +1 -1
- package/dist/oauth-client-id-loopback.js +32 -31
- package/dist/oauth-client-id-loopback.js.map +1 -1
- package/dist/oauth-client-metadata.d.ts +112 -102
- package/dist/oauth-client-metadata.d.ts.map +1 -1
- package/dist/oauth-client-metadata.js +18 -8
- package/dist/oauth-client-metadata.js.map +1 -1
- package/dist/oauth-issuer-identifier.d.ts +2 -1
- package/dist/oauth-issuer-identifier.d.ts.map +1 -1
- package/dist/oauth-issuer-identifier.js +8 -23
- package/dist/oauth-issuer-identifier.js.map +1 -1
- package/dist/oauth-protected-resource-metadata.d.ts +15 -12
- package/dist/oauth-protected-resource-metadata.d.ts.map +1 -1
- package/dist/oauth-protected-resource-metadata.js +15 -5
- package/dist/oauth-protected-resource-metadata.js.map +1 -1
- package/dist/oauth-redirect-uri.d.ts +10 -0
- package/dist/oauth-redirect-uri.d.ts.map +1 -0
- package/dist/oauth-redirect-uri.js +35 -0
- package/dist/oauth-redirect-uri.js.map +1 -0
- package/dist/oauth-refresh-token-grant-token-request.d.ts +0 -3
- package/dist/oauth-refresh-token-grant-token-request.d.ts.map +1 -1
- package/dist/oauth-refresh-token-grant-token-request.js +0 -2
- package/dist/oauth-refresh-token-grant-token-request.js.map +1 -1
- package/dist/oauth-token-request.d.ts +2 -5
- package/dist/oauth-token-request.d.ts.map +1 -1
- package/dist/oauth-token-response.d.ts +9 -12
- package/dist/oauth-token-response.d.ts.map +1 -1
- package/dist/oauth-token-response.js +4 -2
- package/dist/oauth-token-response.js.map +1 -1
- package/dist/uri.d.ts +20 -0
- package/dist/uri.d.ts.map +1 -0
- package/dist/uri.js +127 -0
- package/dist/uri.js.map +1 -0
- package/dist/util.js +5 -6
- package/dist/util.js.map +1 -1
- package/package.json +2 -2
- package/src/atproto-loopback-client-metadata.ts +8 -3
- package/src/constants.ts +0 -16
- package/src/index.ts +2 -0
- package/src/oauth-authorization-code-grant-token-request.ts +2 -1
- package/src/oauth-authorization-details.ts +21 -1
- package/src/oauth-authorization-request-parameters.ts +3 -2
- package/src/oauth-authorization-server-metadata.ts +14 -10
- package/src/oauth-client-id-discoverable.ts +69 -51
- package/src/oauth-client-id-loopback.ts +40 -40
- package/src/oauth-client-metadata.ts +18 -8
- package/src/oauth-issuer-identifier.ts +14 -24
- package/src/oauth-protected-resource-metadata.ts +15 -5
- package/src/oauth-redirect-uri.ts +56 -0
- package/src/oauth-refresh-token-grant-token-request.ts +0 -2
- package/src/oauth-token-response.ts +4 -2
- package/src/uri.ts +171 -0
- package/tsconfig.build.tsbuildinfo +1 -0
package/dist/uri.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { TypeOf, z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Valid, but potentially dangerous URL (`data:`, `file:`, `javascript:`, etc.).
|
|
4
|
+
*
|
|
5
|
+
* Any value that matches this schema is safe to parse using `new URL()`.
|
|
6
|
+
*/
|
|
7
|
+
export declare const dangerousUriSchema: z.ZodEffects<z.ZodString, `${string}:${string}`, string>;
|
|
8
|
+
/**
|
|
9
|
+
* Valid, but potentially dangerous URL (`data:`, `file:`, `javascript:`, etc.).
|
|
10
|
+
*/
|
|
11
|
+
export type DangerousUrl = TypeOf<typeof dangerousUriSchema>;
|
|
12
|
+
export declare const loopbackUriSchema: z.ZodEffects<z.ZodEffects<z.ZodString, `${string}:${string}`, string>, `http://[::1]${string}` | "http://localhost" | `http://localhost#${string}` | `http://localhost?${string}` | `http://localhost/${string}` | `http://localhost:${string}` | "http://127.0.0.1" | `http://127.0.0.1#${string}` | `http://127.0.0.1?${string}` | `http://127.0.0.1/${string}` | `http://127.0.0.1:${string}`, string>;
|
|
13
|
+
export type LoopbackUri = TypeOf<typeof loopbackUriSchema>;
|
|
14
|
+
export declare const httpsUriSchema: z.ZodEffects<z.ZodEffects<z.ZodString, `${string}:${string}`, string>, `https://${string}`, string>;
|
|
15
|
+
export type HttpsUri = TypeOf<typeof httpsUriSchema>;
|
|
16
|
+
export declare const webUriSchema: z.ZodEffects<z.ZodString, `http://[::1]${string}` | "http://localhost" | `http://localhost#${string}` | `http://localhost?${string}` | `http://localhost/${string}` | `http://localhost:${string}` | "http://127.0.0.1" | `http://127.0.0.1#${string}` | `http://127.0.0.1?${string}` | `http://127.0.0.1/${string}` | `http://127.0.0.1:${string}` | `https://${string}`, string>;
|
|
17
|
+
export type WebUri = TypeOf<typeof webUriSchema>;
|
|
18
|
+
export declare const privateUseUriSchema: z.ZodEffects<z.ZodEffects<z.ZodString, `${string}:${string}`, string>, `${string}.${string}:/${string}`, string>;
|
|
19
|
+
export type PrivateUseUri = TypeOf<typeof privateUseUriSchema>;
|
|
20
|
+
//# sourceMappingURL=uri.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uri.d.ts","sourceRoot":"","sources":["../src/uri.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,CAAC,EAAgB,MAAM,KAAK,CAAA;AAG7C;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,0DAQ5B,CAAA;AAEH;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,kBAAkB,CAAC,CAAA;AAE5D,eAAO,MAAM,iBAAiB,2YA6B7B,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,iBAAiB,CAAC,CAAA;AAE1D,eAAO,MAAM,cAAc,qGA6C1B,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,cAAc,CAAC,CAAA;AAEpD,eAAO,MAAM,YAAY,oXAqBrB,CAAA;AAEJ,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,YAAY,CAAC,CAAA;AAEhD,eAAO,MAAM,mBAAmB,kHAsC/B,CAAA;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAA"}
|
package/dist/uri.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.privateUseUriSchema = exports.webUriSchema = exports.httpsUriSchema = exports.loopbackUriSchema = exports.dangerousUriSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const util_js_1 = require("./util.js");
|
|
6
|
+
/**
|
|
7
|
+
* Valid, but potentially dangerous URL (`data:`, `file:`, `javascript:`, etc.).
|
|
8
|
+
*
|
|
9
|
+
* Any value that matches this schema is safe to parse using `new URL()`.
|
|
10
|
+
*/
|
|
11
|
+
exports.dangerousUriSchema = zod_1.z
|
|
12
|
+
.string()
|
|
13
|
+
.refine((data) => data.includes(':') && URL.canParse(data), {
|
|
14
|
+
message: 'Invalid URL',
|
|
15
|
+
});
|
|
16
|
+
exports.loopbackUriSchema = exports.dangerousUriSchema.superRefine((value, ctx) => {
|
|
17
|
+
// Loopback url must use the "http:" protocol
|
|
18
|
+
if (!value.startsWith('http://')) {
|
|
19
|
+
ctx.addIssue({
|
|
20
|
+
code: zod_1.ZodIssueCode.custom,
|
|
21
|
+
message: 'URL must use the "http:" protocol',
|
|
22
|
+
});
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const url = new URL(value);
|
|
26
|
+
if (!(0, util_js_1.isLoopbackHost)(url.hostname)) {
|
|
27
|
+
ctx.addIssue({
|
|
28
|
+
code: zod_1.ZodIssueCode.custom,
|
|
29
|
+
message: 'URL must use "localhost", "127.0.0.1" or "[::1]" as hostname',
|
|
30
|
+
});
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
return true;
|
|
34
|
+
});
|
|
35
|
+
exports.httpsUriSchema = exports.dangerousUriSchema.superRefine((value, ctx) => {
|
|
36
|
+
if (!value.startsWith('https://')) {
|
|
37
|
+
ctx.addIssue({
|
|
38
|
+
code: zod_1.ZodIssueCode.custom,
|
|
39
|
+
message: 'URL must use the "https:" protocol',
|
|
40
|
+
});
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
const url = new URL(value);
|
|
44
|
+
// Disallow loopback URLs with the `https:` protocol
|
|
45
|
+
if ((0, util_js_1.isLoopbackHost)(url.hostname)) {
|
|
46
|
+
ctx.addIssue({
|
|
47
|
+
code: zod_1.ZodIssueCode.custom,
|
|
48
|
+
message: 'https: URL must not use a loopback host',
|
|
49
|
+
});
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
if ((0, util_js_1.isHostnameIP)(url.hostname)) {
|
|
53
|
+
// Hostname is an IP address
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
// Hostname is a domain name
|
|
57
|
+
if (!url.hostname.includes('.')) {
|
|
58
|
+
// we don't depend on PSL here, so we only check for a dot
|
|
59
|
+
ctx.addIssue({
|
|
60
|
+
code: zod_1.ZodIssueCode.custom,
|
|
61
|
+
message: 'Domain name must contain at least two segments',
|
|
62
|
+
});
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
if (url.hostname.endsWith('.local')) {
|
|
66
|
+
ctx.addIssue({
|
|
67
|
+
code: zod_1.ZodIssueCode.custom,
|
|
68
|
+
message: 'Domain name must not end with ".local"',
|
|
69
|
+
});
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return true;
|
|
74
|
+
});
|
|
75
|
+
exports.webUriSchema = zod_1.z
|
|
76
|
+
.string()
|
|
77
|
+
.superRefine((value, ctx) => {
|
|
78
|
+
// discriminated union of `loopbackUriSchema` and `httpsUriSchema`
|
|
79
|
+
if (value.startsWith('http://')) {
|
|
80
|
+
const result = exports.loopbackUriSchema.safeParse(value);
|
|
81
|
+
if (!result.success)
|
|
82
|
+
result.error.issues.forEach(ctx.addIssue, ctx);
|
|
83
|
+
return result.success;
|
|
84
|
+
}
|
|
85
|
+
if (value.startsWith('https://')) {
|
|
86
|
+
const result = exports.httpsUriSchema.safeParse(value);
|
|
87
|
+
if (!result.success)
|
|
88
|
+
result.error.issues.forEach(ctx.addIssue, ctx);
|
|
89
|
+
return result.success;
|
|
90
|
+
}
|
|
91
|
+
ctx.addIssue({
|
|
92
|
+
code: zod_1.ZodIssueCode.custom,
|
|
93
|
+
message: 'URL must use the "http:" or "https:" protocol',
|
|
94
|
+
});
|
|
95
|
+
return false;
|
|
96
|
+
});
|
|
97
|
+
exports.privateUseUriSchema = exports.dangerousUriSchema.superRefine((value, ctx) => {
|
|
98
|
+
const dotIdx = value.indexOf('.');
|
|
99
|
+
const colonIdx = value.indexOf(':');
|
|
100
|
+
// Optimization: avoid parsing the URL if the protocol does not contain a "."
|
|
101
|
+
if (dotIdx === -1 || colonIdx === -1 || dotIdx > colonIdx) {
|
|
102
|
+
ctx.addIssue({
|
|
103
|
+
code: zod_1.ZodIssueCode.custom,
|
|
104
|
+
message: 'Private-use URI scheme requires a "." as part of the protocol',
|
|
105
|
+
});
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
const url = new URL(value);
|
|
109
|
+
// Should be covered by the check before, but let's be extra sure
|
|
110
|
+
if (!url.protocol.includes('.')) {
|
|
111
|
+
ctx.addIssue({
|
|
112
|
+
code: zod_1.ZodIssueCode.custom,
|
|
113
|
+
message: 'Invalid private-use URI scheme',
|
|
114
|
+
});
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
if (url.hostname) {
|
|
118
|
+
// https://datatracker.ietf.org/doc/html/rfc8252#section-7.1
|
|
119
|
+
ctx.addIssue({
|
|
120
|
+
code: zod_1.ZodIssueCode.custom,
|
|
121
|
+
message: 'Private-use URI schemes must not include a hostname (only one "/" is allowed after the protocol, as per RFC 8252)',
|
|
122
|
+
});
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
return true;
|
|
126
|
+
});
|
|
127
|
+
//# sourceMappingURL=uri.js.map
|
package/dist/uri.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uri.js","sourceRoot":"","sources":["../src/uri.ts"],"names":[],"mappings":"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"}
|
package/dist/util.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.isHostnameIP = isHostnameIP;
|
|
4
|
+
exports.isLoopbackHost = isLoopbackHost;
|
|
5
|
+
exports.isLoopbackUrl = isLoopbackUrl;
|
|
6
|
+
exports.safeUrl = safeUrl;
|
|
7
|
+
exports.extractUrlPath = extractUrlPath;
|
|
4
8
|
function isHostnameIP(hostname) {
|
|
5
9
|
// IPv4
|
|
6
10
|
if (hostname.match(/^\d+\.\d+\.\d+\.\d+$/))
|
|
@@ -10,16 +14,13 @@ function isHostnameIP(hostname) {
|
|
|
10
14
|
return true;
|
|
11
15
|
return false;
|
|
12
16
|
}
|
|
13
|
-
exports.isHostnameIP = isHostnameIP;
|
|
14
17
|
function isLoopbackHost(host) {
|
|
15
18
|
return host === 'localhost' || host === '127.0.0.1' || host === '[::1]';
|
|
16
19
|
}
|
|
17
|
-
exports.isLoopbackHost = isLoopbackHost;
|
|
18
20
|
function isLoopbackUrl(input) {
|
|
19
21
|
const url = typeof input === 'string' ? new URL(input) : input;
|
|
20
22
|
return isLoopbackHost(url.hostname);
|
|
21
23
|
}
|
|
22
|
-
exports.isLoopbackUrl = isLoopbackUrl;
|
|
23
24
|
function safeUrl(input) {
|
|
24
25
|
try {
|
|
25
26
|
return new URL(input);
|
|
@@ -28,7 +29,6 @@ function safeUrl(input) {
|
|
|
28
29
|
return null;
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
|
-
exports.safeUrl = safeUrl;
|
|
32
32
|
function extractUrlPath(url) {
|
|
33
33
|
// Extracts the path from a URL, without relying on the URL constructor
|
|
34
34
|
// (because it normalizes the URL)
|
|
@@ -59,5 +59,4 @@ function extractUrlPath(url) {
|
|
|
59
59
|
}
|
|
60
60
|
return url.substring(pathStart, pathEnd);
|
|
61
61
|
}
|
|
62
|
-
exports.extractUrlPath = extractUrlPath;
|
|
63
62
|
//# sourceMappingURL=util.js.map
|
package/dist/util.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":";;AAAA,oCAQC;AAID,wCAEC;AAED,sCAGC;AAED,0BAMC;AAED,wCAsCC;AAnED,SAAgB,YAAY,CAAC,QAAgB;IAC3C,OAAO;IACP,IAAI,QAAQ,CAAC,KAAK,CAAC,sBAAsB,CAAC;QAAE,OAAO,IAAI,CAAA;IAEvD,OAAO;IACP,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAA;IAEnE,OAAO,KAAK,CAAA;AACd,CAAC;AAID,SAAgB,cAAc,CAAC,IAAa;IAC1C,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,OAAO,CAAA;AACzE,CAAC;AAED,SAAgB,aAAa,CAAC,KAAmB;IAC/C,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;IAC9D,OAAO,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;AACrC,CAAC;AAED,SAAgB,OAAO,CAAC,KAAmB;IACzC,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,CAAA;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,SAAgB,cAAc,CAAC,GAAG;IAChC,uEAAuE;IACvE,kCAAkC;IAClC,MAAM,aAAa,GAAG,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC;QAC9C,CAAC,CAAC,CAAC;QACH,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC;YACzB,CAAC,CAAC,CAAC;YACH,CAAC,CAAC,CAAC,CAAC,CAAA;IACR,IAAI,aAAa,KAAK,CAAC,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,SAAS,CAAC,+CAA+C,CAAC,CAAA;IACtE,CAAC;IAED,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,aAAa,CAAC,CAAA;IAC/C,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,aAAa,CAAC,CAAA;IAEnD,MAAM,WAAW,GACf,WAAW,KAAK,CAAC,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,CAAC,IAAI,WAAW,GAAG,OAAO,CAAC;QAC7D,CAAC,CAAC,WAAW;QACb,CAAC,CAAC,CAAC,CAAC,CAAA;IAER,MAAM,OAAO,GACX,OAAO,KAAK,CAAC,CAAC;QACZ,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC;YAClB,CAAC,CAAC,GAAG,CAAC,MAAM;YACZ,CAAC,CAAC,WAAW;QACf,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC;YAClB,CAAC,CAAC,OAAO;YACT,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,WAAW,CAAC,CAAA;IAEtC,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,aAAa,CAAC,CAAA;IAEhD,MAAM,SAAS,GAAG,QAAQ,KAAK,CAAC,CAAC,IAAI,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAA;IAE5E,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;QAChC,MAAM,IAAI,SAAS,CAAC,yBAAyB,CAAC,CAAA;IAChD,CAAC;IAED,OAAO,GAAG,CAAC,SAAS,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;AAC1C,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/oauth-types",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "OAuth typing & validation library",
|
|
6
6
|
"keywords": [
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"@atproto/jwk": "0.1.1"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"typescript": "^5.
|
|
32
|
+
"typescript": "^5.6.3"
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
35
|
"build": "tsc --build tsconfig.build.json"
|
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
OAuthClientIdLoopback,
|
|
3
|
+
parseOAuthLoopbackClientId,
|
|
4
|
+
} from './oauth-client-id-loopback.js'
|
|
2
5
|
import { OAuthClientMetadataInput } from './oauth-client-metadata.js'
|
|
3
6
|
|
|
4
7
|
export function atprotoLoopbackClientMetadata(
|
|
5
8
|
clientId: string,
|
|
6
|
-
): OAuthClientMetadataInput {
|
|
9
|
+
): OAuthClientMetadataInput & {
|
|
10
|
+
client_id: OAuthClientIdLoopback
|
|
11
|
+
} {
|
|
7
12
|
const {
|
|
8
13
|
scope = 'atproto',
|
|
9
14
|
redirect_uris = [`http://127.0.0.1/`, `http://[::1]/`],
|
|
10
15
|
} = parseOAuthLoopbackClientId(clientId)
|
|
11
16
|
|
|
12
17
|
return {
|
|
13
|
-
client_id: clientId,
|
|
18
|
+
client_id: clientId as OAuthClientIdLoopback,
|
|
14
19
|
scope,
|
|
15
20
|
redirect_uris,
|
|
16
21
|
client_name: 'Loopback client',
|
package/src/constants.ts
CHANGED
|
@@ -1,18 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A variable that allows to determine if unsecure origins should be allowed
|
|
3
|
-
* in OAuth related URI's. This variable is only set to `true` when NODE_ENV
|
|
4
|
-
* is either `development` or `test`.
|
|
5
|
-
*/
|
|
6
|
-
export const ALLOW_UNSECURE_ORIGINS = (() => {
|
|
7
|
-
// try/catch to support running in a browser, including when process.env is
|
|
8
|
-
// shimmed (e.g. by webpack)
|
|
9
|
-
try {
|
|
10
|
-
const env = process.env.NODE_ENV
|
|
11
|
-
return env === 'development' || env === 'test'
|
|
12
|
-
} catch {
|
|
13
|
-
return false
|
|
14
|
-
}
|
|
15
|
-
})()
|
|
16
|
-
|
|
17
1
|
export const CLIENT_ASSERTION_TYPE_JWT_BEARER =
|
|
18
2
|
'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export * from './constants.js'
|
|
2
|
+
export * from './uri.js'
|
|
2
3
|
export * from './util.js'
|
|
3
4
|
|
|
4
5
|
export * from './atproto-loopback-client-metadata.js'
|
|
@@ -25,6 +26,7 @@ export * from './oauth-issuer-identifier.js'
|
|
|
25
26
|
export * from './oauth-par-response.js'
|
|
26
27
|
export * from './oauth-password-grant-token-request.js'
|
|
27
28
|
export * from './oauth-protected-resource-metadata.js'
|
|
29
|
+
export * from './oauth-redirect-uri.js'
|
|
28
30
|
export * from './oauth-refresh-token-grant-token-request.js'
|
|
29
31
|
export * from './oauth-refresh-token.js'
|
|
30
32
|
export * from './oauth-request-uri.js'
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
|
+
import { oauthRedirectUriSchema } from './oauth-redirect-uri.js'
|
|
2
3
|
|
|
3
4
|
export const oauthAuthorizationCodeGrantTokenRequestSchema = z.object({
|
|
4
5
|
grant_type: z.literal('authorization_code'),
|
|
5
6
|
code: z.string().min(1),
|
|
6
|
-
redirect_uri:
|
|
7
|
+
redirect_uri: oauthRedirectUriSchema,
|
|
7
8
|
/** @see {@link https://datatracker.ietf.org/doc/html/rfc7636#section-4.1} */
|
|
8
9
|
code_verifier: z
|
|
9
10
|
.string()
|
|
@@ -1,14 +1,34 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
|
+
import { dangerousUriSchema } from './uri.js'
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* @see {@link https://datatracker.ietf.org/doc/html/rfc9396#section-2 | RFC 9396, Section 2}
|
|
5
6
|
*/
|
|
6
7
|
export const oauthAuthorizationDetailSchema = z.object({
|
|
7
8
|
type: z.string(),
|
|
8
|
-
|
|
9
|
+
/**
|
|
10
|
+
* An array of strings representing the location of the resource or RS. These
|
|
11
|
+
* strings are typically URIs identifying the location of the RS.
|
|
12
|
+
*/
|
|
13
|
+
locations: z.array(dangerousUriSchema).optional(),
|
|
14
|
+
/**
|
|
15
|
+
* An array of strings representing the kinds of actions to be taken at the
|
|
16
|
+
* resource.
|
|
17
|
+
*/
|
|
9
18
|
actions: z.array(z.string()).optional(),
|
|
19
|
+
/**
|
|
20
|
+
* An array of strings representing the kinds of data being requested from the
|
|
21
|
+
* resource.
|
|
22
|
+
*/
|
|
10
23
|
datatypes: z.array(z.string()).optional(),
|
|
24
|
+
/**
|
|
25
|
+
* A string identifier indicating a specific resource available at the API.
|
|
26
|
+
*/
|
|
11
27
|
identifier: z.string().optional(),
|
|
28
|
+
/**
|
|
29
|
+
* An array of strings representing the types or levels of privilege being
|
|
30
|
+
* requested at the resource.
|
|
31
|
+
*/
|
|
12
32
|
privileges: z.array(z.string()).optional(),
|
|
13
33
|
})
|
|
14
34
|
|
|
@@ -4,6 +4,7 @@ import { z } from 'zod'
|
|
|
4
4
|
import { oauthAuthorizationDetailsSchema } from './oauth-authorization-details.js'
|
|
5
5
|
import { oauthClientIdSchema } from './oauth-client-id.js'
|
|
6
6
|
import { oauthCodeChallengeMethodSchema } from './oauth-code-challenge-method.js'
|
|
7
|
+
import { oauthRedirectUriSchema } from './oauth-redirect-uri.js'
|
|
7
8
|
import { oauthResponseTypeSchema } from './oauth-response-type.js'
|
|
8
9
|
import { oauthScopeSchema } from './oauth-scope.js'
|
|
9
10
|
import { oidcClaimsParameterSchema } from './oidc-claims-parameter.js'
|
|
@@ -16,7 +17,7 @@ import { oidcEntityTypeSchema } from './oidc-entity-type.js'
|
|
|
16
17
|
export const oauthAuthorizationRequestParametersSchema = z.object({
|
|
17
18
|
client_id: oauthClientIdSchema,
|
|
18
19
|
state: z.string().optional(),
|
|
19
|
-
redirect_uri:
|
|
20
|
+
redirect_uri: oauthRedirectUriSchema.optional(),
|
|
20
21
|
scope: oauthScopeSchema.optional(),
|
|
21
22
|
response_type: oauthResponseTypeSchema,
|
|
22
23
|
|
|
@@ -73,7 +74,7 @@ export const oauthAuthorizationRequestParametersSchema = z.object({
|
|
|
73
74
|
id_token_hint: signedJwtSchema.optional(),
|
|
74
75
|
|
|
75
76
|
// Type of UI the AS is displayed on
|
|
76
|
-
display: z.enum(['page', 'popup', 'touch']).optional(),
|
|
77
|
+
display: z.enum(['page', 'popup', 'touch', 'wap']).optional(),
|
|
77
78
|
|
|
78
79
|
/**
|
|
79
80
|
* - "none" will only be allowed if the user already allowed the client on the same device
|
|
@@ -2,9 +2,13 @@ import { z } from 'zod'
|
|
|
2
2
|
|
|
3
3
|
import { oauthCodeChallengeMethodSchema } from './oauth-code-challenge-method.js'
|
|
4
4
|
import { oauthIssuerIdentifierSchema } from './oauth-issuer-identifier.js'
|
|
5
|
+
import { webUriSchema } from './uri.js'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @see {@link https://datatracker.ietf.org/doc/html/rfc8414}
|
|
9
|
+
* @note we do not enforce https: scheme in URIs to support development
|
|
10
|
+
* environments. Make sure to validate the URIs before using it in a production
|
|
11
|
+
* environment.
|
|
8
12
|
*/
|
|
9
13
|
export const oauthAuthorizationServerMetadataSchema = z.object({
|
|
10
14
|
issuer: oauthIssuerIdentifierSchema,
|
|
@@ -37,31 +41,31 @@ export const oauthAuthorizationServerMetadataSchema = z.object({
|
|
|
37
41
|
.array(z.string())
|
|
38
42
|
.optional(),
|
|
39
43
|
|
|
40
|
-
jwks_uri:
|
|
44
|
+
jwks_uri: webUriSchema.optional(),
|
|
41
45
|
|
|
42
|
-
authorization_endpoint:
|
|
46
|
+
authorization_endpoint: webUriSchema, // .optional(),
|
|
43
47
|
|
|
44
|
-
token_endpoint:
|
|
48
|
+
token_endpoint: webUriSchema, // .optional(),
|
|
45
49
|
token_endpoint_auth_methods_supported: z.array(z.string()).optional(),
|
|
46
50
|
token_endpoint_auth_signing_alg_values_supported: z
|
|
47
51
|
.array(z.string())
|
|
48
52
|
.optional(),
|
|
49
53
|
|
|
50
|
-
revocation_endpoint:
|
|
51
|
-
introspection_endpoint:
|
|
52
|
-
pushed_authorization_request_endpoint:
|
|
54
|
+
revocation_endpoint: webUriSchema.optional(),
|
|
55
|
+
introspection_endpoint: webUriSchema.optional(),
|
|
56
|
+
pushed_authorization_request_endpoint: webUriSchema.optional(),
|
|
53
57
|
|
|
54
58
|
require_pushed_authorization_requests: z.boolean().optional(),
|
|
55
59
|
|
|
56
|
-
userinfo_endpoint:
|
|
57
|
-
end_session_endpoint:
|
|
58
|
-
registration_endpoint:
|
|
60
|
+
userinfo_endpoint: webUriSchema.optional(),
|
|
61
|
+
end_session_endpoint: webUriSchema.optional(),
|
|
62
|
+
registration_endpoint: webUriSchema.optional(),
|
|
59
63
|
|
|
60
64
|
// https://datatracker.ietf.org/doc/html/rfc9449#section-5.1
|
|
61
65
|
dpop_signing_alg_values_supported: z.array(z.string()).optional(),
|
|
62
66
|
|
|
63
67
|
// https://datatracker.ietf.org/doc/html/draft-ietf-oauth-resource-metadata-05#section-4
|
|
64
|
-
protected_resources: z.array(
|
|
68
|
+
protected_resources: z.array(webUriSchema).optional(),
|
|
65
69
|
|
|
66
70
|
// https://drafts.aaronpk.com/draft-parecki-oauth-client-id-metadata-document/draft-parecki-oauth-client-id-metadata-document.html
|
|
67
71
|
client_id_metadata_document_supported: z.boolean().optional(),
|
|
@@ -1,69 +1,87 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TypeOf, z } from 'zod'
|
|
2
|
+
import { oauthClientIdSchema } from './oauth-client-id.js'
|
|
3
|
+
import { httpsUriSchema } from './uri.js'
|
|
2
4
|
import { extractUrlPath, isHostnameIP } from './util.js'
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* @see {@link https://drafts.aaronpk.com/draft-parecki-oauth-client-id-metadata-document/draft-parecki-oauth-client-id-metadata-document.html}
|
|
6
8
|
*/
|
|
7
|
-
export
|
|
9
|
+
export const oauthClientIdDiscoverableSchema = z
|
|
10
|
+
.intersection(oauthClientIdSchema, httpsUriSchema)
|
|
11
|
+
.superRefine((value, ctx): value is `https://${string}/${string}` => {
|
|
12
|
+
const url = new URL(value)
|
|
13
|
+
|
|
14
|
+
if (url.username || url.password) {
|
|
15
|
+
ctx.addIssue({
|
|
16
|
+
code: z.ZodIssueCode.custom,
|
|
17
|
+
message: 'ClientID must not contain credentials',
|
|
18
|
+
})
|
|
19
|
+
return false
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (url.hash) {
|
|
23
|
+
ctx.addIssue({
|
|
24
|
+
code: z.ZodIssueCode.custom,
|
|
25
|
+
message: 'ClientID must not contain a fragment',
|
|
26
|
+
})
|
|
27
|
+
return false
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (url.pathname === '/') {
|
|
31
|
+
ctx.addIssue({
|
|
32
|
+
code: z.ZodIssueCode.custom,
|
|
33
|
+
message:
|
|
34
|
+
'ClientID must contain a path component (e.g. "/client-metadata.json")',
|
|
35
|
+
})
|
|
36
|
+
return false
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (url.pathname.endsWith('/')) {
|
|
40
|
+
ctx.addIssue({
|
|
41
|
+
code: z.ZodIssueCode.custom,
|
|
42
|
+
message: 'ClientID path must not end with a trailing slash',
|
|
43
|
+
})
|
|
44
|
+
return false
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (isHostnameIP(url.hostname)) {
|
|
48
|
+
ctx.addIssue({
|
|
49
|
+
code: z.ZodIssueCode.custom,
|
|
50
|
+
message: 'ClientID hostname must not be an IP address',
|
|
51
|
+
})
|
|
52
|
+
return false
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// URL constructor normalizes the URL, so we extract the path manually to
|
|
56
|
+
// avoid normalization, then compare it to the normalized path to ensure
|
|
57
|
+
// that the URL does not contain path traversal or other unexpected characters
|
|
58
|
+
if (extractUrlPath(value) !== url.pathname) {
|
|
59
|
+
ctx.addIssue({
|
|
60
|
+
code: z.ZodIssueCode.custom,
|
|
61
|
+
message: `ClientID must be in canonical form ("${url.href}", got "${value}")`,
|
|
62
|
+
})
|
|
63
|
+
return false
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return true
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
export type OAuthClientIdDiscoverable = TypeOf<
|
|
70
|
+
typeof oauthClientIdDiscoverableSchema
|
|
71
|
+
>
|
|
8
72
|
|
|
9
73
|
export function isOAuthClientIdDiscoverable(
|
|
10
74
|
clientId: string,
|
|
11
75
|
): clientId is OAuthClientIdDiscoverable {
|
|
12
|
-
|
|
13
|
-
parseOAuthDiscoverableClientId(clientId)
|
|
14
|
-
return true
|
|
15
|
-
} catch {
|
|
16
|
-
return false
|
|
17
|
-
}
|
|
76
|
+
return oauthClientIdDiscoverableSchema.safeParse(clientId).success
|
|
18
77
|
}
|
|
19
78
|
|
|
20
79
|
export function assertOAuthDiscoverableClientId(
|
|
21
80
|
value: string,
|
|
22
81
|
): asserts value is OAuthClientIdDiscoverable {
|
|
23
|
-
void
|
|
82
|
+
void oauthClientIdDiscoverableSchema.parse(value)
|
|
24
83
|
}
|
|
25
84
|
|
|
26
85
|
export function parseOAuthDiscoverableClientId(clientId: string): URL {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (url.protocol !== 'https:') {
|
|
30
|
-
throw new TypeError('ClientID must use the "https:" protocol')
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (url.username || url.password) {
|
|
34
|
-
throw new TypeError('ClientID must not contain credentials')
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (url.hash) {
|
|
38
|
-
throw new TypeError('ClientID must not contain a fragment')
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (url.hostname === 'localhost') {
|
|
42
|
-
throw new TypeError('ClientID hostname must not be "localhost"')
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (url.pathname === '/') {
|
|
46
|
-
throw new TypeError(
|
|
47
|
-
'ClientID must contain a path component (e.g. "/client-metadata.json")',
|
|
48
|
-
)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (url.pathname.endsWith('/')) {
|
|
52
|
-
throw new TypeError('ClientID path must not end with a trailing slash')
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (isHostnameIP(url.hostname)) {
|
|
56
|
-
throw new TypeError('ClientID hostname must not be an IP address')
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// URL constructor normalizes the URL, so we extract the path manually to
|
|
60
|
-
// avoid normalization, then compare it to the normalized path to ensure
|
|
61
|
-
// that the URL does not contain path traversal or other unexpected characters
|
|
62
|
-
if (extractUrlPath(clientId) !== url.pathname) {
|
|
63
|
-
throw new TypeError(
|
|
64
|
-
`ClientID must be in canonical form ("${url.href}", got "${clientId}")`,
|
|
65
|
-
)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return url
|
|
86
|
+
return new URL(oauthClientIdDiscoverableSchema.parse(clientId))
|
|
69
87
|
}
|