Home > Net >  Verify a jwt token with openresty lua-nginx-module every request
Verify a jwt token with openresty lua-nginx-module every request

Time:01-19

I made react website, that request a remote http API (managed by a third party). My website is using https with a valide certificate.

  • The third party API is "secured" by a simple GET param in the URL , "?key=XXXX" . to check if we can access the endpoint.
  • The third party API has CORS

To be able to request the http remote API I made a nginx reverse proxy . With extra header to allow CORS. And to secure a little bit more I added JWT Token verification .

All is working fine , if the Header Authorization Bearer with token is invalid or unset i cannot access the third party api. nginx is blocking me.

But as soon as I provide a valid Authorization Header once , any further request will PASS. the token is not checked anymore , even if i make the request from a different device with different IP.

How to check the token for every single request i make

Here is my nginx conf

server {
       listen 443 ssl http2;

        ssl_certificate /etc/letsencrypt/live/mydomain.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/mydomain.com/privkey.pem;


        location / {
            default_type application/json;
            return 200 '{"message": "Endpoint is required"}';
        }

        location  ^~ /api/ {
            include jwt.conf;
            include headers.conf;

           set $args $args&key={KEY};
           proxy_pass http://www.third-party.com/api/;
        }
    }

Here is my jwt.conf file , I am using the openresty lua-nginx-module

access_by_lua_block {
    local jwt = require "resty.jwt"
    local jwt_obj = jwt:verify("{SECRET}", token, claim_spec)
    local auth_header = ngx.var.http_Authorization

    if auth_header then
        _, _, token = string.find(auth_header, "Bearer%s (. )")
    end

    if token == nil then
        ngx.status = ngx.HTTP_UNAUTHORIZED
        ngx.header.content_type = "application/json; charset=utf-8"
        ngx.say("{\"error\": \"missing JWT token or Authorization header\"}")
        ngx.exit(ngx.HTTP_UNAUTHORIZED)
    end

     if not jwt_obj["verified"] then
         ngx.status = ngx.HTTP_UNAUTHORIZED
         ngx.log(ngx.WARN, jwt_obj.reason)
         ngx.header.content_type = "application/json; charset=utf-8"
         ngx.say("{\"error\": \"" .. jwt_obj.reason .. "\"}")
         ngx.exit(ngx.HTTP_UNAUTHORIZED)
     end
 }

And to be complete , my headers.conf

proxy_set_header                X-Real-IP $remote_addr;
proxy_set_header                X-Forwarded-For $proxy_add_x_forwarded_for;

proxy_redirect                  off;
proxy_buffers                   32 16k;
proxy_busy_buffers_size         64k;


if ($request_method = 'OPTIONS') {
    add_header 'Access-Control-Allow-Origin' '*' always;
    add_header 'Access-Control-Allow-Methods' 'GET, PUT, POST, OPTIONS' always;
    add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
    add_header 'Access-Control-Max-Age' 1728000;
    return 204;
}

# Ajouter les headers de contrôle d'accès CORS
add_header    'Access-Control-Allow-Origin' '*' always;
add_header    'Access-Control-Allow-Methods' 'GET, PUT, POST, OPTIONS' always;
add_header    'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept, Authorization' always;
add_header    'Access-Control-Allow-Credentials' 'true' always;

CodePudding user response:

Here:

_, _, token = string.find(auth_header, "Bearer%s (. )")

you declare token as a global variable. It is strongly discouraged:

Note that the use of global Lua variables is strongly discouraged, as it may lead to unexpected race conditions between concurrent requests.

It explains why subsequent requests are authorized if you do not include the Authorization header -- the token from the initial request is "cached" in the Lua global state.

Moreover, here:

local jwt_obj = jwt:verify("{SECRET}", token, claim_spec)

you reference the token variable before it is declared.

The correct code is something like this:

local jwt = require "resty.jwt"

local auth_header = ngx.var.http_Authorization

-- `local token` is equvalent to `local token = nil`
local token
if auth_header then
    token = string.match(auth_header, "Bearer%s (. )")
end

if token == nil then
    ngx.status = ngx.HTTP_UNAUTHORIZED
    ngx.header.content_type = "application/json; charset=utf-8"
    ngx.say("{\"error\": \"missing JWT token or Authorization header\"}")
    ngx.exit(ngx.HTTP_UNAUTHORIZED)
end

-- BTW, where do the `claim_spec` come from?
local jwt_obj = jwt:verify("{SECRET}", token, claim_spec)

if not jwt_obj["verified"] then
    ngx.status = ngx.HTTP_UNAUTHORIZED
    ngx.log(ngx.WARN, jwt_obj.reason)
    ngx.header.content_type = "application/json; charset=utf-8"
    ngx.say("{\"error\": \"" .. jwt_obj.reason .. "\"}")
    ngx.exit(ngx.HTTP_UNAUTHORIZED)
end

  • Related