Skip to content

Commit

Permalink
Fixed broken identity federation
Browse files Browse the repository at this point in the history
  • Loading branch information
dapolach committed Sep 10, 2024
1 parent 2ee43f2 commit 4e4936e
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public Optional<ApplicationUser> findByUserName(String username) {

@Override
public Optional<ApplicationUser> findByGoogleSubject(String googleSub) {
return this.findAll().stream().filter(it -> googleSub.equals(it.getGoogleSubject())).findAny();
return this.findAll().stream().filter(it -> it.getGoogleSubject().filter(googleSub::equals).isPresent()).findAny();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@
@Configuration(proxyBeanMethods = false)
public class LoginPageSecurityConfiguration {

public static RequestMatcher UI_REQUESTS_MATCHER = new OrRequestMatcher(AntPathRequestMatcher.antMatcher("/login"), AntPathRequestMatcher.antMatcher("/oauth2/**"), AntPathRequestMatcher.antMatcher("/logout"), AntPathRequestMatcher.antMatcher("/login/**"));
public static RequestMatcher LOGIN_REQUESTS_MATCHER = new OrRequestMatcher(AntPathRequestMatcher.antMatcher("/login"), AntPathRequestMatcher.antMatcher("/oauth2/**"), AntPathRequestMatcher.antMatcher("/logout"), AntPathRequestMatcher.antMatcher("/login/**"));

@Bean
@Order(AuthorizationServerConfiguration.AUTH_SERVER_LOGIN_PAGE)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http,
AuthenticationSuccessHandler authenticationSuccessHandler
) throws Exception {
return http
.securityMatcher(UI_REQUESTS_MATCHER)
.formLogin(withDefaults())
.securityMatcher(LOGIN_REQUESTS_MATCHER)
.formLogin(form -> form.successHandler(authenticationSuccessHandler))
.oauth2Login(oauth -> oauth.successHandler(authenticationSuccessHandler))
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.function.Consumer;

@Component
// TODO: complete link of social login to Klabis App user (currently it's not used)
public class RegisterMemberFromSocialLoginHandler implements Consumer<OidcUser> {

private static final Logger LOG = LoggerFactory.getLogger(RegisterMemberFromSocialLoginHandler.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
/**
* <a href="https://docs.spring.io/spring-authorization-server/reference/guides/how-to-social-login.html#advanced-use-cases-capture-users">Social login in authorization server</a>
*/
// TODO: complete link of social login to Klabis App user (currently it's not used)
public class SocialLoginAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
private final AuthenticationSuccessHandler delegate = new SavedRequestAwareAuthenticationSuccessHandler();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package club.klabis.config.authserver.socialloginsupport;

import club.klabis.config.authserver.KlabisOidcUser;
import club.klabis.domain.appusers.ApplicationUser;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
import org.springframework.security.oauth2.core.oidc.StandardClaimNames;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
class CustomOidcUserService extends OidcUserService {
private final Map<String, SocialLoginOidcUserToKlabisOidcUserMapper> mappers;

public CustomOidcUserService(List<SocialLoginOidcUserToKlabisOidcUserMapper> mappers) {
this.mappers = mappers.stream().collect(Collectors.toMap(SocialLoginOidcUserToKlabisOidcUserMapper::getOAuthClientId, Function.identity()));
}

private Optional<SocialLoginOidcUserToKlabisOidcUserMapper> getMapperForRegistrationId(ClientRegistration registration) {
return mappers.values().stream().filter(it -> registration.getRegistrationId().equals(it.getOAuthClientId())).findAny();
}

@Override
public KlabisOidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
OidcUser oidcUser = super.loadUser(userRequest);

SocialLoginOidcUserToKlabisOidcUserMapper mapper = getMapperForRegistrationId(userRequest.getClientRegistration())
.orElseThrow(() -> new RuntimeException("No OIDC mapper defined for registrationId %s".formatted(userRequest.getClientRegistration().getRegistrationId())));

return mapper.findApplicationUserForToken(userRequest.getIdToken())
.map(applicationUser -> createAuthentication(oidcUser.getIdToken(), oidcUser.getUserInfo(), applicationUser, List.of()))
.orElseThrow(() -> new OAuth2AuthenticationException("User with subject %s (%s) not found!".formatted(oidcUser.getSubject(), mapper.getOAuthClientId())));
}

KlabisOidcUser createAuthentication(OidcIdToken idToken, OidcUserInfo userInfo, ApplicationUser user, List<String> roles) {
Set<GrantedAuthority> authorities = roles.stream()
.map(roleName -> new SimpleGrantedAuthority(roleName))
.collect(Collectors.toSet());

Map<String, Object> klabisClaims = new HashMap<>();
//claims.putAll(idToken.getClaims());
klabisClaims.put(StandardClaimNames.SUB, user.getUsername());
klabisClaims.put("memberId", user.getMemberId().orElse(null));
// claims.put(StandardClaimNames.GIVEN_NAME, user.getFirstName());
// claims.put(StandardClaimNames.MIDDLE_NAME, user.getMiddleName());
// claims.put(StandardClaimNames.FAMILY_NAME, user.getLastName());
// claims.put(StandardClaimNames.LOCALE, user.getLocale());
// claims.put(StandardClaimNames.PICTURE, user.getAvatarUrl());

OidcIdToken customIdToken = new OidcIdToken(
idToken.getTokenValue(), idToken.getIssuedAt(), idToken.getExpiresAt(), klabisClaims
);

KlabisOidcUser oidcUser = new KlabisOidcUser(authorities, customIdToken, userInfo);
// oidcUser.setId(user.getId());
// oidcUser.setUsername(user.getUsername());
// oidcUser.setCreatedAt(user.getCreatedAt());
// oidcUser.setActive(user.isActive());
return oidcUser;
};

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,28 @@

import club.klabis.domain.appusers.ApplicationUser;
import club.klabis.domain.appusers.ApplicationUsersRepository;
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
import org.springframework.stereotype.Component;

import java.util.Optional;
import java.util.function.Function;

@Component
class GoogleOidcUserToKlabisSocialLoginOidcUserMapper implements SocialLoginOidcUserToKlabisOidcUserMapper {

private final ApplicationUsersRepository userService;

GoogleOidcUserToKlabisSocialLoginOidcUserMapper(ApplicationUsersRepository userService) {
this.userService = userService;
}

@Override
public String getRegistration() {
public String getOAuthClientId() {
return "google";
}

@Override
public Function<String, Optional<ApplicationUser>> findMemberFunction(ApplicationUsersRepository memberService) {
return memberService::findByGoogleSubject;
public Optional<ApplicationUser> findApplicationUserForToken(OidcIdToken token) {
return userService.findByGoogleSubject(token.getSubject());
}

}
Original file line number Diff line number Diff line change
@@ -1,52 +1,16 @@
package club.klabis.config.authserver.socialloginsupport;

import club.klabis.config.authserver.KlabisOidcUser;
import club.klabis.domain.appusers.ApplicationUser;
import club.klabis.domain.appusers.ApplicationUsersRepository;
import club.klabis.domain.members.Member;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
import org.springframework.security.oauth2.core.oidc.StandardClaimNames;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.Optional;

public interface SocialLoginOidcUserToKlabisOidcUserMapper {
/**
* Returns registration ID for which is mapper supposed to be used
*/
String getRegistration();
String getOAuthClientId();

Function<String, Optional<ApplicationUser>> findMemberFunction(ApplicationUsersRepository memberService);

default KlabisOidcUser map(OidcIdToken idToken, OidcUserInfo userInfo, ApplicationUser user, List<String> roles) {
Set<GrantedAuthority> authorities = roles.stream()
.map(roleName -> new SimpleGrantedAuthority(roleName))
.collect(Collectors.toSet());

Map<String, Object> klabisClaims = new HashMap<>();
//claims.putAll(idToken.getClaims());
klabisClaims.put(StandardClaimNames.SUB, user.getUsername());
klabisClaims.put("memberId", user.getMemberId().orElse(null));
// claims.put(StandardClaimNames.GIVEN_NAME, user.getFirstName());
// claims.put(StandardClaimNames.MIDDLE_NAME, user.getMiddleName());
// claims.put(StandardClaimNames.FAMILY_NAME, user.getLastName());
// claims.put(StandardClaimNames.LOCALE, user.getLocale());
// claims.put(StandardClaimNames.PICTURE, user.getAvatarUrl());

OidcIdToken customIdToken = new OidcIdToken(
idToken.getTokenValue(), idToken.getIssuedAt(), idToken.getExpiresAt(), klabisClaims
);

KlabisOidcUser oidcUser = new KlabisOidcUser(authorities, customIdToken, userInfo);
// oidcUser.setId(user.getId());
// oidcUser.setUsername(user.getUsername());
// oidcUser.setCreatedAt(user.getCreatedAt());
// oidcUser.setActive(user.isActive());
return oidcUser;
};
Optional<ApplicationUser> findApplicationUserForToken(OidcIdToken token);

}

0 comments on commit 4e4936e

Please sign in to comment.