diff --git a/cli/src/api/open-api/api.ts b/cli/src/api/open-api/api.ts index 3819b021f4..b0bf9a8f2f 100644 --- a/cli/src/api/open-api/api.ts +++ b/cli/src/api/open-api/api.ts @@ -2596,6 +2596,12 @@ export interface SystemConfigOAuthDto { * @memberof SystemConfigOAuthDto */ 'scope': string; + /** + * + * @type {string} + * @memberof SystemConfigOAuthDto + */ + 'storageLabelClaim': string; /** * * @type {string} diff --git a/mobile/openapi/doc/SystemConfigOAuthDto.md b/mobile/openapi/doc/SystemConfigOAuthDto.md index 745b13b79a..a79b0729f9 100644 Binary files a/mobile/openapi/doc/SystemConfigOAuthDto.md and b/mobile/openapi/doc/SystemConfigOAuthDto.md differ diff --git a/mobile/openapi/lib/model/system_config_o_auth_dto.dart b/mobile/openapi/lib/model/system_config_o_auth_dto.dart index 364b99fd2f..edfa240eeb 100644 Binary files a/mobile/openapi/lib/model/system_config_o_auth_dto.dart and b/mobile/openapi/lib/model/system_config_o_auth_dto.dart differ diff --git a/mobile/openapi/test/system_config_o_auth_dto_test.dart b/mobile/openapi/test/system_config_o_auth_dto_test.dart index ca5fadad4a..88c8d2aa85 100644 Binary files a/mobile/openapi/test/system_config_o_auth_dto_test.dart and b/mobile/openapi/test/system_config_o_auth_dto_test.dart differ diff --git a/server/immich-openapi-specs.json b/server/immich-openapi-specs.json index 2dc23b1a04..c2c006744c 100644 --- a/server/immich-openapi-specs.json +++ b/server/immich-openapi-specs.json @@ -6503,6 +6503,9 @@ "scope": { "type": "string" }, + "storageLabelClaim": { + "type": "string" + }, "buttonText": { "type": "string" }, @@ -6525,6 +6528,7 @@ "clientId", "clientSecret", "scope", + "storageLabelClaim", "buttonText", "autoRegister", "autoLaunch", diff --git a/server/src/domain/auth/auth.service.ts b/server/src/domain/auth/auth.service.ts index 200ca0a743..a0c0967260 100644 --- a/server/src/domain/auth/auth.service.ts +++ b/server/src/domain/auth/auth.service.ts @@ -240,11 +240,19 @@ export class AuthService { } this.logger.log(`Registering new user: ${profile.email}/${profile.sub}`); + this.logger.verbose(`OAuth Profile: ${JSON.stringify(profile)}`); + + let storageLabel: string | null = profile[config.oauth.storageLabelClaim as keyof OAuthProfile] as string; + if (typeof storageLabel !== 'string') { + storageLabel = null; + } + user = await this.userCore.createUser({ firstName: profile.given_name || '', lastName: profile.family_name || '', email: profile.email, oauthId: profile.sub, + storageLabel, }); } diff --git a/server/src/domain/system-config/dto/system-config-oauth.dto.ts b/server/src/domain/system-config/dto/system-config-oauth.dto.ts index 6cc4590744..e13048761c 100644 --- a/server/src/domain/system-config/dto/system-config-oauth.dto.ts +++ b/server/src/domain/system-config/dto/system-config-oauth.dto.ts @@ -25,6 +25,9 @@ export class SystemConfigOAuthDto { @IsString() scope!: string; + @IsString() + storageLabelClaim!: string; + @IsString() buttonText!: string; diff --git a/server/src/domain/system-config/system-config.core.ts b/server/src/domain/system-config/system-config.core.ts index 771d5d0486..3051b82b3e 100644 --- a/server/src/domain/system-config/system-config.core.ts +++ b/server/src/domain/system-config/system-config.core.ts @@ -48,6 +48,7 @@ export const defaults = Object.freeze({ mobileOverrideEnabled: false, mobileRedirectUri: '', scope: 'openid email profile', + storageLabelClaim: 'preferred_username', buttonText: 'Login with OAuth', autoRegister: true, autoLaunch: false, diff --git a/server/src/domain/system-config/system-config.service.spec.ts b/server/src/domain/system-config/system-config.service.spec.ts index 8ac2efea6e..a3296df922 100644 --- a/server/src/domain/system-config/system-config.service.spec.ts +++ b/server/src/domain/system-config/system-config.service.spec.ts @@ -53,6 +53,7 @@ const updatedConfig = Object.freeze({ mobileOverrideEnabled: false, mobileRedirectUri: '', scope: 'openid email profile', + storageLabelClaim: 'preferred_username', }, passwordLogin: { enabled: true, diff --git a/server/src/domain/user/user.core.ts b/server/src/domain/user/user.core.ts index 1b3e872256..e7c0f7f2b1 100644 --- a/server/src/domain/user/user.core.ts +++ b/server/src/domain/user/user.core.ts @@ -8,9 +8,9 @@ import { } from '@nestjs/common'; import { constants, createReadStream, ReadStream } from 'fs'; import fs from 'fs/promises'; +import sanitize from 'sanitize-filename'; import { AuthUserDto } from '../auth'; import { ICryptoRepository } from '../crypto'; -import { CreateAdminDto, CreateUserDto, CreateUserOAuthDto } from './dto/create-user.dto'; import { IUserRepository, UserListFilter } from './user.repository'; const SALT_ROUNDS = 10; @@ -67,13 +67,13 @@ export class UserCore { } } - async createUser(createUserDto: CreateUserDto | CreateAdminDto | CreateUserOAuthDto): Promise { - const user = await this.userRepository.getByEmail(createUserDto.email); + async createUser(dto: Partial & { email: string }): Promise { + const user = await this.userRepository.getByEmail(dto.email); if (user) { throw new BadRequestException('User exists'); } - if (!(createUserDto as CreateAdminDto).isAdmin) { + if (!dto.isAdmin) { const localAdmin = await this.userRepository.getAdmin(); if (!localAdmin) { throw new BadRequestException('The first registered account must the administrator.'); @@ -81,10 +81,13 @@ export class UserCore { } try { - const payload: Partial = { ...createUserDto }; + const payload: Partial = { ...dto }; if (payload.password) { payload.password = await this.cryptoRepository.hashBcrypt(payload.password, SALT_ROUNDS); } + if (payload.storageLabel) { + payload.storageLabel = sanitize(payload.storageLabel); + } return this.userRepository.create(payload); } catch (e) { Logger.error(e, 'Create new user'); diff --git a/server/src/infra/entities/system-config.entity.ts b/server/src/infra/entities/system-config.entity.ts index 8046546132..af13b47da8 100644 --- a/server/src/infra/entities/system-config.entity.ts +++ b/server/src/infra/entities/system-config.entity.ts @@ -40,6 +40,7 @@ export enum SystemConfigKey { OAUTH_CLIENT_ID = 'oauth.clientId', OAUTH_CLIENT_SECRET = 'oauth.clientSecret', OAUTH_SCOPE = 'oauth.scope', + OAUTH_STORAGE_LABEL_CLAIM = 'oauth.storageLabelClaim', OAUTH_AUTO_LAUNCH = 'oauth.autoLaunch', OAUTH_BUTTON_TEXT = 'oauth.buttonText', OAUTH_AUTO_REGISTER = 'oauth.autoRegister', @@ -89,6 +90,7 @@ export interface SystemConfig { clientId: string; clientSecret: string; scope: string; + storageLabelClaim: string; buttonText: string; autoRegister: boolean; autoLaunch: boolean; diff --git a/web/src/api/open-api/api.ts b/web/src/api/open-api/api.ts index 93ba50c605..66c717c814 100644 --- a/web/src/api/open-api/api.ts +++ b/web/src/api/open-api/api.ts @@ -2596,6 +2596,12 @@ export interface SystemConfigOAuthDto { * @memberof SystemConfigOAuthDto */ 'scope': string; + /** + * + * @type {string} + * @memberof SystemConfigOAuthDto + */ + 'storageLabelClaim': string; /** * * @type {string} diff --git a/web/src/lib/components/admin-page/settings/oauth/oauth-settings.svelte b/web/src/lib/components/admin-page/settings/oauth/oauth-settings.svelte index ffee598f89..3079c5f17e 100644 --- a/web/src/lib/components/admin-page/settings/oauth/oauth-settings.svelte +++ b/web/src/lib/components/admin-page/settings/oauth/oauth-settings.svelte @@ -155,6 +155,16 @@ isEdited={!(oauthConfig.scope == savedConfig.scope)} /> + +