🌱 Infra/KeyCloak

[keycloak 맛보기 #2] OpenID Connection 사용자 인증 이해하기

mini_world 2024. 11. 25. 15:16
목차 접기

참고자료

* 책: https://www.yes24.com/Product/Goods/122459785

* 테스트 소스코드: https://github.com/PacktPublishing/Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition


실습 준비) Keycloak & 테스트 애플리케이션 세팅

Keycloak을 로컬에서 먼저 실행한다.

docker run -p 8080:8080 \
          -e KEYCLOAK_ADMIN=admin \
          -e KEYCLOAK_ADMIN_PASSWORD=admin \
          quay.io/keycloak/keycloak \
          start-dev

이후 테스트 코드 어플리케이션을 실행한다.

git clone https://github.com/PacktPublishing/Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition.git
cd Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition/ch4
npm install
npm start

 


실습 #1) OIDC Discovery Endpoint 확인

*[참고]OpenID Connect로 보안 애플리케이션 및 서비스

 

실습해보기

테스트 페이지 (http://localhost:8000)에 접속한다.

'1 - Discovery' 클릭 및 올바른 Issuer를 입력 후 Load OpenID Provider Configuration을 클릭한다.
그럼 아래 캡쳐사진처럼 메타데이터가 출력된다.

이 메타데이터들은 Well-Known 설정 엔드포인트 (검색(discovery)엔드포인트)이며, 
OpenID Provider(OP)의 설정 정보를 자동으로 찾을 수 있게 해주는 표준화된 엔드포인트이다.

 

주요 메타데이터 의미

아래 표는 주요 메타데이터에 대한 의미를 설명하고있다.

구분 설명
issuer OpenID Provider의 식별자 URL
authorization_endpoint OpenID Provider의 식별자 URL (인증요청)
token_endpoint 토큰을 발급받는 URL (토큰요청)
introspection_endpoint 액세스 토큰이나 리프레시 토큰의 유효성을 확인하는 URL (점검요청)
userinfo_endpoint 사용자 정보를 가져오는 URL (UserInfo요청)
end_session_endpoint 로그아웃을 처리하는 URL
jwks_uri 공개 키를 제공하는 URL
grant_types_supported 지원하는 인증 방식(예: authorization_code, client_credentials 등)
response_types_supported 클라이언트가 요청 시 사용할 수 있는 응답 형식(예: code, token, id_token 등)

 

OIDC discovery endpoint 표준 spec

OpenID Connect Discovery 1.0 스펙은 OpenID Foundation에서 관리하고 있다.

Discovery endpoint의 표준 경로는 아래와 같다.

http://{keycloak domain}/realms/{realm-name}/.well-known/openid-configuration
#http://localhost:8080/realms/myrealm/.well-known/openid-configuration

 


실습 #2) Authorization Code(인가코드) 발급과정 

 

Authentication 필드 확인

먼저 keycloak에서는 oidc-playground 라는 클라이언트를 생성한다. (realm -> clients -> client생성)
또한 사용자도 생성해야한다.
테스트 유저는 임의 ID로 생성한 후 (여기에서는 user01로 생성했다), Credentials탭에서 비밀번호를 설정한다.



이후 테스트 페이지 (http://localhost:8000)에서 2 - Authentication를 클릭한다.

올바른 값을 입력(아래 참고)하고 Generate Authentication Request를 클릭하여 인증요청이 어떻게 생성되는지 확인한다.

첫번째 회색박스 Authentication에서의 각 필드는 스크린샷 내용 혹은 아래 내용을 참고한다.

  • client_id: keycloak에서 생성한 client id
  • scope:
    • 클라이언트가 사용자의 어떤 정보나 리소스에 접근하고 싶은지 나타내는 권한 범위
    • openid는 기본값이며, 그 외에도 profile, email, phone, address 등 사용 가능
  • prompt: 사용자 인증 시 특정 동작을 강제하는 파라미터
    • none: 이미 로그인 된 경우 사용자 상호작용 없이 진행
    • login: 강제로 다시 로그인하도록 함
    • consent: 권한 등의 화면을 강제로 보여줌
    • select_account: 계정 선택 화면을 보여줌
  • max_age: 로그인 세션의 최대 허용 시간(초)
  • login_hint: 로그인 페이지에서 사용자 ID를 미리 채워넣을 때 사용
    • 예를들어 user01을 입력해두면, 사용자 로그인시 미리 user01이 채워져있다.
    • 사용자 편의성을 위해 사용한다.

 

Authentication Request 확인

이번 단계에서는 실제 Authentication Request가 어떻게 생겼는지(?) 확인할 수 있다.

 

Authentication Response 확인

위 요청이 처리되면 Authentication Response를 받게 된다.

응답으로 받은 코드는 ID토큰과 리프레시 토큰을 얻기위해 사용하는 인가코드(Authorization Code)이다.

💡 여기서 궁금한부분 -인증코드라고 하지 않고 인가코드라고 썼을까?
 
인증과 인가는 사전적 정의로 보자면 아래와 같다.
      - 인증(Authentication): "이 사람이 user01이 맞다"
      - 인가(Authorization): "user01은 이 파일을 읽을 수 있다"

OIDC는 OAuth 2.0위에 구축되어있어 사용자를 인증하면서 어플리케이션에 대한 권한도 발급받게 된다.
사용자인증->권한부여 과정이 한번에 연속적으로 일어나며, 이번 단계에서는 인증 흐름 중 인가코드를 발급받는 부분이다.

┌───┐   ┌───┐   ┌───┐   ┌────┐   ┌────┐
  │  사용자  │ -> │    인증   │ -> │   인가     │ -> │   Token    │ -> │   Token    │
  │  로그인  │      │    성공   │      │   코드     │      │   요청        │      │    발급      │  
└───┘   └───┘   └───┘   └────┘   └──┬─┘
                                             (지금여기)                                          ↓
                                                                                        ┌───────┐
                                                                                          │      ID Token        │
                                                                                          │   Access Token  │
                                                                                          │   Refresh Token │
                                                                                        └───────┘

인가코드(Authorization code)는 중간교환수단으로, 실제 리소스 접근권한은 ID Token이 가지고 있고,
OIDC 사용자 인증(Authentication) 과정은 Authorization Code를 통해 ID Token을 발급받음으로써 완성된다.

 

 


실습 #3) ID Token 발급과정

이제 실습#2에서 발급받은 Authorization Code를 가지고 ID Token을 발급받아보자.

실습해보기

테스트 페이지 (http://localhost:8000)에서 3 - Token를 클릭한다.

실습 #2에서 발급받은 인가(Authorization)코드를 가지고 ID Token & Refresh Token, Access Token 등을 발급받았다.

 

Token Response 필드 알아가기 

구분 설명
access_token - 보호된 리소스에 접근하기 위한 토큰
- JWT 형식으로 인코딩된 문자열
- 리소스 서버에 접근할 때 사용
expires_in - 액세스 토큰의 유효 시간 (초)
refresh_expires_in - 리프레시 토큰의 유효 시간 (초)
refresh_token - 액세스 토큰이 만료되었을 때 새로운 토큰을 발급받기 위한 토큰 
- 사용자가 다시 로그인하지 않아도 됨
token_type - 토큰의 타입을 나타냄
- Bearer는 토큰 소지자에게 권한을 부여
id_token - 사용자의 신원 정보를 포함한 JWT
- OIDC에서 추가된 토큰
- 사용자 인증 정보 포함
not-before-policy - 토큰이 유효해지는 시작 시간
- 0은 즉시 유효함을 의미
session_state - 현재 세션의 고유 식별자
scope - 토큰이 가진 권한 범위
- "openid profile email": 기본 인증, 프로필 정보, 이메일 정보 접근 가능

 

id_token, access_token, refresh_token 이해하기

제일 중요한건 아래 세 가지 토큰이다. 
이 세 토큰 모두 JWT형식이다.

[클라이언트]          [인증 서버]           [리소스 서버]
    │                  │                    │
    │  ┌──────────┐    │                    │
    ├─>│Access    │────────────────────────>│ API 접근
    │  │Token     │    │                    │
    │  └──────────┘    │                    │
    │                  │                    │
    │  ┌──────────┐    │                    │
    ├─>│Refresh   │───>│ 새 토큰 발급          │
    │  │Token     │    │                    │
    │  └──────────┘    │                    │
    │                  │                    │
    │  ┌──────────┐    │                    │
    └─>│ID Token  │───>│ 사용자 인증           │
       └──────────┘    │                    │
  • access_token: API 접근용 토큰 (API 호출 시 권한 증명)
    - 권한범위(Scope) / 리소스 접근 권한 / 만료시간 / Client ID
  • refresh_token: 만료된 Access Token 갱신용 토큰
    - 토큰 갱신용으로 최소한의 정보만 담음(보안정보)
  • id_token: 사용자 인증 정보용 토큰 (사용자 신원 정보 확인)
    - 사용자정보 / 인증시점 / 인증방법 / 토큰발급자

Access Token이 만료되면 가지고있던 Refresh Token으로 인증서버에 요청하게 되며,
이때 Access Token 뿐만아니라 모든 Token이 갱신된다. 

[클라이언트]           [인증 서버]          
    │                   │                 
    │   Refresh Token   │                 
    │─────────────────> │                 
    │                   │    
    │                   │  
    │   새로운 토큰 세트    │                 
    │ <─────────────────│                 
    │   - Access Token  │                 
    │   - ID Token      │                 
    │   - Refresh Token │

 

 


실습 #4) Token Refresh

이제 실습#3에서 발급받은 refresh token 를 가지고 토큰을 모두 재발급 해보자!

실습해보기

테스트 페이지 (http://localhost:8000)에서 4 - Refresh를 클릭한다.

Refresh Request 필드에는 RefreshToken이 포함되며, 
모든 Token은 재사용되지 않고, Refresh요청이 오면 모두 재발급 된다.

 

 


실습 #5) UserInfo 요청하기

Access Token으로 UserInfo를 요청해보자.

실습해보기

테스트 페이지 (http://localhost:8000)에서 5 - UserInfo를 클릭한다.

 

UserInfo Endpoint 설명

UserInfo Endpoint는 OIDC의 중요한 기능 중 하나이며 주요 특징은 아래와 같다.

  • ID Token에 있는 정보를 보완
  • 항상 최신 사용자 정보 제공
  • scope 설정에 따라 반환되는 정보가 달라짐
  • Access Token의 권한 범위 확인

UserInfo는 AccessToken을 사용해서 요청하며 아래와 같은 방식으로 동작한다.

#1. UserInfo Request
┌──────────────┐                ┌──────────────┐
│  클라이언트     │                │ UserInfo     │
│              │─── GET ───────>│ Endpoint     │
│              │                │              │
│              │ Authorization: │              │
│              │ Bearer [Access │              │
│              │   Token]       │              │
└──────────────┘                └──────────────┘


#2. UserInfo Response
┌──────────────┐                ┌──────────────┐
│  클라이언트     │                │ UserInfo     │
│              │<── JSON ───────│ Endpoint     │
│              │                │              │
│ {                             │              │
│   "sub": "...",               │              │
│   "name": "user234 test",     │              │
│   "email": "user01@..."       │              │
│ }                             │              │
└──────────────┘                └──────────────┘

 

 

 

728x90