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

Auth rework #512

Merged
merged 17 commits into from
Aug 26, 2023
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
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ Copy `.env.example` to `.env` in the root of the repo.

These will be automatically picked up and used by the game.

### Auth0 Setup
### Auth0 Setup (optional)

You will need to set up an Auth0 tenant in order to run Creature Chess locally.
You can optionally configure Auth0 to manage users and authentication.

See "Environment variables" above for info on how to store them.
You will need to set up an Auth0 tenant and store some environment variables.

- Set up a [machine to machine app](https://auth0.com/docs/applications/set-up-an-application/register-machine-to-machine-applications)

Expand Down
9 changes: 6 additions & 3 deletions apps/server-game/src/external/auth0.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { AUTH0_ENABLED } from "@creature-chess/auth-web/auth0/config";
import { ManagementClient } from "auth0";

import { UserAppMetadata } from "@creature-chess/auth-server";
export const createManagementClient = (): ManagementClient => {
if (!AUTH0_ENABLED) {
return null as any;
}

export const createManagementClient = (): ManagementClient<UserAppMetadata> => {
const {
AUTH0_DOMAIN,
AUTH0_MACHINE_TO_MACHINE_CLIENT_ID,
Expand All @@ -17,7 +20,7 @@ export const createManagementClient = (): ManagementClient<UserAppMetadata> => {
throw Error("No Auth0 configuration found");
}

const client = new ManagementClient<UserAppMetadata>({
const client = new ManagementClient({
domain: AUTH0_DOMAIN,
clientId: AUTH0_MACHINE_TO_MACHINE_CLIENT_ID,
clientSecret: AUTH0_MANAGEMENT_CLIENT_SECRET,
Expand Down
12 changes: 9 additions & 3 deletions apps/server-game/src/external/bots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@ import { LobbyPlayer } from "@creature-chess/models";
export const getBots = async (database: DatabaseConnection, count: number) => {
const output: { player: LobbyPlayer; personality: BotPersonality }[] = [];

const bots = await database.bot.getLeastPlayedBots(count);
for (const { id, nickname, ambition, composure, vision } of bots!) {
const bots = await database.prisma.bots.findMany({
take: count,
orderBy: {
games_played: "asc",
},
});

for (const { id, nickname, ambition, composure, vision } of bots) {
// get a random picture from one to 20 - temporary
const picture = Math.floor(Math.random() * 20) + 1;

const player = {
id: (id + 1) * -1,
id: "bot-" + id,
name: `[BOT] ${nickname}`,
profile: {
title: null,
Expand Down
4 changes: 2 additions & 2 deletions apps/server-game/src/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ export class Game {
this.gamemode.start(entities);
}

public canJoinGame(playerId: number) {
const existing = this.gamemode.getPlayerById(playerId.toString());
public canJoinGame(playerId: string) {
const existing = this.gamemode.getPlayerById(playerId);

if (!existing) {
return false;
Expand Down
41 changes: 39 additions & 2 deletions apps/server-game/src/handshake/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,49 @@ export const onHandshakeSuccess = (

logger.info("Listening for successful handshakes - inner A");

handshakeListener(deps, async (socket, { idToken }) => {
handshakeListener(deps, async (socket, request) => {
try {
logger.info("Authenticating new handshake", {
meta: { socketId: socket.id },
});

const user = await authenticate(authClient, database, idToken);
if (request.type === "guest") {
const guest = await database.prisma.guests.findFirst({
where: {
token: request.data.accessToken,
expires_at: {
gte: new Date(),
},
}
});

if (!guest) {
failHandshake(socket, { error: { type: "authentication" } });
return;
}

logger.info(`[socket ${socket.id}] Handshake successful for guest`);

successHandshake(socket);

const guestSocket = socket as AuthenticatedSocket;

guestSocket.data = {
type: "guest",
id: guest.id,
nickname: `Guest ${guest.id}`,
profile: {
picture: 1,
title: null
}
};

onReceive(guestSocket);

return;
}

const user = await authenticate(authClient, database, request.data.accessToken);

if (!user.registered) {
failHandshake(socket, { error: { type: "not_registered" } });
Expand All @@ -42,6 +78,7 @@ export const onHandshakeSuccess = (
const authenticatedSocket = socket as AuthenticatedSocket;

authenticatedSocket.data = {
type: "player",
id: user.id,
nickname: user.nickname,
profile: user.profile,
Expand Down
4 changes: 3 additions & 1 deletion apps/server-game/src/handshake/listener.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Socket } from "socket.io";

import { HandshakeRequest } from "@creature-chess/networking/handshake";

import { logger } from "../log";
import { HandshakeListenerDependencies, HandshakeRequest } from "./types";
import { HandshakeListenerDependencies } from "./types";

/**
* Attaches to the socket server and raises incoming handshake requests.
Expand Down
5 changes: 1 addition & 4 deletions apps/server-game/src/handshake/types.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { ManagementClient } from "auth0";
import { Server } from "socket.io";

import { UserAppMetadata } from "@creature-chess/auth-server";
import { DatabaseConnection } from "@creature-chess/data";

export type HandshakeListenerDependencies = {
io: Server;
authClient: ManagementClient<UserAppMetadata>;
authClient: ManagementClient;
database: DatabaseConnection;
};

export type HandshakeRequest = { idToken: string };
22 changes: 16 additions & 6 deletions apps/server-game/src/lobby.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export class Lobby {
return this.options.maxPlayers - this.members.length;
}

public isInLobby(playerId: number) {
public isInLobby(playerId: string) {
return this.members.some((m) => m.player.id === playerId);
}

Expand All @@ -50,12 +50,20 @@ export class Lobby {
const existing = this.members.find((m) => m.player.id === socket.data.id);
if (existing) {
existing.socket?.disconnect(true);

existing.socket = socket;
existing.registry = registry;
} else {
const defaultProfile: PlayerProfile = {
picture: 1,
title: null
};

const newMember = {
player: {
id: socket.data.id,
name: socket.data.nickname!,
profile: socket.data.profile!,
profile: socket.data.profile ?? defaultProfile,
},
socket,
registry,
Expand All @@ -65,11 +73,13 @@ export class Lobby {
this.notifyOthers(newMember);
}

this.sendConnected(registry);
setTimeout(() => {
this.sendConnected(registry);

if (this.members.length === this.options.maxPlayers) {
this.start();
}
if (this.members.length === this.options.maxPlayers) {
this.start();
}
}, 500);
}

private start = () => {
Expand Down
6 changes: 4 additions & 2 deletions apps/server-game/src/player/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type Parameters = {
getPlayers: () => PlayerListPlayer[];
};

export const playerNetworking = function* (
export const playerNetworking = function*(
socket: Socket,
{ getRoundInfo, getPlayers }: Parameters
) {
Expand All @@ -38,7 +38,7 @@ export const playerNetworking = function* (

yield* setPacketRegistries(registries);

const teardown = function* () {
const teardown = function*() {
socket!.removeAllListeners();
socket!.disconnect();

Expand All @@ -47,6 +47,8 @@ export const playerNetworking = function* (

yield put(PlayerCommands.setSpectatingIdCommand(null));

yield delay(500);

registries.outgoing.send("gameConnected", {
game: getRoundInfo(),
players: getPlayers(),
Expand Down
15 changes: 13 additions & 2 deletions apps/server-game/src/player/socket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,22 @@ import { DefaultEventsMap } from "socket.io/dist/typed-events";

import { PlayerProfile } from "@creature-chess/models";

type AuthenticatedSocketData = {
id: number;
type GuestSocketData = {
type: "guest";
id: string;
nickname: string;
profile: PlayerProfile;
};

type PlayerSocketData = {
type: "player";
id: string;
nickname: string | null;
profile: PlayerProfile | null;
};

type AuthenticatedSocketData = GuestSocketData | PlayerSocketData;

export type AuthenticatedSocket = Socket<
DefaultEventsMap,
DefaultEventsMap,
Expand Down
Loading
Loading