Documentation

Function access control

Control who can invoke individual functions using an access parameter.
Read time 4 minutesLast updated 2 days ago

Within the context of Stateful Cloud Code, you control who can invoke each Cloud Code function by setting the
Access
parameter on the
[CloudCodeFunction]
attribute. This parameter provides function-level authorization that restricts callers based on their relationship to the scope, for example, session membership, session host status, player ownership, or service account identity.
Note
Function access control is separate from project-level access control policies, which restrict access to entire service APIs. The
Access
parameter controls authorization at the individual function level within your module.

Access levels

The
Access
enum defines five access levels plus a default (
Unspecified
). Each level is either valid in any scope, or restricted to a single scope. The following table describes each value and the scope it requires:

Level

Required scope

Description

Global
AnyAny caller can invoke the function, regardless of session membership or ownership.
Service
AnyOnly service account callers can invoke the function.
Player
PlayerOnly the owning player, whose player ID matches the scope ID, and service accounts can invoke the function.
SessionMember
MultiplayerSessionAny member of the session, including the host, and service accounts can invoke the function.
SessionHost
MultiplayerSessionOnly the host of the session and service accounts can invoke the function.
Unspecified
AnyThe attribute default. Cloud Code resolves the effective access from the class scope when the module loads.
Important
Cloud Code validates every function against this table when the module loads, and rejects the whole module if any function declares an incompatible combination. If you don't specify an access attribute, the module uses
Unspecified
access, for which scope defaults apply. For more information, refer to Default access.
Note
Although there's some hierarchy between the access levels, the values aren't ordinal. Don't compare them numerically, for example with
granted >= required
, because the levels don't form a single ordered chain.

Set an access level on a function

The following code sample passes an
Access
value as the second argument to
[CloudCodeFunction]
:
using Unity.Services.CloudCode.Core;[StateScope(Scope.MultiplayerSession)]public class GameLobby{ [CloudCodeFunction("StartGame", Access.SessionHost)] public string StartGame(IExecutionContext ctx) { return $"Game started by host {ctx.PlayerId}"; } [CloudCodeFunction("GetGameState", Access.SessionMember)] public string GetGameState(IExecutionContext ctx) { return $"State requested by {ctx.PlayerId}"; } [CloudCodeFunction("ResetSeason", Access.Service)] public string ResetSeason(IExecutionContext ctx) { return "Season reset by service"; } [CloudCodeFunction("Ping", Access.Global)] public string Ping() => "pong";}
You can also omit the function name and use the method name as the endpoint. In this case, pass
Access
as the only argument:
[CloudCodeFunction(Access.SessionMember)]public string GetScore(IExecutionContext ctx){ return "100";}

Default access

If you don't specify an access level, the attribute defaults to
Access.Unspecified
. Cloud Code then resolves the effective access from the scope of the function's class when the module loads:

Class scope

Effective access

No
[StateScope]
attribute
Global
[StateScope(Scope.Player)]
Player
[StateScope(Scope.MultiplayerSession)]
SessionMember
Scoped functions are restricted by default. In the following code sample,
UpdateProfile
doesn't declare an access level, so it resolves to
Access.Player
, and only the owning player and service accounts can invoke it:
[StateScope(Scope.Player)]public class PlayerProfile{ [CloudCodeFunction("UpdateProfile")] public string UpdateProfile(IExecutionContext ctx, string displayName) { return $"Profile updated for {ctx.PlayerId}"; }}
To make a function on a scoped class callable by anyone, declare
Access.Global
explicitly.

Behavior by scope type

Cloud Code enforces an access level based on the scope of the function's class.

Session-scoped functions

For
MultiplayerSession
-scoped classes, Cloud Code checks the caller's relationship to the multiplayer session:
  • SessionHost
    : The caller's player ID must match the session's host ID.
  • SessionMember
    : The caller's player ID must be in the session's member list.
[StateScope(Scope.MultiplayerSession)]public class SessionGame{ [CloudCodeFunction("KickPlayer", Access.SessionHost)] public string KickPlayer(IExecutionContext ctx, string targetPlayerId) { return $"Player {targetPlayerId} kicked by host {ctx.PlayerId}"; } [CloudCodeFunction("SubmitMove", Access.SessionMember)] public string SubmitMove(IExecutionContext ctx, string move) { return $"Move '{move}' submitted by {ctx.PlayerId}"; }}

Player-scoped functions

For
Player
-scoped classes, use
Access.Player
to restrict a function to the owning player. Cloud Code compares the caller's player ID with the scope ID, and denies any other player.
[StateScope(Scope.Player)]public class PlayerInventory{ [CloudCodeFunction("GrantItem", Access.Player)] public string GrantItem(IExecutionContext ctx, string itemId) { return $"Item {itemId} granted to {ctx.PlayerId}"; }}

Non-scoped functions

For classes without a
[StateScope]
attribute, only
Global
and
Service
are valid, because there's no scope to check the caller against.
public class AdminTools{ [CloudCodeFunction("MigratePlayers", Access.Service)] public string MigratePlayers(IExecutionContext ctx) { return "Migration started"; } [CloudCodeFunction("GetServerTime")] public DateTime GetServerTime() => DateTime.UtcNow;}

Error handling

When a caller doesn't have the required access level, Cloud Code returns a
403 Forbidden
response with error code
9034
. The following code sample shows how to handle this error on the client side:
try{ var result = await CloudCodeService.Instance.CallModuleEndpointAsync<string>( "GameLobby", "StartGame", scope: new CloudCodeScope(ScopeType.MultiplayerSession, sessionId));}catch (CloudCodeException ex) when (ex.ErrorCode == 9034){ Debug.Log("Access denied: you are not the session host.");}