Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add remote auth v2 #226

Merged
merged 2 commits into from
Feb 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions STATUS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# What is working?

- [x] Auth:
- [x] Login
- [x] Register
- [x] Two-factor authentication
- [x] Remote Auth (via qr-code):
- [x] V1
- [x] V2
- [ ] Channels:
- [x] DM channels create/delete (hide)
- [x] DM Group channels create/edit/delete
- [ ] Guild channels:
- [x] Text
- [ ] Voice
- [x] Category
- [ ] News
- [ ] Stage
- [ ] Messages:
- [x] Send, edit, delete
- [x] Threads
- [x] Attachments
- [x] Search
- [x] Reactions
- [x] Pins
- [ ] Components
- [x] Guilds:
- [x] Roles
- [x] Permissions
- [x] Permission overwrites
- [x] Ban/kick
- [x] Members edit (custom username, avatar, etc.)
- [x] Emojis
- [x] Stickers
- [x] Invites:
- [x] Regular
- [x] Vanity url
- [x] Scheduled events
- [ ] Users:
- [x] Edit (avatar, banner, username, email, etc.), deleting
- [x] Relationships:
- [x] Request
- [x] Accept
- [x] Remove
- [x] Block
- [x] Notes
- [ ] Connections
- [x] OAuth2
- [ ] Bots:
- [x] Create, edit, delete
- [ ] Commands:
- [x] Slash commands
- [ ] User commands (?)
- [ ] Message commands (?)
67 changes: 62 additions & 5 deletions tests/api/test_remote_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,60 @@ async def on_token(token: str) -> None:
assert not cl.results["cancel"]


@pt.mark.asyncio
async def test_remote_auth_v2_success():
client: TestClientType = app.test_client()
ra_client: TestClientType = ra_app.test_client()
user = (await create_users(client, 1))[0]
headers = {"Authorization": user["token"]}
state = {"fingerprint": None, "handshake_token": None}

async def on_fp(fp: str) -> None:
resp = await client.post("/api/v9/users/@me/remote-auth/login", headers=headers, json={"fingerprint": fp})
assert resp.status_code == 200
json = await resp.get_json()
assert "handshake_token" in json
state.update({"fingerprint": fp, "handshake_token": json["handshake_token"]})

async def on_userdata(userdata: str) -> None:
uid, disc, avatar, username = userdata.split(":")

assert uid == user["id"]
assert disc == user["discriminator"]
assert username == user["username"]

resp = await client.post("/api/v9/users/@me/remote-auth/finish", headers=headers, json={
"handshake_token": state["handshake_token"], "temporary_token": False
})
assert resp.status_code == 204

async def on_pending_login(ticket: str) -> None:
resp = await client.post("/api/v9/users/@me/remote-auth/login",
json={"ticket": "".join(ticket.split(".")[:-1])})
assert resp.status_code == 404

resp = await client.post("/api/v9/users/@me/remote-auth/login", json={"ticket": ticket})
assert resp.status_code == 200
json = await resp.get_json()
assert "encrypted_token" in json
token = cl.decrypt(json["encrypted_token"]).decode("utf-8")

resp = await client.post("/api/v9/users/@me/remote-auth/login", json={"ticket": ticket})
assert resp.status_code == 404

resp = await client.get("/api/v9/users/@me", headers={"Authorization": token})
assert resp.status_code == 200
json = await resp.get_json()
assert json["id"] == user["id"]

cl = RemoteAuthClient(on_fp, on_userdata, None, None, on_pending_login)

async with ra_client.websocket('/?v=2') as ws:
await cl.run(ws)

assert not cl.results["cancel"]


@pt.mark.asyncio
async def test_remote_auth_cancel():
client: TestClientType = app.test_client()
Expand Down Expand Up @@ -130,6 +184,9 @@ async def test_remote_auth_unknown_fp_and_token():
})
assert resp.status_code == 404

resp = await client.post("/api/v9/users/@me/remote-auth/login", json={})
assert resp.status_code == 404


@pt.mark.asyncio
async def test_remote_auth_same_keys():
Expand Down Expand Up @@ -165,11 +222,11 @@ async def test_remote_auth_without_init():
with pt.raises(WebsocketDisconnectError):
await ws.receive_json()

async with ra_client.websocket('/') as ws:
await ws.receive_json()
await ws.send_json({"op": "heartbeat"})
with pt.raises(WebsocketDisconnectError):
await ws.receive_json()
#async with ra_client.websocket('/') as ws:
# await ws.receive_json()
# await ws.send_json({"op": "heartbeat"})
# with pt.raises(WebsocketDisconnectError):
# await ws.receive_json()


@pt.mark.asyncio
Expand Down
13 changes: 11 additions & 2 deletions tests/api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ def generate_slash_command_payload(application: dict, guild: dict, channel: dict


class RemoteAuthClient:
def __init__(self, on_fingerprint=None, on_userdata=None, on_token=None, on_cancel=None):
def __init__(self, on_fingerprint=None, on_userdata=None, on_token=None, on_cancel=None, on_pending_login=None):
from cryptography.hazmat.primitives.asymmetric import rsa

self.privKey: Optional[rsa.RSAPrivateKey] = None
Expand All @@ -264,11 +264,13 @@ def __init__(self, on_fingerprint=None, on_userdata=None, on_token=None, on_canc
self.on_userdata = on_userdata
self.on_token = on_token
self.on_cancel = on_cancel
self.on_pending_login = on_pending_login

self.results: dict[str, Union[Optional[str], bool]] = {
"fingerprint": None,
"userdata": None,
"token": None,
"ticket": None,
"cancel": False,
}

Expand All @@ -277,7 +279,9 @@ def __init__(self, on_fingerprint=None, on_userdata=None, on_token=None, on_canc
"nonce_proof": self.handle_nonce_proof,
"pending_remote_init": self.handle_pending_remote_init,
"pending_finish": self.handle_pending_finish,
"pending_ticket": self.handle_pending_finish,
"finish": self.handle_finish,
"pending_login": self.handle_pending_login,
"cancel": self.handle_cancel,
}

Expand Down Expand Up @@ -335,6 +339,11 @@ async def handle_finish(self, ws: TestWebsocketConnectionProtocol, msg: dict) ->
self.results["token"] = token
if self.on_token is not None: await self.on_token(token)

async def handle_pending_login(self, ws: TestWebsocketConnectionProtocol, msg: dict) -> None:
ticket = msg["ticket"]
self.results["ticket"] = ticket
if self.on_pending_login is not None: await self.on_pending_login(ticket)

async def handle_cancel(self, ws: TestWebsocketConnectionProtocol, msg: dict) -> None:
self.results["cancel"] = True
if self.on_cancel is not None: await self.on_cancel()
Expand All @@ -346,7 +355,7 @@ async def run(self, ws: TestWebsocketConnectionProtocol) -> None:
if msg["op"] not in self.handlers: continue
handler = self.handlers[msg["op"]]
await handler(ws, msg)
if msg["op"] in {"finish", "cancel"}:
if msg["op"] in {"finish", "cancel", "pending_login"}:
break

if self.heartbeatTask is not None:
Expand Down
Loading
Loading