Skip to content

Commit

Permalink
feat: Allow socket connection without authentication (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
taorepoara authored Feb 7, 2024
1 parent 274bb82 commit b9f1d82
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 37 deletions.
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,25 @@ const app = new LenraApp({
});
```

Open the websocket connection:
Authenticate the user and open the websocket connection:

```javascript
app.connect();
```

You also can manage them separatly:

```javascript
const token = app.authenticate();
app.openSocket(token);
```

Or just open the websocket connection without authentication:

```javascript
app.openSocket();
```

This while automatically start the authentication flow.

You can then connect to a Lenra route to use it data:
Expand Down
Binary file modified example/app-lenra/bun.lockb
Binary file not shown.
2 changes: 1 addition & 1 deletion example/app-lenra/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"author": "Lenra",
"license": "MIT",
"dependencies": {
"@lenra/app": "^1.1.0"
"@lenra/app": "../../../app-lib-js/"
},
"app-lenra": {
"app": "app.ts",
Expand Down
3 changes: 2 additions & 1 deletion example/app-lenra/src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ const manifest: Manifest = {
path: "/counter/global",
view: View("counter").find(Counter, {
"user": "global"
}).toJSON()
}).toJSON(),
roles: ["guest", "user"]
},
{
path: "/counter/me",
Expand Down
3 changes: 2 additions & 1 deletion example/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@

<body>
<ul>
<li id="me" class="counter">My personnal counter <output>Loading...</output><button>+</button></li>
<li id="global" class="counter">The common counter <output>Loading...</output><button>+</button></li>
<li id="me" class="counter authenticated">My personnal counter <output>Loading...</output><button>+</button></li>
</ul>
<button>Connexion</button>
</body>
<script src="./dist/bundle.js"></script>

Expand Down
53 changes: 35 additions & 18 deletions example/src/index.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,55 @@
import { LenraApp, Listener } from '@lenra/client';

const app = new LenraApp({
appName: "XXX-XXX-XXX",
clientId: "XXX-XXX-XXX",
});

let authenticated = false;

console.log("connecting...");
app.connect().then(() => {
app.openSocket().then(appConnected);

const authenticationButton = document.querySelector("body > button");
authenticationButton.onclick = () => {
app.disconnect();
authenticated = true;
app.connect().then(appConnected);
};
const disconnectButton = document.createElement("button");
disconnectButton.textContent = "Disconnect";
disconnectButton.onclick = () => {
app.disconnect();
disconnectButton.style.display = "none";
};
document.body.appendChild(disconnectButton);

function appConnected() {
console.log("Connected !");

const disconnectButton = document.createElement("button");
disconnectButton.textContent = "Disconnect";
disconnectButton.onclick = () => {
app.disconnect();
disconnectButton.remove();
};
document.body.appendChild(disconnectButton);
disconnectButton.style.display = undefined;

const counters = document.querySelectorAll(".counter");

counters.forEach((counter) => {
// Filter counters that need authentication
if (!authenticated && counter.classList.contains("authenticated")) return;

const button = counter.querySelector("button");
const output = counter.querySelector("output");
app.route(`/counter/${counter.id}`,
console.log("Open route for counter", counter.id);
app.route(`/counter/${counter.id}`,
/**
* @param {{value: number, onIncrement: Listener<{value: string}>}} data
*/
(data) => {
output.textContent = data.value;
button.onclick = () => {
output.classList.add("loading");
data.onIncrement({value: "custom value"}).then(() => {
output.classList.remove("loading");
});
};
});
output.textContent = data.value;
button.onclick = () => {
output.classList.add("loading");
data.onIncrement({ value: "custom value" }).then(() => {
output.classList.remove("loading");
});
};
});
});
});
}
51 changes: 43 additions & 8 deletions src/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,33 @@ import LenraOAuth2Client, { LenraOAuth2Opts } from "./oauth2.js"
import { Callback } from "./route.js";
import LenraSocket, { LenraSocketOpts } from "./socket.js";

type LenraAppOpts = {
appName?: string,
type OAuth2Opts = {
clientId: string,
redirectUri?: string,
scopes?: string[],
redirectUri: string,
scopes: string[],
oauthBaseUri: string,
}

type LenraAppOpts = Partial<OAuth2Opts> & {
appName?: string,
isProd?: boolean
socketEndpoint?: string,
oauthBaseUri?: string,
}

export default class LenraApp {
lenraAppOpts: LenraAppOpts;
lenraOAuth2Client: LenraOAuth2Client;
lenraOAuth2Client?: LenraOAuth2Client;
lenraSocket?: LenraSocket;

constructor(opts: LenraAppOpts) {
if (!opts.appName && !opts.clientId) throw new Error("At least one of appName or clientId must be provided.");
opts.oauthBaseUri = opts.oauthBaseUri ?? (opts.isProd ? "https://auth.lenra.io" : "http://localhost:4444");
opts.redirectUri = opts.redirectUri ?? window.location.origin + "/redirect.html";
opts.scopes = opts.scopes ?? ["app:websocket"];
this.lenraAppOpts = opts;
}

initOAuth2Client(opts: OAuth2Opts) {
const oAuth2Opts: LenraOAuth2Opts = {
clientId: opts.clientId,
redirectUri: opts.redirectUri,
Expand All @@ -35,7 +42,23 @@ export default class LenraApp {
}

async connect(params?: Record<string, any>) {
const accessToken = await this.lenraOAuth2Client.authenticate();
const accessToken = await this.authenticate();
return this.openSocket(accessToken, params);
}

authenticate() {
if (!this.lenraOAuth2Client) {
this.initOAuth2Client({
clientId: this.lenraAppOpts.clientId!,
redirectUri: this.lenraAppOpts.redirectUri!,
scopes: this.lenraAppOpts.scopes!,
oauthBaseUri: this.lenraAppOpts.oauthBaseUri!,
});
};
return this.lenraOAuth2Client!.authenticate();
}

openSocket(accessToken?: string, params?: Record<string, any>) {
const socketOpts: LenraSocketOpts = {
appName: this.lenraAppOpts.appName,
token: accessToken,
Expand All @@ -52,9 +75,21 @@ export default class LenraApp {
return this.lenraSocket.route(routeName, callback);
}

/**
* Close the socket connection and logout the user.
* @returns
*/
async disconnect() {
this.closeSocket();
return this.logout();
}

closeSocket() {
this.lenraSocket?.close();
await this.lenraOAuth2Client.disconnect();
}

async logout() {
await this.lenraOAuth2Client?.disconnect();
}

}
12 changes: 5 additions & 7 deletions src/socket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import LenraRoute, { Callback } from './route.js';

export type LenraSocketOpts = {
appName?: string,
token: string,
token?: string,
additionalParams?: Record<string, any>,
socketEndpoint: string,
}
Expand All @@ -15,12 +15,10 @@ export default class LenraSocket {
constructor(opts: LenraSocketOpts) {
this.opts = opts;

let params = {
...opts.additionalParams,
token: opts.token,
app: opts.appName,
}
this.socket = new Socket(opts.socketEndpoint, { params: params });
let params: any = {}
if (opts.token) params.token = opts.token;
if (opts.appName) params.app = opts.appName;
this.socket = new Socket(opts.socketEndpoint, { params: { ...opts.additionalParams, ...params } });
}

connect() {
Expand Down

0 comments on commit b9f1d82

Please sign in to comment.