@geekity/cms 0.12.0 → 0.13.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.
Files changed (232) hide show
  1. package/README.md +170 -4
  2. package/admin/pages/account/login.njk +3 -0
  3. package/admin/pages/documents/editor.njk +7 -0
  4. package/admin/pages/indieauth/consent.njk +51 -0
  5. package/admin/pages/indieauth/refused.njk +11 -0
  6. package/admin/pages/settings/general.njk +9 -0
  7. package/admin/pages/settings/reading.njk +93 -0
  8. package/admin/pages/users/apps.njk +74 -0
  9. package/admin/pages/users/edit.njk +12 -0
  10. package/admin/static/admin.css +27 -0
  11. package/dist/admin/accounts.d.ts +3 -0
  12. package/dist/admin/accounts.d.ts.map +1 -1
  13. package/dist/admin/accounts.js +5 -0
  14. package/dist/admin/accounts.js.map +1 -1
  15. package/dist/admin/documents.d.ts +2 -0
  16. package/dist/admin/documents.d.ts.map +1 -1
  17. package/dist/admin/documents.js +17 -1
  18. package/dist/admin/documents.js.map +1 -1
  19. package/dist/admin/headers.d.ts +5 -1
  20. package/dist/admin/headers.d.ts.map +1 -1
  21. package/dist/admin/headers.js +7 -3
  22. package/dist/admin/headers.js.map +1 -1
  23. package/dist/admin/menu.d.ts.map +1 -1
  24. package/dist/admin/menu.js +2 -1
  25. package/dist/admin/menu.js.map +1 -1
  26. package/dist/admin/routes.d.ts.map +1 -1
  27. package/dist/admin/routes.js +48 -5
  28. package/dist/admin/routes.js.map +1 -1
  29. package/dist/admin/settings-general.js +1 -1
  30. package/dist/admin/settings-general.js.map +1 -1
  31. package/dist/admin/settings-reading.d.ts.map +1 -1
  32. package/dist/admin/settings-reading.js +15 -1
  33. package/dist/admin/settings-reading.js.map +1 -1
  34. package/dist/admin/settings.d.ts +79 -2
  35. package/dist/admin/settings.d.ts.map +1 -1
  36. package/dist/admin/settings.js +168 -2
  37. package/dist/admin/settings.js.map +1 -1
  38. package/dist/admin/templates.d.ts +3 -0
  39. package/dist/admin/templates.d.ts.map +1 -1
  40. package/dist/admin/templates.js +3 -0
  41. package/dist/admin/templates.js.map +1 -1
  42. package/dist/admin/users.d.ts +8 -0
  43. package/dist/admin/users.d.ts.map +1 -1
  44. package/dist/admin/users.js +9 -0
  45. package/dist/admin/users.js.map +1 -1
  46. package/dist/config.d.ts +30 -0
  47. package/dist/config.d.ts.map +1 -1
  48. package/dist/config.js +1 -0
  49. package/dist/config.js.map +1 -1
  50. package/dist/env.d.ts +14 -0
  51. package/dist/env.d.ts.map +1 -1
  52. package/dist/federation/article.d.ts.map +1 -1
  53. package/dist/federation/article.js +18 -5
  54. package/dist/federation/article.js.map +1 -1
  55. package/dist/images/icons.d.ts +44 -25
  56. package/dist/images/icons.d.ts.map +1 -1
  57. package/dist/images/icons.js +205 -60
  58. package/dist/images/icons.js.map +1 -1
  59. package/dist/images/index.d.ts +2 -2
  60. package/dist/images/index.d.ts.map +1 -1
  61. package/dist/images/index.js +1 -1
  62. package/dist/images/index.js.map +1 -1
  63. package/dist/images/markup.d.ts +14 -0
  64. package/dist/images/markup.d.ts.map +1 -1
  65. package/dist/images/markup.js +16 -11
  66. package/dist/images/markup.js.map +1 -1
  67. package/dist/images/variants.d.ts.map +1 -1
  68. package/dist/images/variants.js +3 -4
  69. package/dist/images/variants.js.map +1 -1
  70. package/dist/index.d.ts +14 -4
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js +28 -1
  73. package/dist/index.js.map +1 -1
  74. package/dist/indexnow.d.ts +34 -0
  75. package/dist/indexnow.d.ts.map +1 -0
  76. package/dist/indexnow.js +141 -0
  77. package/dist/indexnow.js.map +1 -0
  78. package/dist/indieauth/bearer.d.ts +39 -0
  79. package/dist/indieauth/bearer.d.ts.map +1 -0
  80. package/dist/indieauth/bearer.js +63 -0
  81. package/dist/indieauth/bearer.js.map +1 -0
  82. package/dist/indieauth/client.d.ts +61 -0
  83. package/dist/indieauth/client.d.ts.map +1 -0
  84. package/dist/indieauth/client.js +139 -0
  85. package/dist/indieauth/client.js.map +1 -0
  86. package/dist/indieauth/connected-apps.d.ts +23 -0
  87. package/dist/indieauth/connected-apps.d.ts.map +1 -0
  88. package/dist/indieauth/connected-apps.js +73 -0
  89. package/dist/indieauth/connected-apps.js.map +1 -0
  90. package/dist/indieauth/consent.d.ts +28 -0
  91. package/dist/indieauth/consent.d.ts.map +1 -0
  92. package/dist/indieauth/consent.js +185 -0
  93. package/dist/indieauth/consent.js.map +1 -0
  94. package/dist/indieauth/discovery.d.ts +87 -0
  95. package/dist/indieauth/discovery.d.ts.map +1 -0
  96. package/dist/indieauth/discovery.js +127 -0
  97. package/dist/indieauth/discovery.js.map +1 -0
  98. package/dist/indieauth/grants.d.ts +57 -0
  99. package/dist/indieauth/grants.d.ts.map +1 -0
  100. package/dist/indieauth/grants.js +35 -0
  101. package/dist/indieauth/grants.js.map +1 -0
  102. package/dist/indieauth/identity.d.ts +36 -0
  103. package/dist/indieauth/identity.d.ts.map +1 -0
  104. package/dist/indieauth/identity.js +78 -0
  105. package/dist/indieauth/identity.js.map +1 -0
  106. package/dist/indieauth/redeem.d.ts +47 -0
  107. package/dist/indieauth/redeem.d.ts.map +1 -0
  108. package/dist/indieauth/redeem.js +69 -0
  109. package/dist/indieauth/redeem.js.map +1 -0
  110. package/dist/indieauth/request.d.ts +47 -0
  111. package/dist/indieauth/request.d.ts.map +1 -0
  112. package/dist/indieauth/request.js +104 -0
  113. package/dist/indieauth/request.js.map +1 -0
  114. package/dist/indieauth/token.d.ts +19 -0
  115. package/dist/indieauth/token.d.ts.map +1 -0
  116. package/dist/indieauth/token.js +110 -0
  117. package/dist/indieauth/token.js.map +1 -0
  118. package/dist/indieauth/tokens.d.ts +131 -0
  119. package/dist/indieauth/tokens.d.ts.map +1 -0
  120. package/dist/indieauth/tokens.js +205 -0
  121. package/dist/indieauth/tokens.js.map +1 -0
  122. package/dist/web/archive.d.ts +1 -1
  123. package/dist/web/archive.d.ts.map +1 -1
  124. package/dist/web/archive.js +3 -2
  125. package/dist/web/archive.js.map +1 -1
  126. package/dist/web/context.d.ts +25 -0
  127. package/dist/web/context.d.ts.map +1 -1
  128. package/dist/web/context.js +18 -0
  129. package/dist/web/context.js.map +1 -1
  130. package/dist/web/feed-atom.d.ts.map +1 -1
  131. package/dist/web/feed-atom.js +3 -1
  132. package/dist/web/feed-atom.js.map +1 -1
  133. package/dist/web/feed-item.d.ts +11 -2
  134. package/dist/web/feed-item.d.ts.map +1 -1
  135. package/dist/web/feed-item.js +9 -3
  136. package/dist/web/feed-item.js.map +1 -1
  137. package/dist/web/feed-json.d.ts +4 -0
  138. package/dist/web/feed-json.d.ts.map +1 -1
  139. package/dist/web/feed-json.js +4 -1
  140. package/dist/web/feed-json.js.map +1 -1
  141. package/dist/web/feed-rss.d.ts.map +1 -1
  142. package/dist/web/feed-rss.js +16 -3
  143. package/dist/web/feed-rss.js.map +1 -1
  144. package/dist/web/feed-source.d.ts +28 -0
  145. package/dist/web/feed-source.d.ts.map +1 -1
  146. package/dist/web/feed-source.js +31 -0
  147. package/dist/web/feed-source.js.map +1 -1
  148. package/dist/web/feed-xml.d.ts +2 -0
  149. package/dist/web/feed-xml.d.ts.map +1 -1
  150. package/dist/web/feed-xml.js +2 -0
  151. package/dist/web/feed-xml.js.map +1 -1
  152. package/dist/web/feeds.d.ts.map +1 -1
  153. package/dist/web/feeds.js +8 -1
  154. package/dist/web/feeds.js.map +1 -1
  155. package/dist/web/index.d.ts +3 -1
  156. package/dist/web/index.d.ts.map +1 -1
  157. package/dist/web/index.js +2 -1
  158. package/dist/web/index.js.map +1 -1
  159. package/dist/web/indexnow.d.ts +9 -0
  160. package/dist/web/indexnow.d.ts.map +1 -0
  161. package/dist/web/indexnow.js +27 -0
  162. package/dist/web/indexnow.js.map +1 -0
  163. package/dist/web/llms.d.ts +58 -0
  164. package/dist/web/llms.d.ts.map +1 -0
  165. package/dist/web/llms.js +91 -0
  166. package/dist/web/llms.js.map +1 -0
  167. package/dist/web/locale.d.ts +41 -0
  168. package/dist/web/locale.d.ts.map +1 -0
  169. package/dist/web/locale.js +56 -0
  170. package/dist/web/locale.js.map +1 -0
  171. package/dist/web/manifest.d.ts +50 -0
  172. package/dist/web/manifest.d.ts.map +1 -0
  173. package/dist/web/manifest.js +56 -0
  174. package/dist/web/manifest.js.map +1 -0
  175. package/dist/web/negotiate.d.ts.map +1 -1
  176. package/dist/web/negotiate.js +6 -0
  177. package/dist/web/negotiate.js.map +1 -1
  178. package/dist/web/render.d.ts +6 -1
  179. package/dist/web/render.d.ts.map +1 -1
  180. package/dist/web/render.js +41 -10
  181. package/dist/web/render.js.map +1 -1
  182. package/dist/web/robots.d.ts +89 -0
  183. package/dist/web/robots.d.ts.map +1 -0
  184. package/dist/web/robots.js +259 -0
  185. package/dist/web/robots.js.map +1 -0
  186. package/dist/web/routes.d.ts.map +1 -1
  187. package/dist/web/routes.js +105 -6
  188. package/dist/web/routes.js.map +1 -1
  189. package/dist/web/share-image.d.ts +51 -0
  190. package/dist/web/share-image.d.ts.map +1 -0
  191. package/dist/web/share-image.js +43 -0
  192. package/dist/web/share-image.js.map +1 -0
  193. package/dist/web/sitemap.d.ts +0 -26
  194. package/dist/web/sitemap.d.ts.map +1 -1
  195. package/dist/web/sitemap.js +0 -44
  196. package/dist/web/sitemap.js.map +1 -1
  197. package/dist/web/templates.d.ts +16 -9
  198. package/dist/web/templates.d.ts.map +1 -1
  199. package/dist/web/templates.js +83 -55
  200. package/dist/web/templates.js.map +1 -1
  201. package/dist/web/themes.d.ts +33 -0
  202. package/dist/web/themes.d.ts.map +1 -1
  203. package/dist/web/themes.js +57 -0
  204. package/dist/web/themes.js.map +1 -1
  205. package/dist/webmention/discovery.d.ts +4 -2
  206. package/dist/webmention/discovery.d.ts.map +1 -1
  207. package/dist/webmention/discovery.js +14 -8
  208. package/dist/webmention/discovery.js.map +1 -1
  209. package/dist/webmention/fetch-public.d.ts +2 -0
  210. package/dist/webmention/fetch-public.d.ts.map +1 -1
  211. package/dist/webmention/fetch-public.js +1 -1
  212. package/dist/webmention/fetch-public.js.map +1 -1
  213. package/package.json +1 -1
  214. package/themes/default/README.md +119 -29
  215. package/themes/default/layouts/base.njk +95 -16
  216. package/themes/default/layouts/front-page.njk +6 -1
  217. package/themes/default/layouts/home.njk +10 -0
  218. package/themes/default/layouts/page.njk +4 -3
  219. package/themes/default/layouts/post.njk +5 -4
  220. package/themes/default/layouts/search.njk +4 -5
  221. package/themes/default/partials/archive.njk +3 -1
  222. package/themes/default/partials/bio.njk +9 -2
  223. package/themes/default/partials/breadcrumbs.njk +20 -0
  224. package/themes/default/partials/conversation.njk +4 -1
  225. package/themes/default/partials/jsonld.njk +19 -4
  226. package/themes/default/partials/kicker.njk +6 -4
  227. package/themes/default/partials/lang.njk +7 -0
  228. package/themes/default/partials/post-list.njk +5 -3
  229. package/themes/default/partials/reply-context.njk +4 -2
  230. package/themes/default/src/style.css +14 -0
  231. package/themes/default/static/style.css +53 -0
  232. package/themes/default/theme.json +2 -0
