【问题标题】:Doorkeeper ActiveRecord::NotNullViolation on "application_id" (password grant)Doorkeeper ActiveRecord::NotNullViolation on "application_id" (password grant)
【发布时间】:2019-07-19 23:09:28
【问题描述】:

我正在尝试为我的 API 设置 Doorkeeper(使用密码授予流程),但每当我尝试检索访问令牌时,我的 Rails 服务器上都会出现以下错误:

ActiveRecord::NotNullViolation (PG::NotNullViolation: ERROR:  null value in column "application_id" violates not-null constraint
DETAIL:  Failing row contains (1, 1, null, e24EH4dNKBNoHh7OSzzyT_7Cg4Ka52e9TB4TP-vz4aM, null, 7200, null, 2019-07-19 22:50:44.797045, , ).
: INSERT INTO "oauth_access_tokens" ("resource_owner_id", "token", "expires_in", "created_at", "scopes") VALUES ($1, $2, $3, $4, $5) RETURNING "id"):

我遵循了 Doorkeeper 的 rails getting started guide,除了将关联添加到我的用户模型的最后一步——我在遇到错误后尝试这样做,但这没有帮助。我目前没有使用Devise

我没有正确配置一些东西吗?或者也许我必须使用设计?我不确定application_id 字段在哪里发挥作用,因为门卫的文档中有留下的应用程序页面blank

我发送给 API 的内容:

grant_type: password
username: test@user.com
password: password

我的 doorkeeper.rb 初始化文件:

Doorkeeper.configure do
  # Change the ORM that doorkeeper will use (needs plugins)
  orm :active_record

  # This block will be called to check whether the resource owner is authenticated or not.
  resource_owner_authenticator { current_user || render(status: 401) }

  resource_owner_from_credentials do |_routes|
    user = User.find_by_email(params[:username].try(:downcase))
    user if user && user.authenticate(params[:password])
  end

  grant_flows %w[password]
end

我的门卫迁移文件:

class CreateDoorkeeperTables < ActiveRecord::Migration[5.2]
  def change
    create_table :oauth_applications do |t|
      t.string  :name,    null: false
      t.string  :uid,     null: false
      t.string  :secret,  null: false

      # Remove `null: false` if you are planning to use grant flows
      # that doesn't require redirect URI to be used during authorization
      # like Client Credentials flow or Resource Owner Password.
      t.text    :redirect_uri, null: false
      t.string  :scopes,       null: false, default: ''
      t.boolean :confidential, null: false, default: true
      t.timestamps             null: false
    end

    add_index :oauth_applications, :uid, unique: true

    create_table :oauth_access_grants do |t|
      t.references :resource_owner,  null: false
      t.references :application,     null: false
      t.string   :token,             null: false
      t.integer  :expires_in,        null: false
      t.text     :redirect_uri,      null: false
      t.datetime :created_at,        null: false
      t.datetime :revoked_at
      t.string   :scopes
    end

    add_index :oauth_access_grants, :token, unique: true
    add_foreign_key(
      :oauth_access_grants,
      :oauth_applications,
      column: :application_id
    )

    create_table :oauth_access_tokens do |t|
      t.references :resource_owner, index: true
      t.references :application,    null: false

      # If you use a custom token generator you may need to change this column
      # from string to text, so that it accepts tokens larger than 255
      # characters. More info on custom token generators in:
      # https://github.com/doorkeeper-gem/doorkeeper/tree/v3.0.0.rc1#custom-access-token-generator
      #
      # t.text :token, null: false
      t.string :token, null: false

      t.string   :refresh_token
      t.integer  :expires_in
      t.datetime :revoked_at
      t.datetime :created_at, null: false
      t.string   :scopes

      # If there is a previous_refresh_token column,
      # refresh tokens will be revoked after a related access token is used.
      # If there is no previous_refresh_token column,
      # previous tokens are revoked as soon as a new access token is created.
      # Comment out this line if you'd rather have refresh tokens
      # instantly revoked.
      t.string   :previous_refresh_token, null: false, default: ""
    end

    add_index :oauth_access_tokens, :token, unique: true
    add_index :oauth_access_tokens, :refresh_token, unique: true
    add_foreign_key(
      :oauth_access_tokens,
      :oauth_applications,
      column: :application_id
    )

    # Uncomment below to ensure a valid reference to the resource owner's table
    # add_foreign_key :oauth_access_grants, <model>, column: :resource_owner_id
    add_foreign_key :oauth_access_tokens, <model>, column: :resource_owner_id
  end
end

我的用户模型:

class User < ApplicationRecord
  before_save { email.downcase! }
  validates :name, presence: true, length: { maximum: 50 }
  # VALID_EMAIL_REGEX = (removed for this post)
  validates :email, presence: true, length: { maximum: 255 },
                    format: { with: VALID_EMAIL_REGEX },
                    uniqueness: { case_sensitive: false }
  has_secure_password
  validates :password, presence: true, length: { minimum: 6 }
end

【问题讨论】:

    标签: ruby-on-rails ruby oauth-2.0 doorkeeper


    【解决方案1】:
    1. 创建一个门卫应用程序。

      • 无 API 模式:只需导航到 /oauth/applications/new
      • API 模式:您可以从 Rails 控制台创建应用程序:

        application = Doorkeeper::Application.create( name: "ApplicationName", scopes: "read write", confidential: false, redirect_uri: "urn:ietf:wg:oauth:2.0:oob" )

    2. 现在您可以使用applicationuid (client_id) 和secret 来请求访问令牌。示例curl 请求password flow

      `curl --location --request POST 'http://localhost:3000/oauth/token?client_id=CLIENT_ID&client_secret=SECRET' --header 'Content-Type: application/json' --header 'Accept: application/json' --data-raw '{"username": "example@email.com", "password": "password", "grant_type": "password" }'`
      
    3. 其他流(如Authorization 流)要求您获取一个authorization 令牌,您可以使用它来请求access tokens。相关维基:https://github.com/doorkeeper-gem/doorkeeper/wiki/Authorization-Code-Flow

    【讨论】:

    • 嗨!我正在使用密码授予类型,似乎在获得 access_token 之前,用户需要先使用电子邮件和密码以及 client_id 和 client_secret 登录才能正常工作。但是为了获取client_id和client_secret,用户必须首先有一个门卫应用程序。何时是创建门卫应用程序的最佳地点?在 /register 或 /login?我应该为门卫应用程序创建单独的路线吗?因为当然,门卫应用程序的创建不应该是可见的,也不应该包含在用户的 UX 中。你的策略是什么?非常感谢!
    • @Welp 我不确定您是否了解门卫应用程序的目的。您的身份验证提供程序的每个客户端应用程序都与一个 Doorkeeper 应用程序相关联。示例:我有一个 API 服务于两个客户端应用程序,一个 Web 应用程序和一个 android 应用程序。所以我创建了两个 Doorkeeper 应用程序,每个都与一个客户端应用程序相关联。我本来可以为这两个客户端使用一个 Doorkeeper 应用程序,但我想为这两个客户端使用不同的流程和配置。 1/3
    • 现在就我而言,我不打算为第三方应用程序使用打开我的身份验证提供程序(或 API)。所以我禁用了 Doorkeeper 应用程序创建路由。见:doorkeeper.gitbook.io/guides/configuration/skip-authorization2/3
    • 如果您不想这样做,并且想要支持第三方应用程序,您只需使用 Doorkeeper 为您提供的路由。当然,请确保您没有使用 skip_authorization 配置选项。或者您可以提供自己的应用程序创建页面。 3/3
    • 请记住,应用程序与客户端应用程序相关联,而不是用户。
    【解决方案2】:

    如果您使用密码凭据授予流程,您必须单独评论 oauth_access_tokens 表中的 application_id 引用,而不是 oauth_access_grants

    【讨论】:

      【解决方案3】:

      取消注释:

      add_foreign_key :oauth_access_grants, <model>, column: :resource_owner_id
      

      改变

      t.references :application,    null: false
      

      t.references :application,    null: true
      

      【讨论】:

        猜你喜欢
        • 2021-05-04
        • 2018-03-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-22
        • 2013-11-17
        • 2017-03-15
        相关资源
        最近更新 更多