mirror of
https://github.com/immich-app/immich.git
synced 2025-06-01 04:36:19 -04: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
@ -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 ID | string | (required) | Required. Client ID (from previous step) |
|
||||||
| Client Secret | string | (required) | Required. Client Secret (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) |
|
| 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 |
|
| 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 |
|
| 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 |
|
| [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 |
|
| [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** | |
|
**mobileOverrideEnabled** | **bool** | |
|
||||||
**mobileRedirectUri** | **String** | |
|
**mobileRedirectUri** | **String** | |
|
||||||
**scope** | **String** | |
|
**scope** | **String** | |
|
||||||
|
**signingAlgorithm** | **String** | |
|
||||||
**storageLabelClaim** | **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)
|
[[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.mobileOverrideEnabled,
|
||||||
required this.mobileRedirectUri,
|
required this.mobileRedirectUri,
|
||||||
required this.scope,
|
required this.scope,
|
||||||
|
required this.signingAlgorithm,
|
||||||
required this.storageLabelClaim,
|
required this.storageLabelClaim,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -46,6 +47,8 @@ class SystemConfigOAuthDto {
|
|||||||
|
|
||||||
String scope;
|
String scope;
|
||||||
|
|
||||||
|
String signingAlgorithm;
|
||||||
|
|
||||||
String storageLabelClaim;
|
String storageLabelClaim;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -60,6 +63,7 @@ class SystemConfigOAuthDto {
|
|||||||
other.mobileOverrideEnabled == mobileOverrideEnabled &&
|
other.mobileOverrideEnabled == mobileOverrideEnabled &&
|
||||||
other.mobileRedirectUri == mobileRedirectUri &&
|
other.mobileRedirectUri == mobileRedirectUri &&
|
||||||
other.scope == scope &&
|
other.scope == scope &&
|
||||||
|
other.signingAlgorithm == signingAlgorithm &&
|
||||||
other.storageLabelClaim == storageLabelClaim;
|
other.storageLabelClaim == storageLabelClaim;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -75,10 +79,11 @@ class SystemConfigOAuthDto {
|
|||||||
(mobileOverrideEnabled.hashCode) +
|
(mobileOverrideEnabled.hashCode) +
|
||||||
(mobileRedirectUri.hashCode) +
|
(mobileRedirectUri.hashCode) +
|
||||||
(scope.hashCode) +
|
(scope.hashCode) +
|
||||||
|
(signingAlgorithm.hashCode) +
|
||||||
(storageLabelClaim.hashCode);
|
(storageLabelClaim.hashCode);
|
||||||
|
|
||||||
@override
|
@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() {
|
Map<String, dynamic> toJson() {
|
||||||
final json = <String, dynamic>{};
|
final json = <String, dynamic>{};
|
||||||
@ -92,6 +97,7 @@ class SystemConfigOAuthDto {
|
|||||||
json[r'mobileOverrideEnabled'] = this.mobileOverrideEnabled;
|
json[r'mobileOverrideEnabled'] = this.mobileOverrideEnabled;
|
||||||
json[r'mobileRedirectUri'] = this.mobileRedirectUri;
|
json[r'mobileRedirectUri'] = this.mobileRedirectUri;
|
||||||
json[r'scope'] = this.scope;
|
json[r'scope'] = this.scope;
|
||||||
|
json[r'signingAlgorithm'] = this.signingAlgorithm;
|
||||||
json[r'storageLabelClaim'] = this.storageLabelClaim;
|
json[r'storageLabelClaim'] = this.storageLabelClaim;
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
@ -114,6 +120,7 @@ class SystemConfigOAuthDto {
|
|||||||
mobileOverrideEnabled: mapValueOfType<bool>(json, r'mobileOverrideEnabled')!,
|
mobileOverrideEnabled: mapValueOfType<bool>(json, r'mobileOverrideEnabled')!,
|
||||||
mobileRedirectUri: mapValueOfType<String>(json, r'mobileRedirectUri')!,
|
mobileRedirectUri: mapValueOfType<String>(json, r'mobileRedirectUri')!,
|
||||||
scope: mapValueOfType<String>(json, r'scope')!,
|
scope: mapValueOfType<String>(json, r'scope')!,
|
||||||
|
signingAlgorithm: mapValueOfType<String>(json, r'signingAlgorithm')!,
|
||||||
storageLabelClaim: mapValueOfType<String>(json, r'storageLabelClaim')!,
|
storageLabelClaim: mapValueOfType<String>(json, r'storageLabelClaim')!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -172,6 +179,7 @@ class SystemConfigOAuthDto {
|
|||||||
'mobileOverrideEnabled',
|
'mobileOverrideEnabled',
|
||||||
'mobileRedirectUri',
|
'mobileRedirectUri',
|
||||||
'scope',
|
'scope',
|
||||||
|
'signingAlgorithm',
|
||||||
'storageLabelClaim',
|
'storageLabelClaim',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,11 @@ void main() {
|
|||||||
// TODO
|
// TODO
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// String signingAlgorithm
|
||||||
|
test('to test the property `signingAlgorithm`', () async {
|
||||||
|
// TODO
|
||||||
|
});
|
||||||
|
|
||||||
// String storageLabelClaim
|
// String storageLabelClaim
|
||||||
test('to test the property `storageLabelClaim`', () async {
|
test('to test the property `storageLabelClaim`', () async {
|
||||||
// TODO
|
// TODO
|
||||||
|
@ -9679,6 +9679,9 @@
|
|||||||
"scope": {
|
"scope": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"signingAlgorithm": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"storageLabelClaim": {
|
"storageLabelClaim": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
@ -9694,6 +9697,7 @@
|
|||||||
"mobileOverrideEnabled",
|
"mobileOverrideEnabled",
|
||||||
"mobileRedirectUri",
|
"mobileRedirectUri",
|
||||||
"scope",
|
"scope",
|
||||||
|
"signingAlgorithm",
|
||||||
"storageLabelClaim"
|
"storageLabelClaim"
|
||||||
],
|
],
|
||||||
"type": "object"
|
"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
|
* @memberof SystemConfigOAuthDto
|
||||||
*/
|
*/
|
||||||
'scope': string;
|
'scope': string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof SystemConfigOAuthDto
|
||||||
|
*/
|
||||||
|
'signingAlgorithm': string;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
|
@ -73,7 +73,7 @@ describe('AuthService', () => {
|
|||||||
|
|
||||||
jest.spyOn(generators, 'state').mockReturnValue('state');
|
jest.spyOn(generators, 'state').mockReturnValue('state');
|
||||||
jest.spyOn(Issuer, 'discover').mockResolvedValue({
|
jest.spyOn(Issuer, 'discover').mockResolvedValue({
|
||||||
id_token_signing_alg_values_supported: ['HS256'],
|
id_token_signing_alg_values_supported: ['RS256'],
|
||||||
Client: jest.fn().mockResolvedValue({
|
Client: jest.fn().mockResolvedValue({
|
||||||
issuer: {
|
issuer: {
|
||||||
metadata: {
|
metadata: {
|
||||||
|
@ -318,12 +318,25 @@ export class AuthService {
|
|||||||
const redirectUri = this.normalize(config, url.split('?')[0]);
|
const redirectUri = this.normalize(config, url.split('?')[0]);
|
||||||
const client = await this.getOAuthClient(config);
|
const client = await this.getOAuthClient(config);
|
||||||
const params = client.callbackParams(url);
|
const params = client.callbackParams(url);
|
||||||
const tokens = await client.callback(redirectUri, params, { state: params.state });
|
try {
|
||||||
return client.userinfo<OAuthProfile>(tokens.access_token || '');
|
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) {
|
private async getOAuthClient(config: SystemConfig) {
|
||||||
const { enabled, clientId, clientSecret, issuerUrl } = config.oauth;
|
const { enabled, clientId, clientSecret, issuerUrl, signingAlgorithm } = config.oauth;
|
||||||
|
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
throw new BadRequestException('OAuth2 is not enabled');
|
throw new BadRequestException('OAuth2 is not enabled');
|
||||||
@ -337,10 +350,7 @@ export class AuthService {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const issuer = await Issuer.discover(issuerUrl);
|
const issuer = await Issuer.discover(issuerUrl);
|
||||||
const algorithms = (issuer.id_token_signing_alg_values_supported || []) as string[];
|
metadata.id_token_signed_response_alg = signingAlgorithm;
|
||||||
if (algorithms[0] === 'HS256') {
|
|
||||||
metadata.id_token_signed_response_alg = algorithms[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return new issuer.Client(metadata);
|
return new issuer.Client(metadata);
|
||||||
} catch (error: any | AggregateError) {
|
} catch (error: any | AggregateError) {
|
||||||
|
@ -5,12 +5,13 @@ const isOverrideEnabled = (config: SystemConfigOAuthDto) => config.mobileOverrid
|
|||||||
|
|
||||||
export class SystemConfigOAuthDto {
|
export class SystemConfigOAuthDto {
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
enabled!: boolean;
|
autoLaunch!: boolean;
|
||||||
|
|
||||||
|
@IsBoolean()
|
||||||
|
autoRegister!: boolean;
|
||||||
|
|
||||||
@ValidateIf(isEnabled)
|
|
||||||
@IsNotEmpty()
|
|
||||||
@IsString()
|
@IsString()
|
||||||
issuerUrl!: string;
|
buttonText!: string;
|
||||||
|
|
||||||
@ValidateIf(isEnabled)
|
@ValidateIf(isEnabled)
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@ -22,20 +23,13 @@ export class SystemConfigOAuthDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
clientSecret!: string;
|
clientSecret!: string;
|
||||||
|
|
||||||
@IsString()
|
|
||||||
scope!: string;
|
|
||||||
|
|
||||||
@IsString()
|
|
||||||
storageLabelClaim!: string;
|
|
||||||
|
|
||||||
@IsString()
|
|
||||||
buttonText!: string;
|
|
||||||
|
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
autoRegister!: boolean;
|
enabled!: boolean;
|
||||||
|
|
||||||
@IsBoolean()
|
@ValidateIf(isEnabled)
|
||||||
autoLaunch!: boolean;
|
@IsNotEmpty()
|
||||||
|
@IsString()
|
||||||
|
issuerUrl!: string;
|
||||||
|
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
mobileOverrideEnabled!: boolean;
|
mobileOverrideEnabled!: boolean;
|
||||||
@ -43,4 +37,14 @@ export class SystemConfigOAuthDto {
|
|||||||
@ValidateIf(isOverrideEnabled)
|
@ValidateIf(isOverrideEnabled)
|
||||||
@IsUrl()
|
@IsUrl()
|
||||||
mobileRedirectUri!: string;
|
mobileRedirectUri!: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
scope!: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
signingAlgorithm!: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
storageLabelClaim!: string;
|
||||||
}
|
}
|
||||||
|
@ -88,17 +88,18 @@ export const defaults = Object.freeze<SystemConfig>({
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
},
|
},
|
||||||
oauth: {
|
oauth: {
|
||||||
enabled: false,
|
autoLaunch: false,
|
||||||
issuerUrl: '',
|
autoRegister: true,
|
||||||
|
buttonText: 'Login with OAuth',
|
||||||
clientId: '',
|
clientId: '',
|
||||||
clientSecret: '',
|
clientSecret: '',
|
||||||
|
enabled: false,
|
||||||
|
issuerUrl: '',
|
||||||
mobileOverrideEnabled: false,
|
mobileOverrideEnabled: false,
|
||||||
mobileRedirectUri: '',
|
mobileRedirectUri: '',
|
||||||
scope: 'openid email profile',
|
scope: 'openid email profile',
|
||||||
|
signingAlgorithm: 'RS256',
|
||||||
storageLabelClaim: 'preferred_username',
|
storageLabelClaim: 'preferred_username',
|
||||||
buttonText: 'Login with OAuth',
|
|
||||||
autoRegister: true,
|
|
||||||
autoLaunch: false,
|
|
||||||
},
|
},
|
||||||
passwordLogin: {
|
passwordLogin: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
@ -98,6 +98,7 @@ const updatedConfig = Object.freeze<SystemConfig>({
|
|||||||
mobileOverrideEnabled: false,
|
mobileOverrideEnabled: false,
|
||||||
mobileRedirectUri: '',
|
mobileRedirectUri: '',
|
||||||
scope: 'openid email profile',
|
scope: 'openid email profile',
|
||||||
|
signingAlgorithm: 'RS256',
|
||||||
storageLabelClaim: 'preferred_username',
|
storageLabelClaim: 'preferred_username',
|
||||||
},
|
},
|
||||||
passwordLogin: {
|
passwordLogin: {
|
||||||
|
@ -77,17 +77,18 @@ export enum SystemConfigKey {
|
|||||||
|
|
||||||
NEW_VERSION_CHECK_ENABLED = 'newVersionCheck.enabled',
|
NEW_VERSION_CHECK_ENABLED = 'newVersionCheck.enabled',
|
||||||
|
|
||||||
OAUTH_ENABLED = 'oauth.enabled',
|
OAUTH_AUTO_LAUNCH = 'oauth.autoLaunch',
|
||||||
OAUTH_ISSUER_URL = 'oauth.issuerUrl',
|
OAUTH_AUTO_REGISTER = 'oauth.autoRegister',
|
||||||
|
OAUTH_BUTTON_TEXT = 'oauth.buttonText',
|
||||||
OAUTH_CLIENT_ID = 'oauth.clientId',
|
OAUTH_CLIENT_ID = 'oauth.clientId',
|
||||||
OAUTH_CLIENT_SECRET = 'oauth.clientSecret',
|
OAUTH_CLIENT_SECRET = 'oauth.clientSecret',
|
||||||
OAUTH_SCOPE = 'oauth.scope',
|
OAUTH_ENABLED = 'oauth.enabled',
|
||||||
OAUTH_STORAGE_LABEL_CLAIM = 'oauth.storageLabelClaim',
|
OAUTH_ISSUER_URL = 'oauth.issuerUrl',
|
||||||
OAUTH_AUTO_LAUNCH = 'oauth.autoLaunch',
|
|
||||||
OAUTH_BUTTON_TEXT = 'oauth.buttonText',
|
|
||||||
OAUTH_AUTO_REGISTER = 'oauth.autoRegister',
|
|
||||||
OAUTH_MOBILE_OVERRIDE_ENABLED = 'oauth.mobileOverrideEnabled',
|
OAUTH_MOBILE_OVERRIDE_ENABLED = 'oauth.mobileOverrideEnabled',
|
||||||
OAUTH_MOBILE_REDIRECT_URI = 'oauth.mobileRedirectUri',
|
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',
|
PASSWORD_LOGIN_ENABLED = 'passwordLogin.enabled',
|
||||||
|
|
||||||
@ -216,17 +217,18 @@ export interface SystemConfig {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
};
|
};
|
||||||
oauth: {
|
oauth: {
|
||||||
enabled: boolean;
|
autoLaunch: boolean;
|
||||||
issuerUrl: string;
|
autoRegister: boolean;
|
||||||
|
buttonText: string;
|
||||||
clientId: string;
|
clientId: string;
|
||||||
clientSecret: string;
|
clientSecret: string;
|
||||||
scope: string;
|
enabled: boolean;
|
||||||
storageLabelClaim: string;
|
issuerUrl: string;
|
||||||
buttonText: string;
|
|
||||||
autoRegister: boolean;
|
|
||||||
autoLaunch: boolean;
|
|
||||||
mobileOverrideEnabled: boolean;
|
mobileOverrideEnabled: boolean;
|
||||||
mobileRedirectUri: string;
|
mobileRedirectUri: string;
|
||||||
|
scope: string;
|
||||||
|
signingAlgorithm: string;
|
||||||
|
storageLabelClaim: string;
|
||||||
};
|
};
|
||||||
passwordLogin: {
|
passwordLogin: {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
|
@ -45,8 +45,10 @@ export const oauth = {
|
|||||||
const redirectUri = location.href.split('?')[0];
|
const redirectUri = location.href.split('?')[0];
|
||||||
const { data } = await api.oauthApi.startOAuth({ oAuthConfigDto: { redirectUri } });
|
const { data } = await api.oauthApi.startOAuth({ oAuthConfigDto: { redirectUri } });
|
||||||
window.location.href = data.url;
|
window.location.href = data.url;
|
||||||
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, 'Unable to login with OAuth');
|
handleError(error, 'Unable to login with OAuth');
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
login: (location: Location) => {
|
login: (location: Location) => {
|
||||||
|
@ -69,94 +69,106 @@
|
|||||||
>.
|
>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<SettingSwitch {disabled} title="ENABLE" bind:checked={config.oauth.enabled} />
|
<SettingSwitch {disabled} title="ENABLE" subtitle="Login with OAuth" 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)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SettingInputField
|
{#if config.oauth.enabled}
|
||||||
inputType={SettingInputFieldType.TEXT}
|
<hr />
|
||||||
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}
|
|
||||||
<SettingInputField
|
<SettingInputField
|
||||||
inputType={SettingInputFieldType.TEXT}
|
inputType={SettingInputFieldType.TEXT}
|
||||||
label="MOBILE REDIRECT URI"
|
label="ISSUER URL"
|
||||||
bind:value={config.oauth.mobileRedirectUri}
|
bind:value={config.oauth.issuerUrl}
|
||||||
required={true}
|
required={true}
|
||||||
disabled={disabled || !config.oauth.enabled}
|
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}
|
{/if}
|
||||||
|
|
||||||
<SettingButtonsRow
|
<SettingButtonsRow
|
||||||
|
@ -89,7 +89,11 @@
|
|||||||
const handleOAuthLogin = async () => {
|
const handleOAuthLogin = async () => {
|
||||||
oauthLoading = true;
|
oauthLoading = true;
|
||||||
oauthError = '';
|
oauthError = '';
|
||||||
await oauth.authorize(window.location);
|
const success = await oauth.authorize(window.location);
|
||||||
|
if (!success) {
|
||||||
|
oauthLoading = false;
|
||||||
|
oauthError = 'Unable to login with OAuth';
|
||||||
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user