---
title: Generic entitlement interface
slug: editorial/generic-entitlement-interface
docTags: 
createdAt: 2024-03-27T14:49:29.329Z
---

## Introduction

The generic entitlement allows you to connect your entitlement service to Purple.

:::hint{type="danger"}
When choosing to use this interface, Purple simply provides the interface, but the development work to connect to it needs to be done by the customer/other party.
:::



It offers two basic authentication models:

- Username / Password
- OAuth 2.0 with OpenID&#x20;

:::hint{type="info"}
There are sample implementations available for both models:

[Username / Password Entitlement Sample](https://github.com/PurplePublish/generic-entitlement-sample)

[OAuth 2.0 with OpenID Sample](https://github.com/PurplePublish/generic-oauth-entitlement-sample)
:::

## API Specifications

### Username / Password

For username / password entitlements there are four main operations to implement in your backend service:

:::ExpandableHeading
### login

Login takes a username/password combination and returns a token to be used for further requests.

**Path**: /v1/login

**Method**: POST

**Request body (JSON):**

| Field        | Type       | Optional/Required |
| ------------ | ---------- | ----------------- |
| **appId**    | **String** | **Required**      |
| deviceId     | String     | Optional          |
| **username** | **String** | **Required**      |
| password     | String     | Optional          |

**Response body (JSON):**

**Success (HTTP Status 200)**

| Field           | Type       | Optional/Required |
| --------------- | ---------- | ----------------- |
| **accessToken** | **String** | **Required**      |
| userId          | String     | Optional          |

**Error: Invalid credentials (HTTP Status 403)**

| Field       | Type              | Optional/Required | Values                                                                                                                                                                                                             |
| ----------- | ----------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **code**    | **String (Enum)** | **Required**      | - UNKNOWN
- WRONG\_PASSWORD\_OR\_USERNAME
- USER\_DEACTIVATED
- AUTHENTICATION\_ERROR
- WRONG\_PASSWORD
- INSTALLATION\_LIMIT\_EXCEEDED
- SYSTEM\_ERROR\_IN\_REMOTE\_SYSTEM
- PARAMETER\_ERROR\_IN\_REMOTE\_SYSTEM |
| **message** | **String**        | **Required**      |                                                                                                                                                                                                                    |


:::

:::ExpandableHeading
### logout

Logout takes the access token from a session and performs necessary steps to end the session.

**Path**: /v1/logout

**Method**: POST

**Request body (JSON):**

| Field           | Type       | Optional/Required |
| --------------- | ---------- | ----------------- |
| **appId**       | **String** | **Required**      |
| deviceId        | String     | Optional          |
| **accessToken** | **String** | **Required**      |

**Response body (JSON):**

**Success (HTTP Status 200)**

No response body

**Error: Invalid credentials (HTTP Status 403)**

| Field       | Type              | Optional/Required | Values                                                                                                       |
| ----------- | ----------------- | ----------------- | ------------------------------------------------------------------------------------------------------------ |
| **code**    | **String (Enum)** | **Required**      | - UNKNOWN
- AUTHENTICATION\_ERROR
- SYSTEM\_ERROR\_IN\_REMOTE\_SYSTEM
- PARAMETER\_ERROR\_IN\_REMOTE\_SYSTEM |
| **message** | **String**        | **Required**      |                                                                                                              |
:::

:::ExpandableHeading
### verify

Verify takes an active access token, verifies that the session is still valid and optionally  returns a rotated/updated access token back to the client.

**Path**: /v1/verify

**Method**: POST

**Request body (JSON):**

| Field           | Type       | Optional/Required |
| --------------- | ---------- | ----------------- |
| **appId**       | **String** | **Required**      |
| deviceId        | String     | Optional          |
| **accessToken** | **String** | **Required**      |

**Response body (JSON):**

**Success (HTTP Status 200)**

| Field           | Type       | Optional/Required |
| --------------- | ---------- | ----------------- |
| **accessToken** | **String** | **Required**      |
| userId          | String     | Optional          |

**Error: Invalid credentials (HTTP Status 403)**

| Field       | Type              | Optional/Required | Values                                                                                                                                                                                                             |
| ----------- | ----------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **code**    | **String (Enum)** | **Required**      | - UNKNOWN
- WRONG\_PASSWORD\_OR\_USERNAME
- USER\_DEACTIVATED
- AUTHENTICATION\_ERROR
- WRONG\_PASSWORD
- INSTALLATION\_LIMIT\_EXCEEDED
- SYSTEM\_ERROR\_IN\_REMOTE\_SYSTEM
- PARAMETER\_ERROR\_IN\_REMOTE\_SYSTEM |
| **message** | **String**        | **Required**      |                                                                                                                                                                                                                    |
:::

:::ExpandableHeading
### entitlements

The Entitlements endpoint takes an active access token, verifies that the session is still valid and returns a list of entitlements the user has. Please see the [sample code](https://github.com/PurplePublish/generic-entitlement-sample/blob/main/src/main/kotlin/com/sprylab/purple/example/model/UserEntitlement.kt) for all possible entitlement models.

**Path**: /v1/entitlements

**Method**: GET

**Request params:**

| Field           | Type       | Optional/Required |
| --------------- | ---------- | ----------------- |
| **appId**       | **String** | **Required**      |
| deviceId        | String     | Optional          |
| **accessToken** | **String** | **Required**      |

**Response body (JSON):**

**Success (HTTP Status 200)**

List of JSON objects. Each object must have a "type" field with one of the following options:

- universal
- content\_ids
- content\_properties
- content\_property\_values
- content\_tags
- publication\_id
- publication\_ids
- publication\_external\_ids
- publication\_properties

Please see the following documentation regarding the description of each type.

**Error: Invalid credentials (HTTP Status 403)**

| Field       | Type              | Optional/Required | Values                                                                                                                           |
| ----------- | ----------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| **code**    | **String (Enum)** | **Required**      | - UNKNOWN
- USER\_DEACTIVATED
- AUTHENTICATION\_ERROR
- SYSTEM\_ERROR\_IN\_REMOTE\_SYSTEM
- PARAMETER\_ERROR\_IN\_REMOTE\_SYSTEM |
| **message** | **String**        | **Required**      |                                                                                                                                  |
:::

### OAuth 2.0 with OpenID

For the OAuth 2.0 with OpenID entitlement you need an OAuth 2.0 / OpenID authentication server, e.g. Keycloak, and a server for with an endpoint to provide the user entitlements, like the username / password entitlement does.

:::ExpandableHeading
### entitlements

The Entitlements endpoint takes an active access token, verifies that the session is still valid and returns a list of entitlements the user has. Please see the [sample code](https://github.com/PurplePublish/generic-oauth-entitlement-sample/blob/main/src/main/kotlin/com/sprylab/purple/example/model/UserEntitlement.kt) for all possible entitlement models.

**Path**: /v1/entitlements

**Method**: GET

**Request headers:**

- Authorization
  - The access token is provided as a Bearer token

**Request params:**

| Field     | Type       | Optional/Required |
| --------- | ---------- | ----------------- |
| **appId** | **String** | **Required**      |
| deviceId  | String     | Optional          |

**Response body (JSON):**

**Success (HTTP Status 200)**

List of JSON objects. Each object must have a "type" field with one of the following options:

- universal
- content\_ids
- content\_properties
- content\_property\_values
- content\_tags
- publication\_id
- publication\_ids
- publication\_external\_ids
- publication\_properties

Please see the following documentation regarding the description of each type.

**Error: Invalid credentials (HTTP Status 403)**

| Field       | Type              | Optional/Required | Values                                                                                                                           |
| ----------- | ----------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| **code**    | **String (Enum)** | **Required**      | - UNKNOWN
- USER\_DEACTIVATED
- AUTHENTICATION\_ERROR
- SYSTEM\_ERROR\_IN\_REMOTE\_SYSTEM
- PARAMETER\_ERROR\_IN\_REMOTE\_SYSTEM |
| **message** | **String**        | **Required**      |                                                                                                                                  |
:::

## Entitlement types

:::ExpandableHeading
### Universal

**Identifier**: universal

**Description**: Unlocks all contents of the app
:::

:::ExpandableHeading
### Contents with IDs

**Identifier**: content\_ids

**Description**: Unlocks specific contents based on Purple Content IDs (id field of content in Catalog-API).
:::

:::ExpandableHeading
### Contents with custom properties

**Identifier**: content\_properties

**Description**: Unlocks specific contents based on the custom properties of the content
:::

:::ExpandableHeading
### Contents with custom property X

**Identifier**: content\_property\_values

**Description**: Unlocks specific contents based on the values of a custom property of the content
:::

:::ExpandableHeading
### Contents with Tags

**Identifier**: content\_tags

**Description**: Unlocks specific contents based on the tags
:::

:::ExpandableHeading
### Publication with ID

**Identifier**: publication\_date

**Description**: Unlocks the contents of a publication within a date range and optionally limited to week days within that range.
:::

:::ExpandableHeading
### Publications with IDs

**Identifier**: publication\_ids

**Description**: Unlocks the contents of multiple publications (optionally limited to week days)
:::

:::ExpandableHeading
### Publications with External IDs

**Identifier**: publication\_external\_ids

**Description**: Unlocks the contents of publications based on their external ID (optionally limited to week days)
:::

:::ExpandableHeading
### Publications with custom properties

**Identifier**: publication\_properties

**Description**: Unlocks the contents of publications based on their custom properties (optionally limited to week days)
:::

