Skip to content

Instantly share code, notes, and snippets.

@p6l-richard
Last active April 13, 2024 17:19
Show Gist options
  • Save p6l-richard/6c2808ec0a16b0a3a736ce51cf76bb82 to your computer and use it in GitHub Desktop.
Save p6l-richard/6c2808ec0a16b0a3a736ce51cf76bb82 to your computer and use it in GitHub Desktop.
How to create a user on Cal.com with recovery flow
// Create a new user
/** [@calcom] 1. Create the user in cal */
let calUser: Omit<CalManageUserResponse, "status">["data"] | null =
null;
const url = `${env.NEXT_PUBLIC_CAL_API_URL}/oauth-clients/${env.NEXT_PUBLIC_CAL_OAUTH_CLIENT_ID}/users`;
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-cal-secret-key": env.CAL_SECRET,
origin:
env.NODE_ENV === "development"
? "http://localhost:3000"
: // TODO: Replace this after deployment
"https://platform.cal.com",
},
body: JSON.stringify({
email: credentials.data.email,
name: signupData.data.name,
}),
});
if (response.ok) {
const json = (await response.json()) as Omit<
CalManageUserResponse,
"status"
>;
calUser = json.data;
} else {
const text = await response.text();
if (!text.includes("already exists")) {
throw new Error(
`Unable to create user '${credentials.data.email}': Invalid response from Cal after POSTing to ${url}
Response text:
${await response.text()}
`,
);
}
// [@calcom] This means that the user already exists on cal's end but we didn't have them in our db
// We can just look them up by email and create the user in our db:
// let's fetch all users and get it from there.
const res = await fetch(url, {
headers: {
"Content-Type": "application/json",
"x-cal-secret-key": env.CAL_SECRET,
origin:
env.NODE_ENV === "development"
? "http://localhost:3000"
: // TODO: Replace this after deployment
"https://platform.cal.com",
},
});
if (!res.ok) {
throw new Error(
`Unable to create user '${credentials.data.email}': Invalid response from Cal after GETting: ${url}
ℹ️ This means the user already exists in cal, but we can't fetch it to get the id.
Response text:
${await res.text()}
`,
);
}
const calUsers = (await res.json()) as Omit<
CalManageUserResponse,
"data"
> & { data: Array<CalManageUserResponse["data"]["user"]> };
const fromCal = calUsers.data.find((calUser) => {
// [@calcom] the cal email adds `+<clientId>` before the @ in the email, so let's do the same four our matching:
const emailAsCal = credentials.data.email.replace(
"@",
`+${env.NEXT_PUBLIC_CAL_OAUTH_CLIENT_ID}@`,
);
return calUser.email === emailAsCal;
});
if (!fromCal) {
throw new Error(
`Unable to create user '${credentials.data.email}': User not found in Cal
ℹ️ This means the user already exists in cal, but we couldn't reconcile it from the response. Here are the emails:
${calUsers.data.map((u) => u.email).join(", ")}
`,
);
}
// [@calcom] OK, we reconciled the user. Let's force-refresh their tokens so that we can store everything in our db
const forceRefreshUrl = `${env.NEXT_PUBLIC_CAL_API_URL}/oauth-clients/${env.NEXT_PUBLIC_CAL_OAUTH_CLIENT_ID}/users/${fromCal.id}/force-refresh`;
const forceRefreshResponse = await fetch(forceRefreshUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-cal-secret-key": env.CAL_SECRET,
origin:
env.NODE_ENV === "development"
? "http://localhost:3000"
: // TODO: Replace this after deployment
"https://platform.cal.com",
},
});
if (!forceRefreshResponse.ok) {
throw new Error(
`Unable to create user '${credentials.data.email}': Invalid response from Cal after attempting to force-refresh tokens for cal user with id '${fromCal.id}'
Endpoint URL: ${forceRefreshUrl}
Response text:
${await forceRefreshResponse.text()}
`,
);
}
const {
data: { accessToken, refreshToken },
} = (await forceRefreshResponse.json()) as {
status: string;
data: { accessToken: string; refreshToken: string };
};
// [@calcom] ✅ Now, we have successfully recovered our users tokens. Let's allocate this to our `calUser`
calUser = { user: fromCal, accessToken, refreshToken };
}
const { accessToken, refreshToken, user: toCreate } = calUser;
/** [@calcom] 2. Create the user in our db with cal's tokens */
user = await db.user.create({
data: {
username: signupData.data.username,
name: signupData.data.name,
hashedPassword: await hash(credentials.data.password),
email: credentials.data.email,
/** [@calcom] 👇 These are the tokens necessary to make cal operations on behalf of the user */
calAccount: {
create: { ...toCreate, accessToken, refreshToken },
},
/** [@calcom] 👆 */
},
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment