[Spring Authorization Server] 1. Core Model / Components - 1

inseo24·2023년 9월 17일
1

auth

목록 보기
4/4

대상

Spring Authorization Server 를 이용해 인가 서버를 구축하려는 개발자

목적

공식 문서를 읽고 핵심부터 이해하자.

순서

  1. RegisteredClient, OAuth2Authorization, OAuth2AuthorizationConsent <- 여기까지
  2. OAuth2Token
    • Context
    • Generator
    • Customizer
  3. SessionRegistry

RegisteredClient

앞서 OAuth 2.0 스펙 글에서 Client에 대해 말한 적이 있다.

Spring Authorization Server에선 Client를 RegisteredClient 라고 표현한다. Client가 인가 서버에 등록되야 한다는 점에서 RegisteredClient 라는 이름을 사용한다.

Client를 등록에 필요한 메타 데이터는 어떤 인가 플로우를 사용하느냐에 따라 달라진다.
RegisteredClient 객체를 설정함으로써 클라이언트의 특성과 동작을 정의한다.

RegisteredClient registeredClient = RegisteredClient
    .withId(UUID.randomUUID().toString())
	.clientId("client-a")
	.clientSecret("{noop}secret")   (1)
	.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
	.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
    .postLogoutRedirectUri("http://127.0.0.1:8080/logout")
	.redirectUri("http://127.0.0.1:8080/authorized")
	.scope("scope-a")
	.tokenSettings(TokenSettings
         .builder()
         .accessTokenFormat()
         .accessTokenTimeToLive()
         .authorizationCodeTimeToLive()
         .idTokenSignatureAlgorithm()
         .reuseRefreshTokens()
         .build()
    ).clientSettings(ClientSettings
         .builder()
         .requireProofKey()
         .tokenEndpointAuthenticationSigningAlgorithm()
         .jwkSetUrl()
         .requireAuthorizationConsent()
         .build()
    ).build();
  • id: 고유 식별자

  • clientId: 클라이언트 식별자

  • client-secret : 선택 사항, public 타입이라면 없음

    • {noop} 은 스프링 시큐리티의 NoOpPasswordEncoder 를 의미
    • 해당 클라이언트로 토큰 발급 할 때 해당 시크릿 값을 인코딩하지 않음을 의미
  • client-authentication-method : BASIC, POST, JWT, PRIVATE_KEY_JWT, NONE이 있다.

  • authorizationGrantType: 클라이언트가 사용할 수 있는 인가 Grant 유형

  • redirectUris: 클라이언트가 리다이렉트 기반 플로우에서 사용할 수 있는 리디렉트 URI

  • postLogoutRedirectUris: 클라이언트가 로그아웃 시 사용할 수 있는 로그아웃 리디렉트 URI

  • scopes: 클라이언트가 요청할 수 있는 스코프

  • clientSettings: 클라이언트에 대한 사용자 정의 설정

    • requireProofKey : PKCE 방식으로 인가를 받으려면 이 옵션에 true 설정이 필요
    • requireAuthorizationConsent : Consent 옵션이 true가 되야 동의를 받는 과정이 추가된다.
  • tokenSettings: OAuth2 토큰에 대한 사용자 정의 설정으로, 액세스 토큰 및 리프레시 토큰의 수명 및 리프레시 토큰 재사용 여부 등을 설정할 수 있다.

spring:
  security:
    oauth2:
      client:
        registration:
          client-a:
            provider: spring
            client-id: client-a
            client-secret: secret
            authorization-grant-type: authorization_code
            redirect-uri: "http://127.0.0.1:8080/authorized"
            scope: scope-a
        provider:
          spring:
            issuer-uri: http://localhost:9000

위와 같이 yaml로 세팅할 수도 있다.

RegisteredClientRepository

Inmemory를 사용할 수도 있지만 DB에 저장하기 위해선 JdbcRegisteredClientRepository를 구현해야 한다. 다음 글에서 Spring Data JPA를 통해 이걸 구현하는 쪽을 다룰 예정이다.

@Bean으로 등록하거나 OAuth2AuthorizationServerConfigurer를 통해 설정할 수도 있다.

@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
	OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
		new OAuth2AuthorizationServerConfigurer();
	http.apply(authorizationServerConfigurer);

	authorizationServerConfigurer
		.registeredClientRepository(registeredClientRepository);

	...

	return http.build();
}

OAuth2Authorization

OAuth2Authorization은 클라이언트에게 부여된 인가와 관련된 상태를 갖는다. 리소스 소유자 또는 client_credentials 인가 그랜트 유형의 경우 클라이언트 자체에 의해 부여된다.

인가에 성공하면 OAuth2Authorization가 생성되며 OAuth2AccessToken, (선택적) OAuth2RefreshToken 및 실행된 인가 플로우에 특정한 추가 정보가 연결되어 저장된다.

OAuth2Authorization과 연결된 OAuth2Token 인스턴스는 유형에 따라 다양하다.

  • OAuth2 authorization_code의 경우 OAuth2AuthorizationCode, OAuth2AccessToken 및 (선택적) OAuth2RefreshToken이 연결된다.
  • OpenID Connect 1.0 authorization_code 그랜트의 경우 OAuth2AuthorizationCode, OidcIdToken, OAuth2AccessToken 및 (선택적) OAuth2RefreshToken이 연결된다.
  • OAuth2 client_credentials 그랜트의 경우 OAuth2AccessToken만 연결된다.

