From 53049fe7578db2b3efc7c3cca65d11805cb59be5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Fri, 19 Sep 2025 21:15:04 +0200 Subject: [PATCH] pl-api: some mastodon updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- packages/pl-api/lib/client.ts | 19 +++++++++++++ packages/pl-api/lib/entities/account.ts | 6 +++- packages/pl-api/lib/entities/application.ts | 2 +- .../entities/authorization-server-metadata.ts | 28 +++++++++++++++++++ packages/pl-api/lib/entities/index.ts | 2 ++ packages/pl-api/lib/entities/instance.ts | 4 ++- packages/pl-api/lib/entities/user-info.ts | 21 ++++++++++++++ 7 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 packages/pl-api/lib/entities/authorization-server-metadata.ts create mode 100644 packages/pl-api/lib/entities/user-info.ts diff --git a/packages/pl-api/lib/client.ts b/packages/pl-api/lib/client.ts index 18982a5df..5ac520841 100644 --- a/packages/pl-api/lib/client.ts +++ b/packages/pl-api/lib/client.ts @@ -25,6 +25,7 @@ import { announcementSchema, antennaSchema, applicationSchema, + authorizationServerMetadataSchema, backupSchema, bookmarkFolderSchema, chatMessageSchema, @@ -89,6 +90,7 @@ import { tokenSchema, translationSchema, trendsLinkSchema, + userInfoSchema, webPushSubscriptionSchema, } from './entities'; import { coerceObject, filteredArray } from './entities/utils'; @@ -601,6 +603,23 @@ class PlApiClient { return response.json as {}; }, + /** + * Retrieve user information + * Retrieves standardised OIDC claims about the currently authenticated user. + * see {@link https://docs.joinmastodon.org/methods/oauth/#userinfo} + */ + userinfo: async () => { + const response = await this.request('/oauth/userinfo'); + + return v.parse(userInfoSchema, response.json); + }, + + authorizationServerMetadata: async () => { + const response = await this.request('/.well-known/oauth-authorization-server'); + + return v.parse(authorizationServerMetadataSchema, response.json); + }, + /** * Get a new captcha * @see {@link https://docs.pleroma.social/backend/development/API/pleroma_api/#apiv1pleromacaptcha} diff --git a/packages/pl-api/lib/entities/account.ts b/packages/pl-api/lib/entities/account.ts index b745e6971..ddb443c5c 100644 --- a/packages/pl-api/lib/entities/account.ts +++ b/packages/pl-api/lib/entities/account.ts @@ -122,7 +122,9 @@ const baseAccountSchema = v.object({ bot: v.fallback(v.boolean(), false), group: v.fallback(v.boolean(), false), discoverable: v.fallback(v.boolean(), false), + indexable: v.fallback(v.nullable(v.boolean()), null), noindex: v.fallback(v.nullable(v.boolean()), null), + memorial: v.fallback(v.nullable(v.boolean()), null), suspended: v.fallback(v.optional(v.boolean()), undefined), limited: v.fallback(v.optional(v.boolean()), undefined), created_at: v.fallback(datetimeSchema, new Date().toISOString()), @@ -237,10 +239,12 @@ const untypedCredentialAccountSchema = v.pipe(v.any(), preprocessAccount, v.obje sensitive: v.fallback(v.optional(v.boolean()), false), language: v.fallback(v.optional(v.nullable(v.string())), null), follow_requests_count: v.fallback(v.optional(v.pipe(v.number(), v.integer(), v.minValue(0))), 0), + hide_collections: v.fallback(v.nullable(v.boolean()), null), + discoverable: v.fallback(v.nullable(v.boolean()), null), + indexable: v.fallback(v.nullable(v.boolean()), null), show_role: v.fallback(v.optional(v.nullable(v.boolean())), undefined), no_rich_text: v.fallback(v.optional(v.nullable(v.boolean())), undefined), - discoverable: v.fallback(v.optional(v.boolean()), undefined), actor_type: v.fallback(v.optional(v.string()), undefined), show_birthday: v.fallback(v.optional(v.boolean()), undefined), diff --git a/packages/pl-api/lib/entities/application.ts b/packages/pl-api/lib/entities/application.ts index 21e8c0059..e9e6e5038 100644 --- a/packages/pl-api/lib/entities/application.ts +++ b/packages/pl-api/lib/entities/application.ts @@ -35,7 +35,7 @@ const credentialApplicationSchema = v.pipe( ...applicationSchema.pipe[2].entries, client_id: v.string(), client_secret: v.string(), - client_secret_expires_at: v.fallback(v.optional(v.string()), undefined), + client_secret_expires_at: v.pipe(v.unknown(), v.transform(Number)), }), ); diff --git a/packages/pl-api/lib/entities/authorization-server-metadata.ts b/packages/pl-api/lib/entities/authorization-server-metadata.ts new file mode 100644 index 000000000..7371494af --- /dev/null +++ b/packages/pl-api/lib/entities/authorization-server-metadata.ts @@ -0,0 +1,28 @@ +import * as v from 'valibot'; + +/** + * @category Schemas + * @see {@link https://docs.joinmastodon.org/methods/oauth/#response-4} + */ +const authorizationServerMetadataSchema = v.object({ + issuer: v.string(), + service_documentation: v.string(), + authorization_endpoint: v.string(), + token_endpoint: v.string(), + app_registration_endpoint: v.string(), + revocation_endpoint: v.string(), + userinfo_endpoint: v.string(), + scopes_supported: v.array(v.string()), + response_types_supported: v.array(v.string()), + response_modes_supported: v.array(v.string()), + code_challenge_methods_supported: v.array(v.string()), + grant_types_supported: v.array(v.string()), + token_endpoint_auth_methods_supported: v.array(v.string()), +}); + +/** + * @category Entity types + */ +type AuthorizationServerMetadata = v.InferOutput; + +export { authorizationServerMetadataSchema, type AuthorizationServerMetadata }; diff --git a/packages/pl-api/lib/entities/index.ts b/packages/pl-api/lib/entities/index.ts index fa905a3b5..a2c84797c 100644 --- a/packages/pl-api/lib/entities/index.ts +++ b/packages/pl-api/lib/entities/index.ts @@ -23,6 +23,7 @@ export * from './announcement'; export * from './announcement-reaction'; export * from './antenna'; export * from './application'; +export * from './authorization-server-metadata'; export * from './backup'; export * from './bookmark-folder'; export * from './chat'; @@ -90,4 +91,5 @@ export * from './terms-of-service'; export * from './token'; export * from './translation'; export * from './trends-link'; +export * from './user-info'; export * from './web-push-subscription'; diff --git a/packages/pl-api/lib/entities/instance.ts b/packages/pl-api/lib/entities/instance.ts index 26e35452a..bd48236cf 100644 --- a/packages/pl-api/lib/entities/instance.ts +++ b/packages/pl-api/lib/entities/instance.ts @@ -163,6 +163,7 @@ const configurationSchema = coerceObject({ }), urls: coerceObject({ streaming: v.fallback(v.optional(v.pipe(v.string(), v.url())), undefined), + status: v.fallback(v.optional(v.pipe(v.string(), v.url())), undefined), about: v.fallback(v.nullable(v.string()), null), privacy_policy: v.fallback(v.nullable(v.string()), null), terms_of_service: v.fallback(v.nullable(v.string()), null), @@ -263,6 +264,7 @@ const registrations = coerceObject({ message: v.fallback(v.optional(v.string()), undefined), min_age: v.fallback(v.nullable(v.number()), null), reason_required: v.fallback(v.nullable(v.boolean()), null), + url: v.fallback(v.nullable(v.pipe(v.string(), v.url())), null), }); const statsSchema = coerceObject({ @@ -355,7 +357,7 @@ const instanceSchema = v.pipe( })), languages: v.fallback(v.array(v.string()), []), pleroma: pleromaSchema, - registrations: registrations, + registrations, rules: filteredArray(ruleSchema), stats: statsSchema, thumbnail: thumbnailSchema, diff --git a/packages/pl-api/lib/entities/user-info.ts b/packages/pl-api/lib/entities/user-info.ts new file mode 100644 index 000000000..8ec37e868 --- /dev/null +++ b/packages/pl-api/lib/entities/user-info.ts @@ -0,0 +1,21 @@ +import * as v from 'valibot'; + +/** + * @category Schemas + * @see {@link https://docs.joinmastodon.org/methods/oauth/#response-3} + */ +const userInfoSchema = v.object({ + iss: v.string(), + sub: v.string(), + name: v.string(), + preferred_username: v.string(), + profile: v.string(), + picture: v.fallback(v.string(), ''), +}); + +/** + * @category Entity types + */ +type UserInfo = v.InferOutput; + +export { userInfoSchema, type UserInfo };