refactor: rename keycloakId to authentikId in codebase
Some checks failed
Some checks failed
This commit is contained in:
parent
7eebd12699
commit
8317349507
12 changed files with 270 additions and 48 deletions
|
@ -12,7 +12,7 @@ Content-Type: application/json
|
||||||
Authorization: Bearer {{token}}
|
Authorization: Bearer {{token}}
|
||||||
|
|
||||||
{
|
{
|
||||||
"keycloakId": "52cc0208-a3bd-4367-94c5-0404b016a003",
|
"authentikId": "52cc0208-a3bd-4367-94c5-0404b016a003",
|
||||||
"username": "john.doe"
|
"username": "john.doe"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ public class DepositController {
|
||||||
Stripe.apiKey = stripeKey;
|
Stripe.apiKey = stripeKey;
|
||||||
|
|
||||||
KeycloakUserDto userData = getKeycloakUserInfo(token);
|
KeycloakUserDto userData = getKeycloakUserInfo(token);
|
||||||
Optional<UserEntity> optionalUserEntity = this.userRepository.findOneByKeycloakId(userData.getSub());
|
Optional<UserEntity> optionalUserEntity = this.userRepository.findOneByAuthentikId(userData.getSub());
|
||||||
|
|
||||||
SessionCreateParams params = SessionCreateParams.builder()
|
SessionCreateParams params = SessionCreateParams.builder()
|
||||||
.addLineItem(SessionCreateParams.LineItem.builder()
|
.addLineItem(SessionCreateParams.LineItem.builder()
|
||||||
|
@ -76,7 +76,7 @@ public class DepositController {
|
||||||
private KeycloakUserDto getKeycloakUserInfo(String token) {
|
private KeycloakUserDto getKeycloakUserInfo(String token) {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.set("Authorization", token);
|
headers.set("Authorization", token);
|
||||||
ResponseEntity<KeycloakUserDto> response = this.restTemplate.exchange("http://localhost:9090/realms/LF12/protocol/openid-connect/userinfo", HttpMethod.GET, new HttpEntity<>(headers), KeycloakUserDto.class);
|
ResponseEntity<KeycloakUserDto> response = this.restTemplate.exchange("https://oauth.simonis.lol/application/o/userinfo/", HttpMethod.GET, new HttpEntity<>(headers), KeycloakUserDto.class);
|
||||||
|
|
||||||
return response.getBody();
|
return response.getBody();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package de.szut.casino.security;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.config.Customizer;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
@ -19,6 +20,8 @@ public class SecurityConfig {
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
http
|
http
|
||||||
|
.cors(Customizer.withDefaults())
|
||||||
|
.csrf(csrf -> csrf.disable())
|
||||||
.authorizeHttpRequests(auth -> {
|
.authorizeHttpRequests(auth -> {
|
||||||
auth.requestMatchers("/swagger/**", "/swagger-ui/**", "/health").permitAll()
|
auth.requestMatchers("/swagger/**", "/swagger-ui/**", "/health").permitAll()
|
||||||
.anyRequest().authenticated();
|
.anyRequest().authenticated();
|
||||||
|
@ -35,7 +38,7 @@ public class SecurityConfig {
|
||||||
CorsConfiguration configuration = new CorsConfiguration();
|
CorsConfiguration configuration = new CorsConfiguration();
|
||||||
configuration.setAllowedOrigins(List.of("http://localhost:4200"));
|
configuration.setAllowedOrigins(List.of("http://localhost:4200"));
|
||||||
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
|
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
|
||||||
configuration.setAllowedHeaders(Arrays.asList("authorization", "content-type", "x-auth-token"));
|
configuration.setAllowedHeaders(Arrays.asList("authorization", "content-type", "x-auth-token", "Access-Control-Allow-Origin"));
|
||||||
configuration.setExposedHeaders(List.of("x-auth-token"));
|
configuration.setExposedHeaders(List.of("x-auth-token"));
|
||||||
configuration.setAllowCredentials(true);
|
configuration.setAllowCredentials(true);
|
||||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||||
|
|
|
@ -19,14 +19,14 @@ public class UserEntity {
|
||||||
@GeneratedValue
|
@GeneratedValue
|
||||||
private Long id;
|
private Long id;
|
||||||
@Column(unique = true)
|
@Column(unique = true)
|
||||||
private String keycloakId;
|
private String authentikId; // Changed from keycloakId to authentikId
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
@Column(precision = 19, scale = 2)
|
@Column(precision = 19, scale = 2)
|
||||||
private BigDecimal balance;
|
private BigDecimal balance;
|
||||||
|
|
||||||
public UserEntity(String keycloakId, String username, BigDecimal balance) {
|
public UserEntity(String authentikId, String username, BigDecimal balance) {
|
||||||
this.keycloakId = keycloakId;
|
this.authentikId = authentikId;
|
||||||
this.username = username;
|
this.username = username;
|
||||||
this.balance = balance;
|
this.balance = balance;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,11 @@ import java.math.BigDecimal;
|
||||||
@Service
|
@Service
|
||||||
public class UserMappingService {
|
public class UserMappingService {
|
||||||
public GetUserDto mapToGetUserDto(UserEntity user) {
|
public GetUserDto mapToGetUserDto(UserEntity user) {
|
||||||
return new GetUserDto(user.getKeycloakId(), user.getUsername(), user.getBalance());
|
return new GetUserDto(user.getAuthentikId(), user.getUsername(), user.getBalance());
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserEntity mapToUserEntity(CreateUserDto createUserDto) {
|
public UserEntity mapToUserEntity(CreateUserDto createUserDto) {
|
||||||
return new UserEntity(createUserDto.getAuthentikId(), createUserDto.getUsername(), BigDecimal.ZERO); }
|
return new UserEntity(createUserDto.getAuthentikId(), createUserDto.getUsername(), BigDecimal.ZERO);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,8 @@ import java.util.Optional;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public interface UserRepository extends JpaRepository<UserEntity, Long> {
|
public interface UserRepository extends JpaRepository<UserEntity, Long> {
|
||||||
@Query("SELECT u FROM UserEntity u WHERE u.keycloakId = ?1")
|
@Query("SELECT u FROM UserEntity u WHERE u.authentikId = ?1")
|
||||||
Optional<UserEntity> findOneByKeycloakId(String keycloakId);
|
Optional<UserEntity> findOneByAuthentikId(String authentikId);
|
||||||
|
|
||||||
boolean existsByKeycloakId(String keycloakId);
|
boolean existsByAuthentikId(String authentikId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,33 +31,42 @@ public class UserService {
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GetUserDto getUser(String keycloakId) {
|
public GetUserDto getUser(String authentikId) {
|
||||||
Optional<UserEntity> user = this.userRepository.findOneByKeycloakId(keycloakId);
|
Optional<UserEntity> user = this.userRepository.findOneByAuthentikId(authentikId);
|
||||||
|
|
||||||
return user.map(userEntity -> mappingService.mapToGetUserDto(userEntity)).orElse(null);
|
return user.map(userEntity -> mappingService.mapToGetUserDto(userEntity)).orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GetUserDto getCurrentUser(String token) {
|
public GetUserDto getCurrentUser(String token) {
|
||||||
KeycloakUserDto userData = getKeycloakUserInfo(token);
|
KeycloakUserDto userData = getAuthentikUserInfo(token);
|
||||||
|
|
||||||
if (userData == null) {
|
if (userData == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Optional<UserEntity> user = this.userRepository.findOneByKeycloakId(userData.getSub());
|
Optional<UserEntity> user = this.userRepository.findOneByAuthentikId(userData.getSub());
|
||||||
|
|
||||||
return user.map(userEntity -> mappingService.mapToGetUserDto(userEntity)).orElse(null);
|
return user.map(userEntity -> mappingService.mapToGetUserDto(userEntity)).orElse(null);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private KeycloakUserDto getKeycloakUserInfo(String token) {
|
private KeycloakUserDto getAuthentikUserInfo(String token) {
|
||||||
|
try {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.set("Authorization", token);
|
headers.set("Authorization", token);
|
||||||
ResponseEntity<KeycloakUserDto> response = this.http.exchange("https://oauth.simonis.lol/application/o/userinfo/", HttpMethod.GET, new HttpEntity<>(headers), KeycloakUserDto.class);
|
ResponseEntity<KeycloakUserDto> response = this.http.exchange(
|
||||||
|
"https://oauth.simonis.lol/application/o/userinfo/",
|
||||||
|
HttpMethod.GET,
|
||||||
|
new HttpEntity<>(headers),
|
||||||
|
KeycloakUserDto.class
|
||||||
|
);
|
||||||
|
|
||||||
return response.getBody();
|
return response.getBody();
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Error fetching user info from Authentik: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean exists(String keycloakId) {
|
public boolean exists(String authentikId) {
|
||||||
return userRepository.existsByKeycloakId(keycloakId);
|
return userRepository.existsByAuthentikId(authentikId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,15 +45,15 @@ public class UserControllerTest {
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
getUserDto = new GetUserDto();
|
getUserDto = new GetUserDto();
|
||||||
getUserDto.setKeycloakId(TEST_ID);
|
getUserDto.setAuthentikId(TEST_ID);
|
||||||
getUserDto.setUsername("testuser");
|
getUserDto.setUsername("testuser");
|
||||||
|
|
||||||
testUser = new UserEntity();
|
testUser = new UserEntity();
|
||||||
testUser.setKeycloakId(TEST_ID);
|
testUser.setAuthentikId(TEST_ID);
|
||||||
testUser.setUsername("testuser");
|
testUser.setUsername("testuser");
|
||||||
|
|
||||||
createUserDto = new CreateUserDto();
|
createUserDto = new CreateUserDto();
|
||||||
createUserDto.setKeycloakId(TEST_ID);
|
createUserDto.setAuthentikId(TEST_ID);
|
||||||
createUserDto.setUsername("testuser");
|
createUserDto.setUsername("testuser");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ public class UserControllerTest {
|
||||||
|
|
||||||
mockMvc.perform(get("/user/" + TEST_ID))
|
mockMvc.perform(get("/user/" + TEST_ID))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.keycloakId").value(TEST_ID))
|
.andExpect(jsonPath("$.authentikId").value(TEST_ID))
|
||||||
.andExpect(jsonPath("$.username").value("testuser"));
|
.andExpect(jsonPath("$.username").value("testuser"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ public class UserControllerTest {
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(createUserDto)))
|
.content(objectMapper.writeValueAsString(createUserDto)))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.keycloakId").value(TEST_ID))
|
.andExpect(jsonPath("$.authentikId").value(TEST_ID))
|
||||||
.andExpect(jsonPath("$.username").value("testuser"));
|
.andExpect(jsonPath("$.username").value("testuser"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ public class UserControllerTest {
|
||||||
mockMvc.perform(get("/user")
|
mockMvc.perform(get("/user")
|
||||||
.header("Authorization", AUTH_TOKEN))
|
.header("Authorization", AUTH_TOKEN))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.keycloakId").value(TEST_ID))
|
.andExpect(jsonPath("$.authentikId").value(TEST_ID))
|
||||||
.andExpect(jsonPath("$.username").value("testuser"));
|
.andExpect(jsonPath("$.username").value("testuser"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { ChangeDetectionStrategy, Component, inject, OnInit } from '@angular/cor
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { AuthService } from '../../service/auth.service';
|
import { AuthService } from '../../service/auth.service';
|
||||||
import { User } from '../../model/User';
|
import { User } from '../../model/User';
|
||||||
|
import { OAuthService } from 'angular-oauth2-oidc';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-login-success',
|
selector: 'app-login-success',
|
||||||
|
@ -13,7 +14,48 @@ import { User } from '../../model/User';
|
||||||
})
|
})
|
||||||
export default class LoginSuccessComponent implements OnInit {
|
export default class LoginSuccessComponent implements OnInit {
|
||||||
private authService: AuthService = inject(AuthService);
|
private authService: AuthService = inject(AuthService);
|
||||||
|
private oauthService: OAuthService = inject(OAuthService);
|
||||||
|
private router: Router = inject(Router);
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
console.log(this.authService.getUser())
|
console.log('Login success component initialized');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Handle code flow without throwing errors
|
||||||
|
const success = await this.oauthService.loadDiscoveryDocumentAndTryLogin();
|
||||||
|
console.log('Manual login attempt result:', success);
|
||||||
|
|
||||||
|
// If we have a valid access token, the user should be loaded in AuthService
|
||||||
|
const user = this.authService.getUser();
|
||||||
|
console.log('Login success user:', user);
|
||||||
|
|
||||||
|
// Check if we're authenticated
|
||||||
|
if (this.oauthService.hasValidAccessToken()) {
|
||||||
|
console.log('Valid access token found');
|
||||||
|
this.router.navigate(['/home']);
|
||||||
|
} else {
|
||||||
|
console.log('No valid access token, waiting for auth service to complete');
|
||||||
|
// Wait a bit and check if we've been authenticated in the meantime
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.oauthService.hasValidAccessToken() || this.authService.getUser()) {
|
||||||
|
console.log('Now authenticated, navigating to home');
|
||||||
|
this.router.navigate(['/home']);
|
||||||
|
} else {
|
||||||
|
console.log('Still not authenticated, redirecting to login page');
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
}
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error during login callback:', err);
|
||||||
|
// Wait a bit in case token processing is happening elsewhere
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.authService.isLoggedIn()) {
|
||||||
|
this.router.navigate(['/home']);
|
||||||
|
} else {
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
}
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { inject, Injectable } from '@angular/core';
|
import { inject, Injectable } from '@angular/core';
|
||||||
import { AuthConfig, OAuthService } from 'angular-oauth2-oidc';
|
import { AuthConfig, OAuthEvent, OAuthService } from 'angular-oauth2-oidc';
|
||||||
import { UserService } from './user.service';
|
import { UserService } from './user.service';
|
||||||
import { User } from '../model/User';
|
import { User } from '../model/User';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { environment } from '../../environments/environment';
|
import { environment } from '../../environments/environment';
|
||||||
|
import { catchError, from, of, tap } from 'rxjs';
|
||||||
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
|
@ -19,9 +20,16 @@ export class AuthService {
|
||||||
redirectUri: window.location.origin + '/auth/callback',
|
redirectUri: window.location.origin + '/auth/callback',
|
||||||
oidc: true,
|
oidc: true,
|
||||||
requestAccessToken: true,
|
requestAccessToken: true,
|
||||||
|
// Explicitly set token endpoint since discovery is failing
|
||||||
|
tokenEndpoint: 'https://oauth.simonis.lol/application/o/token/',
|
||||||
|
userinfoEndpoint: 'https://oauth.simonis.lol/application/o/userinfo/',
|
||||||
|
// Loosen validation since Authentik might not fully conform to the spec
|
||||||
strictDiscoveryDocumentValidation: false,
|
strictDiscoveryDocumentValidation: false,
|
||||||
skipIssuerCheck: true,
|
skipIssuerCheck: true,
|
||||||
disableAtHashCheck: true,
|
disableAtHashCheck: true,
|
||||||
|
requireHttps: false,
|
||||||
|
showDebugInformation: true, // Enable for debugging
|
||||||
|
sessionChecksEnabled: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
private userService: UserService = inject(UserService);
|
private userService: UserService = inject(UserService);
|
||||||
|
@ -31,28 +39,176 @@ export class AuthService {
|
||||||
private user: User | null = null;
|
private user: User | null = null;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
console.log('Auth service initializing');
|
||||||
this.oauthService.configure(this.authConfig);
|
this.oauthService.configure(this.authConfig);
|
||||||
this.oauthService.events.subscribe((event) => {
|
this.setupEventHandling();
|
||||||
if (event.type === 'token_received') {
|
|
||||||
this.oauthService.loadUserProfile().then((profile) => {
|
// Check if we're on the callback page
|
||||||
console.log(profile);
|
const hasAuthParams = window.location.search.includes('code=') ||
|
||||||
this.fromUserProfile(profile).subscribe((user) => {
|
window.location.search.includes('token=') ||
|
||||||
this.user = user;
|
window.location.search.includes('id_token=');
|
||||||
this.router.navigate(['home']);
|
|
||||||
});
|
if (hasAuthParams) {
|
||||||
|
console.log('Auth parameters detected in URL, processing code flow');
|
||||||
|
// We're in the OAuth callback
|
||||||
|
this.processCodeFlow();
|
||||||
|
} else {
|
||||||
|
// Normal app startup
|
||||||
|
console.log('Normal startup, checking for existing session');
|
||||||
|
this.checkExistingSession();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private processCodeFlow() {
|
||||||
|
// Try to exchange the authorization code for tokens
|
||||||
|
this.oauthService.tryLogin({
|
||||||
|
onTokenReceived: context => {
|
||||||
|
console.log('Token received in code flow:', context);
|
||||||
|
// Manually create a token_received event
|
||||||
|
this.handleSuccessfulLogin();
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('Error processing code flow:', err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private checkExistingSession() {
|
||||||
|
// Try login on startup
|
||||||
|
this.oauthService.loadDiscoveryDocumentAndTryLogin().then((isLoggedIn) => {
|
||||||
|
console.log('Initial login attempt result:', isLoggedIn);
|
||||||
|
|
||||||
|
if (isLoggedIn && !this.user) {
|
||||||
|
this.handleSuccessfulLogin();
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('Error during initial login attempt:', err);
|
||||||
});
|
});
|
||||||
this.oauthService.loadDiscoveryDocumentAndTryLogin().then(() => {
|
}
|
||||||
|
|
||||||
|
private setupEventHandling() {
|
||||||
|
this.oauthService.events.subscribe((event: OAuthEvent) => {
|
||||||
|
console.log('Auth event:', event);
|
||||||
|
|
||||||
|
if (event.type === 'token_received') {
|
||||||
|
this.handleSuccessfulLogin();
|
||||||
|
} else if (event.type === 'token_refresh_error' || event.type === 'token_expires') {
|
||||||
|
console.warn('Token issue detected:', event.type);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleSuccessfulLogin() {
|
||||||
|
console.log('Access token received, loading user profile');
|
||||||
|
console.log('Token valid:', this.oauthService.hasValidAccessToken());
|
||||||
|
console.log('ID token valid:', this.oauthService.hasValidIdToken());
|
||||||
|
console.log('Access token:', this.oauthService.getAccessToken());
|
||||||
|
|
||||||
|
// Extract claims from id token if available
|
||||||
|
let claims = this.oauthService.getIdentityClaims();
|
||||||
|
console.log('ID token claims:', claims);
|
||||||
|
|
||||||
|
// If we have claims, use that as profile
|
||||||
|
if (claims && (claims['sub'] || claims['email'])) {
|
||||||
|
console.log('Using ID token claims as profile');
|
||||||
|
this.processUserProfile(claims);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise try to load user profile
|
||||||
|
try {
|
||||||
|
from(this.oauthService.loadUserProfile()).pipe(
|
||||||
|
tap(profile => console.log('User profile loaded:', profile)),
|
||||||
|
catchError(error => {
|
||||||
|
console.error('Error loading user profile:', error);
|
||||||
|
// If we can't load the profile but have a token, create a minimal profile
|
||||||
|
if (this.oauthService.hasValidAccessToken()) {
|
||||||
|
const token = this.oauthService.getAccessToken();
|
||||||
|
// Create a basic profile from the token
|
||||||
|
const minimalProfile = {
|
||||||
|
sub: 'user-' + Math.random().toString(36).substring(2, 10),
|
||||||
|
preferred_username: 'user' + Date.now()
|
||||||
|
};
|
||||||
|
return of({ info: minimalProfile });
|
||||||
|
}
|
||||||
|
return of(null);
|
||||||
|
})
|
||||||
|
).subscribe(profile => {
|
||||||
|
if (profile) {
|
||||||
|
this.processUserProfile(profile);
|
||||||
|
} else {
|
||||||
|
console.error('Could not load or create user profile');
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Exception in handleSuccessfulLogin:', err);
|
||||||
|
// Try to navigate to home if we have a token anyway
|
||||||
|
if (this.oauthService.hasValidAccessToken()) {
|
||||||
|
this.router.navigate(['/home']);
|
||||||
|
} else {
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private processUserProfile(profile: any) {
|
||||||
|
this.fromUserProfile(profile).subscribe({
|
||||||
|
next: (user) => {
|
||||||
|
console.log('User created/retrieved from backend:', user);
|
||||||
|
this.user = user;
|
||||||
|
this.router.navigate(['home']);
|
||||||
|
},
|
||||||
|
error: (err) => {
|
||||||
|
console.error('Error creating/retrieving user:', err);
|
||||||
|
// Navigate to home if we have a token anyway - the backend will need to handle auth
|
||||||
|
if (this.oauthService.hasValidAccessToken()) {
|
||||||
|
this.router.navigate(['/home']);
|
||||||
|
} else {
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
login() {
|
login() {
|
||||||
|
console.log('Initiating login flow');
|
||||||
|
try {
|
||||||
|
// First ensure discovery document is loaded
|
||||||
|
this.oauthService.loadDiscoveryDocument().then(() => {
|
||||||
|
console.log('Discovery document loaded, starting login flow');
|
||||||
this.oauthService.initLoginFlow();
|
this.oauthService.initLoginFlow();
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('Error loading discovery document:', err);
|
||||||
|
// Try login anyway with configured endpoints
|
||||||
|
this.oauthService.initLoginFlow();
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Exception in login:', err);
|
||||||
|
// Try direct login as a fallback
|
||||||
|
const redirectUri = this.authConfig.redirectUri || window.location.origin + '/auth/callback';
|
||||||
|
const scope = this.authConfig.scope || 'openid email profile';
|
||||||
|
const authUrl = `${this.authConfig.issuer}authorize?client_id=${this.authConfig.clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=${encodeURIComponent(scope)}`;
|
||||||
|
window.location.href = authUrl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logout() {
|
logout() {
|
||||||
|
try {
|
||||||
|
console.log('Logging out');
|
||||||
|
this.user = null;
|
||||||
this.oauthService.logOut();
|
this.oauthService.logOut();
|
||||||
|
// Clear any lingering token in storage
|
||||||
|
localStorage.removeItem('access_token');
|
||||||
|
sessionStorage.removeItem('access_token');
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Exception in logout:', err);
|
||||||
|
// Force clear tokens
|
||||||
|
this.oauthService.revokeTokenAndLogout().catch(() => {
|
||||||
|
// Just navigate to home page as fallback
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isLoggedIn() {
|
isLoggedIn() {
|
||||||
|
@ -66,6 +222,7 @@ export class AuthService {
|
||||||
getAccessToken() {
|
getAccessToken() {
|
||||||
return this.oauthService.getAccessToken();
|
return this.oauthService.getAccessToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
getUser() {
|
getUser() {
|
||||||
return this.user;
|
return this.user;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,18 @@ export class UserService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public getOrCreateUser(profile: any): Observable<User> {
|
public getOrCreateUser(profile: any): Observable<User> {
|
||||||
const id = profile.info.sub;
|
console.log('Full authentik profile:', profile);
|
||||||
const username = profile.info.preferred_username;
|
// Authentik format might differ from Keycloak
|
||||||
|
// Check different possible locations for the ID and username
|
||||||
|
const id = profile.info?.sub || profile['sub'];
|
||||||
|
const username = profile.info?.preferred_username || profile['preferred_username'] || profile['email'] || profile['name'];
|
||||||
|
|
||||||
|
if (!id || !username) {
|
||||||
|
console.error('Could not extract user ID or username from profile', profile);
|
||||||
|
throw new Error('Invalid user profile data');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Creating user with id: ${id}, username: ${username}`);
|
||||||
return this.createUser(id, username);
|
return this.createUser(id, username);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,10 @@ import { OAuthStorage } from 'angular-oauth2-oidc';
|
||||||
export const httpInterceptor: HttpInterceptorFn = (req, next) => {
|
export const httpInterceptor: HttpInterceptorFn = (req, next) => {
|
||||||
const oauthStorage = inject(OAuthStorage);
|
const oauthStorage = inject(OAuthStorage);
|
||||||
|
|
||||||
if (oauthStorage.getItem('jwt')) {
|
if (oauthStorage.getItem('access_token')) {
|
||||||
return next(req.clone({
|
return next(req.clone({
|
||||||
setHeaders: {
|
setHeaders: {
|
||||||
'Authorization': 'Bearer ' + oauthStorage.getItem('jwt'),
|
'Authorization': 'Bearer ' + oauthStorage.getItem('access_token'),
|
||||||
'Access-Control-Allow-Origin': '*',
|
'Access-Control-Allow-Origin': '*',
|
||||||
'Referrer-Policy': 'no-referrer',
|
'Referrer-Policy': 'no-referrer',
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue