Documentations › mmo.oauth › js › oauth Scopes MMO.oauth · 1 symbol · generated 27 Oct 2026
How mmo.oauth decides a scope, in the browser: the same rules as the server's (src/functions.php, classes/tWithRoles.php), so the scopes' editor shows what a rule does before it is saved, and why.
class MMO.oauth.Scopes Namespace MMO.oauthA scope is a path of names separated by dots (`pages.settings.roles`), with an action after a `#` (`data#edit`).
To decide `a.b#edit`, the first rule set among `a.b#edit`, `a.b`, `a#edit`, `a` gives the answer, nothing set
denies: a rule without an action decides every action below it. MMO.oauth.Scopes.resolve({'data': true, 'data#edit': false}, 'data.main.orders#edit').value; // false
Index split(uid) static ancestors(path) static candidates(uid) static resolve(scopes, uid) static Decide a scope, as \MMO\oauth\isAllowed does isAllowed(scopes, uid) static merge(source, scopes, set) static Merge scopes into source, as \MMO\oauth\mergeScopes does: a deny is always set, an allow only where source does not already deny it (unless set) combine(roles, own) static The scopes of a client: its roles merged in their order, then its own rules, which win (tWithRoles::getScopes) context(roles, own) static The rules that apply to a role or a client, ready to explain many scopes (explain) explain(context, uid) static Why uid is allowed or denied index(catalog) static What the application knows of the scopes: built once from the catalog (user/scopes/catalog.json) actionsOf(index, path) static The actions checked under path: its own and its parents' check(index, uid) static Check a rule's name traps(own, scopes) static The rules allowing a path without an action under a rule denying an action: the trap of the order of resolution, a.b is looked for before a#edit, so allowing a.b gives back the edit a#edit …
Methods13 static split(uid) uid stringreturns !Array<?string>[the path, the action or null]
Scopes.split('data.main#edit'); // ['data.main', 'edit']
static ancestors(path) path stringwithout its action returns !Array<string>path and its parents, the closest first: a.b.c => [a.b.c, a.b, a]
static candidates(uid) uid stringthe scope asked returns !Array<string>the rules looked for, in order: a.b#edit, a.b, a#edit, a
Decide a scope, as \MMO\oauth\isAllowed does
static resolve(scopes, uid) scopes !Object<string, boolean>rule => allowed uid stringreturns value: ?boolean, rule: ?string, tried: !Array<string>} value null when no rule is set (denied); rule the one that decided; tried the rules looked for until then
static isAllowed(scopes, uid) scopes uid returns booleantrue if every scope of uid (space-separated) is allowed
Merge scopes into source, as \MMO\oauth\mergeScopes does: a deny is always set, an allow only where source does not already deny it (unless set)
static merge(source, scopes, set = false) source !Object<string, boolean>changed scopes !Object<string, boolean>set = false booleantrue to set every rule as it is returns !Object<string, boolean>source
The scopes of a client: its roles merged in their order, then its own rules, which win (tWithRoles::getScopes)
static combine(roles, own = {}) roles !Array<!Object<string, boolean>>the scopes of each role, in order own = {} !Object<string, boolean>returns !Object<string, boolean>
The rules that apply to a role or a client, ready to explain many scopes (explain)
static context(roles, own) roles own !Object<string, boolean>the rules set on the role or the client edited returns roles: !Array, own: !Object<string, boolean>, scopes: !Object<string, boolean>, reversed: ?Object<string, boolean>}
Why uid is allowed or denied
static explain(context, uid) context uid stringreturns value: boolean, rule: ?string, tried: !Array<string>, source: string, roles: !Array<string>, orderDependent: boolean} source own (a rule set here), roles (set by roles, their labels), combined (a rule the merge of the roles made: a deny of one role pushed onto a rule of another) or none (nothing set: denied)
Scopes.explain(Scopes.context([admin, user], {}), 'pages.settings').roles; // ['user']
What the application knows of the scopes: built once from the catalog (user/scopes/catalog.json)
static index(catalog) catalog returns paths: !Set<string>, actions: !Object<string, !Object<string, string>>, open: !Array<string>}
The actions checked under path: its own and its parents'
static actionsOf(index, path) index path returns !Object<string, string>action => text
Check a rule's name
static check(index, uid) index uid stringreturns ?{code: string, path: string, action: ?string} null when the application knows it: malformed, unknown (no part of the application declares this name) or action (nothing checks this action here)
Scopes.check(index, 'data.main.orders#update'); // {code: 'action', ...}: only show/create/edit/delete/status...
The rules allowing a path without an action under a rule denying an action: the trap of the order of resolution, a.b is looked for before a#edit, so allowing a.b gives back the edit a#edit refuses
static traps(own, scopes) own !Object<string, boolean>the rules set here scopes !Object<string, boolean>all the rules that apply (own, or combined with the roles) returns !Array<{rule: string, denied: string, action: string, fix: string>} fix: the rule to deny to keep the action denied
Scopes.traps({'data.main.orders': true}, {'data#edit': false, 'data.main.orders': true});
// [{rule: 'data.main.orders', denied: 'data#edit', action: 'edit', fix: 'data.main.orders#edit'}]