Refreshing Session Token - Confused by Parameters

Hi all,

I am currently implementing the ability for authenticated users to update their username on the fly, once they have been authenticated.

This did initially pose some problems, but via these forums, and the Nakama docs, I discovered that we can do this via a custom RPC call to nk.authenticateTokenGenerate which will, ostensibly, ask for a new session token which will have the authenticated clients new username.

The problem, however, is that there seems to be conflicting or incomplete instructions on how this call is constructed. See below:

Example from Nakama docs:

token, validity, err := nk.AuthenticateTokenGenerate("user_id", "username", 0)
if err != nil {
    logger.WithField("err", err).Error("Authenticate token generate error.")
    return
}
logger.Info("Session token: %q, valid for %v seconds", token, validity)

This seems pretty simple, you call for a new session token, pass a user ID, a user Username, and a timeout parameter. Notice how there are no additional params passed here. The documentation doesn’t mention anything about that here.

Function as seen on Git:

func (n *runtimeJavascriptNakamaModule) authenticateTokenGenerate(r *goja.Runtime) func(goja.FunctionCall) goja.Value {
	return func(f goja.FunctionCall) goja.Value {
		// Parse input User ID.
		userIDString := getJsString(r, f.Argument(0))
		if userIDString == "" {
			panic(r.NewTypeError("expects user id"))
		}

		uid, err := uuid.FromString(userIDString)
		if err != nil {
			panic(r.NewTypeError("expects valid user id"))
		}

		var username string
		if f.Argument(1) != goja.Null() && f.Argument(1) != goja.Undefined() {
			username = getJsString(r, f.Argument(1))
		}
		if username == "" {
			username = generateUsername()
		}

		exp := time.Now().UTC().Add(time.Duration(n.config.GetSession().TokenExpirySec) * time.Second).Unix()
		if f.Argument(2) != goja.Null() && f.Argument(2) != goja.Undefined() {
			exp = getJsInt(r, f.Argument(2))
		}

		var vars map[string]string
		if f.Argument(3) != goja.Null() && f.Argument(3) != goja.Undefined() {
			vars = getJsStringMap(r, f.Argument(3))
		}

		token, exp := generateTokenWithExpiry(n.config.GetSession().EncryptionKey, userIDString, username, vars, exp)
		n.sessionCache.Add(uid, exp, token, 0, "")

		return r.ToValue(map[string]interface{}{
			"token": token,
			"exp":   exp,
		})
	}
}

Again, makes sense, the provided parameters are sucked up, evaluated, and a new token is returned. Here however, the method appears to be OK with taking an additional params list, ultimately this doesn’t matter.

Example RPC kindly proposed by a Nakama engineer

func refreshToken(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) {
	userId, ok := ctx.Value(runtime.RUNTIME_CTX_USER_ID).(string)
	if !ok {
		logger.Warn("no user ID in context")
		return "", runtime.NewError("permission denied", 7)
	}

	account, err := nk.AccountGetId(ctx, userId)
	if err != nil {
		logger.Error("error retrieving account: %v", err.Error())
		return "", runtime.NewError("error retrieving account", 13)
	}

	// TODO check if the account has been disabled

	token, _, err := nk.AuthenticateTokenGenerate(account.User.Id, account.User.Username, nil, 0)
	if err != nil {
		logger.Error("error generating token: %v", err.Error())
		return "", runtime.NewError("error generating token", 13)
	}

	return token, nil
}

Here, the example provides a user ID, a username (optional), a value of ‘nil’ and a value of zero. I expect that this may simply be an example of out of date syntax, but the engineer appears to expect this to work, which is fine.

I have some confusing feedback however, when I try to implement my own, simple RPC call.

RPC in TS:

function rpcRefreshSessionToken(context: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, payload: string): string {
    
    var token = nk.authenticateTokenGenerate(context.userId,0);
    return JSON.stringify(token);
}

The above complains that the additional ‘vars’ object is missing and required. In addition, if I pass in an empty vars object, while the TS while compile, when the actual RPC is called via the client, it will complain of a type error:

 TypeError: expects string at github.com/heroiclabs/nakama/v3/server.(*runtimeJavascriptNakamaModule).authenticateTokenGenerate.func1

implying somehow that the passed userID cannot be parsed as a string.

I must be missing something fundamental. Any help is appreciated,

These parameters should be fixed in the next release (ref: Update generate token runtime function desc by sesposito · Pull Request #947 · heroiclabs/nakama · GitHub)

For now, if you pass in all the parameters as the provided example does, it should work. It is likely expecting a string because your second parameter is supposed to be the username.