<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Kaan Kahraman</title>
    <description>The latest articles on Forem by Kaan Kahraman (@kaankahraman).</description>
    <link>https://forem.com/kaankahraman</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1213817%2Fcea40960-7d65-46e4-a8b7-8a469d743746.png</url>
      <title>Forem: Kaan Kahraman</title>
      <link>https://forem.com/kaankahraman</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/kaankahraman"/>
    <language>en</language>
    <item>
      <title>Supercharging API Security: APISIX with OpenFGA</title>
      <dc:creator>Kaan Kahraman</dc:creator>
      <pubDate>Wed, 14 Aug 2024 21:04:42 +0000</pubDate>
      <link>https://forem.com/kaankahraman/supercharging-api-security-apisix-with-openfga-45da</link>
      <guid>https://forem.com/kaankahraman/supercharging-api-security-apisix-with-openfga-45da</guid>
      <description>&lt;p&gt;Hello, fellow developers! Today, we're going to dive deep into the world of API authorization by exploring a custom plugin I've been working on: the APISIX-OpenFGA Authz Plugin. This plugin integrates OpenFGA's powerful authorization capabilities with Apache APISIX, allowing for fine-grained access control at the API gateway level.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Need
&lt;/h2&gt;

&lt;p&gt;Before we jump into the technical details, let's briefly discuss why fine-grained authorization at the API gateway level is crucial:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Centralized access control&lt;/li&gt;
&lt;li&gt;Reduced duplication of authorization logic across microservices&lt;/li&gt;
&lt;li&gt;Easier management and updating of access policies&lt;/li&gt;
&lt;li&gt;Improved performance through caching and efficient checks&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Authorization Dilemma
&lt;/h3&gt;

&lt;p&gt;Let's face it: as our applications grow more complex, so do our authorization needs. We're no longer in the simple world of "admin" and "user" roles. Modern apps require sophisticated, fine-grained access control that can handle intricate scenarios without breaking a sweat.&lt;/p&gt;

&lt;h3&gt;
  
  
  OpenFGA to the Rescue
&lt;/h3&gt;

&lt;p&gt;This is where OpenFGA (Fine-Grained Authorization) comes into play. Based on &lt;a href="https://research.google/pubs/zanzibar-googles-consistent-global-authorization-system/" rel="noopener noreferrer"&gt;Google's Zanzibar paper&lt;/a&gt;, OpenFGA offers a powerful authorization model that can handle even the most complex access control scenarios.&lt;/p&gt;

&lt;p&gt;But how do we bridge the gap between APISIX and OpenFGA? That's precisely why I created the APISIX-OpenFGA Authz Plugin!&lt;/p&gt;

&lt;p&gt;Now, let's explore how this plugin achieves these goals.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plugin Architecture
&lt;/h2&gt;

&lt;p&gt;The APISIX-OpenFGA Authz Plugin is designed to be flexible and performant. Here's an overview of its key components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Resource Mapping&lt;/li&gt;
&lt;li&gt;Relation Mapping&lt;/li&gt;
&lt;li&gt;OpenFGA Integration&lt;/li&gt;
&lt;li&gt;Caching Mechanism&lt;/li&gt;
&lt;li&gt;Dynamic Configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's break down each of these components and see how they work together.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Resource Mapping
&lt;/h3&gt;

