Skip to content

Commit 2f0f8f3

Browse files
committed
Add Claris ID auth support
- add Claris ID login flow for fmodata - wire CLI and typegen env/config support - update docs and tests
1 parent 175b0bd commit 2f0f8f3

31 files changed

Lines changed: 1295 additions & 283 deletions

.changeset/early-eels-smile.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@proofkit/fmodata": minor
3+
"@proofkit/typegen": minor
4+
---
5+
6+
Add Claris ID auth support for `fmodata` FileMaker Cloud connections, including CLI and typegen env/config support.

apps/docs/src/app/(home)/page.tsx

Lines changed: 26 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -26,51 +26,34 @@ export default function HomePage() {
2626
<div className="mx-auto flex w-full max-w-screen-lg flex-col items-center justify-center">
2727
<div className="relative flex h-[500px] w-full flex-col items-center justify-center overflow-hidden rounded-lg bg-background">
2828
<InteractiveGridPattern
29-
className={cn(
30-
"absolute inset-0 [mask-image:radial-gradient(400px_circle_at_center,white,transparent)]",
31-
)}
29+
className={cn("absolute inset-0 [mask-image:radial-gradient(400px_circle_at_center,white,transparent)]")}
3230
height={40}
3331
squares={[80, 80]}
3432
squaresClassName="hover:fill-brand/50"
3533
style={{ zIndex: 0 }}
3634
width={40}
3735
/>
38-
<Image
39-
alt="ProofKit Logo"
40-
className="pointer-events-none z-10"
41-
src={ProofKitLogo}
42-
width={400}
43-
/>
36+
<Image alt="ProofKit Logo" className="pointer-events-none z-10" src={ProofKitLogo} width={400} />
4437
</div>
4538

4639
<div className="mt-8 w-full space-y-8 text-center">
47-
<h1 className="font-bold text-4xl">
48-
A collection of tools for FileMaker-aware TypeScript applications
49-
</h1>
40+
<h1 className="font-bold text-4xl">A collection of tools for FileMaker-aware TypeScript applications</h1>
5041
<p className="font-medium text-gray-500 text-xl">
51-
For new and experienced developers alike, the ProofKit toolset is
52-
the best way to build web apps connected to FileMaker data, or rich,
53-
interactive interfaces in a FileMaker webviewer.
42+
For new and experienced developers alike, the ProofKit toolset is the best way to build web apps connected
43+
to FileMaker data, or rich, interactive interfaces in a FileMaker webviewer.
5444
</p>
5545

5646
<Cards className="px-4 text-left">
5747
<Card href="/docs/cli" icon={<Terminal />} title="ProofKit CLI">
58-
A command line tool to start a new project, or easily apply
59-
templates and common patterns with{" "}
60-
<span className="underline">no JavaScript experience</span>{" "}
61-
required.
48+
A command line tool to start a new project, or easily apply templates and common patterns with{" "}
49+
<span className="underline">no JavaScript experience</span> required.
6250
</Card>
6351
<Card href="/docs/typegen" icon={<Code />} title={"Typegen"}>
64-
Automatically generate runtime validators and TypeScript files
65-
from your own FileMaker layouts or table occurrences.
52+
Automatically generate runtime validators and TypeScript files from your own FileMaker layouts or table
53+
occurrences.
6654
</Card>
67-
<Card
68-
href="/docs/fmdapi"
69-
icon={<WebhookIcon />}
70-
title="Filemaker Data API"
71-
>
72-
A type-safe API for your FileMaker layouts. Easily connect without
73-
worrying about token management.
55+
<Card href="/docs/fmdapi" icon={<WebhookIcon />} title="Filemaker Data API">
56+
A type-safe API for your FileMaker layouts. Easily connect without worrying about token management.
7457
</Card>
7558
<Card
7659
href="/docs/fmodata"
@@ -84,16 +67,11 @@ export default function HomePage() {
8467
</span>
8568
}
8669
>
87-
A strongly-typed OData API client with full TypeScript inference,
88-
runtime validation, and a fluent query builder.
70+
A strongly-typed OData API client with full TypeScript inference, runtime validation, and a fluent query
71+
builder.
8972
</Card>
90-
<Card
91-
href="/docs/webviewer"
92-
icon={<Globe />}
93-
title="FileMaker Webviewer"
94-
>
95-
Use async functions in WebViewer code to execute and get the
96-
result of a FileMaker script.
73+
<Card href="/docs/webviewer" icon={<Globe />} title="FileMaker Webviewer">
74+
Use async functions in WebViewer code to execute and get the result of a FileMaker script.
9775
</Card>
9876
<Card
9977
href="/docs/better-auth"
@@ -107,8 +85,7 @@ export default function HomePage() {
10785
</span>
10886
}
10987
>
110-
Own your authentication with FileMaker and the extensible
111-
Better-Auth framework.
88+
Own your authentication with FileMaker and the extensible Better-Auth framework.
11289
</Card>
11390
</Cards>
11491

@@ -118,8 +95,7 @@ export default function HomePage() {
11895
<div className="flex flex-col text-left">
11996
<h2 className="mb-4 font-bold text-3xl">Quick Start</h2>
12097
<p className="mb-0 text-gray-600 text-lg">
121-
Use the ProofKit CLI to launch a full-featured Next.js app in
122-
minutes—no prior experience required.
98+
Use the ProofKit CLI to launch a full-featured Next.js app in minutes—no prior experience required.
12399
</p>
124100
</div>
125101

@@ -152,11 +128,9 @@ export default function HomePage() {
152128
Built for AI Agents
153129
</h2>
154130
<p className="mb-0 text-gray-600 text-lg">
155-
Every ProofKit package ships with agent skills — built from
156-
decades of combined FileMaker integration experience at Proof —
157-
that give AI coding tools like Claude Code and Cursor the
158-
context they need to write correct, production-ready FileMaker
159-
code from day one.
131+
Every ProofKit package ships with agent skills — built from decades of combined FileMaker integration
132+
experience at Proof — that give AI coding tools like Claude Code and Cursor the context they need to
133+
write correct, production-ready FileMaker code from day one.
160134
</p>
161135
</div>
162136

@@ -167,9 +141,8 @@ export default function HomePage() {
167141
Expert knowledge built in
168142
</div>
169143
<p className="text-gray-500 text-sm">
170-
Agent skills cover API patterns, edge cases, and common
171-
mistakes so your AI agent avoids the pitfalls that trip up
172-
even experienced developers.
144+
Agent skills cover API patterns, edge cases, and common mistakes so your AI agent avoids the pitfalls
145+
that trip up even experienced developers.
173146
</p>
174147
</div>
175148
<div className="flex flex-col gap-2 rounded-lg border p-4">
@@ -178,9 +151,8 @@ export default function HomePage() {
178151
Type-safe by default
179152
</div>
180153
<p className="text-gray-500 text-sm">
181-
Schemas generated from your FileMaker field names plus runtime
182-
validators catch bugs early — whether code is written by you
183-
or your AI agent.
154+
Schemas generated from your FileMaker field names plus runtime validators catch bugs early — whether
155+
code is written by you or your AI agent.
184156
</p>
185157
</div>
186158
<div className="flex flex-col gap-2 rounded-lg border p-4">
@@ -189,9 +161,8 @@ export default function HomePage() {
189161
Works with any agent
190162
</div>
191163
<p className="text-gray-500 text-sm">
192-
Skills are bundled with each package — just install and your
193-
AI coding tool picks them up automatically. Compatible with
194-
Claude Code, Cursor, Windsurf, and more.
164+
Skills are bundled with each package — just install and your AI coding tool picks them up
165+
automatically. Compatible with Claude Code, Cursor, Windsurf, and more.
195166
</p>
196167
</div>
197168
</div>

apps/docs/src/app/docs/(docs)/layout.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,7 @@ export default function Layout({ children }: { children: ReactNode }) {
1616
<div className="mt-2 flex items-center justify-center text-muted-foreground text-xs">
1717
<p>
1818
Made with ❤️ by{" "}
19-
<a
20-
className="underline"
21-
href="http://proof.sh"
22-
rel="noopener"
23-
target="_blank"
24-
>
19+
<a className="underline" href="http://proof.sh" rel="noopener" target="_blank">
2520
Proof
2621
</a>
2722
</p>

apps/docs/src/app/docs/templates/layout.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,7 @@ export default async function Layout({ children }: { children: ReactNode }) {
8686
<div className="mt-2 flex items-center justify-center text-muted-foreground text-xs">
8787
<p>
8888
Made with ❤️ by{" "}
89-
<a
90-
className="underline"
91-
href="http://proof.sh"
92-
rel="noopener noreferrer"
93-
target="_blank"
94-
>
89+
<a className="underline" href="http://proof.sh" rel="noopener noreferrer" target="_blank">
9590
Proof
9691
</a>
9792
</p>

packages/fmodata/README.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ const connection = new FMServerConnection({
4040
// OttoFMS API key
4141
apiKey: "your-api-key",
4242

43+
// or Claris ID for FileMaker Cloud
44+
// clarisId: {
45+
// username: "your@claris-id.com",
46+
// password: "password",
47+
// },
48+
4349
// or username and password
4450
// username: "admin",
4551
// password: "password",
@@ -96,7 +102,7 @@ OData relies entirely on the table occurances in the relationship graph for data
96102

97103
### Server Connection
98104

99-
The client can authenticate using username/password or API key:
105+
The client can authenticate using username/password, Otto API key, or Claris ID for FileMaker Cloud:
100106

101107
```typescript
102108
// Username and password authentication
@@ -115,8 +121,26 @@ const connection = new FMServerConnection({
115121
apiKey: "your-api-key",
116122
},
117123
});
124+
125+
// Claris ID authentication for FileMaker Cloud
126+
const connection = new FMServerConnection({
127+
serverUrl: "https://your-filemaker-cloud-host.com",
128+
auth: {
129+
clarisId: {
130+
username: "your@claris-id.com",
131+
password: "password",
132+
},
133+
},
134+
});
118135
```
119136

137+
Notes for Claris ID auth:
138+
139+
- Use a Claris ID account, not an external IdP account.
140+
- Tokens are managed internally and refreshed automatically.
141+
- Claris ID tokens are valid for about one hour.
142+
- MFA is not supported.
143+
120144
### Schema Definitions
121145

122146
This library relies on a schema-first approach for good type-safety and optional runtime validation. Use **`fmTableOccurrence()`** with field builders to create your schemas. This provides full TypeScript type inference for field names in queries.

packages/fmodata/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
},
6262
"dependencies": {
6363
"@fetchkit/ffetch": "^4.2.0",
64+
"amazon-cognito-identity-js": "^6.3.16",
6465
"cli-table3": "^0.6.5",
6566
"commander": "^14.0.2",
6667
"dotenv": "^16.6.1",

packages/fmodata/skills/fmodata-client/SKILL.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,17 @@ const connection = new FMServerConnection({
3333
auth: { username: "admin", password: "secret" },
3434
// OR with OttoFMS API key:
3535
// auth: { apiKey: "your-otto-api-key" },
36+
// OR with Claris ID for FileMaker Cloud:
37+
// auth: { clarisId: { username: "you@claris-id.com", password: "secret" } },
3638
fetchClientOptions: {
3739
retries: 2,
3840
timeout: 30000,
3941
},
4042
});
4143
```
4244

45+
For FileMaker Cloud, use a Claris ID account, not an external IdP account. MFA-backed Claris ID accounts are not supported.
46+
4347
### 2. Create a database reference
4448

4549
```ts

packages/fmodata/src/cli/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ program
2121
.option("--database <name>", `FM database name [env: ${ENV_NAMES.db}]`)
2222
.option("--username <user>", `FM username [env: ${ENV_NAMES.username}]`)
2323
.option("--password <pass>", `FM password [env: ${ENV_NAMES.password}]`)
24+
.option("--claris-id-username <user>", `Claris ID username [env: ${ENV_NAMES.clarisIdUsername}]`)
25+
.option("--claris-id-password <pass>", `Claris ID password [env: ${ENV_NAMES.clarisIdPassword}]`)
2426
.option("--api-key <key>", `OttoFMS API key [env: ${ENV_NAMES.apiKey}]`)
2527
.option("--pretty", "Output as table (default: JSON)", false);
2628

packages/fmodata/src/cli/utils/connection.ts

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ export const ENV_NAMES = {
66
db: "FM_DATABASE",
77
username: "FM_USERNAME",
88
password: "FM_PASSWORD",
9+
clarisIdUsername: "CLARIS_ID_USERNAME",
10+
clarisIdPassword: "CLARIS_ID_PASSWORD",
911
apiKey: "OTTO_API_KEY",
1012
} as const;
1113

@@ -14,6 +16,8 @@ export interface ConnectionOptions {
1416
database?: string;
1517
username?: string;
1618
password?: string;
19+
clarisIdUsername?: string;
20+
clarisIdPassword?: string;
1721
apiKey?: string;
1822
}
1923

@@ -28,21 +32,43 @@ export function buildConnection(opts: ConnectionOptions): BuiltConnection {
2832
const apiKey = opts.apiKey ?? process.env[ENV_NAMES.apiKey];
2933
const username = opts.username ?? process.env[ENV_NAMES.username];
3034
const password = opts.password ?? process.env[ENV_NAMES.password];
35+
const clarisIdUsername = opts.clarisIdUsername ?? process.env[ENV_NAMES.clarisIdUsername];
36+
const clarisIdPassword = opts.clarisIdPassword ?? process.env[ENV_NAMES.clarisIdPassword];
3137

3238
if (!server) {
3339
throw new Error(`Missing required: --server or ${ENV_NAMES.server} environment variable`);
3440
}
3541
if (!database) {
3642
throw new Error(`Missing required: --database or ${ENV_NAMES.db} environment variable`);
3743
}
38-
if (!(apiKey || username)) {
39-
throw new Error(`Missing required auth: --api-key (${ENV_NAMES.apiKey}) or --username (${ENV_NAMES.username})`);
44+
if (!(apiKey || clarisIdUsername || username)) {
45+
throw new Error(
46+
`Missing required auth: --api-key (${ENV_NAMES.apiKey}), --claris-id-username (${ENV_NAMES.clarisIdUsername}), or --username (${ENV_NAMES.username})`,
47+
);
48+
}
49+
if (!apiKey && clarisIdUsername && !clarisIdPassword) {
50+
throw new Error(`Missing required: --claris-id-password (${ENV_NAMES.clarisIdPassword}) when using Claris ID auth`);
4051
}
4152
if (!apiKey && username && !password) {
4253
throw new Error(`Missing required: --password (${ENV_NAMES.password}) when using username auth`);
4354
}
4455

45-
const auth = apiKey ? { apiKey } : { username: username as string, password: password as string };
56+
let auth:
57+
| { apiKey: string }
58+
| { clarisId: { username: string; password: string } }
59+
| { username: string; password: string };
60+
if (apiKey) {
61+
auth = { apiKey };
62+
} else if (clarisIdUsername) {
63+
auth = {
64+
clarisId: {
65+
username: clarisIdUsername as string,
66+
password: clarisIdPassword as string,
67+
},
68+
};
69+
} else {
70+
auth = { username: username as string, password: password as string };
71+
}
4672
const connection = new FMServerConnection({ serverUrl: server, auth });
4773
const db = connection.database(database);
4874
return { connection, db };
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
declare module "amazon-cognito-identity-js" {
2+
export class CognitoRefreshToken {
3+
constructor(data: { RefreshToken: string });
4+
}
5+
6+
export class CognitoAccessToken {
7+
constructor(data: { AccessToken: string });
8+
getJwtToken(): string;
9+
}
10+
11+
export class CognitoIdToken {
12+
constructor(data: { IdToken: string });
13+
getJwtToken(): string;
14+
}
15+
16+
export class CognitoUserSession {
17+
constructor(data: {
18+
AccessToken: CognitoAccessToken;
19+
IdToken: CognitoIdToken;
20+
RefreshToken: CognitoRefreshToken;
21+
});
22+
isValid(): boolean;
23+
getIdToken(): CognitoIdToken;
24+
getAccessToken(): CognitoAccessToken;
25+
getRefreshToken(): CognitoRefreshToken;
26+
}
27+
28+
export class AuthenticationDetails {
29+
constructor(data: { Username: string; Password: string });
30+
getUsername(): string;
31+
}
32+
33+
export class CognitoUserPool {
34+
constructor(data: { UserPoolId: string; ClientId: string });
35+
}
36+
37+
export class CognitoUser {
38+
constructor(data: { Username: string; Pool: CognitoUserPool });
39+
authenticateUser(
40+
details: AuthenticationDetails,
41+
callbacks: {
42+
onSuccess: (session: CognitoUserSession) => void;
43+
onFailure: (error: unknown) => void;
44+
mfaRequired?: (...args: unknown[]) => void;
45+
totpRequired?: (...args: unknown[]) => void;
46+
selectMFAType?: (...args: unknown[]) => void;
47+
mfaSetup?: (...args: unknown[]) => void;
48+
},
49+
): void;
50+
refreshSession(
51+
refreshToken: CognitoRefreshToken,
52+
callback: (error: unknown, session: CognitoUserSession | null) => void,
53+
): void;
54+
}
55+
}

0 commit comments

Comments
 (0)