Skip to content

Commit

Permalink
feat: link accounts to compass app when they first log in if they wer…
Browse files Browse the repository at this point in the history
…e pre-added by case manager or admin
  • Loading branch information
thomhickey committed Oct 18, 2024
1 parent 38eb042 commit 642dad7
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 16 deletions.
15 changes: 11 additions & 4 deletions src/backend/auth/adapter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Adapter, AdapterSession } from "next-auth/adapters";
import { Adapter, AdapterSession, AdapterAccount } from "next-auth/adapters";
import {
KyselyDatabaseInstance,
KyselySchema,
Expand All @@ -7,6 +7,12 @@ import {
import { InsertObject, Selectable } from "kysely";
import { CustomAdapterUser, UserType } from "@/types/auth";

// Extend the Adapter interface to include the implemented methods
export interface ExtendedAdapter extends Adapter {
getUserByEmail: (email: string) => Promise<CustomAdapterUser | null>;
linkAccount: (account: AdapterAccount) => Promise<void>;
}

const mapStoredUserToAdapterUser = (
user: Selectable<ZapatosTableNameToKyselySchema<"user">>
): CustomAdapterUser => ({
Expand Down Expand Up @@ -35,17 +41,18 @@ const mapStoredSessionToAdapterSession = (
*/
export const createPersistedAuthAdapter = (
db: KyselyDatabaseInstance
): Adapter => ({
): ExtendedAdapter => ({
async createUser(user) {
const numOfUsers = await db
.selectFrom("user")
.select((qb) => qb.fn.count("user_id").as("count"))
.executeTakeFirstOrThrow();

// First created user is an admin
// First created user is an admin, else make them a user. This is to ensure there is always an admin user, but also to ensure we don't grant
// para or case_manager to folks not pre-added to the system.
// todo: this should be pulled from an invite or something else instead of defaulting to a para - currently devs signing in are being assigned as paras
const role =
Number(numOfUsers.count) === 0 ? UserType.Admin : UserType.Para;
Number(numOfUsers.count) === 0 ? UserType.Admin : UserType.User;

const [first_name, last_name] = user.name?.split(" ") ?? [
user.email?.split("@")[0],
Expand Down
60 changes: 48 additions & 12 deletions src/backend/auth/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,54 @@ import GoogleProvider from "next-auth/providers/google";
import { createPersistedAuthAdapter } from "@/backend/auth/adapter";
import { KyselyDatabaseInstance } from "../lib";
import type { NextAuthOptions } from "next-auth";
import type { ExtendedAdapter } from "@/backend/auth/adapter";

export const getNextAuthOptions = (
db: KyselyDatabaseInstance
): NextAuthOptions => ({
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID as string,
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
}),
],
adapter: createPersistedAuthAdapter(db),
pages: {
signIn: "/signInPage",
},
});
): NextAuthOptions => {
const adapter: ExtendedAdapter = createPersistedAuthAdapter(db);

return {
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID as string,
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
}),
],
adapter,
pages: {
signIn: "/signInPage",
},
callbacks: {
// hook into the sign in process so we can link accounts if needed
async signIn({ user, account }) {
if (account?.provider === "google") {
const existingUser = await adapter.getUserByEmail(user.email!);

if (existingUser) {
// user exists, check if account is linked
const linkedAccount = await db
.selectFrom("account")
.where("user_id", "=", existingUser.id)
.where("provider_name", "=", account.provider)
.where("provider_account_id", "=", account.providerAccountId)
.selectAll()
.executeTakeFirst();

if (!linkedAccount) {
// user was added by case manager or admin but hasn't logged in before so
// we need to link the user's google account
if (adapter.linkAccount) {
await adapter.linkAccount({
...account,
userId: existingUser.id,
});
}
}
}
}
return true;
},
},
};
};

0 comments on commit 642dad7

Please sign in to comment.