Skip to content

Commit 068aa3d

Browse files
committed
Doc changes for client secret rotation
1 parent 388ecc9 commit 068aa3d

File tree

2 files changed

+208
-0
lines changed

2 files changed

+208
-0
lines changed

docs/hydra/guides/oauth2-clients.mdx

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,128 @@ See [API documentation](../../reference/api#tag/oAuth2/operation/patchOAuth2Clie
131131
</Tabs>
132132
````
133133

134+
## Rotate OAuth2 client secret
135+
136+
OAuth2 client secret rotation allows you to change a client's secret without downtime. When you rotate a secret, the old secret remains valid until you explicitly clean it up, allowing you to update all services using the client credentials without service interruption.
137+
138+
### How secret rotation works
139+
140+
1. **Rotate the secret**: Generate a new secret for the client
141+
2. **Both secrets work**: Old and new secrets both authenticate until cleanup
142+
3. **Update services**: Update your applications to use the new secret
143+
4. **Cleanup**: Manually remove old rotated secrets once all services are updated
144+
145+
### Rotate client secret
146+
147+
To rotate an OAuth2 client secret, use the following methods:
148+
149+
````mdx-code-block
150+
<Tabs>
151+
<TabItem value="rest" label="REST API">
152+
153+
```bash
154+
curl -X POST https://{project.slug}.projects.oryapis.com/admin/clients/{client-id}/secret/rotate \
155+
-H "Authorization: Bearer ory_pat_..."
156+
```
157+
158+
The response includes the new `client_secret`. **Save this value immediately** - it will not be shown again.
159+
160+
See [API documentation](../../reference/api#tag/oAuth2/operation/oauth2RotateClientSecret).
161+
162+
</TabItem>
163+
<TabItem value="sdk" label="Ory SDK">
164+
165+
```typescript
166+
import { Configuration, OAuth2Api } from "@ory/client"
167+
168+
const ory = new OAuth2Api(
169+
new Configuration({
170+
basePath: `https://${projectSlug}.projects.oryapis.com`,
171+
accessToken: "ory_pat_..."
172+
})
173+
)
174+
175+
const { data: client } = await ory.oauth2RotateClientSecret({
176+
id: clientId
177+
})
178+
179+
// Save the new client_secret immediately
180+
console.log("New secret:", client.client_secret)
181+
```
182+
183+
</TabItem>
184+
</Tabs>
185+
````
186+
187+
### Clear rotated secrets
188+
189+
Once all services have been updated to use the new secret, you can remove the old rotated secrets to revoke access using the old credentials:
190+
191+
````mdx-code-block
192+
<Tabs>
193+
<TabItem value="rest" label="REST API">
194+
195+
```bash
196+
curl -X DELETE https://{project.slug}.projects.oryapis.com/admin/clients/{client-id}/secret/rotate \
197+
-H "Authorization: Bearer ory_pat_..."
198+
```
199+
200+
After cleanup, only the current secret will be valid. Old secrets will no longer authenticate.
201+
202+
See [API documentation](../../reference/api#tag/oAuth2/operation/oauth2DeleteRotatedClientSecrets).
203+
204+
</TabItem>
205+
<TabItem value="sdk" label="Ory SDK">
206+
207+
```typescript
208+
await ory.oauth2DeleteRotatedClientSecrets({
209+
id: clientId
210+
})
211+
212+
// Old secrets are now revoked
213+
```
214+
215+
</TabItem>
216+
</Tabs>
217+
````
218+
219+
### Secret rotation workflow example
220+
221+
Here's a complete workflow for rotating a client secret:
222+
223+
```bash
224+
# 1. Get current client
225+
CLIENT_ID="your-client-id"
226+
227+
# 2. Rotate the secret
228+
NEW_SECRET=$(curl -X POST "https://{project.slug}.projects.oryapis.com/admin/clients/$CLIENT_ID/secret/rotate" \
229+
-H "Authorization: Bearer ory_pat_..." | jq -r '.client_secret')
230+
231+
echo "New secret: $NEW_SECRET"
232+
233+
# 3. Update your applications with the new secret
234+
# (Both old and new secrets work during this period)
235+
236+
# 4. Verify the new secret works
237+
curl -X POST "https://{project.slug}.projects.oryapis.com/oauth2/token" \
238+
-u "$CLIENT_ID:$NEW_SECRET" \
239+
-d "grant_type=client_credentials"
240+
241+
# 5. Once all services are updated, clean up old secrets
242+
curl -X DELETE "https://{project.slug}.projects.oryapis.com/admin/clients/$CLIENT_ID/secret/rotate" \
243+
-H "Authorization: Bearer ory_pat_..."
244+
245+
# Old secret is now revoked
246+
```
247+
248+
:::tip Zero-downtime credential rotation
249+
Secret rotation enables zero-downtime credential updates. Both the old and new secrets remain valid until you manually clean up the rotated secrets, allowing you to update all your services without service interruption.
250+
:::
251+
252+
:::warning Security best practice
253+
Rotated secrets remain valid indefinitely until you explicitly clean them up. Always remove old rotated secrets once your migration is complete to ensure that compromised credentials cannot be used.
254+
:::
255+
134256
## Delete OAuth2 client
135257

136258
To delete an existing OAuth2 client, use the following methods:

docs/reference/api.json

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6914,6 +6914,10 @@
69146914
"format": "date-time",
69156915
"type": "string"
69166916
},
6917+
"rotated_secrets": {
6918+
"description": "OAuth 2.0 Client Rotated Secrets\n\nRotatedSecrets holds previously rotated secrets that are still valid for authentication. This allows for secret rotation without downtime. The secrets are stored in hashed format and remain valid until explicitly cleaned up.",
6919+
"type": "string"
6920+
},
69176921
"userinfo_signed_response_alg": {
69186922
"description": "OpenID Connect Request Userinfo Signed Response Algorithm\n\nJWS alg algorithm [JWA] REQUIRED for signing UserInfo Responses. If this is specified, the response will be JWT\n[JWT] serialized, and signed using JWS. The default, if omitted, is for the UserInfo Response to return the Claims\nas a UTF-8 encoded JSON object using the application/json content-type.",
69196923
"type": "string"
@@ -11948,6 +11952,88 @@
1194811952
"tags": ["oAuth2"]
1194911953
}
1195011954
},
11955+
"/admin/clients/{id}/secret/rotate": {
11956+
"post": {
11957+
"description": "Rotate the client secret of an OAuth 2.0 Client. The old secret will be moved to the rotated_secrets list and will remain valid until explicitly cleaned up. This allows for zero-downtime credential rotation.\n\nThe new secret will be returned in the response. Make sure to store it securely as it will not be retrievable later.",
11958+
"operationId": "oauth2RotateClientSecret",
11959+
"parameters": [
11960+
{
11961+
"description": "OAuth 2.0 Client ID",
11962+
"in": "path",
11963+
"name": "id",
11964+
"required": true,
11965+
"schema": {
11966+
"type": "string"
11967+
}
11968+
}
11969+
],
11970+
"responses": {
11971+
"200": {
11972+
"content": {
11973+
"application/json": {
11974+
"schema": {
11975+
"$ref": "#/components/schemas/oAuth2Client"
11976+
}
11977+
}
11978+
},
11979+
"description": "oAuth2Client"
11980+
},
11981+
"404": {
11982+
"$ref": "#/components/responses/errorOAuth2NotFound"
11983+
},
11984+
"default": {
11985+
"$ref": "#/components/responses/errorOAuth2Default"
11986+
}
11987+
},
11988+
"security": [
11989+
{
11990+
"oryAccessToken": []
11991+
}
11992+
],
11993+
"summary": "Rotate OAuth2 Client Secret",
11994+
"tags": ["oAuth2"]
11995+
},
11996+
"delete": {
11997+
"description": "Delete all rotated secrets from an OAuth 2.0 Client. This will revoke access for all previously rotated secrets, leaving only the current secret valid.\n\nUse this endpoint after you have updated all services to use the new secret following a rotation.",
11998+
"operationId": "oauth2DeleteRotatedClientSecrets",
11999+
"parameters": [
12000+
{
12001+
"description": "OAuth 2.0 Client ID",
12002+
"in": "path",
12003+
"name": "id",
12004+
"required": true,
12005+
"schema": {
12006+
"type": "string"
12007+
}
12008+
}
12009+
],
12010+
"responses": {
12011+
"200": {
12012+
"content": {
12013+
"application/json": {
12014+
"schema": {
12015+
"$ref": "#/components/schemas/oAuth2Client"
12016+
}
12017+
}
12018+
},
12019+
"description": "oAuth2Client"
12020+
},
12021+
"404": {
12022+
"$ref": "#/components/responses/errorOAuth2NotFound"
12023+
},
12024+
"default": {
12025+
"$ref": "#/components/responses/errorOAuth2Default"
12026+
}
12027+
},
12028+
"security": [
12029+
{
12030+
"oryAccessToken": []
12031+
}
12032+
],
12033+
"summary": "Delete OAuth2 Client Rotated Secrets",
12034+
"tags": ["oAuth2"]
12035+
}
12036+
},
1195112037
"/admin/courier/messages": {
1195212038
"get": {
1195312039
"description": "Lists all messages by given status and recipient.",

0 commit comments

Comments
 (0)