@boxxy/auth-oauth 0.2.7 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@boxxy/auth-oauth",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"description": "boxxy's public door: an OAuth server for the owner's MCP endpoint, with passkey sign-in",
|
|
5
5
|
"homepage": "https://github.com/zgeoff/boxxy/tree/main/adapters/auth-oauth#readme",
|
|
6
6
|
"bugs": "https://github.com/zgeoff/boxxy/issues",
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"typecheck": "tsrs --noEmit"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@boxxy/contract": "0.2.
|
|
34
|
+
"@boxxy/contract": "0.2.8",
|
|
35
35
|
"@zgeoff/kysely-bun-sqlite": "0.3.2",
|
|
36
|
-
"@zgeoff/mcp-auth": "0.1
|
|
36
|
+
"@zgeoff/mcp-auth": "0.2.1",
|
|
37
37
|
"@zgeoff/std": "0.4.3",
|
|
38
38
|
"elysia": "2.0.0-rc.3",
|
|
39
39
|
"exact-mirror": "1.2.7",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { GrantScope } from '@boxxy/contract';
|
|
2
2
|
import { grantScopes } from '@boxxy/contract';
|
|
3
3
|
import { MCPAuthError } from '@zgeoff/mcp-auth';
|
|
4
|
-
import type { ClientView, GrantID, GrantView, MCPAuth } from '@zgeoff/mcp-auth';
|
|
4
|
+
import type { ClientView, GrantID, GrantView, MCPAuth, OAuthClientID } from '@zgeoff/mcp-auth';
|
|
5
5
|
import { toTagged } from '@zgeoff/std';
|
|
6
6
|
|
|
7
7
|
export interface AuthAdminInput {
|
|
@@ -16,12 +16,7 @@ export type AddClientAnswer =
|
|
|
16
16
|
| { readonly kind: 'refused'; readonly message: string }
|
|
17
17
|
| { readonly kind: 'conflict'; readonly name: string };
|
|
18
18
|
|
|
19
|
-
export type RemoveClientAnswer =
|
|
20
|
-
| { readonly kind: 'removed' }
|
|
21
|
-
| { readonly kind: 'not-found' }
|
|
22
|
-
|
|
23
|
-
// another client has the same name, and the OAuth server removes a client by its name
|
|
24
|
-
| { readonly kind: 'ambiguous'; readonly name: string };
|
|
19
|
+
export type RemoveClientAnswer = { readonly kind: 'removed' } | { readonly kind: 'not-found' };
|
|
25
20
|
|
|
26
21
|
export interface EnrollmentLink {
|
|
27
22
|
readonly token: string;
|
|
@@ -72,17 +67,13 @@ export function createAuthAdmin(input: AuthAdminInput): AuthAdmin {
|
|
|
72
67
|
},
|
|
73
68
|
listClients: () => auth.clients.list(),
|
|
74
69
|
removeClient: async (clientID) => {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const answer = pickClientToRemove(clients, clientID);
|
|
78
|
-
|
|
79
|
-
if (answer.kind === 'remove') {
|
|
80
|
-
await auth.clients.remove(answer.name);
|
|
70
|
+
try {
|
|
71
|
+
await auth.clients.remove(toTagged<OAuthClientID>(clientID));
|
|
81
72
|
|
|
82
73
|
return { kind: 'removed' };
|
|
74
|
+
} catch (error) {
|
|
75
|
+
return pickRemoveRefusal(error);
|
|
83
76
|
}
|
|
84
|
-
|
|
85
|
-
return answer;
|
|
86
77
|
},
|
|
87
78
|
listGrants: async () => {
|
|
88
79
|
const grants = await auth.grants.list();
|
|
@@ -114,24 +105,14 @@ function pickAddRefusal(error: unknown, name: string): AddClientAnswer {
|
|
|
114
105
|
throw error;
|
|
115
106
|
}
|
|
116
107
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
function pickClientToRemove(clients: readonly ClientView[], clientID: string): ClientToRemove {
|
|
122
|
-
const client = clients.find((view) => view.clientID === clientID);
|
|
123
|
-
|
|
124
|
-
if (client === undefined) {
|
|
108
|
+
// The package refuses an unknown client ID with a coded error; any other error is not the owner's
|
|
109
|
+
// input.
|
|
110
|
+
function pickRemoveRefusal(error: unknown): RemoveClientAnswer {
|
|
111
|
+
if (error instanceof MCPAuthError && error.code === 'not-found') {
|
|
125
112
|
return { kind: 'not-found' };
|
|
126
113
|
}
|
|
127
114
|
|
|
128
|
-
|
|
129
|
-
(view) => view.name === client.name && view.clientID !== client.clientID,
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
return isShared
|
|
133
|
-
? { kind: 'ambiguous', name: client.name }
|
|
134
|
-
: { kind: 'remove', name: client.name };
|
|
115
|
+
throw error;
|
|
135
116
|
}
|
|
136
117
|
|
|
137
118
|
function toAuthGrantView(grant: GrantView): AuthGrantView {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { DEFAULT_PATHS } from '@zgeoff/mcp-auth';
|
|
1
|
+
import { DEFAULT_PATHS, normalizeClientName } from '@zgeoff/mcp-auth';
|
|
2
2
|
import type { SecurityEvent } from '@zgeoff/mcp-auth';
|
|
3
|
-
import { normalizeClientName } from './normalize-client-name.ts';
|
|
4
3
|
|
|
5
4
|
// The IDs in an event are tagged strings, and a line needs only their text.
|
|
6
5
|
type LoggedSecurityEvent = Omit<SecurityEvent, 'client' | 'correlationID'> & {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function normalizeClientName(raw: unknown, fallback?: string, maxLength?: number): string;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export function normalizeClientName(
|
|
2
|
-
raw: unknown,
|
|
3
|
-
fallback = 'unnamed client',
|
|
4
|
-
maxLength = 100,
|
|
5
|
-
): string {
|
|
6
|
-
if (typeof raw !== 'string') {
|
|
7
|
-
return fallback;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const folded = raw
|
|
11
|
-
.replaceAll(/[\t\n\v\f\r]/gu, ' ')
|
|
12
|
-
.replaceAll(/[\p{Cc}\p{Cf}]/gu, '')
|
|
13
|
-
.replaceAll(/\s+/gu, ' ')
|
|
14
|
-
.trim();
|
|
15
|
-
|
|
16
|
-
// The cut counts code points, so it never splits a surrogate pair.
|
|
17
|
-
const cut = folded.replace(new RegExp(`^(?<kept>.{${maxLength}}).+$`, 'su'), '$<kept>').trim();
|
|
18
|
-
|
|
19
|
-
return cut === '' ? fallback : cut;
|
|
20
|
-
}
|