본문 바로가기

DevOps/Keycloak

[Keycloak]애플리케이션 보안설정: 인증 및 권한 관리

예시 애플리케이션의 기능

Keycloak을 통합한 애플리케이션은 다음과 같은 주요 기능을 제공합니다:

  • Keycloak 로그인: 안전한 인증 메커니즘 제공.
  • 사용자 이름 표시: ID Token의 사용자 정보를 활용하여 사용자 이름 표시.
  • 사용자 프로필 사진 표시: ID Token에서 사용자 사진 정보(picture)를 추출하여 표시.
  • ID 토큰 및 접근 토큰 발급: 인증 후 ID Token과 Access Token 제공.
  • 토큰 초기화: 갱신된 토큰 정보를 즉시 반영.
  • 보안 엔드포인트 호출: 백엔드의 보안 API와 통신.

엔드포인트 설명

  • /public: 인증 없이 접근 가능한 공개 엔드포인트.
  • /secured: 인증된 사용자만 접근 가능한 보호된 엔드포인트.

Keycloak 인증 흐름

애플리케이션 다이어그램

인증 흐름

흐름 설명

  1. 사용자가 로그인 시도 → Keycloak으로 리다이렉트.
  2. Keycloak은 사용자의 인증 정보를 확인한 후 애플리케이션으로 인증 코드 발급.
  3. 애플리케이션은 인증 코드를 ID 토큰과 Access Token으로 교환.
  4. ID 토큰을 사용해 사용자를 식별하고, Access Token을 통해 보호된 API 호출.
  5. 사용자 이름과 패스워드 검증 후 Keycloak 인가 코드를 애플리케이션에 전송
  6. 애플리케이션 인가 코드를 ID 토큰 및 접근 토큰과 교환, 애플리케이션은 ID 토큰을 확인해 사용자의 ID를 검증한다.

Keycloak을 통한 인증 위임의 장점:

  • 애플리케이션은 인증 구현을 직접 처리할 필요가 없습니다.
  • 인증 방식 변경 시 애플리케이션 수정 없이 Keycloak에서만 설정 변경.

프론트엔드와 백엔드 통신

요청 흐름

흐름 설명

  1. 공개 키 캐싱: Keycloak에서 발급한 공개 키를 메모리에 저장해 효율적으로 토큰 검증.
  2. 토큰 검증: 토큰의 유효성과 발급 주체(Keycloak)를 확인.
  3. 역할(role) 확인: 토큰에 포함된 역할 정보를 기반으로 요청 승인 여부 결정.

Login into Application: Failed

http://localhost:8000/

원인 1: Keycloak에 클라이언트 없음

Keycloak에 애플리케이션 클라이언트가 등록되지 않으면 다음과 같은 문제가 발생:

  • 오류: "Client not found"

원인 2: 클라이언트 비활성화

  • 비활성화된 클라이언트는 인증 요청을 처리할 수 없습니다.

