> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ownid.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Build Server-Side Endpoints

> Implement your server-side endpoints

OwnID provides templates for the three request handlers you'll host on your web server. These form the endpoints called from the OwnID platform, using signed HTTP requests.

These endpoints provide the connection between OwnId and your server for exchange user authentication data. That data is what you save in the `ownIdData` field you created in your database user table.

## Add Web Server Request Handlers

Below are code templates for each of the three request handlers needed to implement your server-side endpoints:

* Set OwnIdData - `setOwnIDDataByLoginId`

* Get OwnIdData - `getOwnIDDataByLoginId`

* Get Session - `getSessionByLoginId`

When the OwnID server receives a login event from your frontend, it then makes the appropriate HTTP requests to these endpoints.

The user's login id (typically the user's email address) is the unique identifier used to look up and save records in the `ownIdData` field.

Select the tab matching your server-side language in the sample templates below.

### Endpoint 1 - Set OwnIdData

This endpoint handles a request to save an `ownIdData` record for a given `loginId` when the user enrolls a new device. This is the VARCHAR field you created previously. It holds the public key of the device used for biometric authentication.

<CodeGroup>
  ```javascript Node.js theme={null}
  router.post('/setOwnIDDataByLoginId', async (req, res) => {
      const email = req.body.loginId; //The unique id of a user in your database, usually email or phone
      const ownIdData = req.body.ownIdData; //OwnID authentication information as string
      const user = await User.findOne({ email: email }).exec();
      user.ownIdData = ownIdData;
      await user.save();
      return res.sendStatus(204);
  }
  ```

  ```java Java theme={null}
  @PostMapping("/setOwnIDDataByLoginId")
  public ResponseEntity <Object> setOwnIdDataByLoginId(@RequestBody OwnIDData req) {
      User user = userRepository.findByLoginId(req.getLoginId());
      user.setOwnIdData(data);
      userRepository.save(user);

      return ResponseEntity.status(HttpStatus.NO_CONTENT).body(null);
  }
  ```

  ```csharp C# theme={null}
  [HttpPost("setOwnIDDataByLoginId")]
  public async Task<ActionResult<Object>> SetOwnIdDataByLoginId(string loginId, string ownIdData)
  {
      var user = _userRepository.Instance.Get(loginId);
      user.ownIdData = ownIdData;
      _userRepository.Instance.Update(user);

      return NoContent();
  }
  ```

  ```python Python theme={null}
  @app.route('/setOwnIDDataByLoginId', methods=['POST'])
  def set_ownid_data_by_login_id():
      data = request.get_json()
      login_id = data['loginId']
      ownid_data = data['ownIdData']
      user = db.session.query(Users).filter(Users.email == login_id).first()
      user.ownid_data = ownid_data
      user.save()
      return ('', 204)
  ```
</CodeGroup>

<sup>Sample request handler for setting an OwnIDData record by user login id. (`setOwnIDDataByLoginId`) </sup>

**Response**\
Implement these responses for `setOwnIDDataByLoginId`:

| Status | Description        | Return            |
| ------ | ------------------ | ----------------- |
| `204`  | Request successful | return empty body |

Example:

```json 204 - Request successful theme={null}
{}
```

***

### Endpoint 2 - Get OwnIdData

This endpoint handles a request to fetch an `ownIdData` record for a given user's login id.

<CodeGroup>
  ```javascript Node.js theme={null}
  router.post('/getOwnIDDataByLoginId', async (req, res) => {
      const email = req.body.loginId; // The unique id of a user in your database, usually email or phone
      
      try {
          const user = await User.findOne({ email: email }).exec();
          
          if (!user) {
              // User not found
              return res.status(404).send();
          }

          // Returning the possibly-empty ownIdData value.
          return res.status(200).json({ ownIdData: user.ownIdData });

      } catch (error) {
          console.error('Error fetching user:', error);
          return res.status(500).json({ error: 'Internal server error' });
      }
  });
  ```

  ```java Java theme={null}
  @PostMapping("/getOwnIDDataByLoginId")
  public ResponseEntity<Map<String, String>> getOwnIDDataByLoginId(@RequestBody Map<String, String> body) {
      String email = body.get("loginId");
      
      Optional<User> userOptional = userRepository.findByEmail(email);
      
      if (userOptional.isEmpty()) {
          return ResponseEntity.notFound().build();
      }
      
      User user = userOptional.get();
      if (user.getOwnIdData() != null && !user.getOwnIdData().isEmpty()) {
          Map<String, String> response = new HashMap<>();
          response.put("ownIdData", user.getOwnIdData());
          return ResponseEntity.ok(response);
      } else {
          return ResponseEntity.noContent().build();
      }
  }
  ```

  ```csharp C# theme={null}
  [HttpPost("getOwnIDDataByLoginId")]
      public async Task<IActionResult> GetOwnIDDataByLoginId([FromBody] LoginIdRequest request)
      {
          var user = await _context.Users.FirstOrDefaultAsync(u => u.Email == request.LoginId);

          if (user == null)
          {
              return NotFound();
          }

          if (!string.IsNullOrEmpty(user.OwnIdData))
          {
              return Ok(new { ownIdData = user.OwnIdData });
          }
          else
          {
              return NoContent();
          }
      }
  ```

  ```python Python theme={null}
  @app.route('/getOwnIDDataByLoginId', methods=['POST'])
  def get_ownid_data_by_login_id():
      data = request.get_json()
      login_id = data['loginId']
      user = db.session.query(Users).filter(Users.email == login_id).first()
      if not user:
          return jsonify({'errorCode': 404})
      return jsonify({'ownidData': user.ownid_data})
  ```
