Remerge of hashtag following (#341)
[akkoma] / docs / docs / configuration / integrations / howto_ejabberd.md
1 # Configuring Ejabberd (XMPP Server) to use Akkoma for authentication
2
3 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.
4
5 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.
6
7 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.
8
9 ```bash
10 cp akkoma_ejabberd_auth.py /etc/ejabberd/akkoma_ejabberd_auth.py
11 chown ejabberd /etc/ejabberd/akkoma_ejabberd_auth.py
12 chmod 700 /etc/ejabberd/akkoma_ejabberd_auth.py
13 ```
14
15 Set external auth params in ejabberd.yaml file:
16
17 ```bash
18 auth_method: [external]
19 extauth_program: "python3 /etc/ejabberd/akkoma_ejabberd_auth.py"
20 extauth_instances: 3
21 auth_use_cache: false
22 ```
23
24 Restart / reload your ejabberd service.
25
26 After restarting your Ejabberd server, your users should now be able to connect with their Akkoma credentials.
27
28
29 ```python
30 import sys
31 import struct
32 import http.client
33 from base64 import b64encode
34 import logging
35
36
37 AKKOMA_HOST = "127.0.0.1"
38 AKKOMA_PORT = "4000"
39 AUTH_ENDPOINT = "/api/v1/accounts/verify_credentials"
40 USER_ENDPOINT = "/api/v1/accounts"
41 LOGFILE = "/var/log/ejabberd/akkoma_auth.log"
42
43 logging.basicConfig(filename=LOGFILE, level=logging.INFO)
44
45
46 # Akkoma functions
47 def create_connection():
48 return http.client.HTTPConnection(AKKOMA_HOST, AKKOMA_PORT)
49
50
51 def verify_credentials(user: str, password: str) -> bool:
52 user_pass_b64 = b64encode("{}:{}".format(
53 user, password).encode('utf-8')).decode("ascii")
54 params = {}
55 headers = {
56 "Authorization": "Basic {}".format(user_pass_b64)
57 }
58
59 try:
60 conn = create_connection()
61 conn.request("GET", AUTH_ENDPOINT, params, headers)
62
63 response = conn.getresponse()
64 if response.status == 200:
65 return True
66
67 return False
68 except Exception as e:
69 logging.info("Can not connect: %s", str(e))
70 return False
71
72
73 def does_user_exist(user: str) -> bool:
74 conn = create_connection()
75 conn.request("GET", "{}/{}".format(USER_ENDPOINT, user))
76
77 response = conn.getresponse()
78 if response.status == 200:
79 return True
80
81 return False
82
83
84 def auth(username: str, server: str, password: str) -> bool:
85 return verify_credentials(username, password)
86
87
88 def isuser(username, server):
89 return does_user_exist(username)
90
91
92 def read():
93 (pkt_size,) = struct.unpack('>H', bytes(sys.stdin.read(2), encoding='utf8'))
94 pkt = sys.stdin.read(pkt_size)
95 cmd = pkt.split(':')[0]
96 if cmd == 'auth':
97 username, server, password = pkt.split(':', 3)[1:]
98 write(auth(username, server, password))
99 elif cmd == 'isuser':
100 username, server = pkt.split(':', 2)[1:]
101 write(isuser(username, server))
102 elif cmd == 'setpass':
103 # u, s, p = pkt.split(':', 3)[1:]
104 write(False)
105 elif cmd == 'tryregister':
106 # u, s, p = pkt.split(':', 3)[1:]
107 write(False)
108 elif cmd == 'removeuser':
109 # u, s = pkt.split(':', 2)[1:]
110 write(False)
111 elif cmd == 'removeuser3':
112 # u, s, p = pkt.split(':', 3)[1:]
113 write(False)
114 else:
115 write(False)
116
117
118 def write(result):
119 if result:
120 sys.stdout.write('\x00\x02\x00\x01')
121 else:
122 sys.stdout.write('\x00\x02\x00\x00')
123 sys.stdout.flush()
124
125
126 if __name__ == "__main__":
127 logging.info("Starting akkoma ejabberd auth daemon...")
128 while True:
129 try:
130 read()
131 except Exception as e:
132 logging.info(
133 "Error while processing data from ejabberd %s", str(e))
134 pass
135
136 ```