요즘 Ruby 개발자에게는 직접 만든 인증 방식부터 플러그 앤 플레이 방식까지 선택할 수 있는 옵션이 무척 다양합니다. 하지만 이미 알고 계시겠지만, 직접 인증 솔루션을 구축하는 것은 비용도 많이 들고 보안상 위험할 수 있습니다. Devise가 많은 팀의 사실상 표준이라면, 이를 대체할 수 있는 또 다른 선택지가 개발자의 삶을 한결 편하게 해줄 수 있습니다.
이 글에서는 Ruby on Rails 애플리케이션에서 Auth0를 설정하고 활용하는 방법을 다룹니다. 역할(role) 처리부터 여러 인증 공급자를 활용한 사용자 인증까지, 제대로 시작하는 데 필요한 모든 내용을 담았습니다.
시작하기
시작하려면 다음 두 가지가 필요합니다.
- Auth0 계정
- Ruby on Rails 애플리케이션 (7.x 버전 이상)
Auth0는 서드파티 인증 서비스로, 최대 7,000명의 사용자까지 무료로 관리할 수 있는 무료 티어(free tier)를 제공합니다. 시작하기에는 충분한 규모이며, 더 고급 기능이 필요하더라도 가격 합리적입니다.
Auth0에서 Ruby 앱 구성하기
애플리케이션은 리다이렉트와 API 호출을 통해 Auth0에 사용자 인증을 위임하므로, 보안을 철저히 유지해야 합니다.
Auth0 계정에서 테넌트(tenant) 안에 애플리케이션을 생성합니다. 하나의 계정에 여러 테넌트를 만들어 다음과 같은 용도로 분리할 수 있습니다.
- 도메인 이름
- 서로 다른 환경(개발, 운영 등)
- 데이터가 저장되는 국가 또는 지역
첫 번째 테넌트를 만들었다면, 그 안에 애플리케이션을 생성할 수 있습니다. 바로 거기서 시작해 보겠습니다.
애플리케이션 패널의 'Settings' 탭으로 이동합니다. 아래 항목들을 복사해서 안전한 곳에 저장해 두세요.
- 도메인 이름:
app-name.[eu,us,..].auth0.com - 클라이언트 ID(Client ID)
- 클라이언트 시크릿(Client Secret)
또한 다음 Application URIs 항목을 채워 넣어야 합니다. 여기서는 로컬 개발 환경 기준의 값을 사용합니다.
- Allowed Callback URLs:
https://localhost:3000/auth/auth0/callback(인증 후 Auth0가 리다이렉트할 URL) - Allowed Logout URLs:
https://localhost:3000(로그아웃 후 Auth0가 리다이렉트할 URL)
이제 본격적으로 앱을 구성해 보겠습니다.
Ruby on Rails 애플리케이션 준비하기
rails new 명령어로 기본 상태의 Ruby on Rails 애플리케이션부터 시작할 수 있습니다.
여기서는 데이터베이스로 SQLite3를, CSS 프레임워크로 Tailwind를 사용하는 프로젝트를 생성해 보겠습니다. minitest 설치는 건너뛰고, 애플리케이션 이름은 "Auth0 article"로 지정합니다.
몇 가지 속성만 가진 User 모델을 생성합니다.
이것만으로도 애플리케이션을 위한 단순하지만 효율적인 기반이 완성됩니다.
앱에 Auth0 Gem 추가하기
Ruby on Rails 애플리케이션에서 Auth0를 사용하려면 omniauth-auth0와 omniauth-rails_csrf_protection 두 젬(gem)이 필요합니다.
Auth0 설정하기
개발 환경에서 자격 증명(credentials)을 저장하기 위해 작은 설정 파일(config/auth0.yml)을 만듭니다.
ERB 문법을 활용하면 환경 변수를 사용할 수도 있습니다.
물론 Rails의 암호화된 credentials 기능을 활용하면 더욱 안전하고 최신 방식으로 관리할 수 있습니다.
이 파일은 Auth0 초기화 파일(config/initializers/auth0.rb)에서 사용됩니다. 바로 만들어 보겠습니다.
여기서는 Rails.application.config_for를 사용해 YAML 파일의 내용을 편리한 해시 형태로 불러옵니다. 이 부분은 Rails의 암호화된 credentials 저장소를 포함해 어떤 시크릿 관리 라이브러리로도 대체할 수 있습니다.
다음 사항에 주목하세요.
- 공급자 이름(
:auth0) - YAML 파일의 세 가지 항목 —
AUTH0_CONFIG해시를 통해 읽고 사용됩니다. - Auth0 콘솔에서 설정한 값과 일치하는
callback_path키와 값 authorize_params와 그 안의scope키 — 뒤에서 다시 살펴보겠습니다.
이제 인증을 처리하기 위한 UI와 라우팅 요소를 만들어야 합니다.
Tailwind로 라우트와 UI 설정하기
이 예제에서는 Rails 7.1 프레임워크와 TailwindCSS를 사용하는 Ruby on Rails 애플리케이션을 다룹니다.
다음 명령어를 사용합니다.
그런 다음 index 액션을 포함한 두 개의 컨트롤러를 추가해 인증 과정을 테스트할 준비를 합니다.
이 두 명령으로 다음이 생성됩니다.
- index 액션이 준비된
public_controller.rb와private_controller.rb컨트롤러 - 관련된 두 개의 라우트
- 관련된 뷰(view) 파일들
이제 콜백과 실패 처리를 담당할 Auth0 컨트롤러를 추가합니다. 컨트롤러 파일을 생성(bin/rails g controller auth0)하고 다음 코드를 추가합니다.
그다음 라우트를 수정해 이 세 가지 액션을 사용하도록 합니다.
그리고 public 뷰에는 Login 버튼을, private 뷰에는 Logout 버튼을 각각 추가합니다.
이제 'https://localhost:3000'에 접속해 보세요. Login 버튼을 누르면 private 페이지로 리다이렉트되고, 그곳에서 Logout 버튼을 찾을 수 있습니다.
다만 아직 몇 가지 빠진 것이 있습니다.
- 사용자 프로필 데이터
- 사용자 식별 및 권한(authorization) 처리
스코프와 데이터 다루기
조금 뒤로 물러나 초기화 파일(config/initializers/auth0.rb)을 다시 살펴보겠습니다.
여기서 핵심은 스코프(scope), 즉 openid profile입니다. 이는 Auth0에게 우리가 관심 있는 정보가 무엇인지 알려주는 역할을 하며, 실제로 받게 되는 데이터는 다음과 같습니다.
- 공급자 이름(Auth0)
uid: 사용자를 매칭하기 위한 고유 식별자info해시: 이름, 프로필 사진 URL, 그리고 빈 'email' 키를 포함extra_info해시: 이름과 프로필 사진 외에 이름(given name)과 성(family name)도 포함
스코프에 대한 자세한 내용은 Auth0 공식 문서를 참고하세요.
위 정보는 매우 유용하므로, 최소한 첫 로그인 시점에는 해당 데이터를 애플리케이션의 테이블에 저장해 두는 것이 좋습니다.
이를 위해 auth0_controller, 특히 callback 액션에서 응답(response)의 내용을 활용해야 합니다.
리다이렉트 코드 직전에 중단점(breakpoint)을 걸어 해시 내용을 직접 들여다보며 실험해 보세요.
보통은 이메일 주소도 필요합니다. 이를 얻으려면 스코프를 다음과 같이 업데이트하면 됩니다.
애플리케이션 서버를 재시작하고 로그인 과정을 다시 진행하면, 추가 정보에 대한 접근 권한을 애플리케이션에 허용해야 하는 화면이 나타납니다.
이제 callback 액션에서 다음과 같은 코드를 작성할 수 있습니다.
이렇게 하면 사용자의 최신 프로필을 로컬에 항상 유지할 수 있습니다.
세션(Session)에 대하여
세션은 Ruby on Rails 애플리케이션에서 특수한 해시입니다. 컨트롤러와 뷰에서 접근할 수 있으며, 방문자마다 고유하게 할당되고 저장 용량에는 제한이 있습니다.
세션은 '열고' '닫을' 수 있습니다. 특정 데이터가 필요하면 세션을 열어두고, 데이터가 없으면 닫힌 것으로 간주합니다.
Auth0 컨트롤러에 있던 다음 코드를 기억하세요.
첫 번째 줄은 해시의 raw_info 키 값을 세션 해시에 기록하고, 두 번째 줄은 세션 해시를 비웁니다.
그다음 ApplicationHelper에 다음 헬퍼 메서드를 정의할 수 있습니다.
보안 관련 고려사항
이제 이를 컨트롤러용 concern으로 활용해 보겠습니다. 목표는 사용자가 로그인해야만 접근할 수 있는 컨트롤러, 즉 private_controller 같은 컨트롤러를 정의하는 것입니다.
다음과 같은 concern 파일을 작성합니다.
그리고 컨트롤러에서 다음처럼 사용합니다.
로그인하지 않은 방문자가 /private/index 페이지에 접근하려고 하면, 자동으로 사이트의 루트 경로로 리다이렉트됩니다.
다른 인증 공급자와의 통합
Auth0를 통해 여러 인증 공급자를 연동할 수 있습니다. 기본값으로 Google이 설정되어 있으며, 많은 회사에서는 이것만으로 충분합니다.
더 많은 공급자가 필요하다면 Auth0의 해당 테넌트에서 Authentication 메뉴로 이동한 뒤, Social 하위 메뉴에서 추가 공급자를 설정하면 됩니다.
또한 Auth0로 다단계 인증(MFA, Multi-Factor Authentication)을 구성할 수도 있다는 점도 알아두면 좋습니다.
설정 단계 외에는 전반적인 프로세스가 동일합니다. 애플리케이션 입장에서는 방문자가 인증되었음을 증명하는 데이터만 돌려받으면 되기 때문입니다.
권한 부여(Authorization)로 확장하기
이제 사용자를 인증하고 콜백을 통해 그 정보를 애플리케이션으로 전달할 수 있게 되었습니다. 이미 해시에서 사용자의 이메일 주소를 꺼내 데이터베이스에서 해당 사용자를 찾는 방법도 살펴봤습니다.
여기서부터 권한 부여 정책을 정의할 수 있습니다.
Pundit은 권한 부여 정책을 정의하고 활용하기에 훌륭한 선택지이지만, 사용자의 역할(role) 확인에 의존합니다.
사실 role 속성을 사용하는 것이 가장 간단한 방법입니다. 사용자가 처음 로그인을 시도하기 전에, Rails 콘솔이나 애플리케이션의 백엔드 관리 화면에서 사용자를 생성할 때 이 값을 채워 넣을 수 있습니다.
또는 관리자 이메일 목록을 만들어 두고, 사용자 생성 시점에 해당 목록과 사용자의 이메일을 대조하는 방법도 있습니다.
이걸로 끝입니다!
정리하며
이 글에서는 다음을 설정했습니다.
- Auth0의 테넌트와 애플리케이션
- Ruby on Rails 애플리케이션에 Auth0 관련 젬
- 애플리케이션의 라우트와 뷰
그리고 다음을 수행했습니다.
- 세션 개념을 복습하고 활용 방법을 확인
- 사용자 로그인 여부를 확인하는 기본 도구 구현
- 컨트롤러 concern을 추가해 로그인 필수 컨트롤러 보안 강화
- 사용자를 역할과 매칭하는 방법 학습
Auth0 같은 인증 서비스를 활용하면 코드를 거의 작성하지 않고도 Ruby on Rails 애플리케이션에 최신 수준의 인증을 통합할 수 있습니다. 인증 레이어를 직접 구현하거나 Devise 같은 젬을 사용하는 것보다 훨씬 나은 선택인 경우가 많습니다.
즐거운 코딩 되세요!
P.S. Ruby Magic의 글을 발행 즉시 읽고 싶으시다면 Ruby Magic 뉴스레터를 구독하고 어떤 글도 놓치지 마세요!
Thomas Riboulet
게스트 저자 Thomas는 프랑스에 거주하는 백엔드 및 클라우드 인프라 엔지니어 겸 컨설턴트입니다. 13년 이상 스타트업과 기업들의 팀, 제품, 인프라 확장을 지원해 왔으며, 프랑스 GNU/Linux 잡지와 자신의 블로그에 여러 차례 글을 발표한 경력도 있습니다.
Thomas Riboulet의 모든 글 보기