liveblocks / liveblocks

Liveblocks is the platform for adding collaborative editing, comments, and notifications into your application.
https://liveblocks.io
Apache License 2.0
3.51k stars 281 forks source link

Authentication endpoint support for backend not built with node.js #12

Closed GuillaumeSalles closed 1 year ago

GuillaumeSalles commented 3 years ago

If you need to build an authentication endpoint but you're not using node.js, please let us know here.

jennyckaplan commented 2 years ago

Any plan to provide an authentication endpoint for a Flask backend?

GuillaumeSalles commented 2 years ago

Hey @jennyckaplan,

We will release a Python package at some point, but not in the short term. In the meantime, building your own should be relatively straightforward. Our node package simply calls one of our REST API to generate a token.

My python is a bit rusty and I've never used Flask but the code should look something like that (untested):

import requests
from flask import Flask
from flask import request

app = Flask(__name__)

@app.route("/auth", methods = ['POST'])
def auth():
  content = request.get_json(silent=True)

  url = "https://liveblocks.io/api/authorize"

  headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer " + YOUR_SECRET_KEY,
  }

  data = {
    "room": content.room,
    "userId": "your-user-id", # Optional
    "userData": {  # Optional
      "firstName": "Testy",
      "lastName": "McTester"
    },
  }

  response = requests.post(url, headers=headers, json=data)
  return response.json();

I hope it helps! :)

If it does not work, please let me know and I'll set up a Flask project on my machine to test it properly.

jennyckaplan commented 2 years ago

Hey @jennyckaplan,

We will release a Python package at some point, but not in the short term. In the meantime, building your own should be relatively straightforward. Our node package simply calls one of our REST API to generate a token.

My python is a bit rusty and I've never used Flask but the code should look something like that (untested):

import requests
from flask import Flask
from flask import request

app = Flask(__name__)

@app.route("/auth", methods = ['POST'])
def auth():
  content = request.get_json(silent=True)

  url = "https://liveblocks.io/api/authorize"

  headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer " + YOUR_SECRET_KEY,
  }

  data = {
    "room": content.room,
    "userId": "your-user-id", # Optional
    "userData": {  # Optional
      "firstName": "Testy",
      "lastName": "McTester"
    },
  }

  response = requests.post(url, headers=headers, json=data)
  return response.json();

I hope it helps! :)

If it does not work, please let me know and I'll set up a Flask project on my machine to test it properly.

Thank you so much for the quick reply @GuillaumeSalles! Super impressed. I'm still not seeing my connection data reflected when using the useOthers hook in React on my client (seeing undefined for both id and info on the connections). On the backend, I'm getting a token returned on each connection so the request is going through. I used the code above, and even tried replacing userData with userInfo (since I saw that in the docs/repo) but I'm not even seeing the id updating - any advice?

Thanks again for all the help!

UPDATE (resolved):

I completely forgot to replace my public key on my client with authEndpoint. Additionally, we have to pass in userInfo instead of userData in the code chunk above.

Example for anyone else who gets stuck on this:

  const client = createClient({
    authEndpoint: async (room) => {
      const response = await Backend.authorizeAndEnterRoom({
        firstName,
        lastName,
        profilePhoto,
        room,
      });

      return response.body;
    },
  });

Backend.authorizeAndEnterRoom calls my auth endpoint with the existing JWT validation I have in my app, and my auth endpoint returns an object: { "status": <status_code>, "body": response.json()} where response is the response from the post request in the above Python code chunk.

This client is then passed into the LiveblocksProvider's client prop. When you create a RoomProvider, this endpoint will automatically be called with the room id passed in.

GuillaumeSalles commented 2 years ago

Hey @jennyckaplan! Sorry for not being able to answer sooner... I'm glad you could figure it out! Thanks a lot for the detailed solution! I'm sure it will help other potential users :)

I would only add one thing (it might not be relevant to you but just in case other users are seeing this):

If you send user information to your authEndpoint from the front-end, there is a possibility of impersonation. Someone could have a valid JWT but try to impersonate someone else in the room, so we recommend extracting user information from your JWT if possible.

Diagrams to make it clearer.

From what I understand, your current setup looks like this: Screen Shot 2022-02-07 at 3 35 52 PM

And we recommend: Screen Shot 2022-02-07 at 3 40 11 PM

It might be okay if you validate the user information against the JWT or for other reasons that I'm not aware of.

In any case, we care about the security of our users so I wanted to take the time to make it as clear as possible. Let me know if you have any other questions.