diff --git a/docs/configuration/one-time-password.md b/docs/configuration/one-time-password.md index 6818faacb..d11536901 100644 --- a/docs/configuration/one-time-password.md +++ b/docs/configuration/one-time-password.md @@ -1,14 +1,14 @@ --- layout: default -title: One-Time Password +title: Time-based One-Time Password parent: Configuration nav_order: 4 --- -# One-Time Password +# Time-based One-Time Password Authelia uses time based one-time passwords as the OTP method. You have -the option to tune the settings of the TOTP generation and you can see a +the option to tune the settings of the TOTP generation, and you can see a full example of TOTP configuration below, as well as sections describing them. ```yaml diff --git a/docs/features/2fa/one-time-password.md b/docs/features/2fa/one-time-password.md index 558ca4357..48cba8faf 100644 --- a/docs/features/2fa/one-time-password.md +++ b/docs/features/2fa/one-time-password.md @@ -1,6 +1,6 @@ --- layout: default -title: One-Time Password +title: Time-based One-Time Password nav_order: 1 parent: Second Factor grand_parent: Features @@ -17,12 +17,11 @@ grand_parent: Features After having successfully completed the first factor, select **One-Time Password method** -option and click on **Not registered yet?** link. This will send you an e-mail to confirm -your identity. +option and click on **Not registered yet?** link. This will e-mail you to confirm your identity. *NOTE: If you're testing **Authelia**, this e-mail has likely been sent to the mailbox available at https://mail.example.com:8080/* -Once this validation step is completed, a QRCode gets displayed. +Once this validation step is completed, a QR Code gets displayed.

