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

Introducing @colyseus/auth + onAuth() change #657

Merged
merged 55 commits into from Dec 27, 2023
Merged

Introducing @colyseus/auth + onAuth() change #657

merged 55 commits into from Dec 27, 2023

Conversation

endel
Copy link
Member

@endel endel commented Dec 1, 2023

Feedback is most welcome, especially on user-facing APIs 🙏

colyseus.js integration PR here colyseus/colyseus.js#133
docs PR colyseus/docs#150


@colyseus/core - onAuth() change

  • static onAuth(token, req) method should be implemented as static
  • onAuth(client, options, req) as an instance method still works, but will be deprecated in the future.

This change allows validating the token earlier in the connection process, without needing an instance of the room available.

This way the auth token is read from the first matchmaking request header instead of as query param in the second WebSocket connection.

Example: Firebase Authentication with static onAuth()

If you are using a custom token (Firebase, PlayFab, etc), you'd provide the token in the SDK like this:

// client-side
import { Client } from "colyseus.js";

const client = new Client();
client.auth.token = "MY FIREBASE AUTH TOKEN";

const room = await client.joinOrCreate("my_room"); // no need to provide "token" here!
// server-side
import { DecodedIdToken, getAuth } from "firebase-admin/auth";

class MyRoom extends Room {
  static onAuth(token: string, req: http.IncomingMessage) {
    return getAuth().verifyIdToken(token);
  }
}

Introducing @colyseus/auth module

  • Uses JWT to secure communication with client SDK
  • Provides authentication express routes:
    • /auth/register - user registration (email + password)
    • /auth/login - login (email + password)
    • /auth/anonymous - anonymous login
    • /auth/userdata - fetch user data
  • Provides OAuth express routes (Thanks to @simov's work on grant module - a MIT-licensed "OAuth Proxy")
    • /auth/provider/:providerId - redirect to provider
    • /auth/provider/:providerId/callback - reply callback from the provider

Database interaction must be implemented by end-user

End-user should implement the following callbacks:

  • auth.settings.onFindUserByEmail = async (email) => {/* query user by email */}
  • auth.settings.onRegisterWithEmailAndPassword = async (email, password, options) => {/* insert user */}
  • auth.settings.onRegisterAnonymously = async (options: T) => {/* insert anonymous user */}

End-user may customize the following callbacks. They come with a default implementation.

  • auth.settings.onParseToken = (jwt: JwtPayload) => jwt
  • auth.settings.onGenerateToken = async (userdata: unknown) => await JWT.sign(userdata)
  • auth.settings.onHashPassword = async (password: string) => Hash.make(password)
  • auth.oauth.onCallback = async (password: string) => Hash.make(password)

Usage example

import { auth } from "@colyseus/auth";

auth.settings.onFindUserByEmail = async (email) => {
  //
  // Your database query to find user by email address
  //
}

auth.settings.onRegisterWithEmailAndPassword = async (email, password, options) => {
  // 
  // Your database query to insert the user with email + password
  // (The "password" is already hashed here)
  //
}

// Configure Discord as OAuth provider
auth.oauth.addProvider('discord', {
  key: "YOUR_DISCORD_KEY",
  secret: "YOUR_DISCORD_SECRET",
  scope: ['identify', 'email'],
});

auth.oauth.onCallback(async (data, provider) => {
  //
  // Your database query/insert. This callback must return the userdata.
  //   data.raw -> { token_type, access_token, expires_in, refresh_token, scope }
  //   data.profile -> { id, username, avatar, discriminator, public_flags, premium_type, flags, banner, accent_color, global_name, avatar_decoration_data, banner_color, mfa_enabled, locale, email }
  //
});

//
// app.config.ts: bind auth express routes
//

initializeExpress: (app) => {
  // bind auth + oauth express routes
  app.use(auth.prefix, auth.routes());

  // custom protected request
  app.get("/profile", auth.middleware(), (req: Request, res) => {
    res.json(req.auth);
  });
},

@endel endel changed the title Introducing @colyseus/auth Introducing @colyseus/auth + onAuth() change Dec 1, 2023
@endel endel merged commit b04685d into master Dec 27, 2023
@endel endel deleted the auth branch December 27, 2023 13:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant