{"action":"created","comment":{"url":"https://api.github.com/repos/auth0/blog/pulls/comments/126214852","pull_request_review_id":48683517,"id":126214852,"diff_hunk":"@@ -0,0 +1,483 @@\n+---\n+layout: post_extend\n+is_extend: true\n+title: \"Securing Webtasks\"\n+description: \"Enable Authorization Using a Shared Secret\"\n+date: 2017-07-06 10:00\n+category: Serverless\n+is_non-tech: false\n+author:\n+  name: \"Randall Tombaugh\"\n+  mail: \"randall.tombaugh@auth0.com\"\n+  avatar: \"https://cdn.auth0.com/website/blog/profiles/randalltombaugh.png\"\n+design:\n+  image: \"https://cdn.auth0.com/website/blog/extend/securing_webtasks/lock_graphic1.png\"\n+  bg_color: \"#3445dc\"\n+  image_bg_color: \"#3445dc\"\n+  bg_merge: trues\n+  image_size: 80%\n+tags:\n+- serverless\n+- extensibility\n+- webtask\n+- github\n+- twilio\n+- authorization\n+press_release: false\n+---\n+# Securing Webtasks\n+\n+In this post I am going to show you how to secure your webtasks so that your code is only invoked by the callers you have authorized.\n+\n+If you aren't familiar with webtasks, it's a technology we developed here at [Auth0](https://auth0.com) for safely running code behind an HTTP endpoint without having to provision and maintain a server. As a technology, webtasks are used to support the rules feature of Auth0's flagship identity product. And just recently, we launched a new product, [Auth0 Extend](https://authom.com/extend) that heavily leverages the webtask technology to enable SaaS providers to give their customers a far superior extensibility experience than with traditional webhooks.\n+\n+If you do happen to already know about webtasks, it may be because you've previously come across [Webtask.io](https://webtask.io/), a free offering that we created to allow anyone to tinker with the webtask technology. Tinkering as we are for the purpose of this post, we'll be using Webtask.io, because it makes it easy to get hosted code up and running behind an HTTP endpoint in just a matter of minutes. \n+\n+## Creating a Simple Webtask\n+\n+Before you can secure something, you need to have something to secure. So let's start by creating a simple webtask.\n+\n+We'll be using the `wt` cli tool and a simple text editor to work with our webtasks. The `wt` cli is an open-source Node.js command line interface that allows us to create and manage webtasks with just a few simple commands.\n+\n+There is also a fully-featured [Webtask.io Online Editor](https://webtask.io/make) that we could use. Without a doubt the online editor is great for authoring and managing webtasks. If you haven't already, I highly encourage you to try it out. I think you'll find it easy and intuitive to use. There's nothing we'll be doing in this post with the `wt` cli tool that you couldn't do from within the online editor, but for the sake of clarity in this post, typing out a handful of commands is preferable.\n+\n+So let's install the `wt` cli tool by entering the following at a command prompt:\n+\n+```bash\n+npm install -g\n+```\n+\n+If you've never used the `wt` cli before, you will need to initialize it. Initializing the tool will create a [Webtask.io](https://webtask.io/) account for you to us. You will need to enter an email or phone number so that you can receive an activation code:\n+\n+```bash\n+wt init <your-email@some-org.com>\n+```\n+\n+With the `wt` cli tool now setup, open your favorite code editor and create a javascript file in your current working directory named `not-secure.js` with the following code:\n+\n+```javascript\n+module.exports = function(context, callback) {\n+  callback(null, { \n+    question: 'Can anyone on the internet invoke me?',\n+    answer: 'Yes. Yes, they can.' \n+  });\n+}\n+```\n+\n+Getting this code up and running behind an HTTP endpoint is now as simple as:\n+\n+```bash\n+wt create ./not-secure.js\n+```\n+\n+The `wt create` command will complete and provide you with the url of your new webtask:\n+\n+```bash\n+Webtask created\n+\n+You can access your webtask at the following url:\n+\n+https://<your-webtask.io-account-domain>.run.webtask.io/not-secure\n+```\n+\n+To invoke your new webtask, it's as simple as:\n+\n+```bash\n+curl https://<your-webtask.io-account-domain>.run.webtask.io/not-secure\n+```\n+\n+Invoking the webtask with `curl` should return you the json payload:\n+\n+```bash\n+{\"question\":\"Can anyone on the internet invoke me?\",\"answer\":\"Yes. Yes, they can.\"}\n+```\n+\n+And just like that we have our code up and running behind an HTTP endpoint. But as the question and answer in our json payload makes clear, our code is now accessible on the open internet and our code can be invoked by anyone. Of course, maybe this is perfectly fine. In many scenarios, this is exactly what we'd want. For example, if you were using webtasks to provide just a bit of backend server functionality for a website that was mostly static--maybe the 'Contact Me' functionality of a blog for example--you'd want anyone to be able to invoke your code. \n+\n+However, there are also scenarios in which you wouldn't want this at all. Instead you'd want to ensure only certain services or callers were able to invoke your code. To this end, we'll consider one mechanism for securing our webtask in the next section: by using a shared secret.\n+\n+## Using a Shared Secret for Authorization\n+\n+As more and more software has moved online as a service, webhooks have grown in popularity as a way for users to respond to events from within the platform. From Slack to Github to Zendesk, webhooks are a powerful mechanism for invoking custom code and webtasks are a great fit for such a scenario. In fact, later on in this post we'll create a webtask that uses Twilio to send out an SMS message whenever a change is committed to a given Github repo.\n+\n+However, if you create a webtask that sends out an SMS message when triggered by Github, you wouldn't want anyone but Github to be able to invoke your webtask. In scenarios like this, the proper way to secure your webtask is with a shared secret, that is, a secret that is known by both you and the caller, but no one else.\n+\n+To illustrate this, let's create a new webtask that only returns an HTTP 200 response if the caller sends along the secret. In your code editor, create a new javascript file named `secure.js` with the following code:\n+\n+```javascript\n+module.exports = function(context, request, response) {\n+  const query = require('url').parse(request.url, true).query\n+  if (!query || query.secret != 'open-sesame') {\n+    response.writeHead(403);\n+    response.end();\n+  } else {\n+    response.writeHead(200, { 'Content-Type': 'application/json' });\n+    response.end(JSON.stringify({ question: 'Am I secure?', answer: 'Yes!' }))\n+  }\n+}\n+```\n+\n+Now you might notice that our `secure.js` code exports a function with a different signature than the `not-secure.js` code. That's because webtasks natively support two different programming models. \n+\n+Our `not-secure.js` code used the more convenient programming model in which our code was invoked with a context object and a callback function. Simply by returning our own object via the callback, we were able to send back an HTTP 200 response with the object serialized as json. This programming model is simple and intuitive to use.\n+\n+But sometimes we need access to more of the functionality of HTTP. That's exactly what this second native programming model of webtasks allows us to do. It provides us the HTTP server request and response objects used within Node.js. Because we want to set the HTTP status code to 403 and not include a body when the request is unauthorized, we're now using this second programming model in our `secure.js` code. \n+\n+In actuality, webtasks can also be configured to support custom programming models. This is done using the wt-compiler feature, which we'll take a look at later in this post.\n+\n+But before we get ahead of ourselves, let's get back to creating our new secure webtask:\n+\n+```bash\n+ wt create ./secure.js\n+```\n+\n+Now first we'll try to invoke it without including the secret in the query-string. Because this request won't be authorized, there won't be a response body, so we'll need to use the `-i` option with the `curl` command to see the HTTP response status code and headers:\n+\n+```bash\n+curl -i https://<your-webtask.io-account-domain>.run.webtask.io/secure\n+```\n+\n+The response shows that we're not authorized to invoke the webtask:\n+\n+```bash\n+HTTP/1.1 403 Forbidden\n+...\n+```\n+\n+However, if we invoke the webtask with the secret provided in the query-string:\n+\n+```bash\n+curl https://<your-webtask.io-account-domain>.run.webtask.io/secure?secret=open-sesame\n+```\n+\n+We now get the response we wanted:\n+\n+```bash\n+{\"question\":\"Am I secure?\",\"answer\":\"Yes!\"}\n+```\n+\n+As we've just shown, only callers that have access to our secret can now invoke our new secure webtask. As a first pass at securing our webtask, this isn't a bad start. Since all webtasks are hosted behind an HTTPS endpoint, the SSL/TSL connection ensures that the query-string is encrypted and so sending our secret over the wire isn't the worse thing in the world. However, we can still do better.\n+\n+First of all, our secret is hardcoded in our `secure.js` code. That means we have to make sure we keep our code just as secure as the webtask itself. We certainly can't make the code public on Github, or even share it with other developers we may or may not trust. Keeping secrets in our code is just an awful idea and it soon becomes untenable with even the simplest of projects.\n+\n+So in the next section we'll look at an important feature of webtasks that allows us to use secrets without keeping them in the code. Then in the section after that we'll look at how we can avoid sending our secret over the wire with each HTTP call.\n+\n+## Keeping Secrets out of Webtask Code\n+\n+It's quite common to write code that contains secrets, whether these are credentials for connecting to a database, or API keys for integrating with a third-party service. Webtasks have a built-in capability for keeping these secrets out of the webtask code but available to the code during execution. Secrets can be specified at the time the webtask is created, such that they are stored in an encrypted state separate from the code. Only during execution are the secrets decrypted and provided to the code via the context object.\n+\n+We'll use this secrets feature of webtasks to store the shared secret we had previously hard coded into the `secure.js` code. Let's update the code to retrieve the shared secrets from the context object. We need to identify the secret via a key name, so we'll use the key `auth-secret` but any key name would suffice:\n+\n+```javascript\n+module.exports = function(context, request, response) {\n+  const query = require('url').parse(request.url, true).query\n+  // Get our shared secret from the context\n+  const sharedSecret = context.secrets['auth-secret'];\n+  if (!query || query.secret != sharedSecret) {\n+    response.writeHead(403);\n+    response.end();\n+  } else {\n+    response.writeHead(200, { 'Content-Type': 'application/json' });\n+    response.end(JSON.stringify({ question: 'Am I secure?', answer: 'Yes!' }))\n+  }\n+}\n+```\n+\n+Now we can recreate the webtask and specify the shared secret on the command line with the `--secret <key>=<value>` option. We just need to make sure we use the same key `auth-secret` that we used within our webtask:\n+\n+```\n+wt create ./secure.js --secret auth-secret=open-sesame\n+```\n+\n+If we send send `curl` commands again with and without the secret in the query string we'll see the same behavior as before. However, now we aren't hard coding the secret in our webtask code so we don't have to worry about keeping out webtask code itself secret from other developers.\n+\n+In the next section, let's make another improvement to our shared secret mechanism such that the secret is also never sent over the wire. We'll do this by forcing the caller to generate a digest using our shared secret. As an added bonus, this will also ensure that the message sent to our webtask hasn't been altered.\n+\n+## Using A Shared Secret to Generate a Digest\n+\n+As mentioned earlier, all webtasks are hosted behind an HTTPS endpoint and the SSL/TSL connection ensures that the HTTP request is encrypted. Still, it's better not to send our shared secret across the wire with each request. Instead, we can require that the caller only prove it has the secret without actually sending it. We'll do this by having the caller generate a digest of the request payload using the shared secret. Within the webtask code, we'll also generate our own digest to compare with the one sent by the caller. If the two digests match, then we can be sure the caller knows the shared secret. Later in this post when we create our webtask to send an SMS message with each commit to a given repo, we'll see that Github uses this exact mechanism of generating a digest.","path":"_posts/2017-07-06-Securing-Webtasks.markdown","position":198,"original_position":198,"commit_id":"35754adce660194bcd0c5233cb71b2fee28a3de1","original_commit_id":"35754adce660194bcd0c5233cb71b2fee28a3de1","user":{"login":"tjanczuk","id":822369,"avatar_url":"https://avatars2.githubusercontent.com/u/822369?v=3","gravatar_id":"","url":"https://api.github.com/users/tjanczuk","html_url":"https://github.com/tjanczuk","followers_url":"https://api.github.com/users/tjanczuk/followers","following_url":"https://api.github.com/users/tjanczuk/following{/other_user}","gists_url":"https://api.github.com/users/tjanczuk/gists{/gist_id}","starred_url":"https://api.github.com/users/tjanczuk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tjanczuk/subscriptions","organizations_url":"https://api.github.com/users/tjanczuk/orgs","repos_url":"https://api.github.com/users/tjanczuk/repos","events_url":"https://api.github.com/users/tjanczuk/events{/privacy}","received_events_url":"https://api.github.com/users/tjanczuk/received_events","type":"User","site_admin":false},"body":"It not only proves the knowledge of the secret but also ensures integrity of the payload. ","created_at":"2017-07-07T18:27:27Z","updated_at":"2017-07-07T18:27:27Z","html_url":"https://github.com/auth0/blog/pull/916#discussion_r126214852","pull_request_url":"https://api.github.com/repos/auth0/blog/pulls/916","_links":{"self":{"href":"https://api.github.com/repos/auth0/blog/pulls/comments/126214852"},"html":{"href":"https://github.com/auth0/blog/pull/916#discussion_r126214852"},"pull_request":{"href":"https://api.github.com/repos/auth0/blog/pulls/916"}}},"pull_request":{"url":"https://api.github.com/repos/auth0/blog/pulls/916","id":129047310,"html_url":"https://github.com/auth0/blog/pull/916","diff_url":"https://github.com/auth0/blog/pull/916.diff","patch_url":"https://github.com/auth0/blog/pull/916.patch","issue_url":"https://api.github.com/repos/auth0/blog/issues/916","number":916,"state":"open","locked":false,"title":"New Extend blog post: Securing Webtasks","user":{"login":"rwtombaugh","id":1031969,"avatar_url":"https://avatars0.githubusercontent.com/u/1031969?v=3","gravatar_id":"","url":"https://api.github.com/users/rwtombaugh","html_url":"https://github.com/rwtombaugh","followers_url":"https://api.github.com/users/rwtombaugh/followers","following_url":"https://api.github.com/users/rwtombaugh/following{/other_user}","gists_url":"https://api.github.com/users/rwtombaugh/gists{/gist_id}","starred_url":"https://api.github.com/users/rwtombaugh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rwtombaugh/subscriptions","organizations_url":"https://api.github.com/users/rwtombaugh/orgs","repos_url":"https://api.github.com/users/rwtombaugh/repos","events_url":"https://api.github.com/users/rwtombaugh/events{/privacy}","received_events_url":"https://api.github.com/users/rwtombaugh/received_events","type":"User","site_admin":false},"body":"","created_at":"2017-07-05T20:24:34Z","updated_at":"2017-07-07T18:27:27Z","closed_at":null,"merged_at":null,"merge_commit_sha":"d38b5b5925cef952e3327d1d3f5cc600ad5b2ca8","assignee":null,"assignees":[],"requested_reviewers":[],"milestone":null,"commits_url":"https://api.github.com/repos/auth0/blog/pulls/916/commits","review_comments_url":"https://api.github.com/repos/auth0/blog/pulls/916/comments","review_comment_url":"https://api.github.com/repos/auth0/blog/pulls/comments{/number}","comments_url":"https://api.github.com/repos/auth0/blog/issues/916/comments","statuses_url":"https://api.github.com/repos/auth0/blog/statuses/35754adce660194bcd0c5233cb71b2fee28a3de1","head":{"label":"rwtombaugh:master","ref":"master","sha":"35754adce660194bcd0c5233cb71b2fee28a3de1","user":{"login":"rwtombaugh","id":1031969,"avatar_url":"https://avatars0.githubusercontent.com/u/1031969?v=3","gravatar_id":"","url":"https://api.github.com/users/rwtombaugh","html_url":"https://github.com/rwtombaugh","followers_url":"https://api.github.com/users/rwtombaugh/followers","following_url":"https://api.github.com/users/rwtombaugh/following{/other_user}","gists_url":"https://api.github.com/users/rwtombaugh/gists{/gist_id}","starred_url":"https://api.github.com/users/rwtombaugh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rwtombaugh/subscriptions","organizations_url":"https://api.github.com/users/rwtombaugh/orgs","repos_url":"https://api.github.com/users/rwtombaugh/repos","events_url":"https://api.github.com/users/rwtombaugh/events{/privacy}","received_events_url":"https://api.github.com/users/rwtombaugh/received_events","type":"User","site_admin":false},"repo":{"id":96132747,"name":"blog","full_name":"rwtombaugh/blog","owner":{"login":"rwtombaugh","id":1031969,"avatar_url":"https://avatars0.githubusercontent.com/u/1031969?v=3","gravatar_id":"","url":"https://api.github.com/users/rwtombaugh","html_url":"https://github.com/rwtombaugh","followers_url":"https://api.github.com/users/rwtombaugh/followers","following_url":"https://api.github.com/users/rwtombaugh/following{/other_user}","gists_url":"https://api.github.com/users/rwtombaugh/gists{/gist_id}","starred_url":"https://api.github.com/users/rwtombaugh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rwtombaugh/subscriptions","organizations_url":"https://api.github.com/users/rwtombaugh/orgs","repos_url":"https://api.github.com/users/rwtombaugh/repos","events_url":"https://api.github.com/users/rwtombaugh/events{/privacy}","received_events_url":"https://api.github.com/users/rwtombaugh/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/rwtombaugh/blog","description":"auth0 blog","fork":true,"url":"https://api.github.com/repos/rwtombaugh/blog","forks_url":"https://api.github.com/repos/rwtombaugh/blog/forks","keys_url":"https://api.github.com/repos/rwtombaugh/blog/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rwtombaugh/blog/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rwtombaugh/blog/teams","hooks_url":"https://api.github.com/repos/rwtombaugh/blog/hooks","issue_events_url":"https://api.github.com/repos/rwtombaugh/blog/issues/events{/number}","events_url":"https://api.github.com/repos/rwtombaugh/blog/events","assignees_url":"https://api.github.com/repos/rwtombaugh/blog/assignees{/user}","branches_url":"https://api.github.com/repos/rwtombaugh/blog/branches{/branch}","tags_url":"https://api.github.com/repos/rwtombaugh/blog/tags","blobs_url":"https://api.github.com/repos/rwtombaugh/blog/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rwtombaugh/blog/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rwtombaugh/blog/git/refs{/sha}","trees_url":"https://api.github.com/repos/rwtombaugh/blog/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rwtombaugh/blog/statuses/{sha}","languages_url":"https://api.github.com/repos/rwtombaugh/blog/languages","stargazers_url":"https://api.github.com/repos/rwtombaugh/blog/stargazers","contributors_url":"https://api.github.com/repos/rwtombaugh/blog/contributors","subscribers_url":"https://api.github.com/repos/rwtombaugh/blog/subscribers","subscription_url":"https://api.github.com/repos/rwtombaugh/blog/subscription","commits_url":"https://api.github.com/repos/rwtombaugh/blog/commits{/sha}","git_commits_url":"https://api.github.com/repos/rwtombaugh/blog/git/commits{/sha}","comments_url":"https://api.github.com/repos/rwtombaugh/blog/comments{/number}","issue_comment_url":"https://api.github.com/repos/rwtombaugh/blog/issues/comments{/number}","contents_url":"https://api.github.com/repos/rwtombaugh/blog/contents/{+path}","compare_url":"https://api.github.com/repos/rwtombaugh/blog/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rwtombaugh/blog/merges","archive_url":"https://api.github.com/repos/rwtombaugh/blog/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rwtombaugh/blog/downloads","issues_url":"https://api.github.com/repos/rwtombaugh/blog/issues{/number}","pulls_url":"https://api.github.com/repos/rwtombaugh/blog/pulls{/number}","milestones_url":"https://api.github.com/repos/rwtombaugh/blog/milestones{/number}","notifications_url":"https://api.github.com/repos/rwtombaugh/blog/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rwtombaugh/blog/labels{/name}","releases_url":"https://api.github.com/repos/rwtombaugh/blog/releases{/id}","deployments_url":"https://api.github.com/repos/rwtombaugh/blog/deployments","created_at":"2017-07-03T16:57:14Z","updated_at":"2017-07-03T16:57:18Z","pushed_at":"2017-07-05T20:23:47Z","git_url":"git://github.com/rwtombaugh/blog.git","ssh_url":"git@github.com:rwtombaugh/blog.git","clone_url":"https://github.com/rwtombaugh/blog.git","svn_url":"https://github.com/rwtombaugh/blog","homepage":"https://auth0.com/blog","size":19064,"stargazers_count":0,"watchers_count":0,"language":"HTML","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"auth0:master","ref":"master","sha":"ea7f8171d876633ec06cb13a8bd1b2793cbf3c83","user":{"login":"auth0","id":2824157,"avatar_url":"https://avatars2.githubusercontent.com/u/2824157?v=3","gravatar_id":"","url":"https://api.github.com/users/auth0","html_url":"https://github.com/auth0","followers_url":"https://api.github.com/users/auth0/followers","following_url":"https://api.github.com/users/auth0/following{/other_user}","gists_url":"https://api.github.com/users/auth0/gists{/gist_id}","starred_url":"https://api.github.com/users/auth0/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/auth0/subscriptions","organizations_url":"https://api.github.com/users/auth0/orgs","repos_url":"https://api.github.com/users/auth0/repos","events_url":"https://api.github.com/users/auth0/events{/privacy}","received_events_url":"https://api.github.com/users/auth0/received_events","type":"Organization","site_admin":false},"repo":{"id":7830124,"name":"blog","full_name":"auth0/blog","owner":{"login":"auth0","id":2824157,"avatar_url":"https://avatars2.githubusercontent.com/u/2824157?v=3","gravatar_id":"","url":"https://api.github.com/users/auth0","html_url":"https://github.com/auth0","followers_url":"https://api.github.com/users/auth0/followers","following_url":"https://api.github.com/users/auth0/following{/other_user}","gists_url":"https://api.github.com/users/auth0/gists{/gist_id}","starred_url":"https://api.github.com/users/auth0/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/auth0/subscriptions","organizations_url":"https://api.github.com/users/auth0/orgs","repos_url":"https://api.github.com/users/auth0/repos","events_url":"https://api.github.com/users/auth0/events{/privacy}","received_events_url":"https://api.github.com/users/auth0/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/auth0/blog","description":"auth0 blog","fork":false,"url":"https://api.github.com/repos/auth0/blog","forks_url":"https://api.github.com/repos/auth0/blog/forks","keys_url":"https://api.github.com/repos/auth0/blog/keys{/key_id}","collaborators_url":"https://api.github.com/repos/auth0/blog/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/auth0/blog/teams","hooks_url":"https://api.github.com/repos/auth0/blog/hooks","issue_events_url":"https://api.github.com/repos/auth0/blog/issues/events{/number}","events_url":"https://api.github.com/repos/auth0/blog/events","assignees_url":"https://api.github.com/repos/auth0/blog/assignees{/user}","branches_url":"https://api.github.com/repos/auth0/blog/branches{/branch}","tags_url":"https://api.github.com/repos/auth0/blog/tags","blobs_url":"https://api.github.com/repos/auth0/blog/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/auth0/blog/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/auth0/blog/git/refs{/sha}","trees_url":"https://api.github.com/repos/auth0/blog/git/trees{/sha}","statuses_url":"https://api.github.com/repos/auth0/blog/statuses/{sha}","languages_url":"https://api.github.com/repos/auth0/blog/languages","stargazers_url":"https://api.github.com/repos/auth0/blog/stargazers","contributors_url":"https://api.github.com/repos/auth0/blog/contributors","subscribers_url":"https://api.github.com/repos/auth0/blog/subscribers","subscription_url":"https://api.github.com/repos/auth0/blog/subscription","commits_url":"https://api.github.com/repos/auth0/blog/commits{/sha}","git_commits_url":"https://api.github.com/repos/auth0/blog/git/commits{/sha}","comments_url":"https://api.github.com/repos/auth0/blog/comments{/number}","issue_comment_url":"https://api.github.com/repos/auth0/blog/issues/comments{/number}","contents_url":"https://api.github.com/repos/auth0/blog/contents/{+path}","compare_url":"https://api.github.com/repos/auth0/blog/compare/{base}...{head}","merges_url":"https://api.github.com/repos/auth0/blog/merges","archive_url":"https://api.github.com/repos/auth0/blog/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/auth0/blog/downloads","issues_url":"https://api.github.com/repos/auth0/blog/issues{/number}","pulls_url":"https://api.github.com/repos/auth0/blog/pulls{/number}","milestones_url":"https://api.github.com/repos/auth0/blog/milestones{/number}","notifications_url":"https://api.github.com/repos/auth0/blog/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/auth0/blog/labels{/name}","releases_url":"https://api.github.com/repos/auth0/blog/releases{/id}","deployments_url":"https://api.github.com/repos/auth0/blog/deployments","created_at":"2013-01-25T22:57:37Z","updated_at":"2017-06-20T19:23:57Z","pushed_at":"2017-07-07T16:06:28Z","git_url":"git://github.com/auth0/blog.git","ssh_url":"git@github.com:auth0/blog.git","clone_url":"https://github.com/auth0/blog.git","svn_url":"https://github.com/auth0/blog","homepage":"https://auth0.com/blog","size":19328,"stargazers_count":35,"watchers_count":35,"language":"HTML","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":65,"mirror_url":null,"open_issues_count":44,"forks":65,"open_issues":44,"watchers":35,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/auth0/blog/pulls/916"},"html":{"href":"https://github.com/auth0/blog/pull/916"},"issue":{"href":"https://api.github.com/repos/auth0/blog/issues/916"},"comments":{"href":"https://api.github.com/repos/auth0/blog/issues/916/comments"},"review_comments":{"href":"https://api.github.com/repos/auth0/blog/pulls/916/comments"},"review_comment":{"href":"https://api.github.com/repos/auth0/blog/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/auth0/blog/pulls/916/commits"},"statuses":{"href":"https://api.github.com/repos/auth0/blog/statuses/35754adce660194bcd0c5233cb71b2fee28a3de1"}}}}
{"actor":{"display_login":"tjanczuk"}}