Permissions
How scopes work for apps built on the SDK. Registering them, requesting them, checking what was granted, adding more later, and telling a missing permission from a rule.
A permission (the technical word is scope) is a single thing a person allows your app to do, such as reading the feed or publishing a post. Signing in needs none beyond openid profile; a UNET client asks for one permission per kind of call. People see the list before they allow it.
Three places that must agree
- The app's registration. In the console, the app has a list of permissions it may ask for. Add only what you use.
- Your
scopes. The list you pass tonew UnetId({ scopes }).openidis always included. - What the person granted. They can allow the request as asked, or leave; the token then carries the scopes that were both granted and registered.
import { UnetId } from "@unet/id-web";
const unet = new UnetId({
clientId: "unet_xxx",
redirectUri: `${location.origin}/auth/callback`,
scopes: ["openid", "profile", "content:read", "notifications:read"],
});Checking what was granted
unet.hasScope("posts:write"); // true or false
unet.getGrantedScopes(); // ["openid", "profile", "content:read", ...] or null when signed outCheck before you show a feature. If the server did not report the granted scopes, the SDK falls back to the ones you requested.
if (unet.hasScope("posts:write")) {
showComposer();
}
declare function showComposer(): void;Adding permissions later
Ask for the least at first and add more when the person reaches a feature that needs it. Add the permission to the app in the console, add it to scopes, then send the person through sign-in again with the permission screen:
await unet.signIn({ prompt: "consent", returnTo: location.pathname });Existing sessions keep the scopes they were granted; the new ones appear after the person approves.
Missing permission or a rule
Calls to the UNET API can answer 403 for two different reasons, and the SDK separates them:
| You get | Meaning | What to do |
|---|---|---|
InsufficientScope | The session lacks the permission this call needs. scope names it. | Ask for it with signIn({ prompt: "consent" }), if your app should have it. |
ApiError with code: "not_allowed" | A rule applies: the person is blocked, comments are off, the connections are private, you cannot repost your own post. | Show a message. A new permission does not help. |
ApiError with code: "connections_private" | The account hides its follower and following lists. | Hide the list. |
import { ApiError, InsufficientScope } from "@unet/id-web";
try {
await api.like(123);
} catch (e) {
if (e instanceof InsufficientScope) {
await unet.signIn({ prompt: "consent" }); // e.scope says which permission is missing
} else if (e instanceof ApiError && e.code === "not_allowed") {
alert("You cannot like this post.");
} else {
throw e;
}
}A bare 403 that does not come from UNET (a proxy or a firewall) is an ApiError with code: "forbidden", not InsufficientScope.
Ask for the least
- Request only what a feature needs. People read the list, and a shorter one is allowed more often.
- Split features by permission: an app that only reads needs no write permission, and it will never get one.
- Do not request
offline_accessunless the person should stay signed in beyond an hour.