</CodeGroup>

<sup> Sample request handler for `getOwnIDDataByLoginId` endpoint</sup>

**Response**

Implement these responses for `getOwnIDDataByLoginId`:

| Status | Description                           | Return                  |
| ------ | ------------------------------------- | ----------------------- |
| `200`  | User found and has ownIdData          | return ownIdData String |
| `204`  | User found but doesn't have ownIdData | return empty body       |
| `404`  | User not found                        | return empty body       |

Examples:

<CodeGroup>
  ```json 200 - Return ownIdData theme={null}
  {
    "ownIdData": "<String>"
  }
  ```

  ```json 204 - Return empty body theme={null}
  {}
  ```

  ```json 404 - Return empty body theme={null}
  {}
  ```
</CodeGroup>

### Endpoint 3 - Get Session

Handle a request to fetch a token for the current session by user's email address.

Note that `token` is a unique session identifier that you generate. OwnID does not validate or use the token in any way, it merely passes the token back to you so you can identify the session it's associated with.

Later, we'll see how the OwnID login event returns this value in the frontend to establish a client session.

<CodeGroup>
  ```javascript Node.js theme={null}
  router.post('/getSessionByLoginId', async (req, res) => {
      const sign = require('jwt-encode');
      const email = req.body.loginId; //The unique id of a user in your database, usually email or phone
      const user = await User.findOne({ email: email }).exec();
      const jwt = sign({ email: user.email }, 'secret');
      return res.json({ token: jwt });
  });
  ```

  ```java Java theme={null}
  @PostMapping("/getSessionByLoginId")
  public ResponseEntity<OwnIDSessionResponse> getSessionByLoginId(@RequestBody OwnIDSearchRequest req) {
      User user = userRepository.findByLoginId(req.getLoginId());
      Algorithm alg = Algorithm.HMAC256("secret");
      String token = JWT.create().
          withClaim("loginId", req.getLoginId()).
          sign(alg);

      return ResponseEntity.status(HttpStatus.OK).body(new OwnIDSessionResponse(token));
  }
  ```

  ```csharp C# theme={null}
  [HttpPost("getSessionByLoginId")]
  public async Task<ActionResult<OwnIDSessionResponse>> GetSessionByLoginId(string loginId)
  {
      var user = _userRepository.Instance.Get(loginId);
      var hashObject = new HMACSHA256(Encoding.UTF8.GetBytes("secret"));
      var signature = hashObject.ComputeHash(Encoding.UTF8.GetBytes(user.LoginId));
      var encodedSignature = Convert.ToBase64String(signature);

      return Ok(new OwnIDSessionResponse() { Token = encodedSignature });
  }
  ```

  ```python Python theme={null}
  @app.route('/ownid/getSessionByLoginId', methods=['POST'])
  def get_session_by_login_id():
      data = request.get_json()
      login_id = data['loginId']
      user = db.session.query(Users).filter(Users.email == login_id).first()
      token = jwt.encode({
          'userId': str(user.email), 'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=3)},
          app.config['SECRET_KEY'],
          "HS256")
      return jsonify({'token': token})
  ```
</CodeGroup>

<sup> Sample request handler for `getSessionByLoginId` endpoint.</sup>

**Response**

Implement these responses for `getSessionByLoginId`:

| Status | Description        | Return                             |
| ------ | ------------------ | ---------------------------------- |
| `200`  | Request successful | return object or String (e.g: jwt) |
| `423`  | User is locked     | return empty body                  |

Example:

