diff --git a/backend/src/api-token/api-token.dto.ts b/backend/src/api-token/api-token.dto.ts
deleted file mode 100644
index 64189fad8..000000000
--- a/backend/src/api-token/api-token.dto.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import { Type } from 'class-transformer';
-import { IsDate, IsNumber, IsOptional, IsString } from 'class-validator';
-
-import { BaseDto } from '../utils/base.dto.';
-import { TimestampMillis } from '../utils/timestamp';
-
-export class ApiTokenDto extends BaseDto {
-  @IsString()
-  label: string;
-
-  @IsString()
-  keyId: string;
-
-  @IsDate()
-  @Type(() => Date)
-  createdAt: Date;
-
-  @IsDate()
-  @Type(() => Date)
-  validUntil: Date;
-
-  @IsDate()
-  @Type(() => Date)
-  @IsOptional()
-  lastUsedAt: Date | null;
-}
-
-export class ApiTokenWithSecretDto extends ApiTokenDto {
-  @IsString()
-  secret: string;
-}
-
-export class ApiTokenCreateDto extends BaseDto {
-  @IsString()
-  label: string;
-
-  @IsNumber()
-  @Type(() => Number)
-  validUntil: TimestampMillis;
-}
diff --git a/backend/src/api-token/api-token.service.spec.ts b/backend/src/api-token/api-token.service.spec.ts
index cac6f8097..a4bea44b4 100644
--- a/backend/src/api-token/api-token.service.spec.ts
+++ b/backend/src/api-token/api-token.service.spec.ts
@@ -1,9 +1,8 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
-import { DeepPartial } from '@hedgedoc/commons';
 import { ConfigModule } from '@nestjs/config';
 import { Test, TestingModule } from '@nestjs/testing';
 import { getRepositoryToken } from '@nestjs/typeorm';
