Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
a672066
feat(coderd/rbac): compare scopes by permission coverage
BobbyHo Aug 14, 2026
4315706
Merge branch 'main' into plat479-1-rbac-scope-coverage
BobbyHo Aug 17, 2026
787c461
docs(coderd/rbac): shorten the ScopesCover doc comment
BobbyHo Aug 17, 2026
2d39e04
Merge branch 'main' into plat479-1-rbac-scope-coverage
BobbyHo Aug 18, 2026
2f6c44e
fix(coderd/rbac): guard allowed-side org and user permissions
BobbyHo Aug 18, 2026
bd40270
refactor(coderd/rbac): drop the unreachable negative skip in coverage
BobbyHo Aug 18, 2026
3139c54
test(coderd/rbac): pin the scope coverage table's weak assertions
BobbyHo Aug 18, 2026
9276bb8
refactor(coderd/rbac): make the coverage guards reachable from tests
BobbyHo Aug 18, 2026
865eb9a
refactor(coderd/rbac): share the alias table and name the canonical c…
BobbyHo Aug 18, 2026
26a6bed
docs(coderd/rbac): document the expansion invariant where it can be b…
BobbyHo Aug 18, 2026
7cca7b3
Merge branch 'main' into plat479-1-rbac-scope-coverage
BobbyHo Aug 19, 2026
1678a77
test(coderd/rbac): pin the coverage guards at one strength
BobbyHo Aug 19, 2026
aba3c6c
fix(coderd/rbac): name the scope once in expansion errors
BobbyHo Aug 19, 2026
9a08105
docs(coderd/rbac): correct the external scope list contract
BobbyHo Aug 19, 2026
2fcce8b
docs(coderd/rbac): trim the restated coverage invariant
BobbyHo Aug 19, 2026
a775a48
docs(coderd/rbac): correct the negative permission cross-reference
BobbyHo Aug 19, 2026
e0c0d4a
docs(coderd/rbac): name every category IsExternalScope admits
BobbyHo Aug 19, 2026
189740d
test(coderd/rbac): pin the alias list invariants on the alias table
BobbyHo Aug 19, 2026
7d08e49
Merge branch 'main' into plat479-1-rbac-scope-coverage
BobbyHo Aug 19, 2026
ca4dc52
feat(coderd/oauth2provider): negotiate and persist authorization scope
BobbyHo Aug 14, 2026
f402e1a
feat(site): state the negotiated scope on the OAuth2 consent page
BobbyHo Aug 14, 2026
28ef5ba
fix(coderd/oauth2provider): return invalid_scope to the client's call…
BobbyHo Aug 14, 2026
62950d3
test(coderd/rbac): close two mutation gaps in scope coverage tests
BobbyHo Aug 20, 2026
554a703
Merge branch 'main' into plat479-2-negotiate-scope
BobbyHo Aug 20, 2026
09bc1b4
refactor(coderd/oauth2provider): rename validateRequestedScope to neg…
BobbyHo Aug 21, 2026
6fe6c71
test(coderd/oauth2provider): bind the wire-level scope reasons to the…
BobbyHo Aug 21, 2026
e296dda
refactor(coderd): log undecidable oauth2 scope coverage instead of re…
BobbyHo Aug 21, 2026
2760db5
fix(coderd/oauth2provider): name the stored allowlist when none of it…
BobbyHo Aug 21, 2026
5b2ca57
refactor(coderd/oauth2provider): reword the oauth2 scope rejection re…
BobbyHo Aug 21, 2026
90a6f83
docs: correct what the oauth2 authorize scope parameter promises
BobbyHo Aug 21, 2026
77654a1
test(coderd/oauth2provider): fold the no-allowlist guarantee into one…
BobbyHo Aug 21, 2026
dfdd33f
Merge branch 'plat479-2-negotiate-scope' into plat479-3-report-negoti…
BobbyHo Aug 22, 2026
ba8a123
Merge branch 'main' into plat479-2-negotiate-scope
BobbyHo Aug 22, 2026
bc40908
test(coderd): close the consent-page and state-echo test gaps
BobbyHo Aug 23, 2026
cb7df2e
fix(site/static): left-align the consent permission list
BobbyHo Aug 23, 2026
41e9f40
fix(coderd/oauth2provider): validate the callback scheme before any r…
BobbyHo Aug 23, 2026
a56cdac
fix(coderd/oauth2provider): replace callback query params instead of …
BobbyHo Aug 23, 2026
009962c
feat: say that the consent permission names are technical
BobbyHo Aug 23, 2026
f983335
fix: state unrestricted OAuth2 consent in its own field
BobbyHo Aug 23, 2026
81bbfb0
docs: trim OAuth2 scope negotiation comments
BobbyHo Aug 23, 2026
b7f88f0
Merge branch 'plat479-2-negotiate-scope' into plat479-3-report-negoti…
BobbyHo Aug 23, 2026
2a2e006
Merge branch 'main' into plat479-2-negotiate-scope
BobbyHo Aug 23, 2026
a11d42c
test(coderd/oauth2provider): pin the consent list semantics and the s…
BobbyHo Aug 23, 2026
eb37c2f
test(coderd/oauth2provider): cover the error redirect in the query-pa…
BobbyHo Aug 23, 2026
a5e3f94
fix(coderd/oauth2provider): answer 500 and log on an unusable callbac…
BobbyHo Aug 23, 2026
60cbe59
fix: refuse to render a consent page for a grant with no permissions
BobbyHo Aug 23, 2026
fdc9532
docs: document invalid_scope rejections on the authorize endpoint
BobbyHo Aug 23, 2026
e63ff3a
docs(coderd): trim the scope test comments
BobbyHo Aug 25, 2026
a9ade4a
Merge branch 'plat479-2-negotiate-scope' into plat479-3-report-negoti…
BobbyHo Aug 25, 2026
5589bf6
docs: trim the consent page and error redirect comments
BobbyHo Aug 25, 2026
24b4f46
docs(coderd): drop the godoc-style comments from the scope tests
BobbyHo Aug 25, 2026
b92b7f9
docs(coderd/oauth2provider): trim the authorize.go comments
BobbyHo Aug 25, 2026
e4cc41d
Merge branch 'plat479-2-negotiate-scope' into plat479-3-report-negoti…
BobbyHo Aug 25, 2026
20caa9c
docs(coderd): trim the scope reporting test comments
BobbyHo Aug 25, 2026
c7d3d53
docs(site): trim the consent page scope comments
BobbyHo Aug 25, 2026
78fbab1
Merge branch 'main' into plat479-2-negotiate-scope
BobbyHo Aug 26, 2026
f675cf1
fix(site/static): make the whole consent page reachable by scrolling
BobbyHo Aug 27, 2026
ceb3997
Update coderd/oauth2provider/authorize.go
BobbyHo Aug 31, 2026
2cd9b60
Update coderd/oauth2provider/authorize.go
BobbyHo Aug 31, 2026
d160bda
docs(coderd/oauth2provider): trim the errNoGrantableScope comment
BobbyHo Aug 31, 2026
b60eee6
refactor(coderd/oauth2provider): canonicalize scopes before the catal…
BobbyHo Aug 31, 2026
4732d1f
refactor(coderd/oauth2provider): canonicalize the allowlist in one pass
BobbyHo Aug 31, 2026
03df6a9
test(coderd/rbac): pin the field sets scope coverage reads
BobbyHo Aug 31, 2026
a2d2856
docs(coderd/rbac): trim the TestCoverageModelFields comment
BobbyHo Aug 31, 2026
d83636b
Merge branch 'plat479-2-negotiate-scope' into plat479-3-report-negoti…
BobbyHo Aug 31, 2026
037e744
Merge remote-tracking branch 'origin/main' into plat479-3-report-nego…
BobbyHo Aug 31, 2026
433e449
docs(coderd/oauth2provider): trim scope negotiation comments
BobbyHo Aug 31, 2026
35ce008
Merge branch 'main' into plat479-3-report-negotiated-scope
BobbyHo Aug 31, 2026
48d9c7f
fix(coderd/oauth2provider): return server_error for undecidable scope…
BobbyHo Sep 1, 2026
0278335
Merge branch 'main' into plat479-3-report-negotiated-scope
BobbyHo Sep 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
255 changes: 144 additions & 111 deletions coderd/oauth2provider/authorize.go

