본문 바로가기
Programming/SPRING

OAuth를 설정하다가 에러가 발생했다.

by 한 땀; 한 땀; 2023. 8. 24.

redirectUriTemplate cannot be empty 라는 에러가 발생해 해당 구간을 확인해봤다.

 

redirectUriTemplate 를 비울 수 없다고 한다. 계속 따라가 본다.

 

ClientRegistration 클래스 객체 생성 중 AuthorizationGrantType이 AUTHORIZATION_CODE 일때 redirectUriTemplte 속성값이 정의가 안되면 발생하는 exception 임을 알 수 있다.

 

그럼 ClientRegistration  클래스 객체는 왜 생성할까? 

 

로그를 기반으로 조금 더 따라가보기로 한다.

 

getClientRegistration 메소드가 호출 되고 있다. 더 따라가본다.

getClientRegistrations 메소드가 호출 되고 있다. 계속 따라가본다.

 

끝까지 따라가 보니 OAuth2ClientRegistrationRepositoryConfiguration 클래스가 스프링에서 관리되면서 InMemoryClientRegistrationRepository 빈을 생성한다.

InMemoryClientRegistrationRepository 는 스프링 시큐리티 OAuth2 모듈에서 OAuth2 클라이언트 등록 정보를 메모리 내에서 관리하는 레포지토리라고 한다.

ClientRegistration  클래스는 스프링에 의해 관리되면서 클라이언트 등록 정보를 가진 객체임을 알 수 있다.

 

스프링시큐리티 OAuth 초기 세팅 관련 흐름은 다음과 같다.

 

1. OAuth2ClientRegistrationRepositoryConfiguration.clientRegistrationRepository

2. OAuth2ClientPropertiesRegistrationAdapter.getClientRegistrations

3. OAuth2ClientPropertiesRegistrationAdapter.getClientRegistration

4. ClientRegistration.build

6. ClientRegistration.validateAuthorizationCodeGrantType

 

위 호출 순서를 기반으로 내용을 정리해본다.


OAuth2ClientRegistrationRepositoryConfiguration

 

1.스프링 시큐리티 OAuth2 모듈은 OAuth2ClientRegistrationRepositoryConfiguration 클래스를 통해 InMemoryClientRegistrationRepository 빈을 생성한다.

InMemoryClientRegistrationRepository 는 메모리형태(Map)로 클라이언트 정보들을 저장하고 있다. 

InMemoryClientRegistrationRepository 는 클라이언트 정보들을@EnableConfigurationProperties(OAuth2ClientProperties.class) 어노테이션을 통해 클래스에 정의된 프로퍼티 값을 application.properties에서 가져와 생성자로 넘겨주며 인스턴스를 생성한다.


OAuth2ClientPropertiesRegistrationAdapter

 

2. getClientRegistrations 메소드를 호출을 통해 OAuth2ClientProperties 에서 Registration 객체들을 가져와 clientRegistrations에 담아 반환한다.

 

3. 조금더 세부적으로 보면 getClientRegistration 메소드를 호출하면서 provider를 가져와 CommonOAuth2Provider에 디폴트로 정의되있는 providerId 가 있으면 가져오고 없으면 map에서 가져온다. 둘다 없으면 예외를 발생시킨다.

PropertyMapper를 통해 builder에 매핑작업을 하고 build를 통해 객체를 생성한다.


ClientRegistration

 

4. 해당 클래스에서 build 할 때, 예외가 떨어진다. 에러를 다시 확인해보면 

Assert.hasText(this.redirectUriTemplate, "redirectUriTemplate cannot be empty"); 에서 발생한걸 알 수 있는데

해당 매핑처리 부분을 확인하면 map.from(properties::getRedirectUri).to(builder::redirectUriTemplate); 부분임을 알 수 있다. 즉, 프로퍼티에서 getRedirectUri를 못가져온 것이다.

나는 현재 naver OAuth를 연동중이었으므로 다음 스크립트를 추가한다.

spring.security.oauth2.client.registration.naver.redirectUri={값}

그렇다면 여기서 값은 무슨 값을 줘야 할까?

공식문서를 확인해보니 다음과 같이 써있었다.

"The default redirect URI template is {baseUrl}/login/oauth2/code/{registrationId}. The registrationId is a unique identifier for the ClientRegistration."

"기본 리디렉션 URI 템플릿은 {baseUrl}/login/oauth2/code/{registrationId}입니다. registrationId는 클라이언트 Registration의 고유 식별자입니다."

 

또한, CommonOAuth2Provider 클래스를 확인해보니 다음과 같이 선언되있음을 알고 있다.

private static final String DEFAULT_REDIRECT_URL = "{baseUrl}/{action}/oauth2/code/{registrationId}";

둘중 하나를 참고해서 사용하면 될 것 같다. 해당 매핑 부분의 코드를 찾아보면 DefaultOAuth2AuthorizationRequestResolver 클래스에 다음과 같이 작성되있다.

 

private String expandRedirectUri(HttpServletRequest request, ClientRegistration clientRegistration, String action) {
    // Supported URI variables -> baseUrl, action, registrationId
    // Used in -> CommonOAuth2Provider.DEFAULT_REDIRECT_URL = "{baseUrl}/{action}/oauth2/code/{registrationId}"
    Map<String, String> uriVariables = new HashMap<>();
    uriVariables.put("registrationId", clientRegistration.getRegistrationId());
    String baseUrl = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))
          .replaceQuery(null)
          .replacePath(request.getContextPath())
          .build()
          .toUriString();
    uriVariables.put("baseUrl", baseUrl);
    if (action != null) {
       uriVariables.put("action", action);
    }
    return UriComponentsBuilder.fromUriString(clientRegistration.getRedirectUriTemplate())
          .buildAndExpand(uriVariables)
          .toUriString();
}

이와 같이 되있음을 알 수 있는데, {baseUrl} 과 같이 변수 선언 없이 "http://~" 처럼 실제 주소를 넣어줘도 상관없어 보인다.

 

결론

 

applications.properties에 redirectUri 속성 값을 안줘서 발생한 에러였다.

 

※참고 

@Deprecated
public String getRedirectUriTemplate() {
    return getRedirectUri();
}

@Deprecated
public void setRedirectUriTemplate(String redirectUri) {
    setRedirectUri(redirectUri);
}

RedirectUriTemplate는 Deprecated 되어있다.

댓글