Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose jwt in the policies context #718

Merged
merged 5 commits into from
May 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

- `THREESCALE_PORTAL_ENDPOINT` and `THREESCALE_CONFIG_FILE` are not required anymore [PR #702](https://github.com/3scale/apicast/pull/702)
- The `scope` of the Rate Limit policy is `service` by default [PR #704](https://github.com/3scale/apicast/pull/704)
- Decoded JWTs are now exposed in the policies context by the APIcast policy [PR #718](https://github.com/3scale/apicast/pull/718)

## [3.2.0-rc2] - 2018-05-11

Expand Down
4 changes: 2 additions & 2 deletions gateway/src/apicast/oauth/oidc.lua
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ function _M:transform_credentials(credentials)
if ngx.config.debug then
ngx.log(ngx.DEBUG, 'JWT object: ', require('inspect')(jwt_obj))
end
return nil, nil, jwt_obj and jwt_obj.reason or err
return nil, nil, nil, jwt_obj and jwt_obj.reason or err
end

local payload = jwt_obj.payload
Expand All @@ -149,7 +149,7 @@ function _M:transform_credentials(credentials)
-- OAuth2 credentials for OIDC
-- @field app_id Client id
-- @table credentials_oauth
return { app_id = app_id }, ttl
return { app_id = app_id }, ttl, payload
end


Expand Down
4 changes: 3 additions & 1 deletion gateway/src/apicast/proxy.lua
Original file line number Diff line number Diff line change
Expand Up @@ -267,14 +267,16 @@ function _M:rewrite(service, context)
local ttl

if self.oauth then
credentials, ttl, err = self.oauth:transform_credentials(credentials)
local jwt_payload
credentials, ttl, jwt_payload, err = self.oauth:transform_credentials(credentials)

if err then
ngx.log(ngx.DEBUG, 'oauth failed with ', err)
return errors.authorization_failed(service)
end
ctx.credentials = credentials
ctx.ttl = ttl
context.jwt = jwt_payload
end

context.credentials = ctx.credentials
Expand Down
16 changes: 8 additions & 8 deletions spec/oauth/oidc_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ describe('OIDC', function()
},
})

local credentials, ttl, err = oidc:transform_credentials({ access_token = access_token })
local credentials, ttl, _, err = oidc:transform_credentials({ access_token = access_token })

assert(credentials, err)

Expand All @@ -53,7 +53,7 @@ describe('OIDC', function()

local stubbed
for _=1, 10 do
local credentials, _, err = oidc:transform_credentials({ access_token = access_token })
local credentials, _, _, err = oidc:transform_credentials({ access_token = access_token })
if not stubbed then
stubbed = stub(jwt, 'verify_jwt_obj', function(_, jwt_obj, _) return jwt_obj end)
end
Expand All @@ -75,7 +75,7 @@ describe('OIDC', function()
},
})

local credentials, _, err = oidc:transform_credentials({ access_token = access_token })
local credentials, _, _, err = oidc:transform_credentials({ access_token = access_token })

assert(credentials, err)
end)
Expand All @@ -93,7 +93,7 @@ describe('OIDC', function()
},
})

local credentials, _, err = oidc:transform_credentials({ access_token = access_token })
local credentials, _, _, err = oidc:transform_credentials({ access_token = access_token })

assert.falsy(credentials)
assert.truthy(err)
Expand All @@ -113,7 +113,7 @@ describe('OIDC', function()

jwt_validators.set_system_clock(function() return 0 end)

local credentials, _, err = oidc:transform_credentials({ access_token = access_token })
local credentials, _, _, err = oidc:transform_credentials({ access_token = access_token })
assert(credentials, err)

jwt_validators.set_system_clock(function() return 1 end)
Expand All @@ -130,7 +130,7 @@ describe('OIDC', function()
payload = { },
})

local credentials, _, err = oidc:transform_credentials({ access_token = access_token })
local credentials, _, _, err = oidc:transform_credentials({ access_token = access_token })

assert.match('invalid alg', err, nil, true)
assert.falsy(credentials, err)
Expand All @@ -150,7 +150,7 @@ describe('OIDC', function()
},
})

local credentials, _, err = oidc:transform_credentials({ access_token = access_token })
local credentials, _, _, err = oidc:transform_credentials({ access_token = access_token })
assert.same("Claim 'typ' ('Not-Bearer') returned failure", err)
assert.falsy(credentials, err)
end)
Expand All @@ -169,7 +169,7 @@ describe('OIDC', function()
},
})

local credentials, _, err = oidc:transform_credentials({ access_token = access_token })
local credentials, _, _, err = oidc:transform_credentials({ access_token = access_token })
assert(credentials, err)
end)
end)
Expand Down
87 changes: 87 additions & 0 deletions t/apicast-policy-headers.t
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use lib 't';
use Test::APIcast::Blackbox 'no_plan';

use Cwd qw(abs_path);

our $rsa = `cat t/fixtures/rsa.pem`;

run_tests();

__DATA__
Expand Down Expand Up @@ -518,3 +522,86 @@ yay, api backend
--- error_code: 200
--- no_error_log
[error]

=== TEST 9: templating with jwt token information
This tests that the headers policy can send headers with jwt information.
The APIcast policy stores the jwt in the policies context, so the headers
policy has access to it.
Notice that in the configuration, oidc.config.public_key is the one in
"/fixtures/rsa.pub".
--- backend
location /transactions/oauth_authrep.xml {
content_by_lua_block {
ngx.exit(200)
}
}
--- configuration
{
"oidc": [
{
"issuer": "https://example.com/auth/realms/apicast",
"config": {
"public_key": "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALClz96cDQ965ENYMfZzG+Acu25lpx2KNpAALBQ+catCA59us7+uLY5rjQR6SOgZpCz5PJiKNAdRPDJMXSmXqM0CAwEAAQ==",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mikz Ideas to avoid hardcoding it here?

Copy link
Contributor

@mikz mikz May 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can do eval filter and use Perl to read it and interpolate it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could possibly work:

 <<"JSON";
{ 
 "oidc": ...
   "public_key": "$::rsa"
}
JSON

"openid": { "id_token_signing_alg_values_supported": [ "RS256" ] }
}
}
],
"services": [
{
"id": 42,
"backend_version": "oauth",
"backend_authentication_type": "service_token",
"backend_authentication_value": "token-value",
"proxy": {
"authentication_method": "oidc",
"oidc_issuer_endpoint": "https://example.com/auth/realms/apicast",
"api_backend": "http://test:$TEST_NGINX_SERVER_PORT/",
"proxy_rules": [
{ "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 2 }
],
"policy_chain": [
{ "name": "apicast.policy.apicast" },
{
"name": "apicast.policy.headers",
"configuration":
{
"request":
[
{
"op": "set",
"header": "Token-aud",
"value": "{{ jwt.aud }}",
"value_type": "liquid"
}
]
}
}
]
}
}
]
}
--- upstream
location / {
content_by_lua_block {
local assert = require('luassert')
assert.same("the_token_audience", ngx.req.get_headers()['Token-aud'])
ngx.say('yay, api backend');
}
}
--- request
GET /
--- more_headers eval
use Crypt::JWT qw(encode_jwt);
my $jwt = encode_jwt(payload => {
aud => 'the_token_audience',
nbf => 0,
iss => 'https://example.com/auth/realms/apicast',
exp => time + 3600 }, key => \$::rsa, alg => 'RS256');
"Authorization: Bearer $jwt"
--- error_code: 200
--- response_body
yay, api backend
--- no_error_log
[error]
oauth failed with