{"action":"created","comment":{"url":"https://api.github.com/repos/Yubico/developers.yubico.com/pulls/comments/188948765","pull_request_review_id":121026094,"id":188948765,"diff_hunk":"@@ -0,0 +1,309 @@\n+== Using a FIDO2 library ==\n+Let us have a look at the FIDO2 authentication sequence diagram:\n+\n+[mscgen]\n+----\n+msc {\n+   # Options\n+   hscale = \"1.5\";\n+\n+   # Entities\n+   d [label = \"Device\"], b [label=\"Browser\"], s [label=\"Server\"];\n+\n+   # Arcs\n+   |||;\n+   b -> s [label = \"username\"];\n+   |||;\n+   s rbox s [label=\"get credentialId\", linecolor=\"#44B724\", textcolor=\"#44B724\"];\n+   s rbox s [label=\"generate challenge\", linecolor=\"#44B724\", textcolor=\"#44B724\"];\n+   s -> b [label = \"credentialId, challenge\"];\n+   |||;\n+   b rbox b [label = \"navigator.credentials.get()\"];\n+   |||;\n+   b => d [label = \"credentialId challenge,\\nrpInfo, tokenBindingInfo\\n\\n\\n\", linecolor=\"#2156a5\", textcolor=\"#2156a5\"];\n+   d rbox d [label=\"user touches button\", linecolor=\"#2156a5\", textcolor=\"#2156a5\"];\n+   |||;\n+   d >> b [label = \"credentialId, clientData,\\nauthData, signature, keyHandle\\n\\n\\n\", linecolor=\"#2156a5\", textcolor=\"#2156a5\"];\n+   |||;\n+   b rbox b [label = \"POST assertion\"];\n+   b -> s [label = \"credentialId, clientData,\\nauthData, signature, keyHandle\\n\\n\\n\"];\n+   s rbox s [label=\"verify response\", linecolor=\"#44B724\", textcolor=\"#44B724\"];\n+}\n+----\n+\n+The blue part is handled by the WebAuthn client (e.g. the web browser) and the green parts are handled by the FIDO2 server library.\n+\n+\n+=== Server-side ===\n+\n+A server-side FIDO2 library has 4 basic functions: _Start registration, Finish registration, Start authentication_ and _Finish authentication_.\n+Below is an example of how these functions can be used in a web server:\n+\n+==== Registration ====\n+\n+[source, java]\n+----\n+// handles HTTPS requests to /register\n+public Either<String, RegistrationRequest> startRegistration(String username, String displayName, String credentialNickname) {\n+  ...\n+\n+  if (userStorage.getRegistrationsByUsername(username).isEmpty()) {\n+      byte[] userId = challengeGenerator.generateChallenge();\n+\n+      RegistrationRequest request = new RegistrationRequest(\n+          username,\n+          credentialNickname,\n+          U2fB64Encoding.encode(challengeGenerator.generateChallenge()),\n+          rp.startRegistration(\n+              new UserIdentity(username, displayName, userId, Optional.empty()),\n+              Optional.of(userStorage.getCredentialIdsForUsername(username)),\n+              Optional.empty()\n+          )\n+      );\n+      registerRequestStorage.put(request.getRequestId(), request);\n+      return new Right(request);\n+  } else {\n+      return new Left(\"The username \\\"\" + username + \"\\\" is already registered.\");\n+  }\n+}\n+----\n+\n+\n+[source, java]\n+----\n+// handles HTTPS requests to /register/finish\n+public Either<List<String>, SuccessfulRegistrationResult> finishRegistration(String responseJson) {\n+  ...\n+  RegistrationResponse response = null;\n+  ...\n+      response = jsonMapper.readValue(responseJson, RegistrationResponse.class);\n+  ...\n+\n+  RegistrationRequest request = registerRequestStorage.getIfPresent(response.getRequestId());\n+  registerRequestStorage.invalidate(response.getRequestId());\n+\n+  ...\n+      Try<RegistrationResult> registrationTry = rp.finishRegistration(\n+          request.getPublicKeyCredentialCreationOptions(),\n+          response.getCredential(),\n+          Optional.empty()\n+      );\n+\n+      if (registrationTry.isSuccess()) {\n+          return Right.apply(\n+              new SuccessfulRegistrationResult(\n+                  request,\n+                  response,\n+                  addRegistration(\n+                      request.getUsername(),\n+                      request.getPublicKeyCredentialCreationOptions().user(),\n+                      request.getCredentialNickname(),\n+                      response,\n+                      registrationTry.get()\n+                  ),\n+                  registrationTry.get().attestationTrusted()\n+              )\n+          );\n+      } else {\n+          ...\n+          return Left.apply(Arrays.asList(\"Registration failed!\", registrationTry.failed().get().getMessage()));\n+      }\n+\n+  ...\n+}\n+----\n+\n+\n+==== Authentication ====\n+\n+[source, java]\n+----\n+// handles HTTPS requests to /authenticate\n+public AssertionRequest startAuthentication(Optional<String> username) {\n+    ...\n+    AssertionRequest request = new AssertionRequest(\n+        username,\n+        U2fB64Encoding.encode(challengeGenerator.generateChallenge()),\n+        rp.startAssertion(\n+            username.map(userStorage::getCredentialIdsForUsername),\n+            Optional.empty()\n+        )\n+    );\n+\n+    assertRequestStorage.put(request.getRequestId(), request);\n+\n+    return request;\n+}\n+----\n+\n+[source, java]\n+----\n+// handles HTTPS requests to /authenticate/finish\n+public Either<List<String>, SuccessfulAuthenticationResult> finishAuthentication(String responseJson) {\n+    ...\n+\n+    final AssertionResponse response;\n+    ...\n+        response = jsonMapper.readValue(responseJson, AssertionResponse.class);\n+    ...\n+\n+    AssertionRequest request = assertRequestStorage.getIfPresent(response.getRequestId());\n+    assertRequestStorage.invalidate(response.getRequestId());\n+\n+    ...\n+        Optional<String> returnedUserHandle = Optional.ofNullable(response.getCredential().response().userHandleBase64());\n+\n+        final String username;\n+        if (request.getUsername().isPresent()) {\n+            username = request.getUsername().get();\n+        } else {\n+            username = userStorage.getUsername(returnedUserHandle.get()).orElse(null);\n+        }\n+\n+        final String userHandle = returnedUserHandle.orElseGet(() ->\n+            username == null\n+                ? null\n+                : userStorage.getUserHandle(username)\n+                    .map(BinaryUtil::toBase64)\n+                    .orElse(null)\n+        );\n+\n+        ...\n+            Try<AssertionResult> assertionTry = rp.finishAssertion(\n+                request.getPublicKeyCredentialRequestOptions(),\n+                response.getCredential(),\n+                () -> userHandle,\n+                Optional.empty()\n+            );\n+\n+            if (assertionTry.isSuccess()) {\n+                final AssertionResult result = assertionTry.get();\n+\n+                if (result.success()) {\n+                    ...\n+                        userStorage.updateSignatureCountForUsername(\n+                            username,\n+                            response.getCredential().id(),\n+                            result.signatureCount()\n+                        );\n+                    ...\n+\n+                    return Right.apply(\n+                        new SuccessfulAuthenticationResult(\n+                            request,\n+                            response,\n+                            userStorage.getRegistrationsByUsername(username)\n+                        )\n+                    );\n+                ...\n+                }\n+\n+            } else {\n+                ...\n+                return Left.apply(Arrays.asList(\"Assertion failed!\", assertionTry.failed().get().getMessage()));\n+            }\n+    ...\n+}\n+----\n+\n+In the example above `assertRequestStorage` and `registerRequestStorage` are a link:https://en.wikipedia.org/wiki/Data_access_object[DAO] that stores\n+challenges temporarily. The other DAO, `userStorage`, persists data permanently. \n+\n+NOTE: FIDO2 only works on HTTPS webpages.\n+\n+=== Client-side  ===\n+This section assumes that you are building a web site. If this is not the case,\n+have a look at our link:/Software_Projects/FIDO2/FIDO2_Host_Libraries/[FIDO2 host libraries] instead.\n+\n+The main part of the client is to\n+be a middle-man between the server and the FIDO2 compliant device.\n+\n+The straightforward way to use FIDO2 in a supported browser is to use the Web Authentication API, which exposes two functions:\n+\n+`navigator.credentials.create`:: Register using a FIDO2 device.\n+`navigator.credentials.get`:: Authenticate using a FIDO2 device.\n+\n+==== Registration ====\n+[source, html]\n+----\n+<script>\n+if (!window.PublicKeyCredential) { /* Platform not capable. Handle error. */ }\n+\n+var publicKey = {\n+  challenge: {challenge}, // The challenge must be produced by the server\n+\n+  // Relying Party:\n+  rp: {\n+    name: \"Demo server\"\n+  },\n+\n+  // User:\n+  user: {\n+    id: {user_id}, // id may be generated by the server\n+    name: \"a.user@example.com\",\n+    displayName: \"A User\",\n+    icon: \"https://example.com/image.png\"\n+  },\n+\n+  // This Relying Party will accept either an ES256 or RS256 credential, but\n+  // prefers an ES256 credential.\n+  pubKeyCredParams: [\n+    {\n+      type: \"public-key\",\n+      alg: -7 // \"ES256\" as registered in the IANA COSE Algorithms registry\n+    },\n+    {\n+      type: \"public-key\",\n+      alg: -257 // Value registered by this specification for \"RS256\"\n+    }\n+  ],\n+\n+  excludeCredentials: [], \n+  attestation: 'direct',\n+  timeout: 60000,  \n+  extensions: {\"loc\": true}  // Include location information in attestation\n+};\n+\n+// Note: The following call will cause the authenticator to display UI.\n+navigator.credentials.create({ publicKey })\n+  .then(function (attestation) {\n+    // Send new credential info to server for verification and registration.\n+  }).catch(function (err) {\n+    // No acceptable authenticator or user refused consent. Handle appropriately.\n+  });\n+</script>\n+----\n+\n+==== Authentication ====\n+[source, html]\n+----\n+<script>\n+if (!window.PublicKeyCredential) { /* Platform not capable. Handle error. */ }\n+\n+  navigator.credentials.get({{","path":"content/FIDO2/Libraries/Using_a_library.adoc","position":283,"original_position":283,"commit_id":"b4a5688a0c768c3783e80c1c09803ca961054e0b","original_commit_id":"b4a5688a0c768c3783e80c1c09803ca961054e0b","user":{"login":"emlun","id":1367758,"avatar_url":"https://avatars3.githubusercontent.com/u/1367758?v=4","gravatar_id":"","url":"https://api.github.com/users/emlun","html_url":"https://github.com/emlun","followers_url":"https://api.github.com/users/emlun/followers","following_url":"https://api.github.com/users/emlun/following{/other_user}","gists_url":"https://api.github.com/users/emlun/gists{/gist_id}","starred_url":"https://api.github.com/users/emlun/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/emlun/subscriptions","organizations_url":"https://api.github.com/users/emlun/orgs","repos_url":"https://api.github.com/users/emlun/repos","events_url":"https://api.github.com/users/emlun/events{/privacy}","received_events_url":"https://api.github.com/users/emlun/received_events","type":"User","site_admin":false},"body":"Why are there double `{{`s and `}}`s throughout this object literal?","created_at":"2018-05-17T13:03:47Z","updated_at":"2018-05-17T13:12:05Z","html_url":"https://github.com/Yubico/developers.yubico.com/pull/123#discussion_r188948765","pull_request_url":"https://api.github.com/repos/Yubico/developers.yubico.com/pulls/123","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/Yubico/developers.yubico.com/pulls/comments/188948765"},"html":{"href":"https://github.com/Yubico/developers.yubico.com/pull/123#discussion_r188948765"},"pull_request":{"href":"https://api.github.com/repos/Yubico/developers.yubico.com/pulls/123"}}},"pull_request":{"url":"https://api.github.com/repos/Yubico/developers.yubico.com/pulls/123","id":188526480,"html_url":"https://github.com/Yubico/developers.yubico.com/pull/123","diff_url":"https://github.com/Yubico/developers.yubico.com/pull/123.diff","patch_url":"https://github.com/Yubico/developers.yubico.com/pull/123.patch","issue_url":"https://api.github.com/repos/Yubico/developers.yubico.com/issues/123","number":123,"state":"open","locked":false,"title":"Added FIDO2 libraries and updated FIDO2 sequence diagrams","user":{"login":"elukewalker","id":2322006,"avatar_url":"https://avatars2.githubusercontent.com/u/2322006?v=4","gravatar_id":"","url":"https://api.github.com/users/elukewalker","html_url":"https://github.com/elukewalker","followers_url":"https://api.github.com/users/elukewalker/followers","following_url":"https://api.github.com/users/elukewalker/following{/other_user}","gists_url":"https://api.github.com/users/elukewalker/gists{/gist_id}","starred_url":"https://api.github.com/users/elukewalker/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/elukewalker/subscriptions","organizations_url":"https://api.github.com/users/elukewalker/orgs","repos_url":"https://api.github.com/users/elukewalker/repos","events_url":"https://api.github.com/users/elukewalker/events{/privacy}","received_events_url":"https://api.github.com/users/elukewalker/received_events","type":"User","site_admin":false},"body":"Add FIDO2 libraries and update FIDO2 sequence diagrams for consistency","created_at":"2018-05-16T18:49:59Z","updated_at":"2018-05-17T13:12:05Z","closed_at":null,"merged_at":null,"merge_commit_sha":"a12fd5ef0442b86987b3a6a5613b8b5731178a05","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/Yubico/developers.yubico.com/pulls/123/commits","review_comments_url":"https://api.github.com/repos/Yubico/developers.yubico.com/pulls/123/comments","review_comment_url":"https://api.github.com/repos/Yubico/developers.yubico.com/pulls/comments{/number}","comments_url":"https://api.github.com/repos/Yubico/developers.yubico.com/issues/123/comments","statuses_url":"https://api.github.com/repos/Yubico/developers.yubico.com/statuses/b4a5688a0c768c3783e80c1c09803ca961054e0b","head":{"label":"elukewalker:master","ref":"master","sha":"b4a5688a0c768c3783e80c1c09803ca961054e0b","user":{"login":"elukewalker","id":2322006,"avatar_url":"https://avatars2.githubusercontent.com/u/2322006?v=4","gravatar_id":"","url":"https://api.github.com/users/elukewalker","html_url":"https://github.com/elukewalker","followers_url":"https://api.github.com/users/elukewalker/followers","following_url":"https://api.github.com/users/elukewalker/following{/other_user}","gists_url":"https://api.github.com/users/elukewalker/gists{/gist_id}","starred_url":"https://api.github.com/users/elukewalker/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/elukewalker/subscriptions","organizations_url":"https://api.github.com/users/elukewalker/orgs","repos_url":"https://api.github.com/users/elukewalker/repos","events_url":"https://api.github.com/users/elukewalker/events{/privacy}","received_events_url":"https://api.github.com/users/elukewalker/received_events","type":"User","site_admin":false},"repo":{"id":131558616,"name":"developers.yubico.com","full_name":"elukewalker/developers.yubico.com","owner":{"login":"elukewalker","id":2322006,"avatar_url":"https://avatars2.githubusercontent.com/u/2322006?v=4","gravatar_id":"","url":"https://api.github.com/users/elukewalker","html_url":"https://github.com/elukewalker","followers_url":"https://api.github.com/users/elukewalker/followers","following_url":"https://api.github.com/users/elukewalker/following{/other_user}","gists_url":"https://api.github.com/users/elukewalker/gists{/gist_id}","starred_url":"https://api.github.com/users/elukewalker/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/elukewalker/subscriptions","organizations_url":"https://api.github.com/users/elukewalker/orgs","repos_url":"https://api.github.com/users/elukewalker/repos","events_url":"https://api.github.com/users/elukewalker/events{/privacy}","received_events_url":"https://api.github.com/users/elukewalker/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/elukewalker/developers.yubico.com","description":"Source code for generating our website","fork":true,"url":"https://api.github.com/repos/elukewalker/developers.yubico.com","forks_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/forks","keys_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/keys{/key_id}","collaborators_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/teams","hooks_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/hooks","issue_events_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/issues/events{/number}","events_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/events","assignees_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/assignees{/user}","branches_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/branches{/branch}","tags_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/tags","blobs_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/git/refs{/sha}","trees_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/git/trees{/sha}","statuses_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/statuses/{sha}","languages_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/languages","stargazers_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/stargazers","contributors_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/contributors","subscribers_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/subscribers","subscription_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/subscription","commits_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/commits{/sha}","git_commits_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/git/commits{/sha}","comments_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/comments{/number}","issue_comment_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/issues/comments{/number}","contents_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/contents/{+path}","compare_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/compare/{base}...{head}","merges_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/merges","archive_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/downloads","issues_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/issues{/number}","pulls_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/pulls{/number}","milestones_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/milestones{/number}","notifications_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/labels{/name}","releases_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/releases{/id}","deployments_url":"https://api.github.com/repos/elukewalker/developers.yubico.com/deployments","created_at":"2018-04-30T05:18:32Z","updated_at":"2018-05-16T18:55:01Z","pushed_at":"2018-05-16T18:55:00Z","git_url":"git://github.com/elukewalker/developers.yubico.com.git","ssh_url":"git@github.com:elukewalker/developers.yubico.com.git","clone_url":"https://github.com/elukewalker/developers.yubico.com.git","svn_url":"https://github.com/elukewalker/developers.yubico.com","homepage":"https://developers.yubico.com","size":10350,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":null,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"Yubico:master","ref":"master","sha":"a564b93f087d758239426a40ec4005c74b8b8437","user":{"login":"Yubico","id":531825,"avatar_url":"https://avatars0.githubusercontent.com/u/531825?v=4","gravatar_id":"","url":"https://api.github.com/users/Yubico","html_url":"https://github.com/Yubico","followers_url":"https://api.github.com/users/Yubico/followers","following_url":"https://api.github.com/users/Yubico/following{/other_user}","gists_url":"https://api.github.com/users/Yubico/gists{/gist_id}","starred_url":"https://api.github.com/users/Yubico/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Yubico/subscriptions","organizations_url":"https://api.github.com/users/Yubico/orgs","repos_url":"https://api.github.com/users/Yubico/repos","events_url":"https://api.github.com/users/Yubico/events{/privacy}","received_events_url":"https://api.github.com/users/Yubico/received_events","type":"Organization","site_admin":false},"repo":{"id":24320511,"name":"developers.yubico.com","full_name":"Yubico/developers.yubico.com","owner":{"login":"Yubico","id":531825,"avatar_url":"https://avatars0.githubusercontent.com/u/531825?v=4","gravatar_id":"","url":"https://api.github.com/users/Yubico","html_url":"https://github.com/Yubico","followers_url":"https://api.github.com/users/Yubico/followers","following_url":"https://api.github.com/users/Yubico/following{/other_user}","gists_url":"https://api.github.com/users/Yubico/gists{/gist_id}","starred_url":"https://api.github.com/users/Yubico/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Yubico/subscriptions","organizations_url":"https://api.github.com/users/Yubico/orgs","repos_url":"https://api.github.com/users/Yubico/repos","events_url":"https://api.github.com/users/Yubico/events{/privacy}","received_events_url":"https://api.github.com/users/Yubico/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/Yubico/developers.yubico.com","description":"Source code for generating our website","fork":false,"url":"https://api.github.com/repos/Yubico/developers.yubico.com","forks_url":"https://api.github.com/repos/Yubico/developers.yubico.com/forks","keys_url":"https://api.github.com/repos/Yubico/developers.yubico.com/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Yubico/developers.yubico.com/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Yubico/developers.yubico.com/teams","hooks_url":"https://api.github.com/repos/Yubico/developers.yubico.com/hooks","issue_events_url":"https://api.github.com/repos/Yubico/developers.yubico.com/issues/events{/number}","events_url":"https://api.github.com/repos/Yubico/developers.yubico.com/events","assignees_url":"https://api.github.com/repos/Yubico/developers.yubico.com/assignees{/user}","branches_url":"https://api.github.com/repos/Yubico/developers.yubico.com/branches{/branch}","tags_url":"https://api.github.com/repos/Yubico/developers.yubico.com/tags","blobs_url":"https://api.github.com/repos/Yubico/developers.yubico.com/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Yubico/developers.yubico.com/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Yubico/developers.yubico.com/git/refs{/sha}","trees_url":"https://api.github.com/repos/Yubico/developers.yubico.com/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Yubico/developers.yubico.com/statuses/{sha}","languages_url":"https://api.github.com/repos/Yubico/developers.yubico.com/languages","stargazers_url":"https://api.github.com/repos/Yubico/developers.yubico.com/stargazers","contributors_url":"https://api.github.com/repos/Yubico/developers.yubico.com/contributors","subscribers_url":"https://api.github.com/repos/Yubico/developers.yubico.com/subscribers","subscription_url":"https://api.github.com/repos/Yubico/developers.yubico.com/subscription","commits_url":"https://api.github.com/repos/Yubico/developers.yubico.com/commits{/sha}","git_commits_url":"https://api.github.com/repos/Yubico/developers.yubico.com/git/commits{/sha}","comments_url":"https://api.github.com/repos/Yubico/developers.yubico.com/comments{/number}","issue_comment_url":"https://api.github.com/repos/Yubico/developers.yubico.com/issues/comments{/number}","contents_url":"https://api.github.com/repos/Yubico/developers.yubico.com/contents/{+path}","compare_url":"https://api.github.com/repos/Yubico/developers.yubico.com/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Yubico/developers.yubico.com/merges","archive_url":"https://api.github.com/repos/Yubico/developers.yubico.com/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Yubico/developers.yubico.com/downloads","issues_url":"https://api.github.com/repos/Yubico/developers.yubico.com/issues{/number}","pulls_url":"https://api.github.com/repos/Yubico/developers.yubico.com/pulls{/number}","milestones_url":"https://api.github.com/repos/Yubico/developers.yubico.com/milestones{/number}","notifications_url":"https://api.github.com/repos/Yubico/developers.yubico.com/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Yubico/developers.yubico.com/labels{/name}","releases_url":"https://api.github.com/repos/Yubico/developers.yubico.com/releases{/id}","deployments_url":"https://api.github.com/repos/Yubico/developers.yubico.com/deployments","created_at":"2014-09-22T08:28:31Z","updated_at":"2018-05-11T18:15:16Z","pushed_at":"2018-05-16T18:55:01Z","git_url":"git://github.com/Yubico/developers.yubico.com.git","ssh_url":"git@github.com:Yubico/developers.yubico.com.git","clone_url":"https://github.com/Yubico/developers.yubico.com.git","svn_url":"https://github.com/Yubico/developers.yubico.com","homepage":"https://developers.yubico.com","size":10333,"stargazers_count":17,"watchers_count":17,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":22,"mirror_url":null,"archived":false,"open_issues_count":24,"license":null,"forks":22,"open_issues":24,"watchers":17,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/Yubico/developers.yubico.com/pulls/123"},"html":{"href":"https://github.com/Yubico/developers.yubico.com/pull/123"},"issue":{"href":"https://api.github.com/repos/Yubico/developers.yubico.com/issues/123"},"comments":{"href":"https://api.github.com/repos/Yubico/developers.yubico.com/issues/123/comments"},"review_comments":{"href":"https://api.github.com/repos/Yubico/developers.yubico.com/pulls/123/comments"},"review_comment":{"href":"https://api.github.com/repos/Yubico/developers.yubico.com/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/Yubico/developers.yubico.com/pulls/123/commits"},"statuses":{"href":"https://api.github.com/repos/Yubico/developers.yubico.com/statuses/b4a5688a0c768c3783e80c1c09803ca961054e0b"}},"author_association":"NONE"}}
{"actor":{"display_login":"emlun"}}