User Svc
The user service is at the heart of Superplatform, managing users, tokens, organizations, permissions and more. Each service and human on the Superplatform network has an account in the User Svc
.
This page is a high level overview of the
User Svc
. For more details, please see the User Svc API documentation.
How It Works
The Token
The User Svc produces a JWT (JSON Web Token) upon /user-svc/login in the token.token
field (see the response documentation).
You can either use this token as a proper JWT - decode it and inspect the contents, or you can just use the token to read the user account that belongs to the token with the /user-svc/user/by-token endpoint.
Decoding the Token
The /user-svc/public-key
will return you the public key of the User Svc which then you can use that to decode the token.
Use the JWT libraries that are available in your programming language to do that, or use the Singularon SDK if your language is supported.
Token Structure
The structure of the JWT is the following:
{
"sui":"usr_dC4K75Cbp6",
"slu":"test-user-slug-0",
"sri":[
"user-svc:user",
"user-svc:org:{org_dC4K7NNDCG}:user"
]
}
The field names are kept short to save space, so perhaps the Go definition is also educational:
type Claims struct {
UserId string `json:"sui"` // `sui`: singulatron user ids
Slug string `json:"slu"` // `slu`: singulatron slug
RoleIds []string `json:"sri"` // `sri`: singulatron role ids
jwt.RegisteredClaims
}
Managing Credentials
The most important thing about the User Svc is that service (machine) and user (human) accounts look and function the same.
Every service you write needs to register at startup, or log in with the credentials it saves and manages if it's already regsitered. Just like a human.
You can do this in a few ways:
- Use the API directly
- Use a language specific client that was generated from the API
- Use a language specific SDK
Services With Multiple Nodes
You might now wonder what happens when a service has multiple instances/nodes. Won't their user accounts "clash" in the User Svc
? The answer to this is that from the User Svc
point of view, each node/instance of a service is the same account.
This is possible because the platform is designed with services having a "Shared Database Access".
Let's say you have a Cassandra network that spans multiple Availability Zones/Regions. Your nodes will also span multiple AZs/Regions and each instance of them will log in as X Svc
.
A Practical Example
A code snippet is worth a thousand words, even if it's in an unfamiliar language, so here is how the Prompt Svc boots up:
func (cs *PromptService) Start() error {
token, err := sdk.RegisterService("prompt-svc", "Prompt Service", cs.router, cs.credentialStore)
if err != nil {
return err
}
cs.router = cs.router.SetBearerToken(token)
return cs.registerPermissions()
}
Roles
Types of Roles
Static
Static roles, such as user-svc:admin
and user-svc:user
defined by the User Svc
are primarily used for simple role-based access control: in the Superplatform UI and API you can edit static roles to add or remove endpoints a user can call.
If you are looking at restricting access to endpoints in other ways, you might be interested in: Policy Svc.
Dynamic
Dynamic roles are generated based on specific user-resource associations, offering more flexible permission management compared to static roles.
Dynamic roles look like user-svc:org:{org_dBZRCej3fo}:admin
. The dynamic values must be surrounded by {}
symbols. The above example is how organization roles are represented.
These dynamic roles, like static roles are stored in the JWT tokens so it is advisable to keep them to a minimum. The organization example is an apt one here: think about how many GitHub or Google organizations you are part of. Likely even a few dozen are at the most extreme upper limit.
JWT tokens (and the dynamic they contain) are sent with each request, so try to be efficient with dynamic roles.
Conventions
Each role created must by prefixed by the slug of the account that created it. Said account becomes the owner of the role and only that account can edit the role.
Permissions
Conventions
Each permission created must by prefixed by the slug of the account that created it. Said account becomes the owner of the permission and only that account can add the permission to a role.
Once you (your service) own a permission (by creating it, and it being prefixed by your account slug), you can add it to any role, not just roles owned by you.
Example; let's say your service is petstore-svc
. Superplatform prefers fine-grained access control, so you are free to define your own permissions, such as petstore-svc:read
or petstore-svc:pet:read
.