Re-enable ForwardAuth and have ForwardAuth work using email address (#1426)

* Fallback to using email for auth if user not defined by forwardauth

* Re-enable RemoteAuth

* Update build env
This commit is contained in:
Chris Debenham
2025-10-24 14:57:08 +11:00
committed by GitHub
parent 1827ae23bd
commit 2f6ae855dd
8 changed files with 46 additions and 3 deletions

View File

@@ -6,6 +6,8 @@ import com.adityachandel.booklore.exception.ApiError;
import com.adityachandel.booklore.model.dto.UserCreateRequest;
import com.adityachandel.booklore.model.dto.request.RefreshTokenRequest;
import com.adityachandel.booklore.model.dto.request.UserLoginRequest;
import com.adityachandel.booklore.model.entity.BookLoreUserEntity;
import com.adityachandel.booklore.repository.UserRepository;
import com.adityachandel.booklore.service.user.UserProvisioningService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -21,6 +23,7 @@ import org.springframework.web.bind.annotation.*;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
@Tag(name = "Authentication", description = "Endpoints for user authentication, registration, and token management")
@Slf4j
@@ -32,6 +35,7 @@ public class AuthenticationController {
private final AppProperties appProperties;
private final UserProvisioningService userProvisioningService;
private final AuthenticationService authenticationService;
private final UserRepository userRepository;
@Operation(summary = "Register a new user", description = "Register a new user. Only admins can register users.")
@ApiResponses({
@@ -81,6 +85,15 @@ public class AuthenticationController {
log.debug("Remote-Auth: retrieved values from headers: name: {}, username: {}, email: {}, groups: {}", name, username, email, groups);
log.debug("Remote-Auth: remote auth settings: {}", appProperties.getRemoteAuth());
if ((username == null || username.isEmpty()) && (email != null && !email.isEmpty())) {
log.debug("Remote-Auth: username is empty, trying to find user by email: {}", email);
Optional<BookLoreUserEntity> user = userRepository.findByEmail(email);
if (user.isPresent()) {
username = user.get().getUsername();
log.debug("Remote-Auth: found user by email, username: {}", username);
}
}
return authenticationService.loginRemote(name, username, email, groups);
}
}

View File

@@ -8,5 +8,6 @@ import lombok.*;
@NoArgsConstructor
public class PublicAppSetting {
private boolean oidcEnabled;
private boolean remoteAuthEnabled;
private OidcProviderDetails oidcProviderDetails;
}

View File

@@ -12,6 +12,8 @@ public interface UserRepository extends JpaRepository<BookLoreUserEntity, Long>
Optional<BookLoreUserEntity> findByUsername(String username);
Optional<BookLoreUserEntity> findByEmail(String email);
Optional<BookLoreUserEntity> findById(Long id);
List<BookLoreUserEntity> findAllByLibraries_Id(Long libraryId);

View File

@@ -73,6 +73,7 @@ public class AppSettingService {
PublicAppSetting.PublicAppSettingBuilder builder = PublicAppSetting.builder();
builder.oidcEnabled(Boolean.parseBoolean(settingPersistenceHelper.getOrCreateSetting(AppSettingKey.OIDC_ENABLED, "false")));
builder.remoteAuthEnabled(appProperties.getRemoteAuth().isEnabled());
builder.oidcProviderDetails(settingPersistenceHelper.getJsonSetting(settingsMap, AppSettingKey.OIDC_PROVIDER_DETAILS, OidcProviderDetails.class, null, false));
return builder.build();
@@ -111,4 +112,4 @@ public class AppSettingService {
return builder.build();
}
}
}

View File

@@ -94,6 +94,18 @@ export function initializeAuthFactory() {
resolve();
});
} else if (publicSettings.remoteAuthEnabled) {
authService.remoteLogin().subscribe({
next: () => {
authInitService.markAsInitialized();
resolve();
},
error: err => {
console.error('[Remote Login] failed:', err);
authInitService.markAsInitialized();
resolve();
}
});
} else {
if (forceLocalOnly) {
console.warn('[OIDC] Forced local-only login via ?localOnly=true');

View File

@@ -8,6 +8,7 @@ import {AuthService} from './auth.service';
export interface PublicAppSettings {
oidcEnabled: boolean;
remoteAuthEnabled: boolean;
oidcProviderDetails: OidcProviderDetails;
}
@@ -76,6 +77,7 @@ export class AppSettingsService {
private syncPublicSettings(appSettings: AppSettings): void {
const updatedPublicSettings: PublicAppSettings = {
oidcEnabled: appSettings.oidcEnabled,
remoteAuthEnabled: appSettings.remoteAuthEnabled,
oidcProviderDetails: appSettings.oidcProviderDetails
};
const current = this.publicAppSettingsSubject.value;
@@ -83,6 +85,7 @@ export class AppSettingsService {
if (
!current ||
current.oidcEnabled !== updatedPublicSettings.oidcEnabled ||
current.remoteAuthEnabled !== updatedPublicSettings.remoteAuthEnabled ||
JSON.stringify(current.oidcProviderDetails) !== JSON.stringify(updatedPublicSettings.oidcProviderDetails)
) {
this.publicAppSettingsSubject.next(updatedPublicSettings);

View File

@@ -47,6 +47,17 @@ export class AuthService {
);
}
remoteLogin(): Observable<{ accessToken: string; refreshToken: string, isDefaultPassword: string }> {
return this.http.get<{ accessToken: string; refreshToken: string, isDefaultPassword: string }>(`${this.apiUrl}/remote`).pipe(
tap((response) => {
if (response.accessToken && response.refreshToken) {
this.saveInternalTokens(response.accessToken, response.refreshToken);
this.initializeWebSocketConnection();
}
})
);
}
saveInternalTokens(accessToken: string, refreshToken: string): void {
localStorage.setItem('accessToken_Internal', accessToken);
localStorage.setItem('refreshToken_Internal', refreshToken);

View File

@@ -1,6 +1,6 @@
services:
backend:
image: gradle:8-jdk21-alpine
image: gradle:9-jdk25-alpine
command: sh -c "cd /booklore-api && ./gradlew bootRun"
ports:
- "${BACKEND_PORT:-8080}:8080"
@@ -58,4 +58,4 @@ services:
volumes:
node_modules:
backend_db:
backend_db: