From 5c56a3fc1a44af9382ef621749327e7443a9f01e Mon Sep 17 00:00:00 2001 From: Christinarlong Date: Mon, 3 Nov 2025 13:39:38 -0800 Subject: [PATCH 1/3] update the refresh token docs to have manual refresh --- .../public-integration.mdx | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/docs/organization/integrations/integration-platform/public-integration.mdx b/docs/organization/integrations/integration-platform/public-integration.mdx index c4611ee3c6dc1..0de3a73fcdd1b 100644 --- a/docs/organization/integrations/integration-platform/public-integration.mdx +++ b/docs/organization/integrations/integration-platform/public-integration.mdx @@ -108,6 +108,46 @@ def refresh_token(install_id): return new_token ``` +### Refreshing Tokens Manually for Integrators +Sometimes incidents or other technical anomalies can lead to token refreshing being committed on the Sentry side but then the token is lost in transmission on the way back. As a result, we've added a method for integrators to explicitly refresh and request a new token for their installers. + +This manual refresh method uses a different authorization scheme where you will need to send a JWT signed with your client secret to the previous +`/api/0/sentry-app-installations/{}/authorizations/` endpoint with the below claims and payload. + +```python +def manual_token_refresh(install_id): + url = u'https://sentry.io/api/0/sentry-app-installations/{}/authorizations/' + url = url.format(install_id) + + now = datetime.now(timezone.utc).timestamp() + client_secret = "XXXX-XXXX-XXXX" + client_id = "1234-5678-9999" + iat = now + exp = now + 60 # 1 minute validity period + + claims = { + 'iss': client_id, + 'sub': client_id, + 'iat': iat, + 'exp': exp, + 'jti': uuid.uuid4(), + } + jwt_token = jwt.encode(claims, client_secret, algorithm="HS256") + headers = jwt.authorization_header(jwt_token) + + payload = { + 'grant_type': 'urn:sentry:params:oauth:grant-type:jwt-bearer', + } + resp = requests.post(url, json=payload, headers=headers) + data = resp.json() + + new_token = data['token'] + new_refresh_token = data['refreshToken'] + # ... Securely update the token and refresh_token in DB... + + return new_token +``` + The data you can expect back for both the initial grant code exchange and subsequent token refreshes is as follows: ```json From ecf67894dd000eef365a7bd9350447ee3e56aa52 Mon Sep 17 00:00:00 2001 From: Christinarlong Date: Mon, 3 Nov 2025 13:48:42 -0800 Subject: [PATCH 2/3] fix sentry comments --- .../integration-platform/public-integration.mdx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/organization/integrations/integration-platform/public-integration.mdx b/docs/organization/integrations/integration-platform/public-integration.mdx index 0de3a73fcdd1b..09ee7d78b7f5f 100644 --- a/docs/organization/integrations/integration-platform/public-integration.mdx +++ b/docs/organization/integrations/integration-platform/public-integration.mdx @@ -130,10 +130,14 @@ def manual_token_refresh(install_id): 'sub': client_id, 'iat': iat, 'exp': exp, - 'jti': uuid.uuid4(), + 'jti': str(uuid.uuid4()), } jwt_token = jwt.encode(claims, client_secret, algorithm="HS256") - headers = jwt.authorization_header(jwt_token) + headers = { + 'Authorization': f'Bearer {jwt_token}', + 'Content-Type': 'application/json' + } + payload = { 'grant_type': 'urn:sentry:params:oauth:grant-type:jwt-bearer', From 2fce2105eee8714b5abbd7acc0ea7dde33d2cc61 Mon Sep 17 00:00:00 2001 From: Christinarlong Date: Mon, 3 Nov 2025 14:12:48 -0800 Subject: [PATCH 3/3] update headers --- .../integration-platform/public-integration.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/organization/integrations/integration-platform/public-integration.mdx b/docs/organization/integrations/integration-platform/public-integration.mdx index 09ee7d78b7f5f..768d0e4965b6b 100644 --- a/docs/organization/integrations/integration-platform/public-integration.mdx +++ b/docs/organization/integrations/integration-platform/public-integration.mdx @@ -133,15 +133,15 @@ def manual_token_refresh(install_id): 'jti': str(uuid.uuid4()), } jwt_token = jwt.encode(claims, client_secret, algorithm="HS256") + headers = { 'Authorization': f'Bearer {jwt_token}', 'Content-Type': 'application/json' - } - - + } payload = { 'grant_type': 'urn:sentry:params:oauth:grant-type:jwt-bearer', } + resp = requests.post(url, json=payload, headers=headers) data = resp.json()