How do I verify that a UserId and AuthToken pair are valid

Given a user ID, and their unexpired auth token, how do I check if that token is valid?

There appears to be a sessionCache.IsValidSession(userID, exp, tokenId) method, but I’m not sure how I can access that interface from within go runtime custom code (Go Runtime - Heroic Labs Documentation)

OK I think I got it working by digging in nakama code.

How do get the encryption_key from the config? It seems I’m only able to access env vars that are under runtime.env

package handlers

import (
	"ac/errors"
	"context"
	"crypto"
	"database/sql"
	"fmt"
	"time"

	"github.com/gofrs/uuid/v5"
	jwt "github.com/golang-jwt/jwt/v4"

	"github.com/heroiclabs/nakama-common/runtime"
)

// Photon Documentation:
// https://doc.photonengine.com/fusion/current/manual/connection-and-matchmaking/authentication/custom-authentication

func RPC_PhotonAuthenticate(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) {
	// Get the auth token from the query parameters
	var authToken string
	queryParams := ctx.Value(runtime.RUNTIME_CTX_QUERY_PARAMS).(map[string][]string)
	value, ok := queryParams["auth_token"]
	if !ok || len(value) == 0 {
		return "{\"ResultCode\": 3, \"Message\": \"Invalid parameters\"}", nil
	}
	authToken = value[0]

	// env := ctx.Value(runtime.RUNTIME_CTX_ENV).(map[string]string)
	encryptionKey := "defaultencryptionkey" //env["encryption_key"]  (DONT HARDCODE)
	encryptionKeyByte := []byte(encryptionKey)

	userID, _, _, _, _, ok := parseToken(encryptionKeyByte, authToken)
	if !ok {
		return "{\"ResultCode\": 2, \"Message\": \"Invalid token\"}", nil
	}

	return fmt.Sprintf("{\"ResultCode\": 1, \"UserId\": \"%s\"}", userID), nil
}

// From: https://github.com/heroiclabs/nakama/blob/master/server/api_authenticate.go
type SessionTokenClaims struct {
	TokenId   string            `json:"tid,omitempty"`
	UserId    string            `json:"uid,omitempty"`
	Username  string            `json:"usn,omitempty"`
	Vars      map[string]string `json:"vrs,omitempty"`
	ExpiresAt int64             `json:"exp,omitempty"`
}

func (stc *SessionTokenClaims) Valid() error {
	// Verify expiry.
	if stc.ExpiresAt <= time.Now().UTC().Unix() {
		vErr := new(jwt.ValidationError)
		vErr.Inner = errors.New("Token is expired")
		vErr.Errors |= jwt.ValidationErrorExpired
		return vErr
	}
	return nil
}

// From: https://github.com/heroiclabs/nakama/blob/master/server/api.go
func parseToken(hmacSecretByte []byte, tokenString string) (userID uuid.UUID, username string, vars map[string]string, exp int64, tokenId string, ok bool) {
	jwtToken, err := jwt.ParseWithClaims(tokenString, &SessionTokenClaims{}, func(token *jwt.Token) (interface{}, error) {
		if s, ok := token.Method.(*jwt.SigningMethodHMAC); !ok || s.Hash != crypto.SHA256 {
			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
		}
		return hmacSecretByte, nil
	})
	if err != nil {
		return
	}
	claims, ok := jwtToken.Claims.(*SessionTokenClaims)
	if !ok || !jwtToken.Valid {
		return
	}
	userID, err = uuid.FromString(claims.UserId)
	if err != nil {
		return
	}
	return userID, claims.Username, claims.Vars, claims.ExpiresAt, claims.TokenId, true
}

Hello @wbronchart, can you elaborate a bit on what it is you’re trying to achieve?

Hi @sesposito!

We’re using photon as our multiplayer solution. For every connection to our photon servers, we need to verify that this user is a legit one with an account.

Photon provides a way to do custom authentication, where it calls an endpoint with player-provided params, and only allows connections if the response from that endpoint is exactly {"ReturnCode": 1}

So what I’m doing here is passing the nakama user AuthToken to photon on initial connect, then photon server hits the RPC_PhotonAuthenticate endpoint to verify if auth token is valid.