apache / apisix

The Cloud-Native API Gateway
https://apisix.apache.org/blog/
Apache License 2.0
14.49k stars 2.52k forks source link

help request: how to authorize by client ID when authenticating with oAuth2 server #9073

Open amyrprv opened 1 year ago

amyrprv commented 1 year ago

Description

My clients will obtain a token from the oAuth2 server with client_id and client_secret. The client_id will be in the azp attribute of the token payload.

  1. I want my Routes to reject or permit accessing upstreams based on the client_id in the claim.
Screenshot 1401-12-24 at 14 26 09

Environment

An-DJ commented 1 year ago

Customing filter by filter_func may meet your requirement.

image

Ref:

amyrprv commented 1 year ago

Can I use it with consumers? so that I could use the consumers_restrictation plugin on the routes?

@An-DJ

amyrprv commented 1 year ago

I duplicated the key-auth plugin and modified it to read the azp attribute from the JWT payload. and then I used the consumer-restriction plugin on Apisix services to whitelist consumers. Is it a good approach for this solution?

here is my code:

local core         = require("apisix.core")
local jwt          = require("resty.jwt")
local consumer_mod = require("apisix.consumer")
local plugin_name  = "j-auth"
local sub_str      = string.sub

local schema = {
    type = "object",
    properties = {
        header = {
            type = "string",
            default = "apikey",
        },
        query = {
            type = "string",
            default = "apikey",
        },
        hide_credentials = {
            type = "boolean",
            default = false,
        }
    },
}

local consumer_schema = {
    type = "object",
    properties = {
        key = { type = "string" },
    },
    encrypt_fields = { "key" },
    required = { "key" },
}

local _M = {
    version = 0.1,
    priority = 2500,
    type = 'auth',
    name = plugin_name,
    schema = schema,
    consumer_schema = consumer_schema,
}

function _M.check_schema(conf, schema_type)
    if schema_type == core.schema.TYPE_CONSUMER then
        return core.schema.check(consumer_schema, conf)
    else
        return core.schema.check(schema, conf)
    end
end

local function fetch_jwt_token(conf, ctx)
    local token = core.request.header(ctx, conf.header)
    if token then
        if conf.hide_credentials then
            -- hide for header
            core.request.set_header(ctx, conf.header, nil)
        end

        local prefix = sub_str(token, 1, 7)
        if prefix == 'Bearer ' or prefix == 'bearer ' then
            return sub_str(token, 8)
        end

        return token
    end
end

function _M.rewrite(conf, ctx)
    local from_header = true

    -- Get token in authoraztion header
    local key = fetch_jwt_token(conf, ctx)

    if not key then
        local uri_args = core.request.get_uri_args(ctx) or {}
        key = uri_args[conf.query]
        from_header = false
    end

    if not key then
        return 401, { message = "Missing API key found in request" }
    end

    --Decode JWT
    local jwt_obj = jwt:load_jwt(key)

    core.log.warn("is Valid: ", jwt_obj.valid)

    --Check Token is valid
    if not jwt_obj.valid then
        core.log.warn("JWT token invalid: ", jwt_obj.reason)
        return 401, { message = "JWT token invalid" }
    end

    key = jwt_obj.payload and jwt_obj.payload.azp
    if not key then
        return 401, { message = "missing user key in JWT token" }
    end

    local consumer_conf = consumer_mod.plugin(plugin_name)
    if not consumer_conf then
        return 401, { message = "Missing related consumer" }
    end

    local consumers = consumer_mod.consumers_kv(plugin_name, consumer_conf, "key")
    local consumer = consumers[key]

    if not consumer then
        return 401, { message = "Invalid API key in request" }
    end
    core.log.info("consumer: ", core.json.delay_encode(consumer))

    if conf.hide_credentials then
        if from_header then
            core.request.set_header(ctx, conf.header, nil)
        else
            local args = core.request.get_uri_args(ctx)
            args[conf.query] = nil
            core.request.set_uri_args(ctx, args)
        end
    end

    consumer_mod.attach_consumer(ctx, consumer, consumer_conf)
    core.log.info("hit j-auth rewrite")
end

return _M
abehnamfard commented 1 year ago

Hello everyone. I have the same problem here. In my case, I have two microservice A and B. A issued a JWT token from my IAM service, so I have a valid token that is permitted to use APIs from microservice A. I want this token just can access microservice A APIs and be restricted to accessing microservice B APIs.