<?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>DEV Community: Gabe Jackson</title>
    <description>The latest articles on DEV Community by Gabe Jackson (@gj).</description>
    <link>https://dev.to/gj</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%2F523225%2Fa267fd40-6e91-41e5-ad0a-b2b21e84460c.jpeg</url>
      <title>DEV Community: Gabe Jackson</title>
      <link>https://dev.to/gj</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gj"/>
    <language>en</language>
    <item>
      <title>Adding Authorization to a Serverless Node.js App</title>
      <dc:creator>Gabe Jackson</dc:creator>
      <pubDate>Mon, 30 Nov 2020 16:38:54 +0000</pubDate>
      <link>https://dev.to/gj/adding-authorization-to-a-serverless-node-js-app-55o8</link>
      <guid>https://dev.to/gj/adding-authorization-to-a-serverless-node-js-app-55o8</guid>
      <description>&lt;p&gt;The main benefit of developing a serverless application is that managing servers, balancing load, scaling up and down, and a hundred other things become &lt;em&gt;someone else's problem&lt;/em&gt; (🎉). However, securing your serverless application with authorization remains decidedly your problem.&lt;/p&gt;

&lt;p&gt;The cloud providers offer some helpful primitives like &lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools.html"&gt;authentication solutions&lt;/a&gt;, access control for &lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/role-based-access-control.html"&gt;their own cloud services&lt;/a&gt;, and hooks for you to &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html"&gt;write your own custom authorization code&lt;/a&gt;, but much of the heavy lifting is left up to you. In addition to writing tons (and &lt;em&gt;tons&lt;/em&gt; and &lt;strong&gt;&lt;em&gt;tons&lt;/em&gt;&lt;/strong&gt;) of JSON, you'll have to figure out the precise baton waves required to orchestrate your authorization service / function / sidecar (/ clowncar) to ensure that everything is wired up correctly (&lt;code&gt;"Resource": "*"&lt;/code&gt; ought to do it) and that it's &lt;em&gt;at least&lt;/em&gt; as available as the service it's protecting.&lt;/p&gt;

&lt;p&gt;Or you could skip all that and use &lt;strong&gt;oso&lt;/strong&gt;, the open source policy engine for authorization:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Kiss gigantic JSON documents goodbye and write authorization logic in &lt;a href="https://docs.osohq.com/using/polar-syntax.html"&gt;a declarative language&lt;/a&gt; that mirrors the way you would explain the logic to a coworker.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Stop worrying about availability and orchestration by adding &lt;a href="https://docs.osohq.com/using/libraries/index.html"&gt;the oso library&lt;/a&gt; as a dependency and deploying it with the rest of your application code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Render authorization decisions &lt;em&gt;quickly&lt;/em&gt; with no time-wasting network hops or secondary service latency to contend with.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this post, we'll show you how oso makes it simple to add extensible, fine-grained authorization to your serverless application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting out
&lt;/h2&gt;

&lt;p&gt;As our canvas, we're going to start with &lt;a href="https://github.com/osohq/serverless-todo-app"&gt;a simple todo app&lt;/a&gt;. The app is written for Lambda's Node.js runtime, so we'll be using &lt;a href="https://docs.osohq.com/using/libraries/node/index.html"&gt;oso's Node.js library&lt;/a&gt; to implement authorization. However, if you wanted to add authorization to code written for a different Lambda runtime, there are oso libraries for &lt;a href="https://docs.osohq.com/using/libraries/python/index.html"&gt;Python&lt;/a&gt;, &lt;a href="https://docs.osohq.com/using/libraries/ruby/index.html"&gt;Ruby&lt;/a&gt;, &lt;a href="https://docs.osohq.com/using/libraries/java/index.html"&gt;Java&lt;/a&gt;, and &lt;a href="https://docs.osohq.com/using/libraries/rust/index.html"&gt;Rust&lt;/a&gt;, with more coming soon.&lt;/p&gt;

&lt;p&gt;The todo app consists of five Lambda functions (fronted by API Gateway) covering the basic CRUD operations on top of a single DynamoDB table. To track ownership, each todo has a &lt;code&gt;creator&lt;/code&gt; field that contains a &lt;code&gt;User&lt;/code&gt; populated with a few fields from the Lambda event payload: &lt;code&gt;country&lt;/code&gt;, &lt;code&gt;sourceIp&lt;/code&gt;, and &lt;code&gt;userAgent&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now that we have the lay of the land, let's fire up our serverless app (sans authorization).&lt;/p&gt;

&lt;h2&gt;
  
  
  No Authorization — No Code's evil twin
&lt;/h2&gt;

&lt;p&gt;If you don't want to get your hands dirty, the app is running (&lt;strong&gt;with authorization in place&lt;/strong&gt;) at &lt;code&gt;serverless-todo-app.oso.dev&lt;/code&gt;. You may substitute that address in every time you see &lt;code&gt;&amp;lt;SERVICE_ENDPOINT&amp;gt;&lt;/code&gt; for the remainder of the post.&lt;/p&gt;

