Skip to content

Commit 51ddb2a

Browse files
authored
FCE-2044 validate credentials at client creation (#271)
## Description Adds credentials validation to the preferred way of creating the client. There is also a separate checkCredentials method if someone needs to create the client synchronously. ## Motivation and Context The SDK client should fail fast when misconfigured. ## Documentation impact - [ ] Documentation update required - [x] Documentation updated [in another PR](fishjam-cloud/documentation#259) - [ ] No documentation update required ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
1 parent 652b8d3 commit 51ddb2a

11 files changed

Lines changed: 152 additions & 21 deletions

File tree

.github/workflows/static.yaml

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,6 @@ jobs:
3939
- name: Check typing
4040
run: yarn typecheck
4141

42-
- name: Generate OpenAPI reference 📸
43-
run: FISHJAM_ID="openapi" FISHJAM_MANAGEMENT_TOKEN="foo" yarn gen:openapi ref.yaml
44-
45-
- name: Compare OpenAPI reference 🔦
46-
run: |
47-
RM_PATH=examples/room-manager
48-
if ! cmp --silent "$RM_PATH/openapi.yaml" "$RM_PATH/ref.yaml"; then
49-
echo "OpenAPI spec is out of date. Please run 'yarn gen:openapi' and commit the changes."
50-
exit 1
51-
fi
52-
5342
- name: Check TypeDoc generation
5443
run: yarn docs
5544

examples/multimodal/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const fishjamConfig = {
1212
managementToken: process.env.FISHJAM_TOKEN,
1313
};
1414

15-
const fishjam = new FishjamService(fishjamConfig);
15+
const fishjam = await FishjamService.create(fishjamConfig);
1616

1717
new MultimodalService(fishjamConfig, process.env.GEMINI_API_KEY);
1818

examples/multimodal/src/service/fishjam.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@ export class FishjamService {
44
roomId?: RoomId;
55
fishjam: FishjamClient;
66

7-
constructor(config: FishjamConfig) {
8-
this.fishjam = new FishjamClient(config);
7+
private constructor(fishjam: FishjamClient) {
8+
this.fishjam = fishjam;
9+
}
10+
11+
static async create(config: FishjamConfig): Promise<FishjamService> {
12+
const fishjam = await FishjamClient.create(config);
13+
return new FishjamService(fishjam);
914
}
1015

1116
async createPeer() {

examples/room-manager/src/plugins/fishjam.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export const fishjamPlugin = fastifyPlugin(async (fastify: FastifyInstance): Pro
3434
throw new Error('The `fishjamPlugin` plugin has already been registered.');
3535
}
3636

37-
const fishjamClient = new FishjamClient({
37+
const fishjamClient = await FishjamClient.create({
3838
fishjamId: fastify.config.FISHJAM_ID,
3939
managementToken: fastify.config.FISHJAM_MANAGEMENT_TOKEN,
4040
});

examples/selective-subscription/backend/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { peerController } from './controllers/peers';
44
import { notificationsController } from './controllers/notifications';
55
import { FishjamService } from './service/fishjam';
66

7-
const fishjam = new FishjamService({
7+
const fishjam = await FishjamService.create({
88
fishjamId: process.env.FISHJAM_ID!,
99
managementToken: process.env.FISHJAM_TOKEN!,
1010
});

examples/selective-subscription/backend/src/service/fishjam.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ export class FishjamService extends EventTarget {
1212
roomId?: RoomId;
1313
fishjam: FishjamClient;
1414

15-
constructor(config: FishjamConfig) {
15+
private constructor(fishjam: FishjamClient, config: FishjamConfig) {
1616
super();
17-
this.fishjam = new FishjamClient(config);
17+
this.fishjam = fishjam;
1818
const notifier = new FishjamWSNotifier(
1919
config,
2020
() => {},
@@ -27,6 +27,11 @@ export class FishjamService extends EventTarget {
2727
notifier.on('trackRemoved', (msg) => this.emit('trackRemoved', msg));
2828
}
2929

30+
static async create(config: FishjamConfig): Promise<FishjamService> {
31+
const fishjam = await FishjamClient.create(config);
32+
return new FishjamService(fishjam, config);
33+
}
34+
3035
async createPeer() {
3136
try {
3237
return await this.makePeer();

examples/transcription/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const fishjamConfig = {
1212
managementToken: process.env.FISHJAM_TOKEN,
1313
};
1414

15-
const fishjam = new FishjamService(fishjamConfig);
15+
const fishjam = await FishjamService.create(fishjamConfig);
1616

1717
new TranscriptionService(fishjamConfig, process.env.GEMINI_API_KEY);
1818

examples/transcription/src/service/fishjam.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@ export class FishjamService {
44
roomId?: RoomId;
55
fishjam: FishjamClient;
66

7-
constructor(config: FishjamConfig) {
8-
this.fishjam = new FishjamClient(config);
7+
private constructor(fishjam: FishjamClient) {
8+
this.fishjam = fishjam;
9+
}
10+
11+
static async create(config: FishjamConfig): Promise<FishjamService> {
12+
const fishjam = await FishjamClient.create(config);
13+
return new FishjamService(fishjam);
914
}
1015

1116
async createPeer() {

packages/js-server-sdk/src/client.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
} from '@fishjam-cloud/fishjam-openapi';
1313
import type { AgentCallbacks, FishjamConfig, PeerId, Room, RoomId, Peer } from './types';
1414
import { mapException } from './exceptions/mapper';
15+
import { InvalidFishjamCredentialsException } from './exceptions';
1516
import { getFishjamUrl } from './utils';
1617
import { FishjamAgent, TrackId } from './agent';
1718
import packageJson from '../package.json';
@@ -32,6 +33,10 @@ export class FishjamClient {
3233
/**
3334
* Create new instance of Fishjam Client.
3435
*
36+
* Does not verify credentials against the backend — use
37+
* {@link FishjamClient.create} or call
38+
* {@link FishjamClient.checkCredentials} afterwards for that.
39+
*
3540
* Example usage:
3641
* ```
3742
* const fishjamClient = new FishjamClient({
@@ -62,6 +67,47 @@ export class FishjamClient {
6267
this.fishjamConfig = config;
6368
}
6469

70+
/**
71+
* Async factory: constructs a client and verifies credentials against
72+
* the backend.
73+
*
74+
* Throws {@link InvalidFishjamCredentialsException} when the
75+
* `fishjamId` / `managementToken` pair is rejected by the backend.
76+
*
77+
* Example:
78+
* ```
79+
* const client = await FishjamClient.create({
80+
* fishjamId: process.env.FISHJAM_ID!,
81+
* managementToken: process.env.FISHJAM_MANAGEMENT_TOKEN!,
82+
* });
83+
* ```
84+
*/
85+
static async create(config: FishjamConfig): Promise<FishjamClient> {
86+
const client = new FishjamClient(config);
87+
await client.checkCredentials();
88+
return client;
89+
}
90+
91+
/**
92+
* Verifies the configured credentials by making a single lightweight
93+
* call to the Fishjam backend. Resolves on success, throws
94+
* {@link InvalidFishjamCredentialsException} on 401/404 from the backend,
95+
* otherwise rethrows the standard mapped exception.
96+
*/
97+
async checkCredentials(): Promise<void> {
98+
try {
99+
await this.roomApi.getAllRooms();
100+
} catch (error) {
101+
if (axios.isAxiosError(error)) {
102+
const status = error.response?.status;
103+
if (status === 401 || status === 404) {
104+
throw new InvalidFishjamCredentialsException(error);
105+
}
106+
}
107+
throw mapException(error);
108+
}
109+
}
110+
65111
private handleDeprecationHeader(headers: RawAxiosResponseHeaders): void {
66112
try {
67113
const deprecationHeader = headers['x-fishjam-api-deprecated'];

packages/js-server-sdk/src/exceptions/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ export class RoomNotFoundException extends FishjamBaseException {}
2828

2929
export class FishjamNotFoundException extends FishjamBaseException {}
3030

31+
export class InvalidFishjamCredentialsException extends FishjamBaseException {}
32+
3133
export class PeerNotFoundException extends FishjamBaseException {}
3234

3335
export class ServiceUnavailableException extends FishjamBaseException {}

0 commit comments

Comments
 (0)