Skip to content

Commit d863037

Browse files
committed
session: add a new session token v2
Session Token v2 solves the delegation, power of attorney, and chain-of-trust problems. It enables: - Account-based authority (direct or NNS-based indirect) - Multi-account subjects (multiple entities can use same token) - Multi-verb operations (GET, PUT, DELETE in single token) - Delegation chains (verifiable like X.509 certificates) - Indirect accounts (NeoFS Name Service resolution) Refs #241. Signed-off-by: Andrey Butusov <andrey@nspcc.io>
1 parent 2fb0e58 commit d863037

File tree

2 files changed

+240
-16
lines changed

2 files changed

+240
-16
lines changed

proto-docs/session.md

Lines changed: 102 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,20 @@
1818

1919
- Messages
2020
- [ContainerSessionContext](#neo.fs.v2.session.ContainerSessionContext)
21+
- [DelegationInfo](#neo.fs.v2.session.DelegationInfo)
2122
- [ObjectSessionContext](#neo.fs.v2.session.ObjectSessionContext)
2223
- [ObjectSessionContext.Target](#neo.fs.v2.session.ObjectSessionContext.Target)
2324
- [RequestMetaHeader](#neo.fs.v2.session.RequestMetaHeader)
2425
- [RequestVerificationHeader](#neo.fs.v2.session.RequestVerificationHeader)
2526
- [ResponseMetaHeader](#neo.fs.v2.session.ResponseMetaHeader)
2627
- [ResponseVerificationHeader](#neo.fs.v2.session.ResponseVerificationHeader)
28+
- [SessionContextV2](#neo.fs.v2.session.SessionContextV2)
2729
- [SessionToken](#neo.fs.v2.session.SessionToken)
2830
- [SessionToken.Body](#neo.fs.v2.session.SessionToken.Body)
29-
- [SessionToken.Body.TokenLifetime](#neo.fs.v2.session.SessionToken.Body.TokenLifetime)
31+
- [SessionTokenV2](#neo.fs.v2.session.SessionTokenV2)
32+
- [SessionTokenV2.Body](#neo.fs.v2.session.SessionTokenV2.Body)
33+
- [Target](#neo.fs.v2.session.Target)
34+
- [TokenLifetime](#neo.fs.v2.session.TokenLifetime)
3035
- [XHeader](#neo.fs.v2.session.XHeader)
3136

3237

@@ -147,6 +152,21 @@ Context information for Session Tokens related to ContainerService requests.
147152
| container_id | [neo.fs.v2.refs.ContainerID](#neo.fs.v2.refs.ContainerID) | | Particular container to which the action applies. Ignored if wildcard flag is set. |
148153

149154

155+
<a name="neo.fs.v2.session.DelegationInfo"></a>
156+
157+
### Message DelegationInfo
158+
DelegationInfo represents a single delegation in a chain of trust.
159+
160+
161+
| Field | Type | Label | Description |
162+
| ----- | ---- | ----- | ----------- |
163+
| issuer | [Target](#neo.fs.v2.session.Target) | | Account that performed this delegation. |
164+
| subject | [Target](#neo.fs.v2.session.Target) | | Account that received the delegation. |
165+
| timestamp | [int64](#int64) | | Unix timestamp when this delegation was created. |
166+
| verbs | [string](#string) | repeated | List of verbs authorized by this delegation. |
167+
| signature | [neo.fs.v2.refs.Signature](#neo.fs.v2.refs.Signature) | | Signature of the issuer confirming this delegation record. The signature is created over the deterministic serialization of this DelegationInfo message excluding this field. |
168+
169+
150170
<a name="neo.fs.v2.session.ObjectSessionContext"></a>
151171

152172
### Message ObjectSessionContext
@@ -185,6 +205,7 @@ request meta headers are folded in matryoshka style.
185205
| ttl | [uint32](#uint32) | | Maximum number of intermediate nodes in the request route |
186206
| x_headers | [XHeader](#neo.fs.v2.session.XHeader) | repeated | Request X-Headers |
187207
| session_token | [SessionToken](#neo.fs.v2.session.SessionToken) | | Session token within which the request is sent |
208+
| session_token_v2 | [SessionTokenV2](#neo.fs.v2.session.SessionTokenV2) | | Session token v2 with delegation chain support. If both session_token and session_token_v2 are set, session_token_v2 takes precedence. |
188209
| bearer_token | [neo.fs.v2.acl.BearerToken](#neo.fs.v2.acl.BearerToken) | | `BearerToken` with eACL overrides for the request |
189210
| origin | [RequestMetaHeader](#neo.fs.v2.session.RequestMetaHeader) | | `RequestMetaHeader` of the origin request |
190211
| magic_number | [uint64](#uint64) | | NeoFS network magic. Must match the value for the network that the server belongs to. |
@@ -234,6 +255,19 @@ Verification info for the response signed by all intermediate nodes
234255
| origin | [ResponseVerificationHeader](#neo.fs.v2.session.ResponseVerificationHeader) | | Chain of previous hops signatures |
235256

236257

258+
<a name="neo.fs.v2.session.SessionContextV2"></a>
259+
260+
### Message SessionContextV2
261+
SessionContextV2 carries unified context for both ObjectService and ContainerService requests.
262+
263+
264+
| Field | Type | Label | Description |
265+
| ----- | ---- | ----- | ----------- |
266+
| container | [neo.fs.v2.refs.ContainerID](#neo.fs.v2.refs.ContainerID) | | Container where operation is allowed. For container operations, this is the container being operated on. For object operations, this is the container holding the objects. |
267+
| objects | [neo.fs.v2.refs.ObjectID](#neo.fs.v2.refs.ObjectID) | repeated | Specific objects where operation is allowed. Only relevant for object operations. Empty list means all objects in the container. |
268+
| verbs | [SessionContextV2.Verb](#neo.fs.v2.session.SessionContextV2.Verb) | repeated | Operations authorized for this context. |
269+
270+
237271
<a name="neo.fs.v2.session.SessionToken"></a>
238272

239273
### Message SessionToken
@@ -256,23 +290,65 @@ Session Token body
256290
| ----- | ---- | ----- | ----------- |
257291
| id | [bytes](#bytes) | | Token identifier is a valid UUIDv4 in binary form |
258292
| owner_id | [neo.fs.v2.refs.OwnerID](#neo.fs.v2.refs.OwnerID) | | Identifier of the session initiator |
259-
| lifetime | [SessionToken.Body.TokenLifetime](#neo.fs.v2.session.SessionToken.Body.TokenLifetime) | | Lifetime of the session |
293+
| lifetime | [TokenLifetime](#neo.fs.v2.session.TokenLifetime) | | Lifetime of the session |
260294
| session_key | [bytes](#bytes) | | Public key used in session |
261295
| object | [ObjectSessionContext](#neo.fs.v2.session.ObjectSessionContext) | | ObjectService session context |
262296
| container | [ContainerSessionContext](#neo.fs.v2.session.ContainerSessionContext) | | ContainerService session context |
263297

264298

265-
<a name="neo.fs.v2.session.SessionToken.Body.TokenLifetime"></a>
299+
<a name="neo.fs.v2.session.SessionTokenV2"></a>
300+
301+
### Message SessionTokenV2
302+
SessionTokenV2 represents NeoFS Session Token with delegation support.
303+
304+
305+
| Field | Type | Label | Description |
306+
| ----- | ---- | ----- | ----------- |
307+
| body | [SessionTokenV2.Body](#neo.fs.v2.session.SessionTokenV2.Body) | | Session token body. |
308+
| signature | [neo.fs.v2.refs.Signature](#neo.fs.v2.refs.Signature) | | Signature of the body by the issuer. |
309+
310+
311+
<a name="neo.fs.v2.session.SessionTokenV2.Body"></a>
266312

267-
### Message SessionToken.Body.TokenLifetime
313+
### Message SessionTokenV2.Body
314+
Session Token body.
315+
316+
317+
| Field | Type | Label | Description |
318+
| ----- | ---- | ----- | ----------- |
319+
| version | [uint32](#uint32) | | Token version. |
320+
| id | [bytes](#bytes) | | Token identifier (UUIDv4 in binary form). |
321+
| issuer | [Target](#neo.fs.v2.session.Target) | | Account that issued this token (who signed it). |
322+
| subjects | [Target](#neo.fs.v2.session.Target) | repeated | Accounts authorized by this token (who can use it). |
323+
| lifetime | [TokenLifetime](#neo.fs.v2.session.TokenLifetime) | | Lifetime of this token. |
324+
| contexts | [SessionContextV2](#neo.fs.v2.session.SessionContextV2) | repeated | Unified session contexts for both object and container operations. Multiple contexts allow authorization for different combinations. |
325+
| delegation_chain | [DelegationInfo](#neo.fs.v2.session.DelegationInfo) | repeated | Full history of authority delegation (chain of trust). |
326+
327+
328+
<a name="neo.fs.v2.session.Target"></a>
329+
330+
### Message Target
331+
Target account for SessionTokenV2.
332+
It can be either direct (OwnerID) or indirect (NNS domain).
333+
334+
335+
| Field | Type | Label | Description |
336+
| ----- | ---- | ----- | ----------- |
337+
| owner_id | [neo.fs.v2.refs.OwnerID](#neo.fs.v2.refs.OwnerID) | | Direct account reference via OwnerID (hash of verification script). |
338+
| nns_name | [string](#string) | | Indirect account reference via NeoFS Name Service. NNS name is a domain name that resolves to an OwnerID through the NeoFS Name Service. The name must be a valid DNS-like domain name (e.g., "example.neofs") that is registered in the NNS contract on the Neo blockchain. The NNS record should contain a string record with the corresponding OwnerID value. |
339+
340+
341+
<a name="neo.fs.v2.session.TokenLifetime"></a>
342+
343+
### Message TokenLifetime
268344
Lifetime parameters of the token. Field names taken from rfc7519.
269345

270346

271347
| Field | Type | Label | Description |
272348
| ----- | ---- | ----- | ----------- |
273349
| exp | [uint64](#uint64) | | Expiration epoch, the last epoch when token is valid. |
274350
| nbf | [uint64](#uint64) | | Not valid before epoch, the first epoch when token is valid. |
275-
| iat | [uint64](#uint64) | | Issued at Epoch |
351+
| iat | [uint64](#uint64) | | Issued at epoch. |
276352

277353

278354
<a name="neo.fs.v2.session.XHeader"></a>
@@ -338,6 +414,27 @@ Object request verbs
338414
| RANGEHASH | 7 | Refers to object.GetRangeHash RPC call |
339415

340416

417+
418+
<a name="neo.fs.v2.session.SessionContextV2.Verb"></a>
419+
420+
### SessionContextV2.Verb
421+
Unified verbs for both object and container operations.
422+
423+
| Name | Number | Description |
424+
| ---- | ------ | ----------- |
425+
| VERB_UNSPECIFIED | 0 | Unknown verb. |
426+
| OBJECT_PUT | 1 | Refers to object.Put RPC call. |
427+
| OBJECT_GET | 2 | Refers to object.Get RPC call. |
428+
| OBJECT_HEAD | 3 | Refers to object.Head RPC call. |
429+
| OBJECT_SEARCH | 4 | Refers to object.Search RPC call. |
430+
| OBJECT_DELETE | 5 | Refers to object.Delete RPC call. |
431+
| OBJECT_RANGE | 6 | Refers to object.GetRange RPC call. |
432+
| OBJECT_RANGEHASH | 7 | Refers to object.GetRangeHash RPC call. |
433+
| CONTAINER_PUT | 8 | Refers to container.Put RPC call. |
434+
| CONTAINER_DELETE | 9 | Refers to container.Delete RPC call. |
435+
| CONTAINER_SETEACL | 10 | Refers to container.SetExtendedACL RPC call. |
436+
437+
341438
<!-- end enums -->
342439

343440

session/types.proto

Lines changed: 138 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,18 @@ message ContainerSessionContext {
8585
refs.ContainerID container_id = 3 [json_name = "containerID"];
8686
}
8787

88+
// Lifetime parameters of the token. Field names taken from rfc7519.
89+
message TokenLifetime {
90+
// Expiration epoch, the last epoch when token is valid.
91+
uint64 exp = 1 [json_name = "exp"];
92+
93+
// Not valid before epoch, the first epoch when token is valid.
94+
uint64 nbf = 2 [json_name = "nbf"];
95+
96+
// Issued at epoch.
97+
uint64 iat = 3 [json_name = "iat"];
98+
}
99+
88100
// NeoFS Session Token.
89101
message SessionToken {
90102
// Session Token body
@@ -95,17 +107,6 @@ message SessionToken {
95107
// Identifier of the session initiator
96108
neo.fs.v2.refs.OwnerID owner_id = 2 [json_name = "ownerID"];
97109

98-
// Lifetime parameters of the token. Field names taken from rfc7519.
99-
message TokenLifetime {
100-
// Expiration epoch, the last epoch when token is valid.
101-
uint64 exp = 1 [json_name = "exp"];
102-
103-
// Not valid before epoch, the first epoch when token is valid.
104-
uint64 nbf = 2 [json_name = "nbf"];
105-
106-
// Issued at Epoch
107-
uint64 iat = 3 [json_name = "iat"];
108-
}
109110
// Lifetime of the session
110111
TokenLifetime lifetime = 3 [json_name = "lifetime"];
111112

@@ -175,6 +176,10 @@ message RequestMetaHeader {
175176
// Session token within which the request is sent
176177
SessionToken session_token = 5 [json_name = "sessionToken"];
177178

179+
// Session token v2 with delegation chain support.
180+
// If both session_token and session_token_v2 are set, session_token_v2 takes precedence.
181+
SessionTokenV2 session_token_v2 = 9 [json_name = "sessionTokenV2"];
182+
178183
// `BearerToken` with eACL overrides for the request
179184
neo.fs.v2.acl.BearerToken bearer_token = 6 [json_name = "bearerToken"];
180185

@@ -232,3 +237,125 @@ message ResponseVerificationHeader {
232237
// Chain of previous hops signatures
233238
ResponseVerificationHeader origin = 4 [json_name = "origin"];
234239
}
240+
241+
// Session Token v2
242+
243+
// Target account for SessionTokenV2.
244+
// It can be either direct (OwnerID) or indirect (NNS domain).
245+
message Target {
246+
// Account identifier.
247+
oneof identifier {
248+
// Direct account reference via OwnerID (hash of verification script).
249+
neo.fs.v2.refs.OwnerID owner_id = 1 [json_name = "ownerID"];
250+
251+
// Indirect account reference via NeoFS Name Service.
252+
// NNS name is a domain name that resolves to an OwnerID through the
253+
// NeoFS Name Service. The name must be a valid DNS-like domain name
254+
// (e.g., "example.neofs") that is registered in the NNS contract on
255+
// the Neo blockchain. The NNS record should contain a string record with
256+
// the corresponding OwnerID value.
257+
string nns_name = 2 [json_name = "nnsName"];
258+
}
259+
}
260+
261+
// DelegationInfo represents a single delegation in a chain of trust.
262+
message DelegationInfo {
263+
// Account that performed this delegation.
264+
Target issuer = 1 [json_name = "issuer"];
265+
266+
// Account that received the delegation.
267+
Target subject = 2 [json_name = "subject"];
268+
269+
// Unix timestamp when this delegation was created.
270+
int64 timestamp = 3 [json_name = "timestamp"];
271+
272+
// List of verbs authorized by this delegation.
273+
repeated string verbs = 4 [json_name = "verbs"];
274+
275+
// Signature of the issuer confirming this delegation record.
276+
// The signature is created over the deterministic serialization
277+
// of this DelegationInfo message excluding this field.
278+
neo.fs.v2.refs.Signature signature = 5 [json_name = "signature"];
279+
}
280+
281+
// SessionContextV2 carries unified context for both ObjectService and ContainerService requests.
282+
message SessionContextV2 {
283+
// Unified verbs for both object and container operations.
284+
enum Verb {
285+
// Unknown verb.
286+
VERB_UNSPECIFIED = 0;
287+
288+
// Object operations
289+
290+
// Refers to object.Put RPC call.
291+
OBJECT_PUT = 1;
292+
// Refers to object.Get RPC call.
293+
OBJECT_GET = 2;
294+
// Refers to object.Head RPC call.
295+
OBJECT_HEAD = 3;
296+
// Refers to object.Search RPC call.
297+
OBJECT_SEARCH = 4;
298+
// Refers to object.Delete RPC call.
299+
OBJECT_DELETE = 5;
300+
// Refers to object.GetRange RPC call.
301+
OBJECT_RANGE = 6;
302+
// Refers to object.GetRangeHash RPC call.
303+
OBJECT_RANGEHASH = 7;
304+
305+
// Container operations
306+
307+
// Refers to container.Put RPC call.
308+
CONTAINER_PUT = 8;
309+
// Refers to container.Delete RPC call.
310+
CONTAINER_DELETE = 9;
311+
// Refers to container.SetExtendedACL RPC call.
312+
CONTAINER_SETEACL = 10;
313+
}
314+
315+
// Container where operation is allowed.
316+
// For container operations, this is the container being operated on.
317+
// For object operations, this is the container holding the objects.
318+
neo.fs.v2.refs.ContainerID container = 1 [json_name = "container"];
319+
320+
// Specific objects where operation is allowed.
321+
// Only relevant for object operations.
322+
// Empty list means all objects in the container.
323+
repeated neo.fs.v2.refs.ObjectID objects = 2 [json_name = "objects"];
324+
325+
// Operations authorized for this context.
326+
repeated Verb verbs = 3 [json_name = "verbs"];
327+
}
328+
329+
// SessionTokenV2 represents NeoFS Session Token with delegation support.
330+
message SessionTokenV2 {
331+
// Session Token body.
332+
message Body {
333+
// Token version.
334+
uint32 version = 1 [json_name = "version"];
335+
336+
// Token identifier (UUIDv4 in binary form).
337+
bytes id = 2 [json_name = "id"];
338+
339+
// Account that issued this token (who signed it).
340+
Target issuer = 3 [json_name = "issuer"];
341+
342+
// Accounts authorized by this token (who can use it).
343+
repeated Target subjects = 4 [json_name = "subjects"];
344+
345+
// Lifetime of this token.
346+
TokenLifetime lifetime = 5 [json_name = "lifetime"];
347+
348+
// Unified session contexts for both object and container operations.
349+
// Multiple contexts allow authorization for different combinations.
350+
repeated SessionContextV2 contexts = 6 [json_name = "contexts"];
351+
352+
// Full history of authority delegation (chain of trust).
353+
repeated DelegationInfo delegation_chain = 7 [json_name = "delegationChain"];
354+
}
355+
356+
// Session token body.
357+
Body body = 1 [json_name = "body"];
358+
359+
// Signature of the body by the issuer.
360+
neo.fs.v2.refs.Signature signature = 2 [json_name = "signature"];
361+
}

0 commit comments

Comments
 (0)