@frogbotai/plugin-api-keys 0.28.0 → 0.30.0
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/README.md +5 -6
- package/dist/client/ApiKeysManager.js +184 -21
- package/dist/client/ApiKeysManager.js.map +1 -1
- package/dist/client/styles.css +78 -76
- package/dist/client.d.ts +0 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +1 -1
- package/dist/client.js.map +1 -1
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +219 -110
- package/dist/collection.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +61 -48
- package/dist/index.js.map +1 -1
- package/dist/server/services.js +63 -28
- package/dist/server/services.js.map +1 -1
- package/dist/server/token.d.ts +1 -1
- package/dist/server/token.d.ts.map +1 -1
- package/dist/server/token.js +16 -11
- package/dist/server/token.js.map +1 -1
- package/dist/strategy.d.ts +5 -4
- package/dist/strategy.d.ts.map +1 -1
- package/dist/strategy.js +58 -29
- package/dist/strategy.js.map +1 -1
- package/package.json +7 -7
- package/dist/client/CostUSD.d.ts +0 -11
- package/dist/client/CostUSD.d.ts.map +0 -1
- package/dist/client/CostUSD.js +0 -19
- package/dist/client/CostUSD.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
import { createApiKeysCollection } from './collection.js';
|
|
2
|
-
import { createApiKeyStrategy } from './strategy.js';
|
|
2
|
+
import { apiKeyMeHook, apiKeyStrategyName, createApiKeyStrategy } from './strategy.js';
|
|
3
3
|
export { ApiKeyServiceError, mintApiKey, revokeApiKey, rotateApiKey } from './server/services.js';
|
|
4
|
-
export { createApiKeyToken, extractApiKeyToken, getApiKeyPrefix, hashApiKeyToken
|
|
4
|
+
export { createApiKeyToken, extractApiKeyToken, getApiKeyPrefix, hashApiKeyToken } from './server/token.js';
|
|
5
5
|
export { isApiKeyStrategy } from './strategy.js';
|
|
6
6
|
export function apiKeysPlugin(options = {}) {
|
|
7
|
-
return (config)
|
|
7
|
+
return (config)=>{
|
|
8
8
|
const authCollection = options.authCollection ?? 'users';
|
|
9
9
|
const collectionSlug = options.collectionSlug ?? 'api-keys';
|
|
10
|
-
const auth = config.collections.find((collection)
|
|
10
|
+
const auth = config.collections.find((collection)=>collection.slug === authCollection);
|
|
11
11
|
if (!auth || auth.auth === undefined || auth.auth === false) {
|
|
12
12
|
throw new Error(`[plugin-api-keys] Auth collection '${authCollection}' must exist and have auth enabled.`);
|
|
13
13
|
}
|
|
14
|
-
const existing = config.collections.find((collection)
|
|
15
|
-
const usageLog = config.ai
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
})
|
|
21
|
-
: undefined;
|
|
14
|
+
const existing = config.collections.find((collection)=>collection.slug === collectionSlug);
|
|
15
|
+
const usageLog = config.ai ? config.collections.find((item)=>item.usageLog === true) ?? {
|
|
16
|
+
slug: 'usage-logs',
|
|
17
|
+
usageLog: true,
|
|
18
|
+
fields: []
|
|
19
|
+
} : undefined;
|
|
22
20
|
const collection = createApiKeysCollection({
|
|
23
21
|
authCollection,
|
|
24
22
|
collectionSlug,
|
|
@@ -26,73 +24,88 @@ export function apiKeysPlugin(options = {}) {
|
|
|
26
24
|
usageCollection: usageLog?.slug,
|
|
27
25
|
canRevokeAnyKey: options.canRevokeAnyKey,
|
|
28
26
|
collection: options.collection,
|
|
29
|
-
existing
|
|
27
|
+
existing
|
|
30
28
|
});
|
|
31
29
|
const strategy = createApiKeyStrategy({
|
|
32
30
|
authCollection,
|
|
33
31
|
collectionSlug,
|
|
34
32
|
headerNames: options.headerNames,
|
|
35
|
-
tokenPrefix: options.tokenPrefix ?? 'fb'
|
|
33
|
+
tokenPrefix: options.tokenPrefix ?? 'fb'
|
|
36
34
|
});
|
|
37
35
|
const usageField = {
|
|
38
36
|
name: 'apiKey',
|
|
39
37
|
type: 'relationship',
|
|
40
38
|
relationTo: collectionSlug,
|
|
41
|
-
index: true
|
|
39
|
+
index: true
|
|
42
40
|
};
|
|
43
|
-
const collections = config.collections.map((item)
|
|
41
|
+
const collections = config.collections.map((item)=>{
|
|
44
42
|
let next = item;
|
|
45
|
-
if (item.slug === collectionSlug)
|
|
46
|
-
next = collection;
|
|
43
|
+
if (item.slug === collectionSlug) next = collection;
|
|
47
44
|
if (item.slug === authCollection) {
|
|
48
45
|
const authConfig = typeof next.auth === 'object' ? next.auth : {};
|
|
49
46
|
next = {
|
|
50
47
|
...next,
|
|
51
|
-
auth: {
|
|
48
|
+
auth: {
|
|
49
|
+
...authConfig,
|
|
50
|
+
strategies: [
|
|
51
|
+
...authConfig.strategies ?? [],
|
|
52
|
+
strategy
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
hooks: {
|
|
56
|
+
...next.hooks,
|
|
57
|
+
me: [
|
|
58
|
+
...next.hooks?.me ?? [],
|
|
59
|
+
apiKeyMeHook
|
|
60
|
+
]
|
|
61
|
+
}
|
|
52
62
|
};
|
|
53
63
|
}
|
|
54
|
-
if (item === usageLog)
|
|
55
|
-
|
|
64
|
+
if (item === usageLog) next = {
|
|
65
|
+
...next,
|
|
66
|
+
fields: [
|
|
67
|
+
...next.fields,
|
|
68
|
+
usageField
|
|
69
|
+
]
|
|
70
|
+
};
|
|
56
71
|
return next;
|
|
57
72
|
});
|
|
58
73
|
return {
|
|
59
74
|
...config,
|
|
60
|
-
settings: [
|
|
61
|
-
...(config.settings ?? []),
|
|
62
|
-
{
|
|
63
|
-
label: 'API Keys',
|
|
64
|
-
path: 'api-keys',
|
|
65
|
-
Component: {
|
|
66
|
-
path: '@frogbotai/next/views#CollectionSettingsRedirect',
|
|
67
|
-
serverProps: { collectionSlug },
|
|
68
|
-
},
|
|
69
|
-
access: collection.access?.read,
|
|
70
|
-
},
|
|
71
|
-
],
|
|
72
75
|
collections: [
|
|
73
76
|
...collections,
|
|
74
|
-
...
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
...existing ? [] : [
|
|
78
|
+
collection
|
|
79
|
+
],
|
|
80
|
+
...usageLog && !config.collections.includes(usageLog) ? [
|
|
81
|
+
{
|
|
82
|
+
...usageLog,
|
|
83
|
+
fields: [
|
|
84
|
+
usageField
|
|
85
|
+
]
|
|
86
|
+
}
|
|
87
|
+
] : []
|
|
78
88
|
],
|
|
79
|
-
...
|
|
89
|
+
...config.ai && {
|
|
80
90
|
ai: {
|
|
81
91
|
...config.ai,
|
|
82
92
|
hooks: {
|
|
83
93
|
...config.ai.hooks,
|
|
84
94
|
beforeOperation: [
|
|
85
|
-
...
|
|
86
|
-
({ req, context })
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
+
...config.ai.hooks?.beforeOperation ?? [],
|
|
96
|
+
({ req, context })=>{
|
|
97
|
+
const user = req?.user;
|
|
98
|
+
const apiKeyId = user?._strategy === apiKeyStrategyName && (typeof user.apiKeyId === 'string' || typeof user.apiKeyId === 'number') ? user.apiKeyId : undefined;
|
|
99
|
+
if (apiKeyId !== undefined) context.usageFields = {
|
|
100
|
+
apiKey: apiKeyId
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
95
107
|
};
|
|
96
108
|
};
|
|
97
109
|
}
|
|
110
|
+
|
|
98
111
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["createApiKeysCollection","apiKeyMeHook","apiKeyStrategyName","createApiKeyStrategy","ApiKeyServiceError","mintApiKey","revokeApiKey","rotateApiKey","createApiKeyToken","extractApiKeyToken","getApiKeyPrefix","hashApiKeyToken","isApiKeyStrategy","apiKeysPlugin","options","config","authCollection","collectionSlug","auth","collections","find","collection","slug","undefined","Error","existing","usageLog","ai","item","fields","tokenPrefix","usageCollection","canRevokeAnyKey","strategy","headerNames","usageField","name","type","relationTo","index","map","next","authConfig","strategies","hooks","me","includes","beforeOperation","req","context","user","apiKeyId","_strategy","usageFields","apiKey"],"mappings":"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","file":"index.js"}
|
package/dist/server/services.js
CHANGED
|
@@ -1,73 +1,108 @@
|
|
|
1
1
|
import { createApiKeyToken, getApiKeyPrefix, hashApiKeyToken } from './token.js';
|
|
2
2
|
export class ApiKeyServiceError extends Error {
|
|
3
3
|
code;
|
|
4
|
-
constructor(code)
|
|
5
|
-
super(code);
|
|
6
|
-
this.code = code;
|
|
4
|
+
constructor(code){
|
|
5
|
+
super(code), this.code = code;
|
|
7
6
|
}
|
|
8
7
|
}
|
|
9
|
-
async function mintForOwner({ req, collectionSlug, tokenPrefix, name, owner
|
|
10
|
-
const token = createApiKeyToken({
|
|
8
|
+
async function mintForOwner({ req, collectionSlug, tokenPrefix, name, owner }) {
|
|
9
|
+
const token = createApiKeyToken({
|
|
10
|
+
tokenPrefix
|
|
11
|
+
});
|
|
11
12
|
const prefix = getApiKeyPrefix(token);
|
|
12
|
-
const doc =
|
|
13
|
+
const doc = await req.frogbot.create({
|
|
13
14
|
collection: collectionSlug,
|
|
14
|
-
data: {
|
|
15
|
+
data: {
|
|
16
|
+
name,
|
|
17
|
+
owner,
|
|
18
|
+
prefix,
|
|
19
|
+
tokenHash: hashApiKeyToken(token)
|
|
20
|
+
},
|
|
15
21
|
overrideAccess: true,
|
|
16
|
-
req
|
|
17
|
-
})
|
|
18
|
-
return {
|
|
22
|
+
req
|
|
23
|
+
});
|
|
24
|
+
return {
|
|
25
|
+
id: doc.id,
|
|
26
|
+
name,
|
|
27
|
+
prefix,
|
|
28
|
+
token,
|
|
29
|
+
createdAt: doc.createdAt
|
|
30
|
+
};
|
|
19
31
|
}
|
|
20
32
|
export async function mintApiKey(options) {
|
|
21
33
|
const owner = options.req.user?.id;
|
|
22
|
-
if (owner === undefined)
|
|
23
|
-
throw new ApiKeyServiceError('authentication_required');
|
|
34
|
+
if (owner === undefined) throw new ApiKeyServiceError('authentication_required');
|
|
24
35
|
return mintForOwner({
|
|
25
36
|
req: options.req,
|
|
26
37
|
collectionSlug: options.collectionSlug,
|
|
27
38
|
tokenPrefix: options.tokenPrefix,
|
|
28
39
|
name: options.name,
|
|
29
|
-
owner
|
|
40
|
+
owner
|
|
30
41
|
});
|
|
31
42
|
}
|
|
32
|
-
export async function rotateApiKey({ req, collectionSlug, id, tokenPrefix, anyOwner
|
|
33
|
-
const revoked = await revokeApiKey({
|
|
43
|
+
export async function rotateApiKey({ req, collectionSlug, id, tokenPrefix, anyOwner }) {
|
|
44
|
+
const revoked = await revokeApiKey({
|
|
45
|
+
req,
|
|
46
|
+
collectionSlug,
|
|
47
|
+
id,
|
|
48
|
+
anyOwner
|
|
49
|
+
});
|
|
34
50
|
return mintForOwner({
|
|
35
51
|
req,
|
|
36
52
|
collectionSlug,
|
|
37
53
|
tokenPrefix,
|
|
38
54
|
name: revoked.name,
|
|
39
|
-
owner: revoked.owner
|
|
55
|
+
owner: revoked.owner
|
|
40
56
|
});
|
|
41
57
|
}
|
|
42
58
|
export async function revokeApiKey({ req, collectionSlug, id, anyOwner }) {
|
|
43
59
|
const owner = req.user?.id;
|
|
44
|
-
if (owner === undefined)
|
|
45
|
-
throw new ApiKeyServiceError('authentication_required');
|
|
60
|
+
if (owner === undefined) throw new ApiKeyServiceError('authentication_required');
|
|
46
61
|
const result = await req.frogbot.find({
|
|
47
62
|
collection: collectionSlug,
|
|
48
63
|
depth: 0,
|
|
49
64
|
limit: 1,
|
|
50
65
|
overrideAccess: true,
|
|
51
66
|
req,
|
|
52
|
-
where: anyOwner
|
|
53
|
-
|
|
54
|
-
|
|
67
|
+
where: anyOwner ? {
|
|
68
|
+
id: {
|
|
69
|
+
equals: id
|
|
70
|
+
}
|
|
71
|
+
} : {
|
|
72
|
+
and: [
|
|
73
|
+
{
|
|
74
|
+
id: {
|
|
75
|
+
equals: id
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
owner: {
|
|
80
|
+
equals: owner
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
}
|
|
55
85
|
});
|
|
56
86
|
const key = result.docs[0];
|
|
57
|
-
if (!key ||
|
|
58
|
-
(typeof key.id !== 'string' && typeof key.id !== 'number') ||
|
|
59
|
-
typeof key.name !== 'string' ||
|
|
60
|
-
(typeof key.owner !== 'string' && typeof key.owner !== 'number')) {
|
|
87
|
+
if (!key || typeof key.id !== 'string' && typeof key.id !== 'number' || typeof key.name !== 'string' || typeof key.owner !== 'string' && typeof key.owner !== 'number') {
|
|
61
88
|
throw new ApiKeyServiceError('not_found');
|
|
62
89
|
}
|
|
63
90
|
const revokedAt = typeof key.revokedAt === 'string' ? key.revokedAt : new Date().toISOString();
|
|
64
91
|
await req.frogbot.update({
|
|
65
92
|
collection: collectionSlug,
|
|
66
93
|
id: key.id,
|
|
67
|
-
data: {
|
|
94
|
+
data: {
|
|
95
|
+
revokedAt
|
|
96
|
+
},
|
|
68
97
|
overrideAccess: true,
|
|
69
|
-
req
|
|
98
|
+
req
|
|
70
99
|
});
|
|
71
|
-
return {
|
|
100
|
+
return {
|
|
101
|
+
id: key.id,
|
|
102
|
+
name: key.name,
|
|
103
|
+
owner: key.owner,
|
|
104
|
+
revokedAt
|
|
105
|
+
};
|
|
72
106
|
}
|
|
107
|
+
|
|
73
108
|
//# sourceMappingURL=services.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"
|
|
1
|
+
{"version":3,"sources":["../../src/server/services.ts"],"names":["createApiKeyToken","getApiKeyPrefix","hashApiKeyToken","ApiKeyServiceError","Error","code","mintForOwner","req","collectionSlug","tokenPrefix","name","owner","token","prefix","doc","frogbot","create","collection","data","tokenHash","overrideAccess","id","createdAt","mintApiKey","options","user","undefined","rotateApiKey","anyOwner","revoked","revokeApiKey","result","find","depth","limit","where","equals","and","key","docs","revokedAt","Date","toISOString","update"],"mappings":"AAEA,SAASA,iBAAiB,EAAEC,eAAe,EAAEC,eAAe,QAAQ,aAAa;AAgBjF,OAAO,MAAMC,2BAA2BC;;IACtC,YAAY,AAAgBC,IAA6C,CAAE;QACzE,KAAK,CAACA,YADoBA,OAAAA;IAE5B;AACF;AAIA,eAAeC,aAAa,EAC1BC,GAAG,EACHC,cAAc,EACdC,WAAW,EACXC,IAAI,EACJC,KAAK,EACe;IACpB,MAAMC,QAAQZ,kBAAkB;QAAES;IAAY;IAC9C,MAAMI,SAASZ,gBAAgBW;IAC/B,MAAME,MAAO,MAAMP,IAAIQ,OAAO,CAACC,MAAM,CAAC;QACpCC,YAAYT;QACZU,MAAM;YAAER;YAAMC;YAAOE;YAAQM,WAAWjB,gBAAgBU;QAAO;QAC/DQ,gBAAgB;QAChBb;IACF;IACA,OAAO;QAAEc,IAAIP,IAAIO,EAAE;QAAEX;QAAMG;QAAQD;QAAOU,WAAWR,IAAIQ,SAAS;IAAC;AACrE;AAEA,OAAO,eAAeC,WAAWC,OAA0B;IACzD,MAAMb,QAAQa,QAAQjB,GAAG,CAACkB,IAAI,EAAEJ;IAChC,IAAIV,UAAUe,WAAW,MAAM,IAAIvB,mBAAmB;IACtD,OAAOG,aAAa;QAClBC,KAAKiB,QAAQjB,GAAG;QAChBC,gBAAgBgB,QAAQhB,cAAc;QACtCC,aAAae,QAAQf,WAAW;QAChCC,MAAMc,QAAQd,IAAI;QAClBC;IACF;AACF;AAIA,OAAO,eAAegB,aAAa,EACjCpB,GAAG,EACHC,cAAc,EACda,EAAE,EACFZ,WAAW,EACXmB,QAAQ,EACY;IACpB,MAAMC,UAAU,MAAMC,aAAa;QAAEvB;QAAKC;QAAgBa;QAAIO;IAAS;IACvE,OAAOtB,aAAa;QAClBC;QACAC;QACAC;QACAC,MAAMmB,QAAQnB,IAAI;QAClBC,OAAOkB,QAAQlB,KAAK;IACtB;AACF;AAEA,OAAO,eAAemB,aAAa,EAAEvB,GAAG,EAAEC,cAAc,EAAEa,EAAE,EAAEO,QAAQ,EAAuB;IAC3F,MAAMjB,QAAQJ,IAAIkB,IAAI,EAAEJ;IACxB,IAAIV,UAAUe,WAAW,MAAM,IAAIvB,mBAAmB;IACtD,MAAM4B,SAAS,MAAMxB,IAAIQ,OAAO,CAACiB,IAAI,CAAC;QACpCf,YAAYT;QACZyB,OAAO;QACPC,OAAO;QACPd,gBAAgB;QAChBb;QACA4B,OAAOP,WACH;YAAEP,IAAI;gBAAEe,QAAQf;YAAG;QAAE,IACrB;YAAEgB,KAAK;gBAAC;oBAAEhB,IAAI;wBAAEe,QAAQf;oBAAG;gBAAE;gBAAG;oBAAEV,OAAO;wBAAEyB,QAAQzB;oBAAM;gBAAE;aAAE;QAAC;IACpE;IACA,MAAM2B,MAAMP,OAAOQ,IAAI,CAAC,EAAE;IAC1B,IACE,CAACD,OACA,OAAOA,IAAIjB,EAAE,KAAK,YAAY,OAAOiB,IAAIjB,EAAE,KAAK,YACjD,OAAOiB,IAAI5B,IAAI,KAAK,YACnB,OAAO4B,IAAI3B,KAAK,KAAK,YAAY,OAAO2B,IAAI3B,KAAK,KAAK,UACvD;QACA,MAAM,IAAIR,mBAAmB;IAC/B;IACA,MAAMqC,YAAY,OAAOF,IAAIE,SAAS,KAAK,WAAWF,IAAIE,SAAS,GAAG,IAAIC,OAAOC,WAAW;IAC5F,MAAMnC,IAAIQ,OAAO,CAAC4B,MAAM,CAAC;QACvB1B,YAAYT;QACZa,IAAIiB,IAAIjB,EAAE;QACVH,MAAM;YAAEsB;QAAU;QAClBpB,gBAAgB;QAChBb;IACF;IACA,OAAO;QAAEc,IAAIiB,IAAIjB,EAAE;QAAEX,MAAM4B,IAAI5B,IAAI;QAAEC,OAAO2B,IAAI3B,KAAK;QAAE6B;IAAU;AACnE","file":"services.js"}
|
package/dist/server/token.d.ts
CHANGED
|
@@ -7,5 +7,5 @@ export type ApiKeyHeaderOptions = {
|
|
|
7
7
|
export declare function createApiKeyToken({ tokenPrefix }?: ApiKeyTokenOptions): string;
|
|
8
8
|
export declare function hashApiKeyToken(token: string): string;
|
|
9
9
|
export declare function getApiKeyPrefix(token: string): string;
|
|
10
|
-
export declare function extractApiKeyToken(headers: Headers, { headerNames }?: ApiKeyHeaderOptions): string | null;
|
|
10
|
+
export declare function extractApiKeyToken(headers: Headers, { headerNames, tokenPrefix }?: ApiKeyHeaderOptions & ApiKeyTokenOptions): string | null;
|
|
11
11
|
//# sourceMappingURL=token.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../src/server/token.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,EAAE,WAAkB,EAAE,GAAE,kBAAuB,GAAG,MAAM,CAEzF;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAErD;
|
|
1
|
+
{"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../src/server/token.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,EAAE,WAAkB,EAAE,GAAE,kBAAuB,GAAG,MAAM,CAEzF;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAErD;AAQD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,OAAO,EAChB,EAAE,WAA2B,EAAE,WAAW,EAAE,GAAE,mBAAmB,GAAG,kBAAuB,GAC1F,MAAM,GAAG,IAAI,CAKf"}
|
package/dist/server/token.js
CHANGED
|
@@ -8,16 +8,21 @@ export function hashApiKeyToken(token) {
|
|
|
8
8
|
export function getApiKeyPrefix(token) {
|
|
9
9
|
return token.slice(0, 12);
|
|
10
10
|
}
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
if (bearer)
|
|
15
|
-
return bearer;
|
|
16
|
-
for (const headerName of headerNames) {
|
|
17
|
-
const value = headers.get(headerName)?.trim();
|
|
18
|
-
if (value && !/\s/.test(value))
|
|
19
|
-
return value;
|
|
20
|
-
}
|
|
21
|
-
return null;
|
|
11
|
+
function isApiKeyToken(value, { tokenPrefix = 'fb' } = {}) {
|
|
12
|
+
const prefix = tokenPrefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
13
|
+
return new RegExp(`^${prefix}_[A-Za-z0-9_-]{43}$`).test(value);
|
|
22
14
|
}
|
|
15
|
+
export function extractApiKeyToken(headers, { headerNames = [
|
|
16
|
+
'x-api-key'
|
|
17
|
+
], tokenPrefix } = {}) {
|
|
18
|
+
const bearer = headers.get('authorization')?.match(/^Bearer ([^\s]+)$/i)?.[1];
|
|
19
|
+
const candidates = [
|
|
20
|
+
bearer,
|
|
21
|
+
...headerNames.map((headerName)=>headers.get(headerName)?.trim())
|
|
22
|
+
];
|
|
23
|
+
return candidates.find((value)=>value && isApiKeyToken(value, {
|
|
24
|
+
tokenPrefix
|
|
25
|
+
})) ?? null;
|
|
26
|
+
}
|
|
27
|
+
|
|
23
28
|
//# sourceMappingURL=token.js.map
|
package/dist/server/token.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"
|
|
1
|
+
{"version":3,"sources":["../../src/server/token.ts"],"names":["createHash","randomBytes","createApiKeyToken","tokenPrefix","toString","hashApiKeyToken","token","update","digest","getApiKeyPrefix","slice","isApiKeyToken","value","prefix","replace","RegExp","test","extractApiKeyToken","headers","headerNames","bearer","get","match","candidates","map","headerName","trim","find"],"mappings":"AAAA,SAASA,UAAU,EAAEC,WAAW,QAAQ,cAAc;AAUtD,OAAO,SAASC,kBAAkB,EAAEC,cAAc,IAAI,EAAsB,GAAG,CAAC,CAAC;IAC/E,OAAO,GAAGA,YAAY,CAAC,EAAEF,YAAY,IAAIG,QAAQ,CAAC,cAAc;AAClE;AAEA,OAAO,SAASC,gBAAgBC,KAAa;IAC3C,OAAON,WAAW,UAAUO,MAAM,CAACD,OAAOE,MAAM,CAAC;AACnD;AAEA,OAAO,SAASC,gBAAgBH,KAAa;IAC3C,OAAOA,MAAMI,KAAK,CAAC,GAAG;AACxB;AAEA,SAASC,cAAcC,KAAa,EAAE,EAAET,cAAc,IAAI,EAAsB,GAAG,CAAC,CAAC;IACnF,MAAMU,SAASV,YAAYW,OAAO,CAAC,uBAAuB;IAE1D,OAAO,IAAIC,OAAO,CAAC,CAAC,EAAEF,OAAO,mBAAmB,CAAC,EAAEG,IAAI,CAACJ;AAC1D;AAEA,OAAO,SAASK,mBACdC,OAAgB,EAChB,EAAEC,cAAc;IAAC;CAAY,EAAEhB,WAAW,EAA4C,GAAG,CAAC,CAAC;IAE3F,MAAMiB,SAASF,QAAQG,GAAG,CAAC,kBAAkBC,MAAM,uBAAuB,CAAC,EAAE;IAC7E,MAAMC,aAAa;QAACH;WAAWD,YAAYK,GAAG,CAAC,CAACC,aAAeP,QAAQG,GAAG,CAACI,aAAaC;KAAQ;IAEhG,OAAOH,WAAWI,IAAI,CAAC,CAACf,QAAUA,SAASD,cAAcC,OAAO;YAAET;QAAY,OAAO;AACvF","file":"token.js"}
|
package/dist/strategy.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export
|
|
1
|
+
import type { AuthStrategy, CollectionMeHook } from 'frogbot';
|
|
2
|
+
export declare const apiKeyStrategyName = "api-key";
|
|
3
3
|
type StrategyOptions = {
|
|
4
4
|
authCollection: string;
|
|
5
5
|
collectionSlug: string;
|
|
6
6
|
headerNames?: string[];
|
|
7
7
|
tokenPrefix: string;
|
|
8
8
|
};
|
|
9
|
-
export declare function createApiKeyStrategy(options: StrategyOptions):
|
|
10
|
-
export declare function isApiKeyStrategy(strategy:
|
|
9
|
+
export declare function createApiKeyStrategy(options: StrategyOptions): AuthStrategy;
|
|
10
|
+
export declare function isApiKeyStrategy(strategy: AuthStrategy): boolean;
|
|
11
|
+
export declare const apiKeyMeHook: CollectionMeHook;
|
|
11
12
|
export {};
|
|
12
13
|
//# sourceMappingURL=strategy.d.ts.map
|
package/dist/strategy.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"strategy.d.ts","sourceRoot":"","sources":["../src/strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"strategy.d.ts","sourceRoot":"","sources":["../src/strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAI9D,eAAO,MAAM,kBAAkB,YAAY,CAAC;AAI5C,KAAK,eAAe,GAAG;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,eAAe,GAAG,YAAY,CA8D3E;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAEhE;AAED,eAAO,MAAM,YAAY,EAAE,gBAI1B,CAAC"}
|
package/dist/strategy.js
CHANGED
|
@@ -1,54 +1,76 @@
|
|
|
1
1
|
import { extractApiKeyToken, hashApiKeyToken } from './server/token.js';
|
|
2
|
+
export const apiKeyStrategyName = 'api-key';
|
|
2
3
|
const apiKeyStrategies = new WeakSet();
|
|
3
4
|
export function createApiKeyStrategy(options) {
|
|
4
5
|
const { authCollection, collectionSlug, headerNames, tokenPrefix } = options;
|
|
5
6
|
const strategy = {
|
|
6
|
-
name:
|
|
7
|
-
authenticate: async ({
|
|
8
|
-
const token = extractApiKeyToken(headers, {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
name: apiKeyStrategyName,
|
|
8
|
+
authenticate: async ({ frogbot, headers })=>{
|
|
9
|
+
const token = extractApiKeyToken(headers, {
|
|
10
|
+
headerNames,
|
|
11
|
+
tokenPrefix
|
|
12
|
+
});
|
|
13
|
+
if (!token) return {
|
|
14
|
+
user: null
|
|
15
|
+
};
|
|
16
|
+
const keys = await frogbot.find({
|
|
14
17
|
collection: collectionSlug,
|
|
15
18
|
depth: 0,
|
|
16
19
|
limit: 1,
|
|
17
20
|
overrideAccess: true,
|
|
18
21
|
where: {
|
|
19
22
|
and: [
|
|
20
|
-
{
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
{
|
|
24
|
+
tokenHash: {
|
|
25
|
+
equals: hashApiKeyToken(token)
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
revokedAt: {
|
|
30
|
+
exists: false
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
24
35
|
});
|
|
25
36
|
const key = keys.docs[0];
|
|
26
|
-
if (!key
|
|
27
|
-
return {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
37
|
+
if (!key || typeof key.id !== 'string' && typeof key.id !== 'number' || typeof key.owner !== 'string' && typeof key.owner !== 'number') {
|
|
38
|
+
return {
|
|
39
|
+
user: null
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const user = await frogbot.findByID({
|
|
43
|
+
collection: authCollection,
|
|
44
|
+
id: key.owner,
|
|
45
|
+
depth: 0,
|
|
46
|
+
overrideAccess: true
|
|
47
|
+
}).catch(()=>null);
|
|
48
|
+
if (!user) return {
|
|
49
|
+
user: null
|
|
50
|
+
};
|
|
51
|
+
await frogbot.update({
|
|
34
52
|
collection: collectionSlug,
|
|
35
53
|
id: key.id,
|
|
36
|
-
data: {
|
|
37
|
-
|
|
54
|
+
data: {
|
|
55
|
+
lastUsedAt: new Date().toISOString()
|
|
56
|
+
},
|
|
57
|
+
overrideAccess: true
|
|
38
58
|
});
|
|
39
59
|
return {
|
|
40
60
|
user: {
|
|
41
61
|
...user,
|
|
42
62
|
collection: authCollection,
|
|
43
|
-
_strategy:
|
|
63
|
+
_strategy: apiKeyStrategyName,
|
|
44
64
|
apiKeyId: key.id,
|
|
45
|
-
...
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
65
|
+
...key.capture !== undefined ? {
|
|
66
|
+
capture: key.capture
|
|
67
|
+
} : {},
|
|
68
|
+
...typeof key.captureSampleRate === 'number' ? {
|
|
69
|
+
captureSampleRate: key.captureSampleRate
|
|
70
|
+
} : {}
|
|
71
|
+
}
|
|
50
72
|
};
|
|
51
|
-
}
|
|
73
|
+
}
|
|
52
74
|
};
|
|
53
75
|
apiKeyStrategies.add(strategy);
|
|
54
76
|
return strategy;
|
|
@@ -56,4 +78,11 @@ export function createApiKeyStrategy(options) {
|
|
|
56
78
|
export function isApiKeyStrategy(strategy) {
|
|
57
79
|
return apiKeyStrategies.has(strategy);
|
|
58
80
|
}
|
|
81
|
+
export const apiKeyMeHook = ({ user })=>{
|
|
82
|
+
if (user?._strategy !== apiKeyStrategyName) return;
|
|
83
|
+
return {
|
|
84
|
+
user
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
|
|
59
88
|
//# sourceMappingURL=strategy.js.map
|
package/dist/strategy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"
|
|
1
|
+
{"version":3,"sources":["../src/strategy.ts"],"names":["extractApiKeyToken","hashApiKeyToken","apiKeyStrategyName","apiKeyStrategies","WeakSet","createApiKeyStrategy","options","authCollection","collectionSlug","headerNames","tokenPrefix","strategy","name","authenticate","frogbot","headers","token","user","keys","find","collection","depth","limit","overrideAccess","where","and","tokenHash","equals","revokedAt","exists","key","docs","id","owner","findByID","catch","update","data","lastUsedAt","Date","toISOString","_strategy","apiKeyId","capture","undefined","captureSampleRate","add","isApiKeyStrategy","has","apiKeyMeHook"],"mappings":"AAEA,SAASA,kBAAkB,EAAEC,eAAe,QAAQ,oBAAoB;AAExE,OAAO,MAAMC,qBAAqB,UAAU;AAE5C,MAAMC,mBAAmB,IAAIC;AAS7B,OAAO,SAASC,qBAAqBC,OAAwB;IAC3D,MAAM,EAAEC,cAAc,EAAEC,cAAc,EAAEC,WAAW,EAAEC,WAAW,EAAE,GAAGJ;IACrE,MAAMK,WAAyB;QAC7BC,MAAMV;QACNW,cAAc,OAAO,EAAEC,OAAO,EAAEC,OAAO,EAAE;YACvC,MAAMC,QAAQhB,mBAAmBe,SAAS;gBAAEN;gBAAaC;YAAY;YAErE,IAAI,CAACM,OAAO,OAAO;gBAAEC,MAAM;YAAK;YAEhC,MAAMC,OAAO,MAAMJ,QAAQK,IAAI,CAAC;gBAC9BC,YAAYZ;gBACZa,OAAO;gBACPC,OAAO;gBACPC,gBAAgB;gBAChBC,OAAO;oBACLC,KAAK;wBACH;4BAAEC,WAAW;gCAAEC,QAAQ1B,gBAAgBe;4BAAO;wBAAE;wBAChD;4BAAEY,WAAW;gCAAEC,QAAQ;4BAAM;wBAAE;qBAChC;gBACH;YACF;YACA,MAAMC,MAAMZ,KAAKa,IAAI,CAAC,EAAE;YAExB,IACE,CAACD,OACA,OAAOA,IAAIE,EAAE,KAAK,YAAY,OAAOF,IAAIE,EAAE,KAAK,YAChD,OAAOF,IAAIG,KAAK,KAAK,YAAY,OAAOH,IAAIG,KAAK,KAAK,UACvD;gBACA,OAAO;oBAAEhB,MAAM;gBAAK;YACtB;YAEA,MAAMA,OAAO,MAAMH,QAChBoB,QAAQ,CAAC;gBAAEd,YAAYb;gBAAgByB,IAAIF,IAAIG,KAAK;gBAAEZ,OAAO;gBAAGE,gBAAgB;YAAK,GACrFY,KAAK,CAAC,IAAM;YAEf,IAAI,CAAClB,MAAM,OAAO;gBAAEA,MAAM;YAAK;YAE/B,MAAMH,QAAQsB,MAAM,CAAC;gBACnBhB,YAAYZ;gBACZwB,IAAIF,IAAIE,EAAE;gBACVK,MAAM;oBAAEC,YAAY,IAAIC,OAAOC,WAAW;gBAAG;gBAC7CjB,gBAAgB;YAClB;YAEA,OAAO;gBACLN,MAAM;oBACJ,GAAGA,IAAI;oBACPG,YAAYb;oBACZkC,WAAWvC;oBACXwC,UAAUZ,IAAIE,EAAE;oBAChB,GAAIF,IAAIa,OAAO,KAAKC,YAAY;wBAAED,SAASb,IAAIa,OAAO;oBAAC,IAAI,CAAC,CAAC;oBAC7D,GAAI,OAAOb,IAAIe,iBAAiB,KAAK,WACjC;wBAAEA,mBAAmBf,IAAIe,iBAAiB;oBAAC,IAC3C,CAAC,CAAC;gBACR;YACF;QACF;IACF;IAEA1C,iBAAiB2C,GAAG,CAACnC;IAErB,OAAOA;AACT;AAEA,OAAO,SAASoC,iBAAiBpC,QAAsB;IACrD,OAAOR,iBAAiB6C,GAAG,CAACrC;AAC9B;AAEA,OAAO,MAAMsC,eAAiC,CAAC,EAAEhC,IAAI,EAAE;IACrD,IAAIA,MAAMwB,cAAcvC,oBAAoB;IAE5C,OAAO;QAAEe;IAAK;AAChB,EAAE","file":"strategy.js"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frogbotai/plugin-api-keys",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"description": "Multiple named API keys for FrogBot.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Colby Gilbert",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"@payloadcms/ui": "3.90.1",
|
|
39
39
|
"react": "^19.0.1 || ^19.1.2 || ^19.2.1",
|
|
40
40
|
"react-dom": "^19.0.1 || ^19.1.2 || ^19.2.1",
|
|
41
|
-
"@frogbotai/gateway": "0.
|
|
42
|
-
"frogbot": "0.
|
|
41
|
+
"@frogbotai/gateway": "0.30.0",
|
|
42
|
+
"frogbot": "0.30.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@payloadcms/ui": "3.90.1",
|
|
@@ -48,15 +48,15 @@
|
|
|
48
48
|
"react": "19.2.6",
|
|
49
49
|
"react-dom": "19.2.6",
|
|
50
50
|
"typescript": "5.6.2",
|
|
51
|
-
"@frogbotai/
|
|
52
|
-
"
|
|
53
|
-
"
|
|
51
|
+
"@frogbotai/plugin-roles": "0.30.0",
|
|
52
|
+
"@frogbotai/gateway": "0.30.0",
|
|
53
|
+
"frogbot": "0.30.0"
|
|
54
54
|
},
|
|
55
55
|
"publishConfig": {
|
|
56
56
|
"access": "public"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
|
-
"build": "
|
|
59
|
+
"build": "node ../../../scripts/build-package.mjs --assets css",
|
|
60
60
|
"clean": "rm -rf dist",
|
|
61
61
|
"typecheck": "tsc --noEmit"
|
|
62
62
|
}
|