feat: add oauth account management url (#30873)
This commit is contained in:
@@ -77,6 +77,7 @@ const setupOAuth = async (token: string, dto: Partial<AdminConfigOAuthDto>) => {
|
||||
...defaults.oauth,
|
||||
buttonText: 'Login with Immich',
|
||||
issuerUrl: `${authServer.internal}/.well-known/openid-configuration`,
|
||||
accountManagementUrl: authServer.internal,
|
||||
allowInsecureRequests: true,
|
||||
...dto,
|
||||
};
|
||||
|
||||
@@ -121,6 +121,7 @@ describe('/server', () => {
|
||||
expect(body).toEqual({
|
||||
loginPageMessage: '',
|
||||
oauthButtonText: 'Login with OAuth',
|
||||
oauthAccountManagementUrl: '',
|
||||
trashDays: 30,
|
||||
userDeleteDelay: 7,
|
||||
isInitialized: true,
|
||||
|
||||
@@ -294,6 +294,8 @@
|
||||
"notification_enable_email_notifications": "Enable email notifications",
|
||||
"notification_settings": "Notification Settings",
|
||||
"notification_settings_description": "Manage notification settings, including email",
|
||||
"oauth_account_management_url": "Account Management URL",
|
||||
"oauth_account_management_url_description": "Location in the external identity provider where a user can manage their settings or profile.",
|
||||
"oauth_allow_insecure_requests": "Allow insecure requests",
|
||||
"oauth_allow_insecure_requests_description": "WARNING: This disables TLS certificate validation for OAuth requests and may expose you to MITM attacks.",
|
||||
"oauth_auto_launch": "Auto launch",
|
||||
@@ -1379,6 +1381,7 @@
|
||||
"manage_media_access_settings": "Open settings",
|
||||
"manage_media_access_subtitle": "Allow the Immich app to manage and move media files.",
|
||||
"manage_media_access_title": "Media Management Access",
|
||||
"manage_oauth_account": "Manage OAuth Account",
|
||||
"manage_sharing_with_other_users": "Manage sharing with other users",
|
||||
"manage_sharing_with_partners": "Manage sharing with partners",
|
||||
"manage_the_app_settings": "Manage the app settings",
|
||||
|
||||
@@ -18232,6 +18232,11 @@
|
||||
},
|
||||
"AdminConfigOAuthDto": {
|
||||
"properties": {
|
||||
"accountManagementUrl": {
|
||||
"default": "",
|
||||
"description": "Account management URL",
|
||||
"type": "string"
|
||||
},
|
||||
"allowInsecureRequests": {
|
||||
"description": "Allow insecure requests",
|
||||
"type": "boolean"
|
||||
@@ -24593,6 +24598,11 @@
|
||||
"minimum": -9007199254740991,
|
||||
"type": "integer"
|
||||
},
|
||||
"oauthAccountManagementUrl": {
|
||||
"default": "",
|
||||
"description": "OAuth account management URL",
|
||||
"type": "string"
|
||||
},
|
||||
"oauthButtonText": {
|
||||
"description": "OAuth button text",
|
||||
"type": "string"
|
||||
|
||||
@@ -313,6 +313,8 @@ export type AdminConfigNotificationsDto = {
|
||||
smtp: AdminConfigSmtpDto;
|
||||
};
|
||||
export type AdminConfigOAuthDto = {
|
||||
/** Account management URL */
|
||||
accountManagementUrl?: string;
|
||||
/** Allow insecure requests */
|
||||
allowInsecureRequests: boolean;
|
||||
/** Auto launch */
|
||||
@@ -2521,6 +2523,8 @@ export type ServerConfigDto = {
|
||||
mapLightStyleUrl: string;
|
||||
/** People min faces server default */
|
||||
minFaces: number;
|
||||
/** OAuth account management URL */
|
||||
oauthAccountManagementUrl?: string;
|
||||
/** OAuth button text */
|
||||
oauthButtonText: string;
|
||||
/** Whether public user registration is enabled */
|
||||
|
||||
@@ -291,6 +291,10 @@ const AdminConfigSchemaWithVisibility = z
|
||||
defaultStorageQuota: z.int().min(0).nullable().describe('Default storage quota'),
|
||||
enabled: configBool.describe('Enabled').meta({ visibility: Public }),
|
||||
issuerUrl: emptyOrUrl('Issuer URL must be an empty string or a valid URL').describe('Issuer URL'),
|
||||
accountManagementUrl: emptyOrUrl('Account management URL must be an empty string or a valid URL')
|
||||
.describe('Account management URL')
|
||||
.optional()
|
||||
.default(''),
|
||||
scope: z.string().describe('Scope'),
|
||||
prompt: z.string().describe('OAuth prompt parameter (e.g. select_account, login, consent)'),
|
||||
endSessionEndpoint: emptyOrUrl('endSessionEndpoint must be an empty string or a valid URL').describe(
|
||||
@@ -668,6 +672,7 @@ export const defaults = Object.freeze<SystemConfig>({
|
||||
defaultStorageQuota: null,
|
||||
enabled: false,
|
||||
issuerUrl: '',
|
||||
accountManagementUrl: '',
|
||||
endSessionEndpoint: '',
|
||||
mobileOverrideEnabled: false,
|
||||
mobileRedirectUri: '',
|
||||
|
||||
@@ -114,6 +114,7 @@ const ServerMediaTypesResponseSchema = z
|
||||
const ServerConfigSchema = z
|
||||
.object({
|
||||
oauthButtonText: z.string().describe('OAuth button text'),
|
||||
oauthAccountManagementUrl: z.string().describe('OAuth account management URL').optional().default(''),
|
||||
loginPageMessage: z.string().describe('Login page message'),
|
||||
trashDays: z.int().describe('Number of days before trashed assets are permanently deleted'),
|
||||
userDeleteDelay: z.int().describe('Delay in days before deleted users are permanently removed'),
|
||||
|
||||
@@ -22,6 +22,7 @@ export type OAuthConfig = {
|
||||
clientId: string;
|
||||
clientSecret?: string;
|
||||
issuerUrl: string;
|
||||
accountManagementUrl: string;
|
||||
endSessionEndpoint: string;
|
||||
mobileOverrideEnabled: boolean;
|
||||
mobileRedirectUri: string;
|
||||
|
||||
@@ -160,6 +160,7 @@ describe(ServerService.name, () => {
|
||||
await expect(sut.getSystemConfig()).resolves.toEqual({
|
||||
loginPageMessage: '',
|
||||
oauthButtonText: 'Login with OAuth',
|
||||
oauthAccountManagementUrl: '',
|
||||
trashDays: 30,
|
||||
userDeleteDelay: 7,
|
||||
isInitialized: false,
|
||||
|
||||
@@ -120,6 +120,7 @@ export class ServerService extends BaseService {
|
||||
trashDays: config.trash.days,
|
||||
userDeleteDelay: config.user.deleteDelay,
|
||||
oauthButtonText: config.oauth.buttonText,
|
||||
oauthAccountManagementUrl: config.oauth.accountManagementUrl,
|
||||
isInitialized,
|
||||
isOnboarded: onboarding?.isOnboarded || false,
|
||||
externalDomain: config.server.externalDomain,
|
||||
|
||||
@@ -154,6 +154,7 @@ const updatedConfig = Object.freeze<SystemConfig>({
|
||||
enabled: true,
|
||||
},
|
||||
oauth: {
|
||||
accountManagementUrl: '',
|
||||
autoLaunch: true,
|
||||
autoRegister: true,
|
||||
buttonText: 'Login with OAuth',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||
import { oauth } from '$lib/utils';
|
||||
@@ -41,13 +42,18 @@
|
||||
|
||||
<section class="my-4">
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<div class="flex justify-end sm:ms-8">
|
||||
<div class="flex justify-end gap-3 sm:ms-8">
|
||||
{#if loading}
|
||||
<div class="flex place-content-center place-items-center">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
{:else if featureFlagsManager.value.oauth}
|
||||
{#if authManager.user.oauthId}
|
||||
{#if serverConfigManager.value.oauthAccountManagementUrl}
|
||||
<Button shape="round" size="small" href={serverConfigManager.value.oauthAccountManagementUrl}
|
||||
>{$t('manage_oauth_account')}</Button
|
||||
>
|
||||
{/if}
|
||||
<Button shape="round" size="small" onclick={() => handleUnlink()}>{$t('unlink_oauth')}</Button>
|
||||
{:else}
|
||||
<Button shape="round" size="small" onclick={() => oauth.authorize(location)}>{$t('link_to_oauth')}</Button>
|
||||
|
||||
@@ -251,6 +251,16 @@
|
||||
isEdited={configToEdit.oauth.buttonText !== config.oauth.buttonText}
|
||||
/>
|
||||
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label={$t('admin.oauth_account_management_url')}
|
||||
description={$t('admin.oauth_account_management_url_description')}
|
||||
bind:value={configToEdit.oauth.accountManagementUrl}
|
||||
required={false}
|
||||
disabled={disabled || !configToEdit.oauth.enabled}
|
||||
isEdited={configToEdit.oauth.accountManagementUrl !== config.oauth.accountManagementUrl}
|
||||
/>
|
||||
|
||||
<SettingSwitch
|
||||
title={$t('admin.oauth_auto_register')}
|
||||
subtitle={$t('admin.oauth_auto_register_description')}
|
||||
|
||||
Reference in New Issue
Block a user