Large diffs are not rendered by default.

100 changes: 100 additions & 0 deletions coderd/oauth2provider/authorize_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ import (
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"

"cdr.dev/slog/v3/sloggers/slogtest"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/codersdk"
)

func TestNegotiateScope(t *testing.T) {
Expand Down Expand Up @@ -265,6 +267,58 @@ func TestNoScopeAllowlist(t *testing.T) {
assert.False(t, noScopeAllowlist(sql.NullString{String: " ", Valid: true}))
}

func TestScopeFailureResponse(t *testing.T) {
t.Parallel()

tests := []struct {
name string
err error
wantCode codersdk.OAuth2ErrorCode
wantDescription string
}{
{
name: "UnknownScope",
err: errUnknownScope,
wantCode: codersdk.OAuth2ErrorCodeInvalidScope,
wantDescription: errUnknownScope.Error(),
},
{
name: "NoGrantableScope",
err: errNoGrantableScope,
wantCode: codersdk.OAuth2ErrorCodeInvalidScope,
wantDescription: errNoGrantableScope.Error(),
},
{
name: "ScopeNotAllowed",
err: errScopeNotAllowed,
wantCode: codersdk.OAuth2ErrorCodeInvalidScope,
wantDescription: errScopeNotAllowed.Error(),
},
{
name: "CoverageUndecidable",
err: errCoverageUndecidable,
wantCode: codersdk.OAuth2ErrorCodeServerError,
wantDescription: "The requested scope could not be evaluated",
},
{
// The sentinel still decides the response once wrapped.
name: "WrappedCoverageUndecidable",
err: xerrors.Errorf("negotiate: %w", errCoverageUndecidable),
wantCode: codersdk.OAuth2ErrorCodeServerError,
wantDescription: "The requested scope could not be evaluated",
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
code, description := scopeFailureResponse(test.err)
require.Equal(t, test.wantCode, code)
require.Equal(t, test.wantDescription, description)
})
}
}

func TestHashOAuth2State(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -306,3 +360,49 @@ func TestHashOAuth2State(t *testing.T) {
"same state should produce identical hash")
})
}

func TestConsentScopes(t *testing.T) {
t.Parallel()

tests := []struct {
name string
granted string
want []string
wantUnrestricted bool
}{
{
name: "NarrowGrantListed",
granted: "workspace:ssh template:read",
want: []string{"workspace:ssh", "template:read"},
},
{
// nil, not the name: the page says "full access" instead.
name: "UnrestrictedAloneCollapses",
granted: string(database.ApiKeyScopeCoderAll),
want: nil,
wantUnrestricted: true,
},
{
name: "UnrestrictedAmongOthersCollapses",
granted: string(database.ApiKeyScopeCoderAll) + " coder:workspaces.access",
want: nil,
wantUnrestricted: true,
},
{
// Unreachable today: negotiateScope returns "" only with an error.
name: "EmptyGrantIsNotUnrestricted",
granted: "",
want: []string{},
wantUnrestricted: false,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
names, unrestricted := consentScopes(test.granted)
require.Equal(t, test.want, names)
require.Equal(t, test.wantUnrestricted, unrestricted)
})
}
}
Loading
Loading