Add JSON response format to access token creation (#2662)

* Add JSON response format to access token creation

Allows programmatic clients (e.g., CLI) to create access tokens
via the API by returning the token, description, and permission
as JSON instead of redirecting to the HTML show page.

* Add JSON response format to access token creation

Allows programmatic clients (e.g., CLI) to create access tokens
via the API by returning the token, description, and permission
as JSON instead of redirecting to the HTML show page.

* Style

---------

Co-authored-by: David Heinemeier Hansson <david@hey.com>
This commit is contained in:
Rob Zolkos
2026-03-05 12:48:51 -05:00
committed by GitHub
parent 734549a4cf
commit 8ee2d7d65f
3 changed files with 88 additions and 2 deletions
+10 -1
View File
@@ -15,11 +15,20 @@ class My::AccessTokensController < ApplicationController
def create
access_token = my_access_tokens.create!(access_token_params)
expiring_id = verifier.generate access_token.id, expires_in: 10.seconds
respond_to do |format|
format.html do
expiring_id = verifier.generate access_token.id, expires_in: 10.seconds
redirect_to my_access_token_path(expiring_id)
end
format.json do
render status: :created, json: \
{ token: access_token.token, description: access_token.description, permission: access_token.permission }
end
end
end
def destroy
my_access_tokens.find(params[:id]).destroy!
redirect_to my_access_tokens_path
+42
View File
@@ -134,6 +134,48 @@ __Response:__
Returns `204 No Content` on success.
#### Create an access token via the API
You can programmatically create a personal access token using either a session cookie or an existing Bearer token:
```bash
curl -X POST \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "Cookie: session_token=eyJfcmFpbHMi..." \
-d '{"access_token": {"description": "Fizzy CLI", "permission": "write"}}' \
https://app.fizzy.do/1234567/my/access_tokens
```
Or with a Bearer token (must have `write` permission):
```bash
curl -X POST \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "Authorization: Bearer put-your-access-token-here" \
-d '{"access_token": {"description": "Fizzy CLI", "permission": "write"}}' \
https://app.fizzy.do/1234567/my/access_tokens
```
The `permission` field accepts `read` or `write`.
__Response:__
```
HTTP/1.1 201 Created
```
```json
{
"token": "4f9Q6d2wXr8Kp1Ls0Vz3BnTa",
"description": "Fizzy CLI",
"permission": "write"
}
```
Store the `token` value securely — it won't be retrievable again. Use it as a Bearer token for subsequent API requests.
## Caching
Most endpoints return [ETag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/ETag) and [Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control) headers. You can use these to avoid re-downloading unchanged data.
@@ -19,6 +19,41 @@ class My::AccessTokensControllerTest < ActionDispatch::IntegrationTest
end
end
test "create new token via JSON with session" do
assert_difference -> { identities(:kevin).access_tokens.count }, +1 do
post my_access_tokens_path, params: { access_token: { description: "Fizzy CLI", permission: "write" } }, as: :json
end
assert_response :created
body = @response.parsed_body
assert body["token"].present?
assert_equal "Fizzy CLI", body["description"]
assert_equal "write", body["permission"]
end
test "create new token via JSON with bearer token" do
sign_out
bearer_token = { "HTTP_AUTHORIZATION" => "Bearer #{identity_access_tokens(:davids_api_token).token}" }
assert_difference -> { identities(:david).access_tokens.count }, +1 do
post my_access_tokens_path, params: { access_token: { description: "Fizzy CLI", permission: "read" } }, env: bearer_token, as: :json
end
assert_response :created
body = @response.parsed_body
assert body["token"].present?
assert_equal "Fizzy CLI", body["description"]
assert_equal "read", body["permission"]
end
test "cannot create new token via JSON with read-only bearer token" do
sign_out
bearer_token = { "HTTP_AUTHORIZATION" => "Bearer #{identity_access_tokens(:jasons_api_token).token}" }
assert_no_difference -> { identities(:jason).access_tokens.count } do
post my_access_tokens_path, params: { access_token: { description: "Fizzy CLI", permission: "read" } }, env: bearer_token, as: :json
end
assert_response :unauthorized
end
test "accessing new token after reveal window redirects to index" do
assert_changes -> { identities(:kevin).access_tokens.count }, +1 do
post my_access_tokens_path, params: { access_token: { description: "GitHub", permission: "read" } }