mirror of
https://github.com/CloudNebulaProject/barycenter.git
synced 2026-04-10 21:20:41 +00:00
125 lines
4 KiB
Markdown
125 lines
4 KiB
Markdown
|
|
# Admin-Enforced 2FA
|
||
|
|
|
||
|
|
Admin-enforced two-factor authentication allows an administrator to mandate that a specific user must always complete a second authentication factor (passkey verification) after entering their password. This is the strongest 2FA enforcement mode -- the user cannot bypass it regardless of what they are accessing.
|
||
|
|
|
||
|
|
## How It Works
|
||
|
|
|
||
|
|
1. An administrator sets the `requires_2fa` flag for a user via the [Admin GraphQL API](../admin/graphql-api.md).
|
||
|
|
2. The flag is stored in the `users` table as `requires_2fa = 1`.
|
||
|
|
3. On the user's next login, after successful password authentication, Barycenter checks the flag.
|
||
|
|
4. If `requires_2fa = 1`, a **partial session** is created with `mfa_verified = 0` and the user is redirected to `/login/2fa`.
|
||
|
|
5. The user completes passkey verification.
|
||
|
|
6. The session is upgraded to `mfa_verified = 1` with `acr = "aal2"`.
|
||
|
|
|
||
|
|
## Enabling 2FA for a User
|
||
|
|
|
||
|
|
Use the `setUser2faRequired` GraphQL mutation on the admin API (default port 9091):
|
||
|
|
|
||
|
|
```graphql
|
||
|
|
mutation {
|
||
|
|
setUser2faRequired(username: "alice", required: true) {
|
||
|
|
success
|
||
|
|
message
|
||
|
|
requires2fa
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
Example using `curl`:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
curl -X POST http://localhost:9091/graphql \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-d '{
|
||
|
|
"query": "mutation { setUser2faRequired(username: \"alice\", required: true) { success message requires2fa } }"
|
||
|
|
}'
|
||
|
|
```
|
||
|
|
|
||
|
|
### Response
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"data": {
|
||
|
|
"setUser2faRequired": {
|
||
|
|
"success": true,
|
||
|
|
"message": "2FA requirement updated for user alice",
|
||
|
|
"requires2fa": true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Disabling 2FA for a User
|
||
|
|
|
||
|
|
Pass `required: false` to remove the enforcement:
|
||
|
|
|
||
|
|
```graphql
|
||
|
|
mutation {
|
||
|
|
setUser2faRequired(username: "alice", required: false) {
|
||
|
|
success
|
||
|
|
message
|
||
|
|
requires2fa
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
This removes the mandatory 2FA requirement. The user will still be prompted for 2FA if a [context-based trigger](./2fa-context-based.md) applies to a specific authorization request.
|
||
|
|
|
||
|
|
## Checking a User's 2FA Status
|
||
|
|
|
||
|
|
Use the `user2faStatus` query to inspect whether a user has 2FA enabled and whether they have passkeys enrolled:
|
||
|
|
|
||
|
|
```graphql
|
||
|
|
query {
|
||
|
|
user2faStatus(username: "alice") {
|
||
|
|
username
|
||
|
|
requires2fa
|
||
|
|
passkeyEnrolled
|
||
|
|
passkeyCount
|
||
|
|
passkeyEnrolledAt
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Response
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"data": {
|
||
|
|
"user2faStatus": {
|
||
|
|
"username": "alice",
|
||
|
|
"requires2fa": true,
|
||
|
|
"passkeyEnrolled": true,
|
||
|
|
"passkeyCount": 2,
|
||
|
|
"passkeyEnrolledAt": "2026-01-15T10:30:00Z"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Database Column
|
||
|
|
|
||
|
|
The enforcement flag is stored in the `users` table:
|
||
|
|
|
||
|
|
| Column | Type | Description |
|
||
|
|
|----------------|---------|---------------------------------------------|
|
||
|
|
| `requires_2fa` | Integer | `0` = not required (default), `1` = required|
|
||
|
|
|
||
|
|
## The Redirect to /login/2fa
|
||
|
|
|
||
|
|
When Barycenter detects that 2FA is required for a user who has just authenticated with a password, it:
|
||
|
|
|
||
|
|
1. Creates a partial session with `mfa_verified = 0`, `amr = ["pwd"]`, `acr = "aal1"`.
|
||
|
|
2. Preserves the pending authorization request parameters in the session.
|
||
|
|
3. Returns an HTTP redirect to `/login/2fa`.
|
||
|
|
|
||
|
|
The `/login/2fa` page presents the user with a passkey verification prompt. Upon successful verification, the session is upgraded and the user is redirected back to `/authorize` to continue the OAuth flow.
|
||
|
|
|
||
|
|
See [2FA Flow Walkthrough](./2fa-flow.md) for the complete sequence.
|
||
|
|
|
||
|
|
## Considerations
|
||
|
|
|
||
|
|
- **Passkey enrollment**: A user must have at least one registered passkey before admin-enforced 2FA can be completed. If the user has no passkeys, they will be unable to satisfy the 2FA requirement and will be stuck at the `/login/2fa` page. Use the `user2faStatus` query to verify enrollment before enabling the flag.
|
||
|
|
- **Existing sessions**: Enabling `requires_2fa` does not invalidate existing sessions. The flag is checked at the next login. To force re-authentication, expire the user's current sessions.
|
||
|
|
- **Admin access**: The GraphQL admin API should be protected and not exposed publicly. See [Admin GraphQL API](../admin/graphql-api.md) for access control guidance.
|