Skip to content
This repository has been archived by the owner on Feb 14, 2018. It is now read-only.

rfc6750 compability #49

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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 hosts/proxy/default/nginx/conf/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ http {
local jwt = require("nginx-jwt")
jwt.auth({
aud="^foo:",
scope="admin",
roles=function (val) return jwt.table_contains(val, "marketing") end
})
';
Expand Down
12 changes: 12 additions & 0 deletions nginx-jwt.lua
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ function M.auth(claim_specs)

if auth_header == nil then
ngx.log(ngx.WARN, "No Authorization header")
ngx.header["WWW-Authenticate"] = 'Bearer'
ngx.exit(ngx.HTTP_UNAUTHORIZED)
end

Expand All @@ -38,6 +39,7 @@ function M.auth(claim_specs)

if token == nil then
ngx.log(ngx.WARN, "Missing token")
ngx.header["WWW-Authenticate"] = 'Bearer'
ngx.exit(ngx.HTTP_UNAUTHORIZED)
end

Expand All @@ -47,6 +49,7 @@ function M.auth(claim_specs)
local jwt_obj = jwt:verify(secret, token, 0)
if jwt_obj.verified == false then
ngx.log(ngx.WARN, "Invalid token: ".. jwt_obj.reason)
ngx.header["WWW-Authenticate"] = 'Bearer error="invalid_token"'
ngx.exit(ngx.HTTP_UNAUTHORIZED)
end

Expand All @@ -63,6 +66,7 @@ function M.auth(claim_specs)

-- process each claim
local blocking_claim = ""
local blocking_spec = ""
for claim, spec in pairs(claim_specs) do
-- make sure token actually contains the claim
local claim_value = jwt_obj.payload[claim]
Expand Down Expand Up @@ -100,12 +104,20 @@ function M.auth(claim_specs)
-- make sure token claim value satisfies the claim spec
if not spec_action(spec, claim_value) then
blocking_claim = claim
blocking_spec = spec
break
end
end

if blocking_claim ~= "" then
ngx.log(ngx.WARN, "User did not satisfy claim: ".. blocking_claim)

-- https://tools.ietf.org/html/rfc6750#section-3.1
if blocking_claim == "scope" then
ngx.header["WWW-Authenticate"] = 'Bearer error="insufficient_scope",' .. blocking_claim .. '=' .. blocking_spec
ngx.exit(ngx.HTTP_FORBIDDEN)
end
ngx.header["WWW-Authenticate"] = 'Bearer'
ngx.exit(ngx.HTTP_UNAUTHORIZED)
end
end
Expand Down
27 changes: 23 additions & 4 deletions test/test_integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ describe('proxy', function () {
return request(url)
.get('/secure')
.expect(401)
.expect('WWW-Authenticate', 'Bearer')
.end();
});

Expand All @@ -86,6 +87,7 @@ describe('proxy', function () {
.get('/secure')
.headers({'Authorization': 'Bearer not-a-valid-jwt'})
.expect(401)
.expect('WWW-Authenticate', 'Bearer error="invalid_token"')
.end();
});

Expand All @@ -110,46 +112,63 @@ describe('proxy', function () {
it("should return 401 when an authenticated user is missing a required claim", function () {
var token = jwt.sign(
// roles claim missing
{ sub: 'foo-user', aud: 'foo1:bar' },
{ sub: 'foo-user', aud: 'foo1:bar', scope: 'admin' },
secret);

return request(url)
.get('/secure/admin')
.headers({'Authorization': 'Bearer ' + token})
.expect(401)
.expect('WWW-Authenticate', 'Bearer')
.end();
});

it("should return 401 when a claim of an authenticated user doesn't pass a 'pattern' claim spec", function () {
var token = jwt.sign(
// aud claim has incorrect value
{ sub: 'foo-user', aud: 'foo1:bar', roles: ["sales", "marketing"] },
{ sub: 'foo-user', aud: 'foo1:bar', roles: ["sales", "marketing"], scope: 'admin' },
secret);

return request(url)
.get('/secure/admin')
.headers({'Authorization': 'Bearer ' + token})
.expect(401)
.expect('WWW-Authenticate', 'Bearer')
.end();
});

it("should return 403 when a scope claim has incorrect value", function () {
var token = jwt.sign(
// scope claim has incorrect value
{ sub: 'foo-user', aud: 'foo:bar', roles: ["sales", "marketing"], scope: 'user' },
secret);

return request(url)
.get('/secure/admin')
.headers({'Authorization': 'Bearer ' + token})
.expect(403)
.expect('WWW-Authenticate', 'Bearer error="insufficient_scope",scope=admin')
.end();
});

it("should return 401 when a claim of an authenticated user doesn't pass a 'function' claim spec", function () {
var token = jwt.sign(
// roles claim is missing 'marketing' role
{ sub: 'foo-user', aud: 'foo:bar', roles: ["sales"] },
{ sub: 'foo-user', aud: 'foo:bar', roles: ["sales"], scope: 'admin' },
secret);

return request(url)
.get('/secure/admin')
.headers({'Authorization': 'Bearer ' + token})
.expect(401)
.expect('WWW-Authenticate', 'Bearer')
.end();
});

it("should return 200 when an authenticated user is also authorized by all claims", function () {
var token = jwt.sign(
// everything is good
{ sub: 'foo-user', aud: 'foo:bar', roles: ["sales", "marketing"] },
{ sub: 'foo-user', aud: 'foo:bar', roles: ["sales", "marketing"], scope: 'admin' },
secret);

return request(url)
Expand Down