How to return Doorkeeper access token as json

2020-06-06 04:45发布

I am trying to create a log in system for an iOS app with a rails back end powered by devise and door keeper.

I want to limit the number of network requests so don't want to have to get the token from credentials then get the user details as a separate request.

Here is my current attempt:

token = Doorkeeper::AccessToken.create!(application_id: @application_id,
    resource_owner_id: current_user.id, :expires_in => 168.hours)
puts token.token
render :json => {:user => current_user, :token => token.as_json(:include=> token)}, 
    status: :ok, location: :users

However what is being returned is:

{"user":{"id":2,"email":"user3@test.com","created_at":"2014-06-12T17:25:12.000Z",
"updated_at":"2014-06-13T12:20:18.536Z",
"firstName":"user","lastName":"test","subscription":null},
"token":{"resource_owner_id":2,"scopes":[],"expires_in_seconds":604800,
"application":{"uid":"[Filtered]"}}}

So the actual access_token key isn't being passed back to allow me to make future calls. I can see that the token itself isn't returned in DoorKeeper::AccessToken.as_json, but token.as_json(:include=> token) still doesn't return it.

Does anyone know how to return the AccessToken, including the access token itself, as json?

5条回答
▲ chillily
2楼-- · 2020-06-06 04:47

I know it's been a while this has been solved. I recently wanted to implement the same behavior on my API and I relied on model association in order to achieve it :

class User < ActiveRecord::Base
  has_one :token, -> { order 'created_at DESC' }, class: Doorkeeper::AccessToken, foreign_key: :resource_owner_id
end

Then using serializers :

class UserSerializer < ActiveModel::Serializer
  attributes :id, :name, :email
  has_one :token, serializer: AccessTokenSerializer
end

class AccessTokenSerializer < ActiveModel::Serializer
  attributes :token, :expires_in_seconds
end

You could simply return your data as :

class DummyController < ApplicationController
  def dummy
    respond_with current_user, status: :ok
  end
end

which will output a JSON that looks like :

{
    "id": 17,
    "name": "Chuck Norris",
    "email": "chuck@norr.is",
    "token": {
        "token": "c62af258f2d1ac816f65606a2a8413b8e0c2ad5b4434f9c75b56765f54ee627b",
        "expires_in_seconds": "5427"
    }
}
查看更多
迷人小祖宗
3楼-- · 2020-06-06 04:52

Easy! you don't need to inherit from Doorkeeper::TokensController but you can if it suits better to your case:

class CustomController < ApplicationController
  def create
    ...
    access_token = Doorkeeper::AccessToken.create!(application_id: @application_id, resource_owner_id: current_user.id, :expires_in => 168.hours)
    render json: Doorkeeper::OAuth::TokenResponse.new(access_token).body
  end
end
查看更多
走好不送
4楼-- · 2020-06-06 04:56

The way I managed to solve this was to create my own AccessToken class that overloads the as_json method to include the fields I wanted.

e.g

class AccessToken < Doorkeeper::AccessToken
   def as_json(options={})
      {
        :token => self.token,
        #:resource_owner_id => self.resource_owner_id,
        #:scopes => self.scopes,
        :created_at => self.created_at,
        :expires_in_seconds => self.expires_in_seconds,
        #:application => { :uid => self.application.uid }
      }
   end
end

If anyone has a better solution I'm all ears

查看更多
再贱就再见
5楼-- · 2020-06-06 05:05

The way I handled this was to create a custom tokens controller and overriding the token request action. There I could append custom stuff to response.

# app/controllers/custom_tokens_controller.rb
class CustomTokensController < Doorkeeper::TokensController

  # Overriding create action
  # POST /oauth/token
  def create
    response = strategy.authorize
    body = response.body

    if response.status == :ok
      # User the resource_owner_id from token to identify the user
      user = User.find(response.token.resource_owner_id) rescue nil

      unless user.nil?
        ### If you want to render user with template
        ### create an ActionController to render out the user
        # ac = ActionController::Base.new()
        # user_json = ac.render_to_string( template: 'api/users/me', locals: { user: user})
        # body[:user] = Oj.load(user_json)

        ### Or if you want to just append user using 'as_json'
        body[:user] = user.as_json
      end
    end

    self.headers.merge! response.headers
    self.response_body = body.to_json
    self.status        = response.status

  rescue Doorkeeper::Errors::DoorkeeperError => e
    handle_token_exception e
  end
end

Just make sure that you point to this controller in routes.rb

# routes.rb
Rails.application.routes.draw do

  # Doorkeeper
  use_doorkeeper do
    controllers :tokens => 'custom_tokens'
  end

  # Your other routes here...

end

This is tested and it works, I am using it in my projects.

查看更多
一夜七次
6楼-- · 2020-06-06 05:11

Actually we can get access token with:

doorkeeper_token.token

you can include it in the json response so you don't need to edit anything inside doorkeeper itself.
However, don't forget to add some conditionals before including it in the json response as it doesn't seem so good to always show it in the response.

查看更多
登录 后发表回答