You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
*`PREFIX`: `sntrys_` - this is static and helps to identify this is a Sentry token.
65
+
*`FACTS`: A base64 encoded JSON string of the facts.
66
+
*`SECRET`: A random secret part for the token. We may use `b64encode(secrets.token_bytes(32)).decode("ascii").rstrip("=")`, but this is an implementation detail.
65
67
66
68
A serialized token is added a custom prefix `sntrys_` (sentry structure) to make
67
69
it possible to detect it by security scrapers. Anyone handling such a token is
68
70
required to check for the `sntrys_` prefix and disregard it before parsing it. This
69
71
can also be used by the client side to detect a structural token if the client is
70
72
interested in extracting data from the token.
71
73
74
+
The purpose of the secret is that the resulting token is not guessable. It should be a randomly generated string that is different for each token.
75
+
72
76
## Token Facts
73
77
74
78
We want to encode certain information into the tokens. The following attributes are defined:
75
79
76
-
*`iss`: The value `sentry.io` indicates that this is a Sentry Org Auth Token.
77
-
*`nonce`: A randomly generated UUID to ensure the token content cannot be guessed.
78
-
*`sentry_url`: references the root domain to be used. A token will always have a
80
+
*`iat`: Timestamp when the token was issued.
81
+
*`url`: references the root domain to be used. A token will always have a
79
82
url in it and clients are not supposed to provide a fallback. This value can be found in `settings.SENTRY_OPTIONS["system.url-prefix"]`. Some APIs are only available on this URL, not on the region URL (see below). e.x. `https://sentry.io/`.
80
-
*`sentry_region_url`: The domain that the organization's API endpoints are available on. This value can be found in `organization.links.regionUrl`. e.x. `http://us.sentry.io`.
81
-
*`sentry_org`: a token is uniquely bound to an org, so the slug of that org is also always
83
+
*`region_url`: The domain that the organization's API endpoints are available on. This value can be found in `organization.links.regionUrl`. e.x. `http://us.sentry.io`.
84
+
*`org`: a token is uniquely bound to an org, so the slug of that org is also always
82
85
contained. Note that the slug is used rather than an org ID as the clients typically
83
86
need these slugs to create API requests.
84
87
85
88
These facts are encoded in the JWT as custom claims:
86
89
87
90
```json
88
91
{
89
-
"iss": "sentry.io",
90
92
"iat": 1684154626,
91
-
"nonce": "abcd-efgh-ijkl-mnop",
92
-
"sentry_region_url": "https://eu.sentry.io/",
93
-
"sentry_url": "https://sentry.io/",
94
-
"sentry_org": "myorg"
93
+
"region_url": "https://eu.sentry.io/",
94
+
"url": "https://sentry.io/",
95
+
"org": "myorg"
95
96
}
96
97
```
97
98
98
-
Encoded the token then is be `sntrys_{encoded_jwt}`.
99
+
Encoded the token then is be `sntrys_{encoded_facts}_secret`.
99
100
100
101
## Token Storage
101
102
@@ -115,26 +116,23 @@ unaware of the structure behind structural tokens nothing changes.
115
116
Clients are strongly encouraged to parse out the containing structure of the token and
116
117
to use this information to route requests. For the keys the following rules apply:
117
118
118
-
*`sentry_url` & `sentry_region_url`: references the target API URL that should be used. A token
119
+
*`url` & `region_url`: references the target API URL that should be used. A token
119
120
will always have a site in it and clients are not supposed to provide an
120
121
automatic fallback.
121
122
*`org`: a token is uniquely bound to an org, so the slug of that org is also always
122
123
contained. Note that the slug is used rather than an org ID as the clients typically
123
124
need these slugs to create API requests.
124
125
125
-
An example of this with a JWT token:
126
+
An example of parsing the token content with python:
126
127
127
-
```python
128
-
>>>import jwt
129
-
>>> tok ="sntrys_eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzZW50cnkuaW8iLCJpYXQiOjE2ODQxNTQ2MjYsInNlbnRyeV9zaXRlIjoiaHR0cHM6Ly9teW9yZy5zZW50cnkuaW8vIiwic2VudHJ5X29yZyI6Im15b3JnIiwic2VudHJ5X3Byb2plY3RzIjpbIm15cHJvamVjdCJdfQ.ROnK3f72jGbH2CLkmswMIxXP1qZHDish9lN6kfCR0DU"
@@ -271,6 +269,12 @@ globally unique IDs. However this today does not work for a handful of reasons:
271
269
for frontend + backend deployment scenarios being able to use one token to manage releases
272
270
across projects might be desirable.
273
271
272
+
## Why not JWT?
273
+
274
+
We initially set out to try to use JWT as a format. However, since we are not interested in signing the tokens (which is a fundamental concept of JWT), this lead to problems. Skipping signing means we have to use `algorithm='none'`, which is not very well supported. When using this algorithm, the resulting tokens always end in `.`, as the final part would be based on the key, which is missing. Having a trailing `.` after each token is an unnecessary error source (users may not copy it, ...). We _could_ try to handle this when decoding, but this would still make this technically invalid JWT.
275
+
276
+
Since we do not need signing/verification of the token client side, we decided against using JWT as a format.
277
+
274
278
## Why not PASETO?
275
279
276
280
PASETO as an alternative to JWT can be an option. This should probably be decided based on what
0 commit comments