mirror of
https://github.com/immich-app/immich.git
synced 2025-12-22 01:11:20 +03: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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user