diff --git a/README.md b/README.md
index ed5f2de..829d3c0 100644
--- a/README.md
+++ b/README.md
@@ -87,9 +87,17 @@ link to set their password.
![forgot-username](images/screenshots/forgot-username.png)
-* If it is enabled in the project, they will also be required to provide a multifactor token after successfully logging in with their username and password
+* If multifactor authentication is enabled in the project, they will also be required to provide a security token after successfully logging in with their username and password. This token can either be sent to their email address or generated by an authenticator app like Google Authenticator or Microsoft Authenticator.
+
+*If authenticator apps are enabled for MFA, users will have a choice of MFA method*
+![mfa-choice](images/screenshots/mfa-choice.png)
+
+*If email is chosen as the MFA method, users will receive an email with a security code*
+![mfa-email](images/screenshots/mfa-email.png)
+
+*If an authenticator app is chosen for the MFA method, users will be prompted to enter a code from their authenticator app*
+![mfa-authenticator-app](images/screenshots/mfa-authenticator-app.png)
-![mfa](images/screenshots/mfa.png)
## Installation
@@ -112,6 +120,8 @@ These are settings/configuration options accessible in the normal External Modul
| **Login Attempts** | Integer | Number of consecutive failed login attempts before being locked out | 3 attempts |
| **Lockout Duration** | Integer | Length of a lockout due to failed login attempts, in seconds | 300 seconds |
| **Multi-Factor Authentication** | Checkbox | Require participants to use multi-factor authentication when logging in. This requires participants to enter a code sent to their email address in addition to their password. | Unchecked |
+| **Multi-Factor Authentication with Authenticator App** | Checkbox | Allow participants to use an authenticator app (like Google Authenticator or Microsoft Authenticator) to generate a security code for multi-factor authentication. If unchecked, participants will only be able to receive a security code via email. | Unchecked |
+| **Restrict Multi-Factor Authentication project settings to REDCap administrators** | Checkbox | If checked, only REDCap administrators will be able to access the multi-factor authentication settings in the project. If unchecked, any REDCapPRO manager will be able to access the multi-factor authentication settings. | Unchecked |
| **Allow Self-Registration** | Checkbox | Allow participants to [register themselves](#self-registration) with **REDCapPRO**. If checked, a link will appear on the login page that will take the participant to a registration page. | Unchecked |
| **Restrict Self-Registration project settings to REDCap administrators** | Checkbox | If checked, only REDCap administrators will be able to access the self-registration settings in the project. If unchecked, any REDCapPRO manager will be able to access the self-registration settings. | Unchecked |
| **Allow Auto-Enroll Upon Self-Registration** | Checkbox | Allow participants to enroll themselves in a project when they register. If checked, the participant will be automatically enrolled in the REDCapPRO project when they self-register | Unchecked |
@@ -234,6 +244,7 @@ accessible by managers.
| **Language** | Dropdown | The language that participant-facing text in the module will appear in. This overrides the default system setting. See the [Translation](#translation) section for more information. | English |
| **Prevent Email Login** | Checkbox | If checked, this prevents participants from using their email address to log in to surveys. Instead, they must use their REDCapPRO username to log in.
*If email logins are prevented at the system level, this setting will not appear in the project setttings tab.* | Unchecked |
| **Multifactor Authentication** | Checkbox | Require participants to use multi-factor authentication when logging in. This requires participants to enter a code sent to their email address in addition to their password. | Unchecked |
+| **Allow MFA Authenticator App** | Checkbox | Allow participants to use an authenticator app (like Google Authenticator or Microsoft Authenticator) to generate a security code for multi-factor authentication. If unchecked, participants will only be able to receive a security code via email. | Unchecked |
| **API** | Checkbox | Enable the API for this project. This allows you to register and enroll participants using the [API](#api). | Unchecked |
| **Allow Self-Registration** | Checkbox | Allow participants to [register themselves](#self-registration) with **REDCapPRO**. If checked, a link will appear on the login page that will take the participant to a registration page. | Unchecked |
| **Auto-Enroll Upon Self-Registration** | Checkbox | Participants will be automatically enrolled in this project when they self-register. | Unchecked |
@@ -352,6 +363,7 @@ information about **REDCapPRO** participants. These are described below:
| Version | Release Date | Description |
| ------- | ------------ | ------------------------------------------------------------------------------------------------------------- |
+| 2.2.0 | 2023-11-21 | Feature release - [Release Notes](https://github.com/AndrewPoppe/REDCap-PRO/releases/tag/2.2.0) |
| 2.1.3 | 2023-11-10 | Minor Change to UI Text - [Release Notes](https://github.com/AndrewPoppe/REDCap-PRO/releases/tag/2.1.3) |
| 2.1.2 | 2023-10-30 | Major Bug fix - [Release Notes](https://github.com/AndrewPoppe/REDCap-PRO/releases/tag/2.1.2) |
| 2.1.1 | 2023-10-27 | Minor Bug fix - [Release Notes](https://github.com/AndrewPoppe/REDCap-PRO/releases/tag/2.1.1) |
diff --git a/REDCapPRO.php b/REDCapPRO.php
index b2f3bef..a00707f 100644
--- a/REDCapPRO.php
+++ b/REDCapPRO.php
@@ -35,17 +35,18 @@ class REDCapPRO extends AbstractExternalModule
public $PROJECT;
public $DAG;
static $COLORS = [
- "primary" => "#900000",
- "secondary" => "#17a2b8",
- "primaryHighlight" => "#c91616",
- "primaryDark" => "#7a0000",
- "primaryLight" => "#ffadad",
- "lightGrey" => "#f9f9f9",
- "mediumGrey" => "#dddddd",
- "darkGrey" => "#6c757d",
- "blue" => "#000090",
- "green" => "#009000",
- "ban" => "tomato"
+ "primary" => "#900000",
+ "secondary" => "#17a2b8",
+ "primaryHighlight" => "#c91616",
+ "primaryDark" => "#7a0000",
+ "primaryLight" => "#ffadad",
+ "primaryExtraLight" => "#fff0f0",
+ "lightGrey" => "#f9f9f9",
+ "mediumGrey" => "#dddddd",
+ "darkGrey" => "#6c757d",
+ "blue" => "#000090",
+ "green" => "#009000",
+ "ban" => "tomato"
];
static $logColumnsCC = [
@@ -213,9 +214,6 @@ public function redcap_survey_page_top(
// Check MFA Token
if ( $settings->mfaEnabled((int) $project_id) && !$auth->is_mfa_verified() ) {
- $code = $auth->get_mfa_code();
- $participantEmail = $participantHelper->getEmail($auth->get_participant_id());
- $this->sendMfaTokenEmail($participantEmail, $code);
header("location: " . $this->framework->getUrl("src/mfa.php", true));
return;
}
@@ -795,6 +793,50 @@ public function sendMfaTokenEmail(string $email, int $token)
}
}
+ /**
+ * Send an email with a link for the participant to get Authenticator App information / QR code
+ *
+ * @param mixed $rcpro_participant_id
+ *
+ * @return mixed
+ */
+ public function sendAuthenticatorAppInfoEmail($rcpro_participant_id)
+ {
+ try {
+
+ $settings = new ProjectSettings($this);
+ $participantHelper = new ParticipantHelper($this);
+
+ // generate token
+ $token = $participantHelper->createAuthenticatorAppInfoToken($rcpro_participant_id);
+ $to = $participantHelper->getEmail($rcpro_participant_id);
+
+ // create email
+ $subject = $this->tt("email_authenticator_app_mfa_info_subject");
+ $from = $settings->getEmailFromAddress();
+ $body = "
" . $this->tt("email_authenticator_app_mfa_info_greeting") . "
+
" . $this->tt("email_authenticator_app_mfa_info_message1") . "
+
" . $this->tt("email_authenticator_app_mfa_info_message2") . "
+
+
" . $this->tt("email_authenticator_app_mfa_info_message5") . "" . $this->tt("email_authenticator_app_mfa_info_link_text") . "
+
" . $this->tt("email_authenticator_app_mfa_info_message4");
+ if ( $this->framework->getProjectId() ) {
+ $study_contact = $this->getContactPerson($subject);
+ if ( isset($study_contact["info"]) ) {
+ $body .= "
" . $study_contact["info"];
+ }
+ }
+ $body .= "
These settings control the use of multi-factor authentication (MFA). Please take care to understand how MFA may be used before enabling it in the system.
`s get reset. However, we also reset the\n// bottom margin to use `rem` units instead of `em`.\n\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n\n// Abbreviations\n//\n// 1. Duplicate behavior to the data-bs-* attribute for our tooltip plugin\n// 2. Add the correct text decoration in Chrome, Edge, Opera, and Safari.\n// 3. Add explicit cursor to indicate changed behavior.\n// 4. Prevent the text-decoration to be skipped.\n\nabbr[title],\nabbr[data-bs-original-title] { // 1\n text-decoration: underline dotted; // 2\n cursor: help; // 3\n text-decoration-skip-ink: none; // 4\n}\n\n\n// Address\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\n\n// Lists\n\nol,\nul {\n padding-left: 2rem;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: $dt-font-weight;\n}\n\n// 1. Undo browser default\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // 1\n}\n\n\n// Blockquote\n\nblockquote {\n margin: 0 0 1rem;\n}\n\n\n// Strong\n//\n// Add the correct font weight in Chrome, Edge, and Safari\n\nb,\nstrong {\n font-weight: $font-weight-bolder;\n}\n\n\n// Small\n//\n// Add the correct font size in all browsers\n\nsmall {\n @include font-size($small-font-size);\n}\n\n\n// Mark\n\nmark {\n padding: $mark-padding;\n background-color: $mark-bg;\n}\n\n\n// Sub and Sup\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n\nsub,\nsup {\n position: relative;\n @include font-size($sub-sup-font-size);\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n// Links\n\na {\n color: $link-color;\n text-decoration: $link-decoration;\n\n &:hover {\n color: $link-hover-color;\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href).\n// It would be more straightforward to just use a[href] in previous block, but that\n// causes specificity issues in many other styles that are too complex to fix.\n// See https://github.com/twbs/bootstrap/issues/19402\n\na:not([href]):not([class]) {\n &,\n &:hover {\n color: inherit;\n text-decoration: none;\n }\n}\n\n\n// Code\n\npre,\ncode,\nkbd,\nsamp {\n font-family: $font-family-code;\n @include font-size(1em); // Correct the odd `em` font sizing in all browsers.\n direction: ltr #{\"/* rtl:ignore */\"};\n unicode-bidi: bidi-override;\n}\n\n// 1. Remove browser default top margin\n// 2. Reset browser default of `1em` to use `rem`s\n// 3. Don't allow content to break outside\n\npre {\n display: block;\n margin-top: 0; // 1\n margin-bottom: 1rem; // 2\n overflow: auto; // 3\n @include font-size($code-font-size);\n color: $pre-color;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n @include font-size(inherit);\n color: inherit;\n word-break: normal;\n }\n}\n\ncode {\n @include font-size($code-font-size);\n color: $code-color;\n word-wrap: break-word;\n\n // Streamline the style when inside anchors to avoid broken underline and more\n a > & {\n color: inherit;\n }\n}\n\nkbd {\n padding: $kbd-padding-y $kbd-padding-x;\n @include font-size($kbd-font-size);\n color: $kbd-color;\n background-color: $kbd-bg;\n @include border-radius($border-radius-sm);\n\n kbd {\n padding: 0;\n @include font-size(1em);\n font-weight: $nested-kbd-font-weight;\n }\n}\n\n\n// Figures\n//\n// Apply a consistent margin strategy (matches our type styles).\n\nfigure {\n margin: 0 0 1rem;\n}\n\n\n// Images and content\n\nimg,\nsvg {\n vertical-align: middle;\n}\n\n\n// Tables\n//\n// Prevent double borders\n\ntable {\n caption-side: bottom;\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: $table-cell-padding-y;\n padding-bottom: $table-cell-padding-y;\n color: $table-caption-color;\n text-align: left;\n}\n\n// 1. Removes font-weight bold by inheriting\n// 2. Matches default `