Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only use session claims #181

Merged
merged 15 commits into from
Nov 10, 2024
19 changes: 18 additions & 1 deletion functions/models/src/types/userRegistration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,13 @@ export const userRegistrationConverter = new Lazy(
}),
)

export interface UserClaims {
type: UserType
organization?: string
}

export class UserRegistration {
// Properties
// Stored Properties

readonly type: UserType
readonly organization?: string
Expand All @@ -80,6 +85,18 @@ export class UserRegistration {
readonly language?: string
readonly timeZone?: string

// Computed Properties

get claims(): UserClaims {
const result: UserClaims = {
type: this.type,
}
if (this.organization !== undefined) {
result.organization = this.organization
}
return result
}

// Constructor

constructor(input: {
Expand Down
17 changes: 14 additions & 3 deletions functions/src/functions/blocking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const beforeUserCreatedFunction = beforeUserCreated(
const credential = event.credential

// Escape hatch for users using invitation code to enroll
if (!credential) return
if (!credential) return { customClaims: {} }

if (event.data.email === undefined)
throw new https.HttpsError(
Expand Down Expand Up @@ -64,6 +64,8 @@ export const beforeUserCreatedFunction = beforeUserCreated(
isSingleSignOn: true,
})
await factory.trigger().userEnrolled(userDoc)

return { customClaims: invitation.content.user.claims }
},
)

Expand All @@ -72,10 +74,19 @@ export const beforeUserSignedInFunction = beforeUserSignedIn(
async (event) => {
try {
const userService = getServiceFactory().user()
await userService.updateClaims(event.data.uid)
logger.info(`beforeUserSignedIn finished successfully.`)
const user = await userService.getUser(event.data.uid)
if (user !== undefined) {
logger.info(`beforeUserSignedIn finished successfully.`)
return {
customClaims: user.content.claims,
sessionClaims: user.content.claims,
}
}
logger.info(`beforeUserSignedIn finished without user.`)
return { customClaims: {} }
} catch (error) {
logger.error(`beforeUserSignedIn finished with error: ${String(error)}`)
return { customClaims: {} }
}
},
)
22 changes: 11 additions & 11 deletions functions/src/services/user/databaseUserService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,16 @@ describe('DatabaseUserService', () => {

const auth = await admin.auth().getUser(userId)
expect(auth.displayName).to.equal(displayName)
expect(auth.customClaims).to.deep.equal({
type: UserType.admin,
})

const userSnapshot = await collectionsService.users.doc(userId).get()
expect(userSnapshot.exists).to.be.true
const userData = userSnapshot.data()
expect(userData).to.exist
expect(userData?.invitationCode).to.equal(invitationCode)
expect(userData?.dateOfEnrollment).to.exist
expect(userData?.claims).to.deep.equal({
type: UserType.admin,
})
})

it('enrolls a clinician', async () => {
Expand Down Expand Up @@ -104,17 +104,17 @@ describe('DatabaseUserService', () => {

const auth = await admin.auth().getUser(userId)
expect(auth.displayName).to.equal(displayName)
expect(auth.customClaims).to.deep.equal({
type: UserType.clinician,
organization: 'mockOrganization',
})

const userSnapshot = await collectionsService.users.doc(userId).get()
expect(userSnapshot.exists).to.be.true
const userData = userSnapshot.data()
expect(userData).to.exist
expect(userData?.invitationCode).to.equal(invitationCode)
expect(userData?.dateOfEnrollment).to.exist
expect(userData?.claims).to.deep.equal({
type: UserType.clinician,
organization: 'mockOrganization',
})
})

it('enrolls a patient', async () => {
Expand Down Expand Up @@ -151,17 +151,17 @@ describe('DatabaseUserService', () => {

const auth = await admin.auth().getUser(userId)
expect(auth.displayName).to.equal(displayName)
expect(auth.customClaims).to.deep.equal({
type: UserType.patient,
organization: 'mockOrganization',
})

const userSnapshot = await collectionsService.users.doc(userId).get()
expect(userSnapshot.exists).to.be.true
const userData = userSnapshot.data()
expect(userData).to.exist
expect(userData?.invitationCode).to.equal(invitationCode)
expect(userData?.dateOfEnrollment).to.exist
expect(userData?.claims).to.deep.equal({
type: UserType.patient,
organization: 'mockOrganization',
})
})
})
})
46 changes: 13 additions & 33 deletions functions/src/services/user/databaseUserService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,6 @@ import {
type DatabaseService,
} from '../database/databaseService.js'

export interface UserClaims {
type: UserType
organization?: string
}

export class DatabaseUserService implements UserService {
// Properties

Expand Down Expand Up @@ -64,35 +59,21 @@ export class DatabaseUserService implements UserService {
}

async updateClaims(userId: string): Promise<void> {
try {
const user = await this.getUser(userId)
if (user !== undefined) {
const claims: UserClaims = {
type: user.content.type,
}
if (user.content.organization !== undefined)
claims.organization = user.content.organization
logger.info(
`Will set claims for user ${userId}: ${JSON.stringify(claims)}`,
)
await this.auth.setCustomUserClaims(userId, claims)
logger.info(`Successfully set claims for user ${userId}.`)
} else {
await this.auth.setCustomUserClaims(userId, {})
logger.info(
`Successfully set claims for not-yet-enrolled user ${userId}.`,
)
}
} catch (error) {
const user = await this.getUser(userId)
if (user === undefined) {
logger.error(
`Failed to update claims for user ${userId}: ${String(error)}`,
)
await this.auth.setCustomUserClaims(userId, {})
logger.debug(
`Successfully reset claims for user ${userId} to empty object.`,
`DatabaseUserService.updateClaims(${userId}): User not found.`,
)
throw error
throw new https.HttpsError('not-found', 'User not found.')
}
const claims = user.content.claims
logger.info(
`DatabaseUserService.updateClaims(${userId}): Will set claims to ${JSON.stringify(claims)}.`,
)
await this.auth.setCustomUserClaims(userId, claims)
logger.info(
`DatabaseUserService.updateClaims(${userId}): User claims updated.`,
)
}

// Invitations
Expand Down Expand Up @@ -157,7 +138,6 @@ export class DatabaseUserService implements UserService {
if (!options.isSingleSignOn) {
await this.auth.updateUser(userId, {
displayName: invitation.content.auth?.displayName ?? undefined,
email: invitation.content.auth?.email ?? undefined,
phoneNumber: invitation.content.auth?.phoneNumber ?? undefined,
photoURL: invitation.content.auth?.photoURL ?? undefined,
})
Expand All @@ -177,7 +157,7 @@ export class DatabaseUserService implements UserService {
transaction.set(userRef, userData)

if (!options.isSingleSignOn) {
await this.updateClaims(userId)
await this.auth.setCustomUserClaims(userId, userData.claims)
}

return {
Expand Down
Loading