fathom-typescript
Version:
Fathom's official TypeScript SDK.
131 lines (110 loc) • 3.52 kB
text/typescript
import * as z from "zod";
import { SDK_METADATA } from "../lib/config.js";
import { Security } from "../sdk/models/shared/security.js";
const tokenResponseSchema = z.object({
access_token: z.string(),
expires_in: z.number().positive(),
refresh_token: z.string(),
token_type: z.string().optional(),
scope: z.string().optional(),
});
const tolerance = 5 * 60;
export function withAuthorization({
clientId,
clientSecret,
code,
redirectUri,
tokenStore,
}: {
clientId: string,
clientSecret: string,
code: string,
redirectUri: string,
tokenStore: TokenStore,
}) {
const url = "https://fathom.video/external/v1/oauth2/token"
return async (): Promise<Security> => {
const session = await tokenStore.get();
const now_seconds = Date.now() / 1000;
if (session && session.token !== '' && session.expires > now_seconds) {
return { bearerAuth: session.token };
}
if (session?.token === '') {
try {
const response = await fetch(url, {
method: "POST",
headers: {
"content-type": "application/x-www-form-urlencoded",
"user-agent": SDK_METADATA.userAgent,
},
body: new URLSearchParams({
client_id: clientId,
client_secret: clientSecret,
code: code,
redirect_uri: redirectUri,
grant_type: "authorization_code",
}),
});
if (!response.ok) {
throw new Error("Unexpected status code: " + response.status);
}
const json = await response.json();
const data = tokenResponseSchema.parse(json);
await tokenStore.set(
data.access_token,
data.refresh_token,
now_seconds + data.expires_in - tolerance,
);
return { bearerAuth: data.access_token };
} catch (error) {
throw new Error("Failed to obtain OAuth token: " + error);
}
}
try {
const response = await fetch(url, {
method: "POST",
headers: {
"content-type": "application/x-www-form-urlencoded",
"user-agent": SDK_METADATA.userAgent,
},
body: new URLSearchParams({
client_id: clientId,
client_secret: clientSecret,
refresh_token: session?.refresh_token || "",
grant_type: "refresh_token",
}),
});
if (!response.ok) {
throw new Error("Unexpected status code: " + response.status);
}
const json = await response.json();
const data = tokenResponseSchema.parse(json);
await tokenStore.set(
data.access_token,
data.refresh_token,
now_seconds + data.expires_in - tolerance,
);
return { bearerAuth: data.access_token };
} catch (error) {
throw new Error("Failed to obtain OAuth token: " + error);
}
};
}
export interface TokenStore {
get(): Promise<{ token: string; refresh_token: string; expires: number } | undefined>;
set(token: string, refresh_token: string, expires: number): Promise<void>;
}
export class InMemoryTokenStore implements TokenStore {
private token = "";
private refresh_token = "";
private expires = Date.now();
constructor() {}
async get() {
return { token: this.token, refresh_token: this.refresh_token, expires: this.expires };
}
async set(token: string, refresh_token: string, expires: number) {
this.token = token;
this.refresh_token = refresh_token;
this.expires = expires;
}
}