How to set up MFA
This guide shows you how to add an authenticator app to an account, and how to sign in with it. Picked supports TOTP authenticator apps only, such as Google Authenticator. Every MFA mutation needs authentication.
Who must use MFA
Picked staff, and every member of an organization that requires MFA, must sign in with an
authenticator app. For these users, most operations return the error code MFA_REQUIRED
until the session reaches AAL2. The operations that a session at AAL1 can use are in
Authenticator assurance levels.
The user.mfa field tells you where a user stands:
query {
user {
mfa { required reason enrolled satisfied }
}
}| Field | Meaning |
|---|---|
required | The user must hold an AAL2 session to use the fields that need it. |
reason | STAFF or ORGANIZATION. Empty when required is false. |
enrolled | The user holds a verified authenticator. |
satisfied | The session that made the request meets the requirement. |
1. Add an authenticator
mutation {
mfaFactorEnroll(input: { friendlyName: "Work phone" }) {
factor { id factorType status }
totp { qrCode secret uri }
}
}A new factor has the status UNVERIFIED. Show qrCode (SVG markup) for the user to scan,
or secret for the user to enter into the app. The API returns it one time only.
Before it adds the factor, the mutation deletes the caller's factors that are not verified.
But it keeps the two newest of these factors if they are less than 60 seconds old. A
different setup can still use them, for example in a different browser tab. The mutation
also deletes a factor that is not verified and has the friendlyName that you give.
Therefore, a setup that did not finish does not stop a new setup.
2. Open a challenge
mutation {
mfaChallengeCreate(input: { factorId: "factor-id" }) {
challengeId
expiresAt
}
}3. Verify the code
mutation {
mfaChallengeVerify(input: {
factorId: "factor-id"
challengeId: "challenge-id"
code: "123456"
}) {
session { accessToken refreshToken aal }
}
}The session moves to AAL2, and the factor status changes to VERIFIED. Use the returned
tokens from this point. A wrong code returns MFA_CHALLENGE_FAILED.
Sign in with an authenticator
After sessionCreate, the session is at AAL1. When session.user.mfa.enrolled is
true, do steps 2 and 3 against one of the user's verified factors. The query
user { mfaFactors { id status } } lists them. When required is true and enrolled
is false, do steps 1 to 3.
Remove an authenticator
mutation {
mfaFactorUnenroll(input: { factorId: "factor-id" }) {
success
}
}To remove a verified factor, the session must be at AAL2. An AAL1 session receives
MFA_REQUIRED.
A user who must use MFA always keeps at least one authenticator. A request to remove
their last one fails with VALIDATION_ERROR. To replace an authenticator, add the new
one first, then remove the old one. A user with the users/reset_mfa scope clears the
last one with userMfaReset.
Related
- Authentication — every mutation and the assurance levels.
- Authorization and scopes — who can reset the MFA of another user.
