diff --git a/packages/backend/migration/1707697398681-indie-auth-client.js b/packages/backend/migration/1707697398681-indie-auth-client.js
new file mode 100644
index 0000000000..8211b668f7
--- /dev/null
+++ b/packages/backend/migration/1707697398681-indie-auth-client.js
@@ -0,0 +1,18 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class IndieAuthClient1707697398681 {
+    name = 'IndieAuthClient1707697398681'
+
+    async up(queryRunner) {
+        await queryRunner.query(`CREATE TABLE "indie_auth_client" ("id" character varying(512) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "name" character varying(256), "redirectUris" character varying(512) array NOT NULL DEFAULT '{}', CONSTRAINT "PK_9a604c83d4dadfa1eb92ee03399" PRIMARY KEY ("id"))`);
+        await queryRunner.query(`CREATE INDEX "IDX_434fcbfbe82b58a90898e557b7" ON "indie_auth_client" ("createdAt") `);
+    }
+
+    async down(queryRunner) {
+        await queryRunner.query(`DROP INDEX "public"."IDX_434fcbfbe82b58a90898e557b7"`);
+        await queryRunner.query(`DROP TABLE "indie_auth_client"`);
+    }
+}
diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts
index 5be838511d..e20b428ab4 100644
--- a/packages/backend/src/di-symbols.ts
+++ b/packages/backend/src/di-symbols.ts
@@ -49,6 +49,7 @@ export const DI = {
 	blockingsRepository: Symbol('blockingsRepository'),
 	swSubscriptionsRepository: Symbol('swSubscriptionsRepository'),
 	hashtagsRepository: Symbol('hashtagsRepository'),
+	indieAuthClientsRepository: Symbol('indieAuthClientsRepository'),
 	abuseUserReportsRepository: Symbol('abuseUserReportsRepository'),
 	registrationTicketsRepository: Symbol('registrationTicketsRepository'),
 	authSessionsRepository: Symbol('authSessionsRepository'),
diff --git a/packages/backend/src/models/IndieAuthClient.ts b/packages/backend/src/models/IndieAuthClient.ts
new file mode 100644
index 0000000000..8498d7f3a6
--- /dev/null
+++ b/packages/backend/src/models/IndieAuthClient.ts
@@ -0,0 +1,30 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { PrimaryColumn, Entity, Column, Index } from 'typeorm';
+
+@Entity('indie_auth_client')
+export class MiIndieAuthClient {
+	@PrimaryColumn('varchar', {
+		length: 512,
+	})
+	public id: string;
+
+	@Index()
+	@Column('timestamp with time zone', {
+		default: () => 'CURRENT_TIMESTAMP',
+	})
+	public createdAt: Date;
+
+	@Column('varchar', {
+		length: 256, nullable: true,
+	})
+	public name: string | null;
+
+	@Column('varchar', {
+		array: true, length: 512, default: '{}',
+	})
+	public redirectUris: string[];
+}
diff --git a/packages/backend/src/models/RepositoryModule.ts b/packages/backend/src/models/RepositoryModule.ts
index 45b9643be8..797d732d0b 100644
--- a/packages/backend/src/models/RepositoryModule.ts
+++ b/packages/backend/src/models/RepositoryModule.ts
@@ -5,7 +5,77 @@
 
 import { Module } from '@nestjs/common';
 import { DI } from '@/di-symbols.js';
-import { MiAbuseReportResolver, MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiAvatarDecoration, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListMembership, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook, MiBubbleGameRecord, MiReversiGame } from './_.js';
+import {
+	MiAbuseReportResolver,
+	MiAbuseUserReport,
+	MiAccessToken,
+	MiAd,
+	MiAnnouncement,
+	MiAnnouncementRead,
+	MiAntenna,
+	MiApp,
+	MiAuthSession,
+	MiAvatarDecoration,
+	MiBlocking,
+	MiChannel,
+	MiChannelFavorite,
+	MiChannelFollowing,
+	MiClip,
+	MiClipFavorite,
+	MiClipNote,
+	MiDriveFile,
+	MiDriveFolder,
+	MiEmoji,
+	MiFlash,
+	MiFlashLike,
+	MiFollowRequest,
+	MiFollowing,
+	MiGalleryLike,
+	MiGalleryPost,
+	MiHashtag,
+	MiIndieAuthClient,
+	MiInstance,
+	MiMeta,
+	MiModerationLog,
+	MiMuting,
+	MiNote,
+	MiNoteFavorite,
+	MiNoteReaction,
+	MiNoteThreadMuting,
+	MiNoteUnread,
+	MiPage,
+	MiPageLike,
+	MiPasswordResetRequest,
+	MiPoll,
+	MiPollVote,
+	MiPromoNote,
+	MiPromoRead,
+	MiRegistrationTicket,
+	MiRegistryItem,
+	MiRelay,
+	MiRenoteMuting,
+	MiRetentionAggregation,
+	MiRole,
+	MiRoleAssignment,
+	MiSignin,
+	MiSwSubscription,
+	MiUsedUsername,
+	MiUser,
+	MiUserIp,
+	MiUserKeypair,
+	MiUserList,
+	MiUserListFavorite,
+	MiUserListMembership,
+	MiUserMemo,
+	MiUserNotePining,
+	MiUserPending,
+	MiUserProfile,
+	MiUserPublickey,
+	MiUserSecurityKey,
+	MiWebhook,
+	MiBubbleGameRecord,
+	MiReversiGame,
+} from './_.js';
 import type { DataSource } from 'typeorm';
 import type { Provider } from '@nestjs/common';
 
@@ -219,6 +289,12 @@ const $hashtagsRepository: Provider = {
 	inject: [DI.db],
 };
 
+const $indieAuthClientsRepository: Provider = {
+	provide: DI.indieAuthClientsRepository,
+	useFactory: (db: DataSource) => db.getRepository(MiIndieAuthClient),
+	inject: [DI.db],
+};
+
 const $abuseUserReportsRepository: Provider = {
 	provide: DI.abuseUserReportsRepository,
 	useFactory: (db: DataSource) => db.getRepository(MiAbuseUserReport),
@@ -456,6 +532,7 @@ const $abuseReportResolversRepository: Provider = {
 		$blockingsRepository,
 		$swSubscriptionsRepository,
 		$hashtagsRepository,
+		$indieAuthClientsRepository,
 		$abuseUserReportsRepository,
 		$registrationTicketsRepository,
 		$authSessionsRepository,
@@ -526,6 +603,7 @@ const $abuseReportResolversRepository: Provider = {
 		$blockingsRepository,
 		$swSubscriptionsRepository,
 		$hashtagsRepository,
+		$indieAuthClientsRepository,
 		$abuseUserReportsRepository,
 		$registrationTicketsRepository,
 		$authSessionsRepository,
diff --git a/packages/backend/src/models/_.ts b/packages/backend/src/models/_.ts
index ec846defcf..309ec44a52 100644
--- a/packages/backend/src/models/_.ts
+++ b/packages/backend/src/models/_.ts
@@ -27,6 +27,7 @@ import { MiFollowRequest } from '@/models/FollowRequest.js';
 import { MiGalleryLike } from '@/models/GalleryLike.js';
 import { MiGalleryPost } from '@/models/GalleryPost.js';
 import { MiHashtag } from '@/models/Hashtag.js';
+import { MiIndieAuthClient } from '@/models/IndieAuthClient.js';
 import { MiInstance } from '@/models/Instance.js';
 import { MiMeta } from '@/models/Meta.js';
 import { MiModerationLog } from '@/models/ModerationLog.js';
@@ -98,6 +99,7 @@ export {
 	MiGalleryLike,
 	MiGalleryPost,
 	MiHashtag,
+	MiIndieAuthClient,
 	MiInstance,
 	MiMeta,
 	MiModerationLog,
@@ -168,6 +170,7 @@ export type FollowRequestsRepository = Repository<MiFollowRequest>;
 export type GalleryLikesRepository = Repository<MiGalleryLike>;
 export type GalleryPostsRepository = Repository<MiGalleryPost>;
 export type HashtagsRepository = Repository<MiHashtag>;
+export type IndieAuthClientsRepository = Repository<MiIndieAuthClient>;
 export type InstancesRepository = Repository<MiInstance>;
 export type MetasRepository = Repository<MiMeta>;
 export type ModerationLogsRepository = Repository<MiModerationLog>;
diff --git a/packages/backend/src/postgres.ts b/packages/backend/src/postgres.ts
index fb0b137c21..454b3d4b2a 100644
--- a/packages/backend/src/postgres.ts
+++ b/packages/backend/src/postgres.ts
@@ -37,6 +37,7 @@ import { MiFollowRequest } from '@/models/FollowRequest.js';
 import { MiGalleryLike } from '@/models/GalleryLike.js';
 import { MiGalleryPost } from '@/models/GalleryPost.js';
 import { MiHashtag } from '@/models/Hashtag.js';
+import { MiIndieAuthClient } from '@/models/IndieAuthClient.js';
 import { MiInstance } from '@/models/Instance.js';
 import { MiMeta } from '@/models/Meta.js';
 import { MiModerationLog } from '@/models/ModerationLog.js';
@@ -172,6 +173,7 @@ export const entities = [
 	MiPollVote,
 	MiEmoji,
 	MiHashtag,
+	MiIndieAuthClient,
 	MiSwSubscription,
 	MiAbuseUserReport,
 	MiRegistrationTicket,
diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts
index 1951893a77..fdfbc55ea0 100644
--- a/packages/backend/src/server/api/EndpointsModule.ts
+++ b/packages/backend/src/server/api/EndpointsModule.ts
@@ -51,6 +51,10 @@ import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federat
 import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js';
 import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js';
 import * as ep___admin_federation_updateInstance from './endpoints/admin/federation/update-instance.js';
+import * as ep___admin_indieAuth_create from './endpoints/admin/indie-auth/create.js';
+import * as ep___admin_indieAuth_delete from './endpoints/admin/indie-auth/delete.js';
+import * as ep___admin_indieAuth_list from './endpoints/admin/indie-auth/list.js';
+import * as ep___admin_indieAuth_update from './endpoints/admin/indie-auth/update.js';
 import * as ep___admin_getIndexStats from './endpoints/admin/get-index-stats.js';
 import * as ep___admin_getTableStats from './endpoints/admin/get-table-stats.js';
 import * as ep___admin_getUserIps from './endpoints/admin/get-user-ips.js';
@@ -428,6 +432,10 @@ const $admin_federation_deleteAllFiles: Provider = { provide: 'ep:admin/federati
 const $admin_federation_refreshRemoteInstanceMetadata: Provider = { provide: 'ep:admin/federation/refresh-remote-instance-metadata', useClass: ep___admin_federation_refreshRemoteInstanceMetadata.default };
 const $admin_federation_removeAllFollowing: Provider = { provide: 'ep:admin/federation/remove-all-following', useClass: ep___admin_federation_removeAllFollowing.default };
 const $admin_federation_updateInstance: Provider = { provide: 'ep:admin/federation/update-instance', useClass: ep___admin_federation_updateInstance.default };
+const $admin_indieAuth_create: Provider = { provide: 'ep:admin/indie-auth/create', useClass: ep___admin_indieAuth_create.default };
+const $admin_indieAuth_delete: Provider = { provide: 'ep:admin/indie-auth/delete', useClass: ep___admin_indieAuth_delete.default };
+const $admin_indieAuth_list: Provider = { provide: 'ep:admin/indie-auth/list', useClass: ep___admin_indieAuth_list.default };
+const $admin_indieAuth_update: Provider = { provide: 'ep:admin/indie-auth/update', useClass: ep___admin_indieAuth_update.default };
 const $admin_getIndexStats: Provider = { provide: 'ep:admin/get-index-stats', useClass: ep___admin_getIndexStats.default };
 const $admin_getTableStats: Provider = { provide: 'ep:admin/get-table-stats', useClass: ep___admin_getTableStats.default };
 const $admin_getUserIps: Provider = { provide: 'ep:admin/get-user-ips', useClass: ep___admin_getUserIps.default };
@@ -809,6 +817,10 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
 		$admin_federation_refreshRemoteInstanceMetadata,
 		$admin_federation_removeAllFollowing,
 		$admin_federation_updateInstance,
+		$admin_indieAuth_create,
+		$admin_indieAuth_delete,
+		$admin_indieAuth_list,
+		$admin_indieAuth_update,
 		$admin_getIndexStats,
 		$admin_getTableStats,
 		$admin_getUserIps,
@@ -1184,6 +1196,10 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
 		$admin_federation_refreshRemoteInstanceMetadata,
 		$admin_federation_removeAllFollowing,
 		$admin_federation_updateInstance,
+		$admin_indieAuth_create,
+		$admin_indieAuth_delete,
+		$admin_indieAuth_list,
+		$admin_indieAuth_update,
 		$admin_getIndexStats,
 		$admin_getTableStats,
 		$admin_getUserIps,
diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts
index 7418fb24cc..16a302739b 100644
--- a/packages/backend/src/server/api/endpoints.ts
+++ b/packages/backend/src/server/api/endpoints.ts
@@ -51,6 +51,10 @@ import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federat
 import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js';
 import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js';
 import * as ep___admin_federation_updateInstance from './endpoints/admin/federation/update-instance.js';
+import * as ep___admin_indieAuth_create from './endpoints/admin/indie-auth/create.js';
+import * as ep___admin_indieAuth_delete from './endpoints/admin/indie-auth/delete.js';
+import * as ep___admin_indieAuth_list from './endpoints/admin/indie-auth/list.js';
+import * as ep___admin_indieAuth_update from './endpoints/admin/indie-auth/update.js';
 import * as ep___admin_getIndexStats from './endpoints/admin/get-index-stats.js';
 import * as ep___admin_getTableStats from './endpoints/admin/get-table-stats.js';
 import * as ep___admin_getUserIps from './endpoints/admin/get-user-ips.js';
@@ -426,6 +430,10 @@ const eps = [
 	['admin/federation/refresh-remote-instance-metadata', ep___admin_federation_refreshRemoteInstanceMetadata],
 	['admin/federation/remove-all-following', ep___admin_federation_removeAllFollowing],
 	['admin/federation/update-instance', ep___admin_federation_updateInstance],
+	['admin/indie-auth/create', ep___admin_indieAuth_create],
+	['admin/indie-auth/delete', ep___admin_indieAuth_delete],
+	['admin/indie-auth/list', ep___admin_indieAuth_list],
+	['admin/indie-auth/update', ep___admin_indieAuth_update],
 	['admin/get-index-stats', ep___admin_getIndexStats],
 	['admin/get-table-stats', ep___admin_getTableStats],
 	['admin/get-user-ips', ep___admin_getUserIps],
diff --git a/packages/backend/src/server/api/endpoints/admin/indie-auth/create.ts b/packages/backend/src/server/api/endpoints/admin/indie-auth/create.ts
new file mode 100644
index 0000000000..a8859a0f19
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/admin/indie-auth/create.ts
@@ -0,0 +1,90 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import type { IndieAuthClientsRepository } from '@/models/_.js';
+import { DI } from '@/di-symbols.js';
+import { ModerationLogService } from '@/core/ModerationLogService.js';
+
+export const meta = {
+	tags: ['admin'],
+
+	requireCredential: true,
+	requireModerator: true,
+	kind: 'write:admin:indie-auth',
+
+	res: {
+		type: 'object',
+		optional: false, nullable: false,
+		properties: {
+			id: {
+				type: 'string',
+				optional: false, nullable: false,
+			},
+			createdAt: {
+				type: 'string',
+				optional: false, nullable: false,
+				format: 'date-time',
+			},
+			name: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
+			redirectUris: {
+				type: 'array',
+				optional: false, nullable: false,
+				items: {
+					type: 'string',
+					optional: false, nullable: false,
+				},
+			},
+		},
+	},
+} as const;
+
+export const paramDef = {
+	type: 'object',
+	properties: {
+		id: { type: 'string', minLength: 1 },
+		name: { type: 'string', nullable: true },
+		redirectUris: {
+			type: 'array', minItems: 1,
+			items: { type: 'string' },
+		},
+	},
+	required: ['id'],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+	constructor(
+		@Inject(DI.indieAuthClientsRepository)
+		private indieAuthClientsRepository: IndieAuthClientsRepository,
+
+		private moderationLogService: ModerationLogService,
+	) {
+		super(meta, paramDef, async (ps, me) => {
+			const indieAuthClient = await this.indieAuthClientsRepository.insert({
+				id: ps.id,
+				createdAt: new Date(),
+				name: ps.name,
+				redirectUris: ps.redirectUris,
+			}).then(r => this.indieAuthClientsRepository.findOneByOrFail({ id: r.identifiers[0].id }));
+
+			this.moderationLogService.log(me, 'createIndieAuthClient', {
+				clientId: indieAuthClient.id,
+				client: indieAuthClient,
+			});
+
+			return {
+				id: indieAuthClient.id,
+				createdAt: indieAuthClient.createdAt.toISOString(),
+				name: indieAuthClient.name,
+				redirectUris: indieAuthClient.redirectUris,
+			};
+		});
+	}
+}
diff --git a/packages/backend/src/server/api/endpoints/admin/indie-auth/delete.ts b/packages/backend/src/server/api/endpoints/admin/indie-auth/delete.ts
new file mode 100644
index 0000000000..0775916ac6
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/admin/indie-auth/delete.ts
@@ -0,0 +1,58 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import type { IndieAuthClientsRepository } from '@/models/_.js';
+import { DI } from '@/di-symbols.js';
+import { ModerationLogService } from '@/core/ModerationLogService.js';
+import { ApiError } from '../../../error.js';
+
+export const meta = {
+	tags: ['admin'],
+
+	requireCredential: true,
+	requireModerator: true,
+	kind: 'write:admin:indie-auth',
+
+	errors: {
+		noSuchIndieAuthClient: {
+			message: 'No such client',
+			code: 'NO_SUCH_CLIENT',
+			id: '02c4e690-af0c-4dc9-9f2f-c436c3b2782d',
+		},
+	},
+} as const;
+
+export const paramDef = {
+	type: 'object',
+	properties: {
+		id: { type: 'string' },
+	},
+	required: ['id'],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+	constructor(
+		@Inject(DI.indieAuthClientsRepository)
+		private indieAuthClientsRepository: IndieAuthClientsRepository,
+
+		private moderationLogService: ModerationLogService,
+	) {
+		super(meta, paramDef, async (ps, me) => {
+			const client = await this.indieAuthClientsRepository.findOneBy({ id: ps.id });
+
+			if (client == null) throw new ApiError(meta.errors.noSuchIndieAuthClient);
+
+			await this.indieAuthClientsRepository.delete(client.id);
+
+			this.moderationLogService.log(me, 'deleteIndieAuthClient', {
+				clientId: client.id,
+				client: client,
+			});
+		});
+	}
+}
diff --git a/packages/backend/src/server/api/endpoints/admin/indie-auth/list.ts b/packages/backend/src/server/api/endpoints/admin/indie-auth/list.ts
new file mode 100644
index 0000000000..69c2d2eb92
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/admin/indie-auth/list.ts
@@ -0,0 +1,75 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import type { IndieAuthClientsRepository } from '@/models/_.js';
+import { DI } from '@/di-symbols.js';
+
+export const meta = {
+	tags: ['admin'],
+
+	requireCredential: true,
+	requireModerator: true,
+	kind: 'read:admin:indie-auth',
+
+	res: {
+		type: 'array',
+		optional: false, nullable: false,
+		items: {
+			type: 'object',
+			optional: false, nullable: false,
+			properties: {
+				id: {
+					type: 'string',
+					optional: false, nullable: false,
+				},
+				createdAt: {
+					type: 'string',
+					optional: false, nullable: false,
+					format: 'date-time',
+				},
+				name: {
+					type: 'string',
+					optional: false, nullable: true,
+				},
+				redirectUris: {
+					type: 'array',
+					optional: false, nullable: false,
+					items: { type: 'string' },
+				},
+			},
+		},
+	},
+} as const;
+
+export const paramDef = {
+	type: 'object',
+	properties: {
+		limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
+		offset: { type: 'integer', default: 0 },
+	},
+	required: [],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+	constructor(
+		@Inject(DI.indieAuthClientsRepository)
+		private indieAuthClientsRepository: IndieAuthClientsRepository,
+	) {
+		super(meta, paramDef, async (ps, me) => {
+			const query = this.indieAuthClientsRepository.createQueryBuilder('client');
+			const clients = await query.offset(ps.offset).limit(ps.limit).getMany();
+
+			return clients.map(client => ({
+				id: client.id,
+				createdAt: client.createdAt.toISOString(),
+				name: client.name,
+				redirectUris: client.redirectUris,
+			}));
+		});
+	}
+}
diff --git a/packages/backend/src/server/api/endpoints/admin/indie-auth/update.ts b/packages/backend/src/server/api/endpoints/admin/indie-auth/update.ts
new file mode 100644
index 0000000000..a40c373360
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/admin/indie-auth/update.ts
@@ -0,0 +1,69 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import type { IndieAuthClientsRepository } from '@/models/_.js';
+import { DI } from '@/di-symbols.js';
+import { ModerationLogService } from '@/core/ModerationLogService.js';
+import { ApiError } from '../../../error.js';
+
+export const meta = {
+	tags: ['admin'],
+
+	requireCredential: true,
+	requireModerator: true,
+	kind: 'write:admin:indie-auth',
+
+	errors: {
+		noSuchIndieAuthClient: {
+			message: 'No such client',
+			code: 'NO_SUCH_CLIENT',
+			id: 'd4f9440a-45aa-495c-af66-b4d1e339d4fc',
+		},
+	},
+} as const;
+
+export const paramDef = {
+	type: 'object',
+	properties: {
+		id: { type: 'string', minLength: 1 },
+		name: { type: 'string', nullable: true },
+		redirectUris: {
+			type: 'array', minItems: 1,
+			items: { type: 'string' },
+		},
+	},
+	required: ['id'],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+	constructor(
+		@Inject(DI.indieAuthClientsRepository)
+		private indieAuthClientsRepository: IndieAuthClientsRepository,
+
+		private moderationLogService: ModerationLogService,
+	) {
+		super(meta, paramDef, async (ps, me) => {
+			const client = await this.indieAuthClientsRepository.findOneBy({ id: ps.id });
+
+			if (client == null) throw new ApiError(meta.errors.noSuchIndieAuthClient);
+
+			await this.indieAuthClientsRepository.update(client.id, {
+				name: ps.name,
+				redirectUris: ps.redirectUris,
+			});
+
+			const updatedClient = await this.indieAuthClientsRepository.findOneByOrFail({ id: client.id });
+
+			this.moderationLogService.log(me, 'updateIndieAuthClient', {
+				clientId: client.id,
+				before: client,
+				after: updatedClient,
+			});
+		});
+	}
+}
diff --git a/packages/backend/src/server/oauth/OAuth2ProviderService.ts b/packages/backend/src/server/oauth/OAuth2ProviderService.ts
index 939f00fae7..1860d30b39 100644
--- a/packages/backend/src/server/oauth/OAuth2ProviderService.ts
+++ b/packages/backend/src/server/oauth/OAuth2ProviderService.ts
@@ -32,7 +32,12 @@ import { HttpRequestService } from '@/core/HttpRequestService.js';
 import type { Config } from '@/config.js';
 import { DI } from '@/di-symbols.js';
 import { bindThis } from '@/decorators.js';
-import type { AccessTokensRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
+import type {
+	AccessTokensRepository,
+	IndieAuthClientsRepository,
+	UserProfilesRepository,
+	UsersRepository
+} from '@/models/_.js';
 import { IdService } from '@/core/IdService.js';
 import { CacheService } from '@/core/CacheService.js';
 import type { MiLocalUser } from '@/models/User.js';
@@ -100,8 +105,8 @@ function validateClientId(raw: string): URL {
 
 interface ClientInformation {
 	id: string;
-	redirectUris: string[];
 	name: string;
+	redirectUris: string[];
 }
 
 // https://indieauth.spec.indieweb.org/#client-information-discovery
@@ -246,6 +251,8 @@ export class OAuth2ProviderService {
 		private redisClient: Redis.Redis,
 		@Inject(DI.accessTokensRepository)
 		private accessTokensRepository: AccessTokensRepository,
+		@Inject(DI.indieAuthClientsRepository)
+		private indieAuthClientsRepository: IndieAuthClientsRepository,
 		@Inject(DI.usersRepository)
 		private usersRepository: UsersRepository,
 		@Inject(DI.userProfilesRepository)
@@ -423,8 +430,10 @@ export class OAuth2ProviderService {
 					}
 				}
 
+				// Find client information from the database.
+				const registeredClientInfo = await this.indieAuthClientsRepository.findOneBy({ id: clientUrl.href }) as ClientInformation | null;
 				// Find client information from the remote.
-				const clientInfo = await discoverClientInformation(this.#logger, this.httpRequestService, clientUrl.href);
+				const clientInfo = registeredClientInfo ?? await discoverClientInformation(this.#logger, this.httpRequestService, clientUrl.href);
 
 				// Require the redirect URI to be included in an explicit list, per
 				// https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.1.3
diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts
index cfac5cd9d4..f4897ec73c 100644
--- a/packages/backend/src/types.ts
+++ b/packages/backend/src/types.ts
@@ -76,6 +76,9 @@ export const moderationLogTypes = [
 	'createAd',
 	'updateAd',
 	'deleteAd',
+	'createIndieAuthClient',
+	'updateIndieAuthClient',
+	'deleteIndieAuthClient',
 	'createAvatarDecoration',
 	'updateAvatarDecoration',
 	'deleteAvatarDecoration',
@@ -242,6 +245,19 @@ export type ModerationLogPayloads = {
 		adId: string;
 		ad: any;
 	};
+	createIndieAuthClient: {
+		clientId: string;
+		client: any;
+	};
+	updateIndieAuthClient: {
+		clientId: string;
+		before: any;
+		after: any;
+	};
+	deleteIndieAuthClient: {
+		clientId: string;
+		client: any;
+	};
 	createAvatarDecoration: {
 		avatarDecorationId: string;
 		avatarDecoration: any;
diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue
index d5d1258733..d32a4e749f 100644
--- a/packages/frontend/src/pages/admin/security.vue
+++ b/packages/frontend/src/pages/admin/security.vue
@@ -131,6 +131,41 @@ SPDX-License-Identifier: AGPL-3.0-only
 						<MkButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
 					</div>
 				</MkFolder>
+
+				<MkFolder>
+					<template #label>IndieAuth Clients</template>
+
+					<div class="_gaps">
+						<MkButton primary full @click="indieAuthAddNew"><i class="ti ti-plus"></i> New</MkButton>
+						<MkFolder v-for="(client, index) in indieAuthClients" :key="`${index}-${client.createdAt}`" :defaultOpen="!client.createdAt">
+							<template #label>{{ client.name || client.id }}</template>
+							<template #icon>
+								<i v-if="client.id" class="ti ti-key"></i>
+								<i v-else class="ti ti-plus"></i>
+							</template>
+							<template v-if="client.name && client.id" #caption>{{ client.id }}</template>
+
+							<div class="_gaps_m">
+								<MkInput v-model="client.id" :disabled="!!client.createdAt">
+									<template #label>Client ID</template>
+								</MkInput>
+								<MkInput v-model="client.name">
+									<template #label>Name</template>
+								</MkInput>
+								<MkTextarea v-model="client.redirectUris">
+									<template #label>Redirect URIs</template>
+								</MkTextarea>
+								<div class="buttons _buttons">
+									<MkButton primary @click="indieAuthSave(client)"><i class="ti ti-device-floppy"></i> Save</MkButton>
+									<MkButton v-if="client.createdAt" warn @click="indieAuthDelete(client)"><i class="ti ti-trash"></i> Delete</MkButton>
+								</div>
+							</div>
+						</MkFolder>
+						<MkButton v-if="indieAuthHasMore" :class="$style.more" :disabled="!indieAuthHasMore" primary rounded @click="indieAuthFetch()">
+							<i class="ti ti-reload"></i>{{ i18n.ts.more }}
+						</MkButton>
+					</div>
+				</MkFolder>
 			</div>
 		</FormSuspense>
 	</MkSpacer>
@@ -155,7 +190,7 @@ import { fetchInstance } from '@/instance.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
-const summalyProxy = ref<string>('');
+const summalyProxy = ref<string | null>('');
 const enableHcaptcha = ref<boolean>(false);
 const enableMcaptcha = ref<boolean>(false);
 const enableRecaptcha = ref<boolean>(false);
@@ -172,6 +207,9 @@ const enableTruemailApi = ref<boolean>(false);
 const truemailInstance = ref<string | null>(null);
 const truemailAuthKey = ref<string | null>(null);
 const bannedEmailDomains = ref<string>('');
+const indieAuthClients = ref<any[]>([]);
+const indieAuthOffset = ref(0);
+const indieAuthHasMore = ref(false);
 
 async function init() {
 	const meta = await misskeyApi('admin/meta');
@@ -201,15 +239,15 @@ async function init() {
 
 function save() {
 	os.apiWithDialog('admin/update-meta', {
-		summalyProxy: summalyProxy.value,
-		sensitiveMediaDetection: sensitiveMediaDetection.value,
+		summalyProxy: summalyProxy.value === '' ? null : summalyProxy.value,
+		sensitiveMediaDetection: sensitiveMediaDetection.value as 'none' | 'all' | 'local' | 'remote',
 		sensitiveMediaDetectionSensitivity:
 			sensitiveMediaDetectionSensitivity.value === 0 ? 'veryLow' :
 			sensitiveMediaDetectionSensitivity.value === 1 ? 'low' :
 			sensitiveMediaDetectionSensitivity.value === 2 ? 'medium' :
 			sensitiveMediaDetectionSensitivity.value === 3 ? 'high' :
 			sensitiveMediaDetectionSensitivity.value === 4 ? 'veryHigh' :
-			0,
+			'veryLow',
 		setSensitiveFlagAutomatically: setSensitiveFlagAutomatically.value,
 		enableSensitiveMediaDetectionForVideos: enableSensitiveMediaDetectionForVideos.value,
 		enableIpLogging: enableIpLogging.value,
@@ -225,6 +263,64 @@ function save() {
 	});
 }
 
+function indieAuthFetch(resetOffset = false) {
+	if (resetOffset) {
+		indieAuthClients.value = [];
+		indieAuthOffset.value = 0;
+	}
+
+	misskeyApi('admin/indie-auth/list', {
+		offsetMode: true,
+		offset: indieAuthOffset.value,
+		limit: 10,
+	}).then(clients => {
+		indieAuthClients.value = indieAuthClients.value.concat(clients.map((client: any) => ({
+			id: client.id,
+			name: client.name,
+			redirectUris: client.redirectUris.join('\n'),
+			createdAt: client.createdAt,
+		})));
+		indieAuthHasMore.value = clients.length === 10;
+		indieAuthOffset.value += clients.length;
+	});
+}
+
+indieAuthFetch(true);
+
+function indieAuthAddNew() {
+	indieAuthClients.value.unshift({
+		id: '',
+		name: '',
+		redirectUris: '',
+	});
+}
+
+function indieAuthDelete(client) {
+	os.confirm({
+		type: 'warning',
+		text: i18n.tsx.deleteAreYouSure({ x: client.id }),
+	}).then(({ canceled }) => {
+		if (canceled) return;
+		indieAuthClients.value = indieAuthClients.value.filter(x => x !== client);
+		misskeyApi('admin/indie-auth/delete', client);
+	});
+}
+
+async function indieAuthSave(client) {
+	const params = {
+		id: client.id,
+		name: client.name,
+		redirectUris: client.redirectUris.split('\n'),
+	};
+
+	if (client.createdAt !== undefined) {
+		await misskeyApi('admin/indie-auth/update', params);
+	} else {
+		await misskeyApi('admin/indie-auth/create', params);
+	}
+	indieAuthFetch(true);
+}
+
 const headerActions = computed(() => []);
 
 const headerTabs = computed(() => []);
@@ -234,3 +330,10 @@ definePageMetadata({
 	icon: 'ti ti-lock',
 });
 </script>
+
+<style lang="scss" module>
+.more {
+	margin-left: auto;
+	margin-right: auto;
+}
+</style>
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index 0e137d26bf..f96b867613 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -214,6 +214,24 @@ type AdminGetUserIpsRequest = operations['admin/get-user-ips']['requestBody']['c
 // @public (undocumented)
 type AdminGetUserIpsResponse = operations['admin/get-user-ips']['responses']['200']['content']['application/json'];
 
+// @public (undocumented)
+type AdminIndieAuthCreateRequest = operations['admin/indie-auth/create']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminIndieAuthCreateResponse = operations['admin/indie-auth/create']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminIndieAuthDeleteRequest = operations['admin/indie-auth/delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminIndieAuthListRequest = operations['admin/indie-auth/list']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminIndieAuthListResponse = operations['admin/indie-auth/list']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminIndieAuthUpdateRequest = operations['admin/indie-auth/update']['requestBody']['content']['application/json'];
+
 // @public (undocumented)
 type AdminInviteCreateRequest = operations['admin/invite/create']['requestBody']['content']['application/json'];
 
@@ -1208,6 +1226,12 @@ declare namespace entities {
         AdminFederationRefreshRemoteInstanceMetadataRequest,
         AdminFederationRemoveAllFollowingRequest,
         AdminFederationUpdateInstanceRequest,
+        AdminIndieAuthCreateRequest,
+        AdminIndieAuthCreateResponse,
+        AdminIndieAuthDeleteRequest,
+        AdminIndieAuthListRequest,
+        AdminIndieAuthListResponse,
+        AdminIndieAuthUpdateRequest,
         AdminGetIndexStatsResponse,
         AdminGetTableStatsResponse,
         AdminGetUserIpsRequest,
@@ -2377,6 +2401,15 @@ type ModerationLog = {
 } | {
     type: 'deleteAd';
     info: ModerationLogPayloads['deleteAd'];
+} | {
+    type: 'createIndieAuthClient';
+    info: ModerationLogPayloads['createIndieAuthClient'];
+} | {
+    type: 'updateIndieAuthClient';
+    info: ModerationLogPayloads['updateIndieAuthClient'];
+} | {
+    type: 'deleteIndieAuthClient';
+    info: ModerationLogPayloads['deleteIndieAuthClient'];
 } | {
     type: 'createAvatarDecoration';
     info: ModerationLogPayloads['createAvatarDecoration'];
@@ -2398,7 +2431,7 @@ type ModerationLog = {
 });
 
 // @public (undocumented)
-export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner"];
+export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation", "createAd", "updateAd", "deleteAd", "createIndieAuthClient", "updateIndieAuthClient", "deleteIndieAuthClient", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner"];
 
 // @public (undocumented)
 type MuteCreateRequest = operations['mute/create']['requestBody']['content']['application/json'];
@@ -2650,7 +2683,7 @@ type PagesUpdateRequest = operations['pages/update']['requestBody']['content']['
 function parse(acct: string): Acct;
 
 // @public (undocumented)
-export const permissions: readonly ["read:account", "write:account", "read:blocks", "write:blocks", "read:drive", "write:drive", "read:favorites", "write:favorites", "read:following", "write:following", "read:messaging", "write:messaging", "read:mutes", "write:mutes", "write:notes", "read:notifications", "write:notifications", "read:reactions", "write:reactions", "write:votes", "read:pages", "write:pages", "write:page-likes", "read:page-likes", "read:user-groups", "write:user-groups", "read:channels", "write:channels", "read:gallery", "write:gallery", "read:gallery-likes", "write:gallery-likes", "read:flash", "write:flash", "read:flash-likes", "write:flash-likes", "read:admin:abuse-user-reports", "read:admin:abuse-report-resolvers", "write:admin:abuse-report-resolvers", "write:admin:delete-account", "write:admin:delete-all-files-of-a-user", "read:admin:index-stats", "read:admin:table-stats", "read:admin:user-ips", "read:admin:meta", "write:admin:reset-password", "write:admin:resolve-abuse-user-report", "write:admin:send-email", "read:admin:server-info", "read:admin:show-moderation-log", "read:admin:show-user", "read:admin:show-users", "write:admin:suspend-user", "write:admin:unset-user-avatar", "write:admin:unset-user-banner", "write:admin:unsuspend-user", "write:admin:meta", "write:admin:user-note", "write:admin:roles", "read:admin:roles", "write:admin:relays", "read:admin:relays", "write:admin:invite-codes", "read:admin:invite-codes", "write:admin:announcements", "read:admin:announcements", "write:admin:avatar-decorations", "read:admin:avatar-decorations", "write:admin:federation", "write:admin:account", "read:admin:account", "write:admin:emoji", "read:admin:emoji", "write:admin:queue", "read:admin:queue", "write:admin:promo", "write:admin:drive", "read:admin:drive", "write:admin:ad", "read:admin:ad", "write:invite-codes", "read:invite-codes", "write:clip-favorite", "read:clip-favorite", "read:federation", "write:report-abuse"];
+export const permissions: readonly ["read:account", "write:account", "read:blocks", "write:blocks", "read:drive", "write:drive", "read:favorites", "write:favorites", "read:following", "write:following", "read:messaging", "write:messaging", "read:mutes", "write:mutes", "write:notes", "read:notifications", "write:notifications", "read:reactions", "write:reactions", "write:votes", "read:pages", "write:pages", "write:page-likes", "read:page-likes", "read:user-groups", "write:user-groups", "read:channels", "write:channels", "read:gallery", "write:gallery", "read:gallery-likes", "write:gallery-likes", "read:flash", "write:flash", "read:flash-likes", "write:flash-likes", "read:admin:abuse-user-reports", "read:admin:abuse-report-resolvers", "write:admin:abuse-report-resolvers", "write:admin:delete-account", "write:admin:delete-all-files-of-a-user", "read:admin:index-stats", "read:admin:table-stats", "read:admin:user-ips", "read:admin:meta", "write:admin:reset-password", "write:admin:resolve-abuse-user-report", "write:admin:send-email", "read:admin:server-info", "read:admin:show-moderation-log", "read:admin:show-user", "read:admin:show-users", "write:admin:suspend-user", "write:admin:unset-user-avatar", "write:admin:unset-user-banner", "write:admin:unsuspend-user", "write:admin:meta", "write:admin:user-note", "write:admin:roles", "read:admin:roles", "write:admin:relays", "read:admin:relays", "write:admin:invite-codes", "read:admin:invite-codes", "write:admin:announcements", "read:admin:announcements", "write:admin:avatar-decorations", "read:admin:avatar-decorations", "write:admin:federation", "write:admin:indie-auth", "read:admin:indie-auth", "write:admin:account", "read:admin:account", "write:admin:emoji", "read:admin:emoji", "write:admin:queue", "read:admin:queue", "write:admin:promo", "write:admin:drive", "read:admin:drive", "write:admin:ad", "read:admin:ad", "write:invite-codes", "read:invite-codes", "write:clip-favorite", "read:clip-favorite", "read:federation", "write:report-abuse"];
 
 // @public (undocumented)
 type PingResponse = operations['ping']['responses']['200']['content']['application/json'];
diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
index a92f481aac..88b8e1805d 100644
--- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts
+++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
@@ -499,6 +499,50 @@ declare module '../api.js' {
       credential?: string | null,
     ): Promise<SwitchCaseResponseType<E, P>>;
 
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:admin:indie-auth*
+     */
+    request<E extends 'admin/indie-auth/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:admin:indie-auth*
+     */
+    request<E extends 'admin/indie-auth/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:admin:indie-auth*
+     */
+    request<E extends 'admin/indie-auth/list', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:admin:indie-auth*
+     */
+    request<E extends 'admin/indie-auth/update', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
     /**
      * No description provided.
      * 
diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts
index 6e1969610a..d3ce04c6be 100644
--- a/packages/misskey-js/src/autogen/endpoint.ts
+++ b/packages/misskey-js/src/autogen/endpoint.ts
@@ -59,6 +59,12 @@ import type {
 	AdminFederationRefreshRemoteInstanceMetadataRequest,
 	AdminFederationRemoveAllFollowingRequest,
 	AdminFederationUpdateInstanceRequest,
+	AdminIndieAuthCreateRequest,
+	AdminIndieAuthCreateResponse,
+	AdminIndieAuthDeleteRequest,
+	AdminIndieAuthListRequest,
+	AdminIndieAuthListResponse,
+	AdminIndieAuthUpdateRequest,
 	AdminGetIndexStatsResponse,
 	AdminGetTableStatsResponse,
 	AdminGetUserIpsRequest,
@@ -608,6 +614,10 @@ export type Endpoints = {
 	'admin/federation/refresh-remote-instance-metadata': { req: AdminFederationRefreshRemoteInstanceMetadataRequest; res: EmptyResponse };
 	'admin/federation/remove-all-following': { req: AdminFederationRemoveAllFollowingRequest; res: EmptyResponse };
 	'admin/federation/update-instance': { req: AdminFederationUpdateInstanceRequest; res: EmptyResponse };
+	'admin/indie-auth/create': { req: AdminIndieAuthCreateRequest; res: AdminIndieAuthCreateResponse };
+	'admin/indie-auth/delete': { req: AdminIndieAuthDeleteRequest; res: EmptyResponse };
+	'admin/indie-auth/list': { req: AdminIndieAuthListRequest; res: AdminIndieAuthListResponse };
+	'admin/indie-auth/update': { req: AdminIndieAuthUpdateRequest; res: EmptyResponse };
 	'admin/get-index-stats': { req: EmptyRequest; res: AdminGetIndexStatsResponse };
 	'admin/get-table-stats': { req: EmptyRequest; res: AdminGetTableStatsResponse };
 	'admin/get-user-ips': { req: AdminGetUserIpsRequest; res: AdminGetUserIpsResponse };
diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts
index 87cba9df16..ae443ebea9 100644
--- a/packages/misskey-js/src/autogen/entities.ts
+++ b/packages/misskey-js/src/autogen/entities.ts
@@ -61,6 +61,12 @@ export type AdminFederationDeleteAllFilesRequest = operations['admin/federation/
 export type AdminFederationRefreshRemoteInstanceMetadataRequest = operations['admin/federation/refresh-remote-instance-metadata']['requestBody']['content']['application/json'];
 export type AdminFederationRemoveAllFollowingRequest = operations['admin/federation/remove-all-following']['requestBody']['content']['application/json'];
 export type AdminFederationUpdateInstanceRequest = operations['admin/federation/update-instance']['requestBody']['content']['application/json'];
+export type AdminIndieAuthCreateRequest = operations['admin/indie-auth/create']['requestBody']['content']['application/json'];
+export type AdminIndieAuthCreateResponse = operations['admin/indie-auth/create']['responses']['200']['content']['application/json'];
+export type AdminIndieAuthDeleteRequest = operations['admin/indie-auth/delete']['requestBody']['content']['application/json'];
+export type AdminIndieAuthListRequest = operations['admin/indie-auth/list']['requestBody']['content']['application/json'];
+export type AdminIndieAuthListResponse = operations['admin/indie-auth/list']['responses']['200']['content']['application/json'];
+export type AdminIndieAuthUpdateRequest = operations['admin/indie-auth/update']['requestBody']['content']['application/json'];
 export type AdminGetIndexStatsResponse = operations['admin/get-index-stats']['responses']['200']['content']['application/json'];
 export type AdminGetTableStatsResponse = operations['admin/get-table-stats']['responses']['200']['content']['application/json'];
 export type AdminGetUserIpsRequest = operations['admin/get-user-ips']['requestBody']['content']['application/json'];
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 75057ec416..183b72f2c3 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -418,6 +418,42 @@ export type paths = {
      */
     post: operations['admin/federation/update-instance'];
   };
+  '/admin/indie-auth/create': {
+    /**
+     * admin/indie-auth/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:admin:indie-auth*
+     */
+    post: operations['admin/indie-auth/create'];
+  };
+  '/admin/indie-auth/delete': {
+    /**
+     * admin/indie-auth/delete
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:admin:indie-auth*
+     */
+    post: operations['admin/indie-auth/delete'];
+  };
+  '/admin/indie-auth/list': {
+    /**
+     * admin/indie-auth/list
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:admin:indie-auth*
+     */
+    post: operations['admin/indie-auth/list'];
+  };
+  '/admin/indie-auth/update': {
+    /**
+     * admin/indie-auth/update
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:admin:indie-auth*
+     */
+    post: operations['admin/indie-auth/update'];
+  };
   '/admin/get-index-stats': {
     /**
      * admin/get-index-stats
@@ -7566,6 +7602,233 @@ export type operations = {
       };
     };
   };
+  /**
+   * admin/indie-auth/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:admin:indie-auth*
+   */
+  'admin/indie-auth/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          id: string;
+          name?: string | null;
+          redirectUris?: string[];
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            id: string;
+            /** Format: date-time */
+            createdAt: string;
+            name: string | null;
+            redirectUris: string[];
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/indie-auth/delete
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:admin:indie-auth*
+   */
+  'admin/indie-auth/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          id: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/indie-auth/list
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:admin:indie-auth*
+   */
+  'admin/indie-auth/list': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** @default 0 */
+          offset?: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': ({
+              id: string;
+              /** Format: date-time */
+              createdAt: string;
+              name: string | null;
+              redirectUris: string[];
+            })[];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/indie-auth/update
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:admin:indie-auth*
+   */
+  'admin/indie-auth/update': {
+    requestBody: {
+      content: {
+        'application/json': {
+          id: string;
+          name?: string | null;
+          redirectUris?: string[];
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
   /**
    * admin/get-index-stats
    * @description No description provided.
diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts
index 7ce1b66df2..06ba3f3c78 100644
--- a/packages/misskey-js/src/consts.ts
+++ b/packages/misskey-js/src/consts.ts
@@ -78,6 +78,8 @@ export const permissions = [
 	'write:admin:avatar-decorations',
 	'read:admin:avatar-decorations',
 	'write:admin:federation',
+	'write:admin:indie-auth',
+	'read:admin:indie-auth',
 	'write:admin:account',
 	'read:admin:account',
 	'write:admin:emoji',
@@ -130,6 +132,9 @@ export const moderationLogTypes = [
 	'createAd',
 	'updateAd',
 	'deleteAd',
+	'createIndieAuthClient',
+	'updateIndieAuthClient',
+	'deleteIndieAuthClient',
 	'createAvatarDecoration',
 	'updateAvatarDecoration',
 	'deleteAvatarDecoration',
@@ -296,6 +301,19 @@ export type ModerationLogPayloads = {
 		adId: string;
 		ad: any;
 	};
+	createIndieAuthClient: {
+		clientId: string;
+		client: any;
+	};
+	updateIndieAuthClient: {
+		clientId: string;
+		before: any;
+		after: any;
+	};
+	deleteIndieAuthClient: {
+		clientId: string;
+		client: any;
+	};
 	createAvatarDecoration: {
 		avatarDecorationId: string;
 		avatarDecoration: any;
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index 772d2bbfa1..a26b692f2e 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -113,6 +113,15 @@ export type ModerationLog = {
 } | {
 	type: 'deleteAd';
 	info: ModerationLogPayloads['deleteAd'];
+} | {
+	type: 'createIndieAuthClient';
+	info: ModerationLogPayloads['createIndieAuthClient'];
+} | {
+	type: 'updateIndieAuthClient';
+	info: ModerationLogPayloads['updateIndieAuthClient'];
+} | {
+	type: 'deleteIndieAuthClient';
+	info: ModerationLogPayloads['deleteIndieAuthClient'];
 } | {
 	type: 'createAvatarDecoration';
 	info: ModerationLogPayloads['createAvatarDecoration'];