<CodeGroup>
  ```json 200 - Request successful theme={null}
  {
    "token": "<String>"
  }
  ```

  ```json 423 - Return empty body theme={null}
  {}
  ```
</CodeGroup>

***

## Secure Your Endpoints

Requests from the OwnID server include two headers that can be used to ensure the request has not been tampered with. The first one, `ownid-signature`, is a hash value that the OwnID server generates from a timestamp and the body of the request. The second one, `ownid-timestamp` can be used by your backend to calculate the signature that is based on the timestamp and request body, and then compare the result to the value of `ownid-signature`. If both signatures do not match, the request has been altered.

Because the signatures are generated using an HMAC with the SHA256 hash function, the OwnID server and your backend must use the same cryptographic key when calculating the hash value. You can obtain this key from the OwnID Console, and then add the code generates a hash and compares it to the signature in your backend.

**Obtaining the HMAC Key**

Before the backend can generate the HMACSHA256 value, you must obtain the secret cryptographic key used in the calculation. Simply open your OwnID application in the [OwnID Console](https://console.ownid.com/) and copy the value from **MyApp > Shared Secret**.

**Request Verification**

Now that you have the cryptographic key, the backend can verify requests by generating each request's expected signature and compare it to the one generated by the OwnID server. The backend code must:

* **Step 1:** Extract the `ownid-signature` and `ownid-timestamp` headers from the request. These headers provide the HMAC code generated by the OwnID server and the timestamp it used to generate it.

* **Step 2:** Validate the `ownid-timestamp` for expiration.
  To prevent replay attacks, check whether the provided timestamp is within an acceptable time window. Define a preferred expiration time, such as 1 minute, and validate against the current time. Requests with a timestamp older than this period should be rejected.

* **Step 3:** Create the data string that will be used as an input to the hash function. To create it you need to concatenate:
  * The request body (in a JSON string format)
  * The character `.`
  * The timestamp (from the `ownid-timestamp` header)

* **Step 4:** Use HMAC with SHA256 to calculate a hashed value from the body-timestamp data string. The cryptographic key used in the calculation is the shared secret for your OwnID application.

* **Step 5:** Compare the hash value generated by your backend with the signature extracted from the `ownid-signature` header.

The following code snippets show how the backend might accomplish these steps:

<CodeGroup>
  ```javascript nodeJS theme={null}
  const crypto = require('crypto');

  // Step 1: Extract values from headers and request body
  let key = "<your-shared-secret>"; // This is the shared secret from OwnID Console
  let keyBuffer = Buffer.from(key, 'base64');
  let body = JSON.stringify(req.body);
  let ownIdSignature = req.headers['ownid-signature'];
  let ownIdTimestamp = req.headers['ownid-timestamp'];
  let dataToSign = `${body}.${ownIdTimestamp}`;

  // Step 2: Validate the `ownid-timestamp` for expiration
  const expirationTimeInMilliseconds = 60 * 1000; 
  const currentTime = Date.now(); /
  const ownIdTimestampMs = parseInt(ownIdTimestamp);

  if (Math.abs(currentTime - ownIdTimestampMs) > expirationTimeInMilliseconds) {
      // The timestamp has expired
      throw new Error("Request rejected: Signature has expired.");
  }

  // Step 3: Generate HMAC signature
  const hmac = crypto.createHmac('sha256', keyBuffer);
  hmac.update(dataToSign);
  let signature = hmac.digest('base64');

  // Step 4: Compare the generated signature with the one from the header
  if (signature !== ownIdSignature) {
      // The request has been tampered with
      throw new Error("Request rejected: Invalid signature.");
  }
  ```

  ```python python theme={null}
  import hmac
  import hashlib
  import base64
  import json
  from datetime import datetime, timedelta

  # Step 1: Extract shared secret, headers, and request body
  key = "<your-shared-secret>"  # This is the shared secret from OwnID Console
  key_buffer = base64.b64decode(key)
  data = json.dumps(request.get_json(), separators=(',', ':'))
  own_id_signature = request.headers.get('ownid-signature')
  own_id_timestamp = request.headers.get('ownid-timestamp')
  data_to_sign = f'{data}.{own_id_timestamp}'.encode('utf-8')

  # Step 2: Validate the `ownid-timestamp` for expiration
  expiration_time = timedelta(minutes=1) # Example expiration period of 1 minute
  current_time = datetime.now()  # Current time in seconds since epoch
  # OwnID returns timestamp with miliseconds, remove them.
  own_id_datetime = datetime.fromtimestamp(int(own_id_timestamp) / 1000.0)

  if current_time - own_id_datetime > expiration_time:
      # The timestamp has expired
      raise Exception("Request rejected: Signature has expired.")

  # Step 3: Generate HMAC signature
  signature = hmac.new(key_buffer, data_to_sign, hashlib.sha256).digest()
  signature = base64.b64encode(signature).decode('utf-8')

  # Step 4: Compare the generated signature with the one from the header
  if not signature == own_id_signature:
      # The request body or timestamp has been tampered with
      raise Exception("Request rejected: Invalid signature.")
  ```

  ```csharp csharp theme={null}
  using System;
  using System.Linq;
  using System.Security.Cryptography;
  using System.Text;
  using System.Text.Encodings.Web;
  using System.Text.Json;

  // Step 1: Extract shared secret, headers, and request body
  string key = "<your-shared-secret>"; // This is the shared secret from OwnID Console
  byte[] keyBuffer = Convert.FromBase64String(key);
  var serializerOptions = new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping };
  string body = JsonSerializer.Serialize(ownIdRequest.Content, serializerOptions);
  string ownIdSignature = ownIdRequest.Headers.GetValues("OwnID-Signature").FirstOrDefault();
  string ownIdTimestamp = ownIdRequest.Headers.GetValues("OwnID-Timestamp").FirstOrDefault();
  string dataToSign = $"{body}.{ownIdTimestamp}";

  // Step 2: Validate the `OwnID-Timestamp` for expiration
  int expirationTimeInSeconds = 60; // Example expiration period of 1 minute
  long currentTimeInSeconds = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); // Current time in seconds since epoch
  long timestamp = long.Parse(ownIdTimestamp);

  if (Math.Abs(currentTimeInSeconds - timestamp) > expirationTimeInSeconds)
  {
      // The timestamp has expired
      throw new SecurityException("Request rejected: Signature has expired.");
  }

  // Step 3: Generate HMAC signature
  byte[] dataToSignBytes = Encoding.UTF8.GetBytes(dataToSign);
  string signature;
  using (HMACSHA256 hmac = new HMACSHA256(keyBuffer))
  {
      signature = Convert.ToBase64String(hmac.ComputeHash(dataToSignBytes));
  }

  // Step 4: Compare the generated signature with the one from the header
  if (signature != ownIdSignature)
  {
      // The request has been tampered with
      throw new SecurityException("Request rejected: Invalid signature.");
  }
  ```

  ```java java theme={null}
  import javax.crypto.Mac;
  import javax.crypto.spec.SecretKeySpec;
  import javax.persistence.EntityNotFoundException;
  import javax.persistence.NoResultException;
  import java.nio.charset.StandardCharsets;
  import java.security.Key;
  import java.util.Base64;
  import java.util.Map;

  // Step 1: Extract shared secret, headers, and request body
  String key = "<your-shared-secret>"; // This is the shared secret from OwnID Console
  byte[] keyBuffer = Base64.getDecoder().decode(key);
  Key hashKey = new SecretKeySpec(keyBuffer, "HmacSHA256");
  String ownIdSignature = headers.get("ownid-signature");
  String ownIdTimestamp = headers.get("ownid-timestamp");
  String dataToSign = String.format("%s.%s", bodyJson, ownIdTimestamp);

  // Step 2: Validate the `ownid-timestamp` for expiration
  long expirationTimeInSeconds = 60; // Example expiration period of 1 minute
  long currentTimeInSeconds = System.currentTimeMillis() / 1000L; // Current time in seconds since epoch
  long timestamp = Long.parseLong(ownIdTimestamp);

  if (Math.abs(currentTimeInSeconds - timestamp) > expirationTimeInSeconds) {
      // The timestamp has expired
      throw new SecurityException("Request rejected: Signature has expired.");
  }

  // Step 3: Generate HMAC signature
  byte[] dataToSignBytes = dataToSign.getBytes(StandardCharsets.UTF_8);
  Mac mac = Mac.getInstance("HmacSHA256");
  mac.init(hashKey);
  byte[] signatureBytes = mac.doFinal(dataToSignBytes);
  String signature = Base64.getEncoder().encodeToString(signatureBytes);

  // Step 4: Compare the generated signature with the one from the header
  if (!signature.contentEquals(ownIdSignature)) {
      // The request has been tampered with
      throw new SecurityException("Request rejected: Invalid signature.");
  }
  ```
</CodeGroup>

## Next Steps

With the request handlers in place, you're ready to build the frontend integrations for login, registration, account recovery, and more.

<Card title="Build Frontend User Journey" icon="screwdriver-wrench" href="/building-blocks/introduction">
  Integrate the OwnID SDK in your frontend journeys.
</Card>
