Skip to content

Commit

Permalink
feat(oauth2) implement 'refresh_ttl' attribute for refresh_tokens
Browse files Browse the repository at this point in the history
A `refresh_token` TTL used to be hard-coded to 14 days.

This pose a problem with scenarios where the token/refresh_token
is only used sporadically. This change adds new config option
`refresh_token_ttl` that specifies a refresh token's TTL. If the value
is `nil` or 0, it means keep forever.

Fix #2024
From #2942
  • Loading branch information
bob983 authored and thibaultcha committed Nov 1, 2017
1 parent c9d6187 commit 8b86700
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 2 deletions.
9 changes: 7 additions & 2 deletions kong/plugins/oauth2/access.lua
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ local function generate_token(conf, api, credential, authenticated_userid, scope
refresh_token = utils.random_string()
end

local refresh_token_ttl
if conf.refresh_token_ttl and conf.refresh_token_ttl > 0 then
refresh_token_ttl = conf.refresh_token_ttl
end

local api_id
if not conf.global_credentials then
api_id = api.id
Expand All @@ -53,8 +58,8 @@ local function generate_token(conf, api, credential, authenticated_userid, scope
expires_in = token_expiration,
refresh_token = refresh_token,
scope = scope
}, {ttl = token_expiration > 0 and 1209600 or nil}) -- Access tokens (and their associated refresh token) are being
-- permanently deleted after 14 days (1209600 seconds)
}, {ttl = token_expiration > 0 and refresh_token_ttl or nil}) -- Access tokens (and their associated refresh token) are being
-- permanently deleted after 'refresh_token_ttl' seconds

