Ruby on Rails Google Login API only mode


Ruby on Rails에서 Social Login을 하기 위해서는 omniauth-oauth2 기반의 gem들을 사용하는게 가장 일반적인 방법이다.

잠깐! 위에서 KakaoLine은 정상동작하지 않는다. 좀 더 자세히 설명하자면 omniauth-google-oauth2를 정상적으로 사용하려면 omniauth-oauth2 1.6버전 이상이 필요한데, omniauth-kakaoomniauth-line1.3버전에서 정상동작하게 되어 있다. 그래서 omniauth-kakaoomniauth-line1.7버전 이상에서 정상동작하도록 Fork떠서 수정하였다. 정상동작하는 gem은 아래에서 확인 가능하다.

Gemfile선언시 아래와 같이 하면 된다.

gem 'omniauth-kakao', git: "https://github.com/DevStarSJ/omniauth-kakao"
gem 'omniauth-line', git: "https://github.com/DevStarSJ/omniauth-line"

둘 다 response되는 정보에 email이 빠져있어서 해당 정보들도 추가하였다.

먼저 검색

위 gem들을 사용해서 Social Login을 구현하기 위해서 자료들을 검색해보았다. 그런데 대부분 MVC로 구현하고 devise를 이용하는 방법밖에 없었다. 참고로 user 인증은 별도로 구현하였으며, API server를 만드는 중이라 View가 필요없다. 해당 gem의 github를 보더라도 MVC로 구현하는 방법은 자세히 나와 있지만, API only에서 활용가능한 방법은 안내가 나와있지 않다. 글 내용을 보면 API only에서는 필요없음 등이 있는 것으로봐서 분명 가능은 하다는 이야기인데 정작 하는 방법에 대해서 설명이 없다. 나와 같이 사용하는 경우에 대해서 안내해주는 글을 못찾았다.

공식 github의 README에 안내해주지 않은 내용 중에 API only로 사용하기 위해서 필요한 내용은 정말 몇가지 2가지 정보만 있으면 된다. 그런데 그게 없다.

  • Google Auth를 호출하는 API path
  • callback을 받을 API path

callback을 받을 위치는 설정이 가능하므로 굳이 없어도 가능은 하다.

정답부터 말하자면

  • Google Auth를 호출하는 API path : /auth/google_oauth2
  • callback을 받을 API path : auth/google_oauth2/callback

참고로 이 사실을 이미 알고 난 뒤 다시 omniauth-google-oauth2를 보니 관련 내용이 아주 짧게 언급되어 있었다.

You can now access the OmniAuth Google OAuth2 URL: /auth/google_oauth2

그동안 같은 고민을 하면서 위치를 못 찾은 분들은 위 정보만으로도 충분할 것이다. 아래 내용은 필요없을 것이다. 나는 이걸 어떻게 발견했냐면 Ajay Ramesh라는 분이 쓴 medium 글에 나와있었다.

https://medium.com/@ajayramesh/social-login-with-omniauth-and-rails-5-0-ad2bbd2a998e

위 글을 참고하여 실습을 진행했고, 정상적으로 동작하는 것을 확인하였다. 그럼 지금부터 내용들을 적어보겠다.

1. Google API에 App 등록

https://console.cloud.google.com로 들어가서 API -> 사용자 인증 정보를 만든 다음 OAuth 2.0 Client ID에서 Client IDClient Secret을 확인한다. 아래에서 필요한 정보이다.

일단 Test는 local에서 실행할 것이며 Rails에서 기본적으로 실행하는 local주소는 http://localhost:3000 이다. 그래서 URIs 정보에 http://localhost:3000를 넣어주고, Authorized redirect URIshttp://localhost:3000/auth/google_oauth2/callback를 추가한다.

2. Gem Install

Gemfile에 아래 줄을 추가한다.

gem 'omniauth-google-oauth2'

Bundle을 실행하여 Install을 한다.

bundle install

3. Model 생성

필자의 경우 User Model과 SocialAuth Model을 분리하였다. 왜냐면 1명의 User에게 여러 Social Login을 가능하게 하기 위해서이다.

3.1 User Model 생성

rails g model User 를 실행하여 Migration 파일을 수정한다.

class CreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      t.string :email, null: false
      t.string :password_digest

      t.timestamps
    end
  end
end

passwordnullable로 설정하였다. 왜냐면 password가 없는 경우 Social Login만을 허용하기 위해서다. 하지만 Model에서 Null Check를 하므로 validation을 하지 않는 것으로 선언해야 한다.

  • user.rb
    class User < ApplicationRecord
    include ActiveModel::SecurePassword
    has_secure_password validations: false
    end
    

rails db:migrate 를 실행하여 Model을 생성한다.

3.2 SocialAuth Model 생성

rails g model SocialAuth를 실행하여 Migration 파일을 생성 후

class CreateSocialAuths < ActiveRecord::Migration[6.0]
  def change
    create_table :social_auths do |t|
      t.string :provider, null: false
      t.string :uid, null: false
      t.string :first_name
      t.string :last_name
      t.string :email
      t.string :photo
      t.references :user, null: false, foreign_key: false

      t.timestamps
    end
  end
end

