Abstract
This specification describes the authorization and authentication process between security-sensitive wallets and untrusted applications.
Since key-related resources should not be accessible to any untrusted applications, such as a website, until the user grants specific permissions on it, most of the endpoints should be guarded by an authentication system.
This specification defines some aspects of the authentication system, including:
- Depiction of permission;
- Two JSON-RPC methods to get and request permissions;
- Two expressive user interfaces to inform permissions to grant and revoke;
- Authorization process between application, wallet and user;
- Authentication process between application and wallet;
Introduction
Wallets co-operating with untrusted applications are doing works viz.
- Provide available addresses;
- Provide live cells (including deps) for constructing transactions;
- Switch between blockchain nodes to submit transactions;
- Sign and send transactions proposed from applications;
- Sign and verify messages from applications.
Some of them should be prohibited until the owner of the wallet grants related permissions on the requester.
For universal experience between a variety of applications and wallets, this specification proposes a canonical process for these tasks. Following this guide, developers can implement permission systems in a general way, and users can avoid mental burden on permission-grants between different applications.
Specification
Permission Restriction
This specification imposes three restrictions on each permission,
1 | restrictions: |
Permission Requests
This specification defines two requests in JSON-RPC method:
Get Permission List
get_permission_list
queries the permission list provided by the wallet. On handling this kind of request, wallet should return the list of permissions in the following format.
1 | permission_name: |
Request Permissions
To request particular permissions, application should POST a request in following format:
1 | id: 1 |
The payload in this request indicates the details as follows:
- Application is requesting
permission_name
permission - The
permission_request
should be available untilexpiration
, eternal ifexpiration
isnull
- The
permission_request
will be revoked automatically afterlimit
invocations,null
for unlimited. - Reason to request
permission_name
Users should be informed with all this information in a proper way, including extra info about the requester.
1 | ┌───────────────────────────────────────────────┐ |
With this interface, requested permissions can be granted as needed, which makes the permission system more flexible.
Once user confirms, wallet should return:
1 | id: 1 |
If user denies, the wallet should return:
1 | id: 1 |
Revoke Permissions
A grant should be able to revoke freely in a kind way with essential annotations:
1 | ┌───────────────────────────────────────────────┐ |
Authorization Process
The authorization process is a sequence of simple tasks that including application requests permissions
, inform user necessary information
, handle confirmation/rejection/exception
.
sequenceDiagram participant Application participant Auth participant User Application ->> Auth: Request Permissions Auth -->> Application: If Exception Thrown Auth ->> User: App[x] Requests Permissions User ->> Auth: Pick Some or Deny Auth ->> Application: Permissions Granted Auth -->> Application: Request Denied
This specification has defined a method to request permissions and an advisable user interface to ask user for confirmation. Now the handlers on confirmation, rejection and exception should be complemented in this procedure.
There are three kinds of result in the end:
- Some permissions are granted;
- Request is denied;
- Exception occurs.
All these cases are covered by three fields of result of request_permissions
defined above:
1 | permissions: # result of request_permissions mentioned above, null for exception |
error
and message
are worthy distinguishing since they describe different kinds of exception.
error
means an unexpected exception is thrown, like database error or internal exception. While message
indicates an expected behavior is delivered, e.g. user denied, or password is incorrect, etc.
With all of these definition, the workflow should be much clearer:
graph TD App[Application] -->|Request| A{Auth} A -->|Granted| G[permissions: non-null
error: null
message: null] A -->|Denied, Password Error, Dep Error| D[permissions: null
error: null
message: non-null] A -->|Unknown Exception| U[permissions: null
error: non-null
message: null]
Authentication Process[Discretionary]
Defining the process of authentication is good for writing SOLID code but it’s not mandatory in this system.
Name authentication mechanism Strategy
, which is a concept from passport, and classify them into LocalStrategy
, JwtStrategy
, etc. by concrete mechanisms (More strategies can be found in Passport).
Suppose an endpoint get_xxx
should be protected by the auth module auth
.
1 | interface Verify { |
With this configuration, app
invokes auth.validate
on each post request before get_xxx
route handler and if validation fails, the message will be passed to the router handler for the next step.
Conclusion
This specification illustrates several facets of authorization and authentication from perspectives of applications, wallets and users. With the APIs defined in this specification, developer experience is unified across different applications while with the request/revoke UIs pictured above, the user experience is also improved across various wallets.