if err then
return responses.send_HTTP_INTERNAL_SERVER_ERROR(err)
Expand Down
18 changes: 18 additions & 0 deletions kong/plugins/oauth2/migrations/cassandra.lua
Original file line number Diff line number Diff line change
Expand Up @@ -172,4 +172,22 @@ return {
end,
down = function(_, _, dao) end -- not implemented
},
{
name = "2017-10-11-oauth2_new_refresh_token_ttl_config_value",
up = function(_, _, dao)
for ok, config, update in plugin_config_iterator(dao, "oauth2") do
if not ok then
return config
end
if config.refresh_token_ttl == nil then
config.refresh_token_ttl = 1209600
local _, err = update(config)
if err then
return err
end
end
end
end,
down = function(_, _, dao) end -- not implemented
}
}
18 changes: 18 additions & 0 deletions kong/plugins/oauth2/migrations/postgres.lua
Original file line number Diff line number Diff line change
Expand Up @@ -185,4 +185,22 @@ return {
end,
down = function(_, _, dao) end -- not implemented
},
{
name = "2017-10-11-oauth2_new_refresh_token_ttl_config_value",
up = function(_, _, dao)
for ok, config, update in plugin_config_iterator(dao, "oauth2") do
if not ok then
return config
end
if config.refresh_token_ttl == nil then
config.refresh_token_ttl = 1209600
local _, err = update(config)
if err then
return err
end
end
end
end,
down = function(_, _, dao) end -- not implemented
}
}
1 change: 1 addition & 0 deletions kong/plugins/oauth2/schema.lua
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ return {
anonymous = {type = "string", default = "", func = check_user},
global_credentials = {type = "boolean", default = false},
auth_header_name = {required = false, type = "string", default = "authorization"},
refresh_token_ttl = {required = true, type = "number", default = 1209600} -- original hardcoded value - 14 days
},
self_check = function(schema, plugin_t, dao, is_update)
if not plugin_t.enable_authorization_code and not plugin_t.enable_implicit_grant
Expand Down
7 changes: 7 additions & 0 deletions spec/03-plugins/26-oauth2/01-schema_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ describe("Plugin: oauth2 (schema)", function()
assert.equal("hello", t.provision_key)
assert.equal("custom_header_name", t.auth_header_name)
end)
it("sets refresh_token_ttl to default value if not set", function()
local t = {enable_authorization_code = true, mandatory_scope = false}
local ok, errors = validate_entity(t, oauth2_schema)
assert.True(ok)
assert.is_nil(errors)
assert.equal(1209600, t.refresh_token_ttl)
end)
describe("errors", function()
it("requires at least one flow", function()
local ok, _, err = validate_entity({}, oauth2_schema)
Expand Down
111 changes: 111 additions & 0 deletions spec/03-plugins/26-oauth2/03-access_spec.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
local cjson = require "cjson"
local helpers = require "spec.helpers"
local utils = require "kong.tools.utils"
local fmt = string.format
local dao_helpers = require "spec.02-integration.03-dao.helpers"

local function provision_code(host, extra_headers, client_id)
local request_client = helpers.proxy_ssl_client()
Expand Down Expand Up @@ -2522,3 +2524,112 @@ describe("Plugin: oauth2 (access)", function()
end)

end)

dao_helpers.for_each_dao(function(kong_config)
describe("Plugin: oauth2 (ttl) with #"..kong_config.database, function()

local client

setup(function()

local api11 = assert(helpers.dao.apis:insert {
name = "api-11",
hosts = { "oauth2_11.com" },
upstream_url = "http://mockbin.com"
})

assert(helpers.dao.plugins:insert {
name = "oauth2",
api_id = api11.id,
config = {
enable_authorization_code = true,
mandatory_scope = false,
provision_key = "provision123",
anonymous = "",
global_credentials = false,
refresh_token_ttl = 2
}
})

local api12 = assert(helpers.dao.apis:insert {
name = "api-12",
hosts = { "oauth2_12.com" },
upstream_url = "http://mockbin.com"
})

assert(helpers.dao.plugins:insert {
name = "oauth2",
api_id = api12.id,
config = {
enable_authorization_code = true,
mandatory_scope = false,
provision_key = "provision123",
anonymous = "",
global_credentials = false,
refresh_token_ttl = 0
}
})

local consumer = assert(helpers.dao.consumers:insert {
username = "bob"
})
assert(helpers.dao.oauth2_credentials:insert {
client_id = "clientid123",
client_secret = "secret123",
redirect_uri = "http://google.com/kong",
name = "testapp",
consumer_id = consumer.id
})
assert(helpers.start_kong())
client = helpers.proxy_client()
end)

teardown(function()
if client then client:close() end
helpers.stop_kong()
end)

local function assert_ttls_records_for_token(uuid, count)
local DB = require "kong.dao.db.postgres"
local _db = DB.new(kong_config)
local query = fmt("SELECT COUNT(*) FROM ttls where table_name='oauth2_tokens' AND primary_uuid_value = '%s'", tostring(uuid))
local result, error = _db:query(query)
assert.falsy(error)
assert.truthy(result[1].count == count)
end

describe("refresh token", function()
it("is deleted after defined TTL", function()
local token = provision_token("oauth2_11.com")
local token_entity = helpers.dao.oauth2_tokens:find_all { access_token = token.access_token }
assert.equal(1, #token_entity)

if kong_config.database == "postgres" then
assert_ttls_records_for_token(token_entity[1].id, 1)
end

ngx.sleep(3)

token_entity = helpers.dao.oauth2_tokens:find_all { access_token = token.access_token }
assert.equal(0, #token_entity)
end)

it("is not deleted when when TTL is 0 == never", function()
local token = provision_token("oauth2_12.com")
local token_entity = helpers.dao.oauth2_tokens:find_all { access_token = token.access_token }
assert.equal(1, #token_entity)

if kong_config.database == "postgres" then
assert_ttls_records_for_token(token_entity[1].id, 0)
end

ngx.sleep(3)

token_entity = helpers.dao.oauth2_tokens:find_all { access_token = token.access_token }
assert.equal(1, #token_entity)
end)
end)

end)

end)

0 comments on commit 8b86700

Please sign in to comment.