Skip to content

Commit a3fe88d

Browse files
committed
feat: add public API key scope endpoint
Add /auth/scopes endpoint returning curated list of public low-level API key scopes (resource:action format). This read-only endpoint requires no authentication and provides SDK constants for all public scopes.
1 parent bf1e4e9 commit a3fe88d

11 files changed

Lines changed: 520 additions & 51 deletions

File tree

Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,7 @@ GEN_FILES := \
646646
coderd/rbac/object_gen.go \
647647
codersdk/rbacresources_gen.go \
648648
coderd/rbac/scopes_constants_gen.go \
649+
codersdk/apikey_scopes_gen.go \
649650
docs/admin/integrations/prometheus.md \
650651
docs/reference/cli/index.md \
651652
docs/admin/security/audit-logs.md \
@@ -846,6 +847,12 @@ codersdk/rbacresources_gen.go: scripts/typegen/codersdk.gotmpl scripts/typegen/m
846847
mv /tmp/rbacresources_gen.go codersdk/rbacresources_gen.go
847848
touch "$@"
848849

850+
codersdk/apikey_scopes_gen.go: scripts/apikeyscopesgen/main.go coderd/rbac/scopes_catalog.go coderd/rbac/scopes.go
851+
# Generate SDK constants for public low-level API key scopes.
852+
go run ./scripts/apikeyscopesgen > /tmp/apikey_scopes_gen.go
853+
mv /tmp/apikey_scopes_gen.go codersdk/apikey_scopes_gen.go
854+
touch "$@"
855+
849856
site/src/api/rbacresourcesGenerated.ts: site/node_modules/.installed scripts/typegen/codersdk.gotmpl scripts/typegen/main.go coderd/rbac/object.go coderd/rbac/policy/policy.go
850857
go run scripts/typegen/main.go rbac typescript > "$@"
851858
(cd site/ && pnpm exec biome format --write src/api/rbacresourcesGenerated.ts)

coderd/apidoc/swagger.json

Lines changed: 79 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package coderd_test
2+
3+
import (
4+
"context"
5+
"testing"
6+
"time"
7+
8+
"github.com/stretchr/testify/require"
9+
10+
"github.com/coder/coder/v2/coderd/coderdtest"
11+
"github.com/coder/coder/v2/codersdk"
12+
)
13+
14+
func TestTokenCreation_AllowsPublicLowLevelScope(t *testing.T) {
15+
client := coderdtest.New(t, nil)
16+
_ = coderdtest.CreateFirstUser(t, client)
17+
18+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
19+
defer cancel()
20+
21+
// Request a token with a public low-level scope
22+
resp, err := client.CreateToken(ctx, codersdk.Me, codersdk.CreateTokenRequest{
23+
Scope: codersdk.APIKeyScope("workspace:read"),
24+
})
25+
require.NoError(t, err)
26+
require.NotEmpty(t, resp.Key)
27+
}
28+
29+
func TestTokenCreation_RejectsInternalOnlyScope(t *testing.T) {
30+
client := coderdtest.New(t, nil)
31+
_ = coderdtest.CreateFirstUser(t, client)
32+
33+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
34+
defer cancel()
35+
36+
// debug_info:read is a valid RBAC pair but not public in the catalog
37+
_, err := client.CreateToken(ctx, codersdk.Me, codersdk.CreateTokenRequest{
38+
Scope: codersdk.APIKeyScope("debug_info:read"),
39+
})
40+
require.Error(t, err)
41+
}
42+
43+
func TestTokenCreation_AllowsLegacyScopes(t *testing.T) {
44+
client := coderdtest.New(t, nil)
45+
_ = coderdtest.CreateFirstUser(t, client)
46+
47+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
48+
defer cancel()
49+
50+
// Legacy: application_connect
51+
resp, err := client.CreateToken(ctx, codersdk.Me, codersdk.CreateTokenRequest{
52+
Scope: codersdk.APIKeyScopeApplicationConnect,
53+
})
54+
require.NoError(t, err)
55+
require.NotEmpty(t, resp.Key)
56+
}

coderd/scopes_catalog.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package coderd
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/coder/coder/v2/coderd/httpapi"
7+
"github.com/coder/coder/v2/coderd/rbac"
8+
)
9+
10+
// listPublicLowLevelScopes returns the curated list of public low-level
11+
// API key scopes (resource:action). This endpoint is read-only and does not
12+
// require authentication.
13+
//
14+
// @Summary List public low-level API key scopes
15+
// @ID list-public-low-level-scopes
16+
// @Produce json
17+
// @Tags Authorization
18+
// @Success 200 {array} string
19+
// @Router /auth/scopes [get]
20+
func (api *API) listPublicLowLevelScopes(rw http.ResponseWriter, r *http.Request) {
21+
httpapi.Write(r.Context(), rw, http.StatusOK, rbac.PublicLowLevelScopeNames())
22+
}

coderd/scopes_catalog_api_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package coderd_test
2+
3+
import (
4+
"encoding/json"
5+
"net/http"
6+
"testing"
7+
8+
"github.com/stretchr/testify/require"
9+
10+
"github.com/coder/coder/v2/coderd/coderdtest"
11+
"github.com/coder/coder/v2/coderd/rbac"
12+
)
13+
14+
func TestListPublicLowLevelScopes(t *testing.T) {
15+
client := coderdtest.New(t, nil)
16+
17+
res, err := client.Request(t.Context(), http.MethodGet, "/api/v2/auth/scopes", nil)
18+
require.NoError(t, err)
19+
defer res.Body.Close()
20+
require.Equal(t, http.StatusOK, res.StatusCode)
21+
22+
var got []string
23+
require.NoError(t, json.NewDecoder(res.Body).Decode(&got))
24+
25+
want := rbac.PublicLowLevelScopeNames()
26+
require.Equal(t, want, got)
27+
}

codersdk/apikey_scopes_gen.go

Lines changed: 88 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/reference/api/authorization.md

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)