@@ -0,0 +1,63 @@
1
+ import { findUserById } from '../admin/accounts.js';
2
+ import { PROTECTED_RESOURCE_METADATA_PATH, siteBaseUrl } from './discovery.js';
3
+ import { findAccessToken, recordUse, verifyAccessToken } from './tokens.js';
4
+ /**
5
+ * Turn the access token on a request into the user and scopes it grants, and
6
+ * refuse the request when it has none, a bad one, or one without `scope`.
7
+ *
8
+ * The token comes from an `Authorization: Bearer` header or, as Micropub
9
+ * allows, an `access_token` field of a form body. A refusal carries
10
+ * `WWW-Authenticate: Bearer` naming the protected resource metadata (RFC
11
+ * 9728), which is how an MCP client finds out where to sign in.
12
+ */
13
+ export function requireBearer(guard) {
14
+ return async (c, next) => {
15
+ const { config } = c.var;
16
+ const presented = await presentedToken(c);
17
+ if (presented === undefined) {
18
+ return refuse(c, 401, 'unauthorized', 'An access token is required.', {});
19
+ }
20
+ const now = config.now();
21
+ const token = guard.audience === 'authorization-server'
22
+ ? findAccessToken(config.dataDir, presented, now)
23
+ : verifyAccessToken(config.dataDir, presented, guard.audience, now);
24
+ const user = token === undefined ? undefined : findUserById(config.dataDir, token.userId);
25
+ if (token === undefined || user === undefined) {
26
+ const description = 'The access token is unknown, expired, revoked or not for this resource.';
27
+ return refuse(c, 401, 'invalid_token', description, { error: 'invalid_token' });
28
+ }
29
+ if (guard.scope !== undefined && !token.scopes.includes(guard.scope)) {
30
+ const description = `The access token was not granted the ${guard.scope} scope.`;
31
+ return refuse(c, 403, 'insufficient_scope', description, {
32
+ error: 'insufficient_scope',
33
+ scope: guard.scope,
34
+ });
35
+ }
36
+ await recordUse(config.dataDir, token, now);
37
+ c.set('bearer', { token, user });
38
+ await next();
39
+ };
40
+ }
41
+ async function presentedToken(c) {
42
+ const header = /^Bearer +(\S+)$/i.exec(c.req.header('authorization') ?? '');
43
+ if (header !== null)
44
+ return header[1];
45
+ const type = c.req.header('content-type') ?? '';
46
+ if (!/^(application\/x-www-form-urlencoded|multipart\/form-data)\b/i.test(type)) {
47
+ return undefined;
48
+ }
49
+ const field = (await c.req.parseBody())['access_token'];
50
+ return typeof field === 'string' && field !== '' ? field : undefined;
51
+ }
52
+ function refuse(c, status, error, description, params) {
53
+ const challenge = Object.entries({
54
+ ...params,
55
+ resource_metadata: `${siteBaseUrl(c)}${PROTECTED_RESOURCE_METADATA_PATH}`,
56
+ })
57
+ .map(([name, value]) => `${name}="${value}"`)
58
+ .join(', ');
59
+ c.header('www-authenticate', `Bearer ${challenge}`);
60
+ c.header('cache-control', 'no-store');
61
+ return c.json({ error, error_description: description }, status);
62
+ }
63
+ //# sourceMappingURL=bearer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bearer.js","sourceRoot":"","sources":["../../src/indieauth/bearer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ import type { HostLookup } from '../webmention/public-address.ts';
2
+ /** What a client says about itself at its client_id URL. */
3
+ export interface ClientInformation {
4
+ /** What to call it on the consent screen. */
5
+ readonly name?: string;
6
+ /** Its home page. */
7
+ readonly url?: string;
8
+ /** Where its logo is, an http or https URL. */
9
+ readonly logo?: string;
10
+ /** Where it may be sent back to, besides its own origin. */
11
+ readonly redirectUris: readonly string[];
12
+ }
13
+ /** How long a client is given to answer. */
14
+ export declare const CLIENT_FETCH_TIMEOUT_MS = 5000;
15
+ /** The most of a client's page or document that is read. */
16
+ export declare const CLIENT_FETCH_MAX_BYTES: number;
17
+ /** The most of a client's logo that is read. */
18
+ export declare const CLIENT_LOGO_MAX_BYTES: number;
19
+ /** What {@link fetchClientInformation} and {@link fetchClientLogo} need. */
20
+ export interface FetchClientOptions {
21
+ readonly lookup: HostLookup;
22
+ readonly timeoutMs?: number | undefined;
23
+ readonly maxBytes?: number | undefined;
24
+ }
25
+ /** What fetching one client_id came to. */
26
+ export type ClientFetch = {
27
+ readonly ok: true;
28
+ readonly client: ClientInformation;
29
+ } | {
30
+ readonly ok: false;
31
+ readonly reason: string;
32
+ };
33
+ /**
34
+ * Fetch a client_id URL and read what it says about the client.
35
+ *
36
+ * The URL is whatever the person's browser was sent here with, so the fetch
37
+ * goes through {@link fetchPublic}: public hosts only, every redirect hop
38
+ * checked, one timeout and a byte limit.
39
+ */
40
+ export declare function fetchClientInformation(clientId: string, options: FetchClientOptions): Promise<ClientFetch>;
41
+ /**
42
+ * Fetch a client's logo as a `data:` URI, or `undefined` when it cannot be had.
43
+ *
44
+ * Fetched here rather than linked from the page, so the admin's `img-src`
45
+ * never names the client and the client never learns the address of the
46
+ * person approving it, or when. The fetch is held to {@link fetchPublic}'s
47
+ * rules like the client_id itself, to image types only, and to
48
+ * {@link CLIENT_LOGO_MAX_BYTES}.
49
+ */
50
+ export declare function fetchClientLogo(url: string, options: FetchClientOptions): Promise<string | undefined>;
51
+ /**
52
+ * What a client_id document says, by its content type.
53
+ *
54
+ * A JSON document is a client metadata document, which the OAuth Client ID
55
+ * Metadata Document draft and MCP use. Its `client_id` must be its own URL;
56
+ * one that names another client is read as saying nothing. Anything else is
57
+ * read as an HTML page with an IndieAuth `h-app` (or the older `h-x-app`) and
58
+ * `rel="redirect_uri"` links, in the response's `Link` header or in the page.
59
+ */
60
+ export declare function readClientInformation(body: string, type: string, clientId: string, base?: string, link?: string | null): ClientInformation;
61
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/indieauth/client.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAElE,4DAA4D;AAC5D,MAAM,WAAW,iBAAiB;IAChC,6CAA6C;IAC7C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,qBAAqB;IACrB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAED,4CAA4C;AAC5C,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAE5C,4DAA4D;AAC5D,eAAO,MAAM,sBAAsB,QAAa,CAAC;AAEjD,gDAAgD;AAChD,eAAO,MAAM,qBAAqB,QAAY,CAAC;AAM/C,4EAA4E;AAC5E,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC;AAED,2CAA2C;AAC3C,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE,GACzD;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpD;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,WAAW,CAAC,CAiBtB;AAED;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAW7B;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,MAAiB,EACvB,IAAI,GAAE,MAAM,GAAG,IAAW,GACzB,iBAAiB,CAEnB"}
@@ -0,0 +1,139 @@
1
+ import { linkTargets } from '../webmention/discovery.js';
2
+ import { fetchPublic, webUrl } from '../webmention/fetch-public.js';
3
+ import { elementsIn, hasRel, parseHtml } from '../webmention/html.js';
4
+ import { itemsIn } from '../webmention/microformats.js';
5
+ /** How long a client is given to answer. */
6
+ export const CLIENT_FETCH_TIMEOUT_MS = 5000;
7
+ /** The most of a client's page or document that is read. */
8
+ export const CLIENT_FETCH_MAX_BYTES = 512 * 1024;
9
+ /** The most of a client's logo that is read. */
10
+ export const CLIENT_LOGO_MAX_BYTES = 64 * 1024;
11
+ /** The image types a logo may be, which a browser shows in an `<img>`. */
12
+ const LOGO_TYPE = /^\s*(image\/(?:png|jpeg|gif|webp|avif|svg\+xml|x-icon|vnd\.microsoft\.icon))\s*(?:;|$)/i;
13
+ /**
14
+ * Fetch a client_id URL and read what it says about the client.
15
+ *
16
+ * The URL is whatever the person's browser was sent here with, so the fetch
17
+ * goes through {@link fetchPublic}: public hosts only, every redirect hop
18
+ * checked, one timeout and a byte limit.
19
+ */
20
+ export async function fetchClientInformation(clientId, options) {
21
+ const fetched = await fetchPublic(clientId, {
22
+ lookup: options.lookup,
23
+ timeoutMs: options.timeoutMs ?? CLIENT_FETCH_TIMEOUT_MS,
24
+ maxBytes: options.maxBytes ?? CLIENT_FETCH_MAX_BYTES,
25
+ accept: 'application/json, text/html;q=0.9',
26
+ contentType: {
27
+ pattern: /^\s*(application\/(?:[\w.+-]+\+)?json|text\/html|application\/xhtml\+xml)/i,
28
+ name: 'JSON client metadata or an HTML page',
29
+ },
30
+ });
31
+ if (!fetched.ok)
32
+ return fetched;
33
+ const body = new TextDecoder().decode(fetched.body);
34
+ return {
35
+ ok: true,
36
+ client: readClientInformation(body, fetched.type, clientId, fetched.url, fetched.link),
37
+ };
38
+ }
39
+ /**
40
+ * Fetch a client's logo as a `data:` URI, or `undefined` when it cannot be had.
41
+ *
42
+ * Fetched here rather than linked from the page, so the admin's `img-src`
43
+ * never names the client and the client never learns the address of the
44
+ * person approving it, or when. The fetch is held to {@link fetchPublic}'s
45
+ * rules like the client_id itself, to image types only, and to
46
+ * {@link CLIENT_LOGO_MAX_BYTES}.
47
+ */
48
+ export async function fetchClientLogo(url, options) {
49
+ const fetched = await fetchPublic(url, {
50
+ lookup: options.lookup,
51
+ timeoutMs: options.timeoutMs ?? CLIENT_FETCH_TIMEOUT_MS,
52
+ maxBytes: options.maxBytes ?? CLIENT_LOGO_MAX_BYTES,
53
+ accept: 'image/*',
54
+ contentType: { pattern: LOGO_TYPE, name: 'an image' },
55
+ });
56
+ if (!fetched.ok)
57
+ return undefined;
58
+ const type = LOGO_TYPE.exec(fetched.type)?.[1]?.toLowerCase() ?? 'image/png';
59
+ return `data:${type};base64,${Buffer.from(fetched.body).toString('base64')}`;
60
+ }
61
+ /**
62
+ * What a client_id document says, by its content type.
63
+ *
64
+ * A JSON document is a client metadata document, which the OAuth Client ID
65
+ * Metadata Document draft and MCP use. Its `client_id` must be its own URL;
66
+ * one that names another client is read as saying nothing. Anything else is
67
+ * read as an HTML page with an IndieAuth `h-app` (or the older `h-x-app`) and
68
+ * `rel="redirect_uri"` links, in the response's `Link` header or in the page.
69
+ */
70
+ export function readClientInformation(body, type, clientId, base = clientId, link = null) {
71
+ return /json/i.test(type) ? fromJson(body, clientId) : fromHtml(body, base, link);
72
+ }
73
+ function fromJson(body, clientId) {
74
+ let document;
75
+ try {
76
+ document = JSON.parse(body);
77
+ }
78
+ catch {
79
+ return { redirectUris: [] };
80
+ }
81
+ if (typeof document !== 'object' || document === null)
82
+ return { redirectUris: [] };
83
+ const fields = document;
84
+ if (!sameUrl(fields['client_id'], clientId))
85
+ return { redirectUris: [] };
86
+ const name = fields['client_name'];
87
+ const url = fields['client_uri'];
88
+ const logo = logoUrl(fields['logo_uri']);
89
+ const redirectUris = Array.isArray(fields['redirect_uris'])
90
+ ? fields['redirect_uris'].filter((uri) => typeof uri === 'string')
91
+ : [];
92
+ return {
93
+ ...(typeof name === 'string' && name.trim() !== '' ? { name: name.trim() } : {}),
94
+ ...(typeof url === 'string' && URL.canParse(url) ? { url } : {}),
95
+ ...(logo === undefined ? {} : { logo }),
96
+ redirectUris,
97
+ };
98
+ }
99
+ function fromHtml(body, base, link) {
100
+ const root = parseHtml(body);
101
+ const app = firstApp(itemsIn(root, base));
102
+ const name = app?.properties['name']?.[0]?.text.trim();
103
+ const url = app?.properties['url']?.[0]?.text;
104
+ const logo = logoUrl(app?.properties['logo']?.[0]?.text);
105
+ const hrefs = linkTargets(link, 'redirect_uri');
106
+ for (const element of elementsIn(root)) {
107
+ if ((element.name === 'link' || element.name === 'a') && hasRel(element, 'redirect_uri')) {
108
+ hrefs.push(element.attributes['href'] ?? '');
109
+ }
110
+ }
111
+ const redirectUris = hrefs.flatMap((href) => URL.parse(href, base)?.href ?? []);
112
+ return {
113
+ ...(name === undefined || name === '' ? {} : { name }),
114
+ ...(url === undefined ? {} : { url }),
115
+ ...(logo === undefined ? {} : { logo }),
116
+ redirectUris,
117
+ };
118
+ }
119
+ /** `value` when it is an absolute http or https URL. */
120
+ function logoUrl(value) {
121
+ return typeof value === 'string' ? webUrl(value)?.href : undefined;
122
+ }
123
+ function firstApp(items) {
124
+ for (const item of items) {
125
+ if (item.types.includes('h-app') || item.types.includes('h-x-app'))
126
+ return item;
127
+ const nested = firstApp(item.children);
128
+ if (nested !== undefined)
129
+ return nested;
130
+ }
131
+ return undefined;
132
+ }
133
+ function sameUrl(value, clientId) {
134
+ if (typeof value !== 'string')
135
+ return false;
136
+ const url = URL.parse(value);
137
+ return url !== null && url.href === new URL(clientId).href;
138
+ }
139
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/indieauth/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Users > Connected apps (TASK-162): the apps that hold a token for whoever is
3
+ * signed in, and the button that cuts one off.
4
+ *
5
+ * Only ever your own connections, because a token acts as the person who
6
+ * approved it. Revoking deletes the connection from `data/indieauth-tokens.json`,
7
+ * so the access token and the refresh token stop working on the next request.
8
+ */
9
+ import type { Hono } from 'hono';
10
+ import type { AdminRender } from '../admin/documents.ts';
11
+ import type { GeekityEnv } from '../env.ts';
12
+ /** Where a Revoke button posts. */
13
+ export declare const REVOKE_CONNECTION_PATH = "/admin/users/apps/revoke";
14
+ /** The field a Revoke button submits: the connection's id. */
15
+ export declare const CONNECTION_FIELD = "connection";
16
+ /**
17
+ * Register the screen and its Revoke button. Mounted before the users
18
+ * screens, whose `/admin/users/:id` would otherwise answer `apps` with a 404.
19
+ */
20
+ export declare function mountConnectedApps(app: Hono<GeekityEnv>, options: {
21
+ render: AdminRender;
22
+ }): void;
23
+ //# sourceMappingURL=connected-apps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connected-apps.d.ts","sourceRoot":"","sources":["../../src/indieauth/connected-apps.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAEjC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAMzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAK5C,mCAAmC;AACnC,eAAO,MAAM,sBAAsB,6BAAkC,CAAC;AAEtE,8DAA8D;AAC9D,eAAO,MAAM,gBAAgB,eAAe,CAAC;AAE7C;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,GAAG,IAAI,CAgChG"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Users > Connected apps (TASK-162): the apps that hold a token for whoever is
3
+ * signed in, and the button that cuts one off.
4
+ *
5
+ * Only ever your own connections, because a token acts as the person who
6
+ * approved it. Revoking deletes the connection from `data/indieauth-tokens.json`,
7
+ * so the access token and the refresh token stop working on the next request.
8
+ */
9
+ import { flash } from '../admin/flash.js';
10
+ import { formatInTimezone } from '../admin/formatting.js';
11
+ import { readSiteSettings } from '../admin/settings.js';
12
+ import { ADMIN_TEMPLATES } from '../admin/templates.js';
13
+ import { CONNECTED_APPS_CHILD, CONNECTED_APPS_PATH } from '../admin/users.js';
14
+ import { SCOPE_LABELS } from './consent.js';
15
+ import { listTokens, revokeConnection } from './tokens.js';
16
+ /** Where a Revoke button posts. */
17
+ export const REVOKE_CONNECTION_PATH = `${CONNECTED_APPS_PATH}/revoke`;
18
+ /** The field a Revoke button submits: the connection's id. */
19
+ export const CONNECTION_FIELD = 'connection';
20
+ /**
21
+ * Register the screen and its Revoke button. Mounted before the users
22
+ * screens, whose `/admin/users/:id` would otherwise answer `apps` with a 404.
23
+ */
24
+ export function mountConnectedApps(app, options) {
25
+ const { render } = options;
26
+ app.get(CONNECTED_APPS_PATH, (c) => {
27
+ const { config } = c.var;
28
+ const userId = c.var.session?.userId;
29
+ const timezone = readSiteSettings(config.contentDir).timezone;
30
+ const now = config.now().getTime();
31
+ const connections = listTokens(config.dataDir)
32
+ .filter((token) => token.userId === userId && Date.parse(token.refreshExpiresAt) > now)
33
+ .sort((a, b) => b.issuedAt.localeCompare(a.issuedAt))
34
+ .map((token) => row(token, timezone));
35
+ return render(c, ADMIN_TEMPLATES.connectedApps, {
36
+ section: 'users',
37
+ child: CONNECTED_APPS_CHILD,
38
+ revokeUrl: REVOKE_CONNECTION_PATH,
39
+ connectionField: CONNECTION_FIELD,
40
+ connections,
41
+ });
42
+ });
43
+ app.post(REVOKE_CONNECTION_PATH, async (c) => {
44
+ const { config, session } = c.var;
45
+ const id = (await c.req.parseBody())[CONNECTION_FIELD];
46
+ const revoked = typeof id === 'string' && session?.userId != null
47
+ ? await revokeConnection(config.dataDir, session.userId, id, config.now())
48
+ : undefined;
49
+ if (revoked === undefined)
50
+ flash(c, 'warning', 'That app was already disconnected.');
51
+ else
52
+ flash(c, 'notice', `${clientLabel(revoked)} can no longer act as you.`);
53
+ return c.redirect(CONNECTED_APPS_PATH, 303);
54
+ });
55
+ }
56
+ /** One connection as the table shows it. */
57
+ function row(token, timezone) {
58
+ const when = (instant) => ({ iso: instant, text: formatInTimezone(instant, timezone) });
59
+ return {
60
+ id: token.id,
61
+ name: clientLabel(token),
62
+ url: token.clientId,
63
+ scopes: token.scopes.map((scope) => SCOPE_LABELS[scope]),
64
+ issued: when(token.issuedAt),
65
+ lastUsed: token.lastUsedAt === undefined ? undefined : when(token.lastUsedAt),
66
+ expires: when(token.refreshExpiresAt),
67
+ };
68
+ }
69
+ /** What to call a client: its own name, or its URL when it gave none. */
70
+ function clientLabel(token) {
71
+ return token.clientName ?? token.clientId;
72
+ }
73
+ //# sourceMappingURL=connected-apps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connected-apps.js","sourceRoot":"","sources":["../../src/indieauth/connected-apps.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { Hono } from 'hono';
2
+ import type { AdminRender } from '../admin/documents.ts';
3
+ import type { GeekityEnv } from '../env.ts';
4
+ import type { Scope } from './request.ts';
5
+ /**
6
+ * Where the person approves or denies a sign-in. Under `/admin`, so the admin
7
+ * guard signs them in first and checks the form's CSRF token.
8
+ */
9
+ export declare const CONSENT_PATH = "/admin/indieauth/consent";
10
+ /** What each scope lets a client learn or do, as the consent and connected apps screens say it. */
11
+ export declare const SCOPE_LABELS: Readonly<Record<Scope, string>>;
12
+ /**
13
+ * The authorization endpoint (TASK-158, TASK-159).
14
+ *
15
+ * A GET is the person arriving from a client, and is handed, query and all,
16
+ * to the consent screen. The screen is under `/admin` so that the admin's
17
+ * guard, login and headers apply to it; the endpoint is not, because a POST
18
+ * here is a client redeeming its code for the profile, with no admin session.
19
+ * No access token is issued on this path; that is the token endpoint's job.
20
+ * Not exempt from maintenance mode: the identity URLs that advertise it
21
+ * answer 503 then too.
22
+ */
23
+ export declare function mountAuthorizationEndpoint(app: Hono<GeekityEnv>): void;
24
+ /** Register the consent screen and the Approve and Deny buttons behind it. */
25
+ export declare function mountConsentScreen(app: Hono<GeekityEnv>, options: {
26
+ render: AdminRender;
27
+ }): void;
28
+ //# sourceMappingURL=consent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consent.d.ts","sourceRoot":"","sources":["../../src/indieauth/consent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,MAAM,CAAC;AAI1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAQ5C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAE1C;;;GAGG;AACH,eAAO,MAAM,YAAY,6BAAsC,CAAC;AAEhE,mGAAmG;AACnG,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAOxD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,GAAG,IAAI,CAwBtE;AAED,8EAA8E;AAC9E,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,GAAG,IAAI,CA6GhG"}
@@ -0,0 +1,185 @@
1
+ import { findUserById, listUsers } from '../admin/accounts.js';
2
+ import { ADMIN_PREFIX } from '../admin/session.js';
3
+ import { effectiveBaseUrl, readSiteSettings } from '../admin/settings.js';
4
+ import { ADMIN_TEMPLATES } from '../admin/templates.js';
5
+ import { isPrivateHost } from '../webmention/public-address.js';
6
+ import { fetchClientInformation, fetchClientLogo } from './client.js';
7
+ import { AUTHORIZATION_PATH, authorizationServerMetadata } from './discovery.js';
8
+ import { meForSignIn } from './identity.js';
9
+ import { profileResponse, redeemCode, redemptionForm } from './redeem.js';
10
+ import { parseAuthorizationRequest } from './request.js';
11
+ /**
12
+ * Where the person approves or denies a sign-in. Under `/admin`, so the admin
13
+ * guard signs them in first and checks the form's CSRF token.
14
+ */
15
+ export const CONSENT_PATH = `${ADMIN_PREFIX}/indieauth/consent`;
16
+ /** What each scope lets a client learn or do, as the consent and connected apps screens say it. */
17
+ export const SCOPE_LABELS = {
18
+ profile: 'Your name, URL and photo',
19
+ email: 'Your email address',
20
+ create: 'Create posts as you',
21
+ update: 'Edit your posts',
22
+ delete: 'Delete your posts',
23
+ media: 'Upload media to your site',
24
+ };
25
+ /**
26
+ * The authorization endpoint (TASK-158, TASK-159).
27
+ *
28
+ * A GET is the person arriving from a client, and is handed, query and all,
29
+ * to the consent screen. The screen is under `/admin` so that the admin's
30
+ * guard, login and headers apply to it; the endpoint is not, because a POST
31
+ * here is a client redeeming its code for the profile, with no admin session.
32
+ * No access token is issued on this path; that is the token endpoint's job.
33
+ * Not exempt from maintenance mode: the identity URLs that advertise it
34
+ * answer 503 then too.
35
+ */
36
+ export function mountAuthorizationEndpoint(app) {
37
+ app.get(AUTHORIZATION_PATH, (c) => c.redirect(`${CONSENT_PATH}${new URL(c.req.url).search}`, 302));
38
+ app.post(AUTHORIZATION_PATH, async (c) => {
39
+ c.header('cache-control', 'no-store');
40
+ const { config } = c.var;
41
+ const form = redemptionForm(await c.req.parseBody());
42
+ const redeemed = redeemCode(c.var.indieauth.codes, form);
43
+ if (!redeemed.ok) {
44
+ return c.json({ error: redeemed.error, error_description: redeemed.description }, 400);
45
+ }
46
+ const { grant } = redeemed;
47
+ const user = findUserById(config.dataDir, grant.userId);
48
+ if (user === undefined) {
49
+ return c.json({ error: 'invalid_grant', error_description: 'The person who approved this is gone.' }, 400);
50
+ }
51
+ const baseUrl = effectiveBaseUrl(config, readSiteSettings(config.contentDir));
52
+ return c.json(profileResponse(grant, user, baseUrl));
53
+ });
54
+ }
55
+ /** Register the consent screen and the Approve and Deny buttons behind it. */
56
+ export function mountConsentScreen(app, options) {
57
+ const { render } = options;
58
+ function refuse(c, message) {
59
+ c.status(400);
60
+ return render(c, ADMIN_TEMPLATES.indieauthRefused, { message });
61
+ }
62
+ app.get(CONSENT_PATH, async (c) => {
63
+ const { config } = c.var;
64
+ const user = signedInUser(c);
65
+ if (user === undefined)
66
+ return c.redirect(ADMIN_PREFIX, 302);
67
+ const settings = readSiteSettings(config.contentDir);
68
+ const baseUrl = effectiveBaseUrl(config, settings);
69
+ const parsed = parseAuthorizationRequest(new URL(c.req.url).searchParams, baseUrl);
70
+ if (parsed.kind === 'unredirectable')
71
+ return refuse(c, parsed.message);
72
+ const { clientId, redirectUri } = parsed.kind === 'valid' ? parsed.request : parsed;
73
+ const client = await clientInformation(c, clientId);
74
+ if (!mayRedirect(clientId, redirectUri, client)) {
75
+ return refuse(c, 'The app asked to send you back to an address it has not published as its own.');
76
+ }
77
+ if (parsed.kind === 'refused') {
78
+ return c.redirect(answer(redirectUri, baseUrl, {
79
+ error: parsed.error,
80
+ error_description: parsed.description,
81
+ state: parsed.state,
82
+ }), 302);
83
+ }
84
+ const { request } = parsed;
85
+ const me = meForSignIn(request.me, user, {
86
+ baseUrl,
87
+ users: listUsers(config.dataDir),
88
+ settings,
89
+ });
90
+ const pending = c.var.indieauth.consents.put({
91
+ request,
92
+ userId: user.id,
93
+ me,
94
+ ...(client.name === undefined ? {} : { clientName: client.name }),
95
+ });
96
+ const redirect = new URL(redirectUri);
97
+ const logo = client.logo === undefined
98
+ ? undefined
99
+ : await fetchClientLogo(client.logo, { lookup: config.hostLookup });
100
+ c.set('cspFormAction', formActionSource(redirect));
101
+ return render(c, ADMIN_TEMPLATES.indieauthConsent, {
102
+ consentUrl: CONSENT_PATH,
103
+ pending,
104
+ client: {
105
+ name: client.name ?? clientId,
106
+ id: clientId,
107
+ named: client.name !== undefined,
108
+ logo,
109
+ },
110
+ redirectHost: redirect.host === '' ? redirect.protocol : redirect.host,
111
+ loopback: redirect.hostname !== '' && isPrivateHost(redirect.hostname),
112
+ identity: me,
113
+ resource: request.resource,
114
+ scopes: request.scopes.map((scope) => ({ name: scope, label: SCOPE_LABELS[scope] })),
115
+ });
116
+ });
117
+ app.post(CONSENT_PATH, async (c) => {
118
+ const { config } = c.var;
119
+ const user = signedInUser(c);
120
+ if (user === undefined)
121
+ return c.redirect(ADMIN_PREFIX, 302);
122
+ const body = await c.req.parseBody({ all: true });
123
+ const pending = c.var.indieauth.consents.take(text(body['request']));
124
+ if (pending?.userId !== user.id) {
125
+ return refuse(c, 'This sign-in was already answered or has expired. Start again from the app.');
126
+ }
127
+ const { request } = pending;
128
+ const baseUrl = effectiveBaseUrl(config, readSiteSettings(config.contentDir));
129
+ if (text(body['decision']) !== 'approve') {
130
+ return c.redirect(answer(request.redirectUri, baseUrl, { error: 'access_denied', state: request.state }), 303);
131
+ }
132
+ const ticked = new Set([body['scope']].flat().map(text));
133
+ const code = c.var.indieauth.codes.put({
134
+ clientId: request.clientId,
135
+ ...(pending.clientName === undefined ? {} : { clientName: pending.clientName }),
136
+ redirectUri: request.redirectUri,
137
+ codeChallenge: request.codeChallenge,
138
+ userId: user.id,
139
+ me: pending.me,
140
+ scopes: request.scopes.filter((scope) => ticked.has(scope)),
141
+ ...(request.resource === undefined ? {} : { resource: request.resource }),
142
+ });
143
+ return c.redirect(answer(request.redirectUri, baseUrl, { code, state: request.state }), 303);
144
+ });
145
+ }
146
+ function signedInUser(c) {
147
+ const userId = c.var.session?.userId;
148
+ return userId == null ? undefined : findUserById(c.var.config.dataDir, userId);
149
+ }
150
+ /** What the client says about itself, or nothing when it could not be read. */
151
+ async function clientInformation(c, clientId) {
152
+ const fetched = await fetchClientInformation(clientId, { lookup: c.var.config.hostLookup });
153
+ if (fetched.ok)
154
+ return fetched.client;
155
+ console.warn(`Could not read IndieAuth client ${clientId}: ${fetched.reason}`);
156
+ return { redirectUris: [] };
157
+ }
158
+ /**
159
+ * Whether a client may be sent back to `redirectUri`: always on its own
160
+ * origin, and elsewhere only when its metadata lists that exact URL.
161
+ */
162
+ function mayRedirect(clientId, redirectUri, client) {
163
+ const target = new URL(redirectUri);
164
+ const sameOrigin = (target.protocol === 'https:' || target.protocol === 'http:') &&
165
+ target.origin === new URL(clientId).origin;
166
+ return sameOrigin || client.redirectUris.includes(redirectUri);
167
+ }
168
+ /** `redirectUri` with the answer and the issuer added to its query. */
169
+ function answer(redirectUri, baseUrl, fields) {
170
+ const url = new URL(redirectUri);
171
+ for (const [name, value] of Object.entries(fields)) {
172
+ if (value !== undefined)
173
+ url.searchParams.set(name, value);
174
+ }
175
+ url.searchParams.set('iss', authorizationServerMetadata(baseUrl).issuer);
176
+ return url.href;
177
+ }
178
+ /** The CSP source that lets a form's redirect reach `url`. */
179
+ function formActionSource(url) {
180
+ return url.protocol === 'https:' || url.protocol === 'http:' ? url.origin : url.protocol;
181
+ }
182
+ function text(value) {
183
+ return typeof value === 'string' ? value : '';
184
+ }
185
+ //# sourceMappingURL=consent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consent.js","sourceRoot":"","sources":["../../src/indieauth/consent.ts"],"names":[],"mappings":"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"}