해결: 클라이언트 생성 및 설정

  1. Keycloak 관리자 콘솔에서 새로운 클라이언트를 생성.
  2. 클라이언트 설정:
    • Valid Redirect URIs: 로그인 성공 후 리다이렉션될 URI 패턴 지정 (e.g., http://localhost:8000/*).
    • Web Origins: 허용된 CORS 출처 설정. http://localhost:8000사용해 유용한 출처 허용.

[Settings]

Login into Application: Success

[앞에서 생성한 Keycloak user를 사용하여 로그인]

[ID Token 확인]

{
  "exp": 1731859069,
  "iat": 1731858769,
  "auth_time": 1731858769,
  "jti": "94889f68-2192-408e-a517-11e5ae75144c",
  "iss": "<http://10.77.77.41:31180/realms/jeff-realm>",
  "aud": "myclient",
  "sub": "a6c96222-8e0f-4049-8bd7-505ef282eb66",
  "typ": "ID",
  "azp": "myclient",
  "nonce": "361ff040-f849-4c09-84c6-b428325562a4",
  "session_state": "456337a4-a0af-4582-8b11-ba253fc935fb",
  "at_hash": "A7BnyuUX4aZKdZh072z3XQ",
  "acr": "1",
  "sid": "456337a4-a0af-4582-8b11-ba253fc935fb",
  "email_verified": false,
  "name": "Jeff Han",
  "preferred_username": "jeff",
  "given_name": "Jeff",
  "family_name": "Han",
  "email": "jeff@grepp.co"
}
  • iss: 토큰 발급 주체 (Keycloak 서버 URL).
  • sub: 인증된 사용자의 고유 ID 값
  • ID Token: ID 토큰은 인증된 사용자의 ID와 애플리케이션을 연동하기 위해 사용된다.

[Show Access Token]

{
  "exp": 1731859069,
  "iat": 1731858769,
  "auth_time": 1731858769,
  "jti": "febc8170-99fa-446d-bc4a-b3b822ad51f7",
  "iss": "<http://10.77.77.41:31180/realms/jeff-realm>",
  "aud": "account",
  "sub": "a6c96222-8e0f-4049-8bd7-505ef282eb66",
  "typ": "Bearer",
  "azp": "myclient",
  "nonce": "361ff040-f849-4c09-84c6-b428325562a4",
  "session_state": "456337a4-a0af-4582-8b11-ba253fc935fb",
  "acr": "1",
  "allowed-origins": [
    "<http://localhost:8000>"
  ],
  "realm_access": {
    "roles": [
      "jeff-global-role",
      "offline_access",
      "default-roles-jeff-realm",
      "uma_authorization"
    ]
  },
  "resource_access": {
    "account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    }
  },
  "scope": "openid email profile",
  "sid": "456337a4-a0af-4582-8b11-ba253fc935fb",
  "email_verified": false,
  "name": "Jeff Han",
  "preferred_username": "jeff",
  "given_name": "Jeff",
  "family_name": "Han",
  "email": "jeff@grepp.co"
}
  • allowed-origins: 애플리케이션에 허용된 웹 출처 리스트. CORS 요청이 있는 경우 백엔드 서비스에서 CORS 요청 승인 여부를 결정하기 위해 해당 필드를 사용
  • realm_access: 글로벌 역할 리스트. 사용자에게 부여된 역할과 클라이언트가 접근할 수 있는 역할 간의 교차점
  • resource_access: 클라이어트의 역할 리스트
  • scope: 범위를 사용해 토큰에 포함할 필드를 결정하고, 백엔드에서 토큰이 접근할 수 있는 API를 결정할 수 있음.

사용자 정의 콘텐츠

{
  "exp": 1732023472,
  "iat": 1732023172,
  "auth_time": 1732022832,
  "jti": "eadf55fa-3df7-4979-85fa-0d74ba68d437",
  "iss": "",
  "aud": "myclient",
  "sub": "a6c96222-8e0f-4049-8bd7-505ef282eb66",
  "typ": "ID",
  "azp": "myclient",
  "nonce": "3c3da935-330f-4b19-8317-f51cb8089dfc",
  "session_state": "fad12676-48bf-4f22-9da6-ba3fff1f030f",
  "at_hash": "sY3nB8-nKr9N5qYEVe27XQ",
  "acr": "0",
  "sid": "fad12676-48bf-4f22-9da6-ba3fff1f030f",
  "email_verified": false,
  "name": "Jeff Han",
  "preferred_username": "jeff",
  "given_name": "Jeff",
  "family_name": "Han",
  "picture": "<https://avatars.githubusercontent.com/u/39541657?v=4>",
  "email": "jeff@grepp.co"
}

ID Token에서 picture 값이 추가된걸 확인할 수 있다.

주요 구현

사용자 프로필 사진 표시

ID 토큰의 picture 필드를 활용하여 사용자 사진 표시.

const userProfile = JSON.parse(idToken);
document.getElementById('profile-pic').src = userProfile.picture;

결론

Keycloak을 사용하면 인증 및 권한 관리를 간소화하고 보안을 강화할 수 있습니다. 특히 ID 토큰과 Access 토큰의 활용을 통해 사용자 데이터와 API 보호를 쉽게 구현할 수 있습니다.