Skip to content

Commit

Permalink
Merge pull request #226 from yepcord/feat/remote-auth-v2
Browse files Browse the repository at this point in the history
add remote auth v2
  • Loading branch information
RuslanUC authored Feb 24, 2024
2 parents 7c37315 + 28ff748 commit 3697054
Show file tree
Hide file tree
Showing 9 changed files with 295 additions and 110 deletions.
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

0 comments on commit 3697054

Please sign in to comment.