&lt;p&gt;The plugin needs to understand how to map incoming requests to OpenFGA resources. This is done through the resource_mappings configuration. Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;luaCopyresource_mappings = {
    {
        uri_pattern = "^/api/resource/([^/]+)$",
        resource_type = "resource",
        id_location = "last_part"
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;uri_pattern&lt;/code&gt; is a regular expression that matches the incoming request URI&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;resource_type&lt;/code&gt; specifies the type of resource in OpenFGA&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;id_location&lt;/code&gt; tells the plugin where to find the resource ID (in this case, the last part of the URI)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The plugin uses this configuration to extract the resource type and ID from the incoming request. For example, a request to &lt;code&gt;/api/resource/123&lt;/code&gt;would be mapped to a resource of type "resource" with ID "123".&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Relation Mapping
&lt;/h3&gt;

&lt;p&gt;Different HTTP methods often correspond to different permissions. The &lt;code&gt;relation_mappings&lt;/code&gt; configuration allows you to map HTTP methods to OpenFGA relations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;relation_mappings = {
    GET = "reader",
    POST = "writer",
    PUT = "writer",
    DELETE = "admin"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration maps GET requests to the "reader" relation, POST and PUT to "writer", and DELETE to "admin".&lt;/p&gt;

&lt;h3&gt;
  
  
  3. OpenFGA Integration
&lt;/h3&gt;

&lt;p&gt;The core of the plugin is its integration with OpenFGA. When a request comes in, the plugin constructs an OpenFGA check request based on the extracted resource information and the mapped relation. Here's a simplified version of how this works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;local res, err = client:request_uri(conf.openfga_url .. "/stores/" .. conf.store_id .. "/check", {
    method = "POST",
    headers = {
        ["Content-Type"] = "application/json",
    },
    body = json.encode({
        tuple_key = {
            user = "user:" .. user_id,
            relation = relation,
            object = resource_type .. ":" .. resource_id,
        },
        authorization_model_id = conf.authorization_model_id,
    }),
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sends a check request to OpenFGA and receives a response indicating whether the action is allowed or not.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Caching Mechanism
&lt;/h3&gt;

&lt;p&gt;To improve performance, the plugin implements a caching mechanism using APISIX's built-in LRU cache:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;local allowed, err = lrucache(cache_key, nil, function()
    -- OpenFGA check logic here
end)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This caches the authorization decision for a configurable period, reducing the number of requests to OpenFGA and improving response times.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Dynamic Configuration
&lt;/h3&gt;

&lt;p&gt;The plugin supports dynamic configuration updates without requiring an APISIX restart. This is achieved through a shared dictionary and an API endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function _M.api()
    return {
        {
            methods = { "POST" },
            uri = "/apisix/plugin/openfga_authz/config",
            handler = function(conf, ctx)
                local req_body = core.request.get_body()
                local config = json.decode(req_body)
                update_dynamic_config("relation_mappings", config.relation_mappings)
                return 200, { message = "Configuration updated successfully" }
            end
        }
    }
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows you to update certain configurations (like relation mappings) on the fly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Different URIs
&lt;/h2&gt;

&lt;p&gt;The plugin's flexibility in handling different URIs comes from its resource mapping configuration. Let's look at a few examples:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Simple resource:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    uri_pattern = "^/api/documents/([^/]+)$",
    resource_type = "document",
    id_location = "last_part"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would match URIs like &lt;code&gt;/api/documents/123&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Nested resources:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    uri_pattern = "^/api/folders/([^/]+)/documents/([^/]+)$",
    resource_type = "document",
    id_location = "last_part"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would match URIs like &lt;code&gt;/api/folders/abc/documents/123&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Query parameter:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    uri_pattern = "^/api/search$",
    resource_type = "search",
    id_location = "query_param",
    id_key = "q"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would match URIs like &lt;code&gt;/api/search?q=hello&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;By configuring multiple resource mappings, you can handle a wide variety of URI patterns in your API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Possibilities and Use Cases
&lt;/h2&gt;

&lt;p&gt;The flexibility of this plugin opens up many possibilities:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Microservices Architecture&lt;/strong&gt;: Implement consistent authorization across all your microservices without duplicating logic.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Multi-tenancy&lt;/strong&gt;: Easily manage access control in multi-tenant applications by incorporating tenant information into your OpenFGA model.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Dynamic Permissions&lt;/strong&gt;: Update access control policies on the fly without redeploying your application.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Audit Logging&lt;/strong&gt;: Since all authorization decisions go through the plugin, it's an ideal place to implement comprehensive audit logging.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;A/B Testing of Authorization Policies&lt;/strong&gt;: Use the dynamic configuration feature to test new authorization policies on a subset of requests.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Inspiration
&lt;/h3&gt;

&lt;p&gt;This plugin was heavily inspired by works of &lt;a href="https://github.com/embesozzi" rel="noopener noreferrer"&gt;Martini Besozzi&lt;/a&gt; if you would like to dive deeper into the world of authentication and authorization, check his amazing Medium Articles:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://embesozzi.medium.com/mastering-access-control-implementing-low-code-authorization-based-on-rebac-and-decoupling-pattern-f6f54f70115e" rel="noopener noreferrer"&gt;Mastering Access Control: Low-Code Authorization with ReBAC, Decoupling Patterns and Policy as Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://embesozzi.medium.com/keycloak-integration-with-openfga-based-on-zanzibar-for-fine-grained-authorization-at-scale-d3376de00f9a" rel="noopener noreferrer"&gt;Keycloak integration with OpenFGA (based on Zanzibar) for Fine-Grained Authorization at Scale (ReBAC)&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The APISIX-OpenFGA Authz Plugin provides a powerful way to implement fine-grained authorization in your API gateway. By understanding its components and configuration options, you can tailor it to fit a wide variety of authorization scenarios.&lt;/p&gt;

&lt;p&gt;Remember, effective authorization is about more than just implementing a plugin, it requires careful thought about your authorization model and how it maps to your business rules. OpenFGA provides a flexible foundation for expressing these rules, and this plugin brings that power to your API gateway.&lt;/p&gt;

&lt;p&gt;I hope this deep dive has given you a better understanding of how fine-grained authorization can be implemented at the API gateway level. Feel free to explore the &lt;a href="https://github.com/k-kahraman/apisix-openfga-authz-plugin" rel="noopener noreferrer"&gt;full source code&lt;/a&gt; and experiment with different configurations to see how it can fit into your architecture. Any feedback and suggestions are more than welcome!&lt;/p&gt;

&lt;p&gt;Happy coding, and here's to building more secure and flexible APIs!&lt;/p&gt;

</description>
      <category>microservices</category>
      <category>cloud</category>
      <category>api</category>
      <category>security</category>
    </item>
  </channel>
</rss>
