Skip to content

Commit 45af0fd

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 45af0fd

File tree

2 files changed

+257
-16
lines changed

2 files changed

+257
-16
lines changed

proto-docs/session.md

Lines changed: 114 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,21 @@
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)
29+
- [SessionContextV2.Target](#neo.fs.v2.session.SessionContextV2.Target)
2730
- [SessionToken](#neo.fs.v2.session.SessionToken)
2831
- [SessionToken.Body](#neo.fs.v2.session.SessionToken.Body)
29-
- [SessionToken.Body.TokenLifetime](#neo.fs.v2.session.SessionToken.Body.TokenLifetime)
32+
- [SessionTokenV2](#neo.fs.v2.session.SessionTokenV2)
33+
- [SessionTokenV2.Body](#neo.fs.v2.session.SessionTokenV2.Body)
34+
- [Target](#neo.fs.v2.session.Target)
35+
- [TokenLifetime](#neo.fs.v2.session.TokenLifetime)
3036
- [XHeader](#neo.fs.v2.session.XHeader)
3137

3238

@@ -147,6 +153,21 @@ Context information for Session Tokens related to ContainerService requests.
147153
| 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. |
148154

149155

156+
<a name="neo.fs.v2.session.DelegationInfo"></a>
157+
158+
### Message DelegationInfo
159+
DelegationInfo represents a single delegation in a chain of trust.
160+
161+
162+
| Field | Type | Label | Description |
163+
| ----- | ---- | ----- | ----------- |
164+
| issuer | [Target](#neo.fs.v2.session.Target) | | Account that performed this delegation. |
165+
| subject | [Target](#neo.fs.v2.session.Target) | | Account that received the delegation. |
166+
| timestamp | [int64](#int64) | | Unix timestamp when this delegation was created. |
167+
| verbs | [string](#string) | repeated | List of verbs authorized by this delegation. |
168+
| 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. |
169+
170+
150171
<a name="neo.fs.v2.session.ObjectSessionContext"></a>
151172

152173
### Message ObjectSessionContext
@@ -185,6 +206,7 @@ request meta headers are folded in matryoshka style.
185206
| ttl | [uint32](#uint32) | | Maximum number of intermediate nodes in the request route |
186207
| x_headers | [XHeader](#neo.fs.v2.session.XHeader) | repeated | Request X-Headers |
187208
| session_token | [SessionToken](#neo.fs.v2.session.SessionToken) | | Session token within which the request is sent |
209+
| 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. |
188210
| bearer_token | [neo.fs.v2.acl.BearerToken](#neo.fs.v2.acl.BearerToken) | | `BearerToken` with eACL overrides for the request |
189211
| origin | [RequestMetaHeader](#neo.fs.v2.session.RequestMetaHeader) | | `RequestMetaHeader` of the origin request |
190212
| magic_number | [uint64](#uint64) | | NeoFS network magic. Must match the value for the network that the server belongs to. |
@@ -234,6 +256,30 @@ Verification info for the response signed by all intermediate nodes
234256
| origin | [ResponseVerificationHeader](#neo.fs.v2.session.ResponseVerificationHeader) | | Chain of previous hops signatures |
235257

236258

259+
<a name="neo.fs.v2.session.SessionContextV2"></a>
260+
261+
### Message SessionContextV2
262+
SessionContextV2 carries unified context for both ObjectService and ContainerService requests.
263+
264+
265+
| Field | Type | Label | Description |
266+
| ----- | ---- | ----- | ----------- |
267+
| verbs | [SessionContextV2.Verb](#neo.fs.v2.session.SessionContextV2.Verb) | repeated | Multiple verbs this context authorizes. |
268+
| targets | [SessionContextV2.Target](#neo.fs.v2.session.SessionContextV2.Target) | repeated | Target resource(s) this authorization applies to. |
269+
270+
271+
<a name="neo.fs.v2.session.SessionContextV2.Target"></a>
272+
273+
### Message SessionContextV2.Target
274+
Target resource specification.
275+
276+
277+
| Field | Type | Label | Description |
278+
| ----- | ---- | ----- | ----------- |
279+
| 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. |
280+
| 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. |
281+
282+
237283
<a name="neo.fs.v2.session.SessionToken"></a>
238284

239285
### Message SessionToken
@@ -256,23 +302,65 @@ Session Token body
256302
| ----- | ---- | ----- | ----------- |
257303
| id | [bytes](#bytes) | | Token identifier is a valid UUIDv4 in binary form |
258304
| 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 |
305+
| lifetime | [TokenLifetime](#neo.fs.v2.session.TokenLifetime) | | Lifetime of the session |
260306
| session_key | [bytes](#bytes) | | Public key used in session |
261307
| object | [ObjectSessionContext](#neo.fs.v2.session.ObjectSessionContext) | | ObjectService session context |
262308
| container | [ContainerSessionContext](#neo.fs.v2.session.ContainerSessionContext) | | ContainerService session context |
263309

264310

265-
<a name="neo.fs.v2.session.SessionToken.Body.TokenLifetime"></a>
311+
<a name="neo.fs.v2.session.SessionTokenV2"></a>
312+
313+
### Message SessionTokenV2
314+
SessionTokenV2 represents NeoFS Session Token with delegation support.
266315

267-
### Message SessionToken.Body.TokenLifetime
316+
317+
| Field | Type | Label | Description |
318+
| ----- | ---- | ----- | ----------- |
319+
| body | [SessionTokenV2.Body](#neo.fs.v2.session.SessionTokenV2.Body) | | Session token body. |
320+
| signature | [neo.fs.v2.refs.Signature](#neo.fs.v2.refs.Signature) | | Signature of the body by the issuer. |
321+
322+
323+
<a name="neo.fs.v2.session.SessionTokenV2.Body"></a>
324+
325+
### Message SessionTokenV2.Body
326+
Session Token body.
327+
328+
329+
| Field | Type | Label | Description |
330+
| ----- | ---- | ----- | ----------- |
331+
| version | [uint32](#uint32) | | Token version. |
332+
| id | [bytes](#bytes) | | Token identifier (UUIDv4 in binary form). |
333+
| issuer | [Target](#neo.fs.v2.session.Target) | | Account that issued this token (who signed it). |
334+
| subjects | [Target](#neo.fs.v2.session.Target) | repeated | Accounts authorized by this token (who can use it). |
335+
| lifetime | [TokenLifetime](#neo.fs.v2.session.TokenLifetime) | | Lifetime of this token. |
336+
| context | [SessionContextV2](#neo.fs.v2.session.SessionContextV2) | | Unified session context for both object and container operations. |
337+
| delegation_chain | [DelegationInfo](#neo.fs.v2.session.DelegationInfo) | repeated | Full history of authority delegation (chain of trust). |
338+
339+
340+
<a name="neo.fs.v2.session.Target"></a>
341+
342+
### Message Target
343+
Target account for SessionTokenV2.
344+
It can be either direct (OwnerID) or indirect (NNS domain).
345+
346+
347+
| Field | Type | Label | Description |
348+
| ----- | ---- | ----- | ----------- |
349+
| owner_id | [neo.fs.v2.refs.OwnerID](#neo.fs.v2.refs.OwnerID) | | Direct account reference via OwnerID (hash of verification script). |
350+
| 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. |
351+
352+
353+
<a name="neo.fs.v2.session.TokenLifetime"></a>
354+
355+
### Message TokenLifetime
268356
Lifetime parameters of the token. Field names taken from rfc7519.
269357

270358

271359
| Field | Type | Label | Description |
272360
| ----- | ---- | ----- | ----------- |
273361
| exp | [uint64](#uint64) | | Expiration epoch, the last epoch when token is valid. |
274362
| nbf | [uint64](#uint64) | | Not valid before epoch, the first epoch when token is valid. |
275-
| iat | [uint64](#uint64) | | Issued at Epoch |
363+
| iat | [uint64](#uint64) | | Issued at epoch. |
276364

277365

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

340428

429+
430+
<a name="neo.fs.v2.session.SessionContextV2.Verb"></a>
431+
432+
### SessionContextV2.Verb
433+
Unified verbs for both object and container operations.
434+
435+
| Name | Number | Description |
436+
| ---- | ------ | ----------- |
437+
| VERB_UNSPECIFIED | 0 | Unknown verb. |
438+
| OBJECT_PUT | 1 | Refers to object.Put RPC call. |
439+
| OBJECT_GET | 2 | Refers to object.Get RPC call. |
440+
| OBJECT_HEAD | 3 | Refers to object.Head RPC call. |
441+
| OBJECT_SEARCH | 4 | Refers to object.Search RPC call. |
442+
| OBJECT_DELETE | 5 | Refers to object.Delete RPC call. |
443+
| OBJECT_RANGE | 6 | Refers to object.GetRange RPC call. |
444+
| OBJECT_RANGEHASH | 7 | Refers to object.GetRangeHash RPC call. |
445+
| CONTAINER_PUT | 8 | Refers to container.Put RPC call. |
446+
| CONTAINER_DELETE | 9 | Refers to container.Delete RPC call. |
447+
| CONTAINER_SETEACL | 10 | Refers to container.SetExtendedACL RPC call. |
448+
449+
341450
<!-- end enums -->
342451

343452

session/types.proto

Lines changed: 143 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,130 @@ 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+
// Multiple verbs this context authorizes.
316+
repeated Verb verbs = 1 [json_name = "verbs"];
317+
318+
// Target resource specification.
319+
message Target {
320+
// Container where operation is allowed.
321+
// For container operations, this is the container being operated on.
322+
// For object operations, this is the container holding the objects.
323+
neo.fs.v2.refs.ContainerID container = 1 [json_name = "container"];
324+
325+
// Specific objects where operation is allowed.
326+
// Only relevant for object operations.
327+
// Empty list means all objects in the container.
328+
repeated neo.fs.v2.refs.ObjectID objects = 2 [json_name = "objects"];
329+
}
330+
331+
// Target resource(s) this authorization applies to.
332+
repeated Target targets = 2 [json_name = "targets"];
333+
}
334+
335+
// SessionTokenV2 represents NeoFS Session Token with delegation support.
336+
message SessionTokenV2 {
337+
// Session Token body.
338+
message Body {
339+
// Token version.
340+
uint32 version = 1 [json_name = "version"];
341+
342+
// Token identifier (UUIDv4 in binary form).
343+
bytes id = 2 [json_name = "id"];
344+
345+
// Account that issued this token (who signed it).
346+
Target issuer = 3 [json_name = "issuer"];
347+
348+
// Accounts authorized by this token (who can use it).
349+
repeated Target subjects = 4 [json_name = "subjects"];
350+
351+
// Lifetime of this token.
352+
TokenLifetime lifetime = 5 [json_name = "lifetime"];
353+
354+
// Unified session context for both object and container operations.
355+
SessionContextV2 context = 6 [json_name = "context"];
356+
357+
// Full history of authority delegation (chain of trust).
358+
repeated DelegationInfo delegation_chain = 7 [json_name = "delegationChain"];
359+
}
360+
361+
// Session token body.
362+
Body body = 1 [json_name = "body"];
363+
364+
// Signature of the body by the issuer.
365+
neo.fs.v2.refs.Signature signature = 2 [json_name = "signature"];
366+
}

0 commit comments

Comments
 (0)