mirror of
https://github.com/immich-app/immich.git
synced 2025-04-10 10:06:25 +02:00
feat(server)!: oauth encryption algorithm setting (#6818)
* feat: add oauth signing algorithm setting * chore: open api * chore: change default to RS256 * feat: test and clean up --------- Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
This commit is contained in:
parent
8a643e5e48
commit
d3404f927c
15 changed files with 189 additions and 127 deletions
docs/docs/administration
mobile/openapi
doc
lib/model
test
open-api
server/src
domain
auth
system-config
infra/entities
web/src
|
@ -66,8 +66,10 @@ Once you have a new OAuth client application configured, Immich can be configure
|
|||
| Client ID | string | (required) | Required. Client ID (from previous step) |
|
||||
| Client Secret | string | (required) | Required. Client Secret (previous step) |
|
||||
| Scope | string | openid email profile | Full list of scopes to send with the request (space delimited) |
|
||||
| Signing Algorithm | string | RS256 | The algorithm used to sign the id token (examples: RS256, HS256) |
|
||||
| Button Text | string | Login with OAuth | Text for the OAuth button on the web |
|
||||
| Auto Register | boolean | true | When true, will automatically register a user the first time they sign in |
|
||||
| Storage Claim | string | preferred_username | Claim mapping for the user's storage label |
|
||||
| [Auto Launch](#auto-launch) | boolean | false | When true, will skip the login page and automatically start the OAuth login process |
|
||||
| [Mobile Redirect URI Override](#mobile-redirect-uri) | URL | (empty) | Http(s) alternative mobile redirect URI |
|
||||
|
||||
|
|
1
mobile/openapi/doc/SystemConfigOAuthDto.md
generated
1
mobile/openapi/doc/SystemConfigOAuthDto.md
generated
|
@ -18,6 +18,7 @@ Name | Type | Description | Notes
|
|||
**mobileOverrideEnabled** | **bool** | |
|
||||
**mobileRedirectUri** | **String** | |
|
||||
**scope** | **String** | |
|
||||
**signingAlgorithm** | **String** | |
|
||||
**storageLabelClaim** | **String** | |
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
|
|
@ -23,6 +23,7 @@ class SystemConfigOAuthDto {
|
|||
required this.mobileOverrideEnabled,
|
||||
required this.mobileRedirectUri,
|
||||
required this.scope,
|
||||
required this.signingAlgorithm,
|
||||
required this.storageLabelClaim,
|
||||
});
|
||||
|
||||
|
@ -46,6 +47,8 @@ class SystemConfigOAuthDto {
|
|||
|
||||
String scope;
|
||||
|
||||
String signingAlgorithm;
|
||||
|
||||
String storageLabelClaim;
|
||||
|
||||
@override
|
||||
|
@ -60,6 +63,7 @@ class SystemConfigOAuthDto {
|
|||
other.mobileOverrideEnabled == mobileOverrideEnabled &&
|
||||
other.mobileRedirectUri == mobileRedirectUri &&
|
||||
other.scope == scope &&
|
||||
other.signingAlgorithm == signingAlgorithm &&
|
||||
other.storageLabelClaim == storageLabelClaim;
|
||||
|
||||
@override
|
||||
|
@ -75,10 +79,11 @@ class SystemConfigOAuthDto {
|
|||
(mobileOverrideEnabled.hashCode) +
|
||||
(mobileRedirectUri.hashCode) +
|
||||
(scope.hashCode) +
|
||||
(signingAlgorithm.hashCode) +
|
||||
(storageLabelClaim.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SystemConfigOAuthDto[autoLaunch=$autoLaunch, autoRegister=$autoRegister, buttonText=$buttonText, clientId=$clientId, clientSecret=$clientSecret, enabled=$enabled, issuerUrl=$issuerUrl, mobileOverrideEnabled=$mobileOverrideEnabled, mobileRedirectUri=$mobileRedirectUri, scope=$scope, storageLabelClaim=$storageLabelClaim]';
|
||||
String toString() => 'SystemConfigOAuthDto[autoLaunch=$autoLaunch, autoRegister=$autoRegister, buttonText=$buttonText, clientId=$clientId, clientSecret=$clientSecret, enabled=$enabled, issuerUrl=$issuerUrl, mobileOverrideEnabled=$mobileOverrideEnabled, mobileRedirectUri=$mobileRedirectUri, scope=$scope, signingAlgorithm=$signingAlgorithm, storageLabelClaim=$storageLabelClaim]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
|
@ -92,6 +97,7 @@ class SystemConfigOAuthDto {
|
|||
json[r'mobileOverrideEnabled'] = this.mobileOverrideEnabled;
|
||||
json[r'mobileRedirectUri'] = this.mobileRedirectUri;
|
||||
json[r'scope'] = this.scope;
|
||||
json[r'signingAlgorithm'] = this.signingAlgorithm;
|
||||
json[r'storageLabelClaim'] = this.storageLabelClaim;
|
||||
return json;
|
||||
}
|
||||
|
@ -114,6 +120,7 @@ class SystemConfigOAuthDto {
|
|||
mobileOverrideEnabled: mapValueOfType<bool>(json, r'mobileOverrideEnabled')!,
|
||||
mobileRedirectUri: mapValueOfType<String>(json, r'mobileRedirectUri')!,
|
||||
scope: mapValueOfType<String>(json, r'scope')!,
|
||||
signingAlgorithm: mapValueOfType<String>(json, r'signingAlgorithm')!,
|
||||
storageLabelClaim: mapValueOfType<String>(json, r'storageLabelClaim')!,
|
||||
);
|
||||
}
|
||||
|
@ -172,6 +179,7 @@ class SystemConfigOAuthDto {
|
|||
'mobileOverrideEnabled',
|
||||
'mobileRedirectUri',
|
||||
'scope',
|
||||
'signingAlgorithm',
|
||||
'storageLabelClaim',
|
||||
};
|
||||
}
|
||||
|
|
|
@ -66,6 +66,11 @@ void main() {
|
|||
// TODO
|
||||
});
|
||||
|
||||
// String signingAlgorithm
|
||||
test('to test the property `signingAlgorithm`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// String storageLabelClaim
|
||||
test('to test the property `storageLabelClaim`', () async {
|
||||
// TODO
|
||||
|
|
|
@ -9679,6 +9679,9 @@
|
|||
"scope": {
|
||||
"type": "string"
|
||||
},
|
||||
"signingAlgorithm": {
|
||||
"type": "string"
|
||||
},
|
||||
"storageLabelClaim": {
|
||||
"type": "string"
|
||||
}
|
||||
|
@ -9694,6 +9697,7 @@
|
|||
"mobileOverrideEnabled",
|
||||
"mobileRedirectUri",
|
||||
"scope",
|
||||
"signingAlgorithm",
|
||||
"storageLabelClaim"
|
||||
],
|
||||
"type": "object"
|
||||
|
|
6
open-api/typescript-sdk/client/api.ts
generated
6
open-api/typescript-sdk/client/api.ts
generated
|
@ -4073,6 +4073,12 @@ export interface SystemConfigOAuthDto {
|
|||
* @memberof SystemConfigOAuthDto
|
||||
*/
|
||||
'scope': string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof SystemConfigOAuthDto
|
||||
*/
|
||||
'signingAlgorithm': string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
|
|
|
@ -73,7 +73,7 @@ describe('AuthService', () => {
|
|||
|
||||
jest.spyOn(generators, 'state').mockReturnValue('state');
|
||||
jest.spyOn(Issuer, 'discover').mockResolvedValue({
|
||||
id_token_signing_alg_values_supported: ['HS256'],
|
||||
id_token_signing_alg_values_supported: ['RS256'],
|
||||
Client: jest.fn().mockResolvedValue({
|
||||
issuer: {
|
||||
metadata: {
|
||||
|
|
|
@ -318,12 +318,25 @@ export class AuthService {
|
|||
const redirectUri = this.normalize(config, url.split('?')[0]);
|
||||
const client = await this.getOAuthClient(config);
|
||||
const params = client.callbackParams(url);
|
||||
const tokens = await client.callback(redirectUri, params, { state: params.state });
|
||||
return client.userinfo<OAuthProfile>(tokens.access_token || '');
|
||||
try {
|
||||
const tokens = await client.callback(redirectUri, params, { state: params.state });
|
||||
return client.userinfo<OAuthProfile>(tokens.access_token || '');
|
||||
} catch (error: Error | any) {
|
||||
if (error.message.includes('unexpected JWT alg received')) {
|
||||
this.logger.warn(
|
||||
[
|
||||
'Algorithm mismatch. Make sure the signing algorithm is set correctly in the OAuth settings.',
|
||||
'Or, that you have specified a signing key in your OAuth provider.',
|
||||
].join(' '),
|
||||
);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private async getOAuthClient(config: SystemConfig) {
|
||||
const { enabled, clientId, clientSecret, issuerUrl } = config.oauth;
|
||||
const { enabled, clientId, clientSecret, issuerUrl, signingAlgorithm } = config.oauth;
|
||||
|
||||
if (!enabled) {
|
||||
throw new BadRequestException('OAuth2 is not enabled');
|
||||
|
@ -337,10 +350,7 @@ export class AuthService {
|
|||
|
||||
try {
|
||||
const issuer = await Issuer.discover(issuerUrl);
|
||||
const algorithms = (issuer.id_token_signing_alg_values_supported || []) as string[];
|
||||
if (algorithms[0] === 'HS256') {
|
||||
metadata.id_token_signed_response_alg = algorithms[0];
|
||||
}
|
||||
metadata.id_token_signed_response_alg = signingAlgorithm;
|
||||
|
||||
return new issuer.Client(metadata);
|
||||
} catch (error: any | AggregateError) {
|
||||
|
|
|
@ -5,12 +5,13 @@ const isOverrideEnabled = (config: SystemConfigOAuthDto) => config.mobileOverrid
|
|||
|
||||
export class SystemConfigOAuthDto {
|
||||
@IsBoolean()
|
||||
enabled!: boolean;
|
||||
autoLaunch!: boolean;
|
||||
|
||||
@IsBoolean()
|
||||
autoRegister!: boolean;
|
||||
|
||||
@ValidateIf(isEnabled)
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
issuerUrl!: string;
|
||||
buttonText!: string;
|
||||
|
||||
@ValidateIf(isEnabled)
|
||||
@IsNotEmpty()
|
||||
|
@ -22,20 +23,13 @@ export class SystemConfigOAuthDto {
|
|||
@IsString()
|
||||
clientSecret!: string;
|
||||
|
||||
@IsString()
|
||||
scope!: string;
|
||||
|
||||
@IsString()
|
||||
storageLabelClaim!: string;
|
||||
|
||||
@IsString()
|
||||
buttonText!: string;
|
||||
|
||||
@IsBoolean()
|
||||
autoRegister!: boolean;
|
||||
enabled!: boolean;
|
||||
|
||||
@IsBoolean()
|
||||
autoLaunch!: boolean;
|
||||
@ValidateIf(isEnabled)
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
issuerUrl!: string;
|
||||
|
||||
@IsBoolean()
|
||||
mobileOverrideEnabled!: boolean;
|
||||
|
@ -43,4 +37,14 @@ export class SystemConfigOAuthDto {
|
|||
@ValidateIf(isOverrideEnabled)
|
||||
@IsUrl()
|
||||
mobileRedirectUri!: string;
|
||||
|
||||
@IsString()
|
||||
scope!: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
signingAlgorithm!: string;
|
||||
|
||||
@IsString()
|
||||
storageLabelClaim!: string;
|
||||
}
|
||||
|
|
|
@ -88,17 +88,18 @@ export const defaults = Object.freeze<SystemConfig>({
|
|||
enabled: true,
|
||||
},
|
||||
oauth: {
|
||||
enabled: false,
|
||||
issuerUrl: '',
|
||||
autoLaunch: false,
|
||||
autoRegister: true,
|
||||
buttonText: 'Login with OAuth',
|
||||
clientId: '',
|
||||
clientSecret: '',
|
||||
enabled: false,
|
||||
issuerUrl: '',
|
||||
mobileOverrideEnabled: false,
|
||||
mobileRedirectUri: '',
|
||||
scope: 'openid email profile',
|
||||
signingAlgorithm: 'RS256',
|
||||
storageLabelClaim: 'preferred_username',
|
||||
buttonText: 'Login with OAuth',
|
||||
autoRegister: true,
|
||||
autoLaunch: false,
|
||||
},
|
||||
passwordLogin: {
|
||||
enabled: true,
|
||||
|
|
|
@ -98,6 +98,7 @@ const updatedConfig = Object.freeze<SystemConfig>({
|
|||
mobileOverrideEnabled: false,
|
||||
mobileRedirectUri: '',
|
||||
scope: 'openid email profile',
|
||||
signingAlgorithm: 'RS256',
|
||||
storageLabelClaim: 'preferred_username',
|
||||
},
|
||||
passwordLogin: {
|
||||
|
|
|
@ -77,17 +77,18 @@ export enum SystemConfigKey {
|
|||
|
||||
NEW_VERSION_CHECK_ENABLED = 'newVersionCheck.enabled',
|
||||
|
||||
OAUTH_ENABLED = 'oauth.enabled',
|
||||
OAUTH_ISSUER_URL = 'oauth.issuerUrl',
|
||||
OAUTH_AUTO_LAUNCH = 'oauth.autoLaunch',
|
||||
OAUTH_AUTO_REGISTER = 'oauth.autoRegister',
|
||||
OAUTH_BUTTON_TEXT = 'oauth.buttonText',
|
||||
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',
|
||||
OAUTH_ENABLED = 'oauth.enabled',
|
||||
OAUTH_ISSUER_URL = 'oauth.issuerUrl',
|
||||
OAUTH_MOBILE_OVERRIDE_ENABLED = 'oauth.mobileOverrideEnabled',
|
||||
OAUTH_MOBILE_REDIRECT_URI = 'oauth.mobileRedirectUri',
|
||||
OAUTH_SCOPE = 'oauth.scope',
|
||||
OAUTH_SIGNING_ALGORITHM = 'oauth.signingAlgorithm',
|
||||
OAUTH_STORAGE_LABEL_CLAIM = 'oauth.storageLabelClaim',
|
||||
|
||||
PASSWORD_LOGIN_ENABLED = 'passwordLogin.enabled',
|
||||
|
||||
|
@ -216,17 +217,18 @@ export interface SystemConfig {
|
|||
enabled: boolean;
|
||||
};
|
||||
oauth: {
|
||||
enabled: boolean;
|
||||
issuerUrl: string;
|
||||
autoLaunch: boolean;
|
||||
autoRegister: boolean;
|
||||
buttonText: string;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
scope: string;
|
||||
storageLabelClaim: string;
|
||||
buttonText: string;
|
||||
autoRegister: boolean;
|
||||
autoLaunch: boolean;
|
||||
enabled: boolean;
|
||||
issuerUrl: string;
|
||||
mobileOverrideEnabled: boolean;
|
||||
mobileRedirectUri: string;
|
||||
scope: string;
|
||||
signingAlgorithm: string;
|
||||
storageLabelClaim: string;
|
||||
};
|
||||
passwordLogin: {
|
||||
enabled: boolean;
|
||||
|
|
|
@ -45,8 +45,10 @@ export const oauth = {
|
|||
const redirectUri = location.href.split('?')[0];
|
||||
const { data } = await api.oauthApi.startOAuth({ oAuthConfigDto: { redirectUri } });
|
||||
window.location.href = data.url;
|
||||
return true;
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to login with OAuth');
|
||||
return false;
|
||||
}
|
||||
},
|
||||
login: (location: Location) => {
|
||||
|
|
|
@ -69,94 +69,106 @@
|
|||
>.
|
||||
</p>
|
||||
|
||||
<SettingSwitch {disabled} title="ENABLE" bind:checked={config.oauth.enabled} />
|
||||
<hr />
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label="ISSUER URL"
|
||||
bind:value={config.oauth.issuerUrl}
|
||||
required={true}
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
isEdited={!(config.oauth.issuerUrl == savedConfig.oauth.issuerUrl)}
|
||||
/>
|
||||
<SettingSwitch {disabled} title="ENABLE" subtitle="Login with OAuth" bind:checked={config.oauth.enabled} />
|
||||
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label="CLIENT ID"
|
||||
bind:value={config.oauth.clientId}
|
||||
required={true}
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
isEdited={!(config.oauth.clientId == savedConfig.oauth.clientId)}
|
||||
/>
|
||||
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label="CLIENT SECRET"
|
||||
bind:value={config.oauth.clientSecret}
|
||||
required={true}
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
isEdited={!(config.oauth.clientSecret == savedConfig.oauth.clientSecret)}
|
||||
/>
|
||||
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label="SCOPE"
|
||||
bind:value={config.oauth.scope}
|
||||
required={true}
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
isEdited={!(config.oauth.scope == savedConfig.oauth.scope)}
|
||||
/>
|
||||
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label="STORAGE LABEL CLAIM"
|
||||
desc="Automatically set the user's storage label to the value of this claim."
|
||||
bind:value={config.oauth.storageLabelClaim}
|
||||
required={true}
|
||||
disabled={disabled || !config.oauth.storageLabelClaim}
|
||||
isEdited={!(config.oauth.storageLabelClaim == savedConfig.oauth.storageLabelClaim)}
|
||||
/>
|
||||
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label="BUTTON TEXT"
|
||||
bind:value={config.oauth.buttonText}
|
||||
required={false}
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
isEdited={!(config.oauth.buttonText == savedConfig.oauth.buttonText)}
|
||||
/>
|
||||
|
||||
<SettingSwitch
|
||||
title="AUTO REGISTER"
|
||||
subtitle="Automatically register new users after signing in with OAuth"
|
||||
bind:checked={config.oauth.autoRegister}
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
/>
|
||||
|
||||
<SettingSwitch
|
||||
title="AUTO LAUNCH"
|
||||
subtitle="Start the OAuth login flow automatically upon navigating to the login page"
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
bind:checked={config.oauth.autoLaunch}
|
||||
/>
|
||||
|
||||
<SettingSwitch
|
||||
title="MOBILE REDIRECT URI OVERRIDE"
|
||||
subtitle="Enable when 'app.immich:/' is an invalid redirect URI."
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
on:click={() => handleToggleOverride()}
|
||||
bind:checked={config.oauth.mobileOverrideEnabled}
|
||||
/>
|
||||
|
||||
{#if config.oauth.mobileOverrideEnabled}
|
||||
{#if config.oauth.enabled}
|
||||
<hr />
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label="MOBILE REDIRECT URI"
|
||||
bind:value={config.oauth.mobileRedirectUri}
|
||||
label="ISSUER URL"
|
||||
bind:value={config.oauth.issuerUrl}
|
||||
required={true}
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
isEdited={!(config.oauth.mobileRedirectUri == savedConfig.oauth.mobileRedirectUri)}
|
||||
isEdited={!(config.oauth.issuerUrl == savedConfig.oauth.issuerUrl)}
|
||||
/>
|
||||
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label="CLIENT ID"
|
||||
bind:value={config.oauth.clientId}
|
||||
required={true}
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
isEdited={!(config.oauth.clientId == savedConfig.oauth.clientId)}
|
||||
/>
|
||||
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label="CLIENT SECRET"
|
||||
bind:value={config.oauth.clientSecret}
|
||||
required={true}
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
isEdited={!(config.oauth.clientSecret == savedConfig.oauth.clientSecret)}
|
||||
/>
|
||||
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label="SCOPE"
|
||||
bind:value={config.oauth.scope}
|
||||
required={true}
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
isEdited={!(config.oauth.scope == savedConfig.oauth.scope)}
|
||||
/>
|
||||
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label="SIGNING ALGORITHM"
|
||||
bind:value={config.oauth.signingAlgorithm}
|
||||
required={true}
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
isEdited={!(config.oauth.signingAlgorithm == savedConfig.oauth.signingAlgorithm)}
|
||||
/>
|
||||
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label="STORAGE LABEL CLAIM"
|
||||
desc="Automatically set the user's storage label to the value of this claim."
|
||||
bind:value={config.oauth.storageLabelClaim}
|
||||
required={true}
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
isEdited={!(config.oauth.storageLabelClaim == savedConfig.oauth.storageLabelClaim)}
|
||||
/>
|
||||
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label="BUTTON TEXT"
|
||||
bind:value={config.oauth.buttonText}
|
||||
required={false}
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
isEdited={!(config.oauth.buttonText == savedConfig.oauth.buttonText)}
|
||||
/>
|
||||
|
||||
<SettingSwitch
|
||||
title="AUTO REGISTER"
|
||||
subtitle="Automatically register new users after signing in with OAuth"
|
||||
bind:checked={config.oauth.autoRegister}
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
/>
|
||||
|
||||
<SettingSwitch
|
||||
title="AUTO LAUNCH"
|
||||
subtitle="Start the OAuth login flow automatically upon navigating to the login page"
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
bind:checked={config.oauth.autoLaunch}
|
||||
/>
|
||||
|
||||
<SettingSwitch
|
||||
title="MOBILE REDIRECT URI OVERRIDE"
|
||||
subtitle="Enable when 'app.immich:/' is an invalid redirect URI."
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
on:click={() => handleToggleOverride()}
|
||||
bind:checked={config.oauth.mobileOverrideEnabled}
|
||||
/>
|
||||
|
||||
{#if config.oauth.mobileOverrideEnabled}
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label="MOBILE REDIRECT URI"
|
||||
bind:value={config.oauth.mobileRedirectUri}
|
||||
required={true}
|
||||
disabled={disabled || !config.oauth.enabled}
|
||||
isEdited={!(config.oauth.mobileRedirectUri == savedConfig.oauth.mobileRedirectUri)}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<SettingButtonsRow
|
||||
|
|
|
@ -89,7 +89,11 @@
|
|||
const handleOAuthLogin = async () => {
|
||||
oauthLoading = true;
|
||||
oauthError = '';
|
||||
await oauth.authorize(window.location);
|
||||
const success = await oauth.authorize(window.location);
|
||||
if (!success) {
|
||||
oauthLoading = false;
|
||||
oauthError = 'Unable to login with OAuth';
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue