From e42ee064ffbed9da95391debb03dd1195191dcf7 Mon Sep 17 00:00:00 2001 From: Thomas Dy Date: Tue, 5 Jun 2018 10:55:06 +0900 Subject: [PATCH] Implement custom use_refresh_token --- NEWS.md | 1 + lib/doorkeeper/config.rb | 15 +++++-- lib/doorkeeper/oauth/authorization/token.rb | 45 +++++++++++-------- lib/doorkeeper/oauth/base_request.rb | 5 ++- .../oauth/client_credentials/issuer.rb | 4 +- lib/doorkeeper/oauth/refresh_token_request.rb | 4 +- .../doorkeeper/templates/initializer.rb | 9 +++- spec/lib/config_spec.rb | 18 ++++++++ spec/lib/oauth/base_request_spec.rb | 24 ++++++++++ 9 files changed, 95 insertions(+), 30 deletions(-) diff --git a/NEWS.md b/NEWS.md index e0e18f7fd..b3e11985b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,6 +6,7 @@ upgrade guides. User-visible changes worth mentioning. ## master +- [#1103] Allow customizing use_refresh_token - [#1089] Removed enable_pkce_without_secret configuration option - [#1102] Expiration time based on scopes - [#1099] All the configuration variables in `Doorkeeper.configuration` now diff --git a/lib/doorkeeper/config.rb b/lib/doorkeeper/config.rb index 1fa6302dd..fc283bcf4 100644 --- a/lib/doorkeeper/config.rb +++ b/lib/doorkeeper/config.rb @@ -103,9 +103,12 @@ def access_token_methods(*methods) @config.instance_variable_set(:@access_token_methods, methods) end - # Issue access tokens with refresh token (disabled by default) - def use_refresh_token - @config.instance_variable_set(:@refresh_token_enabled, true) + # Issue access tokens with refresh token (disabled if not set) + def use_refresh_token(enabled = true, &block) + @config.instance_variable_set( + :@refresh_token_enabled, + block ? block : enabled + ) end # Reuse access token for the same resource owner within an application @@ -289,7 +292,11 @@ def enforce_content_type end def refresh_token_enabled? - !!(defined?(@refresh_token_enabled) && @refresh_token_enabled) + if defined?(@refresh_token_enabled) + @refresh_token_enabled + else + false + end end def enforce_configured_scopes? diff --git a/lib/doorkeeper/oauth/authorization/token.rb b/lib/doorkeeper/oauth/authorization/token.rb index 4c53cae07..675401277 100644 --- a/lib/doorkeeper/oauth/authorization/token.rb +++ b/lib/doorkeeper/oauth/authorization/token.rb @@ -5,30 +5,37 @@ class Token attr_accessor :pre_auth, :resource_owner, :token class << self - def access_token_expires_in(server, pre_auth_or_oauth_client, grant_type, scopes) - if (expiration = custom_expiration(server, pre_auth_or_oauth_client, grant_type, scopes)) - expiration - else - server.access_token_expires_in - end - end - - private - - def custom_expiration(server, pre_auth_or_oauth_client, grant_type, scopes) + def build_context(pre_auth_or_oauth_client, grant_type, scopes) oauth_client = if pre_auth_or_oauth_client.respond_to?(:client) pre_auth_or_oauth_client.client else pre_auth_or_oauth_client end - context = Doorkeeper::OAuth::Authorization::Context.new( + + Doorkeeper::OAuth::Authorization::Context.new( oauth_client, grant_type, scopes ) + end + + def access_token_expires_in(server, context) + if (expiration = server.custom_access_token_expires_in.call(context)) + expiration + else + server.access_token_expires_in + end + end - server.custom_access_token_expires_in.call(context) + def refresh_token_enabled?(server, context) + if server.refresh_token_enabled?.respond_to? :call + server.refresh_token_enabled?.call(context) + else + !!server.refresh_token_enabled? + end end + + private end def initialize(pre_auth, resource_owner) @@ -37,16 +44,16 @@ def initialize(pre_auth, resource_owner) end def issue_token + context = self.class.build_context( + pre_auth.client, + Doorkeeper::OAuth::IMPLICIT, + pre_auth.scopes + ) @token ||= AccessToken.find_or_create_for( pre_auth.client, resource_owner.id, pre_auth.scopes, - self.class.access_token_expires_in( - configuration, - pre_auth, - Doorkeeper::OAuth::IMPLICIT, - pre_auth.scopes - ), + self.class.access_token_expires_in(configuration, context), false ) end diff --git a/lib/doorkeeper/oauth/base_request.rb b/lib/doorkeeper/oauth/base_request.rb index 38fee52a0..636e42346 100644 --- a/lib/doorkeeper/oauth/base_request.rb +++ b/lib/doorkeeper/oauth/base_request.rb @@ -31,12 +31,13 @@ def valid? end def find_or_create_access_token(client, resource_owner_id, scopes, server) + context = Authorization::Token.build_context(client, grant_type, scopes) @access_token = AccessToken.find_or_create_for( client, resource_owner_id, scopes, - Authorization::Token.access_token_expires_in(server, client, grant_type, scopes), - server.refresh_token_enabled? + Authorization::Token.access_token_expires_in(server, context), + Authorization::Token.refresh_token_enabled?(server, context) ) end diff --git a/lib/doorkeeper/oauth/client_credentials/issuer.rb b/lib/doorkeeper/oauth/client_credentials/issuer.rb index 14d0d60e4..8d4bfd9fd 100644 --- a/lib/doorkeeper/oauth/client_credentials/issuer.rb +++ b/lib/doorkeeper/oauth/client_credentials/issuer.rb @@ -25,12 +25,12 @@ def create(client, scopes, creator = Creator.new) private def create_token(client, scopes, creator) - ttl = Authorization::Token.access_token_expires_in( - @server, + context = Authorization::Token.build_context( client, Doorkeeper::OAuth::CLIENT_CREDENTIALS, scopes ) + ttl = Authorization::Token.access_token_expires_in(@server, context) creator.call( client, diff --git a/lib/doorkeeper/oauth/refresh_token_request.rb b/lib/doorkeeper/oauth/refresh_token_request.rb index cb1458be7..774f327e2 100644 --- a/lib/doorkeeper/oauth/refresh_token_request.rb +++ b/lib/doorkeeper/oauth/refresh_token_request.rb @@ -65,12 +65,12 @@ def access_token_attributes end def access_token_expires_in - Authorization::Token.access_token_expires_in( - server, + context = Authorization::Token.build_context( client, Doorkeeper::OAuth::REFRESH_TOKEN, scopes ) + Authorization::Token.access_token_expires_in(server, context) end def validate_token_presence diff --git a/lib/generators/doorkeeper/templates/initializer.rb b/lib/generators/doorkeeper/templates/initializer.rb index 62c21132f..b6ba513aa 100644 --- a/lib/generators/doorkeeper/templates/initializer.rb +++ b/lib/generators/doorkeeper/templates/initializer.rb @@ -66,7 +66,14 @@ # # reuse_access_token - # Issue access tokens with refresh token (disabled by default) + # Issue access tokens with refresh token (disabled by default), you may also + # pass a block which accepts `context` to customize when to give a refresh + # token or not. Similar to `custom_access_token_expires_in`, `context` has + # the properties: + # + # `client` - the OAuth client application (see Doorkeeper::OAuth::Client) + # `grant_type` - the grant type of the request (see Doorkeeper::OAuth) + # `scopes` - the requested scopes (see Doorkeeper::OAuth::Scopes) # # use_refresh_token diff --git a/spec/lib/config_spec.rb b/spec/lib/config_spec.rb index cdb746126..095d80015 100644 --- a/spec/lib/config_spec.rb +++ b/spec/lib/config_spec.rb @@ -144,6 +144,24 @@ expect(subject.refresh_token_enabled?).to eq(true) end + it 'can accept a boolean parameter' do + Doorkeeper.configure do + orm DOORKEEPER_ORM + use_refresh_token false + end + + expect(subject.refresh_token_enabled?).to eq(false) + end + + it 'can accept a block parameter' do + Doorkeeper.configure do + orm DOORKEEPER_ORM + use_refresh_token { |_context| nil } + end + + expect(subject.refresh_token_enabled?).to be_a(Proc) + end + it "does not includes 'refresh_token' in authorization_response_types" do expect(subject.token_grant_types).not_to include 'refresh_token' end diff --git a/spec/lib/oauth/base_request_spec.rb b/spec/lib/oauth/base_request_spec.rb index f0f83ed76..d78aaa79b 100644 --- a/spec/lib/oauth/base_request_spec.rb +++ b/spec/lib/oauth/base_request_spec.rb @@ -119,6 +119,30 @@ module Doorkeeper::OAuth ) expect(result.expires_in).to eql(500) end + + it "respects use_refresh_token with a block" do + server = double(:server, + access_token_expires_in: 100, + custom_access_token_expires_in: ->(_context) { nil }, + refresh_token_enabled?: lambda { |context| + context.scopes == "public" + }) + result = subject.find_or_create_access_token( + client, + "1", + "public", + server + ) + expect(result.refresh_token).to_not be_nil + + result = subject.find_or_create_access_token( + client, + "1", + "private", + server + ) + expect(result.refresh_token).to be_nil + end end describe "#scopes" do