Add Ocapi to a website

Requirements

  • Your website needs to be able to make a private HTTP request towards our service to validate the user response. This cannot be done by a pure Javascript frontend nor a static website.
  • As OCAPI displays images from public institutions, we need to validate the websites using this service. To include Ocapi in your website, please contact us through our contact form or send us an email at team@teklia.com.

Workflow

You can learn more about the Ocapi usage workflow here

Workflow

API documentation

The OpenAPI documentation is available here.

Setup on your website

Information needed from Teklia

You'll need 4 information from Teklia:

  • The API base url to use for requests (it's currently https://ocapi.teklia.com)
  • The Javascript payload URL (it should be something like https://assets.teklia.com/ocapi/X.Y.Z/ocapi-X.Y.Z.js )
  • The credentials for your account on our backend:
  • a public id that will be used by your users
  • a private id that will only be used by your website to validate users

Current releases

Stable version

  • Version 0.3.2
  • Javascript payload
  • Integrity key is: JAI/JIuGPO6SWugsKs3nAWV8y8crq008SfxgJU5oLX1HPqAa08OLEDHeameEzj+t

You can use that version with the following tag on your website:

<script type="text/javascript" src="https://assets.teklia.com/ocapi/0.3.2/ocapi-0.3.2.js" integrity="sha384-JAI/JIuGPO6SWugsKs3nAWV8y8crq008SfxgJU5oLX1HPqAa08OLEDHeameEzj+t" crossorigin="anonymous"></script>

Old Stable versions

  • Version 0.3.1
  • Javascript payload
  • Integrity key is: 2/rlHeJ9ZjwSj+GtTzqopMMJSrkjwZWVmQDQuNzaeoIld+2G7gLBMGoMqOkIb8wj

You can use that version with the following tag on your website:

<script type="text/javascript" src="https://assets.teklia.com/ocapi/0.3.1/ocapi-0.3.1.js" integrity="sha384-2/rlHeJ9ZjwSj+GtTzqopMMJSrkjwZWVmQDQuNzaeoIld+2G7gLBMGoMqOkIb8wj" crossorigin="anonymous"></script>
  • Version 0.3.0
  • Javascript payload
  • Integrity key is: qGnt9bIFK/YpDlqH7dViACryhf0VZwPd9UE0wlKx+Zhf0F/Jyjx9vQ+H3R4fJuvZ

You can use that version with the following tag on your website:

<script type="text/javascript" src="https://assets.teklia.com/captchan/0.3.0/captchan-0.3.0.js" integrity="sha384-qGnt9bIFK/YpDlqH7dViACryhf0VZwPd9UE0wlKx+Zhf0F/Jyjx9vQ+H3R4fJuvZ" crossorigin="anonymous"></script>

This version does not support audio fallbacks.

Legacy version

  • Version 0.2.1
  • Javascript payload
  • Integrity key is: YQKwIw45el29YcGdymv07mSNmYnFNQjaVp70nRSuHePFKeuE8OvB7n9ep2Q0LSl2

You can use that version with the following tag on your website:

<script type="text/javascript" src="https://assets.teklia.com/captchan/0.2.1/captchan-0.2.1.js" integrity="sha384-YQKwIw45el29YcGdymv07mSNmYnFNQjaVp70nRSuHePFKeuE8OvB7n9ep2Q0LSl2" crossorigin="anonymous"></script>

This version is compatible with the legacy server

Adding the challenge to a form

Once you've picked a form on your website, you can easily add our captcha:

  1. Include our Javascript payload on your page, as referenced above in the Current releases section.
  2. Add a placeholder <div> element where you want the challenge to appear on your page
<div
  id="captchan-challenge"
  data-captchan-client="ocapiPublicID"
  data-captchan-base-url="ocapiAPIUrl"
></div>

This element needs the captchan-challenge identifier, and the provided public id as data-captchan-client attribute. The data-captchan-base-url attribute is not necessary and is only used by versions above 0.3.0.

In the versions above 0.3.2, the ocapi-challenge identifier and the data-ocapi-* attributes will be available. The captchan-challenge identifier and the data-captchan-* attributes will be deprecated.

In the version above 0.3.3, the data-ocapi-game-mode attribute will be available. By adding this attribute, the game mode will be activated and the user will have to fill in several captchas successively. At the end, the final score is displayed and the player can play again.

Please note that this element must be inside a <form> to be able to validate the user: an hidden <input> will be added.

⚠ Never include the private id on that page.

Validated challenge

If you use a version above 0.3.2, each time a challenge is validated, the ocapi-challenge-validated event is sent to the document Javascript reference. Its detail contains a map including the ID of the validated challenge. Here is an example of the event detail in json:

{
  "challenge_id" : "aChallengeId"
}

And here is an example of a script that receives and uses the event:

<script type="text/javascript">
  document.addEventListener(
    "ocapi-challenge-validated",
    (evt) => {
      console.log("Challenge " + evt.detail.challenge_id + " has been validated")
    }
  )
  </script>

Validating a user

Once a user has replied to a challenge from your website, your form will include a field named captchan-challenge-id.

⚠ If you use a version above 0.3.1, your form will also include the field named captchan-client-id.

In the versions above 0.3.2, your form will include the fields named ocapi-client-id and ocapi-challenge-id. The captchan-* fields will be deprecated.

Your website needs to use that unique identifier in order to make an HTTP GET request on our service:

  • method GET
  • path: /v1/state/<client_id>/<challenge_id>
  • <client_id> is your public id
  • <challenge_id> is the unique id included in your form
  • header: Authorization: <JWT>

The request must include a Json Web Token Authorization header configured as:

  • the payload is a map including your public client id
  • (⚠ if you use a version above 0.3.1, the payload is a map including the client id included in your form)
  • the hashing algorithm is HS512 (Sha 512)
  • the signing key is the secret key provided by us

Here is the payload in json:

{
  "client_id": "yourPublicIDOrTheClientIDInYourForm"
}

This endpoint may give several response code:

  • 404 when the requested client in the url is not found
  • 403 when the Authorization header is missing or invalid
  • 404 when the requested challenge is not found
  • 200 when the challenge is available

When the challenge is found, you'll get a really simple JSON response payload with a state key. It can either be: - validated if the user has correctly replied to the challenge - rejected if the user provided an invalid answer. You need to reject the user in that case. - created if the user did not answer the question. You need to reject the user in that case.

Demo implementation in PHP

You can view the source code of a demonstration implementation in PHP on https://gitlab.com/teklia/captchan-demo

<?php
  # Configure the service
  define("BACKEND_URL", "https://ocapi.teklia.com/v1/");
  define("FRONTEND_URL", "https://assets.teklia.com/captchan/X.Y.Z.js");
  define("CLIENT_ID", "somePublicID");
  define("SECRET_KEY", "someSecretKey");

  $display_form = true;
  $error = false;

  if ($_POST) {
    if ($_POST["email"] && $_POST["password"]) {
      $challenge_id = $_POST["captchan-challenge-id"];
      $client_id = CLIENT_ID;
      # ⚠ Replace the previous line by this one **only** if you use a version above 0.3.1
      # $client_id = $_POST["captchan-client-id"];

      $api_url = BACKEND_URL."state/".CLIENT_ID."/".$challenge_id;

      # Create JWT
      $payload = [
        "client_id" => $client_id,
      ];
      $authorization = create_jwt($payload, SECRET_KEY);

      # Send request
      $curl = create_curl($api_url, $authorization);
      $data = curl_exec($curl);

      # Check response
      $data = json_decode($data, true);
      if (curl_getinfo($curl , CURLINFO_HTTP_CODE) === 200) {
        if ($data["state"] === "validated") {
          $display_form = false;
        } else {
          $error = "Captcha not validated";
        }
      } else if (curl_getinfo($curl , CURLINFO_HTTP_CODE) === 403) {
        $error = "Authorization error";
      } else if (curl_getinfo($curl , CURLINFO_HTTP_CODE) === 404) {
        $error = "Challenge not found";
      } else {
        $error = curl_error($curl);
      }

      if ($error) error_log($error.": ".var_export($data, true));

      curl_close($curl);

    } else {
      $error = "Invalid POST request";
    }
  }
?>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
    <link rel="stylesheet" href="signin.css">
    <title>Ocapi by Teklia</title>
    <script type="text/javascript" src="<?= FRONTEND_URL ?>"></script>
    <script type="text/javascript">
      document.addEventListener("ocapi-challenge-validated", (evt) => { console.log("Challenge " + evt.detail.challenge_id + " has been validated") })
    </script>
  </head>
  <body class="text-center">
    <?php if($display_form): ?>
      <?php if($error): ?>
        <div class="error h4 mb-3 font-weight-normal"><?= $error ?></div>
      <?php endif; ?>
      <form action="" method="post" class="form-signin">
        <h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
        <div>
          <label class="sr-only" for="email">Email address</label>
          <input class="form-control" type="email" id="email" name="email" required placeholder="Email address">
        </div>

        <div class="field">
          <label class="sr-only" for="password">Password</label>
          <input class="form-control" type="password" id="password" name="password" required placeholder="Password">
        </div>

        <div class="captcha">
          <!-- The challenge will be displayed here -->
          <div
            id="captchan-challenge"
            data-captchan-client="<?= CLIENT_ID ?>"
            data-captchan-base-url="<?= BACKEND_URL ?>"
          ></div>
        </div>

        <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
      </form>

    <?php else: ?>
      <div class="h3 mb-3 font-weight-normal">Hello <?= $_POST["email"] ?></div>
    <?php endif; ?>
  </body>
</html>