API Authorization and Authentication
Tags: ai, llm, rag, vectordatabase
Published: 2024-01-04 20:31:28

[![](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/54_API_Authorization_and_Authentication.ipynb)

[txtai](https://github.com/neuml/txtai) is an all-in-one embeddings database for semantic search, LLM orchestration and language model workflows. txtai can run in Python, with YAML configuration and through an API service.

The default API service implementation runs without any security. This may be OK for a local prototype or if it's run on a small internal network. But in most cases, additional security measures should be taken.

This article will demonstrate how to add authorization, authentication and middleware dependencies to a txtai API service.

# Install dependencies

Install `txtai` and all dependencies. Since this article uses the API, we need to install the api extras package.

```
# Install txtai
pip install txtai[api]
```

# Create an API Service

For this example, we'll load an existing txtai index from the Hugging Face Hub.

```yaml
cloud:
  provider: huggingface-hub
  container: neuml/txtai-intro

embeddings:
```

Next, we'll generate a test token to use for this article.

```python
import uuid
str(uuid.uuid5(uuid.NAMESPACE_DNS, "TokenTest"))
```

```
'edd590d3-bfab-5425-8a85-79b01e3127ee'
```

txtai has a default API token authorization method built-in. We'll set a token and start the service.

**It's important to note that this service is running via HTTP as this is only for demonstration purposes. HTTPS must be added either with a proxy service like NGINX or by passing a SSL cert to Uvicorn. See [this link](https://neuml.github.io/txtai/api/security/) for more.**

```
CONFIG=config.yml TOKEN=`echo -n 'edd590d3-bfab-5425-8a85-79b01e3127ee' | sha256sum | head -c 64` uvicorn "txtai.api:app" &> api.log &
sleep 60
```

# Connect to Service

First, we'll try a request with no token to see what happens.

```
curl -X GET -I 'http://localhost:8000/search?query=feel+good+story&limit=1'
```

```
HTTP/1.1 401 Unauthorized
date: Thu, 04 Jan 2024 15:08:38 GMT
server: uvicorn
content-length: 40
content-type: application/json
```

As expected, we received a HTTP 401 saying the request is not authorized.

Now let's try an invalid token.

```
curl -X GET -I 'http://localhost:8000/search?query=feel+good+story&limit=1' -H 'Authorization: Bearer junk'
```

```
HTTP/1.1 401 Unauthorized
date: Thu, 04 Jan 2024 15:08:38 GMT
server: uvicorn
content-length: 40
content-type: application/json
```

Once again, the request is rejected.

Let's try again, this time passing a valid API token.

```
curl -X GET 'http://localhost:8000/search?query=feel+good+story&limit=1' -H 'Authorization: Bearer edd590d3-bfab-5425-8a85-79b01e3127ee'
```

```
[{"id":"4","text":"Maine man wins $1M from $25 lottery ticket","score":0.08329025655984879}]
```

This time we get search results!

# Dependencies

Next, let's add a custom dependency to test out authentication. A dependency could integrate with external identity providers to validate user credentials such as OAuth, Active Directory, LDAP or another identity management service.

For this simple example, we'll validate user credentials using basic HTTP authentication. The code below checks if a specific username and password are provided. It's based on [this FastAPI example](https://fastapi.tiangolo.com/advanced/security/http-basic-auth/).

```python
import secrets

from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials

security = HTTPBasic()


class Authentication:
    def __call__(self, credentials: HTTPBasicCredentials = Depends(security)):
        user = credentials.username.encode("utf8")
        validuser = secrets.compare_digest(user, b"txtai")

        password = credentials.password.encode("utf8")
        validpassword = secrets.compare_digest(password, b"theembeddingsdb")

        if not (validuser and validpassword):
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Incorrect user or password",
                headers={"WWW-Authenticate": "Basic"},
            )

        return credentials.username
```

Now let's restart the application and add this dependency in.

**Once again, same note as above, this demonstration uses HTTP. Real use-cases must use HTTPS.**

```
killall -9 uvicorn
CONFIG=config.yml DEPENDENCIES=authentication.Authentication uvicorn "txtai.api:app" &> api.log &
sleep 30
```

```
curl -X GET -I 'http://localhost:8000/search?query=feel+good+story&limit=1'
```

```
HTTP/1.1 401 Unauthorized
date: Thu, 04 Jan 2024 15:09:10 GMT
server: uvicorn
www-authenticate: Basic
content-length: 30
content-type: application/json
```

The request is rejected as expected. Next let's try an invalid username/password.

```
curl -X GET -I 'http://localhost:8000/search?query=feel+good+story&limit=1' -H "Authorization: Basic junk"
```

```
HTTP/1.1 401 Unauthorized
date: Thu, 04 Jan 2024 15:09:10 GMT
server: uvicorn
www-authenticate: Basic
content-length: 47
content-type: application/json
```

Once again, the request is rejected.

Now we'll add the expected username/password to the request. [HTTP basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) simply concats the username-password separated by a colon and then base64 encodes it.

```
echo -n txtai:theembeddingsdb | base64
```

```
dHh0YWk6dGhlZW1iZWRkaW5nc2Ri
```

```
curl -X GET 'http://localhost:8000/search?query=feel+good+story&limit=1' -H "Authorization: Basic dHh0YWk6dGhlZW1iZWRkaW5nc2Ri"
```

```
[{"id":"4","text":"Maine man wins $1M from $25 lottery ticket","score":0.08329025655984879}]
```

Now that we have the correct username/password, a response is returned!

# Wrapping up

This article introduced how to add authorization, authentication and middleware dependencies to a txtai API service. As noted multiple times, ensure that HTTPS is enabled when using this in production environments.

For more advanced authentication methods, check out the [FastAPI security documentation](https://fastapi.tiangolo.com/tutorial/security/).