From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: mail.toke.dk; spf=pass (mailfrom) smtp.mailfrom=irif.fr (client-ip=2001:660:3301:8000::1:2; helo=korolev.univ-paris7.fr; envelope-from=jch@irif.fr; receiver=) Received: from korolev.univ-paris7.fr (korolev.univ-paris7.fr [IPv6:2001:660:3301:8000::1:2]) by mail.toke.dk (Postfix) with ESMTPS id CF8D88DA5DA for ; Sat, 30 Oct 2021 02:09:04 +0200 (CEST) Received: from mailhub.math.univ-paris-diderot.fr (mailhub.math.univ-paris-diderot.fr [81.194.30.253]) by korolev.univ-paris7.fr (8.14.4/8.14.4/relay1/82085) with ESMTP id 19U094uT031957 for ; Sat, 30 Oct 2021 02:09:04 +0200 Received: from mailhub.math.univ-paris-diderot.fr (localhost [127.0.0.1]) by mailhub.math.univ-paris-diderot.fr (Postfix) with ESMTP id 82EACD4475 for ; Sat, 30 Oct 2021 02:09:04 +0200 (CEST) X-Virus-Scanned: amavisd-new at math.univ-paris-diderot.fr Received: from mailhub.math.univ-paris-diderot.fr ([127.0.0.1]) by mailhub.math.univ-paris-diderot.fr (mailhub.math.univ-paris-diderot.fr [127.0.0.1]) (amavisd-new, port 10023) with ESMTP id ZeUf58cp6TYS for ; Sat, 30 Oct 2021 02:09:02 +0200 (CEST) Received: from pirx.irif.fr (unknown [78.194.40.74]) (Authenticated sender: jch) by mailhub.math.univ-paris-diderot.fr (Postfix) with ESMTPSA id B7AEBD4473 for ; Sat, 30 Oct 2021 02:09:02 +0200 (CEST) Date: Sat, 30 Oct 2021 02:09:02 +0200 Message-ID: <87tugzwezl.wl-jch@irif.fr> From: Juliusz Chroboczek To: galene@lists.galene.org User-Agent: Wanderlust/2.15.9 (Almost Unreal) Emacs/27.1 Mule/6.0 MIME-Version: 1.0 (generated by SEMI-EPG 1.14.7 - "Harue") Content-Type: text/plain; charset=US-ASCII X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.2.7 (korolev.univ-paris7.fr [194.254.61.138]); Sat, 30 Oct 2021 02:09:04 +0200 (CEST) X-Miltered: at korolev with ID 617C8D20.000 by Joe's j-chkmail (http : // j-chkmail dot ensmp dot fr)! X-j-chkmail-Enveloppe: 617C8D20.000 from mailhub.math.univ-paris-diderot.fr/mailhub.math.univ-paris-diderot.fr/null/mailhub.math.univ-paris-diderot.fr/ X-j-chkmail-Score: MSGID : 617C8D20.000 on korolev.univ-paris7.fr : j-chkmail score : . : R=. U=. O=. B=0.000 -> S=0.000 X-j-chkmail-Status: Ham Message-ID-Hash: AN5ZMXBKSSM6EKRSEKB43OAMP5R6O3TD X-Message-ID-Hash: AN5ZMXBKSSM6EKRSEKB43OAMP5R6O3TD X-MailFrom: jch@irif.fr X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.4 Precedence: list Subject: [Galene] Token authentication List-Id: =?utf-8?q?Gal=C3=A8ne_videoconferencing_server_discussion_list?= Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Hi, I think I've got a working prototype of token authentication for Galene. There are still some features missing (for example authentication for recorded files, or finer The configuration file looks like this: { "authServer": "http://localhost:1234", "authKeys": [{ "alg": "HS256", "k": "AQIDBA==" }] } The authServer field is the URL of the authentication server; HTTP is supported, but not recommended -- it's not a good idea to carry passwords in the clear. The authKeys field is an array of (public) keys in JWK format; multiple keys are allowed if they have distinct "kid" fields. Right now, only HS256, HS384 and HS512 are supported, I'll look at public key crypto at some later date. The protocol is as follows. When a client wishes to join a group, it makes a POST request to the address given by authServer with a JSON object that looks like this: {"group": "groupname", "username": "john", "password": "topsecret"} The server receives the request, checks that the username/password is correct for the given group, then builds the following claim: {"sub": "john", "aud": "groupname", "permissions": {"present": true}, "iat": now, "exp": now+30s} It then wraps it in a signed JWT which it sends in reply to the POST request. The client retrieves the encoded token, and dumps it into the "token" field of the "join" request. The server checks the signature of the token, and, if correct, grants the claimed permissions. What follows is a sample authorisation server. #!/usr/bin/python3 import json import jwt from datetime import datetime, timezone, timedelta from aiohttp import web import aiohttp_cors secret = b"\1\2\3\4" users = { "john": "secret", "peter": "secret2", } async def handler(request): body = await request.json() if not ("username" in body and "group" in body and "password" in body): return web.HTTPBadRequest() username = body["username"] if not (username in users) or users[username] != body["password"]: return web.HTTPUnauthorized() now = datetime.now(tz=timezone.utc) token = { "sub": username, "aud": body["group"], "permissions": {"present": True}, "iat": now, "exp": now + timedelta(seconds=30), } signed = jwt.encode(token, secret, algorithm="HS256") return web.Response( headers={"Content-Type": "aplication/jwt"}, body=signed, ) app = web.Application() route = app.router.add_route("POST", "/", handler) cors = aiohttp_cors.setup(app, defaults={ "*": aiohttp_cors.ResourceOptions( expose_headers="*", allow_headers="*", ) }) cors.add(route) web.run_app(app, port=1234)