&lt;p&gt;If you're following along at home, you'll need a few things to get started:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Clone &lt;a href="https://github.com/osohq/serverless-todo-app"&gt;the repo&lt;/a&gt; and &lt;code&gt;cd&lt;/code&gt; into it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install dependencies with &lt;code&gt;npm install&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/serverless/serverless/blob/d7b6fb748eba364aabeae3862f89b13262751c06/docs/providers/aws/guide/credentials.md"&gt;Set up AWS credentials for Serverless&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you're all set up, &lt;code&gt;npm run serverless -- deploy&lt;/code&gt; is the magic incantation to coax some distant computers into action. After liftoff is achieved, you can use cURL to interact with your extremely scalable todo app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl https://&amp;lt;SERVICE_ENDPOINT&amp;gt;/todos
&lt;span class="go"&gt;[]
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl https://&amp;lt;SERVICE_ENDPOINT&amp;gt;/todos &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"text":"my first todo!"}'&lt;/span&gt;
&lt;span class="go"&gt;{"id":"0cf6cec0-247f-11eb-b64e-4df956b5b3e4","creator":{"country":"US","sourceIp":"1.2.3.4","userAgent":"curl/7.64.1"},"text":"my first todo!","checked":false,"createdAt":1605141365298,"updatedAt":1605141365298}
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-XPUT&lt;/span&gt; https://&amp;lt;SERVICE_ENDPOINT&amp;gt;/todos/0cf6cec0-247f-11eb-b64e-4df956b5b3e4 &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"text":"my first updated todo!"}'&lt;/span&gt;
&lt;span class="go"&gt;{"checked":false,"createdAt":1605141365298,"text":"my first updated todo!","creator":{"sourceIp":"1.2.3.4","country":"US","userAgent":"curl/7.64.1"},"id":"0cf6cec0-247f-11eb-b64e-4df956b5b3e4","updatedAt":1605141518919}
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-XDELETE&lt;/span&gt; https://&amp;lt;SERVICE_ENDPOINT&amp;gt;/todos/0cf6cec0-247f-11eb-b64e-4df956b5b3e4
&lt;span class="go"&gt;{"checked":false,"createdAt":1605141365298,"text":"my first updated todo!","creator":{"sourceIp":"1.2.3.4","country":"US","userAgent":"curl/7.64.1"},"id":"0cf6cec0-247f-11eb-b64e-4df956b5b3e4","updatedAt":1605141518919}
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl https://&amp;lt;SERVICE_ENDPOINT&amp;gt;/todos/0cf6cec0-247f-11eb-b64e-4df956b5b3e4
&lt;span class="go"&gt;Not Found
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that if you're hitting our hosted copy at &lt;code&gt;serverless-todo-app.oso.dev&lt;/code&gt;, requests to the list endpoint (&lt;code&gt;GET /todos&lt;/code&gt;) will return a bunch of existing todos instead of an empty list.&lt;/p&gt;

&lt;p&gt;Our &lt;del&gt;battlestation&lt;/del&gt; serverless todo app is now fully armed and operational, but extremely vulnerable to &lt;del&gt;rebel attacks&lt;/del&gt; unauthorized shenanigans. Let's add some security!&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding authorization with oso
&lt;/h2&gt;

&lt;p&gt;First, add oso to our project: &lt;code&gt;npm install oso&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, create an empty Polar file in the project root: &lt;code&gt;touch policy.polar&lt;/code&gt;.&lt;br&gt;
Polar is the declarative logic language used to write oso authorization rules.&lt;/p&gt;