위 내용과 같이 수정한다.

  • social_auth.rb
    class SocialAuth < ApplicationRecord
    belongs_to :user
    end
    

Model 파일을 수정한 뒤 rails db:migrate 를 실행하여 Model을 생성한다.

4. Initialize 선언

config/initializers/omniauth.rb 파일을 생성하여 아래와 같이 입력한다.

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET']
end

.env 파일에 위에서 확인한 Client IDClient Secret를 각각 GOOGLE_CLIENT_IDGOOGLE_CLIENT_SECRET로 등록한다. 참고로 .env파일을 적용하려면 dotenv-rails를 설치해야 한다.

5. Route 선언

routes.rb파일에 아래 내용을 추가한다.

  get 'auth/google', to: redirect('/auth/google_oauth2')
  get 'auth/google_oauth2/callback', to: 'auth#google'

API 주소를 /auth/google_oauth2에서 auth/google로 수정하였다.

6. Controller, Service 선언

auth_controller.rb 파일을 생성하여 아래와 같이 입력한다.

class AuthController < ApplicationController
  def google
    user = SocialAuthService.google(omniauth_params)
    render json: user
  end

  private
  def omniauth_params
    request.env['omniauth.auth'].except('extra').to_h
  end
end

참고로 인증받은 구글에서의 profile정보는 request.env['omniauth.auth']에서 확인이 가능하다. 예제에서는 User Model을 그대로 response해주는 것으로 해 놓았다. 실제 사용할때는 생성되거나 찾은 사용자 정보에서 JWT Token등을 생성해서 전달해 준다던지, request.env['omniauth.auth']안에 있는 token을 그대로 사용하는 방법이 있겠다.

social_auth_service.rb 파일을 생성하여 아래와 같이 입력한다.

class SocialAuthService
  def self.google(params)
    apply(params, 'google')
  end

  def self.apply(params, provider)
    return nil unless params["provider"].present? && params["uid"].present? && params["info"].present?
    return nil unless params["provider"].include?(provider)

    social_auth = initialize(params, provider)
    social_auth.user
  end

  def self.initialize(params, provider)
    SocialAuth.where(provider: provider, uid: params["uid"]).first_or_create do |auth|
      auth.provider = provider
      auth.uid = params["uid"]
      auth.email = params["info"]["email"]
      auth.first_name = params["info"]["first_name"] ||  params["info"]["name"]
      auth.last_name = params["info"]["last_name"]
      auth.photo = params["info"]["image"]
  
      link_user(auth)
    end
  end

  def self.link_user(auth)
    return if auth.user_id.present?

    user = User.find_by(email: auth.email)
    user = User.create(email: auth.email) if user.blank?
    auth.user = user
  end
end

.first_or_create는 앞에 조건에 만족하는 첫번째 instance를 반환하거나 없다면 block에 선언한대로 새로 생성을 한다. 여기서 찾거나 새로 생성한 SocialAuth에서 전달받은 email정보를 가지고 User를 검색한 후, 있으면 그 User와 연결을 하고 없다면 User를 새로 생성한다. 이 부분 역시 .first_or_create를 사용해도 된다.

7. 실행 및 확인

이제 서버를 실행해서 확인해보자.

rails s

로 서버를 실행한 다음 http://localhost:3000/auth/google 를 Browser에서 실행하면 서버를 실행한 로그에 각 Model이 생성되는 SQL문을 볼 수 있으며, 화면에도 새로 생성된 사용자 정보를 볼 수 있다. 참고로 request.env['omniauth.auth'] 정보를 확인해보고 싶다면 AuthController.google의 내용을 render json: omniauth_params로 수정을 하던지, 아니면 해당 method 안에서 binding.pry를 걸어서 확인해보면 된다.

아래의 모양으로 되어 있다.

{
  "provider"=>"google_oauth2",
  "uid"=>"1234567890",
  "info"=>
  {
    "name"=>"Yun Seokjoon",
    "email"=>"seokjoon.yun@gmail.com",
    "unverified_email"=>"seokjoon.yun@gmail.com",
    "email_verified"=>true,
    "first_name"=>"Yun",
    "last_name"=>"Seokjoon",
    "image"=>"https://lh3.googleusercontent.com/a-/AOh14Ghld2CXOEoClXk9ciHU9XdRHpg5j64ZpqYtcn-W=s96-c"
  },
  "credentials" =>
  {
    "token"=>"xxxx.xxxxxxxxcxcxcxcxcxcxcxcxcxcxcxcxxcxc",
    "refresh_token"=>"x//xcxcxcxcxcxcxcxcxcx-xcxcxcxcxcxcxcxcxcxcxc-xcxcxcxcxcxcxcxcxcxcxxc",
    "expires_at"=>1606465984,
    "expires"=>true
  }
}

마치며…

위에서 소개한 방법을 적용한 예제코드는 아래 Link에서 확인이 가능하다.

https://github.com/DevStarSJ/backend_boilerplates/tree/master/rails

이로서 Google Login에 대한 안내는 마치겠다.

다른 Social Login의 방법은 아래에서 확인이 가능하다.


이 글이 도움이 되셨다면 공감 및 광고 클릭을 부탁드립니다 :)