@@ -34,5 +33,9 @@ From now on, you get tokens generated every 30 seconds that you can use to validate the second factor in **Authelia**. +## Limitations -[Google Authenticator]: https://google-authenticator.com/ \ No newline at end of file +Users currently can only enroll a single TOTP device in **Authelia**. +Multiple single type device enrollment will be available when [this issue](https://github.com/authelia/authelia/issues/275) has been resolved. + +[Google Authenticator]: https://google-authenticator.com/ diff --git a/docs/features/2fa/push-notifications.md b/docs/features/2fa/push-notifications.md index 312d39ec3..7b53439d9 100644 --- a/docs/features/2fa/push-notifications.md +++ b/docs/features/2fa/push-notifications.md @@ -56,6 +56,4 @@ Users must be enrolled via the Duo Admin panel, they cannot enroll a device from It's likely that you have not configured **Authelia** correctly. Please read this documentation again and be sure you had a look at [config.template.yml](https://github.com/authelia/authelia/blob/master/config.template.yml). - - -[Duo]: https://duo.com/ \ No newline at end of file +[Duo]: https://duo.com/ diff --git a/docs/features/2fa/security-key.md b/docs/features/2fa/security-key.md index a547d12cb..a56dda9ee 100644 --- a/docs/features/2fa/security-key.md +++ b/docs/features/2fa/security-key.md @@ -44,6 +44,13 @@ by simply touching the token again when requested: Easy, right?! + +## Limitations + +Users currently can only enroll a single U2F device in **Authelia**. +Multiple single type device enrollment will be available when [this issue](https://github.com/authelia/authelia/issues/275) has been resolved. + + ## FAQ ### Why don't I have access to the *Security Key* option? diff --git a/internal/suites/scenario_available_methods_test.go b/internal/suites/scenario_available_methods_test.go index 328cac3d8..07f9cb1a1 100644 --- a/internal/suites/scenario_available_methods_test.go +++ b/internal/suites/scenario_available_methods_test.go @@ -6,6 +6,8 @@ import ( "time" "github.com/tebeka/selenium" + + "github.com/authelia/authelia/internal/utils" ) type AvailableMethodsScenario struct { @@ -48,16 +50,6 @@ func (s *AvailableMethodsScenario) SetupTest() { s.verifyIsHome(ctx, s.T()) } -func IsStringInList(str string, list []string) bool { - for _, v := range list { - if v == str { - return true - } - } - - return false -} - func (s *AvailableMethodsScenario) TestShouldCheckAvailableMethods() { ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) defer cancel() @@ -85,6 +77,6 @@ func (s *AvailableMethodsScenario) TestShouldCheckAvailableMethods() { s.Assert().Len(optionsList, len(s.methods)) for _, m := range s.methods { - s.Assert().True(IsStringInList(m, optionsList)) + s.Assert().True(utils.IsStringInSlice(m, optionsList)) } } diff --git a/internal/suites/suite_duo_push_test.go b/internal/suites/suite_duo_push_test.go index 6ea2c825a..cbe823657 100644 --- a/internal/suites/suite_duo_push_test.go +++ b/internal/suites/suite_duo_push_test.go @@ -135,7 +135,7 @@ func (s *DuoPushSuite) TestDuoPushRedirectionURLSuite() { func (s *DuoPushSuite) TestAvailableMethodsScenario() { suite.Run(s.T(), NewAvailableMethodsScenario([]string{ - "ONE-TIME PASSWORD", + "TIME-BASED ONE-TIME PASSWORD", "PUSH NOTIFICATION", })) } diff --git a/internal/suites/suite_standalone_test.go b/internal/suites/suite_standalone_test.go index af8dc0c97..edd4881a3 100644 --- a/internal/suites/suite_standalone_test.go +++ b/internal/suites/suite_standalone_test.go @@ -180,7 +180,7 @@ func (s *StandaloneSuite) TestResetPasswordScenario() { } func (s *StandaloneSuite) TestAvailableMethodsScenario() { - suite.Run(s.T(), NewAvailableMethodsScenario([]string{"ONE-TIME PASSWORD"})) + suite.Run(s.T(), NewAvailableMethodsScenario([]string{"TIME-BASED ONE-TIME PASSWORD"})) } func (s *StandaloneSuite) TestRedirectionURLScenario() { diff --git a/internal/templates/html_email.go b/internal/templates/html_email.go index a917324df..818af7b3e 100644 --- a/internal/templates/html_email.go +++ b/internal/templates/html_email.go @@ -93,7 +93,7 @@ const emailHTMLContent = ` } a { - color: #0a8cce; + color: #ffffff; text-decoration: none; text-decoration: none !important; } @@ -105,7 +105,7 @@ const emailHTMLContent = ` .button { padding: 15px 30px; border-radius: 10px; - background: rgb(204, 204, 255); + background: rgb(25, 118, 210); text-decoration: none; } @@ -395,7 +395,7 @@ const emailHTMLContent = ` - Please ignore this email if you did not initiate the process. + Please contact an administrator if you did not initiate the process. diff --git a/internal/templates/plaintext_email.go b/internal/templates/plaintext_email.go index 33196938f..0e855a9dc 100644 --- a/internal/templates/plaintext_email.go +++ b/internal/templates/plaintext_email.go @@ -22,5 +22,5 @@ If you did not initiate the process your credentials might have been compromised To setup your 2FA please visit the following URL: {{.url}} -Please ignore this email if you did not initiate the process. +Please contact an administrator if you did not initiate the process. ` diff --git a/web/src/views/DeviceRegistration/RegisterOneTimePassword.tsx b/web/src/views/DeviceRegistration/RegisterOneTimePassword.tsx index 9f126e139..99a029d0e 100644 --- a/web/src/views/DeviceRegistration/RegisterOneTimePassword.tsx +++ b/web/src/views/DeviceRegistration/RegisterOneTimePassword.tsx @@ -74,7 +74,7 @@ const RegisterOneTimePassword = function () { const qrcodeFuzzyStyle = isLoading || hasErrored ? style.fuzzy : undefined; return ( - +

