You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When a new admin needs to be added to the system, we can't just create them in one place — we need to create them in two places at once and keep them in sync:
Our backend database — a new User record and a linked AdminInfo record
AWS Cognito — a new user with the same email, so they can actually log in
If either of these steps fails, we should not partially create the admin. Both need to succeed together.
What is Cognito (quick recap)?
Cognito is AWS's authentication service. It handles login, passwords, and session tokens. Our app uses it to verify "is this person who they say they are?" Our database handles everything else about the user — their role, their info, their permissions.
Who can create a new admin?
New admin accounts should only be created by an existing authenticated admin — not by just anyone. The chosen approach is the Temporary Password flow, which leans on Cognito's built-in behavior and keeps custom code minimal.
Chosen Approach: Temporary Password via Email (Cognito Native Flow)
An existing admin triggers the creation of a new admin account directly. The backend immediately creates the database records and Cognito user, and Cognito auto-emails the new admin a temporary password. On first login, Cognito forces a password change before the user can do anything.
Why it's reasonably secure:
Only an existing admin can trigger the creation
Temp passwords are short-lived and randomly generated
Cognito enforces a password reset on first use
Complexity: Lower. Most of this is handled by Cognito natively. The main work is wiring up the database creation alongside the Cognito creation and making sure both succeed or both roll back.
The Two-System Problem
Because we're writing to both Cognito and our database, we need to think carefully about what happens if one fails. For example:
What if the Cognito user is created but the database write fails? → We have a ghost user in Cognito with no database record
What if the database write succeeds but Cognito fails? → We have a database record with no way to log in
This ticket should document a strategy for handling these failure cases, even if the actual error handling isn't implemented yet.
Acceptance Criteria
This ticket is scaffolding and documentation only — no real AWS calls, no real database writes. All functions must be mocked and documented.
Overall workflow is documented as a clear numbered sequence covering both options, from "admin clicks Create New Admin" to "new admin is fully set up." This should be written in plain English in the module-level README block and cover:
Who initiates the flow and how
What happens in Cognito
What happens in the database
What the new admin experiences (email, first login, password reset)
createAdminUser(email, temporaryPassword) is scaffolded with a mock and JSDoc that describes:
What it does (calls Cognito AdminCreateUser and triggers the temp password email)
Parameters and expected types
Return value on success (e.g., Cognito username and user status)
What errors it might throw (e.g., email already exists in Cognito)
createAdminDatabaseRecords(email, cognitoUsername) is scaffolded with a mock and JSDoc that describes:
What it does (creates a User record and an AdminInfo record in the database, linked together)
Parameters and expected types
Return value on success (e.g., the new User and AdminInfo objects)
What errors it might throw
provisionNewAdmin(email) is scaffolded as the top-level orchestrator function with a mock and JSDoc that describes:
What it does (calls createAdminUser and createAdminDatabaseRecords in sequence)
That it should only be callable by an authenticated admin (note this as a TODO for auth middleware)
How it handles partial failure — document the strategy even if not yet implemented (e.g., "if database write fails after Cognito succeeds, we should delete the Cognito user and throw an error")
Return value and error cases
handleNewPasswordChallenge(username, tempPassword, newPassword) is scaffolded with a mock and JSDoc that describes:
What it does (calls Cognito RespondToAuthChallenge with NEW_PASSWORD_REQUIRED)
When it is triggered (frontend calls this after the new admin submits their new password on first login)
Parameters, return value (auth tokens), and error cases
resendAdminInvite(email) is scaffolded with a mock and JSDoc that describes:
What it does (re-triggers the Cognito temp password email via MessageAction: RESEND)
When it would be used (e.g., new admin never received the email or the temp password expired)
Parameters, return value, and error cases
All mocks return realistic-looking fake data mirroring actual Cognito and database response shapes. No real AWS or database calls should be made.
A module-level README block exists that covers:
The full flow in plain English
Which AWS SDK package will be used (@aws-sdk/client-cognito-identity-provider)
What environment variables will be needed (e.g., COGNITO_USER_POOL_ID, COGNITO_CLIENT_ID, AWS_REGION) — values can be TODO for now
The two database records that need to be created (User, AdminInfo) and how they relate to each other
A note on the partial failure problem and the intended strategy to handle it
No real AWS credentials, API calls, or database writes appear anywhere in this ticket.
Please branch off of the authentication branch.
Context
When a new admin needs to be added to the system, we can't just create them in one place — we need to create them in two places at once and keep them in sync:
Userrecord and a linkedAdminInforecordIf either of these steps fails, we should not partially create the admin. Both need to succeed together.
What is Cognito (quick recap)?
Cognito is AWS's authentication service. It handles login, passwords, and session tokens. Our app uses it to verify "is this person who they say they are?" Our database handles everything else about the user — their role, their info, their permissions.
Who can create a new admin?
New admin accounts should only be created by an existing authenticated admin — not by just anyone. The chosen approach is the Temporary Password flow, which leans on Cognito's built-in behavior and keeps custom code minimal.
Chosen Approach: Temporary Password via Email (Cognito Native Flow)
An existing admin triggers the creation of a new admin account directly. The backend immediately creates the database records and Cognito user, and Cognito auto-emails the new admin a temporary password. On first login, Cognito forces a password change before the user can do anything.
Why it's reasonably secure:
Complexity: Lower. Most of this is handled by Cognito natively. The main work is wiring up the database creation alongside the Cognito creation and making sure both succeed or both roll back.
The Two-System Problem
Because we're writing to both Cognito and our database, we need to think carefully about what happens if one fails. For example:
This ticket should document a strategy for handling these failure cases, even if the actual error handling isn't implemented yet.
Acceptance Criteria
This ticket is scaffolding and documentation only — no real AWS calls, no real database writes. All functions must be mocked and documented.
Overall workflow is documented as a clear numbered sequence covering both options, from "admin clicks Create New Admin" to "new admin is fully set up." This should be written in plain English in the module-level README block and cover:
createAdminUser(email, temporaryPassword)is scaffolded with a mock and JSDoc that describes:AdminCreateUserand triggers the temp password email)createAdminDatabaseRecords(email, cognitoUsername)is scaffolded with a mock and JSDoc that describes:Userrecord and anAdminInforecord in the database, linked together)UserandAdminInfoobjects)provisionNewAdmin(email)is scaffolded as the top-level orchestrator function with a mock and JSDoc that describes:createAdminUserandcreateAdminDatabaseRecordsin sequence)handleNewPasswordChallenge(username, tempPassword, newPassword)is scaffolded with a mock and JSDoc that describes:RespondToAuthChallengewithNEW_PASSWORD_REQUIRED)resendAdminInvite(email)is scaffolded with a mock and JSDoc that describes:MessageAction: RESEND)All mocks return realistic-looking fake data mirroring actual Cognito and database response shapes. No real AWS or database calls should be made.
A module-level README block exists that covers:
@aws-sdk/client-cognito-identity-provider)COGNITO_USER_POOL_ID,COGNITO_CLIENT_ID,AWS_REGION) — values can beTODOfor nowUser,AdminInfo) and how they relate to each otherNo real AWS credentials, API calls, or database writes appear anywhere in this ticket.