티스토리 뷰

SNS 로그인을 백엔드에서 검증하는 방법은 2가지가 존재합니다. 오늘은 그 중 하나인 OpenID Connect 규격의 Id Token을 활용하는 방법을 간단하게 정리해보려고 합니다.

먼저 OpenId에 대해 이해하고 넘어갈 필요가 있습니다. 외부 서비스의 계정을 이용한 통합 인증 시스템인 OAuth 제정되고 현재까지 다양한 표준들이 제정되었습니다. 

그중 OAuth2.0의 확장 표준?에는 OpenID Connect라는 표준이 존재합니다. 간략하게 소개하면 JWT를 기반으로 하며 JWT내에는 사용자의 여러 정보를 담아서 공개하게 됩니다. 이런 Token은 우리에게 IdToken이라는 이름으로 제공되게 됩니다.

SNS 로그인을 연동하면 제공되는 Jwt 형식의 IdToken이 바로 이 OpenID Connect의 규정입니다. iss와 aud 등등 다양한 필수 요소들이 존재하며 Jwks라는 Public Key 세트를 제공하여 각 사용처에서 직접 Token을 검증하도록 제공하고 있습니다.

각 사용처는 이러한 규격 덕분에IdToken을 신뢰하고 해당 유저를 간편하게 온보딩 시킬 수 있습니다.


idToken을 검증하는 방법은 어떻게 될까요?

idToken은 기본적으로 RSA또는 EC 키를 이용하여 생성한 Private Key 와 Public Key 를 이용하여 생성하게 됩니다. 키페어 중 Private Key를 이용하여 Jwt의 Header와 Payload를 묶어 서명을 진행합니다. 이렇게 생성된 Signature를 Header와 Payload의 꼬리에 붙여 Jwt가 생성됩니다.

JWT 기본 구조 -> {Header}.{Payload}.{Signature}

 

이렇게 생성된 Jwt를 인증하는 방법은 위에서 나온 키페어의 Public Key를 이용하는 방법입니다. 따라서 idToken (OpenId)를 사용하는 서비스에서는 Public Key만 있다면 구글이나 애플에서 생성해주는 토큰을 믿고 사용할 수 있습니다.


이런 Public Key를 제공하는 방법이 바로 Jwks의 endpoint를 공개하는 것입니다. OpenId를 제공하는 모든 Provider는 기본적으로 Jwks의 endpoint를 제공하며 서비스는 여기서 Public Key를 받아 검증하게 됩니다. Jwks는 2개 이상의 Public Key를 제공하는게 보안적으로 안전한데 이는 우리가 받은 idToken Header에 어떤 키를 사용해야하는지 Kid 값을 통해 제공됩니다.

 

Open ID Connect를 활용하면 기존의 AccessToken을 각 백엔드에 직접 물어보는 방식보다 더 저렴하게 처리할 수 있습니다. 일반적으로 Jwks는 주기적으로 업데이트됩니다. 즉, Jwks를 서비스에서 일정 시간 동안 캐싱하여 검증할 수 있다는 말이 됩니다. 모든 인가 요청에 OAuth Provider로 API를 호출하는 비용을 절약하고 더욱 빠르게 유저를 인가할 수 있게 됩니다.

 

Java에서 Jwks로 OpenId를 검증하는 간단한 코드를 소개하고 마무리하도록 하겠습니다. (0Auth의 java-jwt와 jwks-rsa 라이브러리를 활용합니다.)

// jwksUrl을 통해 Jwk를 불러옵니다.
JwkProvider provider = new JwkProviderBuilder(new URL(jwksUrl)).build();
// JWT.decode를 이용하여 idToken을 파싱합니다. -> 검증 X
DecodedJWT jwt = JWT.decode(idToken);

// Jwk는 Public Key만 존재하므로 Provider를 직접 구현하여 Public Key만 사용할 수 있도록 구현합니다.
RSAKeyProvider keyProvider =
    new RSAKeyProvider() {
      @Override
      public RSAPublicKey getPublicKeyById(String kid) {
        try {
          return (RSAPublicKey) provider.get(kid).getPublicKey();
        } catch (JwkException e) {
          return null;
        }
      }

      @Override
      public RSAPrivateKey getPrivateKey() {
        return null;
      }

      @Override
      public String getPrivateKeyId() {
        return jwt.getKeyId();
      }
    };

// RSA Algorithm 객체를 생성함과 동시에 verifier를 build합니다. 
// iss와 aud는 각 Provider에서 제공하는 값으로 설정합니다.
// Provider에 따라 서비스의 App Id이거나 Privder의 Client Key값으로 처리됩니다.
Algorithm algorithm = Algorithm.RSA256(keyProvider);
JWTVerifier verifier =
  JWT.require(algorithm)
     .withIssuer(this.issuer.iss)
     .withAudience(this.issuer.aud)
     .build();

// verifier를 통해 jwt를 검증합니다.
DecodedJWT verifiedJwt = verifier.verify(jwt);

 

반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함