Skip to content

Commit

Permalink
feat: access & refresh tokens to replace api key authorization mechan…
Browse files Browse the repository at this point in the history
…ism (#113)

* feat: separate auth module

* feat: setAuth method

* refactor: remove facade module

* feat: expose headers, and url on response

* feat: use access token and refresh token in api authorization

* docs: update readme for new auth mechanism
  • Loading branch information
faiq-naufal authored Jul 17, 2024
1 parent b624cb5 commit 011884d
Show file tree
Hide file tree
Showing 10 changed files with 394 additions and 181 deletions.
1 change: 1 addition & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ module.exports = {
'jsdoc/require-returns': 'off',
'jsdoc/require-param-description': 'off',
'jsdoc/require-returns-description': 'off',
'jsdoc/require-property-description': 'off',
'prettier/prettier': [
'warn',
{
Expand Down
2 changes: 1 addition & 1 deletion packages/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { InliveApp } from './app/index.js'
export { InliveStream, Stream, Stat } from './stream/index.js'
export { Room, RoomEvent } from './room/index.js'
export { Room, RoomEvent, createAuth } from './room/index.js'
98 changes: 75 additions & 23 deletions packages/room/README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,81 @@
# Room Package

This package is used to work with [inLive Hub and Room API services](https://hub.inlive.app/apidocs/index.html).
This package is used to interact with [inLive Hub and Room API services](https://hub.inlive.app/apidocs/index.html).

## Usage

To use this package, you need to import a `Room` module and initialize it in a global scope where you can export and import the room object returned everywhere in your application.
The first thing to do is to get an application key, also known as an API key. You need to register [an inLive account](https://studio.inlive.app) and go to the [integration page](https://studio.inlive.app/settings/integration) where you can create one. The API key is unique per application and the recommendation is to only use and store it on the server side.

### Initialization

The `Room()` module can be initialized globally, both on the client and server sides. However, some functionalities only work on the client side, especially those related to creating a WebRTC peer connection, which runs only on the browser side.

In the version of **v0.19.0 and higher**, to properly authenticate the `Room()` module, you need to set authentication tokens returned by `createAuth()` using `room.setAuth()` method. However, you can still use the older version of initialization.

```js
import { Room, RoomEvent } from '@inlivedev/inlive-js-sdk';
// Or if you prefer to load only the room module
import { Room, RoomEvent } from '@inlivedev/inlive-js-sdk/dist/room.js';
import { Room, RoomEvent, createAuth } from '@inlivedev/inlive-js-sdk';
// Or if you prefer to load only the room distribution
import { Room, RoomEvent, createAuth } from '@inlivedev/inlive-js-sdk/dist/room.js';

// Older version of initialization. Still working as of now
const room = Room({
api : {
apiKey : 'YOUR_API_KEY'
}
})

// Newer version of initialization and the recommended approach
const auth = await createAuth({
apiKey: 'YOUR_API_KEY',
expirySeconds: 3600,
});

const room = Room();
room.setAuth(auth);

const newRoom = await room.createRoom('a new room');
// ...
```

### Authentication

Version **v0.19.0** introduces a new `createAuth()` module, which is used to create the necessary authentication data and tokens that can be safely used in the client side by the `Room()` module.

You should import and run the `createAuth()` module in the server side. This is important because it will ask for your API key and we don't want the API key to be exposed to the client side. After executed, it will return a promise object containing access and refresh tokens useful for client side authentication. Then, you can safely pass the data and setup the authentication using `room.setAuth()` method.

#### Access token

The access token is a short temporary token which will be used by the `Room()` module as an authentication token when interacting with the API. By default an access token will be generated by `createAuth()` module and will expire after 3600 seconds (1 hour) unless `expirySeconds` data is specified.

You don't need to worry about how to handle access token expiration. When an access token is expired, API will send a `X-Access-Token-Expired` header. The SDK will automatically handle this and regenerate the access token using the refresh token as long as the refresh token is also still valid.

#### Refresh token

The refresh token is a short temporary token, but it has slightly longer expiration time than the access token. It will be used to regenerate the access token, when the access token expires. By default a refresh token will be generated by `createAuth()` module and has two times longer expiration time than the access token.

### Configurations
These are the available config options for initializing the `Room` module. See the [default configuration](./config/config.js).

#### Authentication module
These are the available config options for `createAuth()` module.

```js
{
// Your API key
apiKey: 'YOUR_API_KEY',

// The API server base URL
baseUrl: 'https://api.inlive.app',

// The API server version
apiVersion: 'v1',

// Time needed for the access token to expire. Time needed for the refresh token is two times longer than this value.
expirySeconds: 3600,
}
```

#### Room module
These are the available config options for initializing the `Room()` module. See the [default configuration](./config/config.js).

> [!NOTE]
Some webcam and screenshare configurations might not be always working the way they configured on every browser because each browser has different support for simulcast, svc, and codec.
Expand All @@ -28,7 +84,7 @@ Some webcam and screenshare configurations might not be always working the way t
{
// API server configurations
api: {
// This is the only required part when you want to use functions that require authentication.
// Your API key
apiKey: 'YOUR_API_KEY',

// The API server base URL
Expand Down Expand Up @@ -137,21 +193,11 @@ Room({
})
```

### Authentication
Some function in the Room Object require the apiKey parameter to be defined, since the SDK is designed to be used on Client and Server Side

If the Library is used on the client side you might not need to pass the `apiKey` parameter

The following function require apiKey to be defined :
* `Room.createRoom()`
* `Room.getRoom()`
* `Room.createClient()`

### Room object

The room object is the object created when the `Room` module is initialized. It consists methods that relates to the room scope. Some methods that directly interact to the room API can run on both client and server sides.
The room object is the object created when the `Room()` module is initialized. It consists methods that relates to the room scope. Some methods that directly interact to the room API can run on both client and server sides.

#### Sample Code
#### Sample code

```js
// create a new room
Expand Down Expand Up @@ -187,9 +233,13 @@ await room.endRoom(roomData.data.roomId);

#### Methods

- `room.setAuth(auth)`

A method to setup the authentication data for `Room()` module. It expects the auth object returned by `createAuth()` module as parameter.

- `room.createRoom(name?: string | undefined, id?: string | undefined, config?: object | undefined)`

> 🔐 Require ApiKey
> 🔐 Require authentication
A method to create a new room. If the optional `name` and `id` parameters are passed, the room will be created under those name and id. This method will return a promise.

Expand Down Expand Up @@ -248,13 +298,13 @@ await room.endRoom(roomData.data.roomId);

- `room.getRoom(roomId: string)`

> 🔐 Require ApiKey
> 🔐 Require authentication
A method to get the room data. It expects a `roomId` as a parameter. This method will return a promise.

- `room.createClient(roomId: string, config?: object | undefined)`

> 🔐 Require ApiKey
> 🔐 Require authentication
A method to create and register a new client to the room. It expects two parameters. The `roomId` is required. The second parameter is an optional config to set a custom client config. This method will return a promise.

Expand Down Expand Up @@ -315,6 +365,8 @@ await room.endRoom(roomData.data.roomId);

- `room.endRoom(roomId: string)`

> 🔐 Require authentication
A method to end the room for everyone. When this method is called, everyone will be disconnected from the room and the room session will be ended. It requires a `roomId` parameter to be set. This method will return a promise.

### Peer object
Expand All @@ -325,7 +377,7 @@ The peer object is created when the client call the `room.createPeer()` method.

When `room.createPeer()` method is called, it will create a basic peer connection. To establish the peer connection to the remote peer, the client should add a user [MediaStream](https://developer.mozilla.org/en-US/docs/Web/API/MediaStream) data such as camera or microphone to start connecting and establishing peer connection between local peer and remote peer. The process will be automatically run on the background and the way you know when the connection has been established is by listening the [iceconnectionstatechange event](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/iceconnectionstatechange_event) from peer connection object and check for the value of [iceConnectionState](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/iceConnectionState).

#### Sample Code
#### Sample code

```js
const peer = await room.createPeer(roomData.data.roomId, client.data.clientId);
Expand Down
26 changes: 25 additions & 1 deletion packages/room/api/api-types.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { createFetcher } from './fetcher.js'
import type { createApi } from './api.js'
import type { SharedType } from '../../internal/types/types.js'
import type { RoomType } from '../room-types.js'

export declare namespace RoomAPIType {
type CreateFetcher = typeof createFetcher
Expand All @@ -10,7 +11,8 @@ export declare namespace RoomAPIType {
type InstanceApi = ReturnType<ReturnType<CreateApi>['createInstance']>

type ApiDependencies = {
fetcher: RoomAPIType.InstanceFetcher
fetcher: InstanceFetcher
config: RoomType.Config
}

type TrackSourcesRequestBody = {
Expand Down Expand Up @@ -90,11 +92,17 @@ export declare namespace RoomAPIType {
}

type BaseResponseBody = {
url: string
headers: Headers
code: number
ok: boolean
message: string
}

type BaseResponseReturn = BaseResponseBody & {
data: null
}

type RoomResponseBody = BaseResponseBody & {
data: RoomResponse
}
Expand All @@ -117,6 +125,14 @@ export declare namespace RoomAPIType {
}
}

type RegisterClientReturn = BaseResponseBody & {
data: {
clientId: string
clientName: string
bitrates: BitratesCamelCase
}
}

type GetClientResponseBody = BaseResponseBody & {
data: {
id: string
Expand Down Expand Up @@ -150,4 +166,12 @@ export declare namespace RoomAPIType {
answer: RTCSessionDescription
}
}

type ApiAuth = {
baseUrl: string
apiVersion: string
expirySeconds: number
accessToken: string
refreshToken: string
}
}
Loading

0 comments on commit 011884d

Please sign in to comment.