mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-19 21:31:46 +00:00
feat(oauth): browser-initiated dynamic client registration (DCR)
Adds an OAuth 2.1 public client registration flow so MCP clients can
self-register via a user-facing consent page instead of requiring manual
setup in Settings.
Server:
- DB migration adds `is_public` and `created_via` columns to oauth_clients
- New GET /api/oauth/register/validate — validates DCR params, returns
requested scopes; unauthenticated callers get loginRequired flag
- New POST /api/oauth/register — creates a public client, saves consent,
and redirects with client_id (cookie auth required)
- `authenticateClient` / `refreshTokens` skip secret check for public
clients (PKCE provides the security guarantee)
- `createOAuthClient` accepts options for isPublic/createdVia; public
clients store an opaque secret hash instead of a usable secret
- `rotateOAuthClientSecret` blocked on public clients
- `isValidRedirectUri` extracted as a shared helper
- Discovery metadata now advertises registration_endpoint and auth method
`none`; token/revoke endpoints no longer require client_secret for
public clients
Client:
- New OAuthRegisterPage (/oauth/register) — loading → optional
login-required gate → scope selection → done states
- New ScopeGroupPicker component — collapsible groups, indeterminate
checkboxes, select-all per group or globally
- oauthApi.register.{validate,submit} added to api/client.ts
- apiClient exported so it can be reused outside api/client.ts
- IntegrationsTab tests fixed for new collapsible section structure
- collab_notes fallback changed from undefined to [] in MCP trip tools
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import axios, { AxiosInstance } from 'axios'
|
||||
import { getSocketId } from './websocket'
|
||||
|
||||
const apiClient: AxiosInstance = axios.create({
|
||||
export const apiClient: AxiosInstance = axios.create({
|
||||
baseURL: '/api',
|
||||
withCredentials: true,
|
||||
headers: {
|
||||
@@ -95,6 +95,15 @@ export const oauthApi = {
|
||||
approved: boolean
|
||||
}) => apiClient.post('/oauth/authorize', body).then(r => r.data),
|
||||
|
||||
register: {
|
||||
/** Validate DCR params — called by registration page on load */
|
||||
validate: (params: { redirect_uri: string; client_name?: string; scope?: string; state?: string }) =>
|
||||
apiClient.get('/oauth/register/validate', { params }).then(r => r.data),
|
||||
/** Submit registration approval or cancellation */
|
||||
submit: (body: { client_name: string; redirect_uri: string; scopes: string[]; state?: string; approved: boolean }) =>
|
||||
apiClient.post('/oauth/register', body).then(r => r.data),
|
||||
},
|
||||
|
||||
clients: {
|
||||
list: () => apiClient.get('/oauth/clients').then(r => r.data),
|
||||
create: (data: { name: string; redirect_uris: string[]; allowed_scopes: string[] }) =>
|
||||
|
||||
Reference in New Issue
Block a user