# Configuring Ejabberd (XMPP Server) to use Akkoma for authentication

If you want to give your Akkoma users an XMPP (chat) account, you can configure [Ejabberd](https://github.com/processone/ejabberd) to use your Akkoma server for user authentication, automatically giving every local user an XMPP account.

In general, you just have to follow the configuration described at [https://docs.ejabberd.im/admin/configuration/authentication/#external-script](https://docs.ejabberd.im/admin/configuration/authentication/#external-script). Please read this section carefully. 

Copy the script below to suitable path on your system and set owner and permissions. Also do not forget adjusting `AKKOMA_HOST` and `AKKOMA_PORT`, if necessary.

```bash
cp akkoma_ejabberd_auth.py /etc/ejabberd/akkoma_ejabberd_auth.py
chown ejabberd /etc/ejabberd/akkoma_ejabberd_auth.py
chmod 700 /etc/ejabberd/akkoma_ejabberd_auth.py
```

Set external auth params in ejabberd.yaml file:

```bash
auth_method: [external]
extauth_program: "python3 /etc/ejabberd/akkoma_ejabberd_auth.py"
extauth_instances: 3
auth_use_cache: false
```

Restart / reload your ejabberd service.

After restarting your Ejabberd server, your users should now be able to connect with their Akkoma credentials.


```python
import sys
import struct
import http.client
from base64 import b64encode
import logging


AKKOMA_HOST = "127.0.0.1"
AKKOMA_PORT = "4000"
AUTH_ENDPOINT = "/api/v1/accounts/verify_credentials"
USER_ENDPOINT = "/api/v1/accounts"
LOGFILE = "/var/log/ejabberd/akkoma_auth.log"

logging.basicConfig(filename=LOGFILE, level=logging.INFO)


# Akkoma functions
def create_connection():
    return http.client.HTTPConnection(AKKOMA_HOST, AKKOMA_PORT)


def verify_credentials(user: str, password: str) -> bool:
    user_pass_b64 = b64encode("{}:{}".format(
        user, password).encode('utf-8')).decode("ascii")
    params = {}
    headers = {
        "Authorization": "Basic {}".format(user_pass_b64)
    }

    try:
        conn = create_connection()
        conn.request("GET", AUTH_ENDPOINT, params, headers)

        response = conn.getresponse()
        if response.status == 200:
            return True

        return False
    except Exception as e:
        logging.info("Can not connect: %s", str(e))
        return False


def does_user_exist(user: str) -> bool:
    conn = create_connection()
    conn.request("GET", "{}/{}".format(USER_ENDPOINT, user))

    response = conn.getresponse()
    if response.status == 200:
        return True

    return False


def auth(username: str, server: str, password: str) -> bool:
    return verify_credentials(username, password)


def isuser(username, server):
    return does_user_exist(username)


def read():
    (pkt_size,) = struct.unpack('>H', bytes(sys.stdin.read(2), encoding='utf8'))
    pkt = sys.stdin.read(pkt_size)
    cmd = pkt.split(':')[0]
    if cmd == 'auth':
        username, server, password = pkt.split(':', 3)[1:]
        write(auth(username, server, password))
    elif cmd == 'isuser':
        username, server = pkt.split(':', 2)[1:]
        write(isuser(username, server))
    elif cmd == 'setpass':
        # u, s, p = pkt.split(':', 3)[1:]
        write(False)
    elif cmd == 'tryregister':
        # u, s, p = pkt.split(':', 3)[1:]
        write(False)
    elif cmd == 'removeuser':
        # u, s = pkt.split(':', 2)[1:]
        write(False)
    elif cmd == 'removeuser3':
        # u, s, p = pkt.split(':', 3)[1:]
        write(False)
    else:
        write(False)


def write(result):
    if result:
        sys.stdout.write('\x00\x02\x00\x01')
    else:
        sys.stdout.write('\x00\x02\x00\x00')
    sys.stdout.flush()


if __name__ == "__main__":
    logging.info("Starting akkoma ejabberd auth daemon...")
    while True:
        try:
            read()
        except Exception as e:
            logging.info(
                "Error while processing data from ejabberd %s", str(e))
            pass

```
