Keycloak서버의 구동 및 배포와 관련된 내용들은 이 시리즈의 이전 글들을 참고하자
Keycloak으로 인증서버를 구성했다면, 이 인증서버와 연동될 클라이언트(서비스 또는 애플리케이션)도 있을 것이다.
이번 글에서는 React로 만든 웹서비스에서 keycloak-js
를 활용하여 인증/인가와 관련된 기본적인 기능을 구현해보도록 하겠다.
참고로
@react-keycloak/web
과 같은 써드파티 라이브러리는 사용하지 않고, 순수keycloak-js
를 다룬다.
- Keycloak버전 : 20.0.2
- keycloak-js버전 : 20.0.2
공식문서와 이 유튜브 영상을 매우 추천한다.
유튜브 영상은 최신 버전과 버전차이가 좀 나긴하지만, 전체적인 맥락을 확실히 알 수 있다.
Keycloak은 OAuth2표준을 구현한 인증/인가 프레임워크이다. 기본적으로 OAuth2의 인증/인가 흐름들은 단순하지는 않다. 그래서 직접 이 흐름들에 맞게 개발을 하려고하면 손이 엄청 많이 가고, 안정성도 떨어진다.
그래~서 Keycloak에서는 keycloak-js
라는 어댑터를 제공한다. 쉽게 얘기하자면, sdk같은 것인데, 필요한 모든 기능을 제공한다 👍
바로 이것들을 살펴보도록 하자 ㅎㅎ
import Keycloak from 'keycloak-js'
const config = {
url: "https://keycloak서버.com",
realm: "Realm이름",
clientId: "ClientId"
}
const kc = new Keycloak(config);
먼저 Keycloak의 기능을 사용하려면, Keycloak객체를 초기화해야한다.
초기화하는 방법은 Keycloak객체에 설정값을 넘겨주면 된다.
설정값을 넘겨주는 방법은 json객체 또는 json파일로 구현할 수 있다.
공식문서
export default class KeycloakService {
static #instance;
kc;
constructor(config) {
if (KeycloakService.#instance) return KeycloakService.#instance;
this.kc = new Keycloak(config);
KeycloakService.#instance = this;
}
init(renderCallback) {
this.kc.init({onLoad: 'login-required'})
.then((authenticated) => this.handleAuthenticated(renderCallback, authenticated))
.catch(this.handleError)
}
handleAuthenticated(renderCallback, authenticated) {
if (!authenticated) throw Error('인증되지 않은 접근입니다.');
if (!this.kc.hasRealmRole(ROLE_ADMIN)) throw Error("접근 권한이 없습니다.")
renderCallback()
}
handleError(error) {
console.error(error)
}
login() {
this.kc.login()
}
async logout() {
await this.kc.logout()
}
}
본격적으로 시작하기 전에 Keycloak과 관련된 기능들을 처리하는 KeycloakService라는 클래스를 만들었다. (이렇게 구현하는 것은 전혀 필수적인 것은 아니다.)
메서드들에 대한 소개만 간단히 하고 넘어가도록하겠다.
- init(): Keycloak객체를 초기화한 후, 애플리케이션이 시작하는 시점에 호출할 메서드
- handleAuthenticated(): init()이 성공했을 때, 처리할 로직
- handleError(): init()이 실패했을 때, 처리할 로직
- login(): 로그인 페이지로 이동
- logout(): 로그아웃 기능
인증되지 않은(로그인하지 않은)사용자가 React application으로 접근시 로그인 페이지로 redirect시키는 것을 구현해보겠다.
먼저 React애플리케이션의 main.jsx를 위와 같이 구성했다.
보통은 바로 애플리케이션을 렌더링하지만, .init()
메서드로 감싸줬다.
이 .init()
메서드에서 뭔가를 처리해줄 것이다 ㅎㅎ
.init()
메서드를 살펴보면, onLoad
가 login-required
로 되어 있다.
이 옵션은 로그인을 하지 않은 접근일 경우 강제로 인증서버의 로그인페이지로 redirect시키는 방식으로 동작하게 해준다.
즉, 로그인을 반드시 해야만 React애플리케이션에 접근할 수 있게 되는 것이다.
login-required
옵션만으로 로그인하지 않은 사용자가 접근할 수 없도록 처리가 완료됐다.
그렇다면, 로그인을 하고 나서는 어떻게 될까?
login-required
말고도check-sso
옵션도 있는데, 자세한 내용은 공식문서를 참고하자.
로그인을 한 후에는 이상이 없다면, init()
메서드의 .then()
으로 로직을 타게 되고 이상이 있는 경우엔 .catch()
로 로직을 타게 된다.
나는 then()
에 validation코드를 추가했다.
authenticated된 사용자이면서 특정 권한을 갖고 있어야 React애플리케이션을 렌더링을 하는 것이다. 그렇지 않은 경우엔 에러를 던지고, 던져진 에러는 당연히 .catch()
에서 처리되게 된다.
이와 같은 방식으로 다양한 예외/에러 처리를 할 수 있다.
또, 이 부분에서 로그인한 유저의 AccessToken이나 RefreshToken을 조회해서 쿠키같은 곳에 저장할 수도 있다.
로그아웃은 매우 간단하다.
초기화한 Keycloak객체에는 .logout()
이라는 메서드가 있는데, 이 메서드를 호출하면 바로 로그아웃이 된다. 🤭
이로써, 완전 기본적인 기능들을 살펴봤는데, 추가로 꼭 필요한 기능들을 간단히 짚어보도록 하겠다.
위에서 언급한 것들 말고도 OAuth2 인증/인가를 구현하기 위한 모든 기능들이 Keycloak객체에 들어 있다.
자세한 것은 공식문서를 참고하는 것을 권장한다 ㅎㅎ
토큰들은 Keycloak객체의 variables로 존재하고, 아래와 같이 바로 접근해서 값을 가져올 수 있다.
const keycloak = new Keycloak({...}).init();
kecloak.token;
keycloak.refreshToken;
토큰 갱신은 .updateToken()
이라는 메서드로 구현하면 된다.
위와 같이 다양한 Keycloak의 기능들에 대해서 리스너를 구현할 수 있다.
이 리스너들을 통해서 뭔가 실패했을 때, 사용자에게 알림을 줄 수 있을 것 같다 ㅎㅎ
이번 글에서는 keycloak-js를 통해서 Keycloak인증 서버를 통한 클라이언트의 인증/인가 기능을 구현해보았다.
이런 sdk가 없으면 도대체 어떻게 개발을 할 수 있을까 싶다 ㅎㅎ
땡큐 키클락 🤭