OAuth2Authorization 및 그 속성은 다음과 같이 정의된다.

public class OAuth2Authorization implements Serializable {
    private String id;
    private String registeredClientId;
    private String principalName;
    private AuthorizationGrantType authorizationGrantType;
    private Set<String> authorizedScopes;
    private Map<Class<? extends OAuth2Token>, Token<?>> tokens;
    private Map<String, Object> attributes;
    // ...
  • id: 고유 식별자
  • registeredClientId: 해당 인가와 연결된 클라이언트의 ID
  • principalName: 리소스 소유자(또는 클라이언트)의 주요 이름
  • authorizationGrantType: 사용된 AuthorizationGrantType
  • authorizedScopes: 클라이언트에게 부여된 스코프 집합
  • tokens: 실행된 인가 그랜트 유형에 특정한 OAuth2Token 인스턴스(및 관련된 메타데이터)
  • attributes: 실행된 인가 그랜트 유형에 특정한 추가 속성 (예: 인증된 Principal, OAuth2AuthorizationRequest 등)

OAuth2Authorization 및 관련 OAuth2Token 인스턴스는 수명이 있다. 새로 발급된 OAuth2Token은 활성 상태이며 만료되거나 무효화(박탈)될 때 비활성 상태가 된다. OAuth2Authorization는 모든 연결된 OAuth2Token 인스턴스가 비활성화될 때 (암시적으로) 비활성화된다. 각 OAuth2Token은 OAuth2Authorization.Token 내에 보관되며, isExpired(), isInvalidated(), isActive()에 대한 접근자를 제공한다.

또한 OAuth2Authorization.Token은 OAuth2Token과 관련된 클레임(있는 경우)을 반환하는 getClaims()를 제공한다.

OAuth2AuthorizationService

OAuth2AuthorizationService는 새로운 인가를 저장하고 기존 인가를 조회하는 용으로 사용된다. client 인증, 인가 처리, 토큰 introspection/revokation 등에 사용된다.

마찬가지로 Inmemory와 Jdbc 방식이 있으며, DB에 저장하려면 JdbcOAuth2AuthorizationService를 구현해야 한다.

@Bean으로 등록하거나 OAuth2AuthorizationServerConfigurer를 통해 설정할 수도 있다.

@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
	OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
		new OAuth2AuthorizationServerConfigurer();
	http.apply(authorizationServerConfigurer);

	authorizationServerConfigurer
		.authorizationService(authorizationService);

	...

	return http.build();
}

OAuth2AuthorizationConsent

인가 플로우에서 동의 페이지가 출력 되려면, client settings에서 requireAuthorizationConsent 가 true로 설정되야 한다. 이후 인가 코드 요청 시점에 openid가 아닌 다른 scope를 요청해야 한다.

이 때, 사용자가 해당 scope에 동의 했는지 확인하기 위해 OAuth2AuthorizationConsent를 조회한다. client가 요청한 scope에 대해 사용자가 동의하지 않았을 경우, 동의 페이지로 리다이렉트한다.

주로 authorization code flow에서 사용되는데, 사용자가 scope에 대해 권한을 부여하지 않으면 code가 발급되지 않는다.

OAuth2AuthorizationConsent는 아래와 같이 구성되어 있다.

public final class OAuth2AuthorizationConsent implements Serializable {
	private final String registeredClientId;    
	private final String principalName; 
	private final Set<GrantedAuthority> authorities;    
	// ...
}
  • registeredClientId: RegisteredClient의 고유 식별자
  • principalName: 리소스 소유자의 이름(보통 유저)
  • authorities: 리소스 소유자에 의해 클라이언트에게 부여된 권한

OAuth2AuthorizationConsentService

OAuth2AuthorizationConsentService는 OAuth2 인가 동의를 저장하고 조회하는 중심적인 컴포넌트로, 주로 authorization_code 그랜트와 같은 OAuth2 인가 요청 플로우를 구현하는 구성 요소에서 사용한다.

마찬가지로 Inmemory와 Jdbc 방식이 있으며, DB에 저장하려면 JdbcOAuth2AuthorizationService를 구현해야 한다.

@Bean으로 등록하거나 OAuth2AuthorizationServerConfigurer를 통해 설정할 수도 있다.

@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
	OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
		new OAuth2AuthorizationServerConfigurer();
	http.apply(authorizationServerConfigurer);

	authorizationServerConfigurer
		.authorizationConsentService(authorizationConsentService);

	...

	return http.build();
}

정리

1, RegisteredClient 는 반드시 구현해야 하는 컴포넌트로 인가 서버에 등록되어야 인가 플로우를 진행할 수 있다.
2. Client로 인가 플로우를 진행할 때, 요청하는 scope에 대해 consent를 체크할 때, OAuth2AuthorizationConsent를 이용한다.
3. 인가가 완료되면 관련 데이터가 OAuth2Authorization 에 저장된다.

다음에는

내용이 길어져서 Token과 Session 관련은 다음에 이어 작성하겠다. ~.~

profile
나 개발자

0개의 댓글

관련 채용 정보