46 Commits

Author SHA1 Message Date
6f2834f62a feat: oauth2 - add authorization server metadata endpoint and PKCE support (#18548)
## Summary

  This PR implements critical MCP OAuth2 compliance features for Coder's authorization server, adding PKCE support, resource parameter handling, and OAuth2 server metadata discovery. This brings Coder's OAuth2 implementation significantly closer to production readiness for MCP (Model Context Protocol)
  integrations.

  ## What's Added

  ### OAuth2 Authorization Server Metadata (RFC 8414)
  - Add `/.well-known/oauth-authorization-server` endpoint for automatic client discovery
  - Returns standardized metadata including supported grant types, response types, and PKCE methods
  - Essential for MCP client compatibility and OAuth2 standards compliance

  ### PKCE Support (RFC 7636)
  - Implement Proof Key for Code Exchange with S256 challenge method
  - Add `code_challenge` and `code_challenge_method` parameters to authorization flow
  - Add `code_verifier` validation in token exchange
  - Provides enhanced security for public clients (mobile apps, CLIs)

  ### Resource Parameter Support (RFC 8707)
  - Add `resource` parameter to authorization and token endpoints
  - Store resource URI and bind tokens to specific audiences
  - Critical for MCP's resource-bound token model

  ### Enhanced OAuth2 Error Handling
  - Add OAuth2-compliant error responses with proper error codes
  - Use standard error format: `{"error": "code", "error_description": "details"}`
  - Improve error consistency across OAuth2 endpoints

  ### Authorization UI Improvements
  - Fix authorization flow to use POST-based consent instead of GET redirects
  - Remove dependency on referer headers for security decisions
  - Improve CSRF protection with proper state parameter validation

  ## Why This Matters

  **For MCP Integration:** MCP requires OAuth2 authorization servers to support PKCE, resource parameters, and metadata discovery. Without these features, MCP clients cannot securely authenticate with Coder.

  **For Security:** PKCE prevents authorization code interception attacks, especially critical for public clients. Resource binding ensures tokens are only valid for intended services.

  **For Standards Compliance:** These are widely adopted OAuth2 extensions that improve interoperability with modern OAuth2 clients.

  ## Database Changes

  - **Migration 000343:** Adds `code_challenge`, `code_challenge_method`, `resource_uri` to `oauth2_provider_app_codes`
  - **Migration 000343:** Adds `audience` field to `oauth2_provider_app_tokens` for resource binding
  - **Audit Updates:** New OAuth2 fields properly tracked in audit system
  - **Backward Compatibility:** All changes maintain compatibility with existing OAuth2 flows

  ## Test Coverage

  - Comprehensive PKCE test suite in `coderd/identityprovider/pkce_test.go`
  - OAuth2 metadata endpoint tests in `coderd/oauth2_metadata_test.go`
  - Integration tests covering PKCE + resource parameter combinations
  - Negative tests for invalid PKCE verifiers and malformed requests

  ## Testing Instructions

  ```bash
  # Run the comprehensive OAuth2 test suite
  ./scripts/oauth2/test-mcp-oauth2.sh

  Manual Testing with Interactive Server

  # Start Coder in development mode
  ./scripts/develop.sh

  # In another terminal, set up test app and run interactive flow
  eval $(./scripts/oauth2/setup-test-app.sh)
  ./scripts/oauth2/test-manual-flow.sh
  # Opens browser with OAuth2 flow, handles callback automatically

  # Clean up when done
  ./scripts/oauth2/cleanup-test-app.sh

  Individual Component Testing

  # Test metadata endpoint
  curl -s http://localhost:3000/.well-known/oauth-authorization-server | jq .

  # Test PKCE generation
  ./scripts/oauth2/generate-pkce.sh

  # Run specific test suites
  go test -v ./coderd/identityprovider -run TestVerifyPKCE
  go test -v ./coderd -run TestOAuth2AuthorizationServerMetadata
```

  ### Breaking Changes

  None. All changes maintain backward compatibility with existing OAuth2 flows.

---

Change-Id: Ifbd0d9a543d545f9f56ecaa77ff2238542ff954a
Signed-off-by: Thomas Kosiewski <tk@coder.com>
2025-07-01 15:39:29 +02:00
34494fb330 chore: avoid depending on rbac in slim builds (#17959)
I noticed the `coder-vpn.dylib` (of course alongside the Agent/CLI binaries) had grown substantially (from 29MB to 37MB for the dylib), and discovered that importing RBAC in slim builds was the issue

This PR removes the dependency on RBAC in slim builds, and adds a compile-time check to ensure it can't be imported in the future:

```
$ make build
# github.com/coder/coder/v2/coderd/rbac
coderd/rbac/no_slim.go:8:2: initialization cycle: _DO_NOT_IMPORT_THIS_PACKAGE_IN_SLIM_BUILDS refers to itself
make: *** [Makefile:224: build/coder-slim_2.22.1-devel+7e46d24b4_linux_amd64] Error 1
```

Before and after for `coder-slim_darwin_arm64`:
```
$ gsa before after
┌───────────────────────────────────────────────────────────────────────────────────┐
│ Diff between before and after                                                     │
├─────────┬─────────────────────────────────────────┬──────────┬──────────┬─────────┤
│ PERCENT │ NAME                                    │ OLD SIZE │ NEW SIZE │ DIFF    │
├─────────┼─────────────────────────────────────────┼──────────┼──────────┼─────────┤
│ -100%   │ github.com/gorilla/mux                  │          │          │ +0 B    │
│ -100%   │ github.com/ammario/tlru                 │          │          │ +0 B    │
│ -100%   │ github.com/armon/go-radix               │          │          │ +0 B    │
│ -0.00%  │ gvisor.dev/gvisor                       │ 2.4 MB   │ 2.4 MB   │ -4 B    │
│ -0.21%  │ os                                      │ 155 kB   │ 155 kB   │ -328 B  │
│ -0.23%  │ regexp                                  │ 152 kB   │ 152 kB   │ -346 B  │
│ -0.04%  │ runtime                                 │ 876 kB   │ 876 kB   │ -372 B  │
│ -100%   │ github.com/rcrowley/go-metrics          │ 675 B    │          │ -675 B  │
│ -23.79% │ github.com/cespare/xxhash/v2            │ 3.0 kB   │ 2.3 kB   │ -715 B  │
│ -100%   │ github.com/agnivade/levenshtein         │ 1.4 kB   │          │ -1.4 kB │
│ -100%   │ github.com/go-ini/ini                   │ 1.5 kB   │          │ -1.5 kB │
│ -100%   │ github.com/xeipuuv/gojsonreference      │ 2.4 kB   │          │ -2.4 kB │
│ -100%   │ github.com/xeipuuv/gojsonpointer        │ 5.2 kB   │          │ -5.2 kB │
│ -2.43%  │ go.opentelemetry.io/otel                │ 316 kB   │ 309 kB   │ -7.7 kB │
│ -2.40%  │ slices                                  │ 381 kB   │ 372 kB   │ -9.2 kB │
│ -0.68%  │ crypto                                  │ 1.4 MB   │ 1.4 MB   │ -9.5 kB │
│ -100%   │ github.com/tchap/go-patricia/v2         │ 23 kB    │          │ -23 kB  │
│ -100%   │ github.com/yashtewari/glob-intersection │ 28 kB    │          │ -28 kB  │
│ -4.35%  │ <autogenerated>                         │ 754 kB   │ 721 kB   │ -33 kB  │
│ -100%   │ github.com/sirupsen/logrus              │ 72 kB    │          │ -72 kB  │
│ -2.56%  │ github.com/coder/coder/v2               │ 3.3 MB   │ 3.2 MB   │ -84 kB  │
│ -100%   │ github.com/gobwas/glob                  │ 107 kB   │          │ -107 kB │
│ -100%   │ sigs.k8s.io/yaml                        │ 244 kB   │          │ -244 kB │
│ -100%   │ github.com/open-policy-agent/opa        │ 2.2 MB   │          │ -2.2 MB │
├─────────┼─────────────────────────────────────────┼──────────┼──────────┼─────────┤
│ -7.79%  │ __go_buildinfo __DATA                   │ 18 kB    │ 17 kB    │ -1.4 kB │
│ -6.81%  │ __itablink __DATA_CONST                 │ 23 kB    │ 22 kB    │ -1.6 kB │
│ -6.61%  │ __typelink __DATA_CONST                 │ 71 kB    │ 66 kB    │ -4.7 kB │
│ -2.86%  │ __noptrdata __DATA                      │ 1.0 MB   │ 993 kB   │ -29 kB  │
│ -21.49% │ __data __DATA                           │ 320 kB   │ 251 kB   │ -69 kB  │
│ -6.19%  │ __rodata __DATA_CONST                   │ 6.0 MB   │ 5.6 MB   │ -372 kB │
│ -47.19% │ __rodata __TEXT                         │ 7.6 MB   │ 4.0 MB   │ -3.6 MB │
├─────────┼─────────────────────────────────────────┼──────────┼──────────┼─────────┤
│ -14.02% │ before                                  │ 50 MB    │ 43 MB    │ -7.0 MB │
│         │ after                                   │          │          │         │
└─────────┴─────────────────────────────────────────┴──────────┴──────────┴─────────┘
```
2025-05-22 19:48:23 +10:00
46d4b28384 chore: add x-authz-checks debug header when running in dev mode (#16873) 2025-04-10 11:36:27 -06:00
9bc727e977 chore: add support for one-way websockets to backend (#16853)
Closes https://github.com/coder/coder/issues/16775

## Changes made
- Added `OneWayWebSocket` function that establishes WebSocket
connections that don't allow client-to-server communication
- Added tests for the new function
- Updated API endpoints to make new WS-based endpoints, and mark
previous SSE-based endpoints as deprecated
- Updated existing SSE handlers to use the same core logic as the new WS
handlers

## Notes
- Frontend changes handled via #16855
2025-03-28 17:13:20 -04:00
cccdf1ecac feat: implement WorkspaceCreationBan org role (#16686)
Using negative permissions, this role prevents a user's ability to
create & delete a workspace within a given organization.

Workspaces are uniquely owned by an org and a user, so the org has to
supercede the user permission with a negative permission.

# Use case

Organizations must be able to restrict a member's ability to create a
workspace. This permission is implicitly granted (see
https://github.com/coder/coder/issues/16546#issuecomment-2655437860).

To revoke this permission, the solution chosen was to use negative
permissions in a built in role called `WorkspaceCreationBan`.

# Rational

Using negative permissions is new territory, and not ideal. However,
workspaces are in a unique position.

Workspaces have 2 owners. The organization and the user. To prevent
users from creating a workspace in another organization, an [implied
negative
permission](36d9f5ddb3/coderd/rbac/policy.rego (L172-L192))
is used. So the truth table looks like: _how to read this table
[here](36d9f5ddb3/coderd/rbac/README.md (roles))_

| Role (example)  | Site | Org  | User | Result |
|-----------------|------|------|------|--------|
| non-org-member  | \_   | N    | YN\_ | N      |
| user            | \_   | \_   | Y    | Y      |
| WorkspaceBan    | \_   | N    | Y    | Y      |
| unauthenticated | \_   | \_   | \_   | N      |


This new role, `WorkspaceCreationBan` is the same truth table condition
as if the user was not a member of the organization (when doing a
workspace create/delete). So this behavior **is not entirely new**.

<details>

<summary>How to do it without a negative permission</summary>

The alternate approach would be to remove the implied permission, and
grant it via and organization role. However this would add new behavior
that an organizational role has the ability to grant a user permissions
on their own resources?

It does not make sense for an org role to prevent user from changing
their profile information for example. So the only option is to create a
new truth table column for resources that are owned by both an
organization and a user.

| Role (example)  | Site | Org  |User+Org| User | Result |
|-----------------|------|------|--------|------|--------|
| non-org-member  | \_   | N    |  \_    | \_   | N      |
| user            | \_   | \_   |  \_    | \_   | N      |
| WorkspaceAllow  | \_   | \_   |   Y    | \_   | Y      |
| unauthenticated | \_   | \_   |  \_    | \_   | N      |

Now a user has no opinion on if they can create a workspace, which feels
a little wrong. A user should have the authority over what is theres.

There is fundamental _philosophical_ question of "Who does a workspace
belong to?". The user has some set of autonomy, yet it is the
organization that controls it's existence. A head scratcher 🤔

</details>

## Will we need more negative built in roles?

There are few resources that have shared ownership. Only
`ResourceOrganizationMember` and `ResourceGroupMember`. Since negative
permissions is intended to revoke access to a shared resource, then
**no.** **This is the only one we need**.

Classic resources like `ResourceTemplate` are entirely controlled by the
Organization permissions. And resources entirely in the user control
(like user profile) are only controlled by `User` permissions.


![Uploading Screenshot 2025-02-26 at 22.26.52.png…]()

---------

Co-authored-by: Jaayden Halko <jaayden.halko@gmail.com>
Co-authored-by: ケイラ <mckayla@hey.com>
2025-02-27 06:23:18 -05:00
a376e8dbfe fix: include a link and more useful error details for 403 response codes (#16644)
Currently if a user gets to a page they don't have permission to view
they're greeted with a vague error alert and no actionable items. This
PR adds a link back to _/workspaces_ within the alert as well as more
helpful error details.

Before:
![Screenshot 2025-02-20 at 11 06
06 AM](https://github.com/user-attachments/assets/cea5b86d-673b-482b-ac0b-f132eb518910)

After:
![Screenshot 2025-02-20 at 11 06
19 AM](https://github.com/user-attachments/assets/6bf0e9fd-fc51-4d9a-afbc-fea9f0439aff)
2025-02-21 16:26:07 -05:00
2c7f8ac65f chore: migrate to coder/websocket 1.8.12 (#15898)
Migrates us to `coder/websocket` v1.8.12 rather than `nhooyr/websocket` on an older version.

Works around https://github.com/coder/websocket/issues/504 by adding an explicit test for `xerrors.Is(err, io.EOF)` where we were previously getting `io.EOF` from the netConn.
2024-12-19 00:51:30 +04:00
01a904c133 feat(codersdk): export name validators (#14550)
* feat(codersdk): export name validators

* review
2024-09-04 18:17:53 +10:00
499769187b fix: increase group name limit to 36 from 32 (#14443) 2024-08-26 11:38:31 -08:00
2c13797350 chore: implement deleting custom roles (#14101)
* chore: implement deleting custom roles

* add trigger to delete role from organization members on delete
* chore: add comments to explain populated field
2024-08-07 12:37:55 -05:00
e987ad1d89 fix: don't allow "new" or "create" as url-friendly names (#13596) 2024-06-18 15:36:13 -06:00
44a70a5bc2 feat: edit org display names and descriptions (#13474) 2024-06-06 10:59:59 -06:00
5eb3e1cdaa feat: expose owner_name in coder_workspace resource (#11639) 2024-01-17 13:20:45 +01:00
5cfa34b31e feat: add OAuth2 applications (#11197)
* Add database tables for OAuth2 applications

These are applications that will be able to use OAuth2 to get an API key
from Coder.

* Add endpoints for managing OAuth2 applications

These let you add, update, and remove OAuth2 applications.

* Add frontend for managing OAuth2 applications
2023-12-21 21:38:42 +00:00
6fc1f5276d refactor(coderd/httpapi): remove database, dbauthz and rbac imports (#9481)
Ref: #9380
2023-09-04 19:39:14 +03:00
22e781eced chore: add /v2 to import module path (#9072)
* chore: add /v2 to import module path

go mod requires semantic versioning with versions greater than 1.x

This was a mechanical update by running:
```
go install github.com/marwan-at-work/mod/cmd/mod@latest
mod upgrade
```

Migrate generated files to import /v2

* Fix gen
2023-08-18 18:55:43 +00:00
2f0a9996e7 chore: add derpserver to wsproxy, add proxies to derpmap (#7311) 2023-07-27 02:21:04 +10:00
43e0968dac feat: format healthcheck responses (#7723) 2023-05-30 16:59:39 -05:00
cd416c86dd refactor: workspace builds (#7541)
* refactor workspace builds

Signed-off-by: Spike Curtis <spike@coder.com>

* make gen

Signed-off-by: Spike Curtis <spike@coder.com>

* Remove ParameterResolver from typescript

Signed-off-by: Spike Curtis <spike@coder.com>

* rename conversion -> database/db2sdk

Signed-off-by: Spike Curtis <spike@coder.com>

* tests for db2sdk

Signed-off-by: Spike Curtis <spike@coder.com>

* Tests for ParameterResolver

Signed-off-by: Spike Curtis <spike@coder.com>

* wsbuilder tests

Signed-off-by: Spike Curtis <spike@coder.com>

* Move parameter validation tests to richparameters_test.go

Signed-off-by: Spike Curtis <spike@coder.com>

* Fix CI generation; rename mock->dbmock

Signed-off-by: Spike Curtis <spike@coder.com>

* Fix test imports

Signed-off-by: Spike Curtis <spike@coder.com>

---------

Signed-off-by: Spike Curtis <spike@coder.com>
2023-05-23 12:06:33 +04:00
38e5b9679b chore: Rbac errors should be returned, and not hidden behind 404 (#7122)
* chore: Rbac errors should be returned, and not hidden behind 404

SqlErrNoRows was hiding actual errors
* Replace sql.ErrNoRow checks
* Remove sql err no rows check from dbauthz test
* Fix to use dbauthz system user
2023-04-13 13:06:16 -05:00
b120247213 fix: extend regex for template version name (#6876) 2023-03-30 13:27:58 +02:00
8187992e7f fix: Validate template version name (#6804)
* WIP

* Update

* Validation
2023-03-27 13:54:01 +02:00
3b73321a6c feat: refactor deployment config (#6347) 2023-03-07 15:10:01 -06:00
19ae411f05 fix: actually test httpapi.WebsocketCloseSprintf (#6261) 2023-02-17 17:50:21 +00:00
4fe221a700 feat: add flag to disable password auth (#5991)
Adds a flag --disable-password-auth that prevents the password login
endpoint from working unless the user has the "owner" (aka. site admin)
role.

Adds a subcommand `coder server create-admin-user` which creates a user
directly in the database with the "owner" role, the "admin" role in
every organization, and password auth. This is to avoid lock-out
situations where all accounts have the login type set to an identity
provider and nobody can login.
2023-02-06 14:58:21 +00:00
2042b575dc feat: Add template display name (backend) (#4966)
* Rename to nameValidator

* Refactor: NameValid

* Fix: comment

* Define new migration

* Include display_name

* Update typesGenerated.ts

* Update meta

* Adjust tests

* CLI tests

* Fix: audit

* Fix: omitempty

* site: display_name is optional

* unit: TestUsernameValid

* entities.ts: add display_name

* site: TemplateSettingsPage.test.tsx

* Fix: TemplateSettingsForm.tsx

* Adjust tests

* Add comment to display_name column

* Fix: rename

* Fix: make

* Loosen regexp

* Fix: err check

* Fix: template name length

* Allow for whitespaces

* Update migration number
2022-11-10 21:51:09 +01:00
56b963a940 feat: Make workspace watching realtime instead of polling (#4922)
* feat: Make workspace watching realtime instead of polling

This was leading to performance issues on the frontend, where
the page should only be rendered if changes occur. While this
could be changed on the frontend, it was always the intention
to make this socket ~realtime anyways.

* Fix workspace tests waiting, erroring on workspace update, and add comments to workspace events
2022-11-07 15:25:18 +00:00
e508057d1a fix: Avoid panic in ServerSentEventSender by keeping handler alive (#4821)
The goroutine launched by `ServerSentEventSender` can perform a write
and flush after the calling http handler has exited, at this point the
resources (e.g. `http.ResponseWriter`) are no longer safe to use.

To work around this issue, heartbeats and sending events are now handled
by the goroutine which signals its closure via a channel. This allows
the calling handler to ensure it is kept alive until it's safe to exit.

Fixes #4807
2022-11-01 16:57:38 +02:00
eec406b739 feat: Add Git auth for GitHub, GitLab, Azure DevOps, and BitBucket (#4670)
* Add scaffolding

* Move migration

* Add endpoints for gitauth

* Add configuration files and tests!

* Update typesgen

* Convert configuration format for git auth

* Fix unclosed database conn

* Add overriding VS Code configuration

* Fix Git screen

* Write VS Code special configuration if providers exist

* Enable automatic cloning from VS Code

* Add tests for gitaskpass

* Fix feature visibiliy

* Add banner for too many configurations

* Fix update loop for oauth token

* Jon comments

* Add deployment config page
2022-10-24 19:46:24 -05:00
5de6f86959 feat: trace httpapi.{Read,Write} (#4134) 2022-09-21 17:07:00 -05:00
5e2efb68f1 feat: add SCIM provisioning via Okta (#4132)
Co-authored-by: Ben Potter <ben@coder.com>
2022-09-20 15:16:26 -05:00
63fd4945a2 chore: watch workspace endpoint (#4060) 2022-09-16 18:54:23 +00:00
053fe6ff61 feat: add panic recovery middleware (#3687) 2022-08-29 17:00:52 -05:00
c0cc8b9935 fix: Improve friendly validation error messages (#3390)
* fix: Add validations to `(*codersdk.Error).Friendly`

* fix: Add named validators for template and workspace name
2022-08-09 14:25:23 +03:00
3d0febdd90 feat: Add OIDC authentication (#3314)
* feat: Add OIDC authentication

* Extract username into a separate package and add OIDC tests

* Add test case for invalid tokens

* Add test case for username as email

* Add OIDC to the frontend

* Improve comments from self-review

* Add authentication docs

* Add telemetry

* Update docs/install/auth.md

Co-authored-by: Ammar Bandukwala <ammar@ammar.io>

* Update docs/install/auth.md

Co-authored-by: Ammar Bandukwala <ammar@ammar.io>

* Remove username package

Co-authored-by: Ammar Bandukwala <ammar@ammar.io>
2022-07-31 23:05:35 -05:00
7e9819f2a8 ref: move httpapi.Reponse into codersdk (#2954) 2022-07-12 19:15:02 -05:00
251316751e feat: Return more 404s vs 403s (#2194)
* feat: Return more 404s vs 403s
* Return vague 404 in all cases
2022-06-14 10:14:05 -05:00
af401e3fe1 chore: Linter rule for properly formatted api errors (#2123)
* chore: Linter rule for properly formatted api errors
* Add omitempty to 'Detail' field
2022-06-07 14:33:06 +00:00
6d966963da refactor: rename errors to validations (#2105)
* Update validation error unpacking

* Rename validations on backend

* Format
2022-06-07 09:31:15 -04:00
c9a4642a12 chore: Update BE http errors to be ui friendly (#1994)
* chore: More UI friendly errors

Mainly capitlization + messages prefix error
2022-06-03 21:48:09 +00:00
9929189c45 feat: add tag and value in validation error details (#1760)
* add tag and value in validation error details

* fix unit tests and linter

* add quotes around value

* fix unit tests
2022-05-27 10:13:13 -04:00
4ad5ac2d4a feat: Rbac more coderd endpoints, unit test to confirm (#1437)
* feat: Enforce authorize call on all endpoints
- Make 'request()' exported for running custom requests
* Rbac users endpoints
* 401 -> 403
2022-05-17 13:43:19 -05:00
3311c2f65d refactor: replace Code by Detail in the http API error (#1011) 2022-04-18 11:02:54 -05:00
d9d4599ba9 chore: idea: unify http responses further (#941) 2022-04-12 10:17:33 -05:00
dc46ff407b fix: ensure websocket close messages are truncated to 123 bytes (#779)
It's possible for websocket close messages to be too long, which cause
them to silently fail without a proper close message. See error below:

```
2022-03-31 17:08:34.862 [INFO]	(stdlib)	<close_notjs.go:72>	"2022/03/31 17:08:34 websocket: failed to marshal close frame: reason string max is 123 but got \"insert provisioner daemon:Cannot encode []database.ProvisionerType into oid 19098 - []database.ProvisionerType must implement Encoder or be converted to a string\" with length 161"
```
2022-04-01 18:17:45 +00:00
591523a078 chore: Move httpapi, httpmw, & database into coderd (#568)
* chore: Move httpmw to /coderd directory
httpmw is specific to coderd and should be scoped under coderd

* chore: Move httpapi to /coderd directory
httpapi is specific to coderd and should be scoped under coderd

* chore: Move database  to /coderd directory
database is specific to coderd and should be scoped under coderd

* chore: Update codecov & gitattributes for generated files
* chore: Update Makefile
2022-03-25 16:07:45 -05:00