Need Google Authenticator? diff --git a/web/src/views/LoginPortal/SecondFactor/MethodContainer.tsx b/web/src/views/LoginPortal/SecondFactor/MethodContainer.tsx index d73904788..eb3db9264 100644 --- a/web/src/views/LoginPortal/SecondFactor/MethodContainer.tsx +++ b/web/src/views/LoginPortal/SecondFactor/MethodContainer.tsx @@ -15,6 +15,7 @@ export enum State { export interface Props { id: string; title: string; + registered: boolean; explanation: string; state: State; children: ReactNode; @@ -24,6 +25,7 @@ export interface Props { const DefaultMethodContainer = function (props: Props) { const style = useStyles(); + const registerMessage = props.registered ? "Lost your device?" : "Not registered yet?"; let container: ReactNode; let stateClass: string = ""; @@ -50,7 +52,7 @@ const DefaultMethodContainer = function (props: Props) {
{props.onRegisterClick ? ( - Not registered yet? + {registerMessage} ) : null}
diff --git a/web/src/views/LoginPortal/SecondFactor/MethodSelectionDialog.tsx b/web/src/views/LoginPortal/SecondFactor/MethodSelectionDialog.tsx index 112473309..172a2d276 100644 --- a/web/src/views/LoginPortal/SecondFactor/MethodSelectionDialog.tsx +++ b/web/src/views/LoginPortal/SecondFactor/MethodSelectionDialog.tsx @@ -40,7 +40,7 @@ const MethodSelectionDialog = function (props: Props) { {props.methods.has(SecondFactorMethod.TOTP) ? ( props.onClick(SecondFactorMethod.TOTP)} /> @@ -48,7 +48,7 @@ const MethodSelectionDialog = function (props: Props) { {props.methods.has(SecondFactorMethod.U2F) && props.u2fSupported ? ( } onClick={() => props.onClick(SecondFactorMethod.U2F)} /> diff --git a/web/src/views/LoginPortal/SecondFactor/OneTimePasswordMethod.tsx b/web/src/views/LoginPortal/SecondFactor/OneTimePasswordMethod.tsx index 5e099b48a..128d8800b 100644 --- a/web/src/views/LoginPortal/SecondFactor/OneTimePasswordMethod.tsx +++ b/web/src/views/LoginPortal/SecondFactor/OneTimePasswordMethod.tsx @@ -84,6 +84,7 @@ const OneTimePasswordMethod = function (props: Props) { id={props.id} title="One-Time Password" explanation="Enter one-time password" + registered={props.registered} state={methodState} onRegisterClick={props.onRegisterClick} > diff --git a/web/src/views/LoginPortal/SecondFactor/PushNotificationMethod.tsx b/web/src/views/LoginPortal/SecondFactor/PushNotificationMethod.tsx index 53acd7290..14b7a4874 100644 --- a/web/src/views/LoginPortal/SecondFactor/PushNotificationMethod.tsx +++ b/web/src/views/LoginPortal/SecondFactor/PushNotificationMethod.tsx @@ -98,6 +98,7 @@ const PushNotificationMethod = function (props: Props) { id={props.id} title="Push Notification" explanation="A notification has been sent to your smartphone" + registered={true} state={methodState} >
{icon}
diff --git a/web/src/views/LoginPortal/SecondFactor/SecurityKeyMethod.tsx b/web/src/views/LoginPortal/SecondFactor/SecurityKeyMethod.tsx index b8c825e94..737abfea7 100644 --- a/web/src/views/LoginPortal/SecondFactor/SecurityKeyMethod.tsx +++ b/web/src/views/LoginPortal/SecondFactor/SecurityKeyMethod.tsx @@ -107,6 +107,7 @@ const SecurityKeyMethod = function (props: Props) { id={props.id} title="Security Key" explanation="Touch the token of your security key" + registered={props.registered} state={methodState} onRegisterClick={props.onRegisterClick} >