KeyCIoak - 모던 애플리케이션을 위한 ID 및 접근관리 | 에이콘출판(주) | 스티안 토르거센, 페드로 이고르 실바 지음, 최만균 옮김
서적을 참고하여 요약한 시리즈 글이다.
1. 접근 토큰 획득
1.1 OAuth 2.0 플레이 그라운드 실행
책에서 제공하는 코드 중 ch5를 실행하면 OAuth 2.0을 위한 플레이그라운드 앱에 접속할 수 있다. 실행 방법은 앞선 글들과 유사하다. 백엔드, 프론트엔드 코드 각각에서 /auth 부분을 모두 삭제해준다. 자세한 과정은 생략한다.
백엔드, 프론트엔드 코드를 모두 실행 후 localhost:8000에 접속하면 아래와 같은 프론트엔드 화면을 확인할 수 있다.
1.1.1 클라이언트 등록
다음 클라이언트를 추가한다.
- client id: oauth-playground
- access type: public
- valide redirect urls: http://localhost:8000/
- web origins: http://localhost:8000
access type은 Client authentication 토글을 off하면 public으로 설정되는 것 같다.
1.2 접근 토근 획득
플로우에 대해서 다시 상기해본다.
- 사용자가 애플리케이션으로 인증 요청을 보내면 애플리케이션은 인가 요청을 생성한다.
- 인가 요청을 통해 사용자가 keycloak으로 접속하고, 사용자의 정보를 입력하면 keycloak은 애플리케이션에 인가 코드를 전송해준다.
- 애플리케이션은 인가 코드를 바탕으로 keycloak에 토큰 요청을 전송하고, 받은 토큰을 기반으로 백엔드에 REST API 요청 등을 수행하여 사용자에게 정보를 전달한다.
직접 플레이그라운드로 이 과정들을 수행해본다.
1. Discovery에서 Load OAuth 2.0 Prodiver Configuration 버튼을 누른다. 그 다음 2. Authorization에서 Send Authorization Request를 누른다.
그럼 로그인 페이지로 리다이렉트되고, myrealm에 등록된 사용자로 로그인할 수 있다. 그리고 아래에서 Access Token을 받아오는 것을 확인할 수 있다. 개발자 도구를 켜서 확인해보면 http://localhost:8000/realms/myrealm/protocol/openid-connect/token 주소로 요청을 보낸 것을 볼 수 있다.
JWT 토큰으로 보낸 payload를 파싱하여 정보를 보여주는 것도 확인할 수 있다.
payload에 포함된 정보들 중 눈여겨 볼만한 부분들은 다음과 같다.
- aud: 접근 토큰에서 사용할 수 있는 서비스 리스트
- realm_access: 토큰에서 접근할 수 있는 글로벌 역할의 리스트
- resource_access: 토큰에서 접근할 수 있는 클라이언트의 역할 리스트
- scope: 토큰에 포함된 범위
인가 후 3. invoke에서 invoke를 하면 /secured로 keycloak.protect("realm:myrole")이 적용된 부분이 403 Access Denied가 되지 않고 잘 수행되어야한다. 그런데 나의 경우 realm role 로서 myrole을 만들고 로그인하는 사용자에게 myrole을 부여하여 토큰에 realm_role로 "myrole" 값이 부여되었는데도 계속 잘 안된다.
keycloak doc과 참조 이슈이다.
https://www.keycloak.org/docs/latest/securing_apps/
이 부분은 원리만 이해하고 일단은 넘어가야겠다.
-> 다음 장 Access Token 접근 권한 제한 - audience 설정 부분에서 어떤 것이 문제였는지 나온다. 지금은 일단 넘어가자
2. 사용자 동의 요청
2.1 사용자 동의 요청 on/off
일반적으로 외부 애플리케이션의 경우 항상 사용자의 동의가 필요하지만, 관리자가 잘 알고 있는 애플리케이션의 경우 사용자의 동의 요청을 생략할 수 있다. clients -> oauth playground -> login settings 항목에 보면 consent required 속성이 있다. 이 속성을 켜고 다시 oauth playground로 돌아가서 2. authorization 을 진행해보자. 그럼 아래처럼 사용자 동의 요청 화면이 뜬다.
2.2 사용자 동의 요청 항목 추가
1. client scopes -> create client scope 에서 다음 값들을 입력한다.
- name: albums
- Display on consent screen: ON
- Consent Screen Text: View your photo albums
2. clients -> oauth-playground -> client scopes 에 가서 위 추가한 scope를 Optional로 추가해준다.
이후 다시 palyground에 가서 scope에 albums를 추가하고, 2. Authorization을 시도하면 아래처럼 동의 내용의 항목에 View your photo albums가 추가된 것을 볼 수 있다.
3. Access Token으로 접근 권한 제한
특정 Access Token으로 접근 권한을 제한하는 방법은 아래의 3가지 방법이 있다.
- Audience: Access Token을 허용하는 리소스 공급자 리스트
- Role: 클라이언트가 접근할 수 있는 권한을 역할로써 제한
- Scope: 애플리케이션은 Client Scope를 통해 제어된다.
3.1 Audience를 사용하여 접근 제한
백엔드가 토큰의 audience 값을 확인하도록 on/off 하는 설정값은 backend/keycloak.json의 verify-token-audience 항목에서 확인할 수 있다.
이 값이 false여서 token의 audience 값을 확인하지 않고 있는데, 우선 이 값을 true로 만들어서 토큰의 "aud" 값을 확인하도록 한다.
이후 oauth-playground 클라이언트에서 받아오는 토큰의 "aud" 값에 oauth-backend가 추가되면 백엔드에서 audience를 확인하여 접근 가능 여부를 판단하고 제한을 거는 방식이다.
이제 백엔드에서 제한을 풀도록 audience에 추가해본다.
1. 클라이언트로 oauth-backend를 등록한다. clients -> create client 으로 생성하고, client ID란에 oauth-backend라고 입력한다.
2. clients -> oauth playground -> client scopes -> oauth-playground-dedicated -> mappers -> add mapper -> By Configuration으로 추가한다.
3. audience 타입, 이름은 backend audience, Include Client Audience: oauth-backend로 하고 Add to ID token 토글을 ON 한다.
이제 다시 2.Authorization에 돌아와 요청을 보내면 aud에 oauth-backend가 추가된 것을 확인할 수 있다.
그리고 3. invoke service에서 정상적으로 secret message가 출력되는 것도 볼 수 있다.
3.2 Role을 사용하여 제한
Role은 사용자의 역할을 제어하는 기능도 있고, 애플리케이션의 권한을 제어하기도 한다. 그래서 매우 중요하다.
기본적으로 토큰에 사용자의 역할이 모두 포함되어있다. 이것은 oauth-playground 클라이언트 - cline scopes - oauth-playground-dedicated - Scope에 Full Scope Allowed가 기본적으로 켜져있기 때문이다. 이 설정 값은 기본적으로 클라이언트에게 전송하는 토큰에 사용자의 모든 역할을 포함하여 응답해주는 옵션이다. 프로덕션 환경에서는 이 값을 꺼두어야한다.
3.2.1 Full Scope Allowed
Full Scope Allowed 옵션이 켜진 상태라면 아래처럼 realm_access : role 및 resource_access: role 부분에 사용자가 가진 모든 role들이 존재하는 것을 볼 수 있다.
그러나 옵션을 끄고 다시 Authorization 요청을 보내보면, 이 항목들이 사라지는 것을 볼 수 있다.
3.2.2 role 추가
이제 myrole을 추가해본다. clients -> oauth playground -> client scopes -> oauth playground-dedicated -> roles 탭에서 assign role을 클릭하고 myrole을 추가한다. myrole 검색이 잘 안되면 필터를 Filter by realm roles로 변경해주면 된다.
이후 다시 로그인 해보면 realm_access의 role에 myrole이 추가된 것을 확인할 수 있다.
3.2.3 다른 클라이언트의 범위에 포함된 role 포함
다른 클라이언트의 범위에 포함된 role을 포함시키는 실습을 해보기 위해서 일단 위에서 추가했던 myrole을 제거한다. client scopes에 들어가서 신규 client scope를 생성한다. 이름을 myrole2로 입력하고 Optional로 생성한다. 그리고 scope 탭에 들어가서 myrole을 assign 한다.
이후 3.2.2절의 내용대로 oauth-playground 클라이언트의 client scope에 myrole을 포함시켜준다.
그럼 Authorization 요청 시, realm_access 항목에 글로벌 realm role인 myrole이 추가된 것을 볼 수 있다.
3.3 토큰 접근을 제한하기 위한 scope 활용
3.3.2 Scope 예시
기능, url 별로 scope를 제한할 수 있다. 표준은 없고 각자 reference를 참고하여 scope를 정해야한다. 몇 가지 예시는 다음과 같다.
- albums: view
- albums: create
- https://www.googleapis.com/auth/gmail.compose
- https://www.googleapis.com/auth/calendar.events
3.3.2 실습
실습을 해본다. 먼저 client scopes에 들어가서 다음 세 가지 scope를 만든다. Consent Screen Text에 값을 입력하여 사용자 동의 항목에 표시할 텍스트를 지정해주는 것도 잊지 말자
- albums: view
- albums: create
- albums: delete
다음으로 oauth-playground에 들어가서 만들어둔 client scopes들을 추가한다. view scope는 default로, 나머지는 optional로 지정한다. 테스트 전에 oauth-playground의 settings - Consent Required가 활성화되어있는지 확인해야한다.
다음으로 다시 myrealm의 사용자로 로그인을 시도하면 아래와 같이 사용자 동의 제공 화면이 출력되는 것을 볼 수 있다.
앞선 실습에서 했던 것처럼, 만약 Optional로 지정한 albums: create scope를 사용자 동의 제공 항목에 포함시키고 싶다면 Authorization 요청을 보낼 때 scope 항목에 albums: create를 추가로 주면 된다. 그럼 사용자 동의 제공 화면에 albums:create가 추가되는 것을 볼 수 있다.
4. 접근 토큰 검증
4.1 검증 방법
토큰을 검증하는 가장 기본적인 방법은 토큰 검증용 엔드포인트에 요청을 쏴보는 것이다. 다만 이 방식은 요청 처리에 대한 지연시간이 높아지고 인증 서버에 추가로 로드가 발생한다는 점이 있다. 이미 검증된 토큰은 캐시하고 서비스에서 사용 가능한 시간 동안에는 토큰을 재검증 하지 않도록 하는 것이 하나의 방법이 된다. 다만 토큰 재검증 시간은 일반적으로 몇 분 정도로 상당히 짧아야한다.
또는 애플리케이션에서 Token을 파싱해서 직접 검증해도 된다.
4.2 토큰 검증 엔드포인트 요청 실습
oauth-backend의 자격 증명 및 인코딩된 접근 토큰이 필요하다. credentials 탭에 가서 해당 클라이언트의 secrets를 복사해와야하는데, credentials 탭을 띄울려면 settings -> Capability config -> client authentication 항목을 ON 해줘야한다.
ref.) https://stackoverflow.com/questions/44752273/do-keycloak-clients-have-a-client-secret
save 후 새로 생성된 credentials 탭에서 client secret을 복사하면 된다.
터미널을 열고 SECRET 값을 export 한다.
export SECRET=1eKWb7G7F4Edu0X4kIkveCovPoU6r0J8
터미널을 열어둔 채로 playground에서 신규 접근 토큰을 획득한다. 여기서 하단의 Encoded 부분에서 인코딩된 접근 토큰을 복사하고 아래처럼 환경 변수로 지정한다.
export TOKEN=eyJhbGciOiJSUzI1NiI...
다음으로 터미널에 curl 요청을 실행하면 접근 토큰 검사 엔드포인트를 호출할 수 있다.
curl -d "client_id=oauth-backend&client_secret=$SECRET&token=$TOKEN" \
-H "Content-Type: application/x-www-form-urlencoded" \
-X POST http://localhost:8080/realms/myrealm/protocol/openid-connect/token/introspect
인코딩된 토큰이 만료되기 전에 요청을 보내야하고, SECRET, TOKEN 등이 잘못 입력되지는 않았는지 체크하면서 진행해야한다. 또한 책에서는 localhost:8080/auth/... 의 주소로 되어있는데, 앞에서의 다른 경우들과 마찬가지로 /auth 주소는 제거해줘야 정상 작동되었다.
'Programming-[Backend] > Keycloak' 카테고리의 다른 글
Keycloak - 6. 애플리케이션과 통합, 리버스 프록시 (0) | 2024.08.02 |
---|---|
Keycloak - 5. 보안 (0) | 2024.08.01 |
Keycloak - 3. OpenID Connect 인증, 토큰 관리, 로그아웃 (0) | 2024.07.30 |
Keycloak - 2. 프로젝트, 클라이언트, 인증 프로토콜 (0) | 2024.07.27 |
Keycloak - 1. KeyCloak 사용 이유, 설치하기, 기본 설정 살펴보기 (0) | 2024.07.27 |