@@ -150,7 +149,7 @@ describe('ApiTokenService', () => {
       const [accessToken, secret] = service.createToken(
         user,
         'TestToken',
-        undefined,
+        null,
       );
 
       expect(() =>
@@ -158,7 +157,7 @@ describe('ApiTokenService', () => {
       ).not.toThrow();
     });
     it('AuthToken has wrong hash', () => {
-      const [accessToken] = service.createToken(user, 'TestToken', undefined);
+      const [accessToken] = service.createToken(user, 'TestToken', null);
       expect(() =>
         service.checkToken('secret', accessToken as ApiToken),
       ).toThrow(TokenNotValidError);
@@ -167,7 +166,7 @@ describe('ApiTokenService', () => {
       const [accessToken, secret] = service.createToken(
         user,
         'Test',
-        1549312452000,
+        new Date(1549312452000),
       );
       expect(() => service.checkToken(secret, accessToken as ApiToken)).toThrow(
         TokenNotValidError,
@@ -295,18 +294,16 @@ describe('ApiTokenService', () => {
         jest
           .spyOn(apiTokenRepo, 'save')
           .mockImplementationOnce(
-            async (
-              apiTokenSaved: DeepPartial<ApiToken>,
-              _,
-            ): Promise<ApiToken> => {
+            async (apiTokenSaved: ApiToken, _): Promise<ApiToken> => {
               expect(apiTokenSaved.lastUsedAt).toBeNull();
+              apiTokenSaved.createdAt = new Date(1);
               return apiTokenSaved;
             },
           );
-        const token = await service.addToken(user, identifier, 0);
+        const token = await service.addToken(user, identifier, new Date(0));
         expect(token.label).toEqual(identifier);
         expect(
-          token.validUntil.getTime() -
+          new Date(token.validUntil).getTime() -
             (new Date().getTime() + 2 * 365 * 24 * 60 * 60 * 1000),
         ).toBeLessThanOrEqual(10000);
         expect(token.lastUsedAt).toBeNull();
@@ -317,18 +314,17 @@ describe('ApiTokenService', () => {
         jest
           .spyOn(apiTokenRepo, 'save')
           .mockImplementationOnce(
-            async (
-              apiTokenSaved: DeepPartial<ApiToken>,
-              _,
-            ): Promise<ApiToken> => {
+            async (apiTokenSaved: ApiToken, _): Promise<ApiToken> => {
               expect(apiTokenSaved.lastUsedAt).toBeNull();
+              apiTokenSaved.createdAt = new Date(1);
               return apiTokenSaved;
             },
           );
-        const validUntil = new Date().getTime() + 30000;
+        const validUntil = new Date();
+        validUntil.setTime(validUntil.getTime() + 30000);
         const token = await service.addToken(user, identifier, validUntil);
         expect(token.label).toEqual(identifier);
-        expect(token.validUntil.getTime()).toEqual(validUntil);
+        expect(new Date(token.validUntil)).toEqual(validUntil);
         expect(token.lastUsedAt).toBeNull();
         expect(token.secret.startsWith('hd2.' + token.keyId)).toBeTruthy();
       });
@@ -340,7 +336,8 @@ describe('ApiTokenService', () => {
             inValidToken.length = 201;
             return inValidToken;
           });
-        const validUntil = new Date().getTime() + 30000;
+        const validUntil = new Date();
+        validUntil.setTime(validUntil.getTime() + 30000);
         await expect(
           service.addToken(user, identifier, validUntil),
         ).rejects.toThrow(TooManyTokensError);
@@ -400,17 +397,17 @@ describe('ApiTokenService', () => {
       expect(tokenDto.keyId).toEqual(apiToken.keyId);
       expect(tokenDto.lastUsedAt).toBeNull();
       expect(tokenDto.label).toEqual(apiToken.label);
-      expect(tokenDto.validUntil.getTime()).toEqual(
+      expect(new Date(tokenDto.validUntil).getTime()).toEqual(
         apiToken.validUntil.getTime(),
       );
-      expect(tokenDto.createdAt.getTime()).toEqual(
+      expect(new Date(tokenDto.createdAt).getTime()).toEqual(
         apiToken.createdAt.getTime(),
       );
     });
     it('should have lastUsedAt', () => {
       apiToken.lastUsedAt = new Date();
       const tokenDto = service.toAuthTokenDto(apiToken);
-      expect(tokenDto.lastUsedAt).toEqual(apiToken.lastUsedAt);
+      expect(tokenDto.lastUsedAt).toEqual(apiToken.lastUsedAt.toISOString());
     });
   });
 });
diff --git a/backend/src/api-token/api-token.service.ts b/backend/src/api-token/api-token.service.ts
index adc1b9681..36fbb0f94 100644
--- a/backend/src/api-token/api-token.service.ts
+++ b/backend/src/api-token/api-token.service.ts
@@ -1,8 +1,9 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { ApiTokenDto, ApiTokenWithSecretDto } from '@hedgedoc/commons';
 import { Injectable } from '@nestjs/common';
 import { Cron, Timeout } from '@nestjs/schedule';
 import { InjectRepository } from '@nestjs/typeorm';
@@ -17,8 +18,6 @@ import {
 import { ConsoleLoggerService } from '../logger/console-logger.service';
 import { User } from '../users/user.entity';
 import { bufferToBase64Url } from '../utils/password';
-import { TimestampMillis } from '../utils/timestamp';
-import { ApiTokenDto, ApiTokenWithSecretDto } from './api-token.dto';
 import { ApiToken } from './api-token.entity';
 
 export const AUTH_TOKEN_PREFIX = 'hd2';
@@ -54,19 +53,19 @@ export class ApiTokenService {
   createToken(
     user: User,
     identifier: string,
-    userDefinedValidUntil: TimestampMillis | undefined,
+    userDefinedValidUntil: Date | null,
   ): [Omit<ApiToken, 'id' | 'createdAt'>, string] {
     const secret = bufferToBase64Url(randomBytes(64));
     const keyId = bufferToBase64Url(randomBytes(8));
     // More about the choice of SHA-512 in the dev docs
     const accessTokenHash = createHash('sha512').update(secret).digest('hex');
     // Tokens can only be valid for a maximum of 2 years
-    const maximumTokenValidity =
-      new Date().getTime() + 2 * 365 * 24 * 60 * 60 * 1000;
+    const maximumTokenValidity = new Date();
+    maximumTokenValidity.setTime(
+      maximumTokenValidity.getTime() + 2 * 365 * 24 * 60 * 60 * 1000,
+    );
     const isTokenLimitedToMaximumValidity =
-      !userDefinedValidUntil ||
-      userDefinedValidUntil === 0 ||
-      userDefinedValidUntil > maximumTokenValidity;
+      !userDefinedValidUntil || userDefinedValidUntil > maximumTokenValidity;
     const validUntil = isTokenLimitedToMaximumValidity
       ? maximumTokenValidity
       : userDefinedValidUntil;
@@ -83,7 +82,7 @@ export class ApiTokenService {
   async addToken(
     user: User,
     identifier: string,
-    validUntil: TimestampMillis | undefined,
+    validUntil: Date | null,
   ): Promise<ApiTokenWithSecretDto> {
     user.apiTokens = this.getTokensByUser(user);
 
@@ -176,13 +175,13 @@ export class ApiTokenService {
     const tokenDto: ApiTokenDto = {
       label: authToken.label,
       keyId: authToken.keyId,
-      createdAt: authToken.createdAt,
-      validUntil: authToken.validUntil,
+      createdAt: authToken.createdAt.toISOString(),
+      validUntil: authToken.validUntil.toISOString(),
       lastUsedAt: null,
     };
 
     if (authToken.lastUsedAt) {
-      tokenDto.lastUsedAt = new Date(authToken.lastUsedAt);
+      tokenDto.lastUsedAt = new Date(authToken.lastUsedAt).toISOString();
     }
 
     return tokenDto;
diff --git a/backend/src/api-token/mock-api-token.guard.ts b/backend/src/api-token/mock-api-token.guard.ts
index 4658603f9..68e65438b 100644
--- a/backend/src/api-token/mock-api-token.guard.ts
+++ b/backend/src/api-token/mock-api-token.guard.ts
@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
@@ -23,7 +23,12 @@ export class MockApiTokenGuard {
       try {
         this.user = await this.usersService.getUserByUsername('hardcoded');
       } catch (e) {
-        this.user = await this.usersService.createUser('hardcoded', 'Testy');
+        this.user = await this.usersService.createUser(
+          'hardcoded',
+          'Testy',
+          null,
+          null,
+        );
       }
     }
     req.user = this.user;
diff --git a/backend/src/api/private/alias/alias.controller.ts b/backend/src/api/private/alias/alias.controller.ts
index 3bf49db5f..8a3c54e34 100644
--- a/backend/src/api/private/alias/alias.controller.ts
+++ b/backend/src/api/private/alias/alias.controller.ts
@@ -1,8 +1,11 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { AliasCreateDto } from '@hedgedoc/commons';
+import { AliasUpdateDto } from '@hedgedoc/commons';
+import { AliasDto } from '@hedgedoc/commons';
 import {
   BadRequestException,
   Body,
@@ -18,9 +21,6 @@ import { ApiTags } from '@nestjs/swagger';
 
 import { SessionGuard } from '../../../auth/session.guard';
 import { ConsoleLoggerService } from '../../../logger/console-logger.service';
-import { AliasCreateDto } from '../../../notes/alias-create.dto';
-import { AliasUpdateDto } from '../../../notes/alias-update.dto';
-import { AliasDto } from '../../../notes/alias.dto';
 import { AliasService } from '../../../notes/alias.service';
 import { NotesService } from '../../../notes/notes.service';
 import { PermissionsService } from '../../../permissions/permissions.service';
diff --git a/backend/src/api/private/auth/auth.controller.ts b/backend/src/api/private/auth/auth.controller.ts
index 945023440..87d9d0698 100644
--- a/backend/src/api/private/auth/auth.controller.ts
+++ b/backend/src/api/private/auth/auth.controller.ts
@@ -1,8 +1,14 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import {
+  FullUserInfoDto,
+  LogoutResponseDto,
+  PendingUserConfirmationDto,
+  ProviderType,
+} from '@hedgedoc/commons';
 import {
   BadRequestException,
   Body,
@@ -17,11 +23,8 @@ import { ApiTags } from '@nestjs/swagger';
 
 import { IdentityService } from '../../../auth/identity.service';
 import { OidcService } from '../../../auth/oidc/oidc.service';
-import { PendingUserConfirmationDto } from '../../../auth/pending-user-confirmation.dto';
-import { ProviderType } from '../../../auth/provider-type.enum';
 import { RequestWithSession, SessionGuard } from '../../../auth/session.guard';
 import { ConsoleLoggerService } from '../../../logger/console-logger.service';
-import { FullUserInfoDto } from '../../../users/user-info.dto';
 import { OpenApi } from '../../utils/openapi.decorator';
 
 @ApiTags('auth')
@@ -38,7 +41,7 @@ export class AuthController {
   @UseGuards(SessionGuard)
   @Delete('logout')
   @OpenApi(200, 400, 401)
-  logout(@Req() request: RequestWithSession): { redirect: string } {
+  logout(@Req() request: RequestWithSession): LogoutResponseDto {
     let logoutUrl: string | null = null;
     if (request.session.authProviderType === ProviderType.OIDC) {
       logoutUrl = this.oidcService.getLogoutUrl(request);
@@ -60,9 +63,7 @@ export class AuthController {
 
   @Get('pending-user')
   @OpenApi(200, 400)
-  getPendingUserData(
-    @Req() request: RequestWithSession,
-  ): Partial<FullUserInfoDto> {
+  getPendingUserData(@Req() request: RequestWithSession): FullUserInfoDto {
     if (
       !request.session.newUserData ||
       !request.session.authProviderIdentifier ||
diff --git a/backend/src/api/private/auth/ldap/ldap.controller.ts b/backend/src/api/private/auth/ldap/ldap.controller.ts
index e025885a8..09707ee0d 100644
--- a/backend/src/api/private/auth/ldap/ldap.controller.ts
+++ b/backend/src/api/private/auth/ldap/ldap.controller.ts
@@ -1,8 +1,13 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import {
+  LdapLoginDto,
+  LdapLoginResponseDto,
+  ProviderType,
+} from '@hedgedoc/commons';
 import {
   Body,
   Controller,
@@ -14,14 +19,11 @@ import {
 import { ApiTags } from '@nestjs/swagger';
 
 import { IdentityService } from '../../../../auth/identity.service';
-import { LdapLoginDto } from '../../../../auth/ldap/ldap-login.dto';
 import { LdapService } from '../../../../auth/ldap/ldap.service';
-import { ProviderType } from '../../../../auth/provider-type.enum';
 import { RequestWithSession } from '../../../../auth/session.guard';
 import { NotInDBError } from '../../../../errors/errors';
 import { ConsoleLoggerService } from '../../../../logger/console-logger.service';
 import { UsersService } from '../../../../users/users.service';
-import { makeUsernameLowercase } from '../../../../utils/username';
 import { OpenApi } from '../../../utils/openapi.decorator';
 
 @ApiTags('auth')
@@ -43,7 +45,7 @@ export class LdapController {
     request: RequestWithSession,
     @Param('ldapIdentifier') ldapIdentifier: string,
     @Body() loginDto: LdapLoginDto,
-  ): Promise<{ newUser: boolean }> {
+  ): Promise<LdapLoginResponseDto> {
     const ldapConfig = this.ldapService.getLdapConfig(ldapIdentifier);
     const userInfo = await this.ldapService.getUserInfoFromLdap(
       ldapConfig,
@@ -61,7 +63,7 @@ export class LdapController {
       );
       if (this.identityService.mayUpdateIdentity(ldapIdentifier)) {
         const user = await this.usersService.getUserByUsername(
-          makeUsernameLowercase(loginDto.username),
+          loginDto.username.toLowerCase(),
         );
         await this.usersService.updateUser(
           user,
@@ -70,7 +72,7 @@ export class LdapController {
           userInfo.photoUrl,
         );
       }
-      request.session.username = makeUsernameLowercase(loginDto.username);
+      request.session.username = loginDto.username;
       return { newUser: false };
     } catch (error) {
       if (error instanceof NotInDBError) {
diff --git a/backend/src/api/private/auth/local/local.controller.ts b/backend/src/api/private/auth/local/local.controller.ts
index 67821b974..358d85c42 100644
--- a/backend/src/api/private/auth/local/local.controller.ts
+++ b/backend/src/api/private/auth/local/local.controller.ts
@@ -1,8 +1,14 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import {
+  LoginDto,
+  ProviderType,
+  RegisterDto,
+  UpdatePasswordDto,
+} from '@hedgedoc/commons';
 import {
   Body,
   Controller,
@@ -15,10 +21,6 @@ import {
 import { ApiTags } from '@nestjs/swagger';
 
 import { LocalService } from '../../../../auth/local/local.service';
-import { LoginDto } from '../../../../auth/local/login.dto';
-import { RegisterDto } from '../../../../auth/local/register.dto';
-import { UpdatePasswordDto } from '../../../../auth/local/update-password.dto';
-import { ProviderType } from '../../../../auth/provider-type.enum';
 import {
   RequestWithSession,
   SessionGuard,
@@ -53,6 +55,8 @@ export class LocalController {
     const user = await this.usersService.createUser(
       registerDto.username,
       registerDto.displayName,
+      null,
+      null,
     );
     await this.localIdentityService.createLocalIdentity(
       user,
diff --git a/backend/src/api/private/auth/oidc/oidc.controller.ts b/backend/src/api/private/auth/oidc/oidc.controller.ts
index 3e9ef6e5e..a07be8a2c 100644
--- a/backend/src/api/private/auth/oidc/oidc.controller.ts
+++ b/backend/src/api/private/auth/oidc/oidc.controller.ts
@@ -1,8 +1,9 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { ProviderType } from '@hedgedoc/commons';
 import {
   Controller,
   Get,
@@ -17,7 +18,6 @@ import { ApiTags } from '@nestjs/swagger';
 
 import { IdentityService } from '../../../../auth/identity.service';
 import { OidcService } from '../../../../auth/oidc/oidc.service';
-import { ProviderType } from '../../../../auth/provider-type.enum';
 import { RequestWithSession } from '../../../../auth/session.guard';
 import { ConsoleLoggerService } from '../../../../logger/console-logger.service';
 import { UsersService } from '../../../../users/users.service';
diff --git a/backend/src/api/private/config/config.controller.ts b/backend/src/api/private/config/config.controller.ts
index 63bcede0b..3ebb0ca96 100644
--- a/backend/src/api/private/config/config.controller.ts
+++ b/backend/src/api/private/config/config.controller.ts
@@ -1,12 +1,12 @@
 /*
- * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { FrontendConfigDto } from '@hedgedoc/commons';
 import { Controller, Get } from '@nestjs/common';
 import { ApiTags } from '@nestjs/swagger';
 
-import { FrontendConfigDto } from '../../../frontend-config/frontend-config.dto';
 import { FrontendConfigService } from '../../../frontend-config/frontend-config.service';
 import { ConsoleLoggerService } from '../../../logger/console-logger.service';
 import { OpenApi } from '../../utils/openapi.decorator';
diff --git a/backend/src/api/private/groups/groups.controller.ts b/backend/src/api/private/groups/groups.controller.ts
index 7b8631320..98bf6fe40 100644
--- a/backend/src/api/private/groups/groups.controller.ts
+++ b/backend/src/api/private/groups/groups.controller.ts
@@ -1,13 +1,13 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { GroupInfoDto } from '@hedgedoc/commons';
 import { Controller, Get, Param, UseGuards } from '@nestjs/common';
 import { ApiTags } from '@nestjs/swagger';
 
 import { SessionGuard } from '../../../auth/session.guard';
-import { GroupInfoDto } from '../../../groups/group-info.dto';
 import { GroupsService } from '../../../groups/groups.service';
 import { ConsoleLoggerService } from '../../../logger/console-logger.service';
 import { OpenApi } from '../../utils/openapi.decorator';
diff --git a/backend/src/api/private/me/me.controller.ts b/backend/src/api/private/me/me.controller.ts
index d5660dd9f..a41243e3c 100644
--- a/backend/src/api/private/me/me.controller.ts
+++ b/backend/src/api/private/me/me.controller.ts
@@ -1,16 +1,19 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
-import { Body, Controller, Delete, Get, Post, UseGuards } from '@nestjs/common';
-import { ApiBody, ApiTags } from '@nestjs/swagger';
+import {
+  LoginUserInfoDto,
+  MediaUploadDto,
+  ProviderType,
+} from '@hedgedoc/commons';
+import { Body, Controller, Delete, Get, Put, UseGuards } from '@nestjs/common';
+import { ApiTags } from '@nestjs/swagger';
 
 import { SessionGuard } from '../../../auth/session.guard';
 import { ConsoleLoggerService } from '../../../logger/console-logger.service';
-import { MediaUploadDto } from '../../../media/media-upload.dto';
 import { MediaService } from '../../../media/media.service';
-import { UserLoginInfoDto } from '../../../users/user-info.dto';
 import { User } from '../../../users/user.entity';
 import { UsersService } from '../../../users/users.service';
 import { OpenApi } from '../../utils/openapi.decorator';
@@ -34,9 +37,9 @@ export class MeController {
   @OpenApi(200)
   getMe(
     @RequestUser() user: User,
-    @SessionAuthProvider() authProvider: string,
-  ): UserLoginInfoDto {
-    return this.userService.toUserLoginInfoDto(user, authProvider);
+    @SessionAuthProvider() authProvider: ProviderType,
+  ): LoginUserInfoDto {
+    return this.userService.toLoginUserInfoDto(user, authProvider);
   }
 
   @Get('media')
@@ -60,18 +63,9 @@ export class MeController {
     this.logger.debug(`Deleted ${user.username}`);
   }
 
-  @Post('profile')
-  @ApiBody({
-    schema: {
-      type: 'object',
-      properties: {
-        displayName: { type: 'string', nullable: false },
-      },
-      required: ['displayName'],
-    },
-  })
+  @Put('profile')
   @OpenApi(200)
-  async updateDisplayName(
+  async updateProfile(
     @RequestUser() user: User,
     @Body('displayName') newDisplayName: string,
   ): Promise<void> {
diff --git a/backend/src/api/private/media/media.controller.ts b/backend/src/api/private/media/media.controller.ts
index fe624bb77..4c36da762 100644
--- a/backend/src/api/private/media/media.controller.ts
+++ b/backend/src/api/private/media/media.controller.ts
@@ -1,8 +1,9 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { MediaUploadDto, MediaUploadSchema } from '@hedgedoc/commons';
 import {
   BadRequestException,
   Controller,
@@ -22,7 +23,6 @@ import { Response } from 'express';
 import { SessionGuard } from '../../../auth/session.guard';
 import { PermissionError } from '../../../errors/errors';
 import { ConsoleLoggerService } from '../../../logger/console-logger.service';
-import { MediaUploadDto } from '../../../media/media-upload.dto';
 import { MediaService } from '../../../media/media.service';
 import { MulterFile } from '../../../media/multer-file.interface';
 import { Note } from '../../../notes/note.entity';
@@ -74,7 +74,7 @@ export class MediaController {
     {
       code: 201,
       description: 'The file was uploaded successfully',
-      dto: MediaUploadDto,
+      schema: MediaUploadSchema,
     },
     400,
     403,
diff --git a/backend/src/api/private/notes/notes.controller.ts b/backend/src/api/private/notes/notes.controller.ts
index b650d7b7a..f2f52627b 100644
--- a/backend/src/api/private/notes/notes.controller.ts
+++ b/backend/src/api/private/notes/notes.controller.ts
@@ -1,8 +1,22 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import {
+  ChangeNoteOwnerDto,
+  MediaUploadDto,
+  NoteDto,
+  NoteGroupPermissionEntryDto,
+  NoteGroupPermissionUpdateDto,
+  NoteMediaDeletionDto,
+  NoteMetadataDto,
+  NotePermissionsDto,
+  NoteUserPermissionEntryDto,
+  NoteUserPermissionUpdateDto,
+  RevisionDto,
+  RevisionMetadataDto,
+} from '@hedgedoc/commons';
 import {
   BadRequestException,
   Body,
@@ -22,24 +36,16 @@ import { NotInDBError } from '../../../errors/errors';
 import { GroupsService } from '../../../groups/groups.service';
 import { HistoryService } from '../../../history/history.service';
 import { ConsoleLoggerService } from '../../../logger/console-logger.service';
-import { MediaUploadDto } from '../../../media/media-upload.dto';
 import { MediaService } from '../../../media/media.service';
-import { NoteMetadataDto } from '../../../notes/note-metadata.dto';
-import { NotePermissionsDto } from '../../../notes/note-permissions.dto';
-import { NoteDto } from '../../../notes/note.dto';
 import { Note } from '../../../notes/note.entity';
-import { NoteMediaDeletionDto } from '../../../notes/note.media-deletion.dto';
 import { NotesService } from '../../../notes/notes.service';
 import { PermissionsGuard } from '../../../permissions/permissions.guard';
 import { PermissionsService } from '../../../permissions/permissions.service';
 import { RequirePermission } from '../../../permissions/require-permission.decorator';
 import { RequiredPermission } from '../../../permissions/required-permission.enum';
-import { RevisionMetadataDto } from '../../../revisions/revision-metadata.dto';
-import { RevisionDto } from '../../../revisions/revision.dto';
 import { RevisionsService } from '../../../revisions/revisions.service';
 import { User } from '../../../users/user.entity';
 import { UsersService } from '../../../users/users.service';
-import { Username } from '../../../utils/username';
 import { GetNoteInterceptor } from '../../utils/get-note.interceptor';
 import { MarkdownBody } from '../../utils/markdown-body.decorator';
 import { OpenApi } from '../../utils/openapi.decorator';
@@ -197,15 +203,15 @@ export class NotesController {
     );
   }
 
-  @Put(':noteIdOrAlias/metadata/permissions/users/:userName')
+  @Put(':noteIdOrAlias/metadata/permissions/users/:username')
   @OpenApi(200, 403, 404)
   @UseInterceptors(GetNoteInterceptor)
   @RequirePermission(RequiredPermission.OWNER)
   async setUserPermission(
     @RequestUser() user: User,
     @RequestNote() note: Note,
-    @Param('userName') username: Username,
-    @Body('canEdit') canEdit: boolean,
+    @Param('username') username: NoteUserPermissionUpdateDto['username'],
+    @Body('canEdit') canEdit: NoteUserPermissionUpdateDto['canEdit'],
   ): Promise<NotePermissionsDto> {
     const permissionUser = await this.userService.getUserByUsername(username);
     const returnedNote = await this.permissionService.setUserPermission(
@@ -218,11 +224,11 @@ export class NotesController {
 
   @UseInterceptors(GetNoteInterceptor)
   @RequirePermission(RequiredPermission.OWNER)
-  @Delete(':noteIdOrAlias/metadata/permissions/users/:userName')
+  @Delete(':noteIdOrAlias/metadata/permissions/users/:username')
   async removeUserPermission(
     @RequestUser() user: User,
     @RequestNote() note: Note,
-    @Param('userName') username: Username,
+    @Param('username') username: NoteUserPermissionEntryDto['username'],
   ): Promise<NotePermissionsDto> {
     try {
       const permissionUser = await this.userService.getUserByUsername(username);
@@ -247,8 +253,8 @@ export class NotesController {
   async setGroupPermission(
     @RequestUser() user: User,
     @RequestNote() note: Note,
-    @Param('groupName') groupName: string,
-    @Body('canEdit') canEdit: boolean,
+    @Param('groupName') groupName: NoteGroupPermissionUpdateDto['groupName'],
+    @Body('canEdit') canEdit: NoteGroupPermissionUpdateDto['canEdit'],
   ): Promise<NotePermissionsDto> {
     const permissionGroup = await this.groupService.getGroupByName(groupName);
     const returnedNote = await this.permissionService.setGroupPermission(
@@ -266,7 +272,7 @@ export class NotesController {
   async removeGroupPermission(
     @RequestUser() user: User,
     @RequestNote() note: Note,
-    @Param('groupName') groupName: string,
+    @Param('groupName') groupName: NoteGroupPermissionEntryDto['groupName'],
   ): Promise<NotePermissionsDto> {
     const permissionGroup = await this.groupService.getGroupByName(groupName);
     const returnedNote = await this.permissionService.removeGroupPermission(
@@ -282,9 +288,11 @@ export class NotesController {
   async changeOwner(
     @RequestUser() user: User,
     @RequestNote() note: Note,
-    @Body('newOwner') newOwner: Username,
+    @Body() changeNoteOwnerDto: ChangeNoteOwnerDto,
   ): Promise<NoteDto> {
-    const owner = await this.userService.getUserByUsername(newOwner);
+    const owner = await this.userService.getUserByUsername(
+      changeNoteOwnerDto.owner,
+    );
     return await this.noteService.toNoteDto(
       await this.permissionService.changeOwner(note, owner),
     );
diff --git a/backend/src/api/private/tokens/api-tokens.controller.ts b/backend/src/api/private/tokens/api-tokens.controller.ts
index e9e22e847..a1fad3997 100644
--- a/backend/src/api/private/tokens/api-tokens.controller.ts
+++ b/backend/src/api/private/tokens/api-tokens.controller.ts
@@ -1,8 +1,13 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import {
+  ApiTokenCreateDto,
+  ApiTokenDto,
+  ApiTokenWithSecretDto,
+} from '@hedgedoc/commons';
 import {
   Body,
   Controller,
@@ -15,11 +20,6 @@ import {
 } from '@nestjs/common';
 import { ApiTags } from '@nestjs/swagger';
 
-import {
-  ApiTokenCreateDto,
-  ApiTokenDto,
-  ApiTokenWithSecretDto,
-} from '../../../api-token/api-token.dto';
 import { ApiTokenService } from '../../../api-token/api-token.service';
 import { SessionGuard } from '../../../auth/session.guard';
 import { ConsoleLoggerService } from '../../../logger/console-logger.service';
diff --git a/backend/src/api/private/users/users.controller.ts b/backend/src/api/private/users/users.controller.ts
index 20ce960b9..d8957ba60 100644
--- a/backend/src/api/private/users/users.controller.ts
+++ b/backend/src/api/private/users/users.controller.ts
@@ -1,19 +1,18 @@
 /*
- * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import {
+  UserInfoDto,
+  UsernameCheckDto,
+  UsernameCheckResponseDto,
+} from '@hedgedoc/commons';
 import { Body, Controller, Get, HttpCode, Param, Post } from '@nestjs/common';
 import { ApiTags } from '@nestjs/swagger';
 
 import { ConsoleLoggerService } from '../../../logger/console-logger.service';
-import { UserInfoDto } from '../../../users/user-info.dto';
-import {
-  UsernameCheckDto,
-  UsernameCheckResponseDto,
-} from '../../../users/username-check.dto';
 import { UsersService } from '../../../users/users.service';
-import { Username } from '../../../utils/username';
 import { OpenApi } from '../../utils/openapi.decorator';
 
 @ApiTags('users')
@@ -41,7 +40,7 @@ export class UsersController {
 
   @Get('profile/:username')
   @OpenApi(200)
-  async getUser(@Param('username') username: Username): Promise<UserInfoDto> {
+  async getUser(@Param('username') username: string): Promise<UserInfoDto> {
     return this.userService.toUserDto(
       await this.userService.getUserByUsername(username),
     );
diff --git a/backend/src/api/public/alias/alias.controller.ts b/backend/src/api/public/alias/alias.controller.ts
index 9a18752d9..0b137ec51 100644
--- a/backend/src/api/public/alias/alias.controller.ts
+++ b/backend/src/api/public/alias/alias.controller.ts
@@ -1,8 +1,14 @@
 /*
- * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import {
+  AliasCreateDto,
+  AliasDto,
+  AliasSchema,
+  AliasUpdateDto,
+} from '@hedgedoc/commons';
 import {
   BadRequestException,
   Body,
@@ -18,9 +24,6 @@ import { ApiSecurity, ApiTags } from '@nestjs/swagger';
 
 import { ApiTokenGuard } from '../../../api-token/api-token.guard';
 import { ConsoleLoggerService } from '../../../logger/console-logger.service';
-import { AliasCreateDto } from '../../../notes/alias-create.dto';
-import { AliasUpdateDto } from '../../../notes/alias-update.dto';
-import { AliasDto } from '../../../notes/alias.dto';
 import { AliasService } from '../../../notes/alias.service';
 import { NotesService } from '../../../notes/notes.service';
 import { PermissionsService } from '../../../permissions/permissions.service';
@@ -48,7 +51,7 @@ export class AliasController {
     {
       code: 200,
       description: 'The new alias',
-      dto: AliasDto,
+      schema: AliasSchema,
     },
     403,
     404,
@@ -75,7 +78,7 @@ export class AliasController {
     {
       code: 200,
       description: 'The updated alias',
-      dto: AliasDto,
+      schema: AliasSchema,
     },
     403,
     404,
@@ -110,7 +113,7 @@ export class AliasController {
   )
   async removeAlias(
     @RequestUser() user: User,
-    @Param('alias') alias: string,
+    @Param('alias') alias: AliasDto['name'],
   ): Promise<void> {
     const note = await this.noteService.getNoteByIdOrAlias(alias);
     if (!(await this.permissionsService.isOwner(user, note))) {
diff --git a/backend/src/api/public/me/me.controller.ts b/backend/src/api/public/me/me.controller.ts
index 44394d035..5fc4514d5 100644
--- a/backend/src/api/public/me/me.controller.ts
+++ b/backend/src/api/public/me/me.controller.ts
@@ -1,8 +1,16 @@
 /*
- * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import {
+  FullUserInfoDto,
+  FullUserInfoSchema,
+  MediaUploadDto,
+  MediaUploadSchema,
+  NoteMetadataDto,
+  NoteMetadataSchema,
+} from '@hedgedoc/commons';
 import {
   Body,
   Controller,
@@ -19,12 +27,9 @@ import { HistoryEntryUpdateDto } from '../../../history/history-entry-update.dto
 import { HistoryEntryDto } from '../../../history/history-entry.dto';
 import { HistoryService } from '../../../history/history.service';
 import { ConsoleLoggerService } from '../../../logger/console-logger.service';
-import { MediaUploadDto } from '../../../media/media-upload.dto';
 import { MediaService } from '../../../media/media.service';
-import { NoteMetadataDto } from '../../../notes/note-metadata.dto';
 import { Note } from '../../../notes/note.entity';
 import { NotesService } from '../../../notes/notes.service';
-import { FullUserInfoDto } from '../../../users/user-info.dto';
 import { User } from '../../../users/user.entity';
 import { UsersService } from '../../../users/users.service';
 import { GetNoteInterceptor } from '../../utils/get-note.interceptor';
@@ -52,7 +57,7 @@ export class MeController {
   @OpenApi({
     code: 200,
     description: 'The user information',
-    dto: FullUserInfoDto,
+    schema: FullUserInfoSchema,
   })
   getMe(@RequestUser() user: User): FullUserInfoDto {
     return this.usersService.toFullUserDto(user);
@@ -63,7 +68,6 @@ export class MeController {
     code: 200,
     description: 'The history entries of the user',
     isArray: true,
-    dto: HistoryEntryDto,
   })
   async getUserHistory(@RequestUser() user: User): Promise<HistoryEntryDto[]> {
     const foundEntries = await this.historyService.getEntriesByUser(user);
@@ -78,7 +82,6 @@ export class MeController {
     {
       code: 200,
       description: 'The history entry of the user which points to the note',
-      dto: HistoryEntryDto,
     },
     404,
   )
@@ -96,7 +99,6 @@ export class MeController {
     {
       code: 200,
       description: 'The updated history entry',
-      dto: HistoryEntryDto,
     },
     404,
   )
@@ -125,7 +127,7 @@ export class MeController {
     code: 200,
     description: 'Metadata of all notes of the user',
     isArray: true,
-    dto: NoteMetadataDto,
+    schema: NoteMetadataSchema,
   })
   async getMyNotes(@RequestUser() user: User): Promise<NoteMetadataDto[]> {
     const notes = this.notesService.getUserNotes(user);
@@ -139,7 +141,7 @@ export class MeController {
     code: 200,
     description: 'All media uploads of the user',
     isArray: true,
-    dto: MediaUploadDto,
+    schema: MediaUploadSchema,
   })
   async getMyMedia(@RequestUser() user: User): Promise<MediaUploadDto[]> {
     const media = await this.mediaService.listUploadsByUser(user);
diff --git a/backend/src/api/public/media/media.controller.ts b/backend/src/api/public/media/media.controller.ts
index dbd2f1d27..8e63cbc21 100644
--- a/backend/src/api/public/media/media.controller.ts
+++ b/backend/src/api/public/media/media.controller.ts
@@ -1,8 +1,9 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { MediaUploadDto, MediaUploadSchema } from '@hedgedoc/commons';
 import {
   BadRequestException,
   Controller,
@@ -28,7 +29,6 @@ import { Response } from 'express';
 import { ApiTokenGuard } from '../../../api-token/api-token.guard';
 import { PermissionError } from '../../../errors/errors';
 import { ConsoleLoggerService } from '../../../logger/console-logger.service';
-import { MediaUploadDto } from '../../../media/media-upload.dto';
 import { MediaService } from '../../../media/media.service';
 import { MulterFile } from '../../../media/multer-file.interface';
 import { Note } from '../../../notes/note.entity';
@@ -77,7 +77,7 @@ export class MediaController {
     {
       code: 201,
       description: 'The file was uploaded successfully',
-      dto: MediaUploadDto,
+      schema: MediaUploadSchema,
     },
     400,
     403,
diff --git a/backend/src/api/public/monitoring/monitoring.controller.ts b/backend/src/api/public/monitoring/monitoring.controller.ts
index 73001268a..df91653f5 100644
--- a/backend/src/api/public/monitoring/monitoring.controller.ts
+++ b/backend/src/api/public/monitoring/monitoring.controller.ts
@@ -1,14 +1,14 @@
 /*
- * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { ServerStatusDto, ServerStatusSchema } from '@hedgedoc/commons';
 import { Controller, Get, UseGuards } from '@nestjs/common';
 import { ApiSecurity, ApiTags } from '@nestjs/swagger';
 
 import { ApiTokenGuard } from '../../../api-token/api-token.guard';
 import { MonitoringService } from '../../../monitoring/monitoring.service';
-import { ServerStatusDto } from '../../../monitoring/server-status.dto';
 import { OpenApi } from '../../utils/openapi.decorator';
 
 @UseGuards(ApiTokenGuard)
@@ -24,7 +24,7 @@ export class MonitoringController {
     {
       code: 200,
       description: 'The server info',
-      dto: ServerStatusDto,
+      schema: ServerStatusSchema,
     },
     403,
   )
diff --git a/backend/src/api/public/notes/notes.controller.ts b/backend/src/api/public/notes/notes.controller.ts
index 9a993faf5..644e1224f 100644
--- a/backend/src/api/public/notes/notes.controller.ts
+++ b/backend/src/api/public/notes/notes.controller.ts
@@ -1,8 +1,24 @@
 /*
- * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import {
+  MediaUploadDto,
+  MediaUploadSchema,
+  NoteDto,
+  NoteMediaDeletionDto,
+  NoteMetadataDto,
+  NoteMetadataSchema,
+  NotePermissionsDto,
+  NotePermissionsSchema,
+  NotePermissionsUpdateDto,
+  NoteSchema,
+  RevisionDto,
+  RevisionMetadataDto,
+  RevisionMetadataSchema,
+  RevisionSchema,
+} from '@hedgedoc/commons';
 import {
   BadRequestException,
   Body,
@@ -22,27 +38,16 @@ import { NotInDBError } from '../../../errors/errors';
 import { GroupsService } from '../../../groups/groups.service';
 import { HistoryService } from '../../../history/history.service';
 import { ConsoleLoggerService } from '../../../logger/console-logger.service';
-import { MediaUploadDto } from '../../../media/media-upload.dto';
 import { MediaService } from '../../../media/media.service';
-import { NoteMetadataDto } from '../../../notes/note-metadata.dto';
-import {
-  NotePermissionsDto,
-  NotePermissionsUpdateDto,
-} from '../../../notes/note-permissions.dto';
-import { NoteDto } from '../../../notes/note.dto';
 import { Note } from '../../../notes/note.entity';
-import { NoteMediaDeletionDto } from '../../../notes/note.media-deletion.dto';
 import { NotesService } from '../../../notes/notes.service';
 import { PermissionsGuard } from '../../../permissions/permissions.guard';
 import { PermissionsService } from '../../../permissions/permissions.service';
 import { RequirePermission } from '../../../permissions/require-permission.decorator';
 import { RequiredPermission } from '../../../permissions/required-permission.enum';
-import { RevisionMetadataDto } from '../../../revisions/revision-metadata.dto';
-import { RevisionDto } from '../../../revisions/revision.dto';
 import { RevisionsService } from '../../../revisions/revisions.service';
 import { User } from '../../../users/user.entity';
 import { UsersService } from '../../../users/users.service';
-import { Username } from '../../../utils/username';
 import { GetNoteInterceptor } from '../../utils/get-note.interceptor';
 import { MarkdownBody } from '../../utils/markdown-body.decorator';
 import { OpenApi } from '../../utils/openapi.decorator';
@@ -88,7 +93,7 @@ export class NotesController {
     {
       code: 200,
       description: 'Get information about the newly created note',
-      dto: NoteDto,
+      schema: NoteSchema,
     },
     403,
     404,
@@ -107,7 +112,7 @@ export class NotesController {
     {
       code: 201,
       description: 'Get information about the newly created note',
-      dto: NoteDto,
+      schema: NoteSchema,
     },
     400,
     403,
@@ -155,7 +160,7 @@ export class NotesController {
     {
       code: 200,
       description: 'The new, changed note',
-      dto: NoteDto,
+      schema: NoteSchema,
     },
     403,
     404,
@@ -197,7 +202,7 @@ export class NotesController {
     {
       code: 200,
       description: 'The metadata of the note',
-      dto: NoteMetadataDto,
+      schema: NoteMetadataSchema,
     },
     403,
     404,
@@ -216,7 +221,7 @@ export class NotesController {
     {
       code: 200,
       description: 'The updated permissions of the note',
-      dto: NotePermissionsDto,
+      schema: NotePermissionsSchema,
     },
     403,
     404,
@@ -238,7 +243,7 @@ export class NotesController {
     {
       code: 200,
       description: 'Get the permissions for a note',
-      dto: NotePermissionsDto,
+      schema: NotePermissionsSchema,
     },
     403,
     404,
@@ -257,7 +262,7 @@ export class NotesController {
     {
       code: 200,
       description: 'Set the permissions for a user on a note',
-      dto: NotePermissionsDto,
+      schema: NotePermissionsSchema,
     },
     403,
     404,
@@ -265,7 +270,7 @@ export class NotesController {
   async setUserPermission(
     @RequestUser() user: User,
     @RequestNote() note: Note,
-    @Param('userName') username: Username,
+    @Param('userName') username: string,
     @Body('canEdit') canEdit: boolean,
   ): Promise<NotePermissionsDto> {
     const permissionUser = await this.userService.getUserByUsername(username);
@@ -284,7 +289,7 @@ export class NotesController {
     {
       code: 200,
       description: 'Remove the permission for a user on a note',
-      dto: NotePermissionsDto,
+      schema: NotePermissionsSchema,
     },
     403,
     404,
@@ -292,7 +297,7 @@ export class NotesController {
   async removeUserPermission(
     @RequestUser() user: User,
     @RequestNote() note: Note,
-    @Param('userName') username: Username,
+    @Param('userName') username: string,
   ): Promise<NotePermissionsDto> {
     try {
       const permissionUser = await this.userService.getUserByUsername(username);
@@ -318,7 +323,7 @@ export class NotesController {
     {
       code: 200,
       description: 'Set the permissions for a group on a note',
-      dto: NotePermissionsDto,
+      schema: NotePermissionsSchema,
     },
     403,
     404,
@@ -345,7 +350,7 @@ export class NotesController {
     {
       code: 200,
       description: 'Remove the permission for a group on a note',
-      dto: NotePermissionsDto,
+      schema: NotePermissionsSchema,
     },
     403,
     404,
@@ -370,7 +375,7 @@ export class NotesController {
     {
       code: 200,
       description: 'Changes the owner of the note',
-      dto: NoteDto,
+      schema: NoteSchema,
     },
     403,
     404,
@@ -378,7 +383,7 @@ export class NotesController {
   async changeOwner(
     @RequestUser() user: User,
     @RequestNote() note: Note,
-    @Body('newOwner') newOwner: Username,
+    @Body('newOwner') newOwner: string,
   ): Promise<NoteDto> {
     const owner = await this.userService.getUserByUsername(newOwner);
     return await this.noteService.toNoteDto(
@@ -394,7 +399,7 @@ export class NotesController {
       code: 200,
       description: 'Revisions of the note',
       isArray: true,
-      dto: RevisionMetadataDto,
+      schema: RevisionMetadataSchema,
     },
     403,
     404,
@@ -418,7 +423,7 @@ export class NotesController {
     {
       code: 200,
       description: 'Revision of the note for the given id or alias',
-      dto: RevisionDto,
+      schema: RevisionSchema,
     },
     403,
     404,
@@ -440,7 +445,7 @@ export class NotesController {
     code: 200,
     description: 'All media uploads of the note',
     isArray: true,
-    dto: MediaUploadDto,
+    schema: MediaUploadSchema,
   })
   async getNotesMedia(
     @RequestUser() user: User,
diff --git a/backend/src/api/utils/openapi.decorator.ts b/backend/src/api/utils/openapi.decorator.ts
index ceec342b8..07859b98c 100644
--- a/backend/src/api/utils/openapi.decorator.ts
+++ b/backend/src/api/utils/openapi.decorator.ts
@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
@@ -14,10 +14,12 @@ import {
   ApiNotFoundResponse,
   ApiOkResponse,
   ApiProduces,
+  ApiResponseNoStatusOptions,
   ApiUnauthorizedResponse,
 } from '@nestjs/swagger';
+import { zodToOpenAPI } from 'nestjs-zod';
+import { ZodSchema } from 'zod';
 
-import { BaseDto } from '../../utils/base.dto.';
 import {
   badRequestDescription,
   conflictDescription,
@@ -57,7 +59,7 @@ export interface HttpStatusCodeWithExtraInformation {
   code: HttpStatusCodes;
   description?: string;
   isArray?: boolean;
-  dto?: BaseDto;
+  schema?: ZodSchema;
   mimeType?: string;
 }
 
@@ -89,7 +91,8 @@ export const OpenApi = (
     let code: HttpStatusCodes = 200;
     let description: string | undefined = undefined;
     let isArray: boolean | undefined = undefined;
-    let dto: BaseDto | undefined = undefined;
+    let schema: ZodSchema | undefined = undefined;
+
     if (typeof entry == 'number') {
       code = entry;
     } else {
@@ -97,7 +100,7 @@ export const OpenApi = (
       code = entry.code;
       description = entry.description;
       isArray = entry.isArray;
-      dto = entry.dto;
+      schema = entry.schema;
       if (entry.mimeType) {
         decoratorsToApply.push(
           ApiProduces(entry.mimeType),
@@ -105,22 +108,32 @@ export const OpenApi = (
         );
       }
     }
+
+    let defaultResponseObject: ApiResponseNoStatusOptions = {
+      description: description ?? createdDescription,
+      isArray: isArray,
+    };
+    if (schema) {
+      defaultResponseObject = {
+        ...defaultResponseObject,
+        schema: zodToOpenAPI(schema),
+      };
+    }
+
     switch (code) {
       case 200:
         decoratorsToApply.push(
           ApiOkResponse({
+            ...defaultResponseObject,
             description: description ?? okDescription,
-            isArray: isArray,
-            type: dto ? (): BaseDto => dto : undefined,
           }),
         );
         break;
       case 201:
         decoratorsToApply.push(
           ApiCreatedResponse({
+            ...defaultResponseObject,
             description: description ?? createdDescription,
-            isArray: isArray,
-            type: dto ? (): BaseDto => dto : undefined,
           }),
           HttpCode(201),
         );
diff --git a/backend/src/auth/identity.entity.ts b/backend/src/auth/identity.entity.ts
index 3564aa46e..0bdcdf3f5 100644
--- a/backend/src/auth/identity.entity.ts
+++ b/backend/src/auth/identity.entity.ts
@@ -1,8 +1,9 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { ProviderType } from '@hedgedoc/commons';
 import {
   Column,
   CreateDateColumn,
@@ -13,7 +14,6 @@ import {
 } from 'typeorm';
 
 import { User } from '../users/user.entity';
-import { ProviderType } from './provider-type.enum';
 
 /**
  * The identity represents a single way for a user to login.
diff --git a/backend/src/auth/identity.service.ts b/backend/src/auth/identity.service.ts
index 855103cf0..2af021447 100644
--- a/backend/src/auth/identity.service.ts
+++ b/backend/src/auth/identity.service.ts
@@ -1,8 +1,13 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import {
+  FullUserInfoDto,
+  PendingUserConfirmationDto,
+  ProviderType,
+} from '@hedgedoc/commons';
 import {
   Inject,
   Injectable,
@@ -14,12 +19,9 @@ import { DataSource, Repository } from 'typeorm';
 import AuthConfiguration, { AuthConfig } from '../config/auth.config';
 import { NotInDBError } from '../errors/errors';
 import { ConsoleLoggerService } from '../logger/console-logger.service';
-import { FullUserInfoDto } from '../users/user-info.dto';
 import { User } from '../users/user.entity';
 import { UsersService } from '../users/users.service';
 import { Identity } from './identity.entity';
-import { PendingUserConfirmationDto } from './pending-user-confirmation.dto';
-import { ProviderType } from './provider-type.enum';
 
 @Injectable()
 export class IdentityService {
diff --git a/backend/src/auth/ldap/ldap-login.dto.ts b/backend/src/auth/ldap/ldap-login.dto.ts
deleted file mode 100644
index 9c28af41d..000000000
--- a/backend/src/auth/ldap/ldap-login.dto.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import { IsString } from 'class-validator';
-
-export class LdapLoginDto {
-  @IsString()
-  username: string; // This is not of type Username, because LDAP server may use mixed case usernames
-  @IsString()
-  password: string;
-}
diff --git a/backend/src/auth/ldap/ldap.service.ts b/backend/src/auth/ldap/ldap.service.ts
index 64adbfebe..23f48a3f3 100644
--- a/backend/src/auth/ldap/ldap.service.ts
+++ b/backend/src/auth/ldap/ldap.service.ts
@@ -1,8 +1,9 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { FullUserInfoWithIdDto } from '@hedgedoc/commons';
 import {
   Inject,
   Injectable,
@@ -18,8 +19,6 @@ import authConfiguration, {
   LDAPConfig,
 } from '../../config/auth.config';
 import { ConsoleLoggerService } from '../../logger/console-logger.service';
-import { FullUserInfoWithIdDto } from '../../users/user-info.dto';
-import { Username } from '../../utils/username';
 
 const LDAP_ERROR_MAP: Record<string, string> = {
   /* eslint-disable @typescript-eslint/naming-convention */
@@ -96,7 +95,7 @@ export class LdapService {
             return reject(new UnauthorizedException(LDAP_ERROR_MAP['default']));
           }
 
-          let email: string | undefined = undefined;
+          let email: string | null = null;
           if (userInfo['mail']) {
             if (Array.isArray(userInfo['mail'])) {
               email = userInfo['mail'][0] as string;
@@ -107,10 +106,10 @@ export class LdapService {
 
           return resolve({
             email,
-            username: username as Username,
+            username: username,
             id: userInfo[ldapConfig.userIdField],
             displayName: userInfo[ldapConfig.displayNameField] ?? username,
-            photoUrl: undefined, // TODO LDAP stores images as binaries,
+            photoUrl: null, // TODO LDAP stores images as binaries,
             // we need to convert them into a data-URL or alike
           });
         },
diff --git a/backend/src/auth/local/local.service.ts b/backend/src/auth/local/local.service.ts
index a2bd482b0..a654a210f 100644
--- a/backend/src/auth/local/local.service.ts
+++ b/backend/src/auth/local/local.service.ts
@@ -1,8 +1,9 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { ProviderType } from '@hedgedoc/commons';
 import { Inject, Injectable } from '@nestjs/common';
 import { InjectRepository } from '@nestjs/typeorm';
 import {
@@ -32,7 +33,6 @@ import { User } from '../../users/user.entity';
 import { checkPassword, hashPassword } from '../../utils/password';
 import { Identity } from '../identity.entity';
 import { IdentityService } from '../identity.service';
-import { ProviderType } from '../provider-type.enum';
 
 @Injectable()
 export class LocalService {
diff --git a/backend/src/auth/local/login.dto.ts b/backend/src/auth/local/login.dto.ts
deleted file mode 100644
index cb3ed66ad..000000000
--- a/backend/src/auth/local/login.dto.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import { Type } from 'class-transformer';
-import { IsLowercase, IsString } from 'class-validator';
-
-import { Username } from '../../utils/username';
-
-export class LoginDto {
-  @Type(() => String)
-  @IsString()
-  @IsLowercase()
-  username: Username;
-  @IsString()
-  password: string;
-}
diff --git a/backend/src/auth/local/register.dto.ts b/backend/src/auth/local/register.dto.ts
deleted file mode 100644
index a6eec0351..000000000
--- a/backend/src/auth/local/register.dto.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import { Type } from 'class-transformer';
-import { IsLowercase, IsString } from 'class-validator';
-
-import { Username } from '../../utils/username';
-
-export class RegisterDto {
-  @Type(() => String)
-  @IsString()
-  @IsLowercase()
-  username: Username;
-
-  @IsString()
-  displayName: string;
-
-  @IsString()
-  password: string;
-}
diff --git a/backend/src/auth/local/update-password.dto.ts b/backend/src/auth/local/update-password.dto.ts
deleted file mode 100644
index 8f3edc276..000000000
--- a/backend/src/auth/local/update-password.dto.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import { IsString } from 'class-validator';
-
-export class UpdatePasswordDto {
-  @IsString()
-  currentPassword: string;
-  @IsString()
-  newPassword: string;
-}
diff --git a/backend/src/auth/oidc/oidc.service.ts b/backend/src/auth/oidc/oidc.service.ts
index e3b8c579e..49e1d19b0 100644
--- a/backend/src/auth/oidc/oidc.service.ts
+++ b/backend/src/auth/oidc/oidc.service.ts
@@ -1,8 +1,9 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { FullUserInfoDto, ProviderType } from '@hedgedoc/commons';
 import {
   ForbiddenException,
   Inject,
@@ -20,10 +21,8 @@ import authConfiguration, {
 } from '../../config/auth.config';
 import { NotInDBError } from '../../errors/errors';
 import { ConsoleLoggerService } from '../../logger/console-logger.service';
-import { FullUserInfoDto } from '../../users/user-info.dto';
 import { Identity } from '../identity.entity';
 import { IdentityService } from '../identity.service';
-import { ProviderType } from '../provider-type.enum';
 import { RequestWithSession } from '../session.guard';
 
 interface OidcClientConfigEntry {
@@ -227,8 +226,8 @@ export class OidcService {
     const newUserData = {
       username,
       displayName,
-      photoUrl,
-      email,
+      photoUrl: photoUrl ?? null,
+      email: email ?? null,
     };
     request.session.providerUserId = userId;
     request.session.newUserData = newUserData;
diff --git a/backend/src/auth/pending-user-confirmation.dto.ts b/backend/src/auth/pending-user-confirmation.dto.ts
deleted file mode 100644
index d4a3f82da..000000000
--- a/backend/src/auth/pending-user-confirmation.dto.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import { IsOptional, IsString } from 'class-validator';
-
-import { BaseDto } from '../utils/base.dto.';
-
-export class PendingUserConfirmationDto extends BaseDto {
-  @IsString()
-  username: string;
-
-  @IsString()
-  displayName: string;
-
-  @IsOptional()
-  @IsString()
-  profilePicture: string | undefined;
-}
diff --git a/backend/src/auth/session.guard.ts b/backend/src/auth/session.guard.ts
index 402036952..b366e01ba 100644
--- a/backend/src/auth/session.guard.ts
+++ b/backend/src/auth/session.guard.ts
@@ -1,8 +1,10 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { ProviderType } from '@hedgedoc/commons';
+import { GuestAccess } from '@hedgedoc/commons';
 import {
   CanActivate,
   ExecutionContext,
@@ -13,13 +15,11 @@ import {
 import { Request } from 'express';
 
 import { CompleteRequest } from '../api/utils/request.type';
-import { GuestAccess } from '../config/guest_access.enum';
 import noteConfiguration, { NoteConfig } from '../config/note.config';
 import { NotInDBError } from '../errors/errors';
 import { ConsoleLoggerService } from '../logger/console-logger.service';
 import { SessionState } from '../sessions/session.service';
 import { UsersService } from '../users/users.service';
-import { ProviderType } from './provider-type.enum';
 
 export type RequestWithSession = Request & {
   session: SessionState;
diff --git a/backend/src/config/customization.config.ts b/backend/src/config/customization.config.ts
index 09f00234e..74805dc12 100644
--- a/backend/src/config/customization.config.ts
+++ b/backend/src/config/customization.config.ts
@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
@@ -10,24 +10,24 @@ import { buildErrorMessage } from './utils';
 
 export interface CustomizationConfig {
   branding: {
-    customName: string;
-    customLogo: string;
+    customName: string | null;
+    customLogo: string | null;
   };
   specialUrls: {
-    privacy: string;
-    termsOfUse: string;
-    imprint: string;
+    privacy: string | null;
+    termsOfUse: string | null;
+    imprint: string | null;
   };
 }
 
 const schema = Joi.object({
   branding: Joi.object({
-    customName: Joi.string().optional().label('HD_CUSTOM_NAME'),
+    customName: Joi.string().allow(null).label('HD_CUSTOM_NAME'),
     customLogo: Joi.string()
       .uri({
         scheme: [/https?/],
       })
-      .optional()
+      .allow(null)
       .label('HD_CUSTOM_LOGO'),
   }),
   specialUrls: Joi.object({
@@ -35,19 +35,19 @@ const schema = Joi.object({
       .uri({
         scheme: /https?/,
       })
-      .optional()
+      .allow(null)
       .label('HD_PRIVACY_URL'),
     termsOfUse: Joi.string()
       .uri({
         scheme: /https?/,
       })
-      .optional()
+      .allow(null)
       .label('HD_TERMS_OF_USE_URL'),
     imprint: Joi.string()
       .uri({
         scheme: /https?/,
       })
-      .optional()
+      .allow(null)
       .label('HD_IMPRINT_URL'),
   }),
 });
@@ -56,13 +56,13 @@ export default registerAs('customizationConfig', () => {
   const customizationConfig = schema.validate(
     {
       branding: {
-        customName: process.env.HD_CUSTOM_NAME,
-        customLogo: process.env.HD_CUSTOM_LOGO,
+        customName: process.env.HD_CUSTOM_NAME ?? null,
+        customLogo: process.env.HD_CUSTOM_LOGO ?? null,
       },
       specialUrls: {
-        privacy: process.env.HD_PRIVACY_URL,
-        termsOfUse: process.env.HD_TERMS_OF_USE_URL,
-        imprint: process.env.HD_IMPRINT_URL,
+        privacy: process.env.HD_PRIVACY_URL ?? null,
+        termsOfUse: process.env.HD_TERMS_OF_USE_URL ?? null,
+        imprint: process.env.HD_IMPRINT_URL ?? null,
       },
     },
     {
diff --git a/backend/src/config/external-services.config.ts b/backend/src/config/external-services.config.ts
index 22d428c37..532e2f7e2 100644
--- a/backend/src/config/external-services.config.ts
+++ b/backend/src/config/external-services.config.ts
@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
@@ -9,7 +9,7 @@ import * as Joi from 'joi';
 import { buildErrorMessage } from './utils';
 
 export interface ExternalServicesConfig {
-  plantUmlServer: string;
+  plantUmlServer: string | null;
   imageProxy: string;
 }
 
@@ -18,7 +18,7 @@ const schema = Joi.object({
     .uri({
       scheme: /https?/,
     })
-    .optional()
+    .allow(null)
     .label('HD_PLANTUML_SERVER'),
   imageProxy: Joi.string()
     .uri({
@@ -36,7 +36,7 @@ export default registerAs('externalServicesConfig', () => {
   }
   const externalConfig = schema.validate(
     {
-      plantUmlServer: process.env.HD_PLANTUML_SERVER,
+      plantUmlServer: process.env.HD_PLANTUML_SERVER ?? null,
       imageProxy: process.env.HD_IMAGE_PROXY,
     },
     {
diff --git a/backend/src/config/mock/note.config.mock.ts b/backend/src/config/mock/note.config.mock.ts
index 45d7e1b7d..e0e3e0e65 100644
--- a/backend/src/config/mock/note.config.mock.ts
+++ b/backend/src/config/mock/note.config.mock.ts
@@ -1,13 +1,13 @@
 /*
- * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { GuestAccess } from '@hedgedoc/commons';
 import { ConfigFactoryKeyHost, registerAs } from '@nestjs/config';
 import { ConfigFactory } from '@nestjs/config/dist/interfaces';
 
 import { DefaultAccessLevel } from '../default-access-level.enum';
-import { GuestAccess } from '../guest_access.enum';
 import { NoteConfig } from '../note.config';
 
 export function createDefaultMockNoteConfig(): NoteConfig {
diff --git a/backend/src/config/note.config.spec.ts b/backend/src/config/note.config.spec.ts
index 2f4930976..38f2d0572 100644
--- a/backend/src/config/note.config.spec.ts
+++ b/backend/src/config/note.config.spec.ts
@@ -1,12 +1,12 @@
 /*
- * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { GuestAccess } from '@hedgedoc/commons';
 import mockedEnv from 'mocked-env';
 
 import { DefaultAccessLevel } from './default-access-level.enum';
-import { GuestAccess } from './guest_access.enum';
 import noteConfig from './note.config';
 
 describe('noteConfig', () => {
diff --git a/backend/src/config/note.config.ts b/backend/src/config/note.config.ts
index c7615e437..58126835a 100644
--- a/backend/src/config/note.config.ts
+++ b/backend/src/config/note.config.ts
@@ -1,8 +1,9 @@
 /*
- * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { GuestAccess } from '@hedgedoc/commons';
 import { registerAs } from '@nestjs/config';
 import * as Joi from 'joi';
 
@@ -10,7 +11,6 @@ import {
   DefaultAccessLevel,
   getDefaultAccessLevelOrdinal,
 } from './default-access-level.enum';
-import { GuestAccess } from './guest_access.enum';
 import { buildErrorMessage, parseOptionalNumber, toArrayConfig } from './utils';
 
 export interface NoteConfig {
diff --git a/backend/src/frontend-config/frontend-config.dto.ts b/backend/src/frontend-config/frontend-config.dto.ts
deleted file mode 100644
index 4805084d9..000000000
--- a/backend/src/frontend-config/frontend-config.dto.ts
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import { Type } from 'class-transformer';
-import {
-  IsArray,
-  IsBoolean,
-  IsNumber,
-  IsOptional,
-  IsString,
-  IsUrl,
-  ValidateNested,
-} from 'class-validator';
-import { URL } from 'url';
-
-import { ProviderType } from '../auth/provider-type.enum';
-import { GuestAccess } from '../config/guest_access.enum';
-import { ServerVersion } from '../monitoring/server-status.dto';
-import { BaseDto } from '../utils/base.dto.';
-
-export type AuthProviderTypeWithCustomName =
-  | ProviderType.LDAP
-  | ProviderType.OIDC;
-
-export type AuthProviderTypeWithoutCustomName = ProviderType.LOCAL;
-
-export class AuthProviderWithoutCustomNameDto extends BaseDto {
-  /**
-   * The type of the auth provider.
-   */
-  @IsString()
-  @Type(() => String)
-  type: AuthProviderTypeWithoutCustomName;
-}
-
-export class AuthProviderWithCustomNameDto extends BaseDto {
-  /**
-   * The type of the auth provider.
-   */
-  @IsString()
-  @Type(() => String)
-  type: AuthProviderTypeWithCustomName;
-
-  /**
-   * The identifier with which the auth provider can be called
-   * @example gitlab-fsorg
-   */
-  @IsString()
-  identifier: string;
-
-  /**
-   * The name given to the auth provider
-   * @example GitLab fachschaften.org
-   */
-  @IsString()
-  providerName: string;
-
-  /**
-   * The theme to apply for the login button.
-   * @example gitlab
-   */
-  @IsOptional()
-  @IsString()
-  theme?: string;
-}
-
-export type AuthProviderDto =
-  | AuthProviderWithCustomNameDto
-  | AuthProviderWithoutCustomNameDto;
-
-export class BrandingDto extends BaseDto {
-  /**
-   * The name to be displayed next to the HedgeDoc logo
-   * @example ACME Corp
-   */
-  @IsString()
-  @IsOptional()
-  name?: string;
-
-  /**
-   * The logo to be displayed next to the HedgeDoc logo
-   * @example https://md.example.com/logo.png
-   */
-  @IsUrl()
-  @IsOptional()
-  @Type(() => URL)
-  logo?: URL;
-}
-
-export class SpecialUrlsDto extends BaseDto {
-  /**
-   * A link to the privacy notice
-   * @example https://md.example.com/n/privacy
-   */
-  @IsUrl()
-  @IsOptional()
-  @Type(() => URL)
-  privacy?: URL;
-
-  /**
-   * A link to the terms of use
-   * @example https://md.example.com/n/termsOfUse
-   */
-  @IsUrl()
-  @IsOptional()
-  @Type(() => URL)
-  termsOfUse?: URL;
-
-  /**
-   * A link to the imprint
-   * @example https://md.example.com/n/imprint
-   */
-  @IsUrl()
-  @IsOptional()
-  @Type(() => URL)
-  imprint?: URL;
-}
-
-export class FrontendConfigDto extends BaseDto {
-  /**
-   * Maximum access level for guest users
-   */
-  @IsString()
-  guestAccess: GuestAccess;
-
-  /**
-   * Are users allowed to register on this instance?
-   */
-  @IsBoolean()
-  allowRegister: boolean;
-
-  /**
-   * Are users allowed to edit their profile information?
-   */
-  @IsBoolean()
-  allowProfileEdits: boolean;
-
-  /**
-   * Are users allowed to choose their username when signing up via OIDC?
-   */
-  @IsBoolean()
-  allowChooseUsername: boolean;
-
-  /**
-   * Which auth providers are enabled and how are they configured?
-   */
-  // eslint-disable-next-line @darraghor/nestjs-typed/validated-non-primitive-property-needs-type-decorator
-  @IsArray()
-  @ValidateNested({ each: true })
-  authProviders: AuthProviderDto[];
-
-  /**
-   * Individual branding information
-   */
-  @ValidateNested()
-  @Type(() => BrandingDto)
-  branding: BrandingDto;
-
-  /**
-   * Is an image proxy enabled?
-   */
-  @IsBoolean()
-  useImageProxy: boolean;
-
-  /**
-   * Links to some special pages
-   */
-  @ValidateNested()
-  @Type(() => SpecialUrlsDto)
-  specialUrls: SpecialUrlsDto;
-
-  /**
-   * The version of HedgeDoc
-   */
-  @ValidateNested()
-  @Type(() => ServerVersion)
-  version: ServerVersion;
-
-  /**
-   * The plantUML server that should be used to render.
-   */
-  @IsUrl()
-  @IsOptional()
-  @Type(() => URL)
-  plantUmlServer?: URL;
-
-  /**
-   * The maximal length of each document
-   */
-  @IsNumber()
-  maxDocumentLength: number;
-}
diff --git a/backend/src/frontend-config/frontend-config.service.spec.ts b/backend/src/frontend-config/frontend-config.service.spec.ts
index dec298439..01bb5a34c 100644
--- a/backend/src/frontend-config/frontend-config.service.spec.ts
+++ b/backend/src/frontend-config/frontend-config.service.spec.ts
@@ -1,19 +1,18 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { GuestAccess, ProviderType } from '@hedgedoc/commons';
 import { ConfigModule, registerAs } from '@nestjs/config';
 import { Test, TestingModule } from '@nestjs/testing';
 import { URL } from 'url';
 
-import { ProviderType } from '../auth/provider-type.enum';
 import { AppConfig } from '../config/app.config';
 import { AuthConfig } from '../config/auth.config';
 import { CustomizationConfig } from '../config/customization.config';
 import { DefaultAccessLevel } from '../config/default-access-level.enum';
 import { ExternalServicesConfig } from '../config/external-services.config';
-import { GuestAccess } from '../config/guest_access.enum';
 import { Loglevel } from '../config/loglevel.enum';
 import { NoteConfig } from '../config/note.config';
 import { LoggerModule } from '../logger/logger.module';
@@ -174,14 +173,11 @@ describe('FrontendConfigService', () => {
   const customName = 'Test Branding Name';
 
   let index = 1;
-  for (const customLogo of [undefined, 'https://example.com/logo.png']) {
-    for (const privacyLink of [undefined, 'https://example.com/privacy']) {
-      for (const termsOfUseLink of [undefined, 'https://example.com/terms']) {
-        for (const imprintLink of [undefined, 'https://example.com/imprint']) {
-          for (const plantUmlServer of [
-            undefined,
-            'https://plantuml.example.com',
-          ]) {
+  for (const customLogo of [null, 'https://example.com/logo.png']) {
+    for (const privacyLink of [null, 'https://example.com/privacy']) {
+      for (const termsOfUseLink of [null, 'https://example.com/terms']) {
+        for (const imprintLink of [null, 'https://example.com/imprint']) {
+          for (const plantUmlServer of [null, 'https://plantuml.example.com']) {
             it(`combination #${index} works`, async () => {
               const appConfig: AppConfig = {
                 baseUrl: domain,
@@ -255,20 +251,24 @@ describe('FrontendConfigService', () => {
               expect(config.guestAccess).toEqual(noteConfig.guestAccess);
               expect(config.branding.name).toEqual(customName);
               expect(config.branding.logo).toEqual(
-                customLogo ? new URL(customLogo) : undefined,
+                customLogo !== null ? new URL(customLogo).toString() : null,
               );
               expect(config.maxDocumentLength).toEqual(maxDocumentLength);
               expect(config.plantUmlServer).toEqual(
-                plantUmlServer ? new URL(plantUmlServer) : undefined,
+                plantUmlServer !== null
+                  ? new URL(plantUmlServer).toString()
+                  : null,
               );
               expect(config.specialUrls.imprint).toEqual(
-                imprintLink ? new URL(imprintLink) : undefined,
+                imprintLink !== null ? new URL(imprintLink).toString() : null,
               );
               expect(config.specialUrls.privacy).toEqual(
-                privacyLink ? new URL(privacyLink) : undefined,
+                privacyLink !== null ? new URL(privacyLink).toString() : null,
               );
               expect(config.specialUrls.termsOfUse).toEqual(
-                termsOfUseLink ? new URL(termsOfUseLink) : undefined,
+                termsOfUseLink !== null
+                  ? new URL(termsOfUseLink).toString()
+                  : null,
               );
               expect(config.useImageProxy).toEqual(!!imageProxy);
               expect(config.version).toEqual(
diff --git a/backend/src/frontend-config/frontend-config.service.ts b/backend/src/frontend-config/frontend-config.service.ts
index 5242c4ed9..033853cbb 100644
--- a/backend/src/frontend-config/frontend-config.service.ts
+++ b/backend/src/frontend-config/frontend-config.service.ts
@@ -1,12 +1,18 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import {
+  BrandingDto,
+  FrontendConfigDto,
+  ProviderType,
+  SpecialUrlDto,
+} from '@hedgedoc/commons';
+import { AuthProviderDto } from '@hedgedoc/commons';
 import { Inject, Injectable } from '@nestjs/common';
 import { URL } from 'url';
 
-import { ProviderType } from '../auth/provider-type.enum';
 import appConfiguration, { AppConfig } from '../config/app.config';
 import authConfiguration, { AuthConfig } from '../config/auth.config';
 import customizationConfiguration, {
@@ -18,12 +24,6 @@ import externalServicesConfiguration, {
 import noteConfiguration, { NoteConfig } from '../config/note.config';
 import { ConsoleLoggerService } from '../logger/console-logger.service';
 import { getServerVersionFromPackageJson } from '../utils/serverVersion';
-import {
-  AuthProviderDto,
-  BrandingDto,
-  FrontendConfigDto,
-  SpecialUrlsDto,
-} from './frontend-config.dto';
 
 @Injectable()
 export class FrontendConfigService {
@@ -53,8 +53,8 @@ export class FrontendConfigService {
       branding: this.getBranding(),
       maxDocumentLength: this.noteConfig.maxDocumentLength,
       plantUmlServer: this.externalServicesConfig.plantUmlServer
-        ? new URL(this.externalServicesConfig.plantUmlServer)
-        : undefined,
+        ? new URL(this.externalServicesConfig.plantUmlServer).toString()
+        : null,
       specialUrls: this.getSpecialUrls(),
       useImageProxy: !!this.externalServicesConfig.imageProxy,
       version: await getServerVersionFromPackageJson(),
@@ -73,6 +73,7 @@ export class FrontendConfigService {
         type: ProviderType.LDAP,
         providerName: ldapEntry.providerName,
         identifier: ldapEntry.identifier,
+        theme: null,
       });
     });
     this.authConfig.oidc.forEach((openidConnectEntry) => {
@@ -80,7 +81,7 @@ export class FrontendConfigService {
         type: ProviderType.OIDC,
         providerName: openidConnectEntry.providerName,
         identifier: openidConnectEntry.identifier,
-        theme: openidConnectEntry.theme,
+        theme: openidConnectEntry.theme ?? null,
       });
     });
     return providers;
@@ -89,23 +90,23 @@ export class FrontendConfigService {
   private getBranding(): BrandingDto {
     return {
       logo: this.customizationConfig.branding.customLogo
-        ? new URL(this.customizationConfig.branding.customLogo)
-        : undefined,
+        ? new URL(this.customizationConfig.branding.customLogo).toString()
+        : null,
       name: this.customizationConfig.branding.customName,
     };
   }
 
-  private getSpecialUrls(): SpecialUrlsDto {
+  private getSpecialUrls(): SpecialUrlDto {
     return {
       imprint: this.customizationConfig.specialUrls.imprint
-        ? new URL(this.customizationConfig.specialUrls.imprint)
-        : undefined,
+        ? new URL(this.customizationConfig.specialUrls.imprint).toString()
+        : null,
       privacy: this.customizationConfig.specialUrls.privacy
-        ? new URL(this.customizationConfig.specialUrls.privacy)
-        : undefined,
+        ? new URL(this.customizationConfig.specialUrls.privacy).toString()
+        : null,
       termsOfUse: this.customizationConfig.specialUrls.termsOfUse
-        ? new URL(this.customizationConfig.specialUrls.termsOfUse)
-        : undefined,
+        ? new URL(this.customizationConfig.specialUrls.termsOfUse).toString()
+        : null,
     };
   }
 }
diff --git a/backend/src/groups/group-info.dto.ts b/backend/src/groups/group-info.dto.ts
deleted file mode 100644
index f2ea39672..000000000
--- a/backend/src/groups/group-info.dto.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import { ApiProperty } from '@nestjs/swagger';
-import { IsBoolean, IsString } from 'class-validator';
-
-import { BaseDto } from '../utils/base.dto.';
-
-export class GroupInfoDto extends BaseDto {
-  /**
-   * Name of the group
-   * @example "superheroes"
-   */
-  @IsString()
-  @ApiProperty()
-  name: string;
-
-  /**
-   * Display name of this group
-   * @example "Superheroes"
-   */
-  @IsString()
-  @ApiProperty()
-  displayName: string;
-
-  /**
-   * True if this group must be specially handled
-   * Used for e.g. "everybody", "all logged in users"
-   * @example false
-   */
-  @IsBoolean()
-  @ApiProperty()
-  special: boolean;
-}
diff --git a/backend/src/groups/groups.service.ts b/backend/src/groups/groups.service.ts
index 5b15dea9b..08c1d3751 100644
--- a/backend/src/groups/groups.service.ts
+++ b/backend/src/groups/groups.service.ts
@@ -1,15 +1,15 @@
 /*
- * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { GroupInfoDto } from '@hedgedoc/commons';
 import { Injectable } from '@nestjs/common';
 import { InjectRepository } from '@nestjs/typeorm';
 import { Repository } from 'typeorm';
 
 import { AlreadyInDBError, NotInDBError } from '../errors/errors';
 import { ConsoleLoggerService } from '../logger/console-logger.service';
-import { GroupInfoDto } from './group-info.dto';
 import { Group } from './group.entity';
 import { SpecialGroup } from './groups.special';
 
diff --git a/backend/src/media/media-upload.dto.ts b/backend/src/media/media-upload.dto.ts
deleted file mode 100644
index 37387ec4d..000000000
--- a/backend/src/media/media-upload.dto.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import { ApiProperty } from '@nestjs/swagger';
-import { Type } from 'class-transformer';
-import { IsDate, IsLowercase, IsOptional, IsString } from 'class-validator';
-
-import { BaseDto } from '../utils/base.dto.';
-import { Username } from '../utils/username';
-
-export class MediaUploadDto extends BaseDto {
-  /**
-   * The uuid of the media file.
-   * @example "7697582e-0020-4188-9758-2e00207188ca"
-   */
-  @IsString()
-  @ApiProperty()
-  uuid: string;
-
-  /**
-   * The original filename of the media upload.
-   * @example "example.png"
-   */
-  @IsString()
-  @ApiProperty()
-  fileName: string;
-
-  /**
-   * The publicId of the note to which the uploaded file is linked to.
-   * @example "b604x5885k9k01bq7tsmawvnp0"
-   */
-  @IsString()
-  @IsOptional()
-  @ApiProperty()
-  noteId: string | null;
-
-  /**
-   * The date when the upload objects was created.
-   * @example "2020-12-01 12:23:34"
-   */
-  @IsDate()
-  @Type(() => Date)
-  @ApiProperty()
-  createdAt: Date;
-
-  /**
-   * The username of the user which uploaded the media file.
-   * @example "testuser5"
-   */
-  @IsString()
-  @IsLowercase()
-  @IsOptional()
-  @ApiProperty()
-  username: Username | null;
-}
diff --git a/backend/src/media/media.service.ts b/backend/src/media/media.service.ts
index ef3d7efcf..0a8396a2a 100644
--- a/backend/src/media/media.service.ts
+++ b/backend/src/media/media.service.ts
@@ -1,8 +1,9 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { MediaUploadDto } from '@hedgedoc/commons';
 import { Inject, Injectable } from '@nestjs/common';
 import { ModuleRef } from '@nestjs/core';
 import { InjectRepository } from '@nestjs/typeorm';
@@ -22,7 +23,6 @@ import { ImgurBackend } from './backends/imgur-backend';
 import { S3Backend } from './backends/s3-backend';
 import { WebdavBackend } from './backends/webdav-backend';
 import { MediaBackend } from './media-backend.interface';
-import { MediaUploadDto } from './media-upload.dto';
 import { MediaUpload } from './media-upload.entity';
 
 @Injectable()
@@ -268,7 +268,7 @@ export class MediaService {
       uuid: mediaUpload.uuid,
       fileName: mediaUpload.fileName,
       noteId: (await mediaUpload.note)?.publicId ?? null,
-      createdAt: mediaUpload.createdAt,
+      createdAt: mediaUpload.createdAt.toISOString(),
       username: user?.username ?? null,
     };
   }
diff --git a/backend/src/monitoring/monitoring.service.ts b/backend/src/monitoring/monitoring.service.ts
index ff169dcb7..dcf097623 100644
--- a/backend/src/monitoring/monitoring.service.ts
+++ b/backend/src/monitoring/monitoring.service.ts
@@ -1,12 +1,12 @@
 /*
- * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { ServerStatusDto } from '@hedgedoc/commons';
 import { Injectable } from '@nestjs/common';
 
 import { getServerVersionFromPackageJson } from '../utils/serverVersion';
-import { ServerStatusDto } from './server-status.dto';
 
 @Injectable()
 export class MonitoringService {
diff --git a/backend/src/monitoring/server-status.dto.ts b/backend/src/monitoring/server-status.dto.ts
deleted file mode 100644
index af20ec836..000000000
--- a/backend/src/monitoring/server-status.dto.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
-
-import { BaseDto } from '../utils/base.dto.';
-
-export class ServerVersion {
-  @ApiProperty()
-  major: number;
-  @ApiProperty()
-  minor: number;
-  @ApiProperty()
-  patch: number;
-  @ApiPropertyOptional()
-  preRelease?: string;
-  @ApiPropertyOptional()
-  commit?: string;
-  @ApiProperty()
-  fullString: string;
-}
-
-export class ServerStatusDto extends BaseDto {
-  @ApiProperty()
-  serverVersion: ServerVersion;
-  @ApiProperty()
-  onlineNotes: number;
-  @ApiProperty()
-  onlineUsers: number;
-  @ApiProperty()
-  distinctOnlineUsers: number;
-  @ApiProperty()
-  notesCount: number;
-  @ApiProperty()
-  registeredUsers: number;
-  @ApiProperty()
-  onlineRegisteredUsers: number;
-  @ApiProperty()
-  distinctOnlineRegisteredUsers: number;
-  @ApiProperty()
-  isConnectionBusy: boolean;
-  @ApiProperty()
-  connectionSocketQueueLength: number;
-  @ApiProperty()
-  isDisconnectBusy: boolean;
-  @ApiProperty()
-  disconnectSocketQueueLength: number;
-}
diff --git a/backend/src/notes/__snapshots__/notes.service.spec.ts.snap b/backend/src/notes/__snapshots__/notes.service.spec.ts.snap
index d0d375454..f8c4c6036 100644
--- a/backend/src/notes/__snapshots__/notes.service.spec.ts.snap
+++ b/backend/src/notes/__snapshots__/notes.service.spec.ts.snap
@@ -12,7 +12,7 @@ exports[`NotesService toNoteDto works 1`] = `
         "primaryAlias": true,
       },
     ],
-    "createdAt": undefined,
+    "createdAt": "2019-02-04T20:34:12.000Z",
     "description": "mockDescription",
     "editedBy": [
       "hardcoded",
@@ -39,7 +39,7 @@ exports[`NotesService toNoteDto works 1`] = `
     ],
     "title": "mockTitle",
     "updateUsername": "hardcoded",
-    "updatedAt": 2019-02-04T20:34:12.000Z,
+    "updatedAt": "2019-02-04T20:34:12.000Z",
     "version": undefined,
     "viewCount": 1337,
   },
@@ -55,7 +55,7 @@ exports[`NotesService toNoteMetadataDto works 1`] = `
       "primaryAlias": true,
     },
   ],
-  "createdAt": undefined,
+  "createdAt": "2019-02-04T20:34:12.000Z",
   "description": "mockDescription",
   "editedBy": [
     "hardcoded",
@@ -82,7 +82,7 @@ exports[`NotesService toNoteMetadataDto works 1`] = `
   ],
   "title": "mockTitle",
   "updateUsername": "hardcoded",
-  "updatedAt": 2019-02-04T20:34:12.000Z,
+  "updatedAt": "2019-02-04T20:34:12.000Z",
   "version": undefined,
   "viewCount": 1337,
 }
diff --git a/backend/src/notes/alias-create.dto.ts b/backend/src/notes/alias-create.dto.ts
deleted file mode 100644
index 6c04f4604..000000000
--- a/backend/src/notes/alias-create.dto.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import { ApiProperty } from '@nestjs/swagger';
-import { IsString } from 'class-validator';
-
-import { BaseDto } from '../utils/base.dto.';
-
-export class AliasCreateDto extends BaseDto {
-  /**
-   * The note id or alias, which identifies the note the alias should be added to
-   */
-  @IsString()
-  @ApiProperty()
-  noteIdOrAlias: string;
-
-  /**
-   * The new alias
-   */
-  @IsString()
-  @ApiProperty()
-  newAlias: string;
-}
diff --git a/backend/src/notes/alias-update.dto.ts b/backend/src/notes/alias-update.dto.ts
deleted file mode 100644
index 6ce7bf330..000000000
--- a/backend/src/notes/alias-update.dto.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import { ApiProperty } from '@nestjs/swagger';
-import { IsBoolean } from 'class-validator';
-
-import { BaseDto } from '../utils/base.dto.';
-
-export class AliasUpdateDto extends BaseDto {
-  /**
-   * Whether the alias should become the primary alias or not
-   */
-  @IsBoolean()
-  @ApiProperty()
-  primaryAlias: boolean;
-}
diff --git a/backend/src/notes/alias.dto.ts b/backend/src/notes/alias.dto.ts
deleted file mode 100644
index 1f5c2af29..000000000
--- a/backend/src/notes/alias.dto.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import { ApiProperty } from '@nestjs/swagger';
-import { IsBoolean, IsString } from 'class-validator';
-
-import { BaseDto } from '../utils/base.dto.';
-
-export class AliasDto extends BaseDto {
-  /**
-   * The name of the alias
-   */
-  @IsString()
-  @ApiProperty()
-  name: string;
-
-  /**
-   * Is the alias the primary alias or not
-   */
-  @IsBoolean()
-  @ApiProperty()
-  primaryAlias: boolean;
-
-  /**
-   * The public id of the note the alias is associated with
-   */
-  @IsString()
-  @ApiProperty()
-  noteId: string;
-}
diff --git a/backend/src/notes/alias.service.ts b/backend/src/notes/alias.service.ts
index 2b2a7e055..85bcdb963 100644
--- a/backend/src/notes/alias.service.ts
+++ b/backend/src/notes/alias.service.ts
@@ -1,8 +1,9 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { AliasDto } from '@hedgedoc/commons';
 import { forwardRef, Inject, Injectable } from '@nestjs/common';
 import { InjectRepository } from '@nestjs/typeorm';
 import { Repository } from 'typeorm';
@@ -12,7 +13,6 @@ import {
   PrimaryAliasDeletionForbiddenError,
 } from '../errors/errors';
 import { ConsoleLoggerService } from '../logger/console-logger.service';
-import { AliasDto } from './alias.dto';
 import { Alias } from './alias.entity';
 import { Note } from './note.entity';
 import { NotesService } from './notes.service';
diff --git a/backend/src/notes/note-metadata.dto.ts b/backend/src/notes/note-metadata.dto.ts
deleted file mode 100644
index cbb6a5e78..000000000
--- a/backend/src/notes/note-metadata.dto.ts
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
-import { Type } from 'class-transformer';
-import {
-  IsArray,
-  IsDate,
-  IsNumber,
-  IsOptional,
-  IsString,
-  ValidateNested,
-} from 'class-validator';
-
-import { BaseDto } from '../utils/base.dto.';
-import { AliasDto } from './alias.dto';
-import { NotePermissionsDto } from './note-permissions.dto';
-
-export class NoteMetadataDto extends BaseDto {
-  /**
-   * ID of the note
-   */
-  @IsString()
-  @ApiProperty()
-  id: string;
-
-  /**
-   * All aliases of the note (including the primaryAlias)
-   */
-  @Type(() => AliasDto)
-  @IsArray()
-  @ValidateNested({ each: true })
-  @ApiProperty({ isArray: true, type: AliasDto })
-  aliases: AliasDto[];
-
-  /**
-   * The primary adress of the note
-   * If at least one alias is set, this is the primary alias
-   * If no alias is set, this is the note's ID
-   */
-  @IsString()
-  @ApiProperty()
-  primaryAddress: string;
-
-  /**
-   * Title of the note
-   * Does not contain any markup but might be empty
-   * @example "Shopping List"
-   */
-  @IsString()
-  @ApiProperty()
-  title: string;
-
-  /**
-   * Description of the note
-   * Does not contain any markup but might be empty
-   * @example Everything I want to buy
-   */
-  @IsString()
-  @ApiProperty()
-  description: string;
-
-  /**
-   * List of tags assigned to this note
-   * @example "['shopping', 'personal']
-   */
-  @IsArray()
-  @IsString({ each: true })
-  @ApiProperty({ isArray: true, type: String })
-  tags: string[];
-
-  @IsNumber()
-  @ApiProperty()
-  version: number;
-
-  /**
-   * Datestring of the last time this note was updated
-   * @example "2020-12-01 12:23:34"
-   */
-  @IsDate()
-  @Type(() => Date)
-  @ApiProperty()
-  updatedAt: Date;
-
-  /**
-   * User that last edited the note
-   */
-  // eslint-disable-next-line @darraghor/nestjs-typed/api-property-matches-property-optionality
-  @ApiPropertyOptional()
-  @IsString()
-  @IsOptional()
-  updateUsername: string | null;
-
-  /**
-   * Counts how many times the published note has been viewed
-   * @example 42
-   */
-  @IsNumber()
-  @ApiProperty()
-  viewCount: number;
-
-  /**
-   * Datestring of the time this note was created
-   * @example "2020-12-01 12:23:34"
-   */
-  @IsDate()
-  @Type(() => Date)
-  @ApiProperty()
-  createdAt: Date;
-
-  /**
-   * List of usernames that edited the note
-   * @example "['john.smith', 'jane.smith']"
-   */
-  @IsArray()
-  @IsString({ each: true })
-  @ApiProperty({ isArray: true, type: String })
-  editedBy: string[];
-
-  /**
-   * Permissions currently in effect for the note
-   */
-  @ValidateNested()
-  @Type(() => NotePermissionsDto)
-  @ApiProperty({ type: NotePermissionsDto })
-  permissions: NotePermissionsDto;
-}
-
-export class NoteMetadataUpdateDto {
-  /**
-   * New title of the note
-   * Can not contain any markup and might be empty
-   * @example "Shopping List"
-   */
-  @IsString()
-  @ApiProperty()
-  title: string;
-
-  /**
-   * New description of the note
-   * Can not contain any markup but might be empty
-   * @example Everything I want to buy
-   */
-  @IsString()
-  @ApiProperty()
-  description: string;
-
-  /**
-   * New list of tags assigned to this note
-   * @example "['shopping', 'personal']
-   */
-  @IsArray()
-  @IsString({ each: true })
-  @ApiProperty({ isArray: true, type: String })
-  tags: string[];
-}
diff --git a/backend/src/notes/note-permissions.dto.ts b/backend/src/notes/note-permissions.dto.ts
deleted file mode 100644
index b462d79df..000000000
--- a/backend/src/notes/note-permissions.dto.ts
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
-import { Type } from 'class-transformer';
-import {
-  IsArray,
-  IsBoolean,
-  IsLowercase,
-  IsOptional,
-  IsString,
-  ValidateNested,
-} from 'class-validator';
-
-import { BaseDto } from '../utils/base.dto.';
-import { Username } from '../utils/username';
-
-export class NoteUserPermissionEntryDto extends BaseDto {
-  /**
-   * Username of the User this permission applies to
-   */
-  @Type(() => String)
-  @IsString()
-  @IsLowercase()
-  @ApiProperty()
-  username: Username;
-
-  /**
-   * True if the user is allowed to edit the note
-   * @example false
-   */
-  @IsBoolean()
-  @ApiProperty()
-  canEdit: boolean;
-}
-
-export class NoteUserPermissionUpdateDto {
-  /**
-   * Username of the user this permission should apply to
-   * @example "john.smith"
-   */
-  @Type(() => String)
-  @IsString()
-  @IsLowercase()
-  @ApiProperty()
-  username: Username;
-
-  /**
-   * True if the user should be allowed to edit the note
-   * @example false
-   */
-  @IsBoolean()
-  @ApiProperty()
-  canEdit: boolean;
-}
-
-export class NoteGroupPermissionEntryDto {
-  /**
-   * Name of the Group this permission applies to
-   */
-  @IsString()
-  @ApiProperty()
-  groupName: string;
-
-  /**
-   * True if the group members are allowed to edit the note
-   * @example false
-   */
-  @IsBoolean()
-  @ApiProperty()
-  canEdit: boolean;
-}
-
-export class NoteGroupPermissionUpdateDto {
-  /**
-   * Name of the group this permission should apply to
-   * @example "superheroes"
-   */
-  @IsString()
-  @ApiProperty()
-  groupName: string;
-
-  /**
-   * True if the group members should be allowed to edit the note
-   * @example false
-   */
-  @IsBoolean()
-  @ApiProperty()
-  canEdit: boolean;
-}
-
-export class NotePermissionsDto {
-  /**
-   * Username of the User this permission applies to
-   */
-  // nestjs-typed does not detect '| null' types as optional
-  // eslint-disable-next-line @darraghor/nestjs-typed/api-property-matches-property-optionality
-  @Type(() => String)
-  @IsString()
-  @ApiPropertyOptional()
-  @IsOptional()
-  owner: string | null;
-
-  /**
-   * List of users the note is shared with
-   */
-  @ValidateNested({ each: true })
-  @IsArray()
-  @Type(() => NoteUserPermissionEntryDto)
-  @ApiProperty({ isArray: true, type: NoteUserPermissionEntryDto })
-  sharedToUsers: NoteUserPermissionEntryDto[];
-
-  /**
-   * List of groups the note is shared with
-   */
-  @ValidateNested({ each: true })
-  @IsArray()
-  @Type(() => NoteGroupPermissionEntryDto)
-  @ApiProperty({ isArray: true, type: NoteGroupPermissionEntryDto })
-  sharedToGroups: NoteGroupPermissionEntryDto[];
-}
-
-export class NotePermissionsUpdateDto {
-  /**
-   * List of users the note should be shared with
-   */
-  @IsArray()
-  @ValidateNested({ each: true })
-  @Type(() => NoteUserPermissionUpdateDto)
-  @ApiProperty({ isArray: true, type: NoteUserPermissionUpdateDto })
-  sharedToUsers: NoteUserPermissionUpdateDto[];
-
-  /**
-   * List of groups the note should be shared with
-   */
-  @IsArray()
-  @ValidateNested({ each: true })
-  @Type(() => NoteGroupPermissionUpdateDto)
-  @ApiProperty({ isArray: true, type: NoteGroupPermissionUpdateDto })
-  sharedToGroups: NoteGroupPermissionUpdateDto[];
-}
diff --git a/backend/src/notes/note.dto.ts b/backend/src/notes/note.dto.ts
deleted file mode 100644
index deff740f4..000000000
--- a/backend/src/notes/note.dto.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import { ApiProperty } from '@nestjs/swagger';
-import { Type } from 'class-transformer';
-import { IsArray, IsString, ValidateNested } from 'class-validator';
-
-import { EditDto } from '../revisions/edit.dto';
-import { BaseDto } from '../utils/base.dto.';
-import { NoteMetadataDto } from './note-metadata.dto';
-
-export class NoteDto extends BaseDto {
-  /**
-   * Markdown content of the note
-   * @example "# I am a heading"
-   */
-  @IsString()
-  @ApiProperty()
-  content: string;
-
-  /**
-   * Metadata of the note
-   */
-  @Type(() => NoteMetadataDto)
-  @ValidateNested()
-  @ApiProperty({ type: NoteMetadataDto })
-  metadata: NoteMetadataDto;
-
-  /**
-   * Edit information of this note
-   */
-  @IsArray()
-  @ValidateNested({ each: true })
-  @Type(() => EditDto)
-  @ApiProperty({ isArray: true, type: EditDto })
-  editedByAtPosition: EditDto[];
-}
diff --git a/backend/src/notes/note.media-deletion.dto.ts b/backend/src/notes/note.media-deletion.dto.ts
deleted file mode 100644
index 1d8eaa184..000000000
--- a/backend/src/notes/note.media-deletion.dto.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import { ApiProperty } from '@nestjs/swagger';
-import { IsBoolean } from 'class-validator';
-
-import { BaseDto } from '../utils/base.dto.';
-
-export class NoteMediaDeletionDto extends BaseDto {
-  /**
-   * Should the associated mediaUploads be keept
-   * @default false
-   * @example false
-   */
-  @IsBoolean()
-  @ApiProperty()
-  keepMedia: boolean;
-}
diff --git a/backend/src/notes/notes.service.spec.ts b/backend/src/notes/notes.service.spec.ts
index 7159430a7..f71ad0337 100644
--- a/backend/src/notes/notes.service.spec.ts
+++ b/backend/src/notes/notes.service.spec.ts
@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
@@ -271,6 +271,7 @@ describe('NotesService', () => {
     const note = Mock.of<Note>({
       revisions: Promise.resolve([revision]),
       aliases: Promise.resolve([]),
+      createdAt: new Date(1549312452000),
     });
 
     mockRevisionService(note, revision);
diff --git a/backend/src/notes/notes.service.ts b/backend/src/notes/notes.service.ts
index 0bc8e3691..fdd366d7c 100644
--- a/backend/src/notes/notes.service.ts
+++ b/backend/src/notes/notes.service.ts
@@ -1,8 +1,13 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import {
+  NoteDto,
+  NoteMetadataDto,
+  NotePermissionsDto,
+} from '@hedgedoc/commons';
 import { Optional } from '@mrdrogdrog/optional';
 import { forwardRef, Inject, Injectable } from '@nestjs/common';
 import { EventEmitter2 } from '@nestjs/event-emitter';
@@ -30,9 +35,6 @@ import { User } from '../users/user.entity';
 import { UsersService } from '../users/users.service';
 import { Alias } from './alias.entity';
 import { AliasService } from './alias.service';
-import { NoteMetadataDto } from './note-metadata.dto';
-import { NotePermissionsDto } from './note-permissions.dto';
-import { NoteDto } from './note.dto';
 import { Note } from './note.entity';
 import { Tag } from './tag.entity';
 import { getPrimaryAlias } from './utils';
@@ -427,11 +429,11 @@ export class NotesService {
       title: latestRevision.title,
       description: latestRevision.description,
       tags: (await latestRevision.tags).map((tag) => tag.name),
-      createdAt: note.createdAt,
+      createdAt: note.createdAt.toISOString(),
       editedBy: (await this.getAuthorUsers(note)).map((user) => user.username),
       permissions: await this.toNotePermissionsDto(note),
       version: note.version,
-      updatedAt: latestRevision.createdAt,
+      updatedAt: latestRevision.createdAt.toISOString(),
       updateUsername: updateUser ? updateUser.username : null,
       viewCount: note.viewCount,
     };
diff --git a/backend/src/permissions/permissions.service.spec.ts b/backend/src/permissions/permissions.service.spec.ts
index 5e20804d5..c07138bc9 100644
--- a/backend/src/permissions/permissions.service.spec.ts
+++ b/backend/src/permissions/permissions.service.spec.ts
@@ -1,8 +1,13 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import {
+  GuestAccess,
+  NoteGroupPermissionUpdateDto,
+  NoteUserPermissionUpdateDto,
+} from '@hedgedoc/commons';
 import { ConfigModule } from '@nestjs/config';
 import { EventEmitter2, EventEmitterModule } from '@nestjs/event-emitter';
 import { Test, TestingModule } from '@nestjs/testing';
@@ -14,7 +19,6 @@ import { ApiToken } from '../api-token/api-token.entity';
 import { Identity } from '../auth/identity.entity';
 import { Author } from '../authors/author.entity';
 import { DefaultAccessLevel } from '../config/default-access-level.enum';
-import { GuestAccess } from '../config/guest_access.enum';
 import appConfigMock from '../config/mock/app.config.mock';
 import authConfigMock from '../config/mock/auth.config.mock';
 import databaseConfigMock from '../config/mock/database.config.mock';
@@ -31,10 +35,6 @@ import { GroupsService } from '../groups/groups.service';
 import { LoggerModule } from '../logger/logger.module';
 import { MediaUpload } from '../media/media-upload.entity';
 import { Alias } from '../notes/alias.entity';
-import {
-  NoteGroupPermissionUpdateDto,
-  NoteUserPermissionUpdateDto,
-} from '../notes/note-permissions.dto';
 import { Note } from '../notes/note.entity';
 import { NotesModule } from '../notes/notes.module';
 import { Tag } from '../notes/tag.entity';
@@ -461,12 +461,15 @@ describe('PermissionsService', () => {
   });
 
   describe('updateNotePermissions', () => {
-    const userPermissionUpdate = new NoteUserPermissionUpdateDto();
-    userPermissionUpdate.username = 'hardcoded';
-    userPermissionUpdate.canEdit = true;
-    const groupPermissionUpdate = new NoteGroupPermissionUpdateDto();
-    groupPermissionUpdate.groupName = 'testGroup';
-    groupPermissionUpdate.canEdit = false;
+    const userPermissionUpdate: NoteUserPermissionUpdateDto = {
+      username: 'hardcoded',
+      canEdit: true,
+    };
+
+    const groupPermissionUpdate: NoteGroupPermissionUpdateDto = {
+      groupName: 'testGroup',
+      canEdit: false,
+    };
     const user = User.create(userPermissionUpdate.username, 'Testy') as User;
     const group = Group.create(
       groupPermissionUpdate.groupName,
diff --git a/backend/src/permissions/permissions.service.ts b/backend/src/permissions/permissions.service.ts
index f2f7a79a5..386872a32 100644
--- a/backend/src/permissions/permissions.service.ts
+++ b/backend/src/permissions/permissions.service.ts
@@ -1,14 +1,14 @@
 /*
- * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { GuestAccess, NotePermissionsUpdateDto } from '@hedgedoc/commons';
 import { Inject, Injectable } from '@nestjs/common';
 import { EventEmitter2 } from '@nestjs/event-emitter';
 import { InjectRepository } from '@nestjs/typeorm';
 import { Repository } from 'typeorm';
 
-import { GuestAccess } from '../config/guest_access.enum';
 import noteConfiguration, { NoteConfig } from '../config/note.config';
 import { PermissionsUpdateInconsistentError } from '../errors/errors';
 import { NoteEvent, NoteEventMap } from '../events';
@@ -16,7 +16,6 @@ import { Group } from '../groups/group.entity';
 import { GroupsService } from '../groups/groups.service';
 import { ConsoleLoggerService } from '../logger/console-logger.service';
 import { MediaUpload } from '../media/media-upload.entity';
-import { NotePermissionsUpdateDto } from '../notes/note-permissions.dto';
 import { Note } from '../notes/note.entity';
 import { User } from '../users/user.entity';
 import { UsersService } from '../users/users.service';
diff --git a/backend/src/permissions/utils/convert-guest-access-to-note-permission.spec.ts b/backend/src/permissions/utils/convert-guest-access-to-note-permission.spec.ts
index 04bef2ef7..8dc4eec7d 100644
--- a/backend/src/permissions/utils/convert-guest-access-to-note-permission.spec.ts
+++ b/backend/src/permissions/utils/convert-guest-access-to-note-permission.spec.ts
@@ -1,9 +1,10 @@
 /*
- * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
-import { GuestAccess } from '../../config/guest_access.enum';
+import { GuestAccess } from '@hedgedoc/commons';
+
 import { NotePermission } from '../note-permission.enum';
 import { convertGuestAccessToNotePermission } from './convert-guest-access-to-note-permission';
 
diff --git a/backend/src/permissions/utils/convert-guest-access-to-note-permission.ts b/backend/src/permissions/utils/convert-guest-access-to-note-permission.ts
index 39dc7dd35..f016bf01d 100644
--- a/backend/src/permissions/utils/convert-guest-access-to-note-permission.ts
+++ b/backend/src/permissions/utils/convert-guest-access-to-note-permission.ts
@@ -1,9 +1,10 @@
 /*
- * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
-import { GuestAccess } from '../../config/guest_access.enum';
+import { GuestAccess } from '@hedgedoc/commons';
+
 import { NotePermission } from '../note-permission.enum';
 
 /**
diff --git a/backend/src/realtime/realtime-note/realtime-connection.spec.ts b/backend/src/realtime/realtime-note/realtime-connection.spec.ts
index 42a93aaec..486d548c2 100644
--- a/backend/src/realtime/realtime-note/realtime-connection.spec.ts
+++ b/backend/src/realtime/realtime-note/realtime-connection.spec.ts
@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
@@ -13,7 +13,6 @@ import { Mock } from 'ts-mockery';
 
 import { Note } from '../../notes/note.entity';
 import { User } from '../../users/user.entity';
-import { Username } from '../../utils/username';
 import * as NameRandomizerModule from './random-word-lists/name-randomizer';
 import { RealtimeConnection } from './realtime-connection';
 import { RealtimeNote } from './realtime-note';
@@ -40,7 +39,7 @@ describe('websocket connection', () => {
   let mockedUser: User;
   let mockedMessageTransporter: MessageTransporter;
 
-  const mockedUserName: Username = 'mocked-user-name';
+  const mockedUserName: string = 'mocked-user-name';
   const mockedDisplayName = 'mockedDisplayName';
 
   beforeEach(() => {
diff --git a/backend/src/realtime/realtime-note/realtime-user-status-adapter.ts b/backend/src/realtime/realtime-note/realtime-user-status-adapter.ts
index 7e9b38e31..e0ea326fb 100644
--- a/backend/src/realtime/realtime-note/realtime-user-status-adapter.ts
+++ b/backend/src/realtime/realtime-note/realtime-user-status-adapter.ts
@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
@@ -11,8 +11,6 @@ import {
 } from '@hedgedoc/commons';
 import { Listener } from 'eventemitter2';
 
-import { Username } from '../../utils/username';
-
 export type OtherAdapterCollector = () => RealtimeUserStatusAdapter[];
 
 /**
@@ -22,7 +20,7 @@ export class RealtimeUserStatusAdapter {
   private readonly realtimeUser: RealtimeUser;
 
   constructor(
-    private readonly username: Username | null,
+    private readonly username: string | null,
     private readonly displayName: string,
     private collectOtherAdapters: OtherAdapterCollector,
     private messageTransporter: MessageTransporter,
diff --git a/backend/src/realtime/realtime-note/test-utils/mock-connection.ts b/backend/src/realtime/realtime-note/test-utils/mock-connection.ts
index 7d5da6db4..f32badb14 100644
--- a/backend/src/realtime/realtime-note/test-utils/mock-connection.ts
+++ b/backend/src/realtime/realtime-note/test-utils/mock-connection.ts
@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
@@ -10,7 +10,6 @@ import {
 import { Mock } from 'ts-mockery';
 
 import { User } from '../../../users/user.entity';
-import { Username } from '../../../utils/username';
 import { RealtimeConnection } from '../realtime-connection';
 import { RealtimeNote } from '../realtime-note';
 import { RealtimeUserStatusAdapter } from '../realtime-user-status-adapter';
@@ -22,13 +21,13 @@ enum RealtimeUserState {
   WITH_READONLY,
 }
 
-const MOCK_FALLBACK_USERNAME: Username = 'mock';
+const MOCK_FALLBACK_USERNAME: string = 'mock';
 
 /**
  * Creates a mocked {@link RealtimeConnection realtime connection}.
  */
 export class MockConnectionBuilder {
-  private username: Username | null;
+  private username: string | null;
   private displayName: string | undefined;
   private includeRealtimeUserStatus: RealtimeUserState =
     RealtimeUserState.WITHOUT;
@@ -51,7 +50,7 @@ export class MockConnectionBuilder {
    *
    * @param username the username of the mocked user. If this value is omitted then the builder will user a {@link MOCK_FALLBACK_USERNAME fallback}.
    */
-  public withLoggedInUser(username?: Username): this {
+  public withLoggedInUser(username?: string): this {
     const newUsername = username ?? MOCK_FALLBACK_USERNAME;
     this.username = newUsername;
     this.displayName = newUsername;
diff --git a/backend/src/realtime/websocket/websocket.gateway.spec.ts b/backend/src/realtime/websocket/websocket.gateway.spec.ts
index 2fda7879c..fbfed1266 100644
--- a/backend/src/realtime/websocket/websocket.gateway.spec.ts
+++ b/backend/src/realtime/websocket/websocket.gateway.spec.ts
@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
@@ -41,7 +41,6 @@ import { SessionService } from '../../sessions/session.service';
 import { User } from '../../users/user.entity';
 import { UsersModule } from '../../users/users.module';
 import { UsersService } from '../../users/users.service';
-import { Username } from '../../utils/username';
 import * as websocketConnectionModule from '../realtime-note/realtime-connection';
 import { RealtimeConnection } from '../realtime-note/realtime-connection';
 import { RealtimeNote } from '../realtime-note/realtime-note';
@@ -166,7 +165,7 @@ describe('Websocket gateway', () => {
           ),
       );
 
-    const mockUsername: Username = 'mock-username';
+    const mockUsername: string = 'mock-username';
     jest
       .spyOn(sessionService, 'fetchUsernameForSessionId')
       .mockImplementation((sessionId: string) =>
diff --git a/backend/src/realtime/websocket/websocket.gateway.ts b/backend/src/realtime/websocket/websocket.gateway.ts
index ad0466d46..5968f9c9b 100644
--- a/backend/src/realtime/websocket/websocket.gateway.ts
+++ b/backend/src/realtime/websocket/websocket.gateway.ts
@@ -6,7 +6,6 @@
 import {
   DisconnectReason,
   MessageTransporter,
-  NotePermissions,
   userCanEdit,
 } from '@hedgedoc/commons';
 import { OnGatewayConnection, WebSocketGateway } from '@nestjs/websockets';
@@ -92,10 +91,7 @@ export class WebsocketGateway implements OnGatewayConnection {
       );
 
       const permissions = await this.noteService.toNotePermissionsDto(note);
-      const acceptEdits: boolean = userCanEdit(
-        permissions as NotePermissions,
-        user?.username,
-      );
+      const acceptEdits: boolean = userCanEdit(permissions, user?.username);
 
       const connection = new RealtimeConnection(
         websocketTransporter,
diff --git a/backend/src/revisions/__snapshots__/revisions.service.spec.ts.snap b/backend/src/revisions/__snapshots__/revisions.service.spec.ts.snap
index 815b9ac71..961fa9b4b 100644
--- a/backend/src/revisions/__snapshots__/revisions.service.spec.ts.snap
+++ b/backend/src/revisions/__snapshots__/revisions.service.spec.ts.snap
@@ -77,14 +77,14 @@ exports[`RevisionsService toRevisionDto converts a revision 1`] = `
     "mockusername",
   ],
   "content": "mockContent",
-  "createdAt": 2020-05-20T09:58:00.000Z,
+  "createdAt": "2020-05-20T09:58:00.000Z",
   "description": "mockDescription",
   "edits": [
     {
-      "createdAt": 2020-03-04T22:32:00.000Z,
-      "endPos": 93,
-      "startPos": 34,
-      "updatedAt": 2021-02-10T12:23:00.000Z,
+      "createdAt": "2020-03-04T22:32:00.000Z",
+      "endPosition": 93,
+      "startPosition": 34,
+      "updatedAt": "2021-02-10T12:23:00.000Z",
       "username": "mockusername",
     },
   ],
@@ -104,7 +104,7 @@ exports[`RevisionsService toRevisionMetadataDto converts a revision 1`] = `
   "authorUsernames": [
     "mockusername",
   ],
-  "createdAt": 2020-05-20T09:58:00.000Z,
+  "createdAt": "2020-05-20T09:58:00.000Z",
   "description": "mockDescription",
   "id": 3246,
   "length": 1854,
diff --git a/backend/src/revisions/edit.dto.ts b/backend/src/revisions/edit.dto.ts
deleted file mode 100644
index fcc6facdf..000000000
--- a/backend/src/revisions/edit.dto.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
-import { Type } from 'class-transformer';
-import { IsDate, IsNumber, IsOptional, IsString, Min } from 'class-validator';
-
-import { UserInfoDto } from '../users/user-info.dto';
-import { BaseDto } from '../utils/base.dto.';
-
-export class EditDto extends BaseDto {
-  /**
-   * Username of the user who authored this section
-   * Is `null` if the user is anonymous
-   * @example "john.smith"
-   */
-  // nestjs-typed does not detect '| null' types as optional
-  // eslint-disable-next-line @darraghor/nestjs-typed/api-property-matches-property-optionality
-  @IsString()
-  @IsOptional()
-  @ApiPropertyOptional()
-  username: UserInfoDto['username'] | null;
-
-  /**
-   * Character index of the start of this section
-   * @example 102
-   */
-  @IsNumber()
-  @Min(0)
-  @ApiProperty()
-  startPos: number;
-
-  /**
-   * Character index of the end of this section
-   * Must be greater than {@link startPos}
-   * @example 154
-   */
-  @IsNumber()
-  @Min(0)
-  @ApiProperty()
-  endPos: number;
-
-  /**
-   * Datestring of the time this section was created
-   * @example "2020-12-01 12:23:34"
-   */
-  @IsDate()
-  @Type(() => Date)
-  @ApiProperty()
-  createdAt: Date;
-
-  /**
-   * Datestring of the last time this section was edited
-   * @example "2020-12-01 12:23:34"
-   */
-  @IsDate()
-  @Type(() => Date)
-  @ApiProperty()
-  updatedAt: Date;
-}
diff --git a/backend/src/revisions/edit.service.ts b/backend/src/revisions/edit.service.ts
index 4cdf731fa..99882473d 100644
--- a/backend/src/revisions/edit.service.ts
+++ b/backend/src/revisions/edit.service.ts
@@ -1,11 +1,11 @@
 /*
- * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { EditDto } from '@hedgedoc/commons';
 import { Injectable } from '@nestjs/common';
 
-import { EditDto } from './edit.dto';
 import { Edit } from './edit.entity';
 
 @Injectable()
@@ -15,10 +15,10 @@ export class EditService {
 
     return {
       username: authorUser ? authorUser.username : null,
-      startPos: edit.startPos,
-      endPos: edit.endPos,
-      createdAt: edit.createdAt,
-      updatedAt: edit.updatedAt,
+      startPosition: edit.startPos,
+      endPosition: edit.endPos,
+      createdAt: edit.createdAt.toISOString(),
+      updatedAt: edit.updatedAt.toISOString(),
     };
   }
 }
diff --git a/backend/src/revisions/revision-metadata.dto.ts b/backend/src/revisions/revision-metadata.dto.ts
deleted file mode 100644
index 347edf470..000000000
--- a/backend/src/revisions/revision-metadata.dto.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import { ApiProperty } from '@nestjs/swagger';
-import { Type } from 'class-transformer';
-import { IsArray, IsDate, IsNumber, IsString } from 'class-validator';
-
-import { BaseDto } from '../utils/base.dto.';
-import { Revision } from './revision.entity';
-
-export class RevisionMetadataDto extends BaseDto {
-  /**
-   * ID of this revision
-   * @example 13
-   */
-  @Type(() => Number)
-  @IsNumber()
-  @ApiProperty()
-  id: Revision['id'];
-
-  /**
-   * Datestring of the time this revision was created
-   * @example "2020-12-01 12:23:34"
-   */
-  @IsDate()
-  @Type(() => Date)
-  @ApiProperty()
-  createdAt: Date;
-
-  /**
-   * Number of characters in this revision
-   * @example 142
-   */
-  @IsNumber()
-  @ApiProperty()
-  length: number;
-
-  /**
-   * List of the usernames that have contributed to this revision
-   * Does not include anonymous users
-   */
-  @IsString()
-  @ApiProperty({ isArray: true, type: String })
-  authorUsernames: string[];
-
-  /**
-   * Count of anonymous users that have contributed to this revision
-   */
-  @IsNumber()
-  @ApiProperty()
-  anonymousAuthorCount: number;
-
-  /**
-   * Title of the note
-   * Does not contain any markup but might be empty
-   * @example "Shopping List"
-   */
-  @IsString()
-  @ApiProperty()
-  title: string;
-
-  /**
-   * Description of the note
-   * Does not contain any markup but might be empty
-   * @example Everything I want to buy
-   */
-  @IsString()
-  @ApiProperty()
-  description: string;
-
-  /**
-   * List of tags assigned to this note
-   * @example "['shopping', 'personal']
-   */
-  @IsArray()
-  @IsString({ each: true })
-  @ApiProperty({ isArray: true, type: String })
-  tags: string[];
-}
diff --git a/backend/src/revisions/revision.dto.ts b/backend/src/revisions/revision.dto.ts
deleted file mode 100644
index 53269b8be..000000000
--- a/backend/src/revisions/revision.dto.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import { ApiProperty } from '@nestjs/swagger';
-import { Type } from 'class-transformer';
-import { IsString, ValidateNested } from 'class-validator';
-
-import { EditDto } from './edit.dto';
-import { RevisionMetadataDto } from './revision-metadata.dto';
-
-export class RevisionDto extends RevisionMetadataDto {
-  /**
-   * Markdown content of the revision
-   * @example "# I am a heading"
-   */
-  @IsString()
-  @ApiProperty()
-  content: string;
-
-  /**
-   * Patch from the preceding revision to this one
-   */
-  @IsString()
-  @ApiProperty()
-  patch: string;
-
-  /**
-   * All edit objects which are used in the revision.
-   */
-  @Type(() => EditDto)
-  @ValidateNested({ each: true })
-  @ApiProperty({ isArray: true, type: EditDto })
-  edits: EditDto[];
-}
diff --git a/backend/src/revisions/revisions.service.ts b/backend/src/revisions/revisions.service.ts
index fc0cac81d..c00a9446f 100644
--- a/backend/src/revisions/revisions.service.ts
+++ b/backend/src/revisions/revisions.service.ts
@@ -1,8 +1,9 @@
 /*
- * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { RevisionDto, RevisionMetadataDto } from '@hedgedoc/commons';
 import { Inject, Injectable } from '@nestjs/common';
 import { Cron, Timeout } from '@nestjs/schedule';
 import { InjectRepository } from '@nestjs/typeorm';
@@ -15,8 +16,6 @@ import { ConsoleLoggerService } from '../logger/console-logger.service';
 import { Note } from '../notes/note.entity';
 import { Tag } from '../notes/tag.entity';
 import { EditService } from './edit.service';
-import { RevisionMetadataDto } from './revision-metadata.dto';
-import { RevisionDto } from './revision.dto';
 import { Revision } from './revision.entity';
 import { extractRevisionMetadataFromContent } from './utils/extract-revision-metadata-from-content';
 
@@ -136,7 +135,7 @@ export class RevisionsService {
     return {
       id: revision.id,
       length: revision.length,
-      createdAt: revision.createdAt,
+      createdAt: revision.createdAt.toISOString(),
       authorUsernames: revisionUserInfo.usernames,
       anonymousAuthorCount: revisionUserInfo.anonymousUserCount,
       title: revision.title,
@@ -151,7 +150,7 @@ export class RevisionsService {
       id: revision.id,
       content: revision.content,
       length: revision.length,
-      createdAt: revision.createdAt,
+      createdAt: revision.createdAt.toISOString(),
       title: revision.title,
       tags: (await revision.tags).map((tag) => tag.name),
       description: revision.description,
diff --git a/backend/src/seed.ts b/backend/src/seed.ts
index d5ec98c07..eb1c831de 100644
--- a/backend/src/seed.ts
+++ b/backend/src/seed.ts
@@ -1,13 +1,13 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { ProviderType } from '@hedgedoc/commons';
 import { DataSource } from 'typeorm';
 
 import { ApiToken } from './api-token/api-token.entity';
 import { Identity } from './auth/identity.entity';
-import { ProviderType } from './auth/provider-type.enum';
 import { Author } from './authors/author.entity';
 import { Group } from './groups/group.entity';
 import { HistoryEntry } from './history/history-entry.entity';
diff --git a/backend/src/sessions/session.service.ts b/backend/src/sessions/session.service.ts
index 252342023..6ac7884c9 100644
--- a/backend/src/sessions/session.service.ts
+++ b/backend/src/sessions/session.service.ts
@@ -1,8 +1,9 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { FullUserInfoDto, ProviderType } from '@hedgedoc/commons';
 import { Optional } from '@mrdrogdrog/optional';
 import { Inject, Injectable } from '@nestjs/common';
 import { InjectRepository } from '@nestjs/typeorm';
@@ -12,16 +13,13 @@ import { unsign } from 'cookie-signature';
 import { IncomingMessage } from 'http';
 import { Repository } from 'typeorm';
 
-import { ProviderType } from '../auth/provider-type.enum';
 import authConfiguration, { AuthConfig } from '../config/auth.config';
 import { DatabaseType } from '../config/database-type.enum';
 import databaseConfiguration, {
   DatabaseConfig,
 } from '../config/database.config';
 import { ConsoleLoggerService } from '../logger/console-logger.service';
-import { FullUserInfoDto } from '../users/user-info.dto';
 import { HEDGEDOC_SESSION } from '../utils/session';
-import { Username } from '../utils/username';
 import { Session } from './session.entity';
 
 export interface SessionState {
@@ -29,7 +27,7 @@ export interface SessionState {
   cookie: unknown;
 
   /** Contains the username if logged in completely, is undefined when not being logged in */
-  username?: Username;
+  username?: string;
 
   /** The auth provider that is used for the current login or pending login */
   authProviderType?: ProviderType;
@@ -87,7 +85,7 @@ export class SessionService {
    * @param sessionId The session id for which the owning user should be found
    * @return A Promise that either resolves with the username or rejects with an error
    */
-  fetchUsernameForSessionId(sessionId: string): Promise<Username | undefined> {
+  fetchUsernameForSessionId(sessionId: string): Promise<string | undefined> {
     return new Promise((resolve, reject) => {
       this.logger.debug(
         `Fetching username for sessionId ${sessionId}`,
@@ -102,7 +100,7 @@ export class SessionService {
             'fetchUsernameForSessionId',
           );
           if (error) return reject(error);
-          return resolve(result?.username as Username);
+          return resolve(result?.username);
         },
       );
     });
diff --git a/backend/src/users/user-info.dto.ts b/backend/src/users/user-info.dto.ts
deleted file mode 100644
index 7246c69ce..000000000
--- a/backend/src/users/user-info.dto.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
-import { Type } from 'class-transformer';
-import { IsLowercase, IsOptional, IsString } from 'class-validator';
-
-import { BaseDto } from '../utils/base.dto.';
-import { Username } from '../utils/username';
-
-export class UserInfoDto extends BaseDto {
-  /**
-   * The username
-   * @example "john.smith"
-   */
-  @Type(() => String)
-  @IsString()
-  @IsLowercase()
-  @ApiProperty()
-  username: Username;
-
-  /**
-   * The display name
-   * @example "John Smith"
-   */
-  @IsString()
-  @ApiProperty()
-  displayName: string;
-
-  /**
-   * URL of the profile picture
-   * @example "https://hedgedoc.example.com/uploads/johnsmith.png"
-   */
-  @ApiPropertyOptional({
-    format: 'uri',
-  })
-  @IsOptional()
-  @IsString()
-  photoUrl?: string;
-}
-
-/**
- * This DTO contains all attributes of the standard UserInfoDto
- * in addition to the email address.
- */
-export class FullUserInfoDto extends UserInfoDto {
-  /**
-   * Email address of the user
-   * @example "john.smith@example.com"
-   */
-  @ApiPropertyOptional({
-    format: 'email',
-  })
-  @IsOptional()
-  @IsString()
-  email?: string;
-}
-
-export class FullUserInfoWithIdDto extends FullUserInfoDto {
-  /**
-   * The user's ID
-   * @example 42
-   */
-  @IsString()
-  id: string;
-}
-
-export class UserLoginInfoDto extends UserInfoDto {
-  /**
-   * Identifier of the auth provider that was used to log in
-   */
-  @ApiProperty()
-  @IsString()
-  authProvider: string;
-}
diff --git a/backend/src/users/user.entity.ts b/backend/src/users/user.entity.ts
index 1003a995e..4378174b7 100644
--- a/backend/src/users/user.entity.ts
+++ b/backend/src/users/user.entity.ts
@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
@@ -20,7 +20,6 @@ import { Group } from '../groups/group.entity';
 import { HistoryEntry } from '../history/history-entry.entity';
 import { MediaUpload } from '../media/media-upload.entity';
 import { Note } from '../notes/note.entity';
-import { Username } from '../utils/username';
 
 @Entity()
 export class User {
@@ -30,7 +29,7 @@ export class User {
   @Column({
     unique: true,
   })
-  username: Username;
+  username: string;
 
   @Column()
   displayName: string;
@@ -78,7 +77,7 @@ export class User {
   private constructor() {}
 
   public static create(
-    username: Username,
+    username: string,
     displayName: string,
     email?: string,
     photoUrl?: string,
diff --git a/backend/src/users/username-check.dto.ts b/backend/src/users/username-check.dto.ts
deleted file mode 100644
index 8709aea0b..000000000
--- a/backend/src/users/username-check.dto.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import { IsBoolean, IsLowercase, IsString } from 'class-validator';
-
-import { BaseDto } from '../utils/base.dto.';
-import { Username } from '../utils/username';
-
-export class UsernameCheckDto extends BaseDto {
-  // eslint-disable-next-line @darraghor/nestjs-typed/validated-non-primitive-property-needs-type-decorator
-  @IsString()
-  @IsLowercase()
-  username: Username;
-}
-
-export class UsernameCheckResponseDto extends BaseDto {
-  @IsBoolean()
-  usernameAvailable: boolean;
-}
diff --git a/backend/src/users/users.service.spec.ts b/backend/src/users/users.service.spec.ts
index ecd807b25..54b88b8ed 100644
--- a/backend/src/users/users.service.spec.ts
+++ b/backend/src/users/users.service.spec.ts
@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
@@ -54,7 +54,7 @@ describe('UsersService', () => {
         .mockImplementationOnce(async (user: User): Promise<User> => user);
     });
     it('successfully creates a user', async () => {
-      const user = await service.createUser(username, displayname);
+      const user = await service.createUser(username, displayname, null, null);
       expect(user.username).toEqual(username);
       expect(user.displayName).toEqual(displayname);
     });
@@ -64,11 +64,11 @@ describe('UsersService', () => {
         throw new Error();
       });
       // create first user with username
-      await service.createUser(username, displayname);
+      await service.createUser(username, displayname, null, null);
       // attempt to create second user with username
-      await expect(service.createUser(username, displayname)).rejects.toThrow(
-        AlreadyInDBError,
-      );
+      await expect(
+        service.createUser(username, displayname, null, null),
+      ).rejects.toThrow(AlreadyInDBError);
     });
   });
 
@@ -134,7 +134,7 @@ describe('UsersService', () => {
       expect(photoUrl).toEqual(photo);
     });
     it('works if a user no photoUrl', () => {
-      user.photo = undefined;
+      user.photo = null;
       const photoUrl = service.getPhotoUrl(user);
       expect(photoUrl).toEqual('');
     });
diff --git a/backend/src/users/users.service.ts b/backend/src/users/users.service.ts
index 950af5fc0..8dd7b4c9b 100644
--- a/backend/src/users/users.service.ts
+++ b/backend/src/users/users.service.ts
@@ -1,9 +1,15 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
-import { REGEX_USERNAME } from '@hedgedoc/commons';
+import {
+  FullUserInfoDto,
+  LoginUserInfoDto,
+  ProviderType,
+  REGEX_USERNAME,
+  UserInfoDto,
+} from '@hedgedoc/commons';
 import { BadRequestException, Inject, Injectable } from '@nestjs/common';
 import { InjectRepository } from '@nestjs/typeorm';
 import { Repository } from 'typeorm';
@@ -11,12 +17,6 @@ import { Repository } from 'typeorm';
 import AuthConfiguration, { AuthConfig } from '../config/auth.config';
 import { AlreadyInDBError, NotInDBError } from '../errors/errors';
 import { ConsoleLoggerService } from '../logger/console-logger.service';
-import { Username } from '../utils/username';
-import {
-  FullUserInfoDto,
-  UserInfoDto,
-  UserLoginInfoDto,
-} from './user-info.dto';
 import { UserRelationEnum } from './user-relation.enum';
 import { User } from './user.entity';
 
@@ -34,7 +34,7 @@ export class UsersService {
   /**
    * @async
    * Create a new user with a given username and displayName
-   * @param {Username} username - the username the new user shall have
+   * @param {string} username - the username the new user shall have
    * @param {string} displayName - the display name the new user shall have
    * @param {string} [email] - the email the new user shall have
    * @param {string} [photoUrl] - the photoUrl the new user shall have
@@ -43,17 +43,22 @@ export class UsersService {
    * @throws {AlreadyInDBError} the username is already taken.
    */
   async createUser(
-    username: Username,
+    username: string,
     displayName: string,
-    email?: string,
-    photoUrl?: string,
+    email: string | null,
+    photoUrl: string | null,
   ): Promise<User> {
     if (!REGEX_USERNAME.test(username)) {
       throw new BadRequestException(
         `The username '${username}' is not a valid username.`,
       );
     }
-    const user = User.create(username, displayName, email, photoUrl);
+    const user = User.create(
+      username,
+      displayName,
+      email || undefined,
+      photoUrl || undefined,
+    );
     try {
       return await this.userRepository.save(user);
     } catch {
@@ -123,7 +128,7 @@ export class UsersService {
    * @param username - the username to check
    * @return {boolean} true if the user exists, false otherwise
    */
-  async checkIfUserExists(username: Username): Promise<boolean> {
+  async checkIfUserExists(username: string): Promise<boolean> {
     const user = await this.userRepository.findOne({
       where: { username: username },
     });
@@ -133,12 +138,12 @@ export class UsersService {
   /**
    * @async
    * Get the user specified by the username
-   * @param {Username} username the username by which the user is specified
+   * @param {string} username the username by which the user is specified
    * @param {UserRelationEnum[]} [withRelations=[]] if the returned user object should contain certain relations
    * @return {User} the specified user
    */
   async getUserByUsername(
-    username: Username,
+    username: string,
     withRelations: UserRelationEnum[] = [],
   ): Promise<User> {
     const user = await this.userRepository.findOne({
@@ -191,7 +196,7 @@ export class UsersService {
     };
   }
 
-  toUserLoginInfoDto(user: User, authProvider: string): UserLoginInfoDto {
-    return { ...this.toUserDto(user), authProvider };
+  toLoginUserInfoDto(user: User, authProvider: ProviderType): LoginUserInfoDto {
+    return { ...this.toFullUserDto(user), authProvider };
   }
 }
diff --git a/backend/src/utils/serverVersion.ts b/backend/src/utils/serverVersion.ts
index d5b25b828..bbc0cfe59 100644
--- a/backend/src/utils/serverVersion.ts
+++ b/backend/src/utils/serverVersion.ts
@@ -1,30 +1,29 @@
 /*
- * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { ServerVersionDto } from '@hedgedoc/commons';
 import { Optional } from '@mrdrogdrog/optional';
 import { promises as fs } from 'fs';
 import { join as joinPath } from 'path';
 
-import { ServerVersion } from '../monitoring/server-status.dto';
-
-let versionCache: ServerVersion | undefined = undefined;
+let versionCache: ServerVersionDto | undefined = undefined;
 
 /**
  * Reads the HedgeDoc version from the root package.json. This is done only once per run.
  *
- * @return {Promise<ServerVersion>} A Promise that contains the parsed server version.
+ * @return {Promise<ServerVersionDto>} A Promise that contains the parsed server version.
  * @throws {Error} if the package.json couldn't be found or doesn't contain a correct version.
  */
-export async function getServerVersionFromPackageJson(): Promise<ServerVersion> {
+export async function getServerVersionFromPackageJson(): Promise<ServerVersionDto> {
   if (!versionCache) {
     versionCache = await parseVersionFromPackageJson();
   }
   return versionCache;
 }
 
-async function parseVersionFromPackageJson(): Promise<ServerVersion> {
+async function parseVersionFromPackageJson(): Promise<ServerVersionDto> {
   const rawFileContent: string = await fs.readFile(
     joinPath(__dirname, '../../../package.json'),
     { encoding: 'utf8' },
diff --git a/backend/src/utils/timestamp.ts b/backend/src/utils/timestamp.ts
deleted file mode 100644
index 4e427aab4..000000000
--- a/backend/src/utils/timestamp.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-export type TimestampMillis = number;
diff --git a/backend/src/utils/username.ts b/backend/src/utils/username.ts
deleted file mode 100644
index 4ea20fc70..000000000
--- a/backend/src/utils/username.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-export type Username = Lowercase<string>;
-
-export function makeUsernameLowercase(username: string): Username {
-  return username.toLowerCase() as Username;
-}
diff --git a/backend/test/private-api/alias.e2e-spec.ts b/backend/test/private-api/alias.e2e-spec.ts
index e16e559dc..946724fef 100644
--- a/backend/test/private-api/alias.e2e-spec.ts
+++ b/backend/test/private-api/alias.e2e-spec.ts
@@ -1,12 +1,12 @@
 /*
- * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { AliasCreateDto, AliasUpdateDto } from '@hedgedoc/commons';
 import request from 'supertest';
 
-import { AliasCreateDto } from '../../src/notes/alias-create.dto';
-import { AliasUpdateDto } from '../../src/notes/alias-update.dto';
+import { Note } from '../../src/notes/note.entity';
 import { User } from '../../src/users/user.entity';
 import {
   password1,
@@ -183,11 +183,12 @@ describe('Alias', () => {
           });
       });
       it('if the property primaryAlias is false', async () => {
-        changeAliasDto.primaryAlias = false;
         await agent1
           .put(`/api/private/alias/${newAlias}`)
           .set('Content-Type', 'application/json')
-          .send(changeAliasDto)
+          .send({
+            primaryAlias: false,
+          })
           .expect(400);
       });
       it('if the user is not an owner', async () => {
@@ -204,7 +205,7 @@ describe('Alias', () => {
   describe('DELETE /alias/{alias}', () => {
     const testAlias = 'aliasTest3';
     const newAlias = 'normalAlias3';
-    let note;
+    let note: Note;
 
     beforeEach(async () => {
       note = await testSetup.notesService.createNote(
diff --git a/backend/test/private-api/auth.e2e-spec.ts b/backend/test/private-api/auth.e2e-spec.ts
index c2ec53463..76c244c87 100644
--- a/backend/test/private-api/auth.e2e-spec.ts
+++ b/backend/test/private-api/auth.e2e-spec.ts
@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
@@ -8,21 +8,18 @@
 @typescript-eslint/no-unsafe-assignment,
 @typescript-eslint/no-unsafe-member-access
 */
+import { LoginDto, RegisterDto, UpdatePasswordDto } from '@hedgedoc/commons';
 import request from 'supertest';
 
-import { LoginDto } from '../../src/auth/local/login.dto';
-import { RegisterDto } from '../../src/auth/local/register.dto';
-import { UpdatePasswordDto } from '../../src/auth/local/update-password.dto';
 import { NotInDBError } from '../../src/errors/errors';
 import { UserRelationEnum } from '../../src/users/user-relation.enum';
 import { checkPassword } from '../../src/utils/password';
-import { Username } from '../../src/utils/username';
 import { TestSetup, TestSetupBuilder } from '../test-setup';
 
 describe('Auth', () => {
   let testSetup: TestSetup;
 
-  let username: Username;
+  let username: string;
   let displayName: string;
   let password: string;
 
@@ -75,6 +72,8 @@ describe('Auth', () => {
         const conflictingUser = await testSetup.userService.createUser(
           conflictingUserName,
           displayName,
+          null,
+          null,
         );
         const registrationDto: RegisterDto = {
           displayName: displayName,
diff --git a/backend/test/private-api/groups.e2e-spec.ts b/backend/test/private-api/groups.e2e-spec.ts
index e8093be8a..e03f234e0 100644
--- a/backend/test/private-api/groups.e2e-spec.ts
+++ b/backend/test/private-api/groups.e2e-spec.ts
@@ -1,12 +1,11 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { GuestAccess, LoginDto } from '@hedgedoc/commons';
 import request from 'supertest';
 
-import { LoginDto } from '../../src/auth/local/login.dto';
-import { GuestAccess } from '../../src/config/guest_access.enum';
 import { createDefaultMockNoteConfig } from '../../src/config/mock/note.config.mock';
 import { NoteConfig } from '../../src/config/note.config';
 import {
diff --git a/backend/test/private-api/history.e2e-spec.ts b/backend/test/private-api/history.e2e-spec.ts
index 4d0a1a9b3..d15f72890 100644
--- a/backend/test/private-api/history.e2e-spec.ts
+++ b/backend/test/private-api/history.e2e-spec.ts
@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
@@ -41,7 +41,7 @@ describe('History', () => {
     historyService = moduleRef.get(HistoryService);
     const userService = moduleRef.get(UsersService);
     localIdentityService = moduleRef.get(LocalService);
-    user = await userService.createUser(username, 'Testy');
+    user = await userService.createUser(username, 'Testy', null, null);
     await localIdentityService.createLocalIdentity(user, password);
     const notesService = moduleRef.get(NotesService);
     note = await notesService.createNote(content, user, 'note');
diff --git a/backend/test/private-api/me.e2e-spec.ts b/backend/test/private-api/me.e2e-spec.ts
index 36994bc5f..16fc93abe 100644
--- a/backend/test/private-api/me.e2e-spec.ts
+++ b/backend/test/private-api/me.e2e-spec.ts
@@ -1,14 +1,14 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { LoginUserInfoDto, ProviderType } from '@hedgedoc/commons';
 import { promises as fs } from 'fs';
 import request from 'supertest';
 
 import { NotInDBError } from '../../src/errors/errors';
 import { Note } from '../../src/notes/note.entity';
-import { UserLoginInfoDto } from '../../src/users/user-info.dto';
 import { User } from '../../src/users/user.entity';
 import { TestSetup, TestSetupBuilder } from '../test-setup';
 
@@ -32,7 +32,12 @@ describe('Me', () => {
     const password = 'AHardcodedStrongP@ssword123';
     await testSetup.app.init();
 
-    user = await testSetup.userService.createUser(username, 'Testy');
+    user = await testSetup.userService.createUser(
+      username,
+      'Testy',
+      null,
+      null,
+    );
     await testSetup.localIdentityService.createLocalIdentity(user, password);
 
     content = 'This is a test note.';
@@ -51,12 +56,15 @@ describe('Me', () => {
   });
 
   it('GET /me', async () => {
-    const userInfo = testSetup.userService.toUserLoginInfoDto(user, 'local');
+    const userInfo = testSetup.userService.toLoginUserInfoDto(
+      user,
+      ProviderType.LOCAL,
+    );
     const response = await agent
       .get('/api/private/me')
       .expect('Content-Type', /json/)
       .expect(200);
-    const gotUser = response.body as UserLoginInfoDto;
+    const gotUser = response.body as LoginUserInfoDto;
     expect(gotUser).toEqual(userInfo);
   });
 
@@ -126,15 +134,15 @@ describe('Me', () => {
     await fs.rmdir(uploadPath);
   });
 
-  it('POST /me/profile', async () => {
+  it('PUT /me/profile', async () => {
     const newDisplayName = 'Another name';
     expect(user.displayName).not.toEqual(newDisplayName);
     await agent
-      .post('/api/private/me/profile')
+      .put('/api/private/me/profile')
       .send({
         displayName: newDisplayName,
       })
-      .expect(201);
+      .expect(200);
     const dbUser = await testSetup.userService.getUserByUsername('hardcoded');
     expect(dbUser.displayName).toEqual(newDisplayName);
   });
diff --git a/backend/test/private-api/notes.e2e-spec.ts b/backend/test/private-api/notes.e2e-spec.ts
index 25fc933a4..b9637311c 100644
--- a/backend/test/private-api/notes.e2e-spec.ts
+++ b/backend/test/private-api/notes.e2e-spec.ts
@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
@@ -39,9 +39,19 @@ describe('Notes', () => {
     const password2 = 'AHardcodedStrongP@ssword12';
     const groupname1 = 'groupname1';
 
-    user1 = await testSetup.userService.createUser(username1, 'Testy');
+    user1 = await testSetup.userService.createUser(
+      username1,
+      'Testy',
+      null,
+      null,
+    );
     await testSetup.localIdentityService.createLocalIdentity(user1, password1);
-    user2 = await testSetup.userService.createUser(username2, 'Max Mustermann');
+    user2 = await testSetup.userService.createUser(
+      username2,
+      'Max Mustermann',
+      null,
+      null,
+    );
     await testSetup.localIdentityService.createLocalIdentity(user2, password2);
 
     group1 = await testSetup.groupService.createGroup(groupname1, 'Group 1');
@@ -668,7 +678,7 @@ describe('Notes', () => {
             .put(`/api/private/notes/${alias}/metadata/permissions/owner`)
             .expect('Content-Type', /json/)
             .expect(200)
-            .send({ newOwner: user2.username });
+            .send({ owner: user2.username });
           expect(response.body.metadata.permissions.owner).toBe(user2.username);
         });
       });
diff --git a/backend/test/private-api/register-and-login.e2e-spec.ts b/backend/test/private-api/register-and-login.e2e-spec.ts
index 7c967353c..b9a562d29 100644
--- a/backend/test/private-api/register-and-login.e2e-spec.ts
+++ b/backend/test/private-api/register-and-login.e2e-spec.ts
@@ -1,12 +1,11 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { LoginDto, RegisterDto } from '@hedgedoc/commons';
 import request from 'supertest';
 
-import { LoginDto } from '../../src/auth/local/login.dto';
-import { RegisterDto } from '../../src/auth/local/register.dto';
 import { TestSetup, TestSetupBuilder } from '../test-setup';
 
 describe('Register and Login', () => {
diff --git a/backend/test/public-api/alias.e2e-spec.ts b/backend/test/public-api/alias.e2e-spec.ts
index 6c4d33fe3..5ad23073e 100644
--- a/backend/test/public-api/alias.e2e-spec.ts
+++ b/backend/test/public-api/alias.e2e-spec.ts
@@ -1,11 +1,11 @@
 /*
- * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { AliasUpdateDto } from '@hedgedoc/commons';
 import request from 'supertest';
 
-import { AliasUpdateDto } from '../../src/notes/alias-update.dto';
 import { TestSetup, TestSetupBuilder } from '../test-setup';
 
 describe('Alias', () => {
@@ -166,13 +166,13 @@ describe('Alias', () => {
           .expect(401);
       });
       it('if the property primaryAlias is false', async () => {
-        changeAliasDto.primaryAlias = false;
-
         await request(testSetup.app.getHttpServer())
           .put(`/api/v2/alias/${testAlias}`)
           .set('Authorization', `Bearer ${testSetup.authTokens[0].secret}`)
           .set('Content-Type', 'application/json')
-          .send(changeAliasDto)
+          .send({
+            primaryAlias: false,
+          })
           .expect(400);
       });
     });
diff --git a/backend/test/public-api/me.e2e-spec.ts b/backend/test/public-api/me.e2e-spec.ts
index d2f5a9c12..6e87d48b2 100644
--- a/backend/test/public-api/me.e2e-spec.ts
+++ b/backend/test/public-api/me.e2e-spec.ts
@@ -1,15 +1,15 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { NoteMetadataDto } from '@hedgedoc/commons';
 import { promises as fs } from 'fs';
 import { join } from 'path';
+import { HistoryEntryDto } from 'src/history/history-entry.dto';
 import request from 'supertest';
 
 import { HistoryEntryUpdateDto } from '../../src/history/history-entry-update.dto';
-import { HistoryEntryDto } from '../../src/history/history-entry.dto';
-import { NoteMetadataDto } from '../../src/notes/note-metadata.dto';
 import { User } from '../../src/users/user.entity';
 import { TestSetup, TestSetupBuilder } from '../test-setup';
 
@@ -25,7 +25,12 @@ describe('Me', () => {
     uploadPath =
       testSetup.configService.get('mediaConfig').backend.filesystem.uploadPath;
 
-    user = await testSetup.userService.createUser('hardcoded', 'Testy');
+    user = await testSetup.userService.createUser(
+      'hardcoded',
+      'Testy',
+      null,
+      null,
+    );
     await testSetup.app.init();
   });
 
diff --git a/backend/test/public-api/notes.e2e-spec.ts b/backend/test/public-api/notes.e2e-spec.ts
index 107116de0..9fcdf6676 100644
--- a/backend/test/public-api/notes.e2e-spec.ts
+++ b/backend/test/public-api/notes.e2e-spec.ts
@@ -1,14 +1,14 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { NotePermissionsUpdateDto } from '@hedgedoc/commons';
 import { promises as fs } from 'fs';
 import { join } from 'path';
 import request from 'supertest';
 
 import { NotInDBError } from '../../src/errors/errors';
-import { NotePermissionsUpdateDto } from '../../src/notes/note-permissions.dto';
 import { TestSetup, TestSetupBuilder } from '../test-setup';
 
 describe('Notes', () => {
@@ -219,14 +219,15 @@ describe('Notes', () => {
         testSetup.users[0],
         'deleteTest3',
       );
-      const updateNotePermission = new NotePermissionsUpdateDto();
-      updateNotePermission.sharedToUsers = [
-        {
-          username: testSetup.users[0].username,
-          canEdit: true,
-        },
-      ];
-      updateNotePermission.sharedToGroups = [];
+      const updateNotePermission: NotePermissionsUpdateDto = {
+        sharedToUsers: [
+          {
+            username: testSetup.users[0].username,
+            canEdit: true,
+          },
+        ],
+        sharedToGroups: [],
+      };
       await testSetup.permissionsService.updateNotePermissions(
         note,
         updateNotePermission,
diff --git a/backend/test/test-setup.ts b/backend/test/test-setup.ts
index 441eb30de..59270823f 100644
--- a/backend/test/test-setup.ts
+++ b/backend/test/test-setup.ts
@@ -1,8 +1,9 @@
 /*
- * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
  *
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { ApiTokenWithSecretDto } from '@hedgedoc/commons';
 import { ConfigModule, ConfigService } from '@nestjs/config';
 import { RouterModule, Routes } from '@nestjs/core';
 import { EventEmitterModule } from '@nestjs/event-emitter';
@@ -11,7 +12,6 @@ import { Test, TestingModule, TestingModuleBuilder } from '@nestjs/testing';
 import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
 import { Connection, createConnection } from 'typeorm';
 
-import { ApiTokenWithSecretDto } from '../src/api-token/api-token.dto';
 import { ApiTokenGuard } from '../src/api-token/api-token.guard';
 import { ApiTokenModule } from '../src/api-token/api-token.module';
 import { ApiTokenService } from '../src/api-token/api-token.service';
@@ -389,13 +389,28 @@ export class TestSetupBuilder {
     this.setupPostCompile.push(async () => {
       // Create users
       this.testSetup.users.push(
-        await this.testSetup.userService.createUser(username1, 'Test User 1'),
+        await this.testSetup.userService.createUser(
+          username1,
+          'Test User 1',
+          null,
+          null,
+        ),
       );
       this.testSetup.users.push(
-        await this.testSetup.userService.createUser(username2, 'Test User 2'),
+        await this.testSetup.userService.createUser(
+          username2,
+          'Test User 2',
+          null,
+          null,
+        ),
       );
       this.testSetup.users.push(
-        await this.testSetup.userService.createUser(username3, 'Test User 3'),
+        await this.testSetup.userService.createUser(
+          username3,
+          'Test User 3',
+          null,
+          null,
+        ),
       );
 
       // Create identities for login
@@ -415,10 +430,12 @@ export class TestSetupBuilder {
       // create auth tokens
       this.testSetup.authTokens = await Promise.all(
         this.testSetup.users.map(async (user) => {
+          const validUntil = new Date();
+          validUntil.setTime(validUntil.getTime() + 60 * 60 * 1000);
           return await this.testSetup.publicAuthTokenService.addToken(
             user,
             'test',
-            new Date().getTime() + 60 * 60 * 1000,
+            validUntil,
           );
         }),
       );