&lt;p&gt;The machinery of initializing oso and asking it to make an authorization decision is identical across all five Lambdas, so we can wrap it in a function in &lt;code&gt;src/helpers.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Oso&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;oso&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;may&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;oso&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Oso&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;oso&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;registerClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;oso&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;registerClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;oso&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;policy.polar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;oso&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isAllowed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We initialize oso, register the built-in &lt;code&gt;Date&lt;/code&gt; object and our &lt;code&gt;User&lt;/code&gt; class (both of which we're going to use in our policy), load our Polar file, and then ask oso if the loaded policy permits &lt;code&gt;user&lt;/code&gt; to perform &lt;code&gt;action&lt;/code&gt; on &lt;code&gt;resource&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In each Lambda, we'll call our helper and return a 403 Forbidden if the user is not authorized to perform the action on the resource in question:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/src/todos/update.js b/src/todos/update.js
index 86fff46..a5222a3 100644
&lt;/span&gt;&lt;span class="gd"&gt;--- a/src/todos/update.js
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/src/todos/update.js
&lt;/span&gt;&lt;span class="p"&gt;@@ -5,9 +5,10 @@&lt;/span&gt; const { getTodo, updateTodo } = require('../db');
&lt;span class="gd"&gt;-const { error, success } = require('../helpers');
&lt;/span&gt;&lt;span class="gi"&gt;+const { error, may, success } = require('../helpers');
&lt;/span&gt;
 module.exports.update = async (event, _context, cb) =&amp;gt; {
   try {
&lt;span class="gd"&gt;-    const _user = User.fromEvent(event);
&lt;/span&gt;&lt;span class="gi"&gt;+    const user = User.fromEvent(event);
&lt;/span&gt;     const { id } = event.pathParameters;
     const todo = await getTodo(id);

-    // TODO: authorize access.
&lt;span class="gi"&gt;+    const authorized = await may(user, 'update', todo);
+    if (!authorized) return error(cb, { statusCode: 403 });
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or if we just want to authorize an action generally (as opposed to authorizing an action on specific resources):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/todos/list.js&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authorized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;may&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;authorized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we've added those two lines to all of our Lambdas, we're now enforcing authorization!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/osohq/serverless-todo-app/compare/authorized~"&gt;Click here&lt;/a&gt; to see a full diff of adding oso to the project.&lt;/p&gt;

&lt;p&gt;If you redeploy the app at this point (&lt;code&gt;npm run serverless -- deploy&lt;/code&gt;), every request will 403 because &lt;strong&gt;oso is deny-by-default&lt;/strong&gt;. We haven't added any rules to our policy file yet, so in oso's view of the world no one is authorized to do anything.&lt;/p&gt;

&lt;p&gt;This is obviously a bit &lt;em&gt;too&lt;/em&gt; secure, so let's sketch out our authorization requirements and write some Polar code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing declarative authorization logic
&lt;/h2&gt;

&lt;p&gt;Because of its critical role in application security, authorization logic has a higher bar for readability and auditability than regular old business logic. Polar was designed with readability as a first-class feature.&lt;/p&gt;

&lt;p&gt;We're going to create five authorization rules, one for each Lambda. First, we'll write the rule in prose, and then we'll show &lt;a href="https://github.com/osohq/serverless-todo-app/blob/authorized/policy.polar"&gt;the corresponding Polar code&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Any user is allowed to list todos:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"list"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Any user is allowed to create a new todo:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"create"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;A user is allowed to view a specific todo if they are in the same country as the todo's creator:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"view"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;creator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;A user is allowed to update a todo if their IP address and user agent match those of the todo's creator:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sourceIp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;creator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sourceIp&lt;/span&gt;
    &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;userAgent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;creator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;A user is allowed to delete a todo if they're allowed to update it &lt;strong&gt;and&lt;/strong&gt; the todo was created within the last 5 minutes:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"delete"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;
    &lt;span class="n"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These rules show off a few of oso's strengths. Implementing fine-grained &lt;a href="https://docs.osohq.com/using/examples/abac.html"&gt;attribute-based access control&lt;/a&gt; (ABAC) is simple when we can write rules directly over application data (in this case, our &lt;code&gt;User&lt;/code&gt; class and the structured todo data). The rules are also composable and flexible — instead of duplicating logic in the delete rule, we simply asked if the user was allowed to update the todo and then extended it with an additional time-based check. And, finally, we did some math to determine if five minutes has elapsed since the todo's creation. We could have written a function to calculate the same thing in our Lambda code, but it's a calculation that's only relevant in an authorization context. By writing it here, we maintain &lt;a href="https://docs.osohq.com/more/design-principles.html#separation-of-concerns-but-not-data"&gt;the separation of concerns between our authorization and business logic&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once we've added those rules to our policy file, we can redeploy and interact with our newly secured app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;--user-agent&lt;/span&gt; &lt;span class="s2"&gt;"007"&lt;/span&gt; https://&amp;lt;SERVICE_ENDPOINT&amp;gt;/todos &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"text":"Do something nice for Moneypenny"}'&lt;/span&gt;
&lt;span class="go"&gt;{"id":"9d8b9b02-3175-4211-a8fb-8645d1f70a11","creator":{"country":"US","sourceIp":"67.244.40.223","userAgent":"007"},"text":"Do something nice for Moneypenny","checked":false,"createdAt":1605211750276,"updatedAt":1605211750276}
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;--user-agent&lt;/span&gt; &lt;span class="s2"&gt;"Goldfinger"&lt;/span&gt; &lt;span class="nt"&gt;-XPUT&lt;/span&gt; https://&amp;lt;SERVICE_ENDPOINT&amp;gt;/todos/9d8b9b02-3175-4211-a8fb-8645d1f70a11 &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"text":"Retire, you putz!"}'&lt;/span&gt;
&lt;span class="go"&gt;Can't do that, boss
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;We used oso to quickly add fine-grained authorization to our serverless app. We leveraged the app's existing data model to express our authorization logic in a few concise stanzas instead of commingling it with business logic in a tangle of nested &lt;code&gt;if&lt;/code&gt; / &lt;code&gt;else&lt;/code&gt; statements. And we did it all with &lt;a href="https://github.com/osohq/serverless-todo-app/compare/authorized"&gt;a minimal application footprint&lt;/a&gt; and no external service dependencies.&lt;/p&gt;

&lt;p&gt;A fun extension would be hooking up Cognito for richer user data than the Lambda event object provides, but we'll leave that as an exercise for you, dear reader. Until next time!&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>node</category>
      <category>security</category>
      <category>aws</category>
    </item